mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 00:03:43 +00:00
Merge branch 'master' into sync-from-rust
This commit is contained in:
commit
d39b45a58d
@ -8,4 +8,4 @@ lint = "clippy --all-targets -- -Aclippy::collapsible_if -Aclippy::needless_pass
|
||||
linker = "rust-lld"
|
||||
|
||||
[env]
|
||||
CARGO_WORKSPACE_DIR = { value = "", relative = true }
|
||||
CARGO_WORKSPACE_DIR = { value = "", relative = true }
|
||||
|
8
.github/workflows/ci.yaml
vendored
8
.github/workflows/ci.yaml
vendored
@ -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
|
||||
|
23
.github/workflows/metrics.yaml
vendored
23
.github/workflows/metrics.yaml
vendored
@ -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 📈
|
||||
|
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@ -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
|
||||
|
116
Cargo.lock
generated
116
Cargo.lock
generated
@ -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,15 +1483,35 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_lexer"
|
||||
version = "0.1.0"
|
||||
name = "ra-ap-rustc_index"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1c145702ed3f237918e512685185dc8a4d0edc3a5326c63d20361d8ba9b45b3"
|
||||
checksum = "07b5fa61d34da18e148dc3a81f654488ea07f40938d8aefb17f8b64bb78c6120"
|
||||
dependencies = [
|
||||
"unic-emoji-char",
|
||||
"arrayvec",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ra-ap-rustc_lexer"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2e2f6b48422e4eed5218277ab7cc9733e60dd8f3167f4f36a49a0cafe4dc195"
|
||||
dependencies = [
|
||||
"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"
|
||||
@ -1522,6 +1543,15 @@ dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@ -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",
|
||||
@ -2056,47 +2085,6 @@ version = "1.16.1"
|
||||
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"
|
||||
@ -2127,6 +2115,12 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
12
Cargo.toml
12
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 }
|
||||
|
@ -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 },
|
||||
|
@ -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`.
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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, "<EFBFBD>"),
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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 })
|
||||
}
|
||||
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
502
crates/hir-def/src/hir/format_args.rs
Normal file
502
crates/hir-def/src/hir/format_args.rs
Normal file
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
@ -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.
|
||||
@ -108,12 +110,15 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
|
||||
|
||||
let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
|
||||
let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
|
||||
if per_ns.is_none() { None } else { Some((name, per_ns)) }
|
||||
if per_ns.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some((name, per_ns))
|
||||
}
|
||||
});
|
||||
|
||||
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 +130,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 +207,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
map.shrink_to_fit();
|
||||
map
|
||||
}
|
||||
|
||||
@ -200,11 +236,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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -22,6 +22,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(
|
||||
@ -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.);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
"##]],
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
"##]],
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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}}")?,
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
|
@ -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)?;
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@ -584,6 +586,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(
|
||||
|
@ -163,25 +163,56 @@ impl<'a> DeclValidator<'a> {
|
||||
|| allows.contains(allow::NONSTANDARD_STYLE)
|
||||
})
|
||||
};
|
||||
|
||||
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 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(self.db.upcast()).container.into()),
|
||||
AdtId::EnumId(eid) => Some(eid.lookup(self.db.upcast()).container.into()),
|
||||
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())
|
||||
})
|
||||
};
|
||||
|
||||
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(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) {
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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(_)
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
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_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);
|
||||
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 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);
|
||||
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
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
T::infer(self, inner, &expected_ty, default_bm);
|
||||
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);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let err_ty = self.err_ty();
|
||||
for (_, inner) in subs {
|
||||
T::infer(self, inner, &err_ty, default_bm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty
|
||||
|
@ -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) {
|
||||
|
@ -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)?;
|
||||
|
||||
|
@ -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()));
|
||||
|
@ -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()),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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")?;
|
||||
|
@ -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()))
|
||||
|
@ -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()),
|
||||
}
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
@ -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)?;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -2,55 +2,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(
|
||||
@ -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<'_>
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -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),
|
||||
AssocItem::Function(it) => it.attr_id(),
|
||||
AssocItem::Const(it) => it.attr_id(),
|
||||
AssocItem::TypeAlias(it) => it.attr_id(),
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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`.
|
||||
fn resolve_doc_path(
|
||||
pub fn resolve_doc_path_on(
|
||||
db: &dyn HirDatabase,
|
||||
def: AttrDefId,
|
||||
def: impl HasAttrs,
|
||||
link: &str,
|
||||
ns: Option<Namespace>,
|
||||
) -> Option<DocLinkDef> {
|
||||
let resolver = match def {
|
||||
// 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)
|
||||
}
|
||||
|
||||
fn resolve_doc_path_on_(
|
||||
db: &dyn HirDatabase,
|
||||
link: &str,
|
||||
attr_id: AttrDefId,
|
||||
ns: Option<Namespace>,
|
||||
) -> Option<DocLinkDef> {
|
||||
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)?;
|
||||
|
@ -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)]
|
||||
@ -182,6 +184,13 @@ pub struct PrivateAssocItem {
|
||||
pub item: AssocItem,
|
||||
}
|
||||
|
||||
#[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>>,
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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 {
|
||||
|
159
crates/ide-assists/src/handlers/bind_unused_param.rs
Normal file
159
crates/ide-assists/src/handlers/bind_unused_param.rs
Normal file
@ -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; }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -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(_)
|
||||
|
@ -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)
|
||||
}
|
||||
"#,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
205
crates/ide-assists/src/handlers/into_to_qualified_from.rs
Normal file
205
crates/ide-assists/src/handlers/into_to_qualified_from.rs
Normal file
@ -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);
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
}
|
@ -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(_)
|
||||
@ -179,6 +185,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);
|
||||
|
@ -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(_)
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -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,
|
||||
|
@ -265,6 +265,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(
|
||||
@ -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);
|
||||
}
|
||||
@ -1754,6 +1756,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(
|
||||
|
@ -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(),
|
||||
|
@ -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));
|
||||
// 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);
|
||||
|
||||
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);
|
||||
|
||||
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] = [
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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},
|
||||
|
@ -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},
|
||||
|
@ -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},
|
||||
|
@ -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};
|
||||
|
@ -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),
|
||||
|
@ -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},
|
||||
|
@ -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::{
|
||||
|
@ -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,
|
||||
|
@ -66,11 +66,6 @@ struct Foo;
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inside_nested_attr() {
|
||||
check(r#"#[cfg($0)]"#, expect![[]])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_existing_attr() {
|
||||
check(
|
||||
@ -635,6 +630,32 @@ struct Foo;
|
||||
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(
|
||||
@ -644,6 +665,13 @@ mod cfg {
|
||||
ba little
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"#[cfg(target_endian = b$0"#,
|
||||
expect![[r#"
|
||||
ba big
|
||||
ba little
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
281
crates/ide-db/src/documentation.rs
Normal file
281
crates/ide-db/src/documentation.rs
Normal file
@ -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)
|
||||
}
|
@ -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;
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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```");
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
@ -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
|
||||
) {}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user