mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-24 07:44:10 +00:00
Merge remote-tracking branch 'upstream/master' into sync-from-rust
This commit is contained in:
commit
71499fcd22
@ -7,13 +7,10 @@ trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
|
||||
[*.{rs,toml}]
|
||||
indent_size = 4
|
||||
|
||||
[*.ts]
|
||||
indent_size = 4
|
||||
[*.js]
|
||||
indent_size = 4
|
||||
[*.json]
|
||||
indent_size = 4
|
||||
[*.md]
|
||||
indent_size = 2
|
||||
|
||||
[*.{yml, yaml}]
|
||||
indent_size = 2
|
||||
|
2
.github/workflows/autopublish.yaml
vendored
2
.github/workflows/autopublish.yaml
vendored
@ -49,8 +49,8 @@ jobs:
|
||||
cargo workspaces rename --from project-model project_model
|
||||
cargo workspaces rename --from test-utils test_utils
|
||||
cargo workspaces rename --from text-edit text_edit
|
||||
cargo workspaces rename ra_ap_%n
|
||||
# Remove library crates from the workspaces so we don't auto-publish them as well
|
||||
sed -i 's/ "lib\/\*",//' ./Cargo.toml
|
||||
cargo workspaces rename ra_ap_%n
|
||||
find crates/rust-analyzer -type f -name '*.rs' -exec sed -i 's/rust_analyzer/ra_ap_rust_analyzer/g' {} +
|
||||
cargo workspaces publish --yes --force '*' --exact --no-git-commit --allow-dirty --skip-published custom 0.0.$(($RUN_NUMBER + 133))
|
||||
|
141
.github/workflows/metrics.yaml
vendored
141
.github/workflows/metrics.yaml
vendored
@ -1,8 +1,8 @@
|
||||
name: metrics
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
@ -11,20 +11,135 @@ env:
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
|
||||
jobs:
|
||||
metrics:
|
||||
setup_cargo:
|
||||
if: github.repository == 'rust-lang/rust-analyzer'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup update --no-self-update stable
|
||||
rustup component add rustfmt rust-src
|
||||
rustup default stable
|
||||
- name: Cache cargo
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
key: ${{ runner.os }}-cargo-${{ github.sha }}
|
||||
|
||||
build_metrics:
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup_cargo
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup update --no-self-update stable
|
||||
rustup component add rustfmt rust-src
|
||||
- name: Restore cargo cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
key: ${{ runner.os }}-cargo-${{ github.sha }}
|
||||
|
||||
- name: Collect metrics
|
||||
run: cargo xtask metrics
|
||||
env:
|
||||
METRICS_TOKEN: ${{ secrets.METRICS_TOKEN }}
|
||||
- name: Collect build metrics
|
||||
run: cargo xtask metrics build
|
||||
|
||||
- name: Cache target
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/
|
||||
key: ${{ runner.os }}-target-${{ github.sha }}
|
||||
|
||||
- name: Upload build metrics
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: build-${{ github.sha }}
|
||||
path: target/build.json
|
||||
if-no-files-found: error
|
||||
|
||||
other_metrics:
|
||||
strategy:
|
||||
matrix:
|
||||
names: [self, ripgrep, webrender, diesel]
|
||||
runs-on: ubuntu-latest
|
||||
needs: [setup_cargo, build_metrics]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Restore cargo cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
key: ${{ runner.os }}-cargo-${{ github.sha }}
|
||||
|
||||
- name: Restore target cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/
|
||||
key: ${{ runner.os }}-target-${{ github.sha }}
|
||||
|
||||
- name: Collect metrics
|
||||
run: cargo xtask metrics ${{ matrix.names }}
|
||||
|
||||
- name: Upload metrics
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.names }}-${{ github.sha }}
|
||||
path: target/${{ matrix.names }}.json
|
||||
if-no-files-found: error
|
||||
|
||||
generate_final_metrics:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_metrics, other_metrics]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download build metrics
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-${{ github.sha }}
|
||||
|
||||
- name: Download self metrics
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: self-${{ github.sha }}
|
||||
|
||||
- name: Download ripgrep metrics
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ripgrep-${{ github.sha }}
|
||||
|
||||
- name: Download webrender metrics
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: webrender-${{ github.sha }}
|
||||
|
||||
- name: Download diesel metrics
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: diesel-${{ 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
|
||||
cd metrics
|
||||
git add .
|
||||
git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈
|
||||
git push origin master
|
||||
env:
|
||||
METRICS_TOKEN: ${{ secrets.METRICS_TOKEN }}
|
||||
|
474
Cargo.lock
generated
474
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
24
Cargo.toml
@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-test/imp"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
rust-version = "1.66"
|
||||
rust-version = "1.70"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
authors = ["rust-analyzer team"]
|
||||
@ -35,6 +35,10 @@ debug = 0
|
||||
# chalk-ir = { path = "../chalk/chalk-ir" }
|
||||
# chalk-recursive = { path = "../chalk/chalk-recursive" }
|
||||
# chalk-derive = { path = "../chalk/chalk-derive" }
|
||||
# line-index = { path = "lib/line-index" }
|
||||
# la-arena = { path = "lib/la-arena" }
|
||||
# lsp-server = { path = "lib/lsp-server" }
|
||||
|
||||
|
||||
# ungrammar = { path = "../ungrammar" }
|
||||
|
||||
@ -57,13 +61,13 @@ ide-diagnostics = { path = "./crates/ide-diagnostics", version = "0.0.0" }
|
||||
ide-ssr = { path = "./crates/ide-ssr", version = "0.0.0" }
|
||||
intern = { path = "./crates/intern", version = "0.0.0" }
|
||||
limit = { path = "./crates/limit", version = "0.0.0" }
|
||||
load-cargo = { path = "./crates/load-cargo", version = "0.0.0" }
|
||||
mbe = { path = "./crates/mbe", version = "0.0.0" }
|
||||
parser = { path = "./crates/parser", version = "0.0.0" }
|
||||
paths = { path = "./crates/paths", version = "0.0.0" }
|
||||
proc-macro-api = { path = "./crates/proc-macro-api", version = "0.0.0" }
|
||||
proc-macro-srv = { path = "./crates/proc-macro-srv", version = "0.0.0" }
|
||||
proc-macro-srv-cli = { path = "./crates/proc-macro-srv-cli", version = "0.0.0" }
|
||||
proc-macro-test = { path = "./crates/proc-macro-test", version = "0.0.0" }
|
||||
profile = { path = "./crates/profile", version = "0.0.0" }
|
||||
project-model = { path = "./crates/project-model", version = "0.0.0" }
|
||||
sourcegen = { path = "./crates/sourcegen", version = "0.0.0" }
|
||||
@ -75,7 +79,14 @@ toolchain = { path = "./crates/toolchain", version = "0.0.0" }
|
||||
tt = { path = "./crates/tt", version = "0.0.0" }
|
||||
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
|
||||
vfs = { path = "./crates/vfs", version = "0.0.0" }
|
||||
line-index = { version = "0.1.0-pre.1", path = "./lib/line-index" }
|
||||
|
||||
# local crates that aren't published to crates.io. These should not have versions.
|
||||
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.1" }
|
||||
|
||||
# non-local crates
|
||||
smallvec = { version = "1.10.0", features = [
|
||||
@ -86,9 +97,10 @@ smallvec = { version = "1.10.0", features = [
|
||||
smol_str = "0.2.0"
|
||||
nohash-hasher = "0.2.0"
|
||||
text-size = "1.1.0"
|
||||
# the following crates are pinned to prevent us from pulling in syn 2 until all our dependencies have moved
|
||||
serde = { version = "=1.0.156", features = ["derive"] }
|
||||
serde_json = "1.0.94"
|
||||
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" }
|
||||
|
@ -17,7 +17,7 @@ rustc-hash = "1.1.0"
|
||||
|
||||
triomphe.workspace = true
|
||||
|
||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||
la-arena.workspace = true
|
||||
|
||||
# local deps
|
||||
cfg.workspace = true
|
||||
|
@ -26,7 +26,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
|
||||
let fixture = ChangeFixture::parse(ra_fixture);
|
||||
let mut db = Self::default();
|
||||
fixture.change.apply(&mut db);
|
||||
assert_eq!(fixture.files.len(), 1);
|
||||
assert_eq!(fixture.files.len(), 1, "Multiple file found in the fixture");
|
||||
(db, fixture.files[0])
|
||||
}
|
||||
|
||||
@ -102,6 +102,8 @@ pub struct ChangeFixture {
|
||||
pub change: Change,
|
||||
}
|
||||
|
||||
const SOURCE_ROOT_PREFIX: &str = "/";
|
||||
|
||||
impl ChangeFixture {
|
||||
pub fn parse(ra_fixture: &str) -> ChangeFixture {
|
||||
Self::parse_with_proc_macros(ra_fixture, Vec::new())
|
||||
@ -131,7 +133,6 @@ impl ChangeFixture {
|
||||
|
||||
let mut file_set = FileSet::default();
|
||||
let mut current_source_root_kind = SourceRootKind::Local;
|
||||
let source_root_prefix = "/".to_string();
|
||||
let mut file_id = FileId(0);
|
||||
let mut roots = Vec::new();
|
||||
|
||||
@ -151,19 +152,23 @@ impl ChangeFixture {
|
||||
entry.text.clone()
|
||||
};
|
||||
|
||||
let meta = FileMeta::from(entry);
|
||||
assert!(meta.path.starts_with(&source_root_prefix));
|
||||
let meta = FileMeta::from_fixture(entry, current_source_root_kind);
|
||||
assert!(meta.path.starts_with(SOURCE_ROOT_PREFIX));
|
||||
if !meta.deps.is_empty() {
|
||||
assert!(meta.krate.is_some(), "can't specify deps without naming the crate")
|
||||
}
|
||||
|
||||
if let Some(kind) = &meta.introduce_new_source_root {
|
||||
let root = match current_source_root_kind {
|
||||
if let Some(kind) = meta.introduce_new_source_root {
|
||||
assert!(
|
||||
meta.krate.is_some(),
|
||||
"new_source_root meta doesn't make sense without crate meta"
|
||||
);
|
||||
let prev_kind = mem::replace(&mut current_source_root_kind, kind);
|
||||
let prev_root = match prev_kind {
|
||||
SourceRootKind::Local => SourceRoot::new_local(mem::take(&mut file_set)),
|
||||
SourceRootKind::Library => SourceRoot::new_library(mem::take(&mut file_set)),
|
||||
};
|
||||
roots.push(root);
|
||||
current_source_root_kind = *kind;
|
||||
roots.push(prev_root);
|
||||
}
|
||||
|
||||
if let Some((krate, origin, version)) = meta.krate {
|
||||
@ -185,7 +190,7 @@ impl ChangeFixture {
|
||||
Some(toolchain),
|
||||
);
|
||||
let prev = crates.insert(crate_name.clone(), crate_id);
|
||||
assert!(prev.is_none());
|
||||
assert!(prev.is_none(), "multiple crates with same name: {}", crate_name);
|
||||
for dep in meta.deps {
|
||||
let prelude = meta.extern_prelude.contains(&dep);
|
||||
let dep = CrateName::normalize_dashes(&dep);
|
||||
@ -219,7 +224,7 @@ impl ChangeFixture {
|
||||
false,
|
||||
CrateOrigin::Local { repo: None, name: None },
|
||||
default_target_data_layout
|
||||
.map(|x| x.into())
|
||||
.map(|it| it.into())
|
||||
.ok_or_else(|| "target_data_layout unset".into()),
|
||||
Some(toolchain),
|
||||
);
|
||||
@ -442,51 +447,74 @@ struct FileMeta {
|
||||
target_data_layout: Option<String>,
|
||||
}
|
||||
|
||||
fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) {
|
||||
if let Some((a, b)) = crate_str.split_once('@') {
|
||||
let (version, origin) = match b.split_once(':') {
|
||||
Some(("CratesIo", data)) => match data.split_once(',') {
|
||||
Some((version, url)) => {
|
||||
(version, CrateOrigin::Local { repo: Some(url.to_owned()), name: None })
|
||||
}
|
||||
_ => panic!("Bad crates.io parameter: {data}"),
|
||||
},
|
||||
_ => panic!("Bad string for crate origin: {b}"),
|
||||
};
|
||||
(a.to_owned(), origin, Some(version.to_string()))
|
||||
} else {
|
||||
let crate_origin = match LangCrateOrigin::from(&*crate_str) {
|
||||
LangCrateOrigin::Other => CrateOrigin::Local { repo: None, name: None },
|
||||
origin => CrateOrigin::Lang(origin),
|
||||
};
|
||||
(crate_str, crate_origin, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fixture> for FileMeta {
|
||||
fn from(f: Fixture) -> FileMeta {
|
||||
impl FileMeta {
|
||||
fn from_fixture(f: Fixture, current_source_root_kind: SourceRootKind) -> Self {
|
||||
let mut cfg = CfgOptions::default();
|
||||
f.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into()));
|
||||
f.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into()));
|
||||
for (k, v) in f.cfgs {
|
||||
if let Some(v) = v {
|
||||
cfg.insert_key_value(k.into(), v.into());
|
||||
} else {
|
||||
cfg.insert_atom(k.into());
|
||||
}
|
||||
}
|
||||
|
||||
let introduce_new_source_root = f.introduce_new_source_root.map(|kind| match &*kind {
|
||||
"local" => SourceRootKind::Local,
|
||||
"library" => SourceRootKind::Library,
|
||||
invalid => panic!("invalid source root kind '{invalid}'"),
|
||||
});
|
||||
let current_source_root_kind =
|
||||
introduce_new_source_root.unwrap_or(current_source_root_kind);
|
||||
|
||||
let deps = f.deps;
|
||||
FileMeta {
|
||||
Self {
|
||||
path: f.path,
|
||||
krate: f.krate.map(parse_crate),
|
||||
krate: f.krate.map(|it| parse_crate(it, current_source_root_kind, f.library)),
|
||||
extern_prelude: f.extern_prelude.unwrap_or_else(|| deps.clone()),
|
||||
deps,
|
||||
cfg,
|
||||
edition: f.edition.as_ref().map_or(Edition::CURRENT, |v| Edition::from_str(v).unwrap()),
|
||||
edition: f.edition.map_or(Edition::CURRENT, |v| Edition::from_str(&v).unwrap()),
|
||||
env: f.env.into_iter().collect(),
|
||||
introduce_new_source_root: f.introduce_new_source_root.map(|kind| match &*kind {
|
||||
"local" => SourceRootKind::Local,
|
||||
"library" => SourceRootKind::Library,
|
||||
invalid => panic!("invalid source root kind '{invalid}'"),
|
||||
}),
|
||||
introduce_new_source_root,
|
||||
target_data_layout: f.target_data_layout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_crate(
|
||||
crate_str: String,
|
||||
current_source_root_kind: SourceRootKind,
|
||||
explicit_non_workspace_member: bool,
|
||||
) -> (String, CrateOrigin, Option<String>) {
|
||||
// syntax:
|
||||
// "my_awesome_crate"
|
||||
// "my_awesome_crate@0.0.1,http://example.com"
|
||||
let (name, repo, version) = if let Some((name, remain)) = crate_str.split_once('@') {
|
||||
let (version, repo) =
|
||||
remain.split_once(',').expect("crate meta: found '@' without version and url");
|
||||
(name.to_owned(), Some(repo.to_owned()), Some(version.to_owned()))
|
||||
} else {
|
||||
(crate_str, None, None)
|
||||
};
|
||||
|
||||
let non_workspace_member = explicit_non_workspace_member
|
||||
|| matches!(current_source_root_kind, SourceRootKind::Library);
|
||||
|
||||
let origin = match LangCrateOrigin::from(&*name) {
|
||||
LangCrateOrigin::Other => {
|
||||
let name = name.clone();
|
||||
if non_workspace_member {
|
||||
CrateOrigin::Library { repo, name }
|
||||
} else {
|
||||
CrateOrigin::Local { repo, name: Some(name) }
|
||||
}
|
||||
}
|
||||
origin => CrateOrigin::Lang(origin),
|
||||
};
|
||||
|
||||
(name, origin, version)
|
||||
}
|
||||
|
||||
// Identity mapping
|
||||
#[derive(Debug)]
|
||||
struct IdentityProcMacroExpander;
|
||||
|
@ -138,12 +138,12 @@ impl ops::Deref for CrateName {
|
||||
}
|
||||
}
|
||||
|
||||
/// Origin of the crates. It is used in emitting monikers.
|
||||
/// Origin of the crates.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum CrateOrigin {
|
||||
/// Crates that are from the rustc workspace
|
||||
/// Crates that are from the rustc workspace.
|
||||
Rustc { name: String },
|
||||
/// Crates that are workspace members,
|
||||
/// Crates that are workspace members.
|
||||
Local { repo: Option<String>, name: Option<String> },
|
||||
/// Crates that are non member libraries.
|
||||
Library { repo: Option<String>, name: String },
|
||||
|
@ -18,13 +18,13 @@ rustc-hash = "1.1.0"
|
||||
tt.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.4.0"
|
||||
expect-test = "1.4.1"
|
||||
oorandom = "11.1.3"
|
||||
# We depend on both individually instead of using `features = ["derive"]` to microoptimize the
|
||||
# build graph: if the feature was enabled, syn would be built early on in the graph if `smolstr`
|
||||
# supports `arbitrary`. This way, we avoid feature unification.
|
||||
arbitrary = "1.2.2"
|
||||
derive_arbitrary = "1.2.2"
|
||||
arbitrary = "1.3.0"
|
||||
derive_arbitrary = "1.3.1"
|
||||
|
||||
# local deps
|
||||
mbe.workspace = true
|
||||
|
@ -69,7 +69,7 @@ impl CfgOptions {
|
||||
}
|
||||
|
||||
pub fn get_cfg_keys(&self) -> impl Iterator<Item = &SmolStr> {
|
||||
self.enabled.iter().map(|x| match x {
|
||||
self.enabled.iter().map(|it| match it {
|
||||
CfgAtom::Flag(key) => key,
|
||||
CfgAtom::KeyValue { key, .. } => key,
|
||||
})
|
||||
@ -79,7 +79,7 @@ impl CfgOptions {
|
||||
&'a self,
|
||||
cfg_key: &'a str,
|
||||
) -> impl Iterator<Item = &'a SmolStr> + 'a {
|
||||
self.enabled.iter().filter_map(move |x| match x {
|
||||
self.enabled.iter().filter_map(move |it| match it {
|
||||
CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value),
|
||||
_ => None,
|
||||
})
|
||||
|
@ -12,9 +12,9 @@ rust-version.workspace = true
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
crossbeam-channel = "0.5.5"
|
||||
crossbeam-channel = "0.5.8"
|
||||
tracing = "0.1.37"
|
||||
cargo_metadata = "0.15.0"
|
||||
cargo_metadata = "0.15.4"
|
||||
rustc-hash = "1.1.0"
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
|
@ -21,14 +21,14 @@ dashmap = { version = "=5.4.0", features = ["raw-api"] }
|
||||
drop_bomb = "0.1.5"
|
||||
either = "1.7.0"
|
||||
fst = { version = "0.4.7", default-features = false }
|
||||
hashbrown = { version = "0.12.1", default-features = false }
|
||||
indexmap = "1.9.1"
|
||||
indexmap = "2.0.0"
|
||||
itertools = "0.10.5"
|
||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||
la-arena.workspace = true
|
||||
once_cell = "1.17.0"
|
||||
rustc-hash = "1.1.0"
|
||||
smallvec.workspace = true
|
||||
tracing = "0.1.35"
|
||||
smallvec.workspace = true
|
||||
hashbrown.workspace = true
|
||||
triomphe.workspace = true
|
||||
|
||||
rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
|
||||
|
@ -137,13 +137,16 @@ impl Attrs {
|
||||
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
|
||||
let Some(variant) = enum_.variants.clone().filter(|variant| {
|
||||
let attrs = item_tree.attrs(db, krate, (*variant).into());
|
||||
attrs.is_cfg_enabled(cfg_options)
|
||||
})
|
||||
.zip(0u32..)
|
||||
.find(|(_variant, idx)| it.local_id == Idx::from_raw(RawIdx::from(*idx)))
|
||||
.map(|(variant, _idx)| variant)
|
||||
let Some(variant) = enum_
|
||||
.variants
|
||||
.clone()
|
||||
.filter(|variant| {
|
||||
let attrs = item_tree.attrs(db, krate, (*variant).into());
|
||||
attrs.is_cfg_enabled(cfg_options)
|
||||
})
|
||||
.zip(0u32..)
|
||||
.find(|(_variant, idx)| it.local_id == Idx::from_raw(RawIdx::from(*idx)))
|
||||
.map(|(variant, _idx)| variant)
|
||||
else {
|
||||
return Arc::new(res);
|
||||
};
|
||||
@ -272,6 +275,25 @@ impl Attrs {
|
||||
self.by_key("proc_macro_derive").exists()
|
||||
}
|
||||
|
||||
pub fn is_test(&self) -> bool {
|
||||
self.iter().any(|it| {
|
||||
it.path()
|
||||
.segments()
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(["core", "prelude", "v1", "test"].iter().rev())
|
||||
.all(|it| it.0.as_str() == Some(it.1))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_ignore(&self) -> bool {
|
||||
self.by_key("ignore").exists()
|
||||
}
|
||||
|
||||
pub fn is_bench(&self) -> bool {
|
||||
self.by_key("bench").exists()
|
||||
}
|
||||
|
||||
pub fn is_unstable(&self) -> bool {
|
||||
self.by_key("unstable").exists()
|
||||
}
|
||||
@ -282,7 +304,7 @@ use std::slice::Iter as SliceIter;
|
||||
pub enum DocAtom {
|
||||
/// eg. `#[doc(hidden)]`
|
||||
Flag(SmolStr),
|
||||
/// eg. `#[doc(alias = "x")]`
|
||||
/// eg. `#[doc(alias = "it")]`
|
||||
///
|
||||
/// Note that a key can have multiple values that are all considered "active" at the same time.
|
||||
/// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`.
|
||||
@ -462,6 +484,7 @@ impl AttrsWithOwner {
|
||||
}
|
||||
},
|
||||
AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
|
||||
AttrDefId::ExternCrateId(it) => attrs_from_item_tree_loc(db, it),
|
||||
};
|
||||
|
||||
let attrs = raw_attrs.filter(db.upcast(), def.krate(db));
|
||||
@ -546,6 +569,7 @@ impl AttrsWithOwner {
|
||||
.map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())),
|
||||
},
|
||||
AttrDefId::ExternBlockId(id) => any_has_attrs(db, id),
|
||||
AttrDefId::ExternCrateId(id) => any_has_attrs(db, id),
|
||||
};
|
||||
|
||||
AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs))
|
||||
|
@ -273,10 +273,10 @@ impl Body {
|
||||
|
||||
pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool {
|
||||
match self.binding_owners.get(&binding) {
|
||||
Some(x) => {
|
||||
Some(it) => {
|
||||
// We assign expression ids in a way that outer closures will receive
|
||||
// a lower id
|
||||
x.into_raw() < relative_to.into_raw()
|
||||
it.into_raw() < relative_to.into_raw()
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
|
@ -297,11 +297,11 @@ impl ExprCollector<'_> {
|
||||
let (result_expr_id, prev_binding_owner) =
|
||||
this.initialize_binding_owner(syntax_ptr);
|
||||
let inner_expr = this.collect_block(e);
|
||||
let x = this.db.intern_anonymous_const(ConstBlockLoc {
|
||||
let it = this.db.intern_anonymous_const(ConstBlockLoc {
|
||||
parent: this.owner,
|
||||
root: inner_expr,
|
||||
});
|
||||
this.body.exprs[result_expr_id] = Expr::Const(x);
|
||||
this.body.exprs[result_expr_id] = Expr::Const(it);
|
||||
this.current_binding_owner = prev_binding_owner;
|
||||
result_expr_id
|
||||
})
|
||||
@ -324,10 +324,10 @@ impl ExprCollector<'_> {
|
||||
ast::Expr::CallExpr(e) => {
|
||||
let is_rustc_box = {
|
||||
let attrs = e.attrs();
|
||||
attrs.filter_map(|x| x.as_simple_atom()).any(|x| x == "rustc_box")
|
||||
attrs.filter_map(|it| it.as_simple_atom()).any(|it| it == "rustc_box")
|
||||
};
|
||||
if is_rustc_box {
|
||||
let expr = self.collect_expr_opt(e.arg_list().and_then(|x| x.args().next()));
|
||||
let expr = self.collect_expr_opt(e.arg_list().and_then(|it| it.args().next()));
|
||||
self.alloc_expr(Expr::Box { expr }, syntax_ptr)
|
||||
} else {
|
||||
let callee = self.collect_expr_opt(e.expr());
|
||||
@ -781,7 +781,7 @@ impl ExprCollector<'_> {
|
||||
pat: self.alloc_pat_desugared(some_pat),
|
||||
guard: None,
|
||||
expr: self.with_opt_labeled_rib(label, |this| {
|
||||
this.collect_expr_opt(e.loop_body().map(|x| x.into()))
|
||||
this.collect_expr_opt(e.loop_body().map(|it| it.into()))
|
||||
}),
|
||||
};
|
||||
let iter_name = Name::generate_new_name();
|
||||
@ -874,10 +874,10 @@ impl ExprCollector<'_> {
|
||||
}),
|
||||
guard: None,
|
||||
expr: {
|
||||
let x = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr.clone());
|
||||
let it = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr.clone());
|
||||
let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone());
|
||||
let result = self.alloc_expr(
|
||||
Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
|
||||
Expr::Call { callee, args: Box::new([it]), is_assignee_expr: false },
|
||||
syntax_ptr.clone(),
|
||||
);
|
||||
self.alloc_expr(
|
||||
@ -1240,12 +1240,12 @@ impl ExprCollector<'_> {
|
||||
pats.push(self.collect_pat(first, binding_list));
|
||||
binding_list.reject_new = true;
|
||||
for rest in it {
|
||||
for (_, x) in binding_list.is_used.iter_mut() {
|
||||
*x = false;
|
||||
for (_, it) in binding_list.is_used.iter_mut() {
|
||||
*it = false;
|
||||
}
|
||||
pats.push(self.collect_pat(rest, binding_list));
|
||||
for (&id, &x) in binding_list.is_used.iter() {
|
||||
if !x {
|
||||
for (&id, &is_used) in binding_list.is_used.iter() {
|
||||
if !is_used {
|
||||
self.body.bindings[id].problems =
|
||||
Some(BindingProblems::NotBoundAcrossAll);
|
||||
}
|
||||
@ -1352,9 +1352,9 @@ impl ExprCollector<'_> {
|
||||
// FIXME: implement in a way that also builds source map and calculates assoc resolutions in type inference.
|
||||
ast::Pat::RangePat(p) => {
|
||||
let mut range_part_lower = |p: Option<ast::Pat>| {
|
||||
p.and_then(|x| match &x {
|
||||
ast::Pat::LiteralPat(x) => {
|
||||
Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(x)?.0)))
|
||||
p.and_then(|it| match &it {
|
||||
ast::Pat::LiteralPat(it) => {
|
||||
Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(it)?.0)))
|
||||
}
|
||||
ast::Pat::IdentPat(p) => {
|
||||
let name =
|
||||
@ -1451,9 +1451,7 @@ impl ExprCollector<'_> {
|
||||
&self,
|
||||
lifetime: Option<ast::Lifetime>,
|
||||
) -> Result<Option<LabelId>, BodyDiagnostic> {
|
||||
let Some(lifetime) = lifetime else {
|
||||
return Ok(None)
|
||||
};
|
||||
let Some(lifetime) = lifetime else { return Ok(None) };
|
||||
let name = Name::new_lifetime(&lifetime);
|
||||
|
||||
for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
|
||||
|
@ -105,7 +105,7 @@ struct Printer<'a> {
|
||||
needs_indent: bool,
|
||||
}
|
||||
|
||||
impl<'a> Write for Printer<'a> {
|
||||
impl Write for Printer<'_> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for line in s.split_inclusive('\n') {
|
||||
if self.needs_indent {
|
||||
@ -125,7 +125,7 @@ impl<'a> Write for Printer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Printer<'a> {
|
||||
impl Printer<'_> {
|
||||
fn indented(&mut self, f: impl FnOnce(&mut Self)) {
|
||||
self.indent_level += 1;
|
||||
wln!(self);
|
||||
|
@ -3,12 +3,12 @@ mod block;
|
||||
use base_db::{fixture::WithFixture, SourceDatabase};
|
||||
use expect_test::Expect;
|
||||
|
||||
use crate::ModuleDefId;
|
||||
use crate::{test_db::TestDB, ModuleDefId};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn lower(ra_fixture: &str) -> Arc<Body> {
|
||||
let db = crate::test_db::TestDB::with_files(ra_fixture);
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
|
||||
let krate = db.crate_graph().iter().next().unwrap();
|
||||
let def_map = db.crate_def_map(krate);
|
||||
@ -25,15 +25,15 @@ fn lower(ra_fixture: &str) -> Arc<Body> {
|
||||
db.body(fn_def.unwrap().into())
|
||||
}
|
||||
|
||||
fn block_def_map_at(ra_fixture: &str) -> String {
|
||||
let (db, position) = crate::test_db::TestDB::with_position(ra_fixture);
|
||||
fn def_map_at(ra_fixture: &str) -> String {
|
||||
let (db, position) = TestDB::with_position(ra_fixture);
|
||||
|
||||
let module = db.module_at_position(position);
|
||||
module.def_map(&db).dump(&db)
|
||||
}
|
||||
|
||||
fn check_block_scopes_at(ra_fixture: &str, expect: Expect) {
|
||||
let (db, position) = crate::test_db::TestDB::with_position(ra_fixture);
|
||||
let (db, position) = TestDB::with_position(ra_fixture);
|
||||
|
||||
let module = db.module_at_position(position);
|
||||
let actual = module.def_map(&db).dump_block_scopes(&db);
|
||||
@ -41,7 +41,7 @@ fn check_block_scopes_at(ra_fixture: &str, expect: Expect) {
|
||||
}
|
||||
|
||||
fn check_at(ra_fixture: &str, expect: Expect) {
|
||||
let actual = block_def_map_at(ra_fixture);
|
||||
let actual = def_map_at(ra_fixture);
|
||||
expect.assert_eq(&actual);
|
||||
}
|
||||
|
||||
|
@ -133,6 +133,47 @@ struct Struct {}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn super_imports_2() {
|
||||
check_at(
|
||||
r#"
|
||||
fn outer() {
|
||||
mod m {
|
||||
struct ResolveMe {}
|
||||
fn middle() {
|
||||
mod m2 {
|
||||
fn inner() {
|
||||
use super::ResolveMe;
|
||||
$0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
block scope
|
||||
ResolveMe: t
|
||||
|
||||
block scope
|
||||
m2: t
|
||||
|
||||
block scope::m2
|
||||
inner: v
|
||||
|
||||
block scope
|
||||
m: t
|
||||
|
||||
block scope::m
|
||||
ResolveMe: t
|
||||
middle: v
|
||||
|
||||
crate
|
||||
outer: v
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_module_scoping() {
|
||||
check_block_scopes_at(
|
||||
@ -155,6 +196,42 @@ fn f() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn self_imports() {
|
||||
check_at(
|
||||
r#"
|
||||
fn f() {
|
||||
mod m {
|
||||
struct ResolveMe {}
|
||||
fn g() {
|
||||
fn h() {
|
||||
use self::ResolveMe;
|
||||
$0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
block scope
|
||||
ResolveMe: t
|
||||
|
||||
block scope
|
||||
h: v
|
||||
|
||||
block scope
|
||||
m: t
|
||||
|
||||
block scope::m
|
||||
ResolveMe: t
|
||||
g: v
|
||||
|
||||
crate
|
||||
f: v
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_macro_items() {
|
||||
// Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded
|
||||
|
@ -24,11 +24,12 @@ use crate::{
|
||||
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind},
|
||||
DefMap, MacroSubNs,
|
||||
},
|
||||
path::ImportAlias,
|
||||
type_ref::{TraitRef, TypeBound, TypeRef},
|
||||
visibility::RawVisibility,
|
||||
AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
|
||||
Intern, ItemContainerId, ItemLoc, Lookup, Macro2Id, MacroRulesId, ModuleId, ProcMacroId,
|
||||
StaticId, TraitAliasId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||
AssocItemId, AstIdWithPath, ConstId, ConstLoc, ExternCrateId, FunctionId, FunctionLoc,
|
||||
HasModule, ImplId, Intern, ItemContainerId, ItemLoc, Lookup, Macro2Id, MacroRulesId, ModuleId,
|
||||
ProcMacroId, StaticId, TraitAliasId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -424,6 +425,7 @@ impl MacroRulesData {
|
||||
Arc::new(MacroRulesData { name: makro.name.clone(), macro_export })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ProcMacroData {
|
||||
pub name: Name,
|
||||
@ -460,6 +462,30 @@ impl ProcMacroData {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ExternCrateDeclData {
|
||||
pub name: Name,
|
||||
pub alias: Option<ImportAlias>,
|
||||
pub visibility: RawVisibility,
|
||||
}
|
||||
|
||||
impl ExternCrateDeclData {
|
||||
pub(crate) fn extern_crate_decl_data_query(
|
||||
db: &dyn DefDatabase,
|
||||
extern_crate: ExternCrateId,
|
||||
) -> Arc<ExternCrateDeclData> {
|
||||
let loc = extern_crate.lookup(db);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let extern_crate = &item_tree[loc.id.value];
|
||||
|
||||
Arc::new(Self {
|
||||
name: extern_crate.name.clone(),
|
||||
visibility: item_tree[extern_crate.visibility].clone(),
|
||||
alias: extern_crate.alias.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ConstData {
|
||||
/// `None` for `const _: () = ();`
|
||||
@ -573,7 +599,7 @@ impl<'a> AssocItemCollector<'a> {
|
||||
if !attrs.is_cfg_enabled(self.expander.cfg_options()) {
|
||||
self.diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
self.module_id.local_id,
|
||||
InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()),
|
||||
InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).erase()),
|
||||
attrs.cfg().unwrap(),
|
||||
self.expander.cfg_options().clone(),
|
||||
));
|
||||
|
@ -18,7 +18,6 @@ use triomphe::Arc;
|
||||
use crate::{
|
||||
builtin_type::{BuiltinInt, BuiltinUint},
|
||||
db::DefDatabase,
|
||||
expander::CfgExpander,
|
||||
item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
|
||||
lang_item::LangItem,
|
||||
lower::LowerCtx,
|
||||
@ -29,8 +28,8 @@ use crate::{
|
||||
tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree},
|
||||
type_ref::TypeRef,
|
||||
visibility::RawVisibility,
|
||||
EnumId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StructId, UnionId,
|
||||
VariantId,
|
||||
EnumId, EnumLoc, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StructId,
|
||||
UnionId, VariantId,
|
||||
};
|
||||
|
||||
/// Note that we use `StructData` for unions as well!
|
||||
@ -76,6 +75,7 @@ pub struct EnumData {
|
||||
pub struct EnumVariantData {
|
||||
pub name: Name,
|
||||
pub variant_data: Arc<VariantData>,
|
||||
pub tree_id: la_arena::Idx<crate::item_tree::Variant>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -147,6 +147,7 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
|
||||
}
|
||||
"C" => ReprFlags::IS_C,
|
||||
"transparent" => ReprFlags::IS_TRANSPARENT,
|
||||
"simd" => ReprFlags::IS_SIMD,
|
||||
repr => {
|
||||
if let Some(builtin) = BuiltinInt::from_suffix(repr)
|
||||
.map(Either::Left)
|
||||
@ -325,11 +326,12 @@ impl EnumData {
|
||||
variants.alloc(EnumVariantData {
|
||||
name: var.name.clone(),
|
||||
variant_data: Arc::new(var_data),
|
||||
tree_id,
|
||||
});
|
||||
} else {
|
||||
diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
loc.container.local_id,
|
||||
InFile::new(loc.id.file_id(), var.ast_id.upcast()),
|
||||
InFile::new(loc.id.file_id(), var.ast_id.erase()),
|
||||
attrs.cfg().unwrap(),
|
||||
cfg_options.clone(),
|
||||
))
|
||||
@ -367,9 +369,10 @@ impl HasChildSource<LocalEnumVariantId> for EnumId {
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> {
|
||||
let src = self.lookup(db).source(db);
|
||||
let loc = &self.lookup(db);
|
||||
let src = loc.source(db);
|
||||
let mut trace = Trace::new_for_map();
|
||||
lower_enum(db, &mut trace, &src, self.lookup(db).container);
|
||||
lower_enum(db, &mut trace, &src, loc);
|
||||
src.with_value(trace.into_map())
|
||||
}
|
||||
}
|
||||
@ -378,31 +381,58 @@ fn lower_enum(
|
||||
db: &dyn DefDatabase,
|
||||
trace: &mut Trace<EnumVariantData, ast::Variant>,
|
||||
ast: &InFile<ast::Enum>,
|
||||
module_id: ModuleId,
|
||||
loc: &EnumLoc,
|
||||
) {
|
||||
let expander = CfgExpander::new(db, ast.file_id, module_id.krate);
|
||||
let item_tree = loc.id.item_tree(db);
|
||||
let krate = loc.container.krate;
|
||||
|
||||
let item_tree_variants = item_tree[loc.id.value].variants.clone();
|
||||
|
||||
let cfg_options = &db.crate_graph()[krate].cfg_options;
|
||||
let variants = ast
|
||||
.value
|
||||
.variant_list()
|
||||
.into_iter()
|
||||
.flat_map(|it| it.variants())
|
||||
.filter(|var| expander.is_cfg_enabled(db, var));
|
||||
for var in variants {
|
||||
.zip(item_tree_variants)
|
||||
.filter(|&(_, item_tree_id)| {
|
||||
item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options)
|
||||
});
|
||||
for (var, item_tree_id) in variants {
|
||||
trace.alloc(
|
||||
|| var.clone(),
|
||||
|| EnumVariantData {
|
||||
name: var.name().map_or_else(Name::missing, |it| it.as_name()),
|
||||
variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)),
|
||||
variant_data: Arc::new(VariantData::new(
|
||||
db,
|
||||
ast.with_value(var.kind()),
|
||||
loc.container,
|
||||
&item_tree,
|
||||
item_tree_id,
|
||||
)),
|
||||
tree_id: item_tree_id,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl VariantData {
|
||||
fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self {
|
||||
let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate);
|
||||
fn new(
|
||||
db: &dyn DefDatabase,
|
||||
flavor: InFile<ast::StructKind>,
|
||||
module_id: ModuleId,
|
||||
item_tree: &ItemTree,
|
||||
variant: la_arena::Idx<crate::item_tree::Variant>,
|
||||
) -> Self {
|
||||
let mut trace = Trace::new_for_arena();
|
||||
match lower_struct(db, &mut expander, &mut trace, &flavor) {
|
||||
match lower_struct(
|
||||
db,
|
||||
&mut trace,
|
||||
&flavor,
|
||||
module_id.krate,
|
||||
item_tree,
|
||||
&item_tree[variant].fields,
|
||||
) {
|
||||
StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
|
||||
StructKind::Record => VariantData::Record(trace.into_arena()),
|
||||
StructKind::Unit => VariantData::Unit,
|
||||
@ -434,28 +464,43 @@ impl HasChildSource<LocalFieldId> for VariantId {
|
||||
type Value = Either<ast::TupleField, ast::RecordField>;
|
||||
|
||||
fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
|
||||
let (src, module_id) = match self {
|
||||
let item_tree;
|
||||
let (src, fields, container) = match *self {
|
||||
VariantId::EnumVariantId(it) => {
|
||||
// I don't really like the fact that we call into parent source
|
||||
// here, this might add to more queries then necessary.
|
||||
let lookup = it.parent.lookup(db);
|
||||
item_tree = lookup.id.item_tree(db);
|
||||
let src = it.parent.child_source(db);
|
||||
(src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container)
|
||||
let tree_id = db.enum_data(it.parent).variants[it.local_id].tree_id;
|
||||
let fields = &item_tree[tree_id].fields;
|
||||
(src.map(|map| map[it.local_id].kind()), fields, lookup.container)
|
||||
}
|
||||
VariantId::StructId(it) => {
|
||||
(it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container)
|
||||
let lookup = it.lookup(db);
|
||||
item_tree = lookup.id.item_tree(db);
|
||||
(
|
||||
lookup.source(db).map(|it| it.kind()),
|
||||
&item_tree[lookup.id.value].fields,
|
||||
lookup.container,
|
||||
)
|
||||
}
|
||||
VariantId::UnionId(it) => {
|
||||
let lookup = it.lookup(db);
|
||||
item_tree = lookup.id.item_tree(db);
|
||||
(
|
||||
lookup.source(db).map(|it| {
|
||||
it.record_field_list()
|
||||
.map(ast::StructKind::Record)
|
||||
.unwrap_or(ast::StructKind::Unit)
|
||||
}),
|
||||
&item_tree[lookup.id.value].fields,
|
||||
lookup.container,
|
||||
)
|
||||
}
|
||||
VariantId::UnionId(it) => (
|
||||
it.lookup(db).source(db).map(|it| {
|
||||
it.record_field_list()
|
||||
.map(ast::StructKind::Record)
|
||||
.unwrap_or(ast::StructKind::Unit)
|
||||
}),
|
||||
it.lookup(db).container,
|
||||
),
|
||||
};
|
||||
let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
|
||||
let mut trace = Trace::new_for_map();
|
||||
lower_struct(db, &mut expander, &mut trace, &src);
|
||||
lower_struct(db, &mut trace, &src, container.krate, &item_tree, fields);
|
||||
src.with_value(trace.into_map())
|
||||
}
|
||||
}
|
||||
@ -469,16 +514,19 @@ pub enum StructKind {
|
||||
|
||||
fn lower_struct(
|
||||
db: &dyn DefDatabase,
|
||||
expander: &mut CfgExpander,
|
||||
trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
|
||||
ast: &InFile<ast::StructKind>,
|
||||
krate: CrateId,
|
||||
item_tree: &ItemTree,
|
||||
fields: &Fields,
|
||||
) -> StructKind {
|
||||
let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id);
|
||||
let ctx = LowerCtx::with_file_id(db, ast.file_id);
|
||||
|
||||
match &ast.value {
|
||||
ast::StructKind::Tuple(fl) => {
|
||||
for (i, fd) in fl.fields().enumerate() {
|
||||
if !expander.is_cfg_enabled(db, &fd) {
|
||||
match (&ast.value, fields) {
|
||||
(ast::StructKind::Tuple(fl), Fields::Tuple(fields)) => {
|
||||
let cfg_options = &db.crate_graph()[krate].cfg_options;
|
||||
for ((i, fd), item_tree_id) in fl.fields().enumerate().zip(fields.clone()) {
|
||||
if !item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -493,9 +541,10 @@ fn lower_struct(
|
||||
}
|
||||
StructKind::Tuple
|
||||
}
|
||||
ast::StructKind::Record(fl) => {
|
||||
for fd in fl.fields() {
|
||||
if !expander.is_cfg_enabled(db, &fd) {
|
||||
(ast::StructKind::Record(fl), Fields::Record(fields)) => {
|
||||
let cfg_options = &db.crate_graph()[krate].cfg_options;
|
||||
for (fd, item_tree_id) in fl.fields().zip(fields.clone()) {
|
||||
if !item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -510,7 +559,7 @@ fn lower_struct(
|
||||
}
|
||||
StructKind::Record
|
||||
}
|
||||
ast::StructKind::Unit => StructKind::Unit,
|
||||
_ => StructKind::Unit,
|
||||
}
|
||||
}
|
||||
|
||||
@ -539,8 +588,8 @@ fn lower_fields(
|
||||
InFile::new(
|
||||
current_file_id,
|
||||
match field.ast_id {
|
||||
FieldAstId::Record(it) => it.upcast(),
|
||||
FieldAstId::Tuple(it) => it.upcast(),
|
||||
FieldAstId::Record(it) => it.erase(),
|
||||
FieldAstId::Tuple(it) => it.erase(),
|
||||
},
|
||||
),
|
||||
attrs.cfg().unwrap(),
|
||||
@ -563,8 +612,8 @@ fn lower_fields(
|
||||
InFile::new(
|
||||
current_file_id,
|
||||
match field.ast_id {
|
||||
FieldAstId::Record(it) => it.upcast(),
|
||||
FieldAstId::Tuple(it) => it.upcast(),
|
||||
FieldAstId::Record(it) => it.erase(),
|
||||
FieldAstId::Tuple(it) => it.erase(),
|
||||
},
|
||||
),
|
||||
attrs.cfg().unwrap(),
|
||||
|
@ -12,27 +12,31 @@ use crate::{
|
||||
body::{scope::ExprScopes, Body, BodySourceMap},
|
||||
data::{
|
||||
adt::{EnumData, StructData},
|
||||
ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData,
|
||||
TraitAliasData, TraitData, TypeAliasData,
|
||||
ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData,
|
||||
ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData,
|
||||
},
|
||||
generics::GenericParams,
|
||||
import_map::ImportMap,
|
||||
item_tree::{AttrOwner, ItemTree},
|
||||
lang_item::{LangItem, LangItemTarget, LangItems},
|
||||
lang_item::{self, LangItem, LangItemTarget, LangItems},
|
||||
nameres::{diagnostics::DefDiagnostic, DefMap},
|
||||
visibility::{self, Visibility},
|
||||
AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId,
|
||||
EnumId, EnumLoc, ExternBlockId, ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId,
|
||||
ImplLoc, InTypeConstId, InTypeConstLoc, LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc,
|
||||
MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId,
|
||||
StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId,
|
||||
UnionLoc, VariantId,
|
||||
EnumId, EnumLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId,
|
||||
FunctionLoc, GenericDefId, ImplId, ImplLoc, ImportId, ImportLoc, InTypeConstId, InTypeConstLoc,
|
||||
LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc,
|
||||
ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId,
|
||||
TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
|
||||
};
|
||||
|
||||
#[salsa::query_group(InternDatabaseStorage)]
|
||||
pub trait InternDatabase: SourceDatabase {
|
||||
// region: items
|
||||
#[salsa::interned]
|
||||
fn intern_import(&self, loc: ImportLoc) -> ImportId;
|
||||
#[salsa::interned]
|
||||
fn intern_extern_crate(&self, loc: ExternCrateLoc) -> ExternCrateId;
|
||||
#[salsa::interned]
|
||||
fn intern_function(&self, loc: FunctionLoc) -> FunctionId;
|
||||
#[salsa::interned]
|
||||
fn intern_struct(&self, loc: StructLoc) -> StructId;
|
||||
@ -160,6 +164,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
||||
#[salsa::invoke(ProcMacroData::proc_macro_data_query)]
|
||||
fn proc_macro_data(&self, makro: ProcMacroId) -> Arc<ProcMacroData>;
|
||||
|
||||
#[salsa::invoke(ExternCrateDeclData::extern_crate_decl_data_query)]
|
||||
fn extern_crate_decl_data(&self, extern_crate: ExternCrateId) -> Arc<ExternCrateDeclData>;
|
||||
|
||||
// endregion:data
|
||||
|
||||
#[salsa::invoke(Body::body_with_source_map_query)]
|
||||
@ -197,6 +204,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
|
||||
#[salsa::invoke(AttrsWithOwner::attrs_query)]
|
||||
fn attrs(&self, def: AttrDefId) -> Attrs;
|
||||
|
||||
#[salsa::invoke(lang_item::lang_attr_query)]
|
||||
fn lang_attr(&self, def: AttrDefId) -> Option<LangItem>;
|
||||
|
||||
#[salsa::transparent]
|
||||
#[salsa::invoke(AttrsWithOwner::attrs_with_owner)]
|
||||
fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner;
|
||||
|
@ -8,8 +8,8 @@ use syntax::{ast, AstNode, AstPtr};
|
||||
|
||||
use crate::{
|
||||
dyn_map::{DynMap, Policy},
|
||||
ConstId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, LifetimeParamId, Macro2Id,
|
||||
MacroRulesId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
|
||||
ConstId, EnumId, EnumVariantId, ExternCrateId, FieldId, FunctionId, ImplId, LifetimeParamId,
|
||||
Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
|
||||
TypeOrConstParamId, UnionId,
|
||||
};
|
||||
|
||||
@ -25,6 +25,7 @@ pub const TRAIT_ALIAS: Key<ast::TraitAlias, TraitAliasId> = Key::new();
|
||||
pub const STRUCT: Key<ast::Struct, StructId> = Key::new();
|
||||
pub const UNION: Key<ast::Union, UnionId> = Key::new();
|
||||
pub const ENUM: Key<ast::Enum, EnumId> = Key::new();
|
||||
pub const EXTERN_CRATE: Key<ast::ExternCrate, ExternCrateId> = Key::new();
|
||||
|
||||
pub const VARIANT: Key<ast::Variant, EnumVariantId> = Key::new();
|
||||
pub const TUPLE_FIELD: Key<ast::TupleField, FieldId> = Key::new();
|
||||
|
@ -15,18 +15,11 @@ use crate::{
|
||||
MacroId, ModuleId,
|
||||
};
|
||||
|
||||
/// A subset of Expander that only deals with cfg attributes. We only need it to
|
||||
/// avoid cyclic queries in crate def map during enum processing.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CfgExpander {
|
||||
pub struct Expander {
|
||||
cfg_options: CfgOptions,
|
||||
hygiene: Hygiene,
|
||||
krate: CrateId,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Expander {
|
||||
cfg_expander: CfgExpander,
|
||||
pub(crate) current_file_id: HirFileId,
|
||||
pub(crate) module: ModuleId,
|
||||
/// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
|
||||
@ -34,41 +27,23 @@ pub struct Expander {
|
||||
recursion_limit: Limit,
|
||||
}
|
||||
|
||||
impl CfgExpander {
|
||||
pub(crate) fn new(
|
||||
db: &dyn DefDatabase,
|
||||
current_file_id: HirFileId,
|
||||
krate: CrateId,
|
||||
) -> CfgExpander {
|
||||
let hygiene = Hygiene::new(db.upcast(), current_file_id);
|
||||
let cfg_options = db.crate_graph()[krate].cfg_options.clone();
|
||||
CfgExpander { cfg_options, hygiene, krate }
|
||||
}
|
||||
|
||||
pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
|
||||
Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene))
|
||||
}
|
||||
|
||||
pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool {
|
||||
let attrs = self.parse_attrs(db, owner);
|
||||
attrs.is_cfg_enabled(&self.cfg_options)
|
||||
}
|
||||
|
||||
pub(crate) fn hygiene(&self) -> &Hygiene {
|
||||
&self.hygiene
|
||||
}
|
||||
}
|
||||
|
||||
impl Expander {
|
||||
pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
|
||||
let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
|
||||
let recursion_limit = db.recursion_limit(module.krate);
|
||||
#[cfg(not(test))]
|
||||
let recursion_limit = Limit::new(recursion_limit as usize);
|
||||
// Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
|
||||
#[cfg(test)]
|
||||
let recursion_limit = Limit::new(std::cmp::min(32, recursion_limit as usize));
|
||||
Expander { cfg_expander, current_file_id, module, recursion_depth: 0, recursion_limit }
|
||||
Expander {
|
||||
current_file_id,
|
||||
module,
|
||||
recursion_depth: 0,
|
||||
recursion_limit,
|
||||
cfg_options: db.crate_graph()[module.krate].cfg_options.clone(),
|
||||
hygiene: Hygiene::new(db.upcast(), current_file_id),
|
||||
krate: module.krate,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enter_expand<T: ast::AstNode>(
|
||||
@ -120,7 +95,7 @@ impl Expander {
|
||||
}
|
||||
|
||||
pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
|
||||
self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
|
||||
self.hygiene = Hygiene::new(db.upcast(), mark.file_id);
|
||||
self.current_file_id = mark.file_id;
|
||||
if self.recursion_depth == u32::MAX {
|
||||
// Recursion limit has been reached somewhere in the macro expansion tree. Reset the
|
||||
@ -135,7 +110,7 @@ impl Expander {
|
||||
}
|
||||
|
||||
pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> {
|
||||
LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id)
|
||||
LowerCtx::new(db, &self.hygiene, self.current_file_id)
|
||||
}
|
||||
|
||||
pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
|
||||
@ -143,11 +118,11 @@ impl Expander {
|
||||
}
|
||||
|
||||
pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
|
||||
self.cfg_expander.parse_attrs(db, owner)
|
||||
Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene))
|
||||
}
|
||||
|
||||
pub(crate) fn cfg_options(&self) -> &CfgOptions {
|
||||
&self.cfg_expander.cfg_options
|
||||
&self.cfg_options
|
||||
}
|
||||
|
||||
pub fn current_file_id(&self) -> HirFileId {
|
||||
@ -155,7 +130,7 @@ impl Expander {
|
||||
}
|
||||
|
||||
pub(crate) fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
|
||||
let ctx = LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id);
|
||||
let ctx = LowerCtx::new(db, &self.hygiene, self.current_file_id);
|
||||
Path::from_src(path, &ctx)
|
||||
}
|
||||
|
||||
@ -194,7 +169,7 @@ impl Expander {
|
||||
let parse = value.cast::<T>()?;
|
||||
|
||||
self.recursion_depth += 1;
|
||||
self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
|
||||
self.hygiene = Hygiene::new(db.upcast(), file_id);
|
||||
let old_file_id = std::mem::replace(&mut self.current_file_id, file_id);
|
||||
let mark =
|
||||
Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") };
|
||||
|
@ -360,7 +360,7 @@ fn calculate_best_path(
|
||||
prefer_no_std,
|
||||
)?;
|
||||
cov_mark::hit!(partially_imported);
|
||||
path.push_segment(info.path.segments.last()?.clone());
|
||||
path.push_segment(info.name.clone());
|
||||
Some(path)
|
||||
})
|
||||
});
|
||||
|
@ -67,21 +67,21 @@ pub enum TypeOrConstParamData {
|
||||
impl TypeOrConstParamData {
|
||||
pub fn name(&self) -> Option<&Name> {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(x) => x.name.as_ref(),
|
||||
TypeOrConstParamData::ConstParamData(x) => Some(&x.name),
|
||||
TypeOrConstParamData::TypeParamData(it) => it.name.as_ref(),
|
||||
TypeOrConstParamData::ConstParamData(it) => Some(&it.name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_default(&self) -> bool {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(x) => x.default.is_some(),
|
||||
TypeOrConstParamData::ConstParamData(x) => x.has_default,
|
||||
TypeOrConstParamData::TypeParamData(it) => it.default.is_some(),
|
||||
TypeOrConstParamData::ConstParamData(it) => it.has_default,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_param(&self) -> Option<&TypeParamData> {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(x) => Some(x),
|
||||
TypeOrConstParamData::TypeParamData(it) => Some(it),
|
||||
TypeOrConstParamData::ConstParamData(_) => None,
|
||||
}
|
||||
}
|
||||
@ -89,14 +89,14 @@ impl TypeOrConstParamData {
|
||||
pub fn const_param(&self) -> Option<&ConstParamData> {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(_) => None,
|
||||
TypeOrConstParamData::ConstParamData(x) => Some(x),
|
||||
TypeOrConstParamData::ConstParamData(it) => Some(it),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_trait_self(&self) -> bool {
|
||||
match self {
|
||||
TypeOrConstParamData::TypeParamData(x) => {
|
||||
x.provenance == TypeParamProvenance::TraitSelf
|
||||
TypeOrConstParamData::TypeParamData(it) => {
|
||||
it.provenance == TypeParamProvenance::TraitSelf
|
||||
}
|
||||
TypeOrConstParamData::ConstParamData(_) => false,
|
||||
}
|
||||
|
@ -425,8 +425,8 @@ impl ConstRef {
|
||||
}
|
||||
match expr {
|
||||
ast::Expr::PathExpr(p) if is_path_ident(&p) => {
|
||||
match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) {
|
||||
Some(x) => Self::Path(x.as_name()),
|
||||
match p.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) {
|
||||
Some(it) => Self::Path(it.as_name()),
|
||||
None => Self::Scalar(LiteralConstRef::Unknown),
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
//! A map of all publicly exported items in a crate.
|
||||
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::{fmt, hash::BuildHasherDefault};
|
||||
|
||||
use base_db::CrateId;
|
||||
use fst::{self, Streamer};
|
||||
use hir_expand::name::Name;
|
||||
use indexmap::{map::Entry, IndexMap};
|
||||
use indexmap::IndexMap;
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::{FxHashSet, FxHasher};
|
||||
use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
@ -17,52 +18,23 @@ use crate::{
|
||||
|
||||
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||
|
||||
// FIXME: Support aliases: an item may be exported under multiple names, so `ImportInfo` should
|
||||
// have `Vec<(Name, ModuleId)>` instead of `(Name, ModuleId)`.
|
||||
/// Item import details stored in the `ImportMap`.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ImportInfo {
|
||||
/// A path that can be used to import the item, relative to the crate's root.
|
||||
pub path: ImportPath,
|
||||
/// A name that can be used to import the item, relative to the crate's root.
|
||||
pub name: Name,
|
||||
/// The module containing this item.
|
||||
pub container: ModuleId,
|
||||
/// Whether the import is a trait associated item or not.
|
||||
pub is_trait_assoc_item: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ImportPath {
|
||||
pub segments: Vec<Name>,
|
||||
}
|
||||
|
||||
impl ImportPath {
|
||||
pub fn display<'a>(&'a self, db: &'a dyn DefDatabase) -> impl fmt::Display + 'a {
|
||||
struct Display<'a> {
|
||||
db: &'a dyn DefDatabase,
|
||||
path: &'a ImportPath,
|
||||
}
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(
|
||||
&self.path.segments.iter().map(|it| it.display(self.db.upcast())).format("::"),
|
||||
f,
|
||||
)
|
||||
}
|
||||
}
|
||||
Display { db, path: self }
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.segments.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// A map from publicly exported items to the path needed to import/name them from a downstream
|
||||
/// crate.
|
||||
/// A map from publicly exported items to its name.
|
||||
///
|
||||
/// Reexports of items are taken into account, ie. if something is exported under multiple
|
||||
/// names, the one with the shortest import path will be used.
|
||||
///
|
||||
/// Note that all paths are relative to the containing crate's root, so the crate name still needs
|
||||
/// to be prepended to the `ModPath` before the path is valid.
|
||||
#[derive(Default)]
|
||||
pub struct ImportMap {
|
||||
map: FxIndexMap<ItemInNs, ImportInfo>,
|
||||
@ -70,122 +42,58 @@ pub struct ImportMap {
|
||||
/// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
|
||||
/// values returned by running `fst`.
|
||||
///
|
||||
/// Since a path can refer to multiple items due to namespacing, we store all items with the
|
||||
/// same path right after each other. This allows us to find all items after the FST gives us
|
||||
/// Since a name can refer to multiple items due to namespacing, we store all items with the
|
||||
/// same name right after each other. This allows us to find all items after the FST gives us
|
||||
/// the index of the first one.
|
||||
importables: Vec<ItemInNs>,
|
||||
fst: fst::Map<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl ImportMap {
|
||||
pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
|
||||
pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
|
||||
let _p = profile::span("import_map_query");
|
||||
|
||||
let mut import_map = collect_import_map(db, krate);
|
||||
let map = collect_import_map(db, krate);
|
||||
|
||||
let mut importables = import_map
|
||||
.map
|
||||
let mut importables: Vec<_> = map
|
||||
.iter()
|
||||
.map(|(item, info)| (item, fst_path(db, &info.path)))
|
||||
.collect::<Vec<_>>();
|
||||
importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2));
|
||||
// We've only collected items, whose name cannot be tuple field.
|
||||
.map(|(&item, info)| (item, info.name.as_str().unwrap().to_ascii_lowercase()))
|
||||
.collect();
|
||||
importables.sort_by(|(_, lhs_name), (_, rhs_name)| lhs_name.cmp(rhs_name));
|
||||
|
||||
// Build the FST, taking care not to insert duplicate values.
|
||||
|
||||
let mut builder = fst::MapBuilder::memory();
|
||||
let mut last_batch_start = 0;
|
||||
|
||||
for idx in 0..importables.len() {
|
||||
let key = &importables[last_batch_start].1;
|
||||
if let Some((_, fst_path)) = importables.get(idx + 1) {
|
||||
if key == fst_path {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let _ = builder.insert(key, last_batch_start as u64);
|
||||
|
||||
last_batch_start = idx + 1;
|
||||
let iter = importables.iter().enumerate().dedup_by(|lhs, rhs| lhs.1 .1 == rhs.1 .1);
|
||||
for (start_idx, (_, name)) in iter {
|
||||
let _ = builder.insert(name, start_idx as u64);
|
||||
}
|
||||
|
||||
import_map.fst = builder.into_map();
|
||||
import_map.importables = importables.iter().map(|&(&item, _)| item).collect();
|
||||
|
||||
Arc::new(import_map)
|
||||
}
|
||||
|
||||
/// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
|
||||
pub fn path_of(&self, item: ItemInNs) -> Option<&ImportPath> {
|
||||
self.import_info_for(item).map(|it| &it.path)
|
||||
Arc::new(ImportMap {
|
||||
map,
|
||||
fst: builder.into_map(),
|
||||
importables: importables.into_iter().map(|(item, _)| item).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
|
||||
self.map.get(&item)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn fmt_for_test(&self, db: &dyn DefDatabase) -> String {
|
||||
let mut importable_paths: Vec<_> = self
|
||||
.map
|
||||
.iter()
|
||||
.map(|(item, info)| {
|
||||
let ns = match item {
|
||||
ItemInNs::Types(_) => "t",
|
||||
ItemInNs::Values(_) => "v",
|
||||
ItemInNs::Macros(_) => "m",
|
||||
};
|
||||
format!("- {} ({ns})", info.path.display(db))
|
||||
})
|
||||
.collect();
|
||||
|
||||
importable_paths.sort();
|
||||
importable_paths.join("\n")
|
||||
}
|
||||
|
||||
fn collect_trait_assoc_items(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
tr: TraitId,
|
||||
is_type_in_ns: bool,
|
||||
original_import_info: &ImportInfo,
|
||||
) {
|
||||
let _p = profile::span("collect_trait_assoc_items");
|
||||
for (assoc_item_name, item) in &db.trait_data(tr).items {
|
||||
let module_def_id = match item {
|
||||
AssocItemId::FunctionId(f) => ModuleDefId::from(*f),
|
||||
AssocItemId::ConstId(c) => ModuleDefId::from(*c),
|
||||
// cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias`
|
||||
// qualifier, ergo no need to store it for imports in import_map
|
||||
AssocItemId::TypeAliasId(_) => {
|
||||
cov_mark::hit!(type_aliases_ignored);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let assoc_item = if is_type_in_ns {
|
||||
ItemInNs::Types(module_def_id)
|
||||
} else {
|
||||
ItemInNs::Values(module_def_id)
|
||||
};
|
||||
|
||||
let mut assoc_item_info = original_import_info.clone();
|
||||
assoc_item_info.path.segments.push(assoc_item_name.to_owned());
|
||||
assoc_item_info.is_trait_assoc_item = true;
|
||||
self.map.insert(assoc_item, assoc_item_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap {
|
||||
fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemInNs, ImportInfo> {
|
||||
let _p = profile::span("collect_import_map");
|
||||
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let mut import_map = ImportMap::default();
|
||||
let mut map = FxIndexMap::default();
|
||||
|
||||
// We look only into modules that are public(ly reexported), starting with the crate root.
|
||||
let empty = ImportPath { segments: vec![] };
|
||||
let root = def_map.module_id(DefMap::ROOT);
|
||||
let mut worklist = vec![(root, empty)];
|
||||
while let Some((module, mod_path)) = worklist.pop() {
|
||||
let mut worklist = vec![(root, 0)];
|
||||
// Records items' minimum module depth.
|
||||
let mut depth_map = FxHashMap::default();
|
||||
|
||||
while let Some((module, depth)) = worklist.pop() {
|
||||
let ext_def_map;
|
||||
let mod_data = if module.krate == krate {
|
||||
&def_map[module.local_id]
|
||||
@ -197,56 +105,91 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap {
|
||||
|
||||
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 {
|
||||
let mk_path = || {
|
||||
let mut path = mod_path.clone();
|
||||
path.segments.push(name.clone());
|
||||
path
|
||||
};
|
||||
|
||||
for item in per_ns.iter_items() {
|
||||
let path = mk_path();
|
||||
let path_len = path.len();
|
||||
let import_info =
|
||||
ImportInfo { path, container: module, is_trait_assoc_item: false };
|
||||
let import_info = ImportInfo {
|
||||
name: name.clone(),
|
||||
container: module,
|
||||
is_trait_assoc_item: false,
|
||||
};
|
||||
|
||||
if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
|
||||
import_map.collect_trait_assoc_items(
|
||||
db,
|
||||
tr,
|
||||
matches!(item, ItemInNs::Types(_)),
|
||||
&import_info,
|
||||
);
|
||||
}
|
||||
|
||||
match import_map.map.entry(item) {
|
||||
match depth_map.entry(item) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(import_info);
|
||||
entry.insert(depth);
|
||||
}
|
||||
Entry::Occupied(mut entry) => {
|
||||
// If the new path is shorter, prefer that one.
|
||||
if path_len < entry.get().path.len() {
|
||||
*entry.get_mut() = import_info;
|
||||
if depth < *entry.get() {
|
||||
entry.insert(depth);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we've just added a path to a module, descend into it. We might traverse
|
||||
// modules multiple times, but only if the new path to it is shorter than the
|
||||
// first (else we `continue` above).
|
||||
if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
|
||||
collect_trait_assoc_items(
|
||||
db,
|
||||
&mut map,
|
||||
tr,
|
||||
matches!(item, ItemInNs::Types(_)),
|
||||
&import_info,
|
||||
);
|
||||
}
|
||||
|
||||
map.insert(item, import_info);
|
||||
|
||||
// If we've just added a module, descend into it. We might traverse modules
|
||||
// multiple times, but only if the module depth is smaller (else we `continue`
|
||||
// above).
|
||||
if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
|
||||
worklist.push((mod_id, mk_path()));
|
||||
worklist.push((mod_id, depth + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import_map
|
||||
map
|
||||
}
|
||||
|
||||
fn collect_trait_assoc_items(
|
||||
db: &dyn DefDatabase,
|
||||
map: &mut FxIndexMap<ItemInNs, ImportInfo>,
|
||||
tr: TraitId,
|
||||
is_type_in_ns: bool,
|
||||
trait_import_info: &ImportInfo,
|
||||
) {
|
||||
let _p = profile::span("collect_trait_assoc_items");
|
||||
for (assoc_item_name, item) in &db.trait_data(tr).items {
|
||||
let module_def_id = match item {
|
||||
AssocItemId::FunctionId(f) => ModuleDefId::from(*f),
|
||||
AssocItemId::ConstId(c) => ModuleDefId::from(*c),
|
||||
// cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias`
|
||||
// qualifier, ergo no need to store it for imports in import_map
|
||||
AssocItemId::TypeAliasId(_) => {
|
||||
cov_mark::hit!(type_aliases_ignored);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let assoc_item = if is_type_in_ns {
|
||||
ItemInNs::Types(module_def_id)
|
||||
} else {
|
||||
ItemInNs::Values(module_def_id)
|
||||
};
|
||||
|
||||
let assoc_item_info = ImportInfo {
|
||||
container: trait_import_info.container,
|
||||
name: assoc_item_name.clone(),
|
||||
is_trait_assoc_item: true,
|
||||
};
|
||||
map.insert(assoc_item, assoc_item_info);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ImportMap {
|
||||
@ -260,7 +203,7 @@ impl Eq for ImportMap {}
|
||||
|
||||
impl fmt::Debug for ImportMap {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut importable_paths: Vec<_> = self
|
||||
let mut importable_names: Vec<_> = self
|
||||
.map
|
||||
.iter()
|
||||
.map(|(item, _)| match item {
|
||||
@ -270,56 +213,40 @@ impl fmt::Debug for ImportMap {
|
||||
})
|
||||
.collect();
|
||||
|
||||
importable_paths.sort();
|
||||
f.write_str(&importable_paths.join("\n"))
|
||||
importable_names.sort();
|
||||
f.write_str(&importable_names.join("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
fn fst_path(db: &dyn DefDatabase, path: &ImportPath) -> String {
|
||||
let _p = profile::span("fst_path");
|
||||
let mut s = path.display(db).to_string();
|
||||
s.make_ascii_lowercase();
|
||||
s
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Hash)]
|
||||
pub enum ImportKind {
|
||||
Module,
|
||||
Function,
|
||||
Adt,
|
||||
EnumVariant,
|
||||
Const,
|
||||
Static,
|
||||
Trait,
|
||||
TraitAlias,
|
||||
TypeAlias,
|
||||
BuiltinType,
|
||||
AssociatedItem,
|
||||
Macro,
|
||||
}
|
||||
|
||||
/// A way to match import map contents against the search query.
|
||||
#[derive(Debug)]
|
||||
pub enum SearchMode {
|
||||
enum SearchMode {
|
||||
/// Import map entry should strictly match the query string.
|
||||
Equals,
|
||||
/// Import map entry should contain the query string.
|
||||
Contains,
|
||||
Exact,
|
||||
/// Import map entry should contain all letters from the query string,
|
||||
/// in the same order, but not necessary adjacent.
|
||||
Fuzzy,
|
||||
}
|
||||
|
||||
/// Three possible ways to search for the name in associated and/or other items.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum AssocSearchMode {
|
||||
/// Search for the name in both associated and other items.
|
||||
Include,
|
||||
/// Search for the name in other items only.
|
||||
Exclude,
|
||||
/// Search for the name in the associated items only.
|
||||
AssocItemsOnly,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Query {
|
||||
query: String,
|
||||
lowercased: String,
|
||||
name_only: bool,
|
||||
assoc_items_only: bool,
|
||||
search_mode: SearchMode,
|
||||
assoc_mode: AssocSearchMode,
|
||||
case_sensitive: bool,
|
||||
limit: usize,
|
||||
exclude_import_kinds: FxHashSet<ImportKind>,
|
||||
}
|
||||
|
||||
impl Query {
|
||||
@ -328,30 +255,21 @@ impl Query {
|
||||
Self {
|
||||
query,
|
||||
lowercased,
|
||||
name_only: false,
|
||||
assoc_items_only: false,
|
||||
search_mode: SearchMode::Contains,
|
||||
search_mode: SearchMode::Exact,
|
||||
assoc_mode: AssocSearchMode::Include,
|
||||
case_sensitive: false,
|
||||
limit: usize::max_value(),
|
||||
exclude_import_kinds: FxHashSet::default(),
|
||||
limit: usize::MAX,
|
||||
}
|
||||
}
|
||||
|
||||
/// Matches entries' names only, ignoring the rest of
|
||||
/// the qualifier.
|
||||
/// Example: for `std::marker::PhantomData`, the name is `PhantomData`.
|
||||
pub fn name_only(self) -> Self {
|
||||
Self { name_only: true, ..self }
|
||||
/// Fuzzy finds items instead of exact matching.
|
||||
pub fn fuzzy(self) -> Self {
|
||||
Self { search_mode: SearchMode::Fuzzy, ..self }
|
||||
}
|
||||
|
||||
/// Matches only the entries that are associated items, ignoring the rest.
|
||||
pub fn assoc_items_only(self) -> Self {
|
||||
Self { assoc_items_only: true, ..self }
|
||||
}
|
||||
|
||||
/// Specifies the way to search for the entries using the query.
|
||||
pub fn search_mode(self, search_mode: SearchMode) -> Self {
|
||||
Self { search_mode, ..self }
|
||||
/// Specifies whether we want to include associated items in the result.
|
||||
pub fn assoc_search_mode(self, assoc_mode: AssocSearchMode) -> Self {
|
||||
Self { assoc_mode, ..self }
|
||||
}
|
||||
|
||||
/// Limits the returned number of items to `limit`.
|
||||
@ -364,12 +282,6 @@ impl Query {
|
||||
Self { case_sensitive: true, ..self }
|
||||
}
|
||||
|
||||
/// Do not include imports of the specified kind in the search results.
|
||||
pub fn exclude_import_kind(mut self, import_kind: ImportKind) -> Self {
|
||||
self.exclude_import_kinds.insert(import_kind);
|
||||
self
|
||||
}
|
||||
|
||||
fn import_matches(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
@ -377,49 +289,36 @@ impl Query {
|
||||
enforce_lowercase: bool,
|
||||
) -> bool {
|
||||
let _p = profile::span("import_map::Query::import_matches");
|
||||
if import.is_trait_assoc_item {
|
||||
if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) {
|
||||
return false;
|
||||
}
|
||||
} else if self.assoc_items_only {
|
||||
return false;
|
||||
match (import.is_trait_assoc_item, self.assoc_mode) {
|
||||
(true, AssocSearchMode::Exclude) => return false,
|
||||
(false, AssocSearchMode::AssocItemsOnly) => return false,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let mut input = if import.is_trait_assoc_item || self.name_only {
|
||||
import.path.segments.last().unwrap().display(db.upcast()).to_string()
|
||||
} else {
|
||||
import.path.display(db).to_string()
|
||||
};
|
||||
if enforce_lowercase || !self.case_sensitive {
|
||||
let mut input = import.name.display(db.upcast()).to_string();
|
||||
let case_insensitive = enforce_lowercase || !self.case_sensitive;
|
||||
if case_insensitive {
|
||||
input.make_ascii_lowercase();
|
||||
}
|
||||
|
||||
let query_string =
|
||||
if !enforce_lowercase && self.case_sensitive { &self.query } else { &self.lowercased };
|
||||
let query_string = if case_insensitive { &self.lowercased } else { &self.query };
|
||||
|
||||
match self.search_mode {
|
||||
SearchMode::Equals => &input == query_string,
|
||||
SearchMode::Contains => input.contains(query_string),
|
||||
SearchMode::Exact => &input == query_string,
|
||||
SearchMode::Fuzzy => {
|
||||
let mut unchecked_query_chars = query_string.chars();
|
||||
let mut mismatching_query_char = unchecked_query_chars.next();
|
||||
|
||||
for input_char in input.chars() {
|
||||
match mismatching_query_char {
|
||||
None => return true,
|
||||
Some(matching_query_char) if matching_query_char == input_char => {
|
||||
mismatching_query_char = unchecked_query_chars.next();
|
||||
}
|
||||
_ => (),
|
||||
let mut input_chars = input.chars();
|
||||
for query_char in query_string.chars() {
|
||||
if input_chars.find(|&it| it == query_char).is_none() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
mismatching_query_char.is_none()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Searches dependencies of `krate` for an importable path matching `query`.
|
||||
/// Searches dependencies of `krate` for an importable name matching `query`.
|
||||
///
|
||||
/// This returns a list of items that could be imported from dependencies of `krate`.
|
||||
pub fn search_dependencies(
|
||||
@ -442,65 +341,44 @@ pub fn search_dependencies(
|
||||
|
||||
let mut stream = op.union();
|
||||
|
||||
let mut all_indexed_values = FxHashSet::default();
|
||||
while let Some((_, indexed_values)) = stream.next() {
|
||||
all_indexed_values.extend(indexed_values.iter().copied());
|
||||
}
|
||||
|
||||
let mut res = FxHashSet::default();
|
||||
for indexed_value in all_indexed_values {
|
||||
let import_map = &import_maps[indexed_value.index];
|
||||
let importables = &import_map.importables[indexed_value.value as usize..];
|
||||
while let Some((_, indexed_values)) = stream.next() {
|
||||
for indexed_value in indexed_values {
|
||||
let import_map = &import_maps[indexed_value.index];
|
||||
let importables = &import_map.importables[indexed_value.value as usize..];
|
||||
|
||||
let common_importable_data = &import_map.map[&importables[0]];
|
||||
if !query.import_matches(db, common_importable_data, true) {
|
||||
continue;
|
||||
}
|
||||
let common_importable_data = &import_map.map[&importables[0]];
|
||||
if !query.import_matches(db, common_importable_data, true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Path shared by the importable items in this group.
|
||||
let common_importables_path_fst = fst_path(db, &common_importable_data.path);
|
||||
// Add the items from this `ModPath` group. Those are all subsequent items in
|
||||
// `importables` whose paths match `path`.
|
||||
let iter = importables
|
||||
.iter()
|
||||
.copied()
|
||||
.take_while(|item| {
|
||||
common_importables_path_fst == fst_path(db, &import_map.map[item].path)
|
||||
})
|
||||
.filter(|&item| match item_import_kind(item) {
|
||||
Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
|
||||
None => true,
|
||||
})
|
||||
.filter(|item| {
|
||||
!query.case_sensitive // we've already checked the common importables path case-insensitively
|
||||
// Name shared by the importable items in this group.
|
||||
let common_importable_name =
|
||||
common_importable_data.name.to_smol_str().to_ascii_lowercase();
|
||||
// Add the items from this name group. Those are all subsequent items in
|
||||
// `importables` whose name match `common_importable_name`.
|
||||
let iter = importables
|
||||
.iter()
|
||||
.copied()
|
||||
.take_while(|item| {
|
||||
common_importable_name
|
||||
== import_map.map[item].name.to_smol_str().to_ascii_lowercase()
|
||||
})
|
||||
.filter(|item| {
|
||||
!query.case_sensitive // we've already checked the common importables name case-insensitively
|
||||
|| query.import_matches(db, &import_map.map[item], false)
|
||||
});
|
||||
res.extend(iter);
|
||||
});
|
||||
res.extend(iter);
|
||||
|
||||
if res.len() >= query.limit {
|
||||
return res;
|
||||
if res.len() >= query.limit {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
|
||||
Some(match item.as_module_def_id()? {
|
||||
ModuleDefId::ModuleId(_) => ImportKind::Module,
|
||||
ModuleDefId::FunctionId(_) => ImportKind::Function,
|
||||
ModuleDefId::AdtId(_) => ImportKind::Adt,
|
||||
ModuleDefId::EnumVariantId(_) => ImportKind::EnumVariant,
|
||||
ModuleDefId::ConstId(_) => ImportKind::Const,
|
||||
ModuleDefId::StaticId(_) => ImportKind::Static,
|
||||
ModuleDefId::TraitId(_) => ImportKind::Trait,
|
||||
ModuleDefId::TraitAliasId(_) => ImportKind::TraitAlias,
|
||||
ModuleDefId::TypeAliasId(_) => ImportKind::TypeAlias,
|
||||
ModuleDefId::BuiltinType(_) => ImportKind::BuiltinType,
|
||||
ModuleDefId::MacroId(_) => ImportKind::Macro,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
|
||||
@ -510,16 +388,39 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
impl ImportMap {
|
||||
fn fmt_for_test(&self, db: &dyn DefDatabase) -> String {
|
||||
let mut importable_paths: Vec<_> = self
|
||||
.map
|
||||
.iter()
|
||||
.map(|(item, info)| {
|
||||
let path = render_path(db, info);
|
||||
let ns = match item {
|
||||
ItemInNs::Types(_) => "t",
|
||||
ItemInNs::Values(_) => "v",
|
||||
ItemInNs::Macros(_) => "m",
|
||||
};
|
||||
format!("- {path} ({ns})")
|
||||
})
|
||||
.collect();
|
||||
|
||||
importable_paths.sort();
|
||||
importable_paths.join("\n")
|
||||
}
|
||||
}
|
||||
|
||||
fn check_search(ra_fixture: &str, crate_name: &str, query: Query, expect: Expect) {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
let crate_graph = db.crate_graph();
|
||||
let krate = crate_graph
|
||||
.iter()
|
||||
.find(|krate| {
|
||||
crate_graph[*krate].display_name.as_ref().map(|n| n.to_string())
|
||||
== Some(crate_name.to_string())
|
||||
.find(|&krate| {
|
||||
crate_graph[krate]
|
||||
.display_name
|
||||
.as_ref()
|
||||
.is_some_and(|it| &**it.crate_name() == crate_name)
|
||||
})
|
||||
.unwrap();
|
||||
.expect("could not find crate");
|
||||
|
||||
let actual = search_dependencies(db.upcast(), krate, query)
|
||||
.into_iter()
|
||||
@ -530,7 +431,7 @@ mod tests {
|
||||
let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
|
||||
Some(assoc_item_path) => (assoc_item_path, "a"),
|
||||
None => (
|
||||
dependency_imports.path_of(dependency)?.display(&db).to_string(),
|
||||
render_path(&db, dependency_imports.import_info_for(dependency)?),
|
||||
match dependency {
|
||||
ItemInNs::Types(ModuleDefId::FunctionId(_))
|
||||
| ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
|
||||
@ -560,57 +461,25 @@ mod tests {
|
||||
dependency_imports: &ImportMap,
|
||||
dependency: ItemInNs,
|
||||
) -> Option<String> {
|
||||
let dependency_assoc_item_id = match dependency {
|
||||
ItemInNs::Types(ModuleDefId::FunctionId(id))
|
||||
| ItemInNs::Values(ModuleDefId::FunctionId(id)) => AssocItemId::from(id),
|
||||
ItemInNs::Types(ModuleDefId::ConstId(id))
|
||||
| ItemInNs::Values(ModuleDefId::ConstId(id)) => AssocItemId::from(id),
|
||||
ItemInNs::Types(ModuleDefId::TypeAliasId(id))
|
||||
| ItemInNs::Values(ModuleDefId::TypeAliasId(id)) => AssocItemId::from(id),
|
||||
let (dependency_assoc_item_id, container) = match dependency.as_module_def_id()? {
|
||||
ModuleDefId::FunctionId(id) => (AssocItemId::from(id), id.lookup(db).container),
|
||||
ModuleDefId::ConstId(id) => (AssocItemId::from(id), id.lookup(db).container),
|
||||
ModuleDefId::TypeAliasId(id) => (AssocItemId::from(id), id.lookup(db).container),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let trait_ = assoc_to_trait(db, dependency)?;
|
||||
if let ModuleDefId::TraitId(tr) = trait_.as_module_def_id()? {
|
||||
let trait_data = db.trait_data(tr);
|
||||
let assoc_item_name =
|
||||
trait_data.items.iter().find_map(|(assoc_item_name, assoc_item_id)| {
|
||||
if &dependency_assoc_item_id == assoc_item_id {
|
||||
Some(assoc_item_name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})?;
|
||||
return Some(format!(
|
||||
"{}::{}",
|
||||
dependency_imports.path_of(trait_)?.display(db),
|
||||
assoc_item_name.display(db.upcast())
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> {
|
||||
let assoc: AssocItemId = match item {
|
||||
ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
|
||||
ModuleDefId::TypeAliasId(it) => it.into(),
|
||||
ModuleDefId::FunctionId(it) => it.into(),
|
||||
ModuleDefId::ConstId(it) => it.into(),
|
||||
_ => return None,
|
||||
},
|
||||
_ => return None,
|
||||
let ItemContainerId::TraitId(trait_id) = container else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let container = match assoc {
|
||||
AssocItemId::FunctionId(it) => it.lookup(db).container,
|
||||
AssocItemId::ConstId(it) => it.lookup(db).container,
|
||||
AssocItemId::TypeAliasId(it) => it.lookup(db).container,
|
||||
};
|
||||
let trait_info = dependency_imports.import_info_for(ItemInNs::Types(trait_id.into()))?;
|
||||
|
||||
match container {
|
||||
ItemContainerId::TraitId(it) => Some(ItemInNs::Types(it.into())),
|
||||
_ => None,
|
||||
}
|
||||
let trait_data = db.trait_data(trait_id);
|
||||
let (assoc_item_name, _) = trait_data
|
||||
.items
|
||||
.iter()
|
||||
.find(|(_, assoc_item_id)| &dependency_assoc_item_id == assoc_item_id)?;
|
||||
Some(format!("{}::{}", render_path(db, trait_info), assoc_item_name.display(db.upcast())))
|
||||
}
|
||||
|
||||
fn check(ra_fixture: &str, expect: Expect) {
|
||||
@ -633,6 +502,24 @@ mod tests {
|
||||
expect.assert_eq(&actual)
|
||||
}
|
||||
|
||||
fn render_path(db: &dyn DefDatabase, info: &ImportInfo) -> String {
|
||||
let mut module = info.container;
|
||||
let mut segments = vec![&info.name];
|
||||
|
||||
let def_map = module.def_map(db);
|
||||
assert!(def_map.block_id().is_none(), "block local items should not be in `ImportMap`");
|
||||
|
||||
while let Some(parent) = module.containing_module(db) {
|
||||
let parent_data = &def_map[parent.local_id];
|
||||
let (name, _) =
|
||||
parent_data.children.iter().find(|(_, id)| **id == module.local_id).unwrap();
|
||||
segments.push(name);
|
||||
module = parent;
|
||||
}
|
||||
|
||||
segments.iter().rev().map(|it| it.display(db.upcast())).join("::")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
check(
|
||||
@ -749,6 +636,7 @@ mod tests {
|
||||
#[test]
|
||||
fn module_reexport() {
|
||||
// Reexporting modules from a dependency adds all contents to the import map.
|
||||
// XXX: The rendered paths are relative to the defining crate.
|
||||
check(
|
||||
r"
|
||||
//- /main.rs crate:main deps:lib
|
||||
@ -764,9 +652,9 @@ mod tests {
|
||||
- module::S (t)
|
||||
- module::S (v)
|
||||
main:
|
||||
- module::S (t)
|
||||
- module::S (v)
|
||||
- reexported_module (t)
|
||||
- reexported_module::S (t)
|
||||
- reexported_module::S (v)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
@ -868,10 +756,9 @@ mod tests {
|
||||
check_search(
|
||||
ra_fixture,
|
||||
"main",
|
||||
Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
|
||||
Query::new("fmt".to_string()).fuzzy(),
|
||||
expect![[r#"
|
||||
dep::fmt (t)
|
||||
dep::fmt::Display (t)
|
||||
dep::fmt::Display::FMT_CONST (a)
|
||||
dep::fmt::Display::format_function (a)
|
||||
dep::fmt::Display::format_method (a)
|
||||
@ -898,7 +785,9 @@ mod tests {
|
||||
check_search(
|
||||
ra_fixture,
|
||||
"main",
|
||||
Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(),
|
||||
Query::new("fmt".to_string())
|
||||
.fuzzy()
|
||||
.assoc_search_mode(AssocSearchMode::AssocItemsOnly),
|
||||
expect![[r#"
|
||||
dep::fmt::Display::FMT_CONST (a)
|
||||
dep::fmt::Display::format_function (a)
|
||||
@ -909,23 +798,10 @@ mod tests {
|
||||
check_search(
|
||||
ra_fixture,
|
||||
"main",
|
||||
Query::new("fmt".to_string())
|
||||
.search_mode(SearchMode::Fuzzy)
|
||||
.exclude_import_kind(ImportKind::AssociatedItem),
|
||||
Query::new("fmt".to_string()).fuzzy().assoc_search_mode(AssocSearchMode::Exclude),
|
||||
expect![[r#"
|
||||
dep::fmt (t)
|
||||
dep::fmt::Display (t)
|
||||
"#]],
|
||||
);
|
||||
|
||||
check_search(
|
||||
ra_fixture,
|
||||
"main",
|
||||
Query::new("fmt".to_string())
|
||||
.search_mode(SearchMode::Fuzzy)
|
||||
.assoc_items_only()
|
||||
.exclude_import_kind(ImportKind::AssociatedItem),
|
||||
expect![[r#""#]],
|
||||
dep::fmt (t)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
@ -958,13 +834,12 @@ mod tests {
|
||||
check_search(
|
||||
ra_fixture,
|
||||
"main",
|
||||
Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
|
||||
Query::new("fmt".to_string()).fuzzy(),
|
||||
expect![[r#"
|
||||
dep::Fmt (m)
|
||||
dep::Fmt (t)
|
||||
dep::Fmt (v)
|
||||
dep::fmt (t)
|
||||
dep::fmt::Display (t)
|
||||
dep::fmt::Display::fmt (a)
|
||||
dep::format (f)
|
||||
"#]],
|
||||
@ -973,7 +848,7 @@ mod tests {
|
||||
check_search(
|
||||
ra_fixture,
|
||||
"main",
|
||||
Query::new("fmt".to_string()).search_mode(SearchMode::Equals),
|
||||
Query::new("fmt".to_string()),
|
||||
expect![[r#"
|
||||
dep::Fmt (m)
|
||||
dep::Fmt (t)
|
||||
@ -982,20 +857,6 @@ mod tests {
|
||||
dep::fmt::Display::fmt (a)
|
||||
"#]],
|
||||
);
|
||||
|
||||
check_search(
|
||||
ra_fixture,
|
||||
"main",
|
||||
Query::new("fmt".to_string()).search_mode(SearchMode::Contains),
|
||||
expect![[r#"
|
||||
dep::Fmt (m)
|
||||
dep::Fmt (t)
|
||||
dep::Fmt (v)
|
||||
dep::fmt (t)
|
||||
dep::fmt::Display (t)
|
||||
dep::fmt::Display::fmt (a)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1033,7 +894,6 @@ mod tests {
|
||||
dep::Fmt (t)
|
||||
dep::Fmt (v)
|
||||
dep::fmt (t)
|
||||
dep::fmt::Display (t)
|
||||
dep::fmt::Display::fmt (a)
|
||||
"#]],
|
||||
);
|
||||
@ -1041,7 +901,7 @@ mod tests {
|
||||
check_search(
|
||||
ra_fixture,
|
||||
"main",
|
||||
Query::new("fmt".to_string()).name_only(),
|
||||
Query::new("fmt".to_string()),
|
||||
expect![[r#"
|
||||
dep::Fmt (m)
|
||||
dep::Fmt (t)
|
||||
@ -1106,43 +966,10 @@ mod tests {
|
||||
pub fn no() {}
|
||||
"#,
|
||||
"main",
|
||||
Query::new("".to_string()).limit(2),
|
||||
Query::new("".to_string()).fuzzy().limit(1),
|
||||
expect![[r#"
|
||||
dep::Fmt (m)
|
||||
dep::Fmt (t)
|
||||
dep::Fmt (v)
|
||||
dep::fmt (t)
|
||||
dep::fmt::Display (t)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn search_exclusions() {
|
||||
let ra_fixture = r#"
|
||||
//- /main.rs crate:main deps:dep
|
||||
//- /dep.rs crate:dep
|
||||
|
||||
pub struct fmt;
|
||||
pub struct FMT;
|
||||
"#;
|
||||
|
||||
check_search(
|
||||
ra_fixture,
|
||||
"main",
|
||||
Query::new("FMT".to_string()),
|
||||
expect![[r#"
|
||||
dep::FMT (t)
|
||||
dep::FMT (v)
|
||||
dep::fmt (t)
|
||||
dep::fmt (v)
|
||||
"#]],
|
||||
);
|
||||
|
||||
check_search(
|
||||
ra_fixture,
|
||||
"main",
|
||||
Query::new("FMT".to_string()).exclude_import_kind(ImportKind::Adt),
|
||||
expect![[r#""#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ use stdx::format_to;
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, HasModule,
|
||||
ImplId, LocalModuleId, MacroId, ModuleDefId, ModuleId, TraitId,
|
||||
db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId,
|
||||
ExternCrateId, HasModule, ImplId, LocalModuleId, MacroId, ModuleDefId, ModuleId, TraitId,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -50,6 +50,7 @@ pub struct ItemScope {
|
||||
unnamed_consts: Vec<ConstId>,
|
||||
/// Traits imported via `use Trait as _;`.
|
||||
unnamed_trait_imports: FxHashMap<TraitId, Visibility>,
|
||||
extern_crate_decls: Vec<ExternCrateId>,
|
||||
/// Macros visible in current module in legacy textual scope
|
||||
///
|
||||
/// For macros invoked by an unqualified identifier like `bar!()`, `legacy_macros` will be searched in first.
|
||||
@ -188,7 +189,11 @@ impl ItemScope {
|
||||
}
|
||||
|
||||
pub(crate) fn define_impl(&mut self, imp: ImplId) {
|
||||
self.impls.push(imp)
|
||||
self.impls.push(imp);
|
||||
}
|
||||
|
||||
pub(crate) fn define_extern_crate_decl(&mut self, extern_crate: ExternCrateId) {
|
||||
self.extern_crate_decls.push(extern_crate);
|
||||
}
|
||||
|
||||
pub(crate) fn define_unnamed_const(&mut self, konst: ConstId) {
|
||||
@ -397,6 +402,7 @@ impl ItemScope {
|
||||
legacy_macros,
|
||||
attr_macros,
|
||||
derive_macros,
|
||||
extern_crate_decls,
|
||||
} = self;
|
||||
types.shrink_to_fit();
|
||||
values.shrink_to_fit();
|
||||
@ -409,6 +415,7 @@ impl ItemScope {
|
||||
legacy_macros.shrink_to_fit();
|
||||
attr_macros.shrink_to_fit();
|
||||
derive_macros.shrink_to_fit();
|
||||
extern_crate_decls.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ use ast::{AstNode, HasName, StructKind};
|
||||
use base_db::CrateId;
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
ast_id_map::FileAstId,
|
||||
ast_id_map::{AstIdNode, FileAstId},
|
||||
attrs::RawAttrs,
|
||||
hygiene::Hygiene,
|
||||
name::{name, AsName, Name},
|
||||
@ -314,7 +314,7 @@ from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>), Param(Id
|
||||
|
||||
/// Trait implemented by all item nodes in the item tree.
|
||||
pub trait ItemTreeNode: Clone {
|
||||
type Source: AstNode + Into<ast::Item>;
|
||||
type Source: AstIdNode + Into<ast::Item>;
|
||||
|
||||
fn ast_id(&self) -> FileAstId<Self::Source>;
|
||||
|
||||
|
@ -52,7 +52,7 @@ struct Printer<'a> {
|
||||
needs_indent: bool,
|
||||
}
|
||||
|
||||
impl<'a> Printer<'a> {
|
||||
impl Printer<'_> {
|
||||
fn indented(&mut self, f: impl FnOnce(&mut Self)) {
|
||||
self.indent_level += 1;
|
||||
wln!(self);
|
||||
@ -572,7 +572,7 @@ impl<'a> Printer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Write for Printer<'a> {
|
||||
impl Write for Printer<'_> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for line in s.split_inclusive('\n') {
|
||||
if self.needs_indent {
|
||||
|
@ -180,15 +180,15 @@ impl LangItems {
|
||||
T: Into<AttrDefId> + Copy,
|
||||
{
|
||||
let _p = profile::span("collect_lang_item");
|
||||
if let Some(lang_item) = lang_attr(db, item) {
|
||||
if let Some(lang_item) = db.lang_attr(item.into()) {
|
||||
self.items.entry(lang_item).or_insert_with(|| constructor(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<LangItem> {
|
||||
let attrs = db.attrs(item.into());
|
||||
attrs.by_key("lang").string_value().cloned().and_then(|it| LangItem::from_str(&it))
|
||||
pub(crate) fn lang_attr_query(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangItem> {
|
||||
let attrs = db.attrs(item);
|
||||
attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(&it))
|
||||
}
|
||||
|
||||
pub enum GenericRequirement {
|
||||
|
@ -64,7 +64,7 @@ use std::{
|
||||
|
||||
use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind};
|
||||
use hir_expand::{
|
||||
ast_id_map::FileAstId,
|
||||
ast_id_map::{AstIdNode, FileAstId},
|
||||
attrs::{Attr, AttrId, AttrInput},
|
||||
builtin_attr_macro::BuiltinAttrExpander,
|
||||
builtin_derive_macro::BuiltinDeriveExpander,
|
||||
@ -88,8 +88,8 @@ use crate::{
|
||||
builtin_type::BuiltinType,
|
||||
data::adt::VariantData,
|
||||
item_tree::{
|
||||
Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, Static,
|
||||
Struct, Trait, TraitAlias, TypeAlias, Union,
|
||||
Const, Enum, ExternCrate, Function, Impl, Import, ItemTreeId, ItemTreeNode, MacroDef,
|
||||
MacroRules, Static, Struct, Trait, TraitAlias, TypeAlias, Union,
|
||||
},
|
||||
};
|
||||
|
||||
@ -145,24 +145,28 @@ pub struct ModuleId {
|
||||
}
|
||||
|
||||
impl ModuleId {
|
||||
pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
|
||||
pub fn def_map(self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
|
||||
match self.block {
|
||||
Some(block) => db.block_def_map(block),
|
||||
None => db.crate_def_map(self.krate),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn krate(&self) -> CrateId {
|
||||
pub fn krate(self) -> CrateId {
|
||||
self.krate
|
||||
}
|
||||
|
||||
pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> {
|
||||
pub fn containing_module(self, db: &dyn db::DefDatabase) -> Option<ModuleId> {
|
||||
self.def_map(db).containing_module(self.local_id)
|
||||
}
|
||||
|
||||
pub fn containing_block(&self) -> Option<BlockId> {
|
||||
pub fn containing_block(self) -> Option<BlockId> {
|
||||
self.block
|
||||
}
|
||||
|
||||
pub fn is_block_module(self) -> bool {
|
||||
self.block.is_some() && self.local_id == DefMap::ROOT
|
||||
}
|
||||
}
|
||||
|
||||
/// An ID of a module, **local** to a `DefMap`.
|
||||
@ -313,6 +317,16 @@ pub struct ImplId(salsa::InternId);
|
||||
type ImplLoc = ItemLoc<Impl>;
|
||||
impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct ImportId(salsa::InternId);
|
||||
type ImportLoc = ItemLoc<Import>;
|
||||
impl_intern!(ImportId, ImportLoc, intern_import, lookup_intern_import);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct ExternCrateId(salsa::InternId);
|
||||
type ExternCrateLoc = ItemLoc<ExternCrate>;
|
||||
impl_intern!(ExternCrateId, ExternCrateLoc, intern_extern_crate, lookup_intern_extern_crate);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct ExternBlockId(salsa::InternId);
|
||||
type ExternBlockLoc = ItemLoc<ExternBlock>;
|
||||
@ -392,14 +406,14 @@ impl TypeParamId {
|
||||
|
||||
impl TypeParamId {
|
||||
/// Caller should check if this toc id really belongs to a type
|
||||
pub fn from_unchecked(x: TypeOrConstParamId) -> Self {
|
||||
Self(x)
|
||||
pub fn from_unchecked(it: TypeOrConstParamId) -> Self {
|
||||
Self(it)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TypeParamId> for TypeOrConstParamId {
|
||||
fn from(x: TypeParamId) -> Self {
|
||||
x.0
|
||||
fn from(it: TypeParamId) -> Self {
|
||||
it.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,14 +432,14 @@ impl ConstParamId {
|
||||
|
||||
impl ConstParamId {
|
||||
/// Caller should check if this toc id really belongs to a const
|
||||
pub fn from_unchecked(x: TypeOrConstParamId) -> Self {
|
||||
Self(x)
|
||||
pub fn from_unchecked(it: TypeOrConstParamId) -> Self {
|
||||
Self(it)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConstParamId> for TypeOrConstParamId {
|
||||
fn from(x: ConstParamId) -> Self {
|
||||
x.0
|
||||
fn from(it: ConstParamId) -> Self {
|
||||
it.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -548,14 +562,14 @@ pub enum TypeOwnerId {
|
||||
impl TypeOwnerId {
|
||||
fn as_generic_def_id(self) -> Option<GenericDefId> {
|
||||
Some(match self {
|
||||
TypeOwnerId::FunctionId(x) => GenericDefId::FunctionId(x),
|
||||
TypeOwnerId::ConstId(x) => GenericDefId::ConstId(x),
|
||||
TypeOwnerId::AdtId(x) => GenericDefId::AdtId(x),
|
||||
TypeOwnerId::TraitId(x) => GenericDefId::TraitId(x),
|
||||
TypeOwnerId::TraitAliasId(x) => GenericDefId::TraitAliasId(x),
|
||||
TypeOwnerId::TypeAliasId(x) => GenericDefId::TypeAliasId(x),
|
||||
TypeOwnerId::ImplId(x) => GenericDefId::ImplId(x),
|
||||
TypeOwnerId::EnumVariantId(x) => GenericDefId::EnumVariantId(x),
|
||||
TypeOwnerId::FunctionId(it) => GenericDefId::FunctionId(it),
|
||||
TypeOwnerId::ConstId(it) => GenericDefId::ConstId(it),
|
||||
TypeOwnerId::AdtId(it) => GenericDefId::AdtId(it),
|
||||
TypeOwnerId::TraitId(it) => GenericDefId::TraitId(it),
|
||||
TypeOwnerId::TraitAliasId(it) => GenericDefId::TraitAliasId(it),
|
||||
TypeOwnerId::TypeAliasId(it) => GenericDefId::TypeAliasId(it),
|
||||
TypeOwnerId::ImplId(it) => GenericDefId::ImplId(it),
|
||||
TypeOwnerId::EnumVariantId(it) => GenericDefId::EnumVariantId(it),
|
||||
TypeOwnerId::InTypeConstId(_) | TypeOwnerId::ModuleId(_) | TypeOwnerId::StaticId(_) => {
|
||||
return None
|
||||
}
|
||||
@ -578,15 +592,15 @@ impl_from!(
|
||||
for TypeOwnerId
|
||||
);
|
||||
|
||||
// Every `DefWithBodyId` is a type owner, since bodies can contain type (e.g. `{ let x: Type = _; }`)
|
||||
// Every `DefWithBodyId` is a type owner, since bodies can contain type (e.g. `{ let it: Type = _; }`)
|
||||
impl From<DefWithBodyId> for TypeOwnerId {
|
||||
fn from(value: DefWithBodyId) -> Self {
|
||||
match value {
|
||||
DefWithBodyId::FunctionId(x) => x.into(),
|
||||
DefWithBodyId::StaticId(x) => x.into(),
|
||||
DefWithBodyId::ConstId(x) => x.into(),
|
||||
DefWithBodyId::InTypeConstId(x) => x.into(),
|
||||
DefWithBodyId::VariantId(x) => x.into(),
|
||||
DefWithBodyId::FunctionId(it) => it.into(),
|
||||
DefWithBodyId::StaticId(it) => it.into(),
|
||||
DefWithBodyId::ConstId(it) => it.into(),
|
||||
DefWithBodyId::InTypeConstId(it) => it.into(),
|
||||
DefWithBodyId::VariantId(it) => it.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -594,14 +608,14 @@ impl From<DefWithBodyId> for TypeOwnerId {
|
||||
impl From<GenericDefId> for TypeOwnerId {
|
||||
fn from(value: GenericDefId) -> Self {
|
||||
match value {
|
||||
GenericDefId::FunctionId(x) => x.into(),
|
||||
GenericDefId::AdtId(x) => x.into(),
|
||||
GenericDefId::TraitId(x) => x.into(),
|
||||
GenericDefId::TraitAliasId(x) => x.into(),
|
||||
GenericDefId::TypeAliasId(x) => x.into(),
|
||||
GenericDefId::ImplId(x) => x.into(),
|
||||
GenericDefId::EnumVariantId(x) => x.into(),
|
||||
GenericDefId::ConstId(x) => x.into(),
|
||||
GenericDefId::FunctionId(it) => it.into(),
|
||||
GenericDefId::AdtId(it) => it.into(),
|
||||
GenericDefId::TraitId(it) => it.into(),
|
||||
GenericDefId::TraitAliasId(it) => it.into(),
|
||||
GenericDefId::TypeAliasId(it) => it.into(),
|
||||
GenericDefId::ImplId(it) => it.into(),
|
||||
GenericDefId::EnumVariantId(it) => it.into(),
|
||||
GenericDefId::ConstId(it) => it.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -716,7 +730,7 @@ impl GeneralConstId {
|
||||
.const_data(const_id)
|
||||
.name
|
||||
.as_ref()
|
||||
.and_then(|x| x.as_str())
|
||||
.and_then(|it| it.as_str())
|
||||
.unwrap_or("_")
|
||||
.to_owned(),
|
||||
GeneralConstId::ConstBlockId(id) => format!("{{anonymous const {id:?}}}"),
|
||||
@ -821,6 +835,7 @@ pub enum AttrDefId {
|
||||
ImplId(ImplId),
|
||||
GenericParamId(GenericParamId),
|
||||
ExternBlockId(ExternBlockId),
|
||||
ExternCrateId(ExternCrateId),
|
||||
}
|
||||
|
||||
impl_from!(
|
||||
@ -835,7 +850,8 @@ impl_from!(
|
||||
TypeAliasId,
|
||||
MacroId(Macro2Id, MacroRulesId, ProcMacroId),
|
||||
ImplId,
|
||||
GenericParamId
|
||||
GenericParamId,
|
||||
ExternCrateId
|
||||
for AttrDefId
|
||||
);
|
||||
|
||||
@ -927,6 +943,12 @@ impl HasModule for AdtId {
|
||||
}
|
||||
}
|
||||
|
||||
impl HasModule for ExternCrateId {
|
||||
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
||||
self.lookup(db).container
|
||||
}
|
||||
}
|
||||
|
||||
impl HasModule for VariantId {
|
||||
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
||||
match self {
|
||||
@ -950,17 +972,17 @@ impl HasModule for MacroId {
|
||||
impl HasModule for TypeOwnerId {
|
||||
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
|
||||
match self {
|
||||
TypeOwnerId::FunctionId(x) => x.lookup(db).module(db),
|
||||
TypeOwnerId::StaticId(x) => x.lookup(db).module(db),
|
||||
TypeOwnerId::ConstId(x) => x.lookup(db).module(db),
|
||||
TypeOwnerId::InTypeConstId(x) => x.lookup(db).owner.module(db),
|
||||
TypeOwnerId::AdtId(x) => x.module(db),
|
||||
TypeOwnerId::TraitId(x) => x.lookup(db).container,
|
||||
TypeOwnerId::TraitAliasId(x) => x.lookup(db).container,
|
||||
TypeOwnerId::TypeAliasId(x) => x.lookup(db).module(db),
|
||||
TypeOwnerId::ImplId(x) => x.lookup(db).container,
|
||||
TypeOwnerId::EnumVariantId(x) => x.parent.lookup(db).container,
|
||||
TypeOwnerId::ModuleId(x) => *x,
|
||||
TypeOwnerId::FunctionId(it) => it.lookup(db).module(db),
|
||||
TypeOwnerId::StaticId(it) => it.lookup(db).module(db),
|
||||
TypeOwnerId::ConstId(it) => it.lookup(db).module(db),
|
||||
TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.module(db),
|
||||
TypeOwnerId::AdtId(it) => it.module(db),
|
||||
TypeOwnerId::TraitId(it) => it.lookup(db).container,
|
||||
TypeOwnerId::TraitAliasId(it) => it.lookup(db).container,
|
||||
TypeOwnerId::TypeAliasId(it) => it.lookup(db).module(db),
|
||||
TypeOwnerId::ImplId(it) => it.lookup(db).container,
|
||||
TypeOwnerId::EnumVariantId(it) => it.parent.lookup(db).container,
|
||||
TypeOwnerId::ModuleId(it) => *it,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1050,6 +1072,7 @@ impl AttrDefId {
|
||||
.krate
|
||||
}
|
||||
AttrDefId::MacroId(it) => it.module(db).krate,
|
||||
AttrDefId::ExternCrateId(it) => it.lookup(db).container.krate,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1101,12 +1124,12 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||
|
||||
/// Helper wrapper for `AstId` with `ModPath`
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct AstIdWithPath<T: ast::AstNode> {
|
||||
struct AstIdWithPath<T: AstIdNode> {
|
||||
ast_id: AstId<T>,
|
||||
path: path::ModPath,
|
||||
}
|
||||
|
||||
impl<T: ast::AstNode> AstIdWithPath<T> {
|
||||
impl<T: AstIdNode> AstIdWithPath<T> {
|
||||
fn new(file_id: HirFileId, ast_id: FileAstId<T>, path: path::ModPath) -> AstIdWithPath<T> {
|
||||
AstIdWithPath { ast_id: AstId::new(file_id, ast_id), path }
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
//! Context for lowering paths.
|
||||
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile};
|
||||
use hir_expand::{
|
||||
ast_id_map::{AstIdMap, AstIdNode},
|
||||
hygiene::Hygiene,
|
||||
AstId, HirFileId, InFile,
|
||||
};
|
||||
use once_cell::unsync::OnceCell;
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
@ -37,7 +41,7 @@ impl<'a> LowerCtx<'a> {
|
||||
Path::from_src(ast, self)
|
||||
}
|
||||
|
||||
pub(crate) fn ast_id<N: syntax::AstNode>(&self, item: &N) -> Option<AstId<N>> {
|
||||
pub(crate) fn ast_id<N: AstIdNode>(&self, item: &N) -> Option<AstId<N>> {
|
||||
let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
|
||||
let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
|
||||
Some(InFile::new(file_id, ast_id_map.ast_id(item)))
|
||||
|
@ -278,6 +278,44 @@ impl < > core::cmp::Eq for Command< > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_partial_eq_expand_with_derive_const() {
|
||||
// FIXME: actually expand with const
|
||||
check(
|
||||
r#"
|
||||
//- minicore: derive, eq
|
||||
#[derive_const(PartialEq, Eq)]
|
||||
enum Command {
|
||||
Move { x: i32, y: i32 },
|
||||
Do(&'static str),
|
||||
Jump,
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
#[derive_const(PartialEq, Eq)]
|
||||
enum Command {
|
||||
Move { x: i32, y: i32 },
|
||||
Do(&'static str),
|
||||
Jump,
|
||||
}
|
||||
|
||||
impl < > core::cmp::PartialEq for Command< > where {
|
||||
fn eq(&self , other: &Self ) -> bool {
|
||||
match (self , other) {
|
||||
(Command::Move {
|
||||
x: x_self, y: y_self,
|
||||
}
|
||||
, Command::Move {
|
||||
x: x_other, y: y_other,
|
||||
}
|
||||
)=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false
|
||||
}
|
||||
}
|
||||
}
|
||||
impl < > core::cmp::Eq for Command< > where {}"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_partial_ord_expand() {
|
||||
check(
|
||||
@ -378,6 +416,44 @@ fn test_hash_expand() {
|
||||
//- minicore: derive, hash
|
||||
use core::hash::Hash;
|
||||
|
||||
#[derive(Hash)]
|
||||
struct Foo {
|
||||
x: i32,
|
||||
y: u64,
|
||||
z: (i32, u64),
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
use core::hash::Hash;
|
||||
|
||||
#[derive(Hash)]
|
||||
struct Foo {
|
||||
x: i32,
|
||||
y: u64,
|
||||
z: (i32, u64),
|
||||
}
|
||||
|
||||
impl < > core::hash::Hash for Foo< > where {
|
||||
fn hash<H: core::hash::Hasher>(&self , ra_expand_state: &mut H) {
|
||||
match self {
|
||||
Foo {
|
||||
x: x, y: y, z: z,
|
||||
}
|
||||
=> {
|
||||
x.hash(ra_expand_state);
|
||||
y.hash(ra_expand_state);
|
||||
z.hash(ra_expand_state);
|
||||
}
|
||||
,
|
||||
}
|
||||
}
|
||||
}"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
//- minicore: derive, hash
|
||||
use core::hash::Hash;
|
||||
|
||||
#[derive(Hash)]
|
||||
enum Command {
|
||||
Move { x: i32, y: i32 },
|
||||
|
@ -201,7 +201,7 @@ macro_rules! format_args {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(arg2), ::core::fmt::Debug::fmt), ]);
|
||||
::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), ]);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
@ -235,11 +235,11 @@ macro_rules! format_args {
|
||||
|
||||
fn main() {
|
||||
/* error: no rule matches input tokens */;
|
||||
/* error: no rule matches input tokens */;
|
||||
/* error: no rule matches input tokens */;
|
||||
/* error: no rule matches input tokens */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::Argument::new(&(), ::core::fmt::Display::fmt), ]);
|
||||
/* error: no rule matches input tokens */;
|
||||
::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::Argument::new(&(5), ::core::fmt::Display::fmt), ]);
|
||||
/* 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), ]);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
@ -267,7 +267,7 @@ macro_rules! format_args {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a::<A, B>()), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]);
|
||||
::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), ]);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
@ -300,7 +300,7 @@ macro_rules! format_args {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::Argument::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]);
|
||||
::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), ]);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
@ -334,7 +334,7 @@ macro_rules! format_args {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::Argument::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]);
|
||||
::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), ]);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
@ -364,8 +364,8 @@ macro_rules! format_args {
|
||||
|
||||
fn main() {
|
||||
let _ =
|
||||
/* error: no rule matches input tokens *//* parse error: expected field name or number */
|
||||
::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(), ::core::fmt::Debug::fmt), ]);
|
||||
/* 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), ]);
|
||||
}
|
||||
"##]],
|
||||
);
|
||||
|
@ -98,6 +98,42 @@ fn#19 main#20(#21)#21 {#22
|
||||
"##]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn token_mapping_eager() {
|
||||
check(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
macro_rules! format_args {}
|
||||
|
||||
macro_rules! identity {
|
||||
($expr:expr) => { $expr };
|
||||
}
|
||||
|
||||
fn main(foo: ()) {
|
||||
format_args/*+tokenids*/!("{} {} {}", format_args!("{}", 0), foo, identity!(10), "bar")
|
||||
}
|
||||
|
||||
"#,
|
||||
expect![[r##"
|
||||
#[rustc_builtin_macro]
|
||||
#[macro_export]
|
||||
macro_rules! format_args {}
|
||||
|
||||
macro_rules! identity {
|
||||
($expr:expr) => { $expr };
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
"##]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_field_access_macro_input() {
|
||||
check(
|
||||
|
@ -20,8 +20,8 @@ use ::mbe::TokenMap;
|
||||
use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase};
|
||||
use expect_test::Expect;
|
||||
use hir_expand::{
|
||||
db::{ExpandDatabase, TokenExpander},
|
||||
AstId, InFile, MacroDefId, MacroDefKind, MacroFile,
|
||||
db::{DeclarativeMacroExpander, ExpandDatabase},
|
||||
AstId, InFile, MacroFile,
|
||||
};
|
||||
use stdx::format_to;
|
||||
use syntax::{
|
||||
@ -100,32 +100,29 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
||||
let call_offset = macro_.syntax().text_range().start().into();
|
||||
let file_ast_id = db.ast_id_map(source.file_id).ast_id(¯o_);
|
||||
let ast_id = AstId::new(source.file_id, file_ast_id.upcast());
|
||||
let kind = MacroDefKind::Declarative(ast_id);
|
||||
|
||||
let macro_def = db
|
||||
.macro_def(MacroDefId { krate, kind, local_inner: false, allow_internal_unsafe: false })
|
||||
.unwrap();
|
||||
if let TokenExpander::DeclarativeMacro { mac, def_site_token_map } = &*macro_def {
|
||||
let tt = match ¯o_ {
|
||||
ast::Macro::MacroRules(mac) => mac.token_tree().unwrap(),
|
||||
ast::Macro::MacroDef(_) => unimplemented!(""),
|
||||
};
|
||||
let DeclarativeMacroExpander { mac, def_site_token_map } =
|
||||
&*db.decl_macro_expander(krate, ast_id);
|
||||
assert_eq!(mac.err(), None);
|
||||
let tt = match ¯o_ {
|
||||
ast::Macro::MacroRules(mac) => mac.token_tree().unwrap(),
|
||||
ast::Macro::MacroDef(_) => unimplemented!(""),
|
||||
};
|
||||
|
||||
let tt_start = tt.syntax().text_range().start();
|
||||
tt.syntax().descendants_with_tokens().filter_map(SyntaxElement::into_token).for_each(
|
||||
|token| {
|
||||
let range = token.text_range().checked_sub(tt_start).unwrap();
|
||||
if let Some(id) = def_site_token_map.token_by_range(range) {
|
||||
let offset = (range.end() + tt_start).into();
|
||||
text_edits.push((offset..offset, format!("#{}", id.0)));
|
||||
}
|
||||
},
|
||||
);
|
||||
text_edits.push((
|
||||
call_offset..call_offset,
|
||||
format!("// call ids will be shifted by {:?}\n", mac.shift()),
|
||||
));
|
||||
}
|
||||
let tt_start = tt.syntax().text_range().start();
|
||||
tt.syntax().descendants_with_tokens().filter_map(SyntaxElement::into_token).for_each(
|
||||
|token| {
|
||||
let range = token.text_range().checked_sub(tt_start).unwrap();
|
||||
if let Some(id) = def_site_token_map.token_by_range(range) {
|
||||
let offset = (range.end() + tt_start).into();
|
||||
text_edits.push((offset..offset, format!("#{}", id.0)));
|
||||
}
|
||||
},
|
||||
);
|
||||
text_edits.push((
|
||||
call_offset..call_offset,
|
||||
format!("// call ids will be shifted by {:?}\n", mac.shift()),
|
||||
));
|
||||
}
|
||||
|
||||
for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
|
||||
@ -190,7 +187,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
||||
let range: Range<usize> = range.into();
|
||||
|
||||
if show_token_ids {
|
||||
if let Some((tree, map, _)) = arg.as_deref() {
|
||||
if let Some((tree, map, _)) = arg.value.as_deref() {
|
||||
let tt_range = call.token_tree().unwrap().syntax().text_range();
|
||||
let mut ranges = Vec::new();
|
||||
extract_id_ranges(&mut ranges, map, tree);
|
||||
@ -239,7 +236,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
||||
|
||||
for impl_id in def_map[local_id].scope.impls() {
|
||||
let src = impl_id.lookup(&db).source(&db);
|
||||
if src.file_id.is_builtin_derive(&db).is_some() {
|
||||
if src.file_id.is_builtin_derive(&db) {
|
||||
let pp = pretty_print_macro_expansion(src.value.syntax().clone(), None);
|
||||
format_to!(expanded_text, "\n{}", pp)
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ mod tests;
|
||||
use std::{cmp::Ord, ops::Deref};
|
||||
|
||||
use base_db::{CrateId, Edition, FileId, ProcMacroKind};
|
||||
use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId};
|
||||
use hir_expand::{name::Name, HirFileId, InFile, MacroCallId, MacroDefId};
|
||||
use itertools::Itertools;
|
||||
use la_arena::Arena;
|
||||
use profile::Count;
|
||||
@ -196,6 +196,10 @@ impl BlockRelativeModuleId {
|
||||
fn into_module(self, krate: CrateId) -> ModuleId {
|
||||
ModuleId { krate, block: self.block, local_id: self.local_id }
|
||||
}
|
||||
|
||||
fn is_block_module(self) -> bool {
|
||||
self.block.is_some() && self.local_id == DefMap::ROOT
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<LocalModuleId> for DefMap {
|
||||
@ -278,7 +282,9 @@ pub struct ModuleData {
|
||||
pub origin: ModuleOrigin,
|
||||
/// Declared visibility of this module.
|
||||
pub visibility: Visibility,
|
||||
/// Always [`None`] for block modules
|
||||
/// Parent module in the same `DefMap`.
|
||||
///
|
||||
/// [`None`] for block modules because they are always its `DefMap`'s root.
|
||||
pub parent: Option<LocalModuleId>,
|
||||
pub children: FxHashMap<Name, LocalModuleId>,
|
||||
pub scope: ItemScope,
|
||||
@ -626,6 +632,17 @@ impl ModuleData {
|
||||
self.origin.definition_source(db)
|
||||
}
|
||||
|
||||
/// Same as [`definition_source`] but only returns the file id to prevent parsing the ASt.
|
||||
pub fn definition_source_file_id(&self) -> HirFileId {
|
||||
match self.origin {
|
||||
ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => {
|
||||
definition.into()
|
||||
}
|
||||
ModuleOrigin::Inline { definition, .. } => definition.file_id,
|
||||
ModuleOrigin::BlockExpr { block } => block.file_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
|
||||
/// `None` for the crate root or block.
|
||||
pub fn declaration_source(&self, db: &dyn DefDatabase) -> Option<InFile<ast::Module>> {
|
||||
|
@ -52,10 +52,10 @@ use crate::{
|
||||
tt,
|
||||
visibility::{RawVisibility, Visibility},
|
||||
AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId,
|
||||
ExternBlockLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId,
|
||||
Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, ModuleDefId,
|
||||
ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, TraitLoc,
|
||||
TypeAliasLoc, UnionLoc, UnresolvedMacro,
|
||||
ExternBlockLoc, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, ImportLoc, Intern,
|
||||
ItemContainerId, LocalModuleId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId,
|
||||
MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc,
|
||||
TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro,
|
||||
};
|
||||
|
||||
static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
|
||||
@ -156,10 +156,9 @@ struct Import {
|
||||
alias: Option<ImportAlias>,
|
||||
visibility: RawVisibility,
|
||||
kind: ImportKind,
|
||||
is_prelude: bool,
|
||||
is_extern_crate: bool,
|
||||
is_macro_use: bool,
|
||||
source: ImportSource,
|
||||
is_prelude: bool,
|
||||
is_macro_use: bool,
|
||||
}
|
||||
|
||||
impl Import {
|
||||
@ -168,26 +167,23 @@ impl Import {
|
||||
krate: CrateId,
|
||||
tree: &ItemTree,
|
||||
id: ItemTreeId<item_tree::Import>,
|
||||
) -> Vec<Self> {
|
||||
mut cb: impl FnMut(Self),
|
||||
) {
|
||||
let it = &tree[id.value];
|
||||
let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
|
||||
let visibility = &tree[it.visibility];
|
||||
let is_prelude = attrs.by_key("prelude_import").exists();
|
||||
|
||||
let mut res = Vec::new();
|
||||
it.use_tree.expand(|idx, path, kind, alias| {
|
||||
res.push(Self {
|
||||
cb(Self {
|
||||
path,
|
||||
alias,
|
||||
visibility: visibility.clone(),
|
||||
kind,
|
||||
is_prelude,
|
||||
is_extern_crate: false,
|
||||
is_macro_use: false,
|
||||
source: ImportSource::Import { id, use_tree: idx },
|
||||
});
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
fn from_extern_crate(
|
||||
@ -205,7 +201,6 @@ impl Import {
|
||||
visibility: visibility.clone(),
|
||||
kind: ImportKind::Plain,
|
||||
is_prelude: false,
|
||||
is_extern_crate: true,
|
||||
is_macro_use: attrs.by_key("macro_use").exists(),
|
||||
source: ImportSource::ExternCrate(id),
|
||||
}
|
||||
@ -776,7 +771,7 @@ impl DefCollector<'_> {
|
||||
let _p = profile::span("resolve_import")
|
||||
.detail(|| format!("{}", import.path.display(self.db.upcast())));
|
||||
tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
|
||||
if import.is_extern_crate {
|
||||
if matches!(import.source, ImportSource::ExternCrate { .. }) {
|
||||
let name = import
|
||||
.path
|
||||
.as_ident()
|
||||
@ -813,11 +808,8 @@ impl DefCollector<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether all namespace is resolved
|
||||
if def.take_types().is_some()
|
||||
&& def.take_values().is_some()
|
||||
&& def.take_macros().is_some()
|
||||
{
|
||||
// Check whether all namespaces are resolved.
|
||||
if def.is_full() {
|
||||
PartialResolvedImport::Resolved(def)
|
||||
} else {
|
||||
PartialResolvedImport::Indeterminate(def)
|
||||
@ -826,7 +818,7 @@ impl DefCollector<'_> {
|
||||
}
|
||||
|
||||
fn resolve_extern_crate(&self, name: &Name) -> Option<CrateRootModuleId> {
|
||||
if *name == name!(self) {
|
||||
if *name == name![self] {
|
||||
cov_mark::hit!(extern_crate_self_as);
|
||||
Some(self.def_map.crate_root())
|
||||
} else {
|
||||
@ -867,7 +859,7 @@ impl DefCollector<'_> {
|
||||
tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
|
||||
|
||||
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
|
||||
if import.is_extern_crate
|
||||
if matches!(import.source, ImportSource::ExternCrate { .. })
|
||||
&& self.def_map.block.is_none()
|
||||
&& module_id == DefMap::ROOT
|
||||
{
|
||||
@ -1585,21 +1577,34 @@ impl ModCollector<'_, '_> {
|
||||
match item {
|
||||
ModItem::Mod(m) => self.collect_module(m, &attrs),
|
||||
ModItem::Import(import_id) => {
|
||||
let imports = Import::from_use(
|
||||
let _import_id = ImportLoc {
|
||||
container: module,
|
||||
id: ItemTreeId::new(self.tree_id, import_id),
|
||||
}
|
||||
.intern(db);
|
||||
Import::from_use(
|
||||
db,
|
||||
krate,
|
||||
self.item_tree,
|
||||
ItemTreeId::new(self.tree_id, import_id),
|
||||
);
|
||||
self.def_collector.unresolved_imports.extend(imports.into_iter().map(
|
||||
|import| ImportDirective {
|
||||
module_id: self.module_id,
|
||||
import,
|
||||
status: PartialResolvedImport::Unresolved,
|
||||
|import| {
|
||||
self.def_collector.unresolved_imports.push(ImportDirective {
|
||||
module_id: self.module_id,
|
||||
import,
|
||||
status: PartialResolvedImport::Unresolved,
|
||||
});
|
||||
},
|
||||
));
|
||||
)
|
||||
}
|
||||
ModItem::ExternCrate(import_id) => {
|
||||
let extern_crate_id = ExternCrateLoc {
|
||||
container: module,
|
||||
id: ItemTreeId::new(self.tree_id, import_id),
|
||||
}
|
||||
.intern(db);
|
||||
self.def_collector.def_map.modules[self.module_id]
|
||||
.scope
|
||||
.define_extern_crate_decl(extern_crate_id);
|
||||
self.def_collector.unresolved_imports.push(ImportDirective {
|
||||
module_id: self.module_id,
|
||||
import: Import::from_extern_crate(
|
||||
@ -2230,8 +2235,12 @@ impl ModCollector<'_, '_> {
|
||||
}
|
||||
|
||||
fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) {
|
||||
let Some((source, target)) = Self::borrow_modules(self.def_collector.def_map.modules.as_mut(), module_id, self.module_id) else {
|
||||
return
|
||||
let Some((source, target)) = Self::borrow_modules(
|
||||
self.def_collector.def_map.modules.as_mut(),
|
||||
module_id,
|
||||
self.module_id,
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
|
||||
for (name, macs) in source.scope.legacy_macros() {
|
||||
@ -2271,7 +2280,7 @@ impl ModCollector<'_, '_> {
|
||||
fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) {
|
||||
let ast_id = item.ast_id(self.item_tree);
|
||||
|
||||
let ast_id = InFile::new(self.file_id(), ast_id.upcast());
|
||||
let ast_id = InFile::new(self.file_id(), ast_id.erase());
|
||||
self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code(
|
||||
self.module_id,
|
||||
ast_id,
|
||||
|
@ -2,12 +2,9 @@
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use hir_expand::{attrs::AttrId, MacroCallKind};
|
||||
use hir_expand::{attrs::AttrId, ErasedAstId, MacroCallKind};
|
||||
use la_arena::Idx;
|
||||
use syntax::{
|
||||
ast::{self, AnyHasAttrs},
|
||||
SyntaxError,
|
||||
};
|
||||
use syntax::{ast, SyntaxError};
|
||||
|
||||
use crate::{
|
||||
item_tree::{self, ItemTreeId},
|
||||
@ -24,7 +21,7 @@ pub enum DefDiagnosticKind {
|
||||
|
||||
UnresolvedImport { id: ItemTreeId<item_tree::Import>, index: Idx<ast::UseTree> },
|
||||
|
||||
UnconfiguredCode { ast: AstId<AnyHasAttrs>, cfg: CfgExpr, opts: CfgOptions },
|
||||
UnconfiguredCode { ast: ErasedAstId, cfg: CfgExpr, opts: CfgOptions },
|
||||
|
||||
UnresolvedProcMacro { ast: MacroCallKind, krate: CrateId },
|
||||
|
||||
@ -81,7 +78,7 @@ impl DefDiagnostic {
|
||||
|
||||
pub fn unconfigured_code(
|
||||
container: LocalModuleId,
|
||||
ast: AstId<ast::AnyHasAttrs>,
|
||||
ast: ErasedAstId,
|
||||
cfg: CfgExpr,
|
||||
opts: CfgOptions,
|
||||
) -> Self {
|
||||
|
@ -12,11 +12,12 @@
|
||||
|
||||
use base_db::Edition;
|
||||
use hir_expand::name::Name;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
item_scope::BUILTIN_SCOPE,
|
||||
nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs},
|
||||
nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs},
|
||||
path::{ModPath, PathKind},
|
||||
per_ns::PerNs,
|
||||
visibility::{RawVisibility, Visibility},
|
||||
@ -159,13 +160,15 @@ impl DefMap {
|
||||
(None, new) => new,
|
||||
};
|
||||
|
||||
match ¤t_map.block {
|
||||
Some(block) => {
|
||||
match current_map.block {
|
||||
Some(block) if original_module == Self::ROOT => {
|
||||
// Block modules "inherit" names from its parent module.
|
||||
original_module = block.parent.local_id;
|
||||
arc = block.parent.def_map(db, current_map.krate);
|
||||
current_map = &*arc;
|
||||
current_map = &arc;
|
||||
}
|
||||
None => return result,
|
||||
// Proper (non-block) modules, including those in block `DefMap`s, don't.
|
||||
_ => return result,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -189,7 +192,7 @@ impl DefMap {
|
||||
));
|
||||
|
||||
let mut segments = path.segments().iter().enumerate();
|
||||
let mut curr_per_ns: PerNs = match path.kind {
|
||||
let mut curr_per_ns = match path.kind {
|
||||
PathKind::DollarCrate(krate) => {
|
||||
if krate == self.krate {
|
||||
cov_mark::hit!(macro_dollar_crate_self);
|
||||
@ -241,51 +244,54 @@ impl DefMap {
|
||||
)
|
||||
}
|
||||
PathKind::Super(lvl) => {
|
||||
let mut module = original_module;
|
||||
for i in 0..lvl {
|
||||
match self.modules[module].parent {
|
||||
Some(it) => module = it,
|
||||
None => match &self.block {
|
||||
Some(block) => {
|
||||
// Look up remaining path in parent `DefMap`
|
||||
let new_path = ModPath::from_segments(
|
||||
PathKind::Super(lvl - i),
|
||||
path.segments().to_vec(),
|
||||
);
|
||||
tracing::debug!(
|
||||
"`super` path: {} -> {} in parent map",
|
||||
path.display(db.upcast()),
|
||||
new_path.display(db.upcast())
|
||||
);
|
||||
return block
|
||||
.parent
|
||||
.def_map(db, self.krate)
|
||||
.resolve_path_fp_with_macro(
|
||||
db,
|
||||
mode,
|
||||
block.parent.local_id,
|
||||
&new_path,
|
||||
shadow,
|
||||
expected_macro_subns,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
tracing::debug!("super path in root module");
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
||||
}
|
||||
},
|
||||
let mut local_id = original_module;
|
||||
let mut ext;
|
||||
let mut def_map = self;
|
||||
|
||||
// Adjust `local_id` to `self`, i.e. the nearest non-block module.
|
||||
if def_map.module_id(local_id).is_block_module() {
|
||||
(ext, local_id) = adjust_to_nearest_non_block_module(db, def_map, local_id);
|
||||
def_map = &ext;
|
||||
}
|
||||
|
||||
// Go up the module tree but skip block modules as `super` always refers to the
|
||||
// nearest non-block module.
|
||||
for _ in 0..lvl {
|
||||
// Loop invariant: at the beginning of each loop, `local_id` must refer to a
|
||||
// non-block module.
|
||||
if let Some(parent) = def_map.modules[local_id].parent {
|
||||
local_id = parent;
|
||||
if def_map.module_id(local_id).is_block_module() {
|
||||
(ext, local_id) =
|
||||
adjust_to_nearest_non_block_module(db, def_map, local_id);
|
||||
def_map = &ext;
|
||||
}
|
||||
} else {
|
||||
stdx::always!(def_map.block.is_none());
|
||||
tracing::debug!("super path in root module");
|
||||
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve `self` to the containing crate-rooted module if we're a block
|
||||
self.with_ancestor_maps(db, module, &mut |def_map, module| {
|
||||
if def_map.block.is_some() {
|
||||
None // keep ascending
|
||||
} else {
|
||||
Some(PerNs::types(def_map.module_id(module).into(), Visibility::Public))
|
||||
}
|
||||
})
|
||||
.expect("block DefMap not rooted in crate DefMap")
|
||||
let module = def_map.module_id(local_id);
|
||||
stdx::never!(module.is_block_module());
|
||||
|
||||
if self.block != def_map.block {
|
||||
// If we have a different `DefMap` from `self` (the orignal `DefMap` we started
|
||||
// with), resolve the remaining path segments in that `DefMap`.
|
||||
let path =
|
||||
ModPath::from_segments(PathKind::Super(0), path.segments().iter().cloned());
|
||||
return def_map.resolve_path_fp_with_macro(
|
||||
db,
|
||||
mode,
|
||||
local_id,
|
||||
&path,
|
||||
shadow,
|
||||
expected_macro_subns,
|
||||
);
|
||||
}
|
||||
|
||||
PerNs::types(module.into(), Visibility::Public)
|
||||
}
|
||||
PathKind::Abs => {
|
||||
// 2018-style absolute path -- only extern prelude
|
||||
@ -508,3 +514,27 @@ impl DefMap {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a block module, returns its nearest non-block module and the `DefMap` it blongs to.
|
||||
fn adjust_to_nearest_non_block_module(
|
||||
db: &dyn DefDatabase,
|
||||
def_map: &DefMap,
|
||||
mut local_id: LocalModuleId,
|
||||
) -> (Arc<DefMap>, LocalModuleId) {
|
||||
// INVARIANT: `local_id` in `def_map` must be a block module.
|
||||
stdx::always!(def_map.module_id(local_id).is_block_module());
|
||||
|
||||
let mut ext;
|
||||
// This needs to be a local variable due to our mighty lifetime.
|
||||
let mut def_map = def_map;
|
||||
loop {
|
||||
let BlockInfo { parent, .. } = def_map.block.expect("block module without parent module");
|
||||
|
||||
ext = parent.def_map(db, def_map.krate);
|
||||
def_map = &ext;
|
||||
local_id = parent.local_id;
|
||||
if !parent.is_block_module() {
|
||||
return (ext, local_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ pub enum Path {
|
||||
/// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
|
||||
generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
|
||||
},
|
||||
/// A link to a lang item. It is used in desugaring of things like `x?`. We can show these
|
||||
/// 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),
|
||||
}
|
||||
@ -135,10 +135,7 @@ impl Path {
|
||||
|
||||
pub fn segments(&self) -> PathSegments<'_> {
|
||||
let Path::Normal { mod_path, generic_args, .. } = self else {
|
||||
return PathSegments {
|
||||
segments: &[],
|
||||
generic_args: None,
|
||||
};
|
||||
return PathSegments { segments: &[], generic_args: None };
|
||||
};
|
||||
let s =
|
||||
PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
|
||||
|
@ -74,8 +74,8 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
|
||||
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
||||
Some(trait_ref) => {
|
||||
let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
|
||||
Path::from_src(trait_ref.path()?, ctx)? else
|
||||
{
|
||||
Path::from_src(trait_ref.path()?, ctx)?
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
let num_segments = mod_path.segments().len();
|
||||
|
@ -12,8 +12,8 @@ use crate::{
|
||||
};
|
||||
|
||||
pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
|
||||
if let Path::LangItem(x) = path {
|
||||
return write!(buf, "$lang_item::{x:?}");
|
||||
if let Path::LangItem(it) = path {
|
||||
return write!(buf, "$lang_item::{it:?}");
|
||||
}
|
||||
match path.type_anchor() {
|
||||
Some(anchor) => {
|
||||
|
@ -22,10 +22,10 @@ use crate::{
|
||||
per_ns::PerNs,
|
||||
visibility::{RawVisibility, Visibility},
|
||||
AdtId, AssocItemId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId,
|
||||
EnumVariantId, ExternBlockId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId,
|
||||
ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId,
|
||||
ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
|
||||
TypeOrConstParamId, TypeOwnerId, TypeParamId, VariantId,
|
||||
EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId,
|
||||
HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId,
|
||||
MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId,
|
||||
TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, VariantId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -186,12 +186,12 @@ impl Resolver {
|
||||
Path::LangItem(l) => {
|
||||
return Some((
|
||||
match *l {
|
||||
LangItemTarget::Union(x) => TypeNs::AdtId(x.into()),
|
||||
LangItemTarget::TypeAlias(x) => TypeNs::TypeAliasId(x),
|
||||
LangItemTarget::Struct(x) => TypeNs::AdtId(x.into()),
|
||||
LangItemTarget::EnumVariant(x) => TypeNs::EnumVariantId(x),
|
||||
LangItemTarget::EnumId(x) => TypeNs::AdtId(x.into()),
|
||||
LangItemTarget::Trait(x) => TypeNs::TraitId(x),
|
||||
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,
|
||||
@ -273,10 +273,10 @@ impl Resolver {
|
||||
Path::Normal { mod_path, .. } => mod_path,
|
||||
Path::LangItem(l) => {
|
||||
return Some(ResolveValueResult::ValueNs(match *l {
|
||||
LangItemTarget::Function(x) => ValueNs::FunctionId(x),
|
||||
LangItemTarget::Static(x) => ValueNs::StaticId(x),
|
||||
LangItemTarget::Struct(x) => ValueNs::StructId(x),
|
||||
LangItemTarget::EnumVariant(x) => ValueNs::EnumVariantId(x),
|
||||
LangItemTarget::Function(it) => ValueNs::FunctionId(it),
|
||||
LangItemTarget::Static(it) => ValueNs::StaticId(it),
|
||||
LangItemTarget::Struct(it) => ValueNs::StructId(it),
|
||||
LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it),
|
||||
LangItemTarget::Union(_)
|
||||
| LangItemTarget::ImplDef(_)
|
||||
| LangItemTarget::TypeAlias(_)
|
||||
@ -425,14 +425,14 @@ impl Resolver {
|
||||
/// The shadowing is accounted for: in
|
||||
///
|
||||
/// ```
|
||||
/// let x = 92;
|
||||
/// let it = 92;
|
||||
/// {
|
||||
/// let x = 92;
|
||||
/// let it = 92;
|
||||
/// $0
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// there will be only one entry for `x` in the result.
|
||||
/// there will be only one entry for `it` in the result.
|
||||
///
|
||||
/// The result is ordered *roughly* from the innermost scope to the
|
||||
/// outermost: when the name is introduced in two namespaces in two scopes,
|
||||
@ -1018,20 +1018,26 @@ impl HasResolver for ExternBlockId {
|
||||
}
|
||||
}
|
||||
|
||||
impl HasResolver for ExternCrateId {
|
||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||
self.lookup(db).container.resolver(db)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasResolver for TypeOwnerId {
|
||||
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
|
||||
match self {
|
||||
TypeOwnerId::FunctionId(x) => x.resolver(db),
|
||||
TypeOwnerId::StaticId(x) => x.resolver(db),
|
||||
TypeOwnerId::ConstId(x) => x.resolver(db),
|
||||
TypeOwnerId::InTypeConstId(x) => x.lookup(db).owner.resolver(db),
|
||||
TypeOwnerId::AdtId(x) => x.resolver(db),
|
||||
TypeOwnerId::TraitId(x) => x.resolver(db),
|
||||
TypeOwnerId::TraitAliasId(x) => x.resolver(db),
|
||||
TypeOwnerId::TypeAliasId(x) => x.resolver(db),
|
||||
TypeOwnerId::ImplId(x) => x.resolver(db),
|
||||
TypeOwnerId::EnumVariantId(x) => x.resolver(db),
|
||||
TypeOwnerId::ModuleId(x) => x.resolver(db),
|
||||
TypeOwnerId::FunctionId(it) => it.resolver(db),
|
||||
TypeOwnerId::StaticId(it) => it.resolver(db),
|
||||
TypeOwnerId::ConstId(it) => it.resolver(db),
|
||||
TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.resolver(db),
|
||||
TypeOwnerId::AdtId(it) => it.resolver(db),
|
||||
TypeOwnerId::TraitId(it) => it.resolver(db),
|
||||
TypeOwnerId::TraitAliasId(it) => it.resolver(db),
|
||||
TypeOwnerId::TypeAliasId(it) => it.resolver(db),
|
||||
TypeOwnerId::ImplId(it) => it.resolver(db),
|
||||
TypeOwnerId::EnumVariantId(it) => it.resolver(db),
|
||||
TypeOwnerId::ModuleId(it) => it.resolver(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,9 @@ cov-mark = "2.0.0-pre.1"
|
||||
tracing = "0.1.35"
|
||||
either = "1.7.0"
|
||||
rustc-hash = "1.1.0"
|
||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||
la-arena.workspace = true
|
||||
itertools = "0.10.5"
|
||||
hashbrown = { version = "0.12.1", features = [
|
||||
"inline-more",
|
||||
], default-features = false }
|
||||
hashbrown.workspace = true
|
||||
smallvec.workspace = true
|
||||
triomphe.workspace = true
|
||||
|
||||
|
@ -18,47 +18,89 @@ use rustc_hash::FxHasher;
|
||||
use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
|
||||
|
||||
/// `AstId` points to an AST node in a specific file.
|
||||
pub struct FileAstId<N: AstNode> {
|
||||
pub struct FileAstId<N: AstIdNode> {
|
||||
raw: ErasedFileAstId,
|
||||
covariant: PhantomData<fn() -> N>,
|
||||
}
|
||||
|
||||
impl<N: AstNode> Clone for FileAstId<N> {
|
||||
impl<N: AstIdNode> Clone for FileAstId<N> {
|
||||
fn clone(&self) -> FileAstId<N> {
|
||||
*self
|
||||
}
|
||||
}
|
||||
impl<N: AstNode> Copy for FileAstId<N> {}
|
||||
impl<N: AstIdNode> Copy for FileAstId<N> {}
|
||||
|
||||
impl<N: AstNode> PartialEq for FileAstId<N> {
|
||||
impl<N: AstIdNode> PartialEq for FileAstId<N> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.raw == other.raw
|
||||
}
|
||||
}
|
||||
impl<N: AstNode> Eq for FileAstId<N> {}
|
||||
impl<N: AstNode> Hash for FileAstId<N> {
|
||||
impl<N: AstIdNode> Eq for FileAstId<N> {}
|
||||
impl<N: AstIdNode> Hash for FileAstId<N> {
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
self.raw.hash(hasher);
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: AstNode> fmt::Debug for FileAstId<N> {
|
||||
impl<N: AstIdNode> fmt::Debug for FileAstId<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "FileAstId::<{}>({})", type_name::<N>(), self.raw.into_raw())
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: AstNode> FileAstId<N> {
|
||||
impl<N: AstIdNode> FileAstId<N> {
|
||||
// Can't make this a From implementation because of coherence
|
||||
pub fn upcast<M: AstNode>(self) -> FileAstId<M>
|
||||
pub fn upcast<M: AstIdNode>(self) -> FileAstId<M>
|
||||
where
|
||||
N: Into<M>,
|
||||
{
|
||||
FileAstId { raw: self.raw, covariant: PhantomData }
|
||||
}
|
||||
|
||||
pub fn erase(self) -> ErasedFileAstId {
|
||||
self.raw
|
||||
}
|
||||
}
|
||||
|
||||
type ErasedFileAstId = Idx<SyntaxNodePtr>;
|
||||
pub type ErasedFileAstId = Idx<SyntaxNodePtr>;
|
||||
|
||||
pub trait AstIdNode: AstNode {}
|
||||
macro_rules! register_ast_id_node {
|
||||
(impl AstIdNode for $($ident:ident),+ ) => {
|
||||
$(
|
||||
impl AstIdNode for ast::$ident {}
|
||||
)+
|
||||
fn should_alloc_id(kind: syntax::SyntaxKind) -> bool {
|
||||
$(
|
||||
ast::$ident::can_cast(kind)
|
||||
)||+
|
||||
}
|
||||
};
|
||||
}
|
||||
register_ast_id_node! {
|
||||
impl AstIdNode for
|
||||
Item,
|
||||
Adt,
|
||||
Enum,
|
||||
Struct,
|
||||
Union,
|
||||
Const,
|
||||
ExternBlock,
|
||||
ExternCrate,
|
||||
Fn,
|
||||
Impl,
|
||||
Macro,
|
||||
MacroDef,
|
||||
MacroRules,
|
||||
MacroCall,
|
||||
Module,
|
||||
Static,
|
||||
Trait,
|
||||
TraitAlias,
|
||||
TypeAlias,
|
||||
Use,
|
||||
AssocItem, BlockExpr, Variant, RecordField, TupleField, ConstArg
|
||||
}
|
||||
|
||||
/// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
|
||||
#[derive(Default)]
|
||||
@ -92,14 +134,7 @@ impl AstIdMap {
|
||||
// change parent's id. This means that, say, adding a new function to a
|
||||
// trait does not change ids of top-level items, which helps caching.
|
||||
bdfs(node, |it| {
|
||||
let kind = it.kind();
|
||||
if ast::Item::can_cast(kind)
|
||||
|| ast::BlockExpr::can_cast(kind)
|
||||
|| ast::Variant::can_cast(kind)
|
||||
|| ast::RecordField::can_cast(kind)
|
||||
|| ast::TupleField::can_cast(kind)
|
||||
|| ast::ConstArg::can_cast(kind)
|
||||
{
|
||||
if should_alloc_id(it.kind()) {
|
||||
res.alloc(&it);
|
||||
true
|
||||
} else {
|
||||
@ -120,15 +155,19 @@ impl AstIdMap {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
|
||||
pub fn ast_id<N: AstIdNode>(&self, item: &N) -> FileAstId<N> {
|
||||
let raw = self.erased_ast_id(item.syntax());
|
||||
FileAstId { raw, covariant: PhantomData }
|
||||
}
|
||||
|
||||
pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
|
||||
pub fn get<N: AstIdNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
|
||||
AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_raw(&self, id: ErasedFileAstId) -> SyntaxNodePtr {
|
||||
self.arena[id].clone()
|
||||
}
|
||||
|
||||
fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
|
||||
let ptr = SyntaxNodePtr::new(item);
|
||||
let hash = hash_ptr(&ptr);
|
||||
|
@ -35,7 +35,7 @@ macro_rules! register_builtin {
|
||||
|
||||
impl BuiltinAttrExpander {
|
||||
pub fn is_derive(self) -> bool {
|
||||
matches!(self, BuiltinAttrExpander::Derive)
|
||||
matches!(self, BuiltinAttrExpander::Derive | BuiltinAttrExpander::DeriveConst)
|
||||
}
|
||||
pub fn is_test(self) -> bool {
|
||||
matches!(self, BuiltinAttrExpander::Test)
|
||||
@ -50,6 +50,8 @@ register_builtin! {
|
||||
(cfg_accessible, CfgAccessible) => dummy_attr_expand,
|
||||
(cfg_eval, CfgEval) => dummy_attr_expand,
|
||||
(derive, Derive) => derive_attr_expand,
|
||||
// derive const is equivalent to derive for our proposes.
|
||||
(derive_const, DeriveConst) => derive_attr_expand,
|
||||
(global_allocator, GlobalAllocator) => dummy_attr_expand,
|
||||
(test, Test) => dummy_attr_expand,
|
||||
(test_case, TestCase) => dummy_attr_expand
|
||||
|
@ -12,9 +12,7 @@ use crate::{
|
||||
name::{AsName, Name},
|
||||
tt::{self, TokenId},
|
||||
};
|
||||
use syntax::ast::{
|
||||
self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName, HasTypeBounds,
|
||||
};
|
||||
use syntax::ast::{self, AstNode, FieldList, HasAttrs, HasGenericParams, HasName, HasTypeBounds};
|
||||
|
||||
use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult, MacroCallId};
|
||||
|
||||
@ -30,12 +28,13 @@ macro_rules! register_builtin {
|
||||
&self,
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
tt: &ast::Adt,
|
||||
token_map: &TokenMap,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let expander = match *self {
|
||||
$( BuiltinDeriveExpander::$trait => $expand, )*
|
||||
};
|
||||
expander(db, id, tt)
|
||||
expander(db, id, tt, token_map)
|
||||
}
|
||||
|
||||
fn find_by_name(name: &name::Name) -> Option<Self> {
|
||||
@ -72,12 +71,12 @@ enum VariantShape {
|
||||
}
|
||||
|
||||
fn tuple_field_iterator(n: usize) -> impl Iterator<Item = tt::Ident> {
|
||||
(0..n).map(|x| Ident::new(format!("f{x}"), tt::TokenId::unspecified()))
|
||||
(0..n).map(|it| Ident::new(format!("f{it}"), tt::TokenId::unspecified()))
|
||||
}
|
||||
|
||||
impl VariantShape {
|
||||
fn as_pattern(&self, path: tt::Subtree) -> tt::Subtree {
|
||||
self.as_pattern_map(path, |x| quote!(#x))
|
||||
self.as_pattern_map(path, |it| quote!(#it))
|
||||
}
|
||||
|
||||
fn field_names(&self) -> Vec<tt::Ident> {
|
||||
@ -95,17 +94,17 @@ impl VariantShape {
|
||||
) -> tt::Subtree {
|
||||
match self {
|
||||
VariantShape::Struct(fields) => {
|
||||
let fields = fields.iter().map(|x| {
|
||||
let mapped = field_map(x);
|
||||
quote! { #x : #mapped , }
|
||||
let fields = fields.iter().map(|it| {
|
||||
let mapped = field_map(it);
|
||||
quote! { #it : #mapped , }
|
||||
});
|
||||
quote! {
|
||||
#path { ##fields }
|
||||
}
|
||||
}
|
||||
&VariantShape::Tuple(n) => {
|
||||
let fields = tuple_field_iterator(n).map(|x| {
|
||||
let mapped = field_map(&x);
|
||||
let fields = tuple_field_iterator(n).map(|it| {
|
||||
let mapped = field_map(&it);
|
||||
quote! {
|
||||
#mapped ,
|
||||
}
|
||||
@ -118,16 +117,16 @@ impl VariantShape {
|
||||
}
|
||||
}
|
||||
|
||||
fn from(value: Option<FieldList>, token_map: &TokenMap) -> Result<Self, ExpandError> {
|
||||
fn from(tm: &TokenMap, value: Option<FieldList>) -> Result<Self, ExpandError> {
|
||||
let r = match value {
|
||||
None => VariantShape::Unit,
|
||||
Some(FieldList::RecordFieldList(x)) => VariantShape::Struct(
|
||||
x.fields()
|
||||
.map(|x| x.name())
|
||||
.map(|x| name_to_token(token_map, x))
|
||||
Some(FieldList::RecordFieldList(it)) => VariantShape::Struct(
|
||||
it.fields()
|
||||
.map(|it| it.name())
|
||||
.map(|it| name_to_token(tm, it))
|
||||
.collect::<Result<_, _>>()?,
|
||||
),
|
||||
Some(FieldList::TupleFieldList(x)) => VariantShape::Tuple(x.fields().count()),
|
||||
Some(FieldList::TupleFieldList(it)) => VariantShape::Tuple(it.fields().count()),
|
||||
};
|
||||
Ok(r)
|
||||
}
|
||||
@ -141,7 +140,7 @@ enum AdtShape {
|
||||
|
||||
impl AdtShape {
|
||||
fn as_pattern(&self, name: &tt::Ident) -> Vec<tt::Subtree> {
|
||||
self.as_pattern_map(name, |x| quote!(#x))
|
||||
self.as_pattern_map(name, |it| quote!(#it))
|
||||
}
|
||||
|
||||
fn field_names(&self) -> Vec<Vec<tt::Ident>> {
|
||||
@ -190,32 +189,19 @@ struct BasicAdtInfo {
|
||||
associated_types: Vec<tt::Subtree>,
|
||||
}
|
||||
|
||||
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
||||
let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems);
|
||||
let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
|
||||
debug!("derive node didn't parse");
|
||||
ExpandError::other("invalid item definition")
|
||||
})?;
|
||||
let item = macro_items.items().next().ok_or_else(|| {
|
||||
debug!("no module item parsed");
|
||||
ExpandError::other("no item found")
|
||||
})?;
|
||||
let adt = ast::Adt::cast(item.syntax().clone()).ok_or_else(|| {
|
||||
debug!("expected adt, found: {:?}", item);
|
||||
ExpandError::other("expected struct, enum or union")
|
||||
})?;
|
||||
fn parse_adt(tm: &TokenMap, adt: &ast::Adt) -> Result<BasicAdtInfo, ExpandError> {
|
||||
let (name, generic_param_list, shape) = match &adt {
|
||||
ast::Adt::Struct(it) => (
|
||||
it.name(),
|
||||
it.generic_param_list(),
|
||||
AdtShape::Struct(VariantShape::from(it.field_list(), &token_map)?),
|
||||
AdtShape::Struct(VariantShape::from(tm, it.field_list())?),
|
||||
),
|
||||
ast::Adt::Enum(it) => {
|
||||
let default_variant = it
|
||||
.variant_list()
|
||||
.into_iter()
|
||||
.flat_map(|x| x.variants())
|
||||
.position(|x| x.attrs().any(|x| x.simple_name() == Some("default".into())));
|
||||
.flat_map(|it| it.variants())
|
||||
.position(|it| it.attrs().any(|it| it.simple_name() == Some("default".into())));
|
||||
(
|
||||
it.name(),
|
||||
it.generic_param_list(),
|
||||
@ -224,11 +210,11 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
||||
variants: it
|
||||
.variant_list()
|
||||
.into_iter()
|
||||
.flat_map(|x| x.variants())
|
||||
.map(|x| {
|
||||
.flat_map(|it| it.variants())
|
||||
.map(|it| {
|
||||
Ok((
|
||||
name_to_token(&token_map, x.name())?,
|
||||
VariantShape::from(x.field_list(), &token_map)?,
|
||||
name_to_token(tm, it.name())?,
|
||||
VariantShape::from(tm, it.field_list())?,
|
||||
))
|
||||
})
|
||||
.collect::<Result<_, ExpandError>>()?,
|
||||
@ -246,16 +232,16 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
||||
let name = {
|
||||
let this = param.name();
|
||||
match this {
|
||||
Some(x) => {
|
||||
param_type_set.insert(x.as_name());
|
||||
mbe::syntax_node_to_token_tree(x.syntax()).0
|
||||
Some(it) => {
|
||||
param_type_set.insert(it.as_name());
|
||||
mbe::syntax_node_to_token_tree(it.syntax()).0
|
||||
}
|
||||
None => tt::Subtree::empty(),
|
||||
}
|
||||
};
|
||||
let bounds = match ¶m {
|
||||
ast::TypeOrConstParam::Type(x) => {
|
||||
x.type_bound_list().map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
|
||||
ast::TypeOrConstParam::Type(it) => {
|
||||
it.type_bound_list().map(|it| mbe::syntax_node_to_token_tree(it.syntax()).0)
|
||||
}
|
||||
ast::TypeOrConstParam::Const(_) => None,
|
||||
};
|
||||
@ -296,9 +282,9 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
||||
let name = p.path()?.qualifier()?.as_single_name_ref()?.as_name();
|
||||
param_type_set.contains(&name).then_some(p)
|
||||
})
|
||||
.map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
|
||||
.map(|it| mbe::syntax_node_to_token_tree(it.syntax()).0)
|
||||
.collect();
|
||||
let name_token = name_to_token(&token_map, name)?;
|
||||
let name_token = name_to_token(&tm, name)?;
|
||||
Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types })
|
||||
}
|
||||
|
||||
@ -345,11 +331,12 @@ fn name_to_token(token_map: &TokenMap, name: Option<ast::Name>) -> Result<tt::Id
|
||||
/// where B1, ..., BN are the bounds given by `bounds_paths`. Z is a phantom type, and
|
||||
/// therefore does not get bound by the derived trait.
|
||||
fn expand_simple_derive(
|
||||
tt: &tt::Subtree,
|
||||
tt: &ast::Adt,
|
||||
tm: &TokenMap,
|
||||
trait_path: tt::Subtree,
|
||||
make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let info = match parse_adt(tt) {
|
||||
let info = match parse_adt(tm, tt) {
|
||||
Ok(info) => info,
|
||||
Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
|
||||
};
|
||||
@ -373,10 +360,10 @@ fn expand_simple_derive(
|
||||
})
|
||||
.unzip();
|
||||
|
||||
where_block.extend(info.associated_types.iter().map(|x| {
|
||||
let x = x.clone();
|
||||
where_block.extend(info.associated_types.iter().map(|it| {
|
||||
let it = it.clone();
|
||||
let bound = trait_path.clone();
|
||||
quote! { #x : #bound , }
|
||||
quote! { #it : #bound , }
|
||||
}));
|
||||
|
||||
let name = info.name;
|
||||
@ -405,19 +392,21 @@ fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId) -> tt::TokenTree
|
||||
fn copy_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
tt: &ast::Adt,
|
||||
tm: &TokenMap,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::marker::Copy }, |_| quote! {})
|
||||
expand_simple_derive(tt, tm, quote! { #krate::marker::Copy }, |_| quote! {})
|
||||
}
|
||||
|
||||
fn clone_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
tt: &ast::Adt,
|
||||
tm: &TokenMap,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::clone::Clone }, |adt| {
|
||||
expand_simple_derive(tt, tm, quote! { #krate::clone::Clone }, |adt| {
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
let star = tt::Punct {
|
||||
char: '*',
|
||||
@ -444,7 +433,7 @@ fn clone_expand(
|
||||
}
|
||||
let name = &adt.name;
|
||||
let patterns = adt.shape.as_pattern(name);
|
||||
let exprs = adt.shape.as_pattern_map(name, |x| quote! { #x .clone() });
|
||||
let exprs = adt.shape.as_pattern_map(name, |it| quote! { #it .clone() });
|
||||
let arms = patterns.into_iter().zip(exprs.into_iter()).map(|(pat, expr)| {
|
||||
let fat_arrow = fat_arrow();
|
||||
quote! {
|
||||
@ -479,10 +468,11 @@ fn and_and() -> ::tt::Subtree<TokenId> {
|
||||
fn default_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
tt: &ast::Adt,
|
||||
tm: &TokenMap,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::default::Default }, |adt| {
|
||||
expand_simple_derive(tt, tm, quote! { #krate::default::Default }, |adt| {
|
||||
let body = match &adt.shape {
|
||||
AdtShape::Struct(fields) => {
|
||||
let name = &adt.name;
|
||||
@ -518,16 +508,17 @@ fn default_expand(
|
||||
fn debug_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
tt: &ast::Adt,
|
||||
tm: &TokenMap,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::fmt::Debug }, |adt| {
|
||||
expand_simple_derive(tt, tm, quote! { #krate::fmt::Debug }, |adt| {
|
||||
let for_variant = |name: String, v: &VariantShape| match v {
|
||||
VariantShape::Struct(fields) => {
|
||||
let for_fields = fields.iter().map(|x| {
|
||||
let x_string = x.to_string();
|
||||
let for_fields = fields.iter().map(|it| {
|
||||
let x_string = it.to_string();
|
||||
quote! {
|
||||
.field(#x_string, & #x)
|
||||
.field(#x_string, & #it)
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
@ -535,9 +526,9 @@ fn debug_expand(
|
||||
}
|
||||
}
|
||||
VariantShape::Tuple(n) => {
|
||||
let for_fields = tuple_field_iterator(*n).map(|x| {
|
||||
let for_fields = tuple_field_iterator(*n).map(|it| {
|
||||
quote! {
|
||||
.field( & #x)
|
||||
.field( & #it)
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
@ -598,10 +589,11 @@ fn debug_expand(
|
||||
fn hash_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
tt: &ast::Adt,
|
||||
tm: &TokenMap,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::hash::Hash }, |adt| {
|
||||
expand_simple_derive(tt, tm, quote! { #krate::hash::Hash }, |adt| {
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
// FIXME: Return expand error here
|
||||
return quote! {};
|
||||
@ -621,7 +613,7 @@ fn hash_expand(
|
||||
let arms = adt.shape.as_pattern(&adt.name).into_iter().zip(adt.shape.field_names()).map(
|
||||
|(pat, names)| {
|
||||
let expr = {
|
||||
let it = names.iter().map(|x| quote! { #x . hash(ra_expand_state); });
|
||||
let it = names.iter().map(|it| quote! { #it . hash(ra_expand_state); });
|
||||
quote! { {
|
||||
##it
|
||||
} }
|
||||
@ -632,9 +624,14 @@ fn hash_expand(
|
||||
}
|
||||
},
|
||||
);
|
||||
let check_discriminant = if matches!(&adt.shape, AdtShape::Enum { .. }) {
|
||||
quote! { #krate::mem::discriminant(self).hash(ra_expand_state); }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
quote! {
|
||||
fn hash<H: #krate::hash::Hasher>(&self, ra_expand_state: &mut H) {
|
||||
#krate::mem::discriminant(self).hash(ra_expand_state);
|
||||
#check_discriminant
|
||||
match self {
|
||||
##arms
|
||||
}
|
||||
@ -646,19 +643,21 @@ fn hash_expand(
|
||||
fn eq_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
tt: &ast::Adt,
|
||||
tm: &TokenMap,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::cmp::Eq }, |_| quote! {})
|
||||
expand_simple_derive(tt, tm, quote! { #krate::cmp::Eq }, |_| quote! {})
|
||||
}
|
||||
|
||||
fn partial_eq_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
tt: &ast::Adt,
|
||||
tm: &TokenMap,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }, |adt| {
|
||||
expand_simple_derive(tt, tm, quote! { #krate::cmp::PartialEq }, |adt| {
|
||||
if matches!(adt.shape, AdtShape::Union) {
|
||||
// FIXME: Return expand error here
|
||||
return quote! {};
|
||||
@ -674,9 +673,9 @@ fn partial_eq_expand(
|
||||
quote!(true)
|
||||
}
|
||||
[first, rest @ ..] => {
|
||||
let rest = rest.iter().map(|x| {
|
||||
let t1 = Ident::new(format!("{}_self", x.text), x.span);
|
||||
let t2 = Ident::new(format!("{}_other", x.text), x.span);
|
||||
let rest = rest.iter().map(|it| {
|
||||
let t1 = Ident::new(format!("{}_self", it.text), it.span);
|
||||
let t2 = Ident::new(format!("{}_other", it.text), it.span);
|
||||
let and_and = and_and();
|
||||
quote!(#and_and #t1 .eq( #t2 ))
|
||||
});
|
||||
@ -708,12 +707,12 @@ fn self_and_other_patterns(
|
||||
adt: &BasicAdtInfo,
|
||||
name: &tt::Ident,
|
||||
) -> (Vec<tt::Subtree>, Vec<tt::Subtree>) {
|
||||
let self_patterns = adt.shape.as_pattern_map(name, |x| {
|
||||
let t = Ident::new(format!("{}_self", x.text), x.span);
|
||||
let self_patterns = adt.shape.as_pattern_map(name, |it| {
|
||||
let t = Ident::new(format!("{}_self", it.text), it.span);
|
||||
quote!(#t)
|
||||
});
|
||||
let other_patterns = adt.shape.as_pattern_map(name, |x| {
|
||||
let t = Ident::new(format!("{}_other", x.text), x.span);
|
||||
let other_patterns = adt.shape.as_pattern_map(name, |it| {
|
||||
let t = Ident::new(format!("{}_other", it.text), it.span);
|
||||
quote!(#t)
|
||||
});
|
||||
(self_patterns, other_patterns)
|
||||
@ -722,10 +721,11 @@ fn self_and_other_patterns(
|
||||
fn ord_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
tt: &ast::Adt,
|
||||
tm: &TokenMap,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::cmp::Ord }, |adt| {
|
||||
expand_simple_derive(tt, tm, quote! { #krate::cmp::Ord }, |adt| {
|
||||
fn compare(
|
||||
krate: &tt::TokenTree,
|
||||
left: tt::Subtree,
|
||||
@ -747,9 +747,6 @@ fn ord_expand(
|
||||
// FIXME: Return expand error here
|
||||
return quote!();
|
||||
}
|
||||
let left = quote!(#krate::intrinsics::discriminant_value(self));
|
||||
let right = quote!(#krate::intrinsics::discriminant_value(other));
|
||||
|
||||
let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name);
|
||||
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
|
||||
|(pat1, pat2, fields)| {
|
||||
@ -764,17 +761,17 @@ fn ord_expand(
|
||||
},
|
||||
);
|
||||
let fat_arrow = fat_arrow();
|
||||
let body = compare(
|
||||
krate,
|
||||
left,
|
||||
right,
|
||||
quote! {
|
||||
match (self, other) {
|
||||
##arms
|
||||
_unused #fat_arrow #krate::cmp::Ordering::Equal
|
||||
}
|
||||
},
|
||||
);
|
||||
let mut body = quote! {
|
||||
match (self, other) {
|
||||
##arms
|
||||
_unused #fat_arrow #krate::cmp::Ordering::Equal
|
||||
}
|
||||
};
|
||||
if matches!(&adt.shape, AdtShape::Enum { .. }) {
|
||||
let left = quote!(#krate::intrinsics::discriminant_value(self));
|
||||
let right = quote!(#krate::intrinsics::discriminant_value(other));
|
||||
body = compare(krate, left, right, body);
|
||||
}
|
||||
quote! {
|
||||
fn cmp(&self, other: &Self) -> #krate::cmp::Ordering {
|
||||
#body
|
||||
@ -786,10 +783,11 @@ fn ord_expand(
|
||||
fn partial_ord_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
tt: &ast::Adt,
|
||||
tm: &TokenMap,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let krate = &find_builtin_crate(db, id);
|
||||
expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }, |adt| {
|
||||
expand_simple_derive(tt, tm, quote! { #krate::cmp::PartialOrd }, |adt| {
|
||||
fn compare(
|
||||
krate: &tt::TokenTree,
|
||||
left: tt::Subtree,
|
||||
|
@ -339,7 +339,7 @@ fn format_args_expand_general(
|
||||
parts.push(mem::take(&mut last_part));
|
||||
let arg_tree = if argument.is_empty() {
|
||||
match args.next() {
|
||||
Some(x) => x,
|
||||
Some(it) => it,
|
||||
None => {
|
||||
err = Some(mbe::ExpandError::NoMatchingRule.into());
|
||||
tt::Subtree::empty()
|
||||
@ -361,7 +361,7 @@ fn format_args_expand_general(
|
||||
quote!(::core::fmt::Display::fmt)
|
||||
}
|
||||
};
|
||||
arg_tts.push(quote! { ::core::fmt::Argument::new(&(#arg_tree), #formatter), });
|
||||
arg_tts.push(quote! { ::core::fmt::ArgumentV1::new(&(#arg_tree), #formatter), });
|
||||
}
|
||||
'}' => {
|
||||
if format_iter.peek() == Some(&'}') {
|
||||
@ -378,11 +378,11 @@ fn format_args_expand_general(
|
||||
if !last_part.is_empty() {
|
||||
parts.push(last_part);
|
||||
}
|
||||
let part_tts = parts.into_iter().map(|x| {
|
||||
let part_tts = parts.into_iter().map(|it| {
|
||||
let text = if let Some(raw) = &raw_sharps {
|
||||
format!("r{raw}\"{}\"{raw}", x).into()
|
||||
format!("r{raw}\"{}\"{raw}", it).into()
|
||||
} else {
|
||||
format!("\"{}\"", x).into()
|
||||
format!("\"{}\"", it).into()
|
||||
};
|
||||
let l = tt::Literal { span: tt::TokenId::unspecified(), text };
|
||||
quote!(#l ,)
|
||||
@ -574,7 +574,7 @@ fn concat_bytes_expand(
|
||||
syntax::SyntaxKind::BYTE => bytes.push(token.text().to_string()),
|
||||
syntax::SyntaxKind::BYTE_STRING => {
|
||||
let components = unquote_byte_string(lit).unwrap_or_default();
|
||||
components.into_iter().for_each(|x| bytes.push(x.to_string()));
|
||||
components.into_iter().for_each(|it| bytes.push(it.to_string()));
|
||||
}
|
||||
_ => {
|
||||
err.get_or_insert(mbe::ExpandError::UnexpectedToken.into());
|
||||
@ -692,7 +692,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: Some(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)?;
|
||||
|
@ -1,9 +1,9 @@
|
||||
//! Defines database & queries for macro expansion.
|
||||
|
||||
use base_db::{salsa, Edition, SourceDatabase};
|
||||
use base_db::{salsa, CrateId, Edition, SourceDatabase};
|
||||
use either::Either;
|
||||
use limit::Limit;
|
||||
use mbe::syntax_node_to_token_tree;
|
||||
use mbe::{syntax_node_to_token_tree, ValueResult};
|
||||
use rustc_hash::FxHashSet;
|
||||
use syntax::{
|
||||
ast::{self, HasAttrs, HasDocComments},
|
||||
@ -13,7 +13,7 @@ use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion,
|
||||
builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, BuiltinAttrExpander,
|
||||
builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, AstId, BuiltinAttrExpander,
|
||||
BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, ExpandError, ExpandResult,
|
||||
ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
|
||||
MacroDefKind, MacroFile, ProcMacroExpander,
|
||||
@ -27,62 +27,68 @@ use crate::{
|
||||
/// Actual max for `analysis-stats .` at some point: 30672.
|
||||
static TOKEN_LIMIT: Limit = Limit::new(1_048_576);
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
/// Old-style `macro_rules` or the new macros 2.0
|
||||
pub struct DeclarativeMacroExpander {
|
||||
pub mac: mbe::DeclarativeMacro,
|
||||
pub def_site_token_map: mbe::TokenMap,
|
||||
}
|
||||
|
||||
impl DeclarativeMacroExpander {
|
||||
pub fn expand(&self, tt: tt::Subtree) -> ExpandResult<tt::Subtree> {
|
||||
match self.mac.err() {
|
||||
Some(e) => ExpandResult::new(
|
||||
tt::Subtree::empty(),
|
||||
ExpandError::other(format!("invalid macro definition: {e}")),
|
||||
),
|
||||
None => self.mac.expand(tt).map_err(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_id_down(&self, token_id: tt::TokenId) -> tt::TokenId {
|
||||
self.mac.map_id_down(token_id)
|
||||
}
|
||||
|
||||
pub fn map_id_up(&self, token_id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
|
||||
self.mac.map_id_up(token_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum TokenExpander {
|
||||
/// Old-style `macro_rules` or the new macros 2.0
|
||||
DeclarativeMacro { mac: mbe::DeclarativeMacro, def_site_token_map: mbe::TokenMap },
|
||||
DeclarativeMacro(Arc<DeclarativeMacroExpander>),
|
||||
/// Stuff like `line!` and `file!`.
|
||||
Builtin(BuiltinFnLikeExpander),
|
||||
BuiltIn(BuiltinFnLikeExpander),
|
||||
/// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.)
|
||||
BuiltinEager(EagerExpander),
|
||||
BuiltInEager(EagerExpander),
|
||||
/// `global_allocator` and such.
|
||||
BuiltinAttr(BuiltinAttrExpander),
|
||||
BuiltInAttr(BuiltinAttrExpander),
|
||||
/// `derive(Copy)` and such.
|
||||
BuiltinDerive(BuiltinDeriveExpander),
|
||||
BuiltInDerive(BuiltinDeriveExpander),
|
||||
/// The thing we love the most here in rust-analyzer -- procedural macros.
|
||||
ProcMacro(ProcMacroExpander),
|
||||
}
|
||||
|
||||
// FIXME: Get rid of these methods
|
||||
impl TokenExpander {
|
||||
fn expand(
|
||||
&self,
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
match self {
|
||||
TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt).map_err(Into::into),
|
||||
TokenExpander::Builtin(it) => it.expand(db, id, tt).map_err(Into::into),
|
||||
TokenExpander::BuiltinEager(it) => it.expand(db, id, tt).map_err(Into::into),
|
||||
TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt),
|
||||
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
|
||||
TokenExpander::ProcMacro(_) => {
|
||||
// We store the result in salsa db to prevent non-deterministic behavior in
|
||||
// some proc-macro implementation
|
||||
// See #4315 for details
|
||||
db.expand_proc_macro(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
|
||||
match self {
|
||||
TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_down(id),
|
||||
TokenExpander::Builtin(..)
|
||||
| TokenExpander::BuiltinEager(..)
|
||||
| TokenExpander::BuiltinAttr(..)
|
||||
| TokenExpander::BuiltinDerive(..)
|
||||
TokenExpander::DeclarativeMacro(expander) => expander.map_id_down(id),
|
||||
TokenExpander::BuiltIn(..)
|
||||
| TokenExpander::BuiltInEager(..)
|
||||
| TokenExpander::BuiltInAttr(..)
|
||||
| TokenExpander::BuiltInDerive(..)
|
||||
| TokenExpander::ProcMacro(..) => id,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
|
||||
match self {
|
||||
TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_up(id),
|
||||
TokenExpander::Builtin(..)
|
||||
| TokenExpander::BuiltinEager(..)
|
||||
| TokenExpander::BuiltinAttr(..)
|
||||
| TokenExpander::BuiltinDerive(..)
|
||||
TokenExpander::DeclarativeMacro(expander) => expander.map_id_up(id),
|
||||
TokenExpander::BuiltIn(..)
|
||||
| TokenExpander::BuiltInEager(..)
|
||||
| TokenExpander::BuiltInAttr(..)
|
||||
| TokenExpander::BuiltInDerive(..)
|
||||
| TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
|
||||
}
|
||||
}
|
||||
@ -118,14 +124,26 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||
fn macro_arg(
|
||||
&self,
|
||||
id: MacroCallId,
|
||||
) -> Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>>;
|
||||
) -> ValueResult<
|
||||
Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>>,
|
||||
Arc<Box<[SyntaxError]>>,
|
||||
>;
|
||||
/// Extracts syntax node, corresponding to a macro call. That's a firewall
|
||||
/// query, only typing in the macro call itself changes the returned
|
||||
/// subtree.
|
||||
fn macro_arg_text(&self, id: MacroCallId) -> Option<GreenNode>;
|
||||
/// Gets the expander for this macro. This compiles declarative macros, and
|
||||
/// just fetches procedural ones.
|
||||
fn macro_def(&self, id: MacroDefId) -> Result<Arc<TokenExpander>, mbe::ParseError>;
|
||||
fn macro_arg_node(
|
||||
&self,
|
||||
id: MacroCallId,
|
||||
) -> ValueResult<Option<GreenNode>, Arc<Box<[SyntaxError]>>>;
|
||||
/// Fetches the expander for this macro.
|
||||
#[salsa::transparent]
|
||||
fn macro_expander(&self, id: MacroDefId) -> TokenExpander;
|
||||
/// Fetches (and compiles) the expander of this decl macro.
|
||||
fn decl_macro_expander(
|
||||
&self,
|
||||
def_crate: CrateId,
|
||||
id: AstId<ast::Macro>,
|
||||
) -> Arc<DeclarativeMacroExpander>;
|
||||
|
||||
/// Expand macro call to a token tree.
|
||||
// This query is LRU cached
|
||||
@ -141,8 +159,8 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||
/// Special case of the previous query for procedural macros. We can't LRU
|
||||
/// proc macros, since they are not deterministic in general, and
|
||||
/// non-determinism breaks salsa in a very, very, very bad way.
|
||||
/// @edwin0cheng heroically debugged this once!
|
||||
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<tt::Subtree>;
|
||||
/// @edwin0cheng heroically debugged this once! See #4315 for details
|
||||
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<Arc<tt::Subtree>>;
|
||||
/// Firewall query that returns the errors from the `parse_macro_expansion` query.
|
||||
fn parse_macro_expansion_error(
|
||||
&self,
|
||||
@ -163,7 +181,6 @@ pub fn expand_speculative(
|
||||
token_to_map: SyntaxToken,
|
||||
) -> Option<(SyntaxNode, SyntaxToken)> {
|
||||
let loc = db.lookup_intern_macro_call(actual_macro_call);
|
||||
let macro_def = db.macro_def(loc.def).ok()?;
|
||||
let token_range = token_to_map.text_range();
|
||||
|
||||
// Build the subtree and token mapping for the speculative args
|
||||
@ -221,7 +238,12 @@ pub fn expand_speculative(
|
||||
None => {
|
||||
let range = token_range.checked_sub(speculative_args.text_range().start())?;
|
||||
let token_id = spec_args_tmap.token_by_range(range)?;
|
||||
macro_def.map_id_down(token_id)
|
||||
match loc.def.kind {
|
||||
MacroDefKind::Declarative(it) => {
|
||||
db.decl_macro_expander(loc.krate, it).map_id_down(token_id)
|
||||
}
|
||||
_ => token_id,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -235,7 +257,17 @@ pub fn expand_speculative(
|
||||
MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => {
|
||||
pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?)
|
||||
}
|
||||
_ => macro_def.expand(db, actual_macro_call, &tt),
|
||||
MacroDefKind::BuiltInDerive(expander, ..) => {
|
||||
// this cast is a bit sus, can we avoid losing the typedness here?
|
||||
let adt = ast::Adt::cast(speculative_args.clone()).unwrap();
|
||||
expander.expand(db, actual_macro_call, &adt, &spec_args_tmap)
|
||||
}
|
||||
MacroDefKind::Declarative(it) => db.decl_macro_expander(loc.krate, it).expand(tt),
|
||||
MacroDefKind::BuiltIn(it, _) => it.expand(db, actual_macro_call, &tt).map_err(Into::into),
|
||||
MacroDefKind::BuiltInEager(it, _) => {
|
||||
it.expand(db, actual_macro_call, &tt).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt),
|
||||
};
|
||||
|
||||
let expand_to = macro_expand_to(db, actual_macro_call);
|
||||
@ -297,17 +329,31 @@ fn parse_macro_expansion(
|
||||
ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
|
||||
}
|
||||
|
||||
fn parse_macro_expansion_error(
|
||||
db: &dyn ExpandDatabase,
|
||||
macro_call_id: MacroCallId,
|
||||
) -> ExpandResult<Box<[SyntaxError]>> {
|
||||
db.parse_macro_expansion(MacroFile { macro_call_id })
|
||||
.map(|it| it.0.errors().to_vec().into_boxed_slice())
|
||||
}
|
||||
|
||||
fn macro_arg(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
) -> Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>> {
|
||||
) -> ValueResult<
|
||||
Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>>,
|
||||
Arc<Box<[SyntaxError]>>,
|
||||
> {
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
|
||||
if let Some(EagerCallInfo { arg, arg_id: Some(_), error: _ }) = loc.eager.as_deref() {
|
||||
return Some(Arc::new((arg.0.clone(), arg.1.clone(), Default::default())));
|
||||
if let Some(EagerCallInfo { arg, arg_id: _, error: _ }) = loc.eager.as_deref() {
|
||||
return ValueResult::ok(Some(Arc::new((arg.0.clone(), arg.1.clone(), Default::default()))));
|
||||
}
|
||||
|
||||
let arg = db.macro_arg_text(id)?;
|
||||
let ValueResult { value, err } = db.macro_arg_node(id);
|
||||
let Some(arg) = value else {
|
||||
return ValueResult { value: None, err };
|
||||
};
|
||||
|
||||
let node = SyntaxNode::new_root(arg);
|
||||
let censor = censor_for_macro_input(&loc, &node);
|
||||
@ -325,9 +371,16 @@ fn macro_arg(
|
||||
// proc macros expect their inputs without parentheses, MBEs expect it with them included
|
||||
tt.delimiter = tt::Delimiter::unspecified();
|
||||
}
|
||||
Some(Arc::new((tt, tmap, fixups.undo_info)))
|
||||
let val = Some(Arc::new((tt, tmap, fixups.undo_info)));
|
||||
match err {
|
||||
Some(err) => ValueResult::new(val, err),
|
||||
None => ValueResult::ok(val),
|
||||
}
|
||||
}
|
||||
|
||||
/// Certain macro calls expect some nodes in the input to be preprocessed away, namely:
|
||||
/// - derives expect all `#[derive(..)]` invocations up to the currently invoked one to be stripped
|
||||
/// - attributes expect the invoking attribute to be stripped
|
||||
fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<SyntaxNode> {
|
||||
// FIXME: handle `cfg_attr`
|
||||
(|| {
|
||||
@ -364,9 +417,44 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn macro_arg_text(db: &dyn ExpandDatabase, id: MacroCallId) -> Option<GreenNode> {
|
||||
fn macro_arg_node(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
) -> ValueResult<Option<GreenNode>, Arc<Box<[SyntaxError]>>> {
|
||||
let err = || -> Arc<Box<[_]>> {
|
||||
Arc::new(Box::new([SyntaxError::new_at_offset(
|
||||
"invalid macro call".to_owned(),
|
||||
syntax::TextSize::from(0),
|
||||
)]))
|
||||
};
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
let arg = loc.kind.arg(db)?;
|
||||
let arg = if let MacroDefKind::BuiltInEager(..) = loc.def.kind {
|
||||
let res = if let Some(EagerCallInfo { arg, .. }) = loc.eager.as_deref() {
|
||||
Some(mbe::token_tree_to_syntax_node(&arg.0, mbe::TopEntryPoint::Expr).0)
|
||||
} else {
|
||||
loc.kind
|
||||
.arg(db)
|
||||
.and_then(|arg| ast::TokenTree::cast(arg.value))
|
||||
.map(|tt| tt.reparse_as_expr().to_syntax())
|
||||
};
|
||||
|
||||
match res {
|
||||
Some(res) if res.errors().is_empty() => res.syntax_node(),
|
||||
Some(res) => {
|
||||
return ValueResult::new(
|
||||
Some(res.syntax_node().green().into()),
|
||||
// Box::<[_]>::from(res.errors()), not stable yet
|
||||
Arc::new(res.errors().to_vec().into_boxed_slice()),
|
||||
);
|
||||
}
|
||||
None => return ValueResult::only_err(err()),
|
||||
}
|
||||
} else {
|
||||
match loc.kind.arg(db) {
|
||||
Some(res) => res.value,
|
||||
None => return ValueResult::only_err(err()),
|
||||
}
|
||||
};
|
||||
if matches!(loc.kind, MacroCallKind::FnLike { .. }) {
|
||||
let first = arg.first_child_or_token().map_or(T![.], |it| it.kind());
|
||||
let last = arg.last_child_or_token().map_or(T![.], |it| it.kind());
|
||||
@ -381,101 +469,146 @@ fn macro_arg_text(db: &dyn ExpandDatabase, id: MacroCallId) -> Option<GreenNode>
|
||||
// Some day, we'll have explicit recursion counters for all
|
||||
// recursive things, at which point this code might be removed.
|
||||
cov_mark::hit!(issue9358_bad_macro_stack_overflow);
|
||||
return None;
|
||||
return ValueResult::only_err(Arc::new(Box::new([SyntaxError::new(
|
||||
"unbalanced token tree".to_owned(),
|
||||
arg.text_range(),
|
||||
)])));
|
||||
}
|
||||
}
|
||||
if let Some(EagerCallInfo { arg, .. }) = loc.eager.as_deref() {
|
||||
Some(
|
||||
mbe::token_tree_to_syntax_node(&arg.0, mbe::TopEntryPoint::Expr)
|
||||
.0
|
||||
.syntax_node()
|
||||
.green()
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
Some(arg.green().into())
|
||||
}
|
||||
ValueResult::ok(Some(arg.green().into()))
|
||||
}
|
||||
|
||||
fn macro_def(
|
||||
fn decl_macro_expander(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroDefId,
|
||||
) -> Result<Arc<TokenExpander>, mbe::ParseError> {
|
||||
def_crate: CrateId,
|
||||
id: AstId<ast::Macro>,
|
||||
) -> Arc<DeclarativeMacroExpander> {
|
||||
let is_2021 = db.crate_graph()[def_crate].edition >= Edition::Edition2021;
|
||||
let (mac, def_site_token_map) = match id.to_node(db) {
|
||||
ast::Macro::MacroRules(macro_rules) => match macro_rules.token_tree() {
|
||||
Some(arg) => {
|
||||
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
|
||||
let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021);
|
||||
(mac, def_site_token_map)
|
||||
}
|
||||
None => (
|
||||
mbe::DeclarativeMacro::from_err(
|
||||
mbe::ParseError::Expected("expected a token tree".into()),
|
||||
is_2021,
|
||||
),
|
||||
Default::default(),
|
||||
),
|
||||
},
|
||||
ast::Macro::MacroDef(macro_def) => match macro_def.body() {
|
||||
Some(arg) => {
|
||||
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
|
||||
let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021);
|
||||
(mac, def_site_token_map)
|
||||
}
|
||||
None => (
|
||||
mbe::DeclarativeMacro::from_err(
|
||||
mbe::ParseError::Expected("expected a token tree".into()),
|
||||
is_2021,
|
||||
),
|
||||
Default::default(),
|
||||
),
|
||||
},
|
||||
};
|
||||
Arc::new(DeclarativeMacroExpander { mac, def_site_token_map })
|
||||
}
|
||||
|
||||
fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander {
|
||||
match id.kind {
|
||||
MacroDefKind::Declarative(ast_id) => {
|
||||
let is_2021 = db.crate_graph()[id.krate].edition >= Edition::Edition2021;
|
||||
let (mac, def_site_token_map) = match ast_id.to_node(db) {
|
||||
ast::Macro::MacroRules(macro_rules) => {
|
||||
let arg = macro_rules
|
||||
.token_tree()
|
||||
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
|
||||
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
|
||||
let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021)?;
|
||||
(mac, def_site_token_map)
|
||||
}
|
||||
ast::Macro::MacroDef(macro_def) => {
|
||||
let arg = macro_def
|
||||
.body()
|
||||
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
|
||||
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
|
||||
let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021)?;
|
||||
(mac, def_site_token_map)
|
||||
}
|
||||
};
|
||||
Ok(Arc::new(TokenExpander::DeclarativeMacro { mac, def_site_token_map }))
|
||||
TokenExpander::DeclarativeMacro(db.decl_macro_expander(id.krate, ast_id))
|
||||
}
|
||||
MacroDefKind::BuiltIn(expander, _) => Ok(Arc::new(TokenExpander::Builtin(expander))),
|
||||
MacroDefKind::BuiltInAttr(expander, _) => {
|
||||
Ok(Arc::new(TokenExpander::BuiltinAttr(expander)))
|
||||
}
|
||||
MacroDefKind::BuiltInDerive(expander, _) => {
|
||||
Ok(Arc::new(TokenExpander::BuiltinDerive(expander)))
|
||||
}
|
||||
MacroDefKind::BuiltInEager(expander, ..) => {
|
||||
Ok(Arc::new(TokenExpander::BuiltinEager(expander)))
|
||||
}
|
||||
MacroDefKind::ProcMacro(expander, ..) => Ok(Arc::new(TokenExpander::ProcMacro(expander))),
|
||||
MacroDefKind::BuiltIn(expander, _) => TokenExpander::BuiltIn(expander),
|
||||
MacroDefKind::BuiltInAttr(expander, _) => TokenExpander::BuiltInAttr(expander),
|
||||
MacroDefKind::BuiltInDerive(expander, _) => TokenExpander::BuiltInDerive(expander),
|
||||
MacroDefKind::BuiltInEager(expander, ..) => TokenExpander::BuiltInEager(expander),
|
||||
MacroDefKind::ProcMacro(expander, ..) => TokenExpander::ProcMacro(expander),
|
||||
}
|
||||
}
|
||||
|
||||
fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
|
||||
let _p = profile::span("macro_expand");
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
if let Some(EagerCallInfo { arg, arg_id: None, error }) = loc.eager.as_deref() {
|
||||
// This is an input expansion for an eager macro. These are already pre-expanded
|
||||
return ExpandResult { value: Arc::new(arg.0.clone()), err: error.clone() };
|
||||
}
|
||||
let expander = match db.macro_def(loc.def) {
|
||||
Ok(it) => it,
|
||||
// FIXME: We should make sure to enforce a variant that invalid macro
|
||||
// definitions do not get expanders that could reach this call path!
|
||||
Err(err) => {
|
||||
return ExpandResult {
|
||||
value: Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
token_trees: vec![],
|
||||
}),
|
||||
err: Some(ExpandError::other(format!("invalid macro definition: {err}"))),
|
||||
}
|
||||
|
||||
let ExpandResult { value: tt, mut err } = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(id),
|
||||
MacroDefKind::BuiltInDerive(expander, ..) => {
|
||||
let arg = db.macro_arg_node(id).value.unwrap();
|
||||
|
||||
let node = SyntaxNode::new_root(arg);
|
||||
let censor = censor_for_macro_input(&loc, &node);
|
||||
let mut fixups = fixup::fixup_syntax(&node);
|
||||
fixups.replace.extend(censor.into_iter().map(|node| (node.into(), Vec::new())));
|
||||
let (tmap, _) = mbe::syntax_node_to_token_map_with_modifications(
|
||||
&node,
|
||||
fixups.token_map,
|
||||
fixups.next_id,
|
||||
fixups.replace,
|
||||
fixups.append,
|
||||
);
|
||||
|
||||
// this cast is a bit sus, can we avoid losing the typedness here?
|
||||
let adt = ast::Adt::cast(node).unwrap();
|
||||
let mut res = expander.expand(db, id, &adt, &tmap);
|
||||
fixup::reverse_fixups(&mut res.value, &tmap, &fixups.undo_info);
|
||||
res
|
||||
}
|
||||
_ => {
|
||||
let ValueResult { value, err } = db.macro_arg(id);
|
||||
let Some(macro_arg) = value else {
|
||||
return ExpandResult {
|
||||
value: Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
token_trees: Vec::new(),
|
||||
}),
|
||||
// FIXME: We should make sure to enforce an invariant that invalid macro
|
||||
// calls do not reach this call path!
|
||||
err: Some(ExpandError::other("invalid token tree")),
|
||||
};
|
||||
};
|
||||
|
||||
let (arg, arg_tm, undo_info) = &*macro_arg;
|
||||
let mut res = match loc.def.kind {
|
||||
MacroDefKind::Declarative(id) => {
|
||||
db.decl_macro_expander(loc.def.krate, id).expand(arg.clone())
|
||||
}
|
||||
MacroDefKind::BuiltIn(it, _) => it.expand(db, id, &arg).map_err(Into::into),
|
||||
// This might look a bit odd, but we do not expand the inputs to eager macros here.
|
||||
// Eager macros inputs are expanded, well, eagerly when we collect the macro calls.
|
||||
// That kind of expansion uses the ast id map of an eager macros input though which goes through
|
||||
// the HirFileId machinery. As eager macro inputs are assigned a macro file id that query
|
||||
// will end up going through here again, whereas we want to just want to inspect the raw input.
|
||||
// As such we just return the input subtree here.
|
||||
MacroDefKind::BuiltInEager(..) if loc.eager.is_none() => {
|
||||
let mut arg = arg.clone();
|
||||
fixup::reverse_fixups(&mut arg, arg_tm, undo_info);
|
||||
|
||||
return ExpandResult {
|
||||
value: Arc::new(arg),
|
||||
err: err.map(|err| {
|
||||
let mut buf = String::new();
|
||||
for err in &**err {
|
||||
use std::fmt::Write;
|
||||
_ = write!(buf, "{}, ", err);
|
||||
}
|
||||
buf.pop();
|
||||
buf.pop();
|
||||
ExpandError::other(buf)
|
||||
}),
|
||||
};
|
||||
}
|
||||
MacroDefKind::BuiltInEager(it, _) => it.expand(db, id, &arg).map_err(Into::into),
|
||||
MacroDefKind::BuiltInAttr(it, _) => it.expand(db, id, &arg),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
fixup::reverse_fixups(&mut res.value, arg_tm, undo_info);
|
||||
res
|
||||
}
|
||||
};
|
||||
let Some(macro_arg) = db.macro_arg(id) else {
|
||||
return ExpandResult {
|
||||
value: Arc::new(
|
||||
tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
token_trees: Vec::new(),
|
||||
},
|
||||
),
|
||||
// FIXME: We should make sure to enforce a variant that invalid macro
|
||||
// calls do not reach this call path!
|
||||
err: Some(ExpandError::other(
|
||||
"invalid token tree"
|
||||
)),
|
||||
};
|
||||
};
|
||||
let (arg_tt, arg_tm, undo_info) = &*macro_arg;
|
||||
let ExpandResult { value: mut tt, mut err } = expander.expand(db, id, arg_tt);
|
||||
|
||||
if let Some(EagerCallInfo { error, .. }) = loc.eager.as_deref() {
|
||||
// FIXME: We should report both errors!
|
||||
@ -483,48 +616,29 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt
|
||||
}
|
||||
|
||||
// Set a hard limit for the expanded tt
|
||||
let count = tt.count();
|
||||
if TOKEN_LIMIT.check(count).is_err() {
|
||||
return ExpandResult {
|
||||
value: Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
token_trees: vec![],
|
||||
}),
|
||||
err: Some(ExpandError::other(format!(
|
||||
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
|
||||
count,
|
||||
TOKEN_LIMIT.inner(),
|
||||
))),
|
||||
};
|
||||
if let Err(value) = check_tt_count(&tt) {
|
||||
return value;
|
||||
}
|
||||
|
||||
fixup::reverse_fixups(&mut tt, arg_tm, undo_info);
|
||||
|
||||
ExpandResult { value: Arc::new(tt), err }
|
||||
}
|
||||
|
||||
fn parse_macro_expansion_error(
|
||||
db: &dyn ExpandDatabase,
|
||||
macro_call_id: MacroCallId,
|
||||
) -> ExpandResult<Box<[SyntaxError]>> {
|
||||
db.parse_macro_expansion(MacroFile { macro_call_id })
|
||||
.map(|it| it.0.errors().to_vec().into_boxed_slice())
|
||||
}
|
||||
|
||||
fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::Subtree> {
|
||||
fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
let Some(macro_arg) = db.macro_arg(id) else {
|
||||
let Some(macro_arg) = db.macro_arg(id).value else {
|
||||
return ExpandResult {
|
||||
value: tt::Subtree {
|
||||
value: Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
token_trees: Vec::new(),
|
||||
},
|
||||
err: Some(ExpandError::other(
|
||||
"invalid token tree"
|
||||
)),
|
||||
}),
|
||||
// FIXME: We should make sure to enforce an invariant that invalid macro
|
||||
// calls do not reach this call path!
|
||||
err: Some(ExpandError::other("invalid token tree")),
|
||||
};
|
||||
};
|
||||
|
||||
let (arg_tt, arg_tm, undo_info) = &*macro_arg;
|
||||
|
||||
let expander = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(expander, ..) => expander,
|
||||
_ => unreachable!(),
|
||||
@ -533,13 +647,23 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<t
|
||||
let attr_arg = match &loc.kind {
|
||||
MacroCallKind::Attr { attr_args, .. } => {
|
||||
let mut attr_args = attr_args.0.clone();
|
||||
mbe::Shift::new(¯o_arg.0).shift_all(&mut attr_args);
|
||||
mbe::Shift::new(arg_tt).shift_all(&mut attr_args);
|
||||
Some(attr_args)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
expander.expand(db, loc.def.krate, loc.krate, ¯o_arg.0, attr_arg.as_ref())
|
||||
let ExpandResult { value: mut tt, err } =
|
||||
expander.expand(db, loc.def.krate, loc.krate, arg_tt, attr_arg.as_ref());
|
||||
|
||||
// Set a hard limit for the expanded tt
|
||||
if let Err(value) = check_tt_count(&tt) {
|
||||
return value;
|
||||
}
|
||||
|
||||
fixup::reverse_fixups(&mut tt, arg_tm, undo_info);
|
||||
|
||||
ExpandResult { value: Arc::new(tt), err }
|
||||
}
|
||||
|
||||
fn hygiene_frame(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<HygieneFrame> {
|
||||
@ -563,3 +687,22 @@ fn token_tree_to_syntax_node(
|
||||
};
|
||||
mbe::token_tree_to_syntax_node(tt, entry_point)
|
||||
}
|
||||
|
||||
fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<Arc<tt::Subtree>>> {
|
||||
let count = tt.count();
|
||||
if TOKEN_LIMIT.check(count).is_err() {
|
||||
Err(ExpandResult {
|
||||
value: Arc::new(tt::Subtree {
|
||||
delimiter: tt::Delimiter::UNSPECIFIED,
|
||||
token_trees: vec![],
|
||||
}),
|
||||
err: Some(ExpandError::other(format!(
|
||||
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
|
||||
count,
|
||||
TOKEN_LIMIT.inner(),
|
||||
))),
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,8 @@
|
||||
//!
|
||||
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
|
||||
use base_db::CrateId;
|
||||
use syntax::{ted, Parse, SyntaxNode};
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::{ted, Parse, SyntaxNode, TextRange, TextSize, WalkEvent};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
@ -38,19 +39,8 @@ pub fn expand_eager_macro_input(
|
||||
def: MacroDefId,
|
||||
resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
|
||||
assert!(matches!(def.kind, MacroDefKind::BuiltInEager(..)));
|
||||
let token_tree = macro_call.value.token_tree();
|
||||
|
||||
let Some(token_tree) = token_tree else {
|
||||
return Ok(ExpandResult { value: None, err:
|
||||
Some(ExpandError::other(
|
||||
"invalid token tree"
|
||||
)),
|
||||
});
|
||||
};
|
||||
let (parsed_args, arg_token_map) = mbe::syntax_node_to_token_tree(token_tree.syntax());
|
||||
|
||||
let ast_map = db.ast_id_map(macro_call.file_id);
|
||||
// the expansion which the ast id map is built upon has no whitespace, so the offsets are wrong as macro_call is from the token tree that has whitespace!
|
||||
let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(¯o_call.value));
|
||||
let expand_to = ExpandTo::from_call_site(¯o_call.value);
|
||||
|
||||
@ -61,41 +51,69 @@ pub fn expand_eager_macro_input(
|
||||
let arg_id = db.intern_macro_call(MacroCallLoc {
|
||||
def,
|
||||
krate,
|
||||
eager: Some(Box::new(EagerCallInfo {
|
||||
arg: Arc::new((parsed_args, arg_token_map)),
|
||||
arg_id: None,
|
||||
error: None,
|
||||
})),
|
||||
eager: None,
|
||||
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
|
||||
});
|
||||
let arg_as_expr = match db.macro_arg_text(arg_id) {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
return Ok(ExpandResult {
|
||||
value: None,
|
||||
err: Some(ExpandError::other("invalid token tree")),
|
||||
})
|
||||
let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } =
|
||||
db.parse_macro_expansion(arg_id.as_macro_file());
|
||||
// we need this map here as the expansion of the eager input fake file loses whitespace ...
|
||||
let mut ws_mapping = FxHashMap::default();
|
||||
if let Some((_, tm, _)) = db.macro_arg(arg_id).value.as_deref() {
|
||||
ws_mapping.extend(tm.entries().filter_map(|(id, range)| {
|
||||
Some((arg_exp_map.first_range_by_token(id, syntax::SyntaxKind::TOMBSTONE)?, range))
|
||||
}));
|
||||
}
|
||||
|
||||
let ExpandResult { value: expanded_eager_input, err } = {
|
||||
eager_macro_recur(
|
||||
db,
|
||||
&Hygiene::new(db, macro_call.file_id),
|
||||
InFile::new(arg_id.as_file(), arg_exp.syntax_node()),
|
||||
krate,
|
||||
resolver,
|
||||
)?
|
||||
};
|
||||
let err = parse_err.or(err);
|
||||
|
||||
let Some((expanded_eager_input, mapping)) = expanded_eager_input else {
|
||||
return Ok(ExpandResult { value: None, err });
|
||||
};
|
||||
|
||||
let og_tmap = mbe::syntax_node_to_token_map(
|
||||
macro_call.value.token_tree().expect("macro_arg_text succeeded").syntax(),
|
||||
);
|
||||
|
||||
let (mut subtree, expanded_eager_input_token_map) =
|
||||
mbe::syntax_node_to_token_tree(&expanded_eager_input);
|
||||
|
||||
// The tokenmap and ids of subtree point into the expanded syntax node, but that is inaccessible from the outside
|
||||
// so we need to remap them to the original input of the eager macro.
|
||||
subtree.visit_ids(&|id| {
|
||||
// Note: we discard all token ids of braces and the like here, but that's not too bad and only a temporary fix
|
||||
|
||||
if let Some(range) =
|
||||
expanded_eager_input_token_map.first_range_by_token(id, syntax::SyntaxKind::TOMBSTONE)
|
||||
{
|
||||
// remap from expanded eager input to eager input expansion
|
||||
if let Some(og_range) = mapping.get(&range) {
|
||||
// remap from eager input expansion to original eager input
|
||||
if let Some(&og_range) = ws_mapping.get(og_range) {
|
||||
if let Some(og_token) = og_tmap.token_by_range(og_range) {
|
||||
return og_token;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let ExpandResult { value: expanded_eager_input, err } = eager_macro_recur(
|
||||
db,
|
||||
&Hygiene::new(db, macro_call.file_id),
|
||||
InFile::new(arg_id.as_file(), SyntaxNode::new_root(arg_as_expr)),
|
||||
krate,
|
||||
resolver,
|
||||
)?;
|
||||
let Some(expanded_eager_input) = expanded_eager_input else {
|
||||
return Ok(ExpandResult { value: None, err })
|
||||
};
|
||||
let (mut subtree, token_map) = mbe::syntax_node_to_token_tree(&expanded_eager_input);
|
||||
tt::TokenId::UNSPECIFIED
|
||||
});
|
||||
subtree.delimiter = crate::tt::Delimiter::unspecified();
|
||||
|
||||
let loc = MacroCallLoc {
|
||||
def,
|
||||
krate,
|
||||
eager: Some(Box::new(EagerCallInfo {
|
||||
arg: Arc::new((subtree, token_map)),
|
||||
arg_id: Some(arg_id),
|
||||
arg: Arc::new((subtree, og_tmap)),
|
||||
arg_id,
|
||||
error: err.clone(),
|
||||
})),
|
||||
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
|
||||
@ -109,19 +127,16 @@ fn lazy_expand(
|
||||
def: &MacroDefId,
|
||||
macro_call: InFile<ast::MacroCall>,
|
||||
krate: CrateId,
|
||||
) -> ExpandResult<InFile<Parse<SyntaxNode>>> {
|
||||
) -> ExpandResult<(InFile<Parse<SyntaxNode>>, Arc<mbe::TokenMap>)> {
|
||||
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value);
|
||||
|
||||
let expand_to = ExpandTo::from_call_site(¯o_call.value);
|
||||
let id = def.as_lazy_macro(
|
||||
db,
|
||||
krate,
|
||||
MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to },
|
||||
);
|
||||
|
||||
let ast_id = macro_call.with_value(ast_id);
|
||||
let id = def.as_lazy_macro(db, krate, MacroCallKind::FnLike { ast_id, expand_to });
|
||||
let macro_file = id.as_macro_file();
|
||||
|
||||
db.parse_macro_expansion(macro_file).map(|parse| InFile::new(macro_file.into(), parse.0))
|
||||
db.parse_macro_expansion(macro_file)
|
||||
.map(|parse| (InFile::new(macro_file.into(), parse.0), parse.1))
|
||||
}
|
||||
|
||||
fn eager_macro_recur(
|
||||
@ -130,18 +145,43 @@ fn eager_macro_recur(
|
||||
curr: InFile<SyntaxNode>,
|
||||
krate: CrateId,
|
||||
macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
||||
) -> Result<ExpandResult<Option<SyntaxNode>>, UnresolvedMacro> {
|
||||
) -> Result<ExpandResult<Option<(SyntaxNode, FxHashMap<TextRange, TextRange>)>>, UnresolvedMacro> {
|
||||
let original = curr.value.clone_for_update();
|
||||
let mut mapping = FxHashMap::default();
|
||||
|
||||
let children = original.descendants().filter_map(ast::MacroCall::cast);
|
||||
let mut replacements = Vec::new();
|
||||
|
||||
// Note: We only report a single error inside of eager expansions
|
||||
let mut error = None;
|
||||
let mut offset = 0i32;
|
||||
let apply_offset = |it: TextSize, offset: i32| {
|
||||
TextSize::from(u32::try_from(offset + u32::from(it) as i32).unwrap_or_default())
|
||||
};
|
||||
let mut children = original.preorder_with_tokens();
|
||||
|
||||
// Collect replacement
|
||||
for child in children {
|
||||
let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
|
||||
while let Some(child) = children.next() {
|
||||
let WalkEvent::Enter(child) = child else { continue };
|
||||
let call = match child {
|
||||
syntax::NodeOrToken::Node(node) => match ast::MacroCall::cast(node) {
|
||||
Some(it) => {
|
||||
children.skip_subtree();
|
||||
it
|
||||
}
|
||||
None => continue,
|
||||
},
|
||||
syntax::NodeOrToken::Token(t) => {
|
||||
mapping.insert(
|
||||
TextRange::new(
|
||||
apply_offset(t.text_range().start(), offset),
|
||||
apply_offset(t.text_range().end(), offset),
|
||||
),
|
||||
t.text_range(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let def = match call.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
|
||||
Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
|
||||
None => {
|
||||
error = Some(ExpandError::other("malformed macro invocation"));
|
||||
@ -153,7 +193,7 @@ fn eager_macro_recur(
|
||||
let ExpandResult { value, err } = match expand_eager_macro_input(
|
||||
db,
|
||||
krate,
|
||||
curr.with_value(child.clone()),
|
||||
curr.with_value(call.clone()),
|
||||
def,
|
||||
macro_resolver,
|
||||
) {
|
||||
@ -161,9 +201,22 @@ fn eager_macro_recur(
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
match value {
|
||||
Some(call) => {
|
||||
Some(call_id) => {
|
||||
let ExpandResult { value, err: err2 } =
|
||||
db.parse_macro_expansion(call.as_macro_file());
|
||||
db.parse_macro_expansion(call_id.as_macro_file());
|
||||
|
||||
let call_tt_start =
|
||||
call.token_tree().unwrap().syntax().text_range().start();
|
||||
let call_start = apply_offset(call.syntax().text_range().start(), offset);
|
||||
if let Some((_, arg_map, _)) = db.macro_arg(call_id).value.as_deref() {
|
||||
mapping.extend(arg_map.entries().filter_map(|(tid, range)| {
|
||||
value
|
||||
.1
|
||||
.first_range_by_token(tid, syntax::SyntaxKind::TOMBSTONE)
|
||||
.map(|r| (r + call_start, range + call_tt_start))
|
||||
}));
|
||||
};
|
||||
|
||||
ExpandResult {
|
||||
value: Some(value.0.syntax_node().clone_for_update()),
|
||||
err: err.or(err2),
|
||||
@ -177,36 +230,61 @@ fn eager_macro_recur(
|
||||
| MacroDefKind::BuiltInAttr(..)
|
||||
| MacroDefKind::BuiltInDerive(..)
|
||||
| MacroDefKind::ProcMacro(..) => {
|
||||
let ExpandResult { value, err } =
|
||||
lazy_expand(db, &def, curr.with_value(child.clone()), krate);
|
||||
let ExpandResult { value: (parse, tm), err } =
|
||||
lazy_expand(db, &def, curr.with_value(call.clone()), krate);
|
||||
let decl_mac = if let MacroDefKind::Declarative(ast_id) = def.kind {
|
||||
Some(db.decl_macro_expander(def.krate, ast_id))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// replace macro inside
|
||||
let hygiene = Hygiene::new(db, value.file_id);
|
||||
let hygiene = Hygiene::new(db, parse.file_id);
|
||||
let ExpandResult { value, err: error } = eager_macro_recur(
|
||||
db,
|
||||
&hygiene,
|
||||
// FIXME: We discard parse errors here
|
||||
value.map(|it| it.syntax_node()),
|
||||
parse.as_ref().map(|it| it.syntax_node()),
|
||||
krate,
|
||||
macro_resolver,
|
||||
)?;
|
||||
let err = err.or(error);
|
||||
ExpandResult { value, err }
|
||||
|
||||
let call_tt_start = call.token_tree().unwrap().syntax().text_range().start();
|
||||
let call_start = apply_offset(call.syntax().text_range().start(), offset);
|
||||
if let Some((_tt, arg_map, _)) = parse
|
||||
.file_id
|
||||
.macro_file()
|
||||
.and_then(|id| db.macro_arg(id.macro_call_id).value)
|
||||
.as_deref()
|
||||
{
|
||||
mapping.extend(arg_map.entries().filter_map(|(tid, range)| {
|
||||
tm.first_range_by_token(
|
||||
decl_mac.as_ref().map(|it| it.map_id_down(tid)).unwrap_or(tid),
|
||||
syntax::SyntaxKind::TOMBSTONE,
|
||||
)
|
||||
.map(|r| (r + call_start, range + call_tt_start))
|
||||
}));
|
||||
};
|
||||
// FIXME: Do we need to re-use _m here?
|
||||
ExpandResult { value: value.map(|(n, _m)| n), err }
|
||||
}
|
||||
};
|
||||
if err.is_some() {
|
||||
error = err;
|
||||
}
|
||||
// check if the whole original syntax is replaced
|
||||
if child.syntax() == &original {
|
||||
return Ok(ExpandResult { value, err: error });
|
||||
if call.syntax() == &original {
|
||||
return Ok(ExpandResult { value: value.zip(Some(mapping)), err: error });
|
||||
}
|
||||
|
||||
if let Some(insert) = value {
|
||||
replacements.push((child, insert));
|
||||
offset += u32::from(insert.text_range().len()) as i32
|
||||
- u32::from(call.syntax().text_range().len()) as i32;
|
||||
replacements.push((call, insert));
|
||||
}
|
||||
}
|
||||
|
||||
replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
|
||||
Ok(ExpandResult { value: Some(original), err: error })
|
||||
Ok(ExpandResult { value: Some((original, mapping)), err: error })
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ pub(crate) struct SyntaxFixups {
|
||||
/// This is the information needed to reverse the fixups.
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct SyntaxFixupUndoInfo {
|
||||
original: Vec<Subtree>,
|
||||
original: Box<[Subtree]>,
|
||||
}
|
||||
|
||||
const EMPTY_ID: SyntheticTokenId = SyntheticTokenId(!0);
|
||||
@ -272,7 +272,7 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
|
||||
replace,
|
||||
token_map,
|
||||
next_id,
|
||||
undo_info: SyntaxFixupUndoInfo { original },
|
||||
undo_info: SyntaxFixupUndoInfo { original: original.into_boxed_slice() },
|
||||
}
|
||||
}
|
||||
|
||||
@ -472,13 +472,13 @@ fn foo () {match __ra_fixup {}}
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
match x {
|
||||
match it {
|
||||
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {match x {}}
|
||||
fn foo () {match it {}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
@ -547,11 +547,11 @@ fn foo () {a . __ra_fixup ; bar () ;}
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
let x = a
|
||||
let it = a
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {let x = a ;}
|
||||
fn foo () {let it = a ;}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
@ -561,11 +561,11 @@ fn foo () {let x = a ;}
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
let x = a.
|
||||
let it = a.
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {let x = a . __ra_fixup ;}
|
||||
fn foo () {let it = a . __ra_fixup ;}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ struct HygieneInfo {
|
||||
/// The start offset of the `macro_rules!` arguments or attribute input.
|
||||
attr_input_or_mac_def_start: Option<InFile<TextSize>>,
|
||||
|
||||
macro_def: Arc<TokenExpander>,
|
||||
macro_def: TokenExpander,
|
||||
macro_arg: Arc<(crate::tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>,
|
||||
macro_arg_shift: mbe::Shift,
|
||||
exp_map: Arc<mbe::TokenMap>,
|
||||
@ -149,19 +149,15 @@ impl HygieneInfo {
|
||||
token_id = unshifted;
|
||||
(&attr_args.1, self.attr_input_or_mac_def_start?)
|
||||
}
|
||||
None => (
|
||||
&self.macro_arg.1,
|
||||
InFile::new(loc.kind.file_id(), loc.kind.arg(db)?.text_range().start()),
|
||||
),
|
||||
None => (&self.macro_arg.1, loc.kind.arg(db)?.map(|it| it.text_range().start())),
|
||||
},
|
||||
_ => match origin {
|
||||
mbe::Origin::Call => (
|
||||
&self.macro_arg.1,
|
||||
InFile::new(loc.kind.file_id(), loc.kind.arg(db)?.text_range().start()),
|
||||
),
|
||||
mbe::Origin::Def => match (&*self.macro_def, &self.attr_input_or_mac_def_start) {
|
||||
(TokenExpander::DeclarativeMacro { def_site_token_map, .. }, Some(tt)) => {
|
||||
(def_site_token_map, *tt)
|
||||
mbe::Origin::Call => {
|
||||
(&self.macro_arg.1, loc.kind.arg(db)?.map(|it| it.text_range().start()))
|
||||
}
|
||||
mbe::Origin::Def => match (&self.macro_def, &self.attr_input_or_mac_def_start) {
|
||||
(TokenExpander::DeclarativeMacro(expander), Some(tt)) => {
|
||||
(&expander.def_site_token_map, *tt)
|
||||
}
|
||||
_ => panic!("`Origin::Def` used with non-`macro_rules!` macro"),
|
||||
},
|
||||
@ -198,9 +194,9 @@ fn make_hygiene_info(
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let macro_def = db.macro_def(loc.def).ok()?;
|
||||
let macro_def = db.macro_expander(loc.def);
|
||||
let (_, exp_map) = db.parse_macro_expansion(macro_file).value;
|
||||
let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| {
|
||||
let macro_arg = db.macro_arg(macro_file.macro_call_id).value.unwrap_or_else(|| {
|
||||
Arc::new((
|
||||
tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
|
||||
Default::default(),
|
||||
|
@ -37,11 +37,11 @@ use either::Either;
|
||||
use syntax::{
|
||||
algo::{self, skip_trivia_token},
|
||||
ast::{self, AstNode, HasDocComments},
|
||||
Direction, SyntaxNode, SyntaxToken,
|
||||
AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ast_id_map::FileAstId,
|
||||
ast_id_map::{AstIdNode, ErasedFileAstId, FileAstId},
|
||||
attrs::AttrId,
|
||||
builtin_attr_macro::BuiltinAttrExpander,
|
||||
builtin_derive_macro::BuiltinDeriveExpander,
|
||||
@ -127,7 +127,8 @@ impl_intern_key!(MacroCallId);
|
||||
pub struct MacroCallLoc {
|
||||
pub def: MacroDefId,
|
||||
pub(crate) krate: CrateId,
|
||||
/// Some if `def` is a builtin eager macro.
|
||||
/// Some if this is a macro call for an eager macro. Note that this is `None`
|
||||
/// for the eager input macro file.
|
||||
eager: Option<Box<EagerCallInfo>>,
|
||||
pub kind: MacroCallKind,
|
||||
}
|
||||
@ -152,11 +153,10 @@ pub enum MacroDefKind {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
struct EagerCallInfo {
|
||||
/// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
|
||||
/// The expanded argument of the eager macro.
|
||||
arg: Arc<(tt::Subtree, TokenMap)>,
|
||||
/// call id of the eager macro's input file. If this is none, macro call containing this call info
|
||||
/// is an eager macro's input, otherwise it is its output.
|
||||
arg_id: Option<MacroCallId>,
|
||||
/// Call id of the eager macro's input file (this is the macro file for its fully expanded input).
|
||||
arg_id: MacroCallId,
|
||||
error: Option<ExpandError>,
|
||||
}
|
||||
|
||||
@ -221,11 +221,7 @@ impl HirFileId {
|
||||
HirFileIdRepr::FileId(id) => break id,
|
||||
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
|
||||
let is_include_expansion = loc.def.is_include()
|
||||
&& matches!(
|
||||
loc.eager.as_deref(),
|
||||
Some(EagerCallInfo { arg_id: Some(_), .. })
|
||||
);
|
||||
let is_include_expansion = loc.def.is_include() && loc.eager.is_some();
|
||||
file_id = match is_include_expansion.then(|| db.include_expand(macro_call_id)) {
|
||||
Some(Ok((_, file))) => file.into(),
|
||||
_ => loc.kind.file_id(),
|
||||
@ -270,57 +266,13 @@ impl HirFileId {
|
||||
/// Return expansion information if it is a macro-expansion file
|
||||
pub fn expansion_info(self, db: &dyn db::ExpandDatabase) -> Option<ExpansionInfo> {
|
||||
let macro_file = self.macro_file()?;
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
|
||||
let arg_tt = loc.kind.arg(db)?;
|
||||
|
||||
let macro_def = db.macro_def(loc.def).ok()?;
|
||||
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
|
||||
let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| {
|
||||
Arc::new((
|
||||
tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
))
|
||||
});
|
||||
|
||||
let def = loc.def.ast_id().left().and_then(|id| {
|
||||
let def_tt = match id.to_node(db) {
|
||||
ast::Macro::MacroRules(mac) => mac.token_tree()?,
|
||||
ast::Macro::MacroDef(_) if matches!(*macro_def, TokenExpander::BuiltinAttr(_)) => {
|
||||
return None
|
||||
}
|
||||
ast::Macro::MacroDef(mac) => mac.body()?,
|
||||
};
|
||||
Some(InFile::new(id.file_id, def_tt))
|
||||
});
|
||||
let attr_input_or_mac_def = def.or_else(|| match loc.kind {
|
||||
MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
|
||||
// FIXME: handle `cfg_attr`
|
||||
let tt = ast_id
|
||||
.to_node(db)
|
||||
.doc_comments_and_attrs()
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(Either::left)?
|
||||
.token_tree()?;
|
||||
Some(InFile::new(ast_id.file_id, tt))
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
|
||||
Some(ExpansionInfo {
|
||||
expanded: InFile::new(self, parse.syntax_node()),
|
||||
arg: InFile::new(loc.kind.file_id(), arg_tt),
|
||||
attr_input_or_mac_def,
|
||||
macro_arg_shift: mbe::Shift::new(¯o_arg.0),
|
||||
macro_arg,
|
||||
macro_def,
|
||||
exp_map,
|
||||
})
|
||||
ExpansionInfo::new(db, macro_file)
|
||||
}
|
||||
|
||||
/// Indicate it is macro file generated for builtin derive
|
||||
pub fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> Option<InFile<ast::Attr>> {
|
||||
pub fn as_builtin_derive_attr_node(
|
||||
&self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> Option<InFile<ast::Attr>> {
|
||||
let macro_file = self.macro_file()?;
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
let attr = match loc.def.kind {
|
||||
@ -333,8 +285,22 @@ impl HirFileId {
|
||||
pub fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool {
|
||||
match self.macro_file() {
|
||||
Some(macro_file) => {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
matches!(loc.def.kind, MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _))
|
||||
matches!(
|
||||
db.lookup_intern_macro_call(macro_file.macro_call_id).def.kind,
|
||||
MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _)
|
||||
)
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> bool {
|
||||
match self.macro_file() {
|
||||
Some(macro_file) => {
|
||||
matches!(
|
||||
db.lookup_intern_macro_call(macro_file.macro_call_id).def.kind,
|
||||
MacroDefKind::BuiltInDerive(..)
|
||||
)
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
@ -344,8 +310,7 @@ impl HirFileId {
|
||||
pub fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool {
|
||||
match self.macro_file() {
|
||||
Some(macro_file) => {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
loc.def.is_include()
|
||||
db.lookup_intern_macro_call(macro_file.macro_call_id).def.is_include()
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
@ -355,7 +320,7 @@ impl HirFileId {
|
||||
match self.macro_file() {
|
||||
Some(macro_file) => {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
matches!(loc.eager.as_deref(), Some(EagerCallInfo { .. }))
|
||||
matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
@ -536,9 +501,9 @@ impl MacroCallKind {
|
||||
};
|
||||
|
||||
let range = match kind {
|
||||
MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
|
||||
MacroCallKind::Derive { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
|
||||
MacroCallKind::Attr { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
|
||||
MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(),
|
||||
MacroCallKind::Derive { ast_id, .. } => ast_id.to_ptr(db).text_range(),
|
||||
MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).text_range(),
|
||||
};
|
||||
|
||||
FileRange { range, file_id }
|
||||
@ -588,13 +553,18 @@ impl MacroCallKind {
|
||||
FileRange { range, file_id }
|
||||
}
|
||||
|
||||
fn arg(&self, db: &dyn db::ExpandDatabase) -> Option<SyntaxNode> {
|
||||
fn arg(&self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> {
|
||||
match self {
|
||||
MacroCallKind::FnLike { ast_id, .. } => {
|
||||
Some(ast_id.to_node(db).token_tree()?.syntax().clone())
|
||||
MacroCallKind::FnLike { ast_id, .. } => ast_id
|
||||
.to_in_file_node(db)
|
||||
.map(|it| Some(it.token_tree()?.syntax().clone()))
|
||||
.transpose(),
|
||||
MacroCallKind::Derive { ast_id, .. } => {
|
||||
Some(ast_id.to_in_file_node(db).syntax().cloned())
|
||||
}
|
||||
MacroCallKind::Attr { ast_id, .. } => {
|
||||
Some(ast_id.to_in_file_node(db).syntax().cloned())
|
||||
}
|
||||
MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
|
||||
MacroCallKind::Attr { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -612,13 +582,13 @@ impl MacroCallId {
|
||||
/// ExpansionInfo mainly describes how to map text range between src and expanded macro
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ExpansionInfo {
|
||||
expanded: InFile<SyntaxNode>,
|
||||
expanded: InMacroFile<SyntaxNode>,
|
||||
/// The argument TokenTree or item for attributes
|
||||
arg: InFile<SyntaxNode>,
|
||||
/// The `macro_rules!` or attribute input.
|
||||
attr_input_or_mac_def: Option<InFile<ast::TokenTree>>,
|
||||
|
||||
macro_def: Arc<TokenExpander>,
|
||||
macro_def: TokenExpander,
|
||||
macro_arg: Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>,
|
||||
/// A shift built from `macro_arg`'s subtree, relevant for attributes as the item is the macro arg
|
||||
/// and as such we need to shift tokens if they are part of an attributes input instead of their item.
|
||||
@ -628,7 +598,7 @@ pub struct ExpansionInfo {
|
||||
|
||||
impl ExpansionInfo {
|
||||
pub fn expanded(&self) -> InFile<SyntaxNode> {
|
||||
self.expanded.clone()
|
||||
self.expanded.clone().into()
|
||||
}
|
||||
|
||||
pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
|
||||
@ -659,7 +629,7 @@ impl ExpansionInfo {
|
||||
let token_id_in_attr_input = if let Some(item) = item {
|
||||
// check if we are mapping down in an attribute input
|
||||
// this is a special case as attributes can have two inputs
|
||||
let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
|
||||
let call_id = self.expanded.file_id.macro_call_id;
|
||||
let loc = db.lookup_intern_macro_call(call_id);
|
||||
|
||||
let token_range = token.value.text_range();
|
||||
@ -705,7 +675,7 @@ impl ExpansionInfo {
|
||||
let relative_range =
|
||||
token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
|
||||
let token_id = self.macro_arg.1.token_by_range(relative_range)?;
|
||||
// conditionally shift the id by a declaratives macro definition
|
||||
// conditionally shift the id by a declarative macro definition
|
||||
self.macro_def.map_id_down(token_id)
|
||||
}
|
||||
};
|
||||
@ -715,7 +685,7 @@ impl ExpansionInfo {
|
||||
.ranges_by_token(token_id, token.value.kind())
|
||||
.flat_map(move |range| self.expanded.value.covering_element(range).into_token());
|
||||
|
||||
Some(tokens.map(move |token| self.expanded.with_value(token)))
|
||||
Some(tokens.map(move |token| InFile::new(self.expanded.file_id.into(), token)))
|
||||
}
|
||||
|
||||
/// Map a token up out of the expansion it resides in into the arguments of the macro call of the expansion.
|
||||
@ -724,18 +694,17 @@ impl ExpansionInfo {
|
||||
db: &dyn db::ExpandDatabase,
|
||||
token: InFile<&SyntaxToken>,
|
||||
) -> Option<(InFile<SyntaxToken>, Origin)> {
|
||||
assert_eq!(token.file_id, self.expanded.file_id.into());
|
||||
// Fetch the id through its text range,
|
||||
let token_id = self.exp_map.token_by_range(token.value.text_range())?;
|
||||
// conditionally unshifting the id to accommodate for macro-rules def site
|
||||
let (mut token_id, origin) = self.macro_def.map_id_up(token_id);
|
||||
|
||||
let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
|
||||
let call_id = self.expanded.file_id.macro_call_id;
|
||||
let loc = db.lookup_intern_macro_call(call_id);
|
||||
|
||||
// Special case: map tokens from `include!` expansions to the included file
|
||||
if loc.def.is_include()
|
||||
&& matches!(loc.eager.as_deref(), Some(EagerCallInfo { arg_id: Some(_), .. }))
|
||||
{
|
||||
if loc.def.is_include() {
|
||||
if let Ok((tt_and_map, file_id)) = db.include_expand(call_id) {
|
||||
let range = tt_and_map.1.first_range_by_token(token_id, token.value.kind())?;
|
||||
let source = db.parse(file_id);
|
||||
@ -765,9 +734,9 @@ impl ExpansionInfo {
|
||||
}
|
||||
_ => match origin {
|
||||
mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
|
||||
mbe::Origin::Def => match (&*self.macro_def, &self.attr_input_or_mac_def) {
|
||||
(TokenExpander::DeclarativeMacro { def_site_token_map, .. }, Some(tt)) => {
|
||||
(def_site_token_map, tt.syntax().cloned())
|
||||
mbe::Origin::Def => match (&self.macro_def, &self.attr_input_or_mac_def) {
|
||||
(TokenExpander::DeclarativeMacro(expander), Some(tt)) => {
|
||||
(&expander.def_site_token_map, tt.syntax().cloned())
|
||||
}
|
||||
_ => panic!("`Origin::Def` used with non-`macro_rules!` macro"),
|
||||
},
|
||||
@ -779,6 +748,58 @@ impl ExpansionInfo {
|
||||
tt.value.covering_element(range + tt.value.text_range().start()).into_token()?;
|
||||
Some((tt.with_value(token), origin))
|
||||
}
|
||||
|
||||
fn new(db: &dyn db::ExpandDatabase, macro_file: MacroFile) -> Option<ExpansionInfo> {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
|
||||
let arg_tt = loc.kind.arg(db)?;
|
||||
|
||||
let macro_def = db.macro_expander(loc.def);
|
||||
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
|
||||
let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
|
||||
|
||||
let macro_arg = db.macro_arg(macro_file.macro_call_id).value.unwrap_or_else(|| {
|
||||
Arc::new((
|
||||
tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
))
|
||||
});
|
||||
|
||||
let def = loc.def.ast_id().left().and_then(|id| {
|
||||
let def_tt = match id.to_node(db) {
|
||||
ast::Macro::MacroRules(mac) => mac.token_tree()?,
|
||||
ast::Macro::MacroDef(_) if matches!(macro_def, TokenExpander::BuiltInAttr(_)) => {
|
||||
return None
|
||||
}
|
||||
ast::Macro::MacroDef(mac) => mac.body()?,
|
||||
};
|
||||
Some(InFile::new(id.file_id, def_tt))
|
||||
});
|
||||
let attr_input_or_mac_def = def.or_else(|| match loc.kind {
|
||||
MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
|
||||
// FIXME: handle `cfg_attr`
|
||||
let tt = ast_id
|
||||
.to_node(db)
|
||||
.doc_comments_and_attrs()
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(Either::left)?
|
||||
.token_tree()?;
|
||||
Some(InFile::new(ast_id.file_id, tt))
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
|
||||
Some(ExpansionInfo {
|
||||
expanded,
|
||||
arg: arg_tt,
|
||||
attr_input_or_mac_def,
|
||||
macro_arg_shift: mbe::Shift::new(¯o_arg.0),
|
||||
macro_arg,
|
||||
macro_def,
|
||||
exp_map,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// `AstId` points to an AST node in any file.
|
||||
@ -786,10 +807,26 @@ impl ExpansionInfo {
|
||||
/// It is stable across reparses, and can be used as salsa key/value.
|
||||
pub type AstId<N> = InFile<FileAstId<N>>;
|
||||
|
||||
impl<N: AstNode> AstId<N> {
|
||||
impl<N: AstIdNode> AstId<N> {
|
||||
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N {
|
||||
let root = db.parse_or_expand(self.file_id);
|
||||
db.ast_id_map(self.file_id).get(self.value).to_node(&root)
|
||||
self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
|
||||
}
|
||||
pub fn to_in_file_node(&self, db: &dyn db::ExpandDatabase) -> InFile<N> {
|
||||
InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)))
|
||||
}
|
||||
pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> AstPtr<N> {
|
||||
db.ast_id_map(self.file_id).get(self.value)
|
||||
}
|
||||
}
|
||||
|
||||
pub type ErasedAstId = InFile<ErasedFileAstId>;
|
||||
|
||||
impl ErasedAstId {
|
||||
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
|
||||
self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
|
||||
}
|
||||
pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> SyntaxNodePtr {
|
||||
db.ast_id_map(self.file_id).get_raw(self.value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -850,7 +887,7 @@ impl<L, R> InFile<Either<L, R>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InFile<&'a SyntaxNode> {
|
||||
impl InFile<&SyntaxNode> {
|
||||
pub fn ancestors_with_macros(
|
||||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
@ -1011,6 +1048,18 @@ impl InFile<SyntaxToken> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
pub struct InMacroFile<T> {
|
||||
pub file_id: MacroFile,
|
||||
pub value: T,
|
||||
}
|
||||
|
||||
impl<T> From<InMacroFile<T>> for InFile<T> {
|
||||
fn from(macro_file: InMacroFile<T>) -> Self {
|
||||
InFile { file_id: macro_file.file_id.into(), value: macro_file.value }
|
||||
}
|
||||
}
|
||||
|
||||
fn ascend_node_border_tokens(
|
||||
db: &dyn db::ExpandDatabase,
|
||||
InFile { file_id, value: node }: InFile<&SyntaxNode>,
|
||||
|
@ -126,7 +126,7 @@ struct Display<'a> {
|
||||
path: &'a ModPath,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Display<'a> {
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
display_fmt_path(self.db, self.path, f, true)
|
||||
}
|
||||
@ -137,7 +137,7 @@ struct UnescapedDisplay<'a> {
|
||||
path: &'a UnescapedModPath<'a>,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for UnescapedDisplay<'a> {
|
||||
impl fmt::Display for UnescapedDisplay<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
display_fmt_path(self.db, self.path.0, f, false)
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ enum Repr {
|
||||
TupleField(usize),
|
||||
}
|
||||
|
||||
impl<'a> UnescapedName<'a> {
|
||||
impl UnescapedName<'_> {
|
||||
/// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over
|
||||
/// [`ToString::to_string`] if possible as this conversion is cheaper in the general case.
|
||||
pub fn to_smol_str(&self) -> SmolStr {
|
||||
@ -40,7 +40,7 @@ impl<'a> UnescapedName<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
|
||||
pub fn display(&self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + '_ {
|
||||
_ = db;
|
||||
UnescapedDisplay { name: self }
|
||||
}
|
||||
@ -96,6 +96,15 @@ impl Name {
|
||||
Name::new_inline("[missing name]")
|
||||
}
|
||||
|
||||
/// Returns true if this is a fake name for things missing in the source code. See
|
||||
/// [`missing()`][Self::missing] for details.
|
||||
///
|
||||
/// Use this method instead of comparing with `Self::missing()` as missing names
|
||||
/// (ideally should) have a `gensym` semantics.
|
||||
pub fn is_missing(&self) -> bool {
|
||||
self == &Name::missing()
|
||||
}
|
||||
|
||||
/// Generates a new name which is only equal to itself, by incrementing a counter. Due
|
||||
/// its implementation, it should not be used in things that salsa considers, like
|
||||
/// type names or field names, and it should be only used in names of local variables
|
||||
@ -162,7 +171,7 @@ struct Display<'a> {
|
||||
name: &'a Name,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Display<'a> {
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.name.0 {
|
||||
Repr::Text(text) => fmt::Display::fmt(&text, f),
|
||||
@ -175,7 +184,7 @@ struct UnescapedDisplay<'a> {
|
||||
name: &'a UnescapedName<'a>,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for UnescapedDisplay<'a> {
|
||||
impl fmt::Display for UnescapedDisplay<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.name.0 .0 {
|
||||
Repr::Text(text) => {
|
||||
@ -282,8 +291,10 @@ pub mod known {
|
||||
alloc,
|
||||
iter,
|
||||
ops,
|
||||
fmt,
|
||||
future,
|
||||
result,
|
||||
string,
|
||||
boxed,
|
||||
option,
|
||||
prelude,
|
||||
@ -311,6 +322,7 @@ pub mod known {
|
||||
RangeToInclusive,
|
||||
RangeTo,
|
||||
Range,
|
||||
String,
|
||||
Neg,
|
||||
Not,
|
||||
None,
|
||||
@ -321,6 +333,7 @@ pub mod known {
|
||||
iter_mut,
|
||||
len,
|
||||
is_empty,
|
||||
as_str,
|
||||
new,
|
||||
// Builtin macros
|
||||
asm,
|
||||
@ -334,6 +347,7 @@ pub mod known {
|
||||
core_panic,
|
||||
env,
|
||||
file,
|
||||
format,
|
||||
format_args_nl,
|
||||
format_args,
|
||||
global_asm,
|
||||
@ -365,6 +379,7 @@ pub mod known {
|
||||
cfg_eval,
|
||||
crate_type,
|
||||
derive,
|
||||
derive_const,
|
||||
global_allocator,
|
||||
no_core,
|
||||
no_std,
|
||||
|
@ -19,14 +19,15 @@ bitflags = "2.1.0"
|
||||
smallvec.workspace = true
|
||||
ena = "0.14.0"
|
||||
either = "1.7.0"
|
||||
oorandom = "11.1.3"
|
||||
tracing = "0.1.35"
|
||||
rustc-hash = "1.1.0"
|
||||
scoped-tls = "1.0.0"
|
||||
chalk-solve = { version = "0.91.0", default-features = false }
|
||||
chalk-ir = "0.91.0"
|
||||
chalk-recursive = { version = "0.91.0", default-features = false }
|
||||
chalk-derive = "0.91.0"
|
||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||
chalk-solve = { version = "0.92.0", default-features = false }
|
||||
chalk-ir = "0.92.0"
|
||||
chalk-recursive = { version = "0.92.0", default-features = false }
|
||||
chalk-derive = "0.92.0"
|
||||
la-arena.workspace = true
|
||||
once_cell = "1.17.0"
|
||||
triomphe.workspace = true
|
||||
nohash-hasher.workspace = true
|
||||
@ -47,7 +48,6 @@ limit.workspace = true
|
||||
expect-test = "1.4.0"
|
||||
tracing = "0.1.35"
|
||||
tracing-subscriber = { version = "0.3.16", default-features = false, features = [
|
||||
"env-filter",
|
||||
"registry",
|
||||
] }
|
||||
tracing-tree = "0.2.1"
|
||||
|
@ -36,7 +36,7 @@ pub fn autoderef(
|
||||
) -> impl Iterator<Item = Ty> {
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let ty = table.instantiate_canonical(ty);
|
||||
let mut autoderef = Autoderef::new(&mut table, ty);
|
||||
let mut autoderef = Autoderef::new(&mut table, ty, false);
|
||||
let mut v = Vec::new();
|
||||
while let Some((ty, _steps)) = autoderef.next() {
|
||||
// `ty` may contain unresolved inference variables. Since there's no chance they would be
|
||||
@ -63,12 +63,13 @@ pub(crate) struct Autoderef<'a, 'db> {
|
||||
ty: Ty,
|
||||
at_start: bool,
|
||||
steps: Vec<(AutoderefKind, Ty)>,
|
||||
explicit: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'db> Autoderef<'a, 'db> {
|
||||
pub(crate) fn new(table: &'a mut InferenceTable<'db>, ty: Ty) -> Self {
|
||||
pub(crate) fn new(table: &'a mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self {
|
||||
let ty = table.resolve_ty_shallow(&ty);
|
||||
Autoderef { table, ty, at_start: true, steps: Vec::new() }
|
||||
Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit }
|
||||
}
|
||||
|
||||
pub(crate) fn step_count(&self) -> usize {
|
||||
@ -97,7 +98,7 @@ impl Iterator for Autoderef<'_, '_> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (kind, new_ty) = autoderef_step(self.table, self.ty.clone())?;
|
||||
let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?;
|
||||
|
||||
self.steps.push((kind, self.ty.clone()));
|
||||
self.ty = new_ty;
|
||||
@ -109,8 +110,9 @@ impl Iterator for Autoderef<'_, '_> {
|
||||
pub(crate) fn autoderef_step(
|
||||
table: &mut InferenceTable<'_>,
|
||||
ty: Ty,
|
||||
explicit: bool,
|
||||
) -> Option<(AutoderefKind, Ty)> {
|
||||
if let Some(derefed) = builtin_deref(table, &ty, false) {
|
||||
if let Some(derefed) = builtin_deref(table, &ty, explicit) {
|
||||
Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
|
||||
} else {
|
||||
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
|
||||
@ -124,7 +126,6 @@ pub(crate) fn builtin_deref<'ty>(
|
||||
) -> Option<&'ty Ty> {
|
||||
match ty.kind(Interner) {
|
||||
TyKind::Ref(.., ty) => Some(ty),
|
||||
// FIXME: Maybe accept this but diagnose if its not explicit?
|
||||
TyKind::Raw(.., ty) if explicit => Some(ty),
|
||||
&TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => {
|
||||
if crate::lang_items::is_box(table.db, adt) {
|
||||
|
@ -5,13 +5,13 @@ use std::{iter, sync::Arc};
|
||||
|
||||
use tracing::debug;
|
||||
|
||||
use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds};
|
||||
use chalk_ir::{cast::Caster, fold::shift::Shift, CanonicalVarKinds};
|
||||
use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_def::{
|
||||
hir::Movability,
|
||||
lang_item::{lang_attr, LangItem, LangItemTarget},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId,
|
||||
};
|
||||
use hir_expand::name::name;
|
||||
@ -46,7 +46,7 @@ pub(crate) type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Inte
|
||||
pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>;
|
||||
pub(crate) type Variances = chalk_ir::Variances<Interner>;
|
||||
|
||||
impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
||||
impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
|
||||
fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> {
|
||||
self.db.associated_ty_data(id)
|
||||
}
|
||||
@ -60,9 +60,37 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
||||
// FIXME: keep track of these
|
||||
Arc::new(rust_ir::AdtRepr { c: false, packed: false, int: None })
|
||||
}
|
||||
fn discriminant_type(&self, _ty: chalk_ir::Ty<Interner>) -> chalk_ir::Ty<Interner> {
|
||||
// FIXME: keep track of this
|
||||
chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32)).intern(Interner)
|
||||
fn discriminant_type(&self, ty: chalk_ir::Ty<Interner>) -> chalk_ir::Ty<Interner> {
|
||||
if let chalk_ir::TyKind::Adt(id, _) = ty.kind(Interner) {
|
||||
if let hir_def::AdtId::EnumId(e) = id.0 {
|
||||
let enum_data = self.db.enum_data(e);
|
||||
let ty = enum_data.repr.unwrap_or_default().discr_type();
|
||||
return chalk_ir::TyKind::Scalar(match ty {
|
||||
hir_def::layout::IntegerType::Pointer(is_signed) => match is_signed {
|
||||
true => chalk_ir::Scalar::Int(chalk_ir::IntTy::Isize),
|
||||
false => chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize),
|
||||
},
|
||||
hir_def::layout::IntegerType::Fixed(size, is_signed) => match is_signed {
|
||||
true => chalk_ir::Scalar::Int(match size {
|
||||
hir_def::layout::Integer::I8 => chalk_ir::IntTy::I8,
|
||||
hir_def::layout::Integer::I16 => chalk_ir::IntTy::I16,
|
||||
hir_def::layout::Integer::I32 => chalk_ir::IntTy::I32,
|
||||
hir_def::layout::Integer::I64 => chalk_ir::IntTy::I64,
|
||||
hir_def::layout::Integer::I128 => chalk_ir::IntTy::I128,
|
||||
}),
|
||||
false => chalk_ir::Scalar::Uint(match size {
|
||||
hir_def::layout::Integer::I8 => chalk_ir::UintTy::U8,
|
||||
hir_def::layout::Integer::I16 => chalk_ir::UintTy::U16,
|
||||
hir_def::layout::Integer::I32 => chalk_ir::UintTy::U32,
|
||||
hir_def::layout::Integer::I64 => chalk_ir::UintTy::U64,
|
||||
hir_def::layout::Integer::I128 => chalk_ir::UintTy::U128,
|
||||
}),
|
||||
},
|
||||
})
|
||||
.intern(Interner);
|
||||
}
|
||||
}
|
||||
chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U8)).intern(Interner)
|
||||
}
|
||||
fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> {
|
||||
self.db.impl_datum(self.krate, impl_id)
|
||||
@ -565,7 +593,7 @@ pub(crate) fn trait_datum_query(
|
||||
let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars);
|
||||
let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect();
|
||||
let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses };
|
||||
let well_known = lang_attr(db.upcast(), trait_).and_then(well_known_trait_from_lang_item);
|
||||
let well_known = db.lang_attr(trait_.into()).and_then(well_known_trait_from_lang_item);
|
||||
let trait_datum = TraitDatum {
|
||||
id: trait_id,
|
||||
binders: make_binders(db, &generic_params, trait_datum_bound),
|
||||
@ -593,6 +621,7 @@ fn well_known_trait_from_lang_item(item: LangItem) -> Option<WellKnownTrait> {
|
||||
LangItem::Unsize => WellKnownTrait::Unsize,
|
||||
LangItem::Tuple => WellKnownTrait::Tuple,
|
||||
LangItem::PointeeTrait => WellKnownTrait::Pointee,
|
||||
LangItem::FnPtrTrait => WellKnownTrait::FnPtr,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@ -614,6 +643,7 @@ fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem {
|
||||
WellKnownTrait::Unpin => LangItem::Unpin,
|
||||
WellKnownTrait::Unsize => LangItem::Unsize,
|
||||
WellKnownTrait::Pointee => LangItem::PointeeTrait,
|
||||
WellKnownTrait::FnPtr => LangItem::FnPtrTrait,
|
||||
}
|
||||
}
|
||||
|
||||
@ -844,28 +874,34 @@ pub(super) fn generic_predicate_to_inline_bound(
|
||||
}
|
||||
let args_no_self = trait_ref.substitution.as_slice(Interner)[1..]
|
||||
.iter()
|
||||
.map(|ty| ty.clone().cast(Interner))
|
||||
.cloned()
|
||||
.casted(Interner)
|
||||
.collect();
|
||||
let trait_bound = rust_ir::TraitBound { trait_id: trait_ref.trait_id, args_no_self };
|
||||
Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound)))
|
||||
}
|
||||
WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
|
||||
let trait_ = projection_ty.trait_(db);
|
||||
if projection_ty.self_type_parameter(db) != self_ty_shifted_in {
|
||||
let generics =
|
||||
generics(db.upcast(), from_assoc_type_id(projection_ty.associated_ty_id).into());
|
||||
let (assoc_args, trait_args) =
|
||||
projection_ty.substitution.as_slice(Interner).split_at(generics.len_self());
|
||||
let (self_ty, args_no_self) =
|
||||
trait_args.split_first().expect("projection without trait self type");
|
||||
if self_ty.assert_ty_ref(Interner) != &self_ty_shifted_in {
|
||||
return None;
|
||||
}
|
||||
let args_no_self = projection_ty.substitution.as_slice(Interner)[1..]
|
||||
.iter()
|
||||
.map(|ty| ty.clone().cast(Interner))
|
||||
.collect();
|
||||
|
||||
let args_no_self = args_no_self.iter().cloned().casted(Interner).collect();
|
||||
let parameters = assoc_args.to_vec();
|
||||
|
||||
let alias_eq_bound = rust_ir::AliasEqBound {
|
||||
value: ty.clone(),
|
||||
trait_bound: rust_ir::TraitBound {
|
||||
trait_id: to_chalk_trait_id(trait_),
|
||||
trait_id: to_chalk_trait_id(projection_ty.trait_(db)),
|
||||
args_no_self,
|
||||
},
|
||||
associated_ty_id: projection_ty.associated_ty_id,
|
||||
parameters: Vec::new(), // FIXME we don't support generic associated types yet
|
||||
parameters,
|
||||
};
|
||||
Some(chalk_ir::Binders::new(
|
||||
binders,
|
||||
|
@ -343,7 +343,8 @@ impl TyExt for Ty {
|
||||
|
||||
fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool {
|
||||
let crate_id = owner.module(db.upcast()).krate();
|
||||
let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else {
|
||||
let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|it| it.as_trait())
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build();
|
||||
|
@ -88,7 +88,7 @@ pub(crate) fn path_to_const(
|
||||
ConstValue::Placeholder(to_placeholder_idx(db, p.into()))
|
||||
}
|
||||
ParamLoweringMode::Variable => match args.param_idx(p.into()) {
|
||||
Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)),
|
||||
Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)),
|
||||
None => {
|
||||
never!(
|
||||
"Generic list doesn't contain this param: {:?}, {:?}, {:?}",
|
||||
@ -139,11 +139,11 @@ pub fn intern_const_ref(
|
||||
let bytes = match value {
|
||||
LiteralConstRef::Int(i) => {
|
||||
// FIXME: We should handle failure of layout better.
|
||||
let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
|
||||
let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16);
|
||||
ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
|
||||
}
|
||||
LiteralConstRef::UInt(i) => {
|
||||
let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
|
||||
let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16);
|
||||
ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
|
||||
}
|
||||
LiteralConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
|
||||
@ -171,7 +171,7 @@ pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option<u128> {
|
||||
chalk_ir::ConstValue::InferenceVar(_) => None,
|
||||
chalk_ir::ConstValue::Placeholder(_) => None,
|
||||
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
|
||||
ConstScalar::Bytes(x, _) => Some(u128::from_le_bytes(pad16(&x, false))),
|
||||
ConstScalar::Bytes(it, _) => Some(u128::from_le_bytes(pad16(&it, false))),
|
||||
ConstScalar::UnevaluatedConst(c, subst) => {
|
||||
let ec = db.const_eval(*c, subst.clone()).ok()?;
|
||||
try_const_usize(db, &ec)
|
||||
@ -228,7 +228,7 @@ pub(crate) fn const_eval_query(
|
||||
}
|
||||
GeneralConstId::InTypeConstId(c) => db.mir_body(c.into())?,
|
||||
};
|
||||
let c = interpret_mir(db, &body, false).0?;
|
||||
let c = interpret_mir(db, body, false).0?;
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
@ -241,7 +241,7 @@ pub(crate) fn const_eval_static_query(
|
||||
Substitution::empty(Interner),
|
||||
db.trait_environment_for_body(def.into()),
|
||||
)?;
|
||||
let c = interpret_mir(db, &body, false).0?;
|
||||
let c = interpret_mir(db, body, false).0?;
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
@ -268,7 +268,7 @@ pub(crate) fn const_eval_discriminant_variant(
|
||||
Substitution::empty(Interner),
|
||||
db.trait_environment_for_body(def),
|
||||
)?;
|
||||
let c = interpret_mir(db, &mir_body, false).0?;
|
||||
let c = interpret_mir(db, mir_body, false).0?;
|
||||
let c = try_const_usize(db, &c).unwrap() as i128;
|
||||
Ok(c)
|
||||
}
|
||||
@ -293,7 +293,7 @@ pub(crate) fn eval_to_const(
|
||||
}
|
||||
let infer = ctx.clone().resolve_all();
|
||||
if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
|
||||
if let Ok(result) = interpret_mir(db, &mir_body, true).0 {
|
||||
if let Ok(result) = interpret_mir(db, Arc::new(mir_body), true).0 {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
use base_db::{fixture::WithFixture, FileId};
|
||||
use chalk_ir::Substitution;
|
||||
use hir_def::db::DefDatabase;
|
||||
use test_utils::skip_slow_tests;
|
||||
|
||||
use crate::{
|
||||
consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar,
|
||||
Interner,
|
||||
Interner, MemoryMap,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@ -16,7 +17,7 @@ mod intrinsics;
|
||||
|
||||
fn simplify(e: ConstEvalError) -> ConstEvalError {
|
||||
match e {
|
||||
ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e, _, _)) => {
|
||||
ConstEvalError::MirEvalError(MirEvalError::InFunction(e, _)) => {
|
||||
simplify(ConstEvalError::MirEvalError(*e))
|
||||
}
|
||||
_ => e,
|
||||
@ -36,7 +37,37 @@ fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
|
||||
|
||||
#[track_caller]
|
||||
fn check_number(ra_fixture: &str, answer: i128) {
|
||||
let (db, file_id) = TestDB::with_single_file(ra_fixture);
|
||||
check_answer(ra_fixture, |b, _| {
|
||||
assert_eq!(
|
||||
b,
|
||||
&answer.to_le_bytes()[0..b.len()],
|
||||
"Bytes differ. In decimal form: actual = {}, expected = {answer}",
|
||||
i128::from_le_bytes(pad16(b, true))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_str(ra_fixture: &str, answer: &str) {
|
||||
check_answer(ra_fixture, |b, mm| {
|
||||
let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
|
||||
let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
|
||||
let Some(bytes) = mm.get(addr, size) else {
|
||||
panic!("string data missed in the memory map");
|
||||
};
|
||||
assert_eq!(
|
||||
bytes,
|
||||
answer.as_bytes(),
|
||||
"Bytes differ. In string form: actual = {}, expected = {answer}",
|
||||
String::from_utf8_lossy(bytes)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8], &MemoryMap)) {
|
||||
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
|
||||
let file_id = *file_ids.last().unwrap();
|
||||
let r = match eval_goal(&db, file_id) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
@ -46,13 +77,8 @@ fn check_number(ra_fixture: &str, answer: i128) {
|
||||
};
|
||||
match &r.data(Interner).value {
|
||||
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
|
||||
ConstScalar::Bytes(b, _) => {
|
||||
assert_eq!(
|
||||
b,
|
||||
&answer.to_le_bytes()[0..b.len()],
|
||||
"Bytes differ. In decimal form: actual = {}, expected = {answer}",
|
||||
i128::from_le_bytes(pad16(b, true))
|
||||
);
|
||||
ConstScalar::Bytes(b, mm) => {
|
||||
check(b, mm);
|
||||
}
|
||||
x => panic!("Expected number but found {:?}", x),
|
||||
},
|
||||
@ -87,7 +113,7 @@ fn eval_goal(db: &TestDB, file_id: FileId) -> Result<Const, ConstEvalError> {
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.unwrap();
|
||||
.expect("No const named GOAL found in the test");
|
||||
db.const_eval(const_id.into(), Substitution::empty(Interner))
|
||||
}
|
||||
|
||||
@ -108,6 +134,7 @@ fn bit_op() {
|
||||
check_fail(r#"const GOAL: i8 = 1 << 8"#, |e| {
|
||||
e == ConstEvalError::MirEvalError(MirEvalError::Panic("Overflow in Shl".to_string()))
|
||||
});
|
||||
check_number(r#"const GOAL: i32 = 100000000i32 << 11"#, (100000000i32 << 11) as i128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -166,14 +193,21 @@ fn casts() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, index, slice
|
||||
struct X {
|
||||
unsize_field: [u8],
|
||||
}
|
||||
|
||||
const GOAL: usize = {
|
||||
let a = [10, 20, 3, 15];
|
||||
let x: &[i32] = &a;
|
||||
let y: *const [i32] = x;
|
||||
let z = y as *const [u8]; // slice fat pointer cast don't touch metadata
|
||||
let q = z as *const str;
|
||||
let p = q as *const [u8];
|
||||
let w = unsafe { &*z };
|
||||
let x: *const [i32] = x;
|
||||
let x = x as *const [u8]; // slice fat pointer cast don't touch metadata
|
||||
let x = x as *const str;
|
||||
let x = x as *const X;
|
||||
let x = x as *const [i16];
|
||||
let x = x as *const X;
|
||||
let x = x as *const [u8];
|
||||
let w = unsafe { &*x };
|
||||
w.len()
|
||||
};
|
||||
"#,
|
||||
@ -198,6 +232,30 @@ fn raw_pointer_equality() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alignment() {
|
||||
check_answer(
|
||||
r#"
|
||||
//- minicore: transmute
|
||||
use core::mem::transmute;
|
||||
const GOAL: usize = {
|
||||
let x: i64 = 2;
|
||||
transmute(&x)
|
||||
}
|
||||
"#,
|
||||
|b, _| assert_eq!(b[0] % 8, 0),
|
||||
);
|
||||
check_answer(
|
||||
r#"
|
||||
//- minicore: transmute
|
||||
use core::mem::transmute;
|
||||
static X: i64 = 12;
|
||||
const GOAL: usize = transmute(&X);
|
||||
"#,
|
||||
|b, _| assert_eq!(b[0] % 8, 0),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locals() {
|
||||
check_number(
|
||||
@ -1550,6 +1608,30 @@ fn closures() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manual_fn_trait_impl() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: fn, copy
|
||||
struct S(i32);
|
||||
|
||||
impl FnOnce<(i32, i32)> for S {
|
||||
type Output = i32;
|
||||
|
||||
extern "rust-call" fn call_once(self, arg: (i32, i32)) -> i32 {
|
||||
arg.0 + arg.1 + self.0
|
||||
}
|
||||
}
|
||||
|
||||
const GOAL: i32 = {
|
||||
let s = S(1);
|
||||
s(2, 3)
|
||||
};
|
||||
"#,
|
||||
6,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closure_and_impl_fn() {
|
||||
check_number(
|
||||
@ -1661,6 +1743,18 @@ fn function_pointer() {
|
||||
"#,
|
||||
5,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
fn add2(x: u8) -> u8 {
|
||||
x + 2
|
||||
}
|
||||
const GOAL: u8 = {
|
||||
let plus2 = add2 as fn(u8) -> u8;
|
||||
plus2(3)
|
||||
};
|
||||
"#,
|
||||
5,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, index, slice
|
||||
@ -1849,6 +1943,38 @@ fn dyn_trait() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coerce_unsized() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, deref_mut, slice, index, transmute, non_null
|
||||
use core::ops::{Deref, DerefMut, CoerceUnsized};
|
||||
use core::{marker::Unsize, mem::transmute, ptr::NonNull};
|
||||
|
||||
struct ArcInner<T: ?Sized> {
|
||||
strong: usize,
|
||||
weak: usize,
|
||||
data: T,
|
||||
}
|
||||
|
||||
pub struct Arc<T: ?Sized> {
|
||||
inner: NonNull<ArcInner<T>>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Arc<U>> for Arc<T> {}
|
||||
|
||||
const GOAL: usize = {
|
||||
let x = transmute::<usize, Arc<[i32; 3]>>(12);
|
||||
let y: Arc<[i32]> = x;
|
||||
let z = transmute::<Arc<[i32]>, (usize, usize)>(y);
|
||||
z.1
|
||||
};
|
||||
|
||||
"#,
|
||||
3,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boxes() {
|
||||
check_number(
|
||||
@ -1960,6 +2086,17 @@ fn array_and_index() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string() {
|
||||
check_str(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, index, slice
|
||||
const GOAL: &str = "hello";
|
||||
"#,
|
||||
"hello",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn byte_string() {
|
||||
check_number(
|
||||
@ -2018,6 +2155,57 @@ fn consts() {
|
||||
"#,
|
||||
6,
|
||||
);
|
||||
|
||||
check_number(
|
||||
r#"
|
||||
const F1: i32 = 2147483647;
|
||||
const F2: i32 = F1 - 25;
|
||||
const GOAL: i32 = F2;
|
||||
"#,
|
||||
2147483622,
|
||||
);
|
||||
|
||||
check_number(
|
||||
r#"
|
||||
const F1: i32 = -2147483648;
|
||||
const F2: i32 = F1 + 18;
|
||||
const GOAL: i32 = F2;
|
||||
"#,
|
||||
-2147483630,
|
||||
);
|
||||
|
||||
check_number(
|
||||
r#"
|
||||
const F1: i32 = 10;
|
||||
const F2: i32 = F1 - 20;
|
||||
const GOAL: i32 = F2;
|
||||
"#,
|
||||
-10,
|
||||
);
|
||||
|
||||
check_number(
|
||||
r#"
|
||||
const F1: i32 = 25;
|
||||
const F2: i32 = F1 - 25;
|
||||
const GOAL: i32 = F2;
|
||||
"#,
|
||||
0,
|
||||
);
|
||||
|
||||
check_number(
|
||||
r#"
|
||||
const A: i32 = -2147483648;
|
||||
const GOAL: bool = A > 0;
|
||||
"#,
|
||||
0,
|
||||
);
|
||||
|
||||
check_number(
|
||||
r#"
|
||||
const GOAL: i64 = (-2147483648_i32) as i64;
|
||||
"#,
|
||||
-2147483648,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -2285,6 +2473,25 @@ fn const_trait_assoc() {
|
||||
"#,
|
||||
32,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- /a/lib.rs crate:a
|
||||
pub trait ToConst {
|
||||
const VAL: usize;
|
||||
}
|
||||
pub const fn to_const<T: ToConst>() -> usize {
|
||||
T::VAL
|
||||
}
|
||||
//- /main.rs crate:main deps:a
|
||||
use a::{ToConst, to_const};
|
||||
struct U0;
|
||||
impl ToConst for U0 {
|
||||
const VAL: usize = 5;
|
||||
}
|
||||
const GOAL: usize = to_const::<U0>();
|
||||
"#,
|
||||
5,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
struct S<T>(*mut T);
|
||||
@ -2310,22 +2517,12 @@ fn const_trait_assoc() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic_messages() {
|
||||
check_fail(
|
||||
r#"
|
||||
//- minicore: panic
|
||||
const GOAL: u8 = {
|
||||
let x: u16 = 2;
|
||||
panic!("hello");
|
||||
};
|
||||
"#,
|
||||
|e| e == ConstEvalError::MirEvalError(MirEvalError::Panic("hello".to_string())),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_limits() {
|
||||
if skip_slow_tests() {
|
||||
return;
|
||||
}
|
||||
|
||||
check_fail(
|
||||
r#"
|
||||
const GOAL: usize = loop {};
|
||||
@ -2339,7 +2536,7 @@ fn exec_limits() {
|
||||
}
|
||||
const GOAL: i32 = f(0);
|
||||
"#,
|
||||
|e| e == ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
|
||||
|e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
|
||||
);
|
||||
// Reasonable code should still work
|
||||
check_number(
|
||||
@ -2362,6 +2559,28 @@ fn exec_limits() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn memory_limit() {
|
||||
check_fail(
|
||||
r#"
|
||||
extern "Rust" {
|
||||
#[rustc_allocator]
|
||||
fn __rust_alloc(size: usize, align: usize) -> *mut u8;
|
||||
}
|
||||
|
||||
const GOAL: u8 = unsafe {
|
||||
__rust_alloc(30_000_000_000, 1); // 30GB
|
||||
2
|
||||
};
|
||||
"#,
|
||||
|e| {
|
||||
e == ConstEvalError::MirEvalError(MirEvalError::Panic(
|
||||
"Memory allocation of 30000000000 bytes failed".to_string(),
|
||||
))
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_error() {
|
||||
check_fail(
|
||||
@ -2376,6 +2595,37 @@ fn type_error() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsized_field() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, index, slice, transmute
|
||||
use core::mem::transmute;
|
||||
|
||||
struct Slice([u8]);
|
||||
struct Slice2(Slice);
|
||||
|
||||
impl Slice2 {
|
||||
fn as_inner(&self) -> &Slice {
|
||||
&self.0
|
||||
}
|
||||
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
&self.as_inner().0
|
||||
}
|
||||
}
|
||||
|
||||
const GOAL: u8 = unsafe {
|
||||
let x: &[u8] = &[1, 2, 3];
|
||||
let x: &Slice2 = transmute(x);
|
||||
let x = x.as_bytes();
|
||||
x[0] + x[1] + x[2]
|
||||
};
|
||||
"#,
|
||||
6,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsized_local() {
|
||||
check_fail(
|
||||
|
@ -14,6 +14,171 @@ fn size_of() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_of_val() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized
|
||||
extern "rust-intrinsic" {
|
||||
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
|
||||
}
|
||||
|
||||
struct X(i32, u8);
|
||||
|
||||
const GOAL: usize = size_of_val(&X(1, 2));
|
||||
"#,
|
||||
8,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized
|
||||
extern "rust-intrinsic" {
|
||||
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
|
||||
}
|
||||
|
||||
const GOAL: usize = {
|
||||
let it: &[i32] = &[1, 2, 3];
|
||||
size_of_val(it)
|
||||
};
|
||||
"#,
|
||||
12,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, transmute
|
||||
use core::mem::transmute;
|
||||
|
||||
extern "rust-intrinsic" {
|
||||
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
|
||||
}
|
||||
|
||||
struct X {
|
||||
x: i64,
|
||||
y: u8,
|
||||
t: [i32],
|
||||
}
|
||||
|
||||
const GOAL: usize = unsafe {
|
||||
let y: &X = transmute([0usize, 3]);
|
||||
size_of_val(y)
|
||||
};
|
||||
"#,
|
||||
24,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, transmute
|
||||
use core::mem::transmute;
|
||||
|
||||
extern "rust-intrinsic" {
|
||||
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
|
||||
}
|
||||
|
||||
struct X {
|
||||
x: i32,
|
||||
y: i64,
|
||||
t: [u8],
|
||||
}
|
||||
|
||||
const GOAL: usize = unsafe {
|
||||
let y: &X = transmute([0usize, 15]);
|
||||
size_of_val(y)
|
||||
};
|
||||
"#,
|
||||
32,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, fmt, builtin_impls
|
||||
extern "rust-intrinsic" {
|
||||
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
|
||||
}
|
||||
|
||||
const GOAL: usize = {
|
||||
let x: &i16 = &5;
|
||||
let y: &dyn core::fmt::Debug = x;
|
||||
let z: &dyn core::fmt::Debug = &y;
|
||||
size_of_val(x) + size_of_val(y) * 10 + size_of_val(z) * 100
|
||||
};
|
||||
"#,
|
||||
1622,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized
|
||||
extern "rust-intrinsic" {
|
||||
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
|
||||
}
|
||||
|
||||
const GOAL: usize = {
|
||||
size_of_val("salam")
|
||||
};
|
||||
"#,
|
||||
5,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn min_align_of_val() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized
|
||||
extern "rust-intrinsic" {
|
||||
pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;
|
||||
}
|
||||
|
||||
struct X(i32, u8);
|
||||
|
||||
const GOAL: usize = min_align_of_val(&X(1, 2));
|
||||
"#,
|
||||
4,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized
|
||||
extern "rust-intrinsic" {
|
||||
pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;
|
||||
}
|
||||
|
||||
const GOAL: usize = {
|
||||
let x: &[i32] = &[1, 2, 3];
|
||||
min_align_of_val(x)
|
||||
};
|
||||
"#,
|
||||
4,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_name() {
|
||||
check_str(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn type_name<T: ?Sized>() -> &'static str;
|
||||
}
|
||||
|
||||
const GOAL: &str = type_name::<i32>();
|
||||
"#,
|
||||
"i32",
|
||||
);
|
||||
check_str(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn type_name<T: ?Sized>() -> &'static str;
|
||||
}
|
||||
|
||||
mod mod1 {
|
||||
pub mod mod2 {
|
||||
pub struct Ty;
|
||||
}
|
||||
}
|
||||
|
||||
const GOAL: &str = type_name::<mod1::mod2::Ty>();
|
||||
"#,
|
||||
"mod1::mod2::Ty",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transmute() {
|
||||
check_number(
|
||||
@ -28,10 +193,29 @@ fn transmute() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_via_copy() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn read_via_copy<T>(e: *const T) -> T;
|
||||
pub fn volatile_load<T>(e: *const T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: i32 = {
|
||||
let x = 2;
|
||||
read_via_copy(&x) + volatile_load(&x)
|
||||
};
|
||||
"#,
|
||||
4,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn const_eval_select() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: fn
|
||||
extern "rust-intrinsic" {
|
||||
pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET
|
||||
where
|
||||
@ -68,7 +252,7 @@ fn wrapping_add() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saturating_add() {
|
||||
fn saturating() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
@ -79,6 +263,16 @@ fn saturating_add() {
|
||||
"#,
|
||||
255,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn saturating_sub<T>(a: T, b: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: bool = saturating_sub(5u8, 7) == 0 && saturating_sub(8u8, 4) == 4;
|
||||
"#,
|
||||
1,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
@ -112,6 +306,7 @@ fn allocator() {
|
||||
*ptr = 23;
|
||||
*ptr2 = 32;
|
||||
let ptr = __rust_realloc(ptr, 4, 1, 8);
|
||||
let ptr = __rust_realloc(ptr, 8, 1, 3);
|
||||
let ptr2 = ((ptr as usize) + 1) as *mut u8;
|
||||
*ptr + *ptr2
|
||||
};
|
||||
@ -159,6 +354,24 @@ fn needs_drop() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn discriminant_value() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: discriminant, option
|
||||
use core::marker::DiscriminantKind;
|
||||
extern "rust-intrinsic" {
|
||||
pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;
|
||||
}
|
||||
const GOAL: bool = {
|
||||
discriminant_value(&Some(2i32)) == discriminant_value(&Some(5i32))
|
||||
&& discriminant_value(&Some(2i32)) != discriminant_value(&None::<i32>)
|
||||
};
|
||||
"#,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn likely() {
|
||||
check_number(
|
||||
@ -327,6 +540,24 @@ fn copy_nonoverlapping() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_bytes() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
|
||||
}
|
||||
|
||||
const GOAL: i32 = unsafe {
|
||||
let mut x = 2;
|
||||
write_bytes(&mut x, 5, 1);
|
||||
x
|
||||
};
|
||||
"#,
|
||||
0x05050505,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy() {
|
||||
check_number(
|
||||
@ -362,6 +593,20 @@ fn ctpop() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ctlz() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn ctlz<T: Copy>(x: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: u8 = ctlz(0b0001_1100_u8);
|
||||
"#,
|
||||
3,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cttz() {
|
||||
check_number(
|
||||
@ -375,3 +620,85 @@ fn cttz() {
|
||||
3,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rotate() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn rotate_left<T: Copy>(x: T, y: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: i64 = rotate_left(0xaa00000000006e1i64, 12);
|
||||
"#,
|
||||
0x6e10aa,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn rotate_right<T: Copy>(x: T, y: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: i64 = rotate_right(0x6e10aa, 12);
|
||||
"#,
|
||||
0xaa00000000006e1,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn rotate_left<T: Copy>(x: T, y: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: i8 = rotate_left(129, 2);
|
||||
"#,
|
||||
6,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn rotate_right<T: Copy>(x: T, y: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: i32 = rotate_right(10006016, 1020315);
|
||||
"#,
|
||||
320192512,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simd() {
|
||||
check_number(
|
||||
r#"
|
||||
pub struct i8x16(
|
||||
i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,
|
||||
);
|
||||
extern "platform-intrinsic" {
|
||||
pub fn simd_bitmask<T, U>(x: T) -> U;
|
||||
}
|
||||
const GOAL: u16 = simd_bitmask(i8x16(
|
||||
0, 1, 0, 0, 2, 255, 100, 0, 50, 0, 1, 1, 0, 0, 0, 0
|
||||
));
|
||||
"#,
|
||||
0b0000110101110010,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
pub struct i8x16(
|
||||
i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,
|
||||
);
|
||||
extern "platform-intrinsic" {
|
||||
pub fn simd_lt<T, U>(x: T, y: T) -> U;
|
||||
pub fn simd_bitmask<T, U>(x: T) -> U;
|
||||
}
|
||||
const GOAL: u16 = simd_bitmask(simd_lt::<i8x16, i8x16>(
|
||||
i8x16(
|
||||
-105, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
||||
),
|
||||
i8x16(
|
||||
-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
|
||||
),
|
||||
));
|
||||
"#,
|
||||
0xFFFF,
|
||||
);
|
||||
}
|
||||
|
@ -110,6 +110,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||
#[salsa::invoke(crate::layout::target_data_layout_query)]
|
||||
fn target_data_layout(&self, krate: CrateId) -> Option<Arc<TargetDataLayout>>;
|
||||
|
||||
#[salsa::invoke(crate::method_resolution::lookup_impl_method_query)]
|
||||
fn lookup_impl_method(
|
||||
&self,
|
||||
env: Arc<crate::TraitEnvironment>,
|
||||
func: FunctionId,
|
||||
fn_subst: Substitution,
|
||||
) -> (FunctionId, Substitution);
|
||||
|
||||
#[salsa::invoke(crate::lower::callable_item_sig)]
|
||||
fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
|
||||
|
||||
|
@ -5,7 +5,7 @@ mod unsafe_check;
|
||||
mod decl_check;
|
||||
|
||||
pub use crate::diagnostics::{
|
||||
decl_check::{incorrect_case, IncorrectCase},
|
||||
decl_check::{incorrect_case, CaseType, IncorrectCase},
|
||||
expr::{
|
||||
record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic,
|
||||
},
|
||||
|
@ -57,11 +57,11 @@ pub fn incorrect_case(
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CaseType {
|
||||
// `some_var`
|
||||
/// `some_var`
|
||||
LowerSnakeCase,
|
||||
// `SOME_CONST`
|
||||
/// `SOME_CONST`
|
||||
UpperSnakeCase,
|
||||
// `SomeStruct`
|
||||
/// `SomeStruct`
|
||||
UpperCamelCase,
|
||||
}
|
||||
|
||||
@ -181,6 +181,7 @@ impl<'a> DeclValidator<'a> {
|
||||
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()),
|
||||
// These warnings should not explore macro definitions at all
|
||||
AttrDefId::MacroId(_) => None,
|
||||
AttrDefId::AdtId(aid) => match aid {
|
||||
|
@ -192,7 +192,7 @@ pub trait HirDisplay {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> HirFormatter<'a> {
|
||||
impl HirFormatter<'_> {
|
||||
pub fn write_joined<T: HirDisplay>(
|
||||
&mut self,
|
||||
iter: impl IntoIterator<Item = T>,
|
||||
@ -342,7 +342,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
|
||||
impl<T> fmt::Display for HirDisplayWrapper<'_, T>
|
||||
where
|
||||
T: HirDisplay,
|
||||
{
|
||||
@ -360,7 +360,7 @@ where
|
||||
|
||||
const TYPE_HINT_TRUNCATION: &str = "…";
|
||||
|
||||
impl<T: HirDisplay> HirDisplay for &'_ T {
|
||||
impl<T: HirDisplay> HirDisplay for &T {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
HirDisplay::hir_fmt(*self, f)
|
||||
}
|
||||
@ -446,28 +446,6 @@ impl HirDisplay for Const {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HexifiedConst(pub Const);
|
||||
|
||||
impl HirDisplay for HexifiedConst {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
let data = &self.0.data(Interner);
|
||||
if let TyKind::Scalar(s) = data.ty.kind(Interner) {
|
||||
if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) {
|
||||
if let ConstValue::Concrete(c) = &data.value {
|
||||
if let ConstScalar::Bytes(b, m) = &c.interned {
|
||||
let value = u128::from_le_bytes(pad16(b, false));
|
||||
if value >= 10 {
|
||||
render_const_scalar(f, &b, m, &data.ty)?;
|
||||
return write!(f, " ({:#X})", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.0.hir_fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
fn render_const_scalar(
|
||||
f: &mut HirFormatter<'_>,
|
||||
b: &[u8],
|
||||
@ -481,28 +459,28 @@ fn render_const_scalar(
|
||||
TyKind::Scalar(s) => match s {
|
||||
Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }),
|
||||
Scalar::Char => {
|
||||
let x = u128::from_le_bytes(pad16(b, false)) as u32;
|
||||
let Ok(c) = char::try_from(x) else {
|
||||
let it = u128::from_le_bytes(pad16(b, false)) as u32;
|
||||
let Ok(c) = char::try_from(it) else {
|
||||
return f.write_str("<unicode-error>");
|
||||
};
|
||||
write!(f, "{c:?}")
|
||||
}
|
||||
Scalar::Int(_) => {
|
||||
let x = i128::from_le_bytes(pad16(b, true));
|
||||
write!(f, "{x}")
|
||||
let it = i128::from_le_bytes(pad16(b, true));
|
||||
write!(f, "{it}")
|
||||
}
|
||||
Scalar::Uint(_) => {
|
||||
let x = u128::from_le_bytes(pad16(b, false));
|
||||
write!(f, "{x}")
|
||||
let it = u128::from_le_bytes(pad16(b, false));
|
||||
write!(f, "{it}")
|
||||
}
|
||||
Scalar::Float(fl) => match fl {
|
||||
chalk_ir::FloatTy::F32 => {
|
||||
let x = f32::from_le_bytes(b.try_into().unwrap());
|
||||
write!(f, "{x:?}")
|
||||
let it = f32::from_le_bytes(b.try_into().unwrap());
|
||||
write!(f, "{it:?}")
|
||||
}
|
||||
chalk_ir::FloatTy::F64 => {
|
||||
let x = f64::from_le_bytes(b.try_into().unwrap());
|
||||
write!(f, "{x:?}")
|
||||
let it = f64::from_le_bytes(b.try_into().unwrap());
|
||||
write!(f, "{it:?}")
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -636,7 +614,8 @@ fn render_const_scalar(
|
||||
}
|
||||
hir_def::AdtId::EnumId(e) => {
|
||||
let Some((var_id, var_layout)) =
|
||||
detect_variant_from_bytes(&layout, f.db, krate, b, e) else {
|
||||
detect_variant_from_bytes(&layout, f.db, krate, b, e)
|
||||
else {
|
||||
return f.write_str("<failed-to-detect-variant>");
|
||||
};
|
||||
let data = &f.db.enum_data(e).variants[var_id];
|
||||
@ -658,8 +637,8 @@ fn render_const_scalar(
|
||||
}
|
||||
TyKind::FnDef(..) => ty.hir_fmt(f),
|
||||
TyKind::Function(_) | TyKind::Raw(_, _) => {
|
||||
let x = u128::from_le_bytes(pad16(b, false));
|
||||
write!(f, "{:#X} as ", x)?;
|
||||
let it = u128::from_le_bytes(pad16(b, false));
|
||||
write!(f, "{:#X} as ", it)?;
|
||||
ty.hir_fmt(f)
|
||||
}
|
||||
TyKind::Array(ty, len) => {
|
||||
@ -735,7 +714,7 @@ fn render_variant_after_name(
|
||||
}
|
||||
write!(f, " }}")?;
|
||||
} else {
|
||||
let mut it = it.map(|x| x.0);
|
||||
let mut it = it.map(|it| it.0);
|
||||
write!(f, "(")?;
|
||||
if let Some(id) = it.next() {
|
||||
render_field(f, id)?;
|
||||
@ -1277,19 +1256,20 @@ fn hir_fmt_generics(
|
||||
i: usize,
|
||||
parameters: &Substitution,
|
||||
) -> bool {
|
||||
if parameter.ty(Interner).map(|x| x.kind(Interner)) == Some(&TyKind::Error)
|
||||
if parameter.ty(Interner).map(|it| it.kind(Interner))
|
||||
== Some(&TyKind::Error)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if let Some(ConstValue::Concrete(c)) =
|
||||
parameter.constant(Interner).map(|x| &x.data(Interner).value)
|
||||
parameter.constant(Interner).map(|it| &it.data(Interner).value)
|
||||
{
|
||||
if c.interned == ConstScalar::Unknown {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
let default_parameter = match default_parameters.get(i) {
|
||||
Some(x) => x,
|
||||
Some(it) => it,
|
||||
None => return true,
|
||||
};
|
||||
let actual_default =
|
||||
|
@ -290,7 +290,7 @@ impl Default for InternedStandardTypes {
|
||||
/// ```
|
||||
///
|
||||
/// Note that for a struct, the 'deep' unsizing of the struct is not recorded.
|
||||
/// E.g., `struct Foo<T> { x: T }` we can coerce &Foo<[i32; 4]> to &Foo<[i32]>
|
||||
/// E.g., `struct Foo<T> { it: T }` we can coerce &Foo<[i32; 4]> to &Foo<[i32]>
|
||||
/// The autoderef and -ref are the same as in the above example, but the type
|
||||
/// stored in `unsize` is `Foo<[i32]>`, we don't store any further detail about
|
||||
/// the underlying conversions from `[i32; 4]` to `[i32]`.
|
||||
@ -1172,7 +1172,7 @@ impl<'a> InferenceContext<'a> {
|
||||
unresolved: Option<usize>,
|
||||
path: &ModPath,
|
||||
) -> (Ty, Option<VariantId>) {
|
||||
let remaining = unresolved.map(|x| path.segments()[x..].len()).filter(|x| x > &0);
|
||||
let remaining = unresolved.map(|it| path.segments()[it..].len()).filter(|it| it > &0);
|
||||
match remaining {
|
||||
None => {
|
||||
let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
|
||||
@ -1232,7 +1232,9 @@ impl<'a> InferenceContext<'a> {
|
||||
.as_function()?
|
||||
.lookup(self.db.upcast())
|
||||
.container
|
||||
else { return None };
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
self.resolve_output_on(trait_)
|
||||
}
|
||||
|
||||
@ -1322,7 +1324,7 @@ impl Expectation {
|
||||
/// The primary use case is where the expected type is a fat pointer,
|
||||
/// like `&[isize]`. For example, consider the following statement:
|
||||
///
|
||||
/// let x: &[isize] = &[1, 2, 3];
|
||||
/// let it: &[isize] = &[1, 2, 3];
|
||||
///
|
||||
/// In this case, the expected type for the `&[1, 2, 3]` expression is
|
||||
/// `&[isize]`. If however we were to say that `[1, 2, 3]` has the
|
||||
|
@ -139,7 +139,7 @@ impl HirPlace {
|
||||
) -> CaptureKind {
|
||||
match current_capture {
|
||||
CaptureKind::ByRef(BorrowKind::Mut { .. }) => {
|
||||
if self.projections[len..].iter().any(|x| *x == ProjectionElem::Deref) {
|
||||
if self.projections[len..].iter().any(|it| *it == ProjectionElem::Deref) {
|
||||
current_capture = CaptureKind::ByRef(BorrowKind::Unique);
|
||||
}
|
||||
}
|
||||
@ -199,7 +199,7 @@ impl CapturedItem {
|
||||
.to_string(),
|
||||
VariantData::Tuple(fields) => fields
|
||||
.iter()
|
||||
.position(|x| x.0 == f.local_id)
|
||||
.position(|it| it.0 == f.local_id)
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
VariantData::Unit => "[missing field]".to_string(),
|
||||
@ -439,10 +439,10 @@ impl InferenceContext<'_> {
|
||||
}
|
||||
|
||||
fn walk_expr(&mut self, tgt_expr: ExprId) {
|
||||
if let Some(x) = self.result.expr_adjustments.get_mut(&tgt_expr) {
|
||||
if let Some(it) = self.result.expr_adjustments.get_mut(&tgt_expr) {
|
||||
// FIXME: this take is completely unneeded, and just is here to make borrow checker
|
||||
// happy. Remove it if you can.
|
||||
let x_taken = mem::take(x);
|
||||
let x_taken = mem::take(it);
|
||||
self.walk_expr_with_adjust(tgt_expr, &x_taken);
|
||||
*self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken;
|
||||
} else {
|
||||
@ -536,7 +536,7 @@ impl InferenceContext<'_> {
|
||||
if let &Some(expr) = spread {
|
||||
self.consume_expr(expr);
|
||||
}
|
||||
self.consume_exprs(fields.iter().map(|x| x.expr));
|
||||
self.consume_exprs(fields.iter().map(|it| it.expr));
|
||||
}
|
||||
Expr::Field { expr, name: _ } => self.select_from_expr(*expr),
|
||||
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
|
||||
@ -548,7 +548,7 @@ impl InferenceContext<'_> {
|
||||
} else if let Some((f, _)) = self.result.method_resolution(tgt_expr) {
|
||||
let mutability = 'b: {
|
||||
if let Some(deref_trait) =
|
||||
self.resolve_lang_item(LangItem::DerefMut).and_then(|x| x.as_trait())
|
||||
self.resolve_lang_item(LangItem::DerefMut).and_then(|it| it.as_trait())
|
||||
{
|
||||
if let Some(deref_fn) =
|
||||
self.db.trait_data(deref_trait).method_by_name(&name![deref_mut])
|
||||
@ -615,8 +615,8 @@ impl InferenceContext<'_> {
|
||||
"We sort closures, so we should always have data for inner closures",
|
||||
);
|
||||
let mut cc = mem::take(&mut self.current_captures);
|
||||
cc.extend(captures.iter().filter(|x| self.is_upvar(&x.place)).map(|x| {
|
||||
CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind, span: x.span }
|
||||
cc.extend(captures.iter().filter(|it| self.is_upvar(&it.place)).map(|it| {
|
||||
CapturedItemWithoutTy { place: it.place.clone(), kind: it.kind, span: it.span }
|
||||
}));
|
||||
self.current_captures = cc;
|
||||
}
|
||||
@ -694,7 +694,7 @@ impl InferenceContext<'_> {
|
||||
},
|
||||
},
|
||||
}
|
||||
if self.result.pat_adjustments.get(&p).map_or(false, |x| !x.is_empty()) {
|
||||
if self.result.pat_adjustments.get(&p).map_or(false, |it| !it.is_empty()) {
|
||||
for_mut = BorrowKind::Unique;
|
||||
}
|
||||
self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut));
|
||||
@ -706,9 +706,9 @@ impl InferenceContext<'_> {
|
||||
|
||||
fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty {
|
||||
let mut ty = None;
|
||||
if let Some(x) = self.result.expr_adjustments.get(&e) {
|
||||
if let Some(x) = x.last() {
|
||||
ty = Some(x.target.clone());
|
||||
if let Some(it) = self.result.expr_adjustments.get(&e) {
|
||||
if let Some(it) = it.last() {
|
||||
ty = Some(it.target.clone());
|
||||
}
|
||||
}
|
||||
ty.unwrap_or_else(|| self.expr_ty(e))
|
||||
@ -727,7 +727,7 @@ impl InferenceContext<'_> {
|
||||
// FIXME: We handle closure as a special case, since chalk consider every closure as copy. We
|
||||
// should probably let chalk know which closures are copy, but I don't know how doing it
|
||||
// without creating query cycles.
|
||||
return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true);
|
||||
return self.result.closure_info.get(id).map(|it| it.1 == FnTrait::Fn).unwrap_or(true);
|
||||
}
|
||||
self.table.resolve_completely(ty).is_copy(self.db, self.owner)
|
||||
}
|
||||
@ -748,7 +748,7 @@ impl InferenceContext<'_> {
|
||||
}
|
||||
|
||||
fn minimize_captures(&mut self) {
|
||||
self.current_captures.sort_by_key(|x| x.place.projections.len());
|
||||
self.current_captures.sort_by_key(|it| it.place.projections.len());
|
||||
let mut hash_map = HashMap::<HirPlace, usize>::new();
|
||||
let result = mem::take(&mut self.current_captures);
|
||||
for item in result {
|
||||
@ -759,7 +759,7 @@ impl InferenceContext<'_> {
|
||||
break Some(*k);
|
||||
}
|
||||
match it.next() {
|
||||
Some(x) => lookup_place.projections.push(x.clone()),
|
||||
Some(it) => lookup_place.projections.push(it.clone()),
|
||||
None => break None,
|
||||
}
|
||||
};
|
||||
@ -780,7 +780,7 @@ impl InferenceContext<'_> {
|
||||
}
|
||||
|
||||
fn consume_with_pat(&mut self, mut place: HirPlace, pat: PatId) {
|
||||
let cnt = self.result.pat_adjustments.get(&pat).map(|x| x.len()).unwrap_or_default();
|
||||
let cnt = self.result.pat_adjustments.get(&pat).map(|it| it.len()).unwrap_or_default();
|
||||
place.projections = place
|
||||
.projections
|
||||
.iter()
|
||||
@ -894,10 +894,10 @@ impl InferenceContext<'_> {
|
||||
|
||||
fn closure_kind(&self) -> FnTrait {
|
||||
let mut r = FnTrait::Fn;
|
||||
for x in &self.current_captures {
|
||||
for it in &self.current_captures {
|
||||
r = cmp::min(
|
||||
r,
|
||||
match &x.kind {
|
||||
match &it.kind {
|
||||
CaptureKind::ByRef(BorrowKind::Unique | BorrowKind::Mut { .. }) => {
|
||||
FnTrait::FnMut
|
||||
}
|
||||
@ -933,7 +933,7 @@ impl InferenceContext<'_> {
|
||||
}
|
||||
self.minimize_captures();
|
||||
let result = mem::take(&mut self.current_captures);
|
||||
let captures = result.into_iter().map(|x| x.with_ty(self)).collect::<Vec<_>>();
|
||||
let captures = result.into_iter().map(|it| it.with_ty(self)).collect::<Vec<_>>();
|
||||
self.result.closure_info.insert(closure, (captures, closure_kind));
|
||||
closure_kind
|
||||
}
|
||||
@ -973,20 +973,20 @@ impl InferenceContext<'_> {
|
||||
fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>)> {
|
||||
let mut deferred_closures = mem::take(&mut self.deferred_closures);
|
||||
let mut dependents_count: FxHashMap<ClosureId, usize> =
|
||||
deferred_closures.keys().map(|x| (*x, 0)).collect();
|
||||
deferred_closures.keys().map(|it| (*it, 0)).collect();
|
||||
for (_, deps) in &self.closure_dependencies {
|
||||
for dep in deps {
|
||||
*dependents_count.entry(*dep).or_default() += 1;
|
||||
}
|
||||
}
|
||||
let mut queue: Vec<_> =
|
||||
deferred_closures.keys().copied().filter(|x| dependents_count[x] == 0).collect();
|
||||
deferred_closures.keys().copied().filter(|it| dependents_count[it] == 0).collect();
|
||||
let mut result = vec![];
|
||||
while let Some(x) = queue.pop() {
|
||||
if let Some(d) = deferred_closures.remove(&x) {
|
||||
result.push((x, d));
|
||||
while let Some(it) = queue.pop() {
|
||||
if let Some(d) = deferred_closures.remove(&it) {
|
||||
result.push((it, d));
|
||||
}
|
||||
for dep in self.closure_dependencies.get(&x).into_iter().flat_map(|x| x.iter()) {
|
||||
for dep in self.closure_dependencies.get(&it).into_iter().flat_map(|it| it.iter()) {
|
||||
let cnt = dependents_count.get_mut(dep).unwrap();
|
||||
*cnt -= 1;
|
||||
if *cnt == 0 {
|
||||
|
@ -220,7 +220,7 @@ pub(crate) fn coerce(
|
||||
Ok((adjustments, table.resolve_with_fallback(ty, &fallback)))
|
||||
}
|
||||
|
||||
impl<'a> InferenceContext<'a> {
|
||||
impl InferenceContext<'_> {
|
||||
/// Unify two types, but may coerce the first one to the second one
|
||||
/// using "implicit coercion rules" if needed.
|
||||
pub(super) fn coerce(
|
||||
@ -239,7 +239,7 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InferenceTable<'a> {
|
||||
impl InferenceTable<'_> {
|
||||
/// Unify two types, but may coerce the first one to the second one
|
||||
/// using "implicit coercion rules" if needed.
|
||||
pub(crate) fn coerce(
|
||||
@ -377,7 +377,7 @@ impl<'a> InferenceTable<'a> {
|
||||
|
||||
let snapshot = self.snapshot();
|
||||
|
||||
let mut autoderef = Autoderef::new(self, from_ty.clone());
|
||||
let mut autoderef = Autoderef::new(self, from_ty.clone(), false);
|
||||
let mut first_error = None;
|
||||
let mut found = None;
|
||||
|
||||
|
@ -50,7 +50,7 @@ use super::{
|
||||
InferenceContext, InferenceDiagnostic, TypeMismatch,
|
||||
};
|
||||
|
||||
impl<'a> InferenceContext<'a> {
|
||||
impl InferenceContext<'_> {
|
||||
pub(crate) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
let ty = self.infer_expr_inner(tgt_expr, expected);
|
||||
if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
|
||||
@ -316,7 +316,7 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
Expr::Call { callee, args, .. } => {
|
||||
let callee_ty = self.infer_expr(*callee, &Expectation::none());
|
||||
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone());
|
||||
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false);
|
||||
let (res, derefed_callee) = 'b: {
|
||||
// manual loop to be able to access `derefs.table`
|
||||
while let Some((callee_deref_ty, _)) = derefs.next() {
|
||||
@ -928,7 +928,7 @@ impl<'a> InferenceContext<'a> {
|
||||
if let TyKind::Ref(Mutability::Mut, _, inner) = derefed_callee.kind(Interner) {
|
||||
if adjustments
|
||||
.last()
|
||||
.map(|x| matches!(x.kind, Adjust::Borrow(_)))
|
||||
.map(|it| matches!(it.kind, Adjust::Borrow(_)))
|
||||
.unwrap_or(true)
|
||||
{
|
||||
// prefer reborrow to move
|
||||
@ -1385,7 +1385,7 @@ impl<'a> InferenceContext<'a> {
|
||||
receiver_ty: &Ty,
|
||||
name: &Name,
|
||||
) -> Option<(Ty, Option<FieldId>, Vec<Adjustment>, bool)> {
|
||||
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone());
|
||||
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false);
|
||||
let mut private_field = None;
|
||||
let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
|
||||
let (field_id, parameters) = match derefed_ty.kind(Interner) {
|
||||
@ -1449,6 +1449,13 @@ impl<'a> InferenceContext<'a> {
|
||||
|
||||
fn infer_field_access(&mut self, tgt_expr: ExprId, receiver: ExprId, name: &Name) -> Ty {
|
||||
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
|
||||
|
||||
if name.is_missing() {
|
||||
// Bail out early, don't even try to look up field. Also, we don't issue an unresolved
|
||||
// field diagnostic because this is a syntax error rather than a semantic error.
|
||||
return self.err_ty();
|
||||
}
|
||||
|
||||
match self.lookup_field(&receiver_ty, name) {
|
||||
Some((ty, field_id, adjustments, is_public)) => {
|
||||
self.write_expr_adj(receiver, adjustments);
|
||||
|
@ -12,7 +12,7 @@ use crate::{lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, Ov
|
||||
|
||||
use super::InferenceContext;
|
||||
|
||||
impl<'a> InferenceContext<'a> {
|
||||
impl InferenceContext<'_> {
|
||||
pub(crate) fn infer_mut_body(&mut self) {
|
||||
self.infer_mut_expr(self.body.body_expr, Mutability::Not);
|
||||
}
|
||||
@ -73,12 +73,12 @@ impl<'a> InferenceContext<'a> {
|
||||
self.infer_mut_expr(c, Mutability::Not);
|
||||
self.infer_mut_expr(body, Mutability::Not);
|
||||
}
|
||||
Expr::MethodCall { receiver: x, method_name: _, args, generic_args: _ }
|
||||
| Expr::Call { callee: x, args, is_assignee_expr: _ } => {
|
||||
self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*x)));
|
||||
Expr::MethodCall { receiver: it, method_name: _, args, generic_args: _ }
|
||||
| Expr::Call { callee: it, args, is_assignee_expr: _ } => {
|
||||
self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*it)));
|
||||
}
|
||||
Expr::Match { expr, arms } => {
|
||||
let m = self.pat_iter_bound_mutability(arms.iter().map(|x| x.pat));
|
||||
let m = self.pat_iter_bound_mutability(arms.iter().map(|it| it.pat));
|
||||
self.infer_mut_expr(*expr, m);
|
||||
for arm in arms.iter() {
|
||||
self.infer_mut_expr(arm.expr, Mutability::Not);
|
||||
@ -96,7 +96,7 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
}
|
||||
Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => {
|
||||
self.infer_mut_not_expr_iter(fields.iter().map(|x| x.expr).chain(*spread))
|
||||
self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread))
|
||||
}
|
||||
&Expr::Index { base, index } => {
|
||||
if mutability == Mutability::Mut {
|
||||
@ -204,8 +204,8 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
|
||||
/// Checks if the pat contains a `ref mut` binding. Such paths makes the context of bounded expressions
|
||||
/// mutable. For example in `let (ref mut x0, ref x1) = *x;` we need to use `DerefMut` for `*x` but in
|
||||
/// `let (ref x0, ref x1) = *x;` we should use `Deref`.
|
||||
/// mutable. For example in `let (ref mut x0, ref x1) = *it;` we need to use `DerefMut` for `*it` but in
|
||||
/// `let (ref x0, ref x1) = *it;` we should use `Deref`.
|
||||
fn pat_bound_mutability(&self, pat: PatId) -> Mutability {
|
||||
let mut r = Mutability::Not;
|
||||
self.body.walk_bindings_in_pat(pat, |b| {
|
||||
|
@ -56,7 +56,7 @@ impl PatLike for PatId {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InferenceContext<'a> {
|
||||
impl InferenceContext<'_> {
|
||||
/// Infers type for tuple struct pattern or its corresponding assignee expression.
|
||||
///
|
||||
/// Ellipses found in the original pattern or expression must be filtered out.
|
||||
@ -306,7 +306,7 @@ impl<'a> InferenceContext<'a> {
|
||||
self.result
|
||||
.pat_adjustments
|
||||
.get(&pat)
|
||||
.and_then(|x| x.first())
|
||||
.and_then(|it| it.first())
|
||||
.unwrap_or(&self.result.type_of_pat[pat])
|
||||
.clone()
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ use crate::{
|
||||
TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
|
||||
};
|
||||
|
||||
impl<'a> InferenceContext<'a> {
|
||||
impl InferenceContext<'_> {
|
||||
pub(super) fn canonicalize<T: TypeFoldable<Interner> + HasInterner<Interner = Interner>>(
|
||||
&mut self,
|
||||
t: T,
|
||||
@ -91,7 +91,7 @@ pub(crate) fn unify(
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let vars = Substitution::from_iter(
|
||||
Interner,
|
||||
tys.binders.iter(Interner).map(|x| match &x.kind {
|
||||
tys.binders.iter(Interner).map(|it| match &it.kind {
|
||||
chalk_ir::VariableKind::Ty(_) => {
|
||||
GenericArgData::Ty(table.new_type_var()).intern(Interner)
|
||||
}
|
||||
@ -547,7 +547,7 @@ impl<'a> InferenceTable<'a> {
|
||||
table: &'a mut InferenceTable<'b>,
|
||||
highest_known_var: InferenceVar,
|
||||
}
|
||||
impl<'a, 'b> TypeFolder<Interner> for VarFudger<'a, 'b> {
|
||||
impl TypeFolder<Interner> for VarFudger<'_, '_> {
|
||||
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
|
||||
self
|
||||
}
|
||||
@ -686,8 +686,8 @@ impl<'a> InferenceTable<'a> {
|
||||
|
||||
let mut arg_tys = vec![];
|
||||
let arg_ty = TyBuilder::tuple(num_args)
|
||||
.fill(|x| {
|
||||
let arg = match x {
|
||||
.fill(|it| {
|
||||
let arg = match it {
|
||||
ParamKind::Type => self.new_type_var(),
|
||||
ParamKind::Const(ty) => {
|
||||
never!("Tuple with const parameter");
|
||||
@ -753,7 +753,7 @@ impl<'a> InferenceTable<'a> {
|
||||
{
|
||||
fold_tys_and_consts(
|
||||
ty,
|
||||
|x, _| match x {
|
||||
|it, _| match it {
|
||||
Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)),
|
||||
Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)),
|
||||
},
|
||||
@ -798,7 +798,7 @@ impl<'a> InferenceTable<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for InferenceTable<'a> {
|
||||
impl fmt::Debug for InferenceTable<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("InferenceTable").field("num_vars", &self.type_variable_table.len()).finish()
|
||||
}
|
||||
@ -826,7 +826,7 @@ mod resolve {
|
||||
pub(super) var_stack: &'a mut Vec<InferenceVar>,
|
||||
pub(super) fallback: F,
|
||||
}
|
||||
impl<'a, 'b, F> TypeFolder<Interner> for Resolver<'a, 'b, F>
|
||||
impl<F> TypeFolder<Interner> for Resolver<'_, '_, F>
|
||||
where
|
||||
F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
|
||||
{
|
||||
|
@ -7,7 +7,7 @@ use hir_def::{
|
||||
Abi, FieldsShape, Integer, LayoutCalculator, LayoutS, Primitive, ReprOptions, Scalar, Size,
|
||||
StructKind, TargetDataLayout, WrappingRange,
|
||||
},
|
||||
LocalEnumVariantId, LocalFieldId,
|
||||
LocalEnumVariantId, LocalFieldId, StructId,
|
||||
};
|
||||
use la_arena::{Idx, RawIdx};
|
||||
use stdx::never;
|
||||
@ -24,8 +24,8 @@ pub use self::{
|
||||
};
|
||||
|
||||
macro_rules! user_error {
|
||||
($x: expr) => {
|
||||
return Err(LayoutError::UserError(format!($x)))
|
||||
($it: expr) => {
|
||||
return Err(LayoutError::UserError(format!($it)))
|
||||
};
|
||||
}
|
||||
|
||||
@ -77,18 +77,101 @@ impl<'a> LayoutCalculator for LayoutCx<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: move this to the `rustc_abi`.
|
||||
fn layout_of_simd_ty(
|
||||
db: &dyn HirDatabase,
|
||||
id: StructId,
|
||||
subst: &Substitution,
|
||||
krate: CrateId,
|
||||
dl: &TargetDataLayout,
|
||||
) -> Result<Arc<Layout>, LayoutError> {
|
||||
let fields = db.field_types(id.into());
|
||||
|
||||
// Supported SIMD vectors are homogeneous ADTs with at least one field:
|
||||
//
|
||||
// * #[repr(simd)] struct S(T, T, T, T);
|
||||
// * #[repr(simd)] struct S { it: T, y: T, z: T, w: T }
|
||||
// * #[repr(simd)] struct S([T; 4])
|
||||
//
|
||||
// where T is a primitive scalar (integer/float/pointer).
|
||||
|
||||
let f0_ty = match fields.iter().next() {
|
||||
Some(it) => it.1.clone().substitute(Interner, subst),
|
||||
None => {
|
||||
user_error!("simd type with zero fields");
|
||||
}
|
||||
};
|
||||
|
||||
// The element type and number of elements of the SIMD vector
|
||||
// are obtained from:
|
||||
//
|
||||
// * the element type and length of the single array field, if
|
||||
// the first field is of array type, or
|
||||
//
|
||||
// * 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(), krate)?.fields else {
|
||||
user_error!("Array with non array layout");
|
||||
};
|
||||
|
||||
(e_ty.clone(), count, true)
|
||||
} else {
|
||||
// First ADT field is not an array:
|
||||
(f0_ty, fields.iter().count() as u64, false)
|
||||
};
|
||||
|
||||
// Compute the ABI of the element type:
|
||||
let e_ly = db.layout_of_ty(e_ty, krate)?;
|
||||
let Abi::Scalar(e_abi) = e_ly.abi else {
|
||||
user_error!("simd type with inner non scalar type");
|
||||
};
|
||||
|
||||
// Compute the size and alignment of the vector:
|
||||
let size = e_ly.size.checked_mul(e_len, dl).ok_or(LayoutError::SizeOverflow)?;
|
||||
let align = dl.vector_align(size);
|
||||
let size = size.align_to(align.abi);
|
||||
|
||||
// Compute the placement of the vector fields:
|
||||
let fields = if is_array {
|
||||
FieldsShape::Arbitrary { offsets: [Size::ZERO].into(), memory_index: [0].into() }
|
||||
} else {
|
||||
FieldsShape::Array { stride: e_ly.size, count: e_len }
|
||||
};
|
||||
|
||||
Ok(Arc::new(Layout {
|
||||
variants: Variants::Single { index: struct_variant_idx() },
|
||||
fields,
|
||||
abi: Abi::Vector { element: e_abi, count: e_len },
|
||||
largest_niche: e_ly.largest_niche,
|
||||
size,
|
||||
align,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn layout_of_ty_query(
|
||||
db: &dyn HirDatabase,
|
||||
ty: Ty,
|
||||
krate: CrateId,
|
||||
) -> Result<Arc<Layout>, LayoutError> {
|
||||
let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
|
||||
let Some(target) = db.target_data_layout(krate) else {
|
||||
return Err(LayoutError::TargetLayoutNotAvailable);
|
||||
};
|
||||
let cx = LayoutCx { krate, target: &target };
|
||||
let dl = &*cx.current_data_layout();
|
||||
let trait_env = Arc::new(TraitEnvironment::empty(krate));
|
||||
let ty = normalize(db, trait_env, ty.clone());
|
||||
let result = match ty.kind(Interner) {
|
||||
TyKind::Adt(AdtId(def), subst) => return db.layout_of_adt(*def, subst.clone(), krate),
|
||||
TyKind::Adt(AdtId(def), subst) => {
|
||||
if let hir_def::AdtId::StructId(s) = def {
|
||||
let data = db.struct_data(*s);
|
||||
let repr = data.repr.unwrap_or_default();
|
||||
if repr.simd() {
|
||||
return layout_of_simd_ty(db, *s, subst, krate, &target);
|
||||
}
|
||||
};
|
||||
return db.layout_of_adt(*def, subst.clone(), krate);
|
||||
}
|
||||
TyKind::Scalar(s) => match s {
|
||||
chalk_ir::Scalar::Bool => Layout::scalar(
|
||||
dl,
|
||||
@ -147,7 +230,7 @@ pub fn layout_of_ty_query(
|
||||
.iter(Interner)
|
||||
.map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), krate))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let fields = fields.iter().map(|x| &**x).collect::<Vec<_>>();
|
||||
let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
|
||||
let fields = fields.iter().collect::<Vec<_>>();
|
||||
cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)?
|
||||
}
|
||||
@ -265,14 +348,14 @@ pub fn layout_of_ty_query(
|
||||
let (captures, _) = infer.closure_info(c);
|
||||
let fields = captures
|
||||
.iter()
|
||||
.map(|x| {
|
||||
.map(|it| {
|
||||
db.layout_of_ty(
|
||||
x.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()),
|
||||
it.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()),
|
||||
krate,
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let fields = fields.iter().map(|x| &**x).collect::<Vec<_>>();
|
||||
let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
|
||||
let fields = fields.iter().collect::<Vec<_>>();
|
||||
cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized)
|
||||
.ok_or(LayoutError::Unknown)?
|
||||
@ -315,7 +398,10 @@ fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty {
|
||||
let data = db.struct_data(*i);
|
||||
let mut it = data.variant_data.fields().iter().rev();
|
||||
match it.next() {
|
||||
Some((f, _)) => field_ty(db, (*i).into(), f, subst),
|
||||
Some((f, _)) => {
|
||||
let last_field_ty = field_ty(db, (*i).into(), f, subst);
|
||||
struct_tail_erasing_lifetimes(db, last_field_ty)
|
||||
}
|
||||
None => pointee,
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,9 @@ pub fn layout_of_adt_query(
|
||||
subst: Substitution,
|
||||
krate: CrateId,
|
||||
) -> Result<Arc<Layout>, LayoutError> {
|
||||
let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
|
||||
let Some(target) = db.target_data_layout(krate) else {
|
||||
return Err(LayoutError::TargetLayoutNotAvailable);
|
||||
};
|
||||
let cx = LayoutCx { krate, target: &target };
|
||||
let dl = cx.current_data_layout();
|
||||
let handle_variant = |def: VariantId, var: &VariantData| {
|
||||
@ -70,9 +72,9 @@ pub fn layout_of_adt_query(
|
||||
};
|
||||
let variants = variants
|
||||
.iter()
|
||||
.map(|x| x.iter().map(|x| &**x).collect::<Vec<_>>())
|
||||
.map(|it| it.iter().map(|it| &**it).collect::<Vec<_>>())
|
||||
.collect::<SmallVec<[_; 1]>>();
|
||||
let variants = variants.iter().map(|x| x.iter().collect()).collect();
|
||||
let variants = variants.iter().map(|it| it.iter().collect()).collect();
|
||||
let result = if matches!(def, AdtId::UnionId(..)) {
|
||||
cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)?
|
||||
} else {
|
||||
@ -103,7 +105,7 @@ pub fn layout_of_adt_query(
|
||||
&& variants
|
||||
.iter()
|
||||
.next()
|
||||
.and_then(|x| x.last().map(|x| x.is_unsized()))
|
||||
.and_then(|it| it.last().map(|it| !it.is_unsized()))
|
||||
.unwrap_or(true),
|
||||
)
|
||||
.ok_or(LayoutError::SizeOverflow)?
|
||||
@ -116,9 +118,9 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>,
|
||||
let get = |name| {
|
||||
let attr = attrs.by_key(name).tt_values();
|
||||
for tree in attr {
|
||||
if let Some(x) = tree.token_trees.first() {
|
||||
if let Ok(x) = x.to_string().parse() {
|
||||
return Bound::Included(x);
|
||||
if let Some(it) = tree.token_trees.first() {
|
||||
if let Ok(it) = it.to_string().parse() {
|
||||
return Bound::Included(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -270,6 +270,20 @@ struct Goal(Foo<S>);
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simd_types() {
|
||||
check_size_and_align(
|
||||
r#"
|
||||
#[repr(simd)]
|
||||
struct SimdType(i64, i64);
|
||||
struct Goal(SimdType);
|
||||
"#,
|
||||
"",
|
||||
16,
|
||||
16,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_position_impl_trait() {
|
||||
size_and_align_expr! {
|
||||
@ -343,6 +357,24 @@ fn return_position_impl_trait() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsized_ref() {
|
||||
size_and_align! {
|
||||
struct S1([u8]);
|
||||
struct S2(S1);
|
||||
struct S3(i32, str);
|
||||
struct S4(u64, S3);
|
||||
#[allow(dead_code)]
|
||||
struct S5 {
|
||||
field1: u8,
|
||||
field2: i16,
|
||||
field_last: S4,
|
||||
}
|
||||
|
||||
struct Goal(&'static S1, &'static S2, &'static S3, &'static S4, &'static S5);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enums() {
|
||||
size_and_align! {
|
||||
@ -369,11 +401,11 @@ fn tuple() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_zero() {
|
||||
fn non_zero_and_non_null() {
|
||||
size_and_align! {
|
||||
minicore: non_zero, option;
|
||||
use core::num::NonZeroU8;
|
||||
struct Goal(Option<NonZeroU8>);
|
||||
minicore: non_zero, non_null, option;
|
||||
use core::{num::NonZeroU8, ptr::NonNull};
|
||||
struct Goal(Option<NonZeroU8>, Option<NonNull<i32>>);
|
||||
}
|
||||
}
|
||||
|
||||
@ -432,3 +464,41 @@ fn enums_with_discriminants() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn core_mem_discriminant() {
|
||||
size_and_align! {
|
||||
minicore: discriminant;
|
||||
struct S(i32, u64);
|
||||
struct Goal(core::mem::Discriminant<S>);
|
||||
}
|
||||
size_and_align! {
|
||||
minicore: discriminant;
|
||||
#[repr(u32)]
|
||||
enum S {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
}
|
||||
struct Goal(core::mem::Discriminant<S>);
|
||||
}
|
||||
size_and_align! {
|
||||
minicore: discriminant;
|
||||
enum S {
|
||||
A(i32),
|
||||
B(i64),
|
||||
C(u8),
|
||||
}
|
||||
struct Goal(core::mem::Discriminant<S>);
|
||||
}
|
||||
size_and_align! {
|
||||
minicore: discriminant;
|
||||
#[repr(C, u16)]
|
||||
enum S {
|
||||
A(i32),
|
||||
B(i64) = 200,
|
||||
C = 1000,
|
||||
}
|
||||
struct Goal(core::mem::Discriminant<S>);
|
||||
}
|
||||
}
|
||||
|
@ -180,9 +180,16 @@ impl MemoryMap {
|
||||
/// allocator function as `f` and it will return a mapping of old addresses to new addresses.
|
||||
fn transform_addresses(
|
||||
&self,
|
||||
mut f: impl FnMut(&[u8]) -> Result<usize, MirEvalError>,
|
||||
mut f: impl FnMut(&[u8], usize) -> Result<usize, MirEvalError>,
|
||||
) -> Result<HashMap<usize, usize>, MirEvalError> {
|
||||
self.memory.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
|
||||
self.memory
|
||||
.iter()
|
||||
.map(|x| {
|
||||
let addr = *x.0;
|
||||
let align = if addr == 0 { 64 } else { (addr - (addr & (addr - 1))).min(64) };
|
||||
Ok((addr, f(x.1, align)?))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get<'a>(&'a self, addr: usize, size: usize) -> Option<&'a [u8]> {
|
||||
|
@ -23,7 +23,7 @@ use hir_def::{
|
||||
generics::{
|
||||
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
|
||||
},
|
||||
lang_item::{lang_attr, LangItem},
|
||||
lang_item::LangItem,
|
||||
nameres::MacroSubNs,
|
||||
path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
|
||||
resolver::{HasResolver, Resolver, TypeNs},
|
||||
@ -959,10 +959,10 @@ impl<'a> TyLoweringContext<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn lower_where_predicate(
|
||||
&'a self,
|
||||
where_predicate: &'a WherePredicate,
|
||||
&self,
|
||||
where_predicate: &WherePredicate,
|
||||
ignore_bindings: bool,
|
||||
) -> impl Iterator<Item = QuantifiedWhereClause> + 'a {
|
||||
) -> impl Iterator<Item = QuantifiedWhereClause> {
|
||||
match where_predicate {
|
||||
WherePredicate::ForLifetime { target, bound, .. }
|
||||
| WherePredicate::TypeBound { target, bound } => {
|
||||
@ -1012,7 +1012,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
// (So ideally, we'd only ignore `~const Drop` here)
|
||||
// - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until
|
||||
// the builtin impls are supported by Chalk, we ignore them here.
|
||||
if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) {
|
||||
if let Some(lang) = self.db.lang_attr(tr.hir_trait_id().into()) {
|
||||
if matches!(lang, LangItem::Drop | LangItem::Destruct) {
|
||||
return false;
|
||||
}
|
||||
|
@ -534,7 +534,7 @@ impl ReceiverAdjustments {
|
||||
let mut ty = table.resolve_ty_shallow(&ty);
|
||||
let mut adjust = Vec::new();
|
||||
for _ in 0..self.autoderefs {
|
||||
match autoderef::autoderef_step(table, ty.clone()) {
|
||||
match autoderef::autoderef_step(table, ty.clone(), true) {
|
||||
None => {
|
||||
never!("autoderef not possible for {:?}", ty);
|
||||
ty = TyKind::Error.intern(Interner);
|
||||
@ -559,10 +559,10 @@ impl ReceiverAdjustments {
|
||||
adjust.push(a);
|
||||
}
|
||||
if self.unsize_array {
|
||||
ty = 'x: {
|
||||
ty = 'it: {
|
||||
if let TyKind::Ref(m, l, inner) = ty.kind(Interner) {
|
||||
if let TyKind::Array(inner, _) = inner.kind(Interner) {
|
||||
break 'x TyKind::Ref(
|
||||
break 'it TyKind::Ref(
|
||||
m.clone(),
|
||||
l.clone(),
|
||||
TyKind::Slice(inner.clone()).intern(Interner),
|
||||
@ -666,7 +666,7 @@ pub fn is_dyn_method(
|
||||
let self_ty = trait_ref.self_type_parameter(Interner);
|
||||
if let TyKind::Dyn(d) = self_ty.kind(Interner) {
|
||||
let is_my_trait_in_bounds =
|
||||
d.bounds.skip_binders().as_slice(Interner).iter().any(|x| match x.skip_binders() {
|
||||
d.bounds.skip_binders().as_slice(Interner).iter().any(|it| match it.skip_binders() {
|
||||
// rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter
|
||||
// what the generics are, we are sure that the method is come from the vtable.
|
||||
WhereClause::Implemented(tr) => tr.trait_id == trait_ref.trait_id,
|
||||
@ -682,14 +682,14 @@ pub fn is_dyn_method(
|
||||
/// Looks up the impl method that actually runs for the trait method `func`.
|
||||
///
|
||||
/// Returns `func` if it's not a method defined in a trait or the lookup failed.
|
||||
pub fn lookup_impl_method(
|
||||
pub(crate) fn lookup_impl_method_query(
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
func: FunctionId,
|
||||
fn_subst: Substitution,
|
||||
) -> (FunctionId, Substitution) {
|
||||
let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
|
||||
return (func, fn_subst)
|
||||
return (func, fn_subst);
|
||||
};
|
||||
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
|
||||
let fn_params = fn_subst.len(Interner) - trait_params;
|
||||
@ -699,8 +699,8 @@ pub fn lookup_impl_method(
|
||||
};
|
||||
|
||||
let name = &db.function_data(func).name;
|
||||
let Some((impl_fn, impl_subst)) = lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
|
||||
.and_then(|assoc| {
|
||||
let Some((impl_fn, impl_subst)) =
|
||||
lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name).and_then(|assoc| {
|
||||
if let (AssocItemId::FunctionId(id), subst) = assoc {
|
||||
Some((id, subst))
|
||||
} else {
|
||||
@ -731,7 +731,7 @@ fn lookup_impl_assoc_item_for_trait_ref(
|
||||
let impls = db.trait_impls_in_deps(env.krate);
|
||||
let self_impls = match self_ty.kind(Interner) {
|
||||
TyKind::Adt(id, _) => {
|
||||
id.0.module(db.upcast()).containing_block().map(|x| db.trait_impls_in_block(x))
|
||||
id.0.module(db.upcast()).containing_block().map(|it| db.trait_impls_in_block(it))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
@ -895,8 +895,8 @@ pub fn iterate_method_candidates_dyn(
|
||||
// (just as rustc does an autoderef and then autoref again).
|
||||
|
||||
// We have to be careful about the order we're looking at candidates
|
||||
// in here. Consider the case where we're resolving `x.clone()`
|
||||
// where `x: &Vec<_>`. This resolves to the clone method with self
|
||||
// in here. Consider the case where we're resolving `it.clone()`
|
||||
// where `it: &Vec<_>`. This resolves to the clone method with self
|
||||
// type `Vec<_>`, *not* `&_`. I.e. we need to consider methods where
|
||||
// the receiver type exactly matches before cases where we have to
|
||||
// do autoref. But in the autoderef steps, the `&_` self type comes
|
||||
@ -1012,8 +1012,8 @@ fn iterate_method_candidates_by_receiver(
|
||||
let snapshot = table.snapshot();
|
||||
// We're looking for methods with *receiver* type receiver_ty. These could
|
||||
// be found in any of the derefs of receiver_ty, so we have to go through
|
||||
// that.
|
||||
let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone());
|
||||
// that, including raw derefs.
|
||||
let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone(), true);
|
||||
while let Some((self_ty, _)) = autoderef.next() {
|
||||
iterate_inherent_methods(
|
||||
&self_ty,
|
||||
@ -1028,7 +1028,7 @@ fn iterate_method_candidates_by_receiver(
|
||||
|
||||
table.rollback_to(snapshot);
|
||||
|
||||
let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone());
|
||||
let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone(), true);
|
||||
while let Some((self_ty, _)) = autoderef.next() {
|
||||
iterate_trait_method_candidates(
|
||||
&self_ty,
|
||||
@ -1480,8 +1480,8 @@ fn generic_implements_goal(
|
||||
.push(self_ty.value.clone())
|
||||
.fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
|
||||
.build();
|
||||
kinds.extend(trait_ref.substitution.iter(Interner).skip(1).map(|x| {
|
||||
let vk = match x.data(Interner) {
|
||||
kinds.extend(trait_ref.substitution.iter(Interner).skip(1).map(|it| {
|
||||
let vk = match it.data(Interner) {
|
||||
chalk_ir::GenericArgData::Ty(_) => {
|
||||
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
|
||||
}
|
||||
@ -1504,7 +1504,7 @@ fn autoderef_method_receiver(
|
||||
ty: Ty,
|
||||
) -> Vec<(Canonical<Ty>, ReceiverAdjustments)> {
|
||||
let mut deref_chain: Vec<_> = Vec::new();
|
||||
let mut autoderef = autoderef::Autoderef::new(table, ty);
|
||||
let mut autoderef = autoderef::Autoderef::new(table, ty, true);
|
||||
while let Some((ty, derefs)) = autoderef.next() {
|
||||
deref_chain.push((
|
||||
autoderef.table.canonicalize(ty).value,
|
||||
|
@ -3,9 +3,14 @@
|
||||
use std::{fmt::Display, iter};
|
||||
|
||||
use crate::{
|
||||
consteval::usize_const, db::HirDatabase, display::HirDisplay, infer::PointerCast,
|
||||
lang_items::is_box, mapping::ToChalk, CallableDefId, ClosureId, Const, ConstScalar,
|
||||
InferenceResult, Interner, MemoryMap, Substitution, Ty, TyKind,
|
||||
consteval::usize_const,
|
||||
db::HirDatabase,
|
||||
display::HirDisplay,
|
||||
infer::{normalize, PointerCast},
|
||||
lang_items::is_box,
|
||||
mapping::ToChalk,
|
||||
CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap,
|
||||
Substitution, TraitEnvironment, Ty, TyKind,
|
||||
};
|
||||
use base_db::CrateId;
|
||||
use chalk_ir::Mutability;
|
||||
@ -22,7 +27,9 @@ mod pretty;
|
||||
mod monomorphization;
|
||||
|
||||
pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
|
||||
pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError, VTableMap};
|
||||
pub use eval::{
|
||||
interpret_mir, pad16, render_const_using_debug_impl, Evaluator, MirEvalError, VTableMap,
|
||||
};
|
||||
pub use lower::{
|
||||
lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
|
||||
};
|
||||
@ -32,6 +39,7 @@ pub use monomorphization::{
|
||||
};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use stdx::{impl_from, never};
|
||||
use triomphe::Arc;
|
||||
|
||||
use super::consteval::{intern_const_scalar, try_const_usize};
|
||||
|
||||
@ -129,11 +137,19 @@ pub enum ProjectionElem<V, T> {
|
||||
impl<V, T> ProjectionElem<V, T> {
|
||||
pub fn projected_ty(
|
||||
&self,
|
||||
base: Ty,
|
||||
mut base: Ty,
|
||||
db: &dyn HirDatabase,
|
||||
closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty,
|
||||
krate: CrateId,
|
||||
) -> Ty {
|
||||
if matches!(base.data(Interner).kind, TyKind::Alias(_) | TyKind::AssociatedType(..)) {
|
||||
base = normalize(
|
||||
db,
|
||||
// FIXME: we should get this from caller
|
||||
Arc::new(TraitEnvironment::empty(krate)),
|
||||
base,
|
||||
);
|
||||
}
|
||||
match self {
|
||||
ProjectionElem::Deref => match &base.data(Interner).kind {
|
||||
TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
|
||||
@ -321,8 +337,8 @@ impl SwitchTargets {
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct Terminator {
|
||||
span: MirSpan,
|
||||
kind: TerminatorKind,
|
||||
pub span: MirSpan,
|
||||
pub kind: TerminatorKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
|
@ -52,7 +52,7 @@ fn all_mir_bodies(
|
||||
let closures = body.closures.clone();
|
||||
Box::new(
|
||||
iter::once(Ok(body))
|
||||
.chain(closures.into_iter().flat_map(|x| for_closure(db, x))),
|
||||
.chain(closures.into_iter().flat_map(|it| for_closure(db, it))),
|
||||
)
|
||||
}
|
||||
Err(e) => Box::new(iter::once(Err(e))),
|
||||
@ -62,7 +62,7 @@ fn all_mir_bodies(
|
||||
Ok(body) => {
|
||||
let closures = body.closures.clone();
|
||||
Box::new(
|
||||
iter::once(Ok(body)).chain(closures.into_iter().flat_map(|x| for_closure(db, x))),
|
||||
iter::once(Ok(body)).chain(closures.into_iter().flat_map(|it| for_closure(db, it))),
|
||||
)
|
||||
}
|
||||
Err(e) => Box::new(iter::once(Err(e))),
|
||||
@ -171,7 +171,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
|
||||
}
|
||||
TerminatorKind::Call { func, args, .. } => {
|
||||
for_operand(func, terminator.span);
|
||||
args.iter().for_each(|x| for_operand(x, terminator.span));
|
||||
args.iter().for_each(|it| for_operand(it, terminator.span));
|
||||
}
|
||||
TerminatorKind::Assert { cond, .. } => {
|
||||
for_operand(cond, terminator.span);
|
||||
@ -245,7 +245,7 @@ fn ever_initialized_map(
|
||||
body: &MirBody,
|
||||
) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
|
||||
let mut result: ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> =
|
||||
body.basic_blocks.iter().map(|x| (x.0, ArenaMap::default())).collect();
|
||||
body.basic_blocks.iter().map(|it| (it.0, ArenaMap::default())).collect();
|
||||
fn dfs(
|
||||
db: &dyn HirDatabase,
|
||||
body: &MirBody,
|
||||
@ -271,7 +271,10 @@ fn ever_initialized_map(
|
||||
}
|
||||
}
|
||||
let Some(terminator) = &block.terminator else {
|
||||
never!("Terminator should be none only in construction.\nThe body:\n{}", body.pretty_print(db));
|
||||
never!(
|
||||
"Terminator should be none only in construction.\nThe body:\n{}",
|
||||
body.pretty_print(db)
|
||||
);
|
||||
return;
|
||||
};
|
||||
let targets = match &terminator.kind {
|
||||
@ -311,7 +314,7 @@ fn ever_initialized_map(
|
||||
result[body.start_block].insert(l, true);
|
||||
dfs(db, body, body.start_block, l, &mut result);
|
||||
}
|
||||
for l in body.locals.iter().map(|x| x.0) {
|
||||
for l in body.locals.iter().map(|it| it.0) {
|
||||
if !result[body.start_block].contains_idx(l) {
|
||||
result[body.start_block].insert(l, false);
|
||||
dfs(db, body, body.start_block, l, &mut result);
|
||||
@ -325,10 +328,10 @@ fn mutability_of_locals(
|
||||
body: &MirBody,
|
||||
) -> ArenaMap<LocalId, MutabilityReason> {
|
||||
let mut result: ArenaMap<LocalId, MutabilityReason> =
|
||||
body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect();
|
||||
body.locals.iter().map(|it| (it.0, MutabilityReason::Not)).collect();
|
||||
let mut push_mut_span = |local, span| match &mut result[local] {
|
||||
MutabilityReason::Mut { spans } => spans.push(span),
|
||||
x @ MutabilityReason::Not => *x = MutabilityReason::Mut { spans: vec![span] },
|
||||
it @ MutabilityReason::Not => *it = MutabilityReason::Mut { spans: vec![span] },
|
||||
};
|
||||
let ever_init_maps = ever_initialized_map(db, body);
|
||||
for (block_id, mut ever_init_map) in ever_init_maps.into_iter() {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,20 +3,26 @@
|
||||
|
||||
use std::cmp;
|
||||
|
||||
use chalk_ir::TyKind;
|
||||
use hir_def::resolver::HasResolver;
|
||||
use hir_expand::mod_path::ModPath;
|
||||
|
||||
use super::*;
|
||||
|
||||
mod simd;
|
||||
|
||||
macro_rules! from_bytes {
|
||||
($ty:tt, $value:expr) => {
|
||||
($ty::from_le_bytes(match ($value).try_into() {
|
||||
Ok(x) => x,
|
||||
Ok(it) => it,
|
||||
Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! not_supported {
|
||||
($x: expr) => {
|
||||
return Err(MirEvalError::NotSupported(format!($x)))
|
||||
($it: expr) => {
|
||||
return Err(MirEvalError::NotSupported(format!($it)))
|
||||
};
|
||||
}
|
||||
|
||||
@ -26,7 +32,7 @@ impl Evaluator<'_> {
|
||||
def: FunctionId,
|
||||
args: &[IntervalAndTy],
|
||||
generic_args: &Substitution,
|
||||
locals: &Locals<'_>,
|
||||
locals: &Locals,
|
||||
destination: Interval,
|
||||
span: MirSpan,
|
||||
) -> Result<bool> {
|
||||
@ -53,6 +59,28 @@ impl Evaluator<'_> {
|
||||
)?;
|
||||
return Ok(true);
|
||||
}
|
||||
let is_platform_intrinsic = match &function_data.abi {
|
||||
Some(abi) => *abi == Interned::new_str("platform-intrinsic"),
|
||||
None => match def.lookup(self.db.upcast()).container {
|
||||
hir_def::ItemContainerId::ExternBlockId(block) => {
|
||||
let id = block.lookup(self.db.upcast()).id;
|
||||
id.item_tree(self.db.upcast())[id.value].abi.as_deref()
|
||||
== Some("platform-intrinsic")
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
};
|
||||
if is_platform_intrinsic {
|
||||
self.exec_platform_intrinsic(
|
||||
function_data.name.as_text().unwrap_or_default().as_str(),
|
||||
args,
|
||||
generic_args,
|
||||
destination,
|
||||
&locals,
|
||||
span,
|
||||
)?;
|
||||
return Ok(true);
|
||||
}
|
||||
let is_extern_c = match def.lookup(self.db.upcast()).container {
|
||||
hir_def::ItemContainerId::ExternBlockId(block) => {
|
||||
let id = block.lookup(self.db.upcast()).id;
|
||||
@ -74,25 +102,25 @@ impl Evaluator<'_> {
|
||||
let alloc_fn = function_data
|
||||
.attrs
|
||||
.iter()
|
||||
.filter_map(|x| x.path().as_ident())
|
||||
.filter_map(|x| x.as_str())
|
||||
.find(|x| {
|
||||
.filter_map(|it| it.path().as_ident())
|
||||
.filter_map(|it| it.as_str())
|
||||
.find(|it| {
|
||||
[
|
||||
"rustc_allocator",
|
||||
"rustc_deallocator",
|
||||
"rustc_reallocator",
|
||||
"rustc_allocator_zeroed",
|
||||
]
|
||||
.contains(x)
|
||||
.contains(it)
|
||||
});
|
||||
if let Some(alloc_fn) = alloc_fn {
|
||||
self.exec_alloc_fn(alloc_fn, args, destination)?;
|
||||
return Ok(true);
|
||||
}
|
||||
if let Some(x) = self.detect_lang_function(def) {
|
||||
if let Some(it) = self.detect_lang_function(def) {
|
||||
let arg_bytes =
|
||||
args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::<Result<Vec<_>>>()?;
|
||||
let result = self.exec_lang_item(x, generic_args, &arg_bytes, locals, span)?;
|
||||
args.iter().map(|it| Ok(it.get(&self)?.to_owned())).collect::<Result<Vec<_>>>()?;
|
||||
let result = self.exec_lang_item(it, generic_args, &arg_bytes, locals, span)?;
|
||||
destination.write_from_bytes(self, &result)?;
|
||||
return Ok(true);
|
||||
}
|
||||
@ -112,7 +140,7 @@ impl Evaluator<'_> {
|
||||
};
|
||||
let size = from_bytes!(usize, size.get(self)?);
|
||||
let align = from_bytes!(usize, align.get(self)?);
|
||||
let result = self.heap_allocate(size, align);
|
||||
let result = self.heap_allocate(size, align)?;
|
||||
destination.write_from_bytes(self, &result.to_bytes())?;
|
||||
}
|
||||
"rustc_deallocator" => { /* no-op for now */ }
|
||||
@ -120,14 +148,18 @@ impl Evaluator<'_> {
|
||||
let [ptr, old_size, align, new_size] = args else {
|
||||
return Err(MirEvalError::TypeError("rustc_allocator args are not provided"));
|
||||
};
|
||||
let ptr = Address::from_bytes(ptr.get(self)?)?;
|
||||
let old_size = from_bytes!(usize, old_size.get(self)?);
|
||||
let new_size = from_bytes!(usize, new_size.get(self)?);
|
||||
let align = from_bytes!(usize, align.get(self)?);
|
||||
let result = self.heap_allocate(new_size, align);
|
||||
Interval { addr: result, size: old_size }
|
||||
.write_from_interval(self, Interval { addr: ptr, size: old_size })?;
|
||||
destination.write_from_bytes(self, &result.to_bytes())?;
|
||||
if old_size >= new_size {
|
||||
destination.write_from_interval(self, ptr.interval)?;
|
||||
} else {
|
||||
let ptr = Address::from_bytes(ptr.get(self)?)?;
|
||||
let align = from_bytes!(usize, align.get(self)?);
|
||||
let result = self.heap_allocate(new_size, align)?;
|
||||
Interval { addr: result, size: old_size }
|
||||
.write_from_interval(self, Interval { addr: ptr, size: old_size })?;
|
||||
destination.write_from_bytes(self, &result.to_bytes())?;
|
||||
}
|
||||
}
|
||||
_ => not_supported!("unknown alloc function"),
|
||||
}
|
||||
@ -136,7 +168,7 @@ impl Evaluator<'_> {
|
||||
|
||||
fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
|
||||
use LangItem::*;
|
||||
let candidate = lang_attr(self.db.upcast(), def)?;
|
||||
let candidate = self.db.lang_attr(def.into())?;
|
||||
// We want to execute these functions with special logic
|
||||
if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
|
||||
return Some(candidate);
|
||||
@ -146,56 +178,35 @@ impl Evaluator<'_> {
|
||||
|
||||
fn exec_lang_item(
|
||||
&mut self,
|
||||
x: LangItem,
|
||||
it: LangItem,
|
||||
generic_args: &Substitution,
|
||||
args: &[Vec<u8>],
|
||||
locals: &Locals<'_>,
|
||||
locals: &Locals,
|
||||
span: MirSpan,
|
||||
) -> Result<Vec<u8>> {
|
||||
use LangItem::*;
|
||||
let mut args = args.iter();
|
||||
match x {
|
||||
match it {
|
||||
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_string())),
|
||||
PanicFmt => {
|
||||
let message = (|| {
|
||||
let arguments_struct =
|
||||
self.db.lang_item(self.crate_id, LangItem::FormatArguments)?.as_struct()?;
|
||||
let arguments_layout = self
|
||||
.layout_adt(arguments_struct.into(), Substitution::empty(Interner))
|
||||
.ok()?;
|
||||
let arguments_field_pieces =
|
||||
self.db.struct_data(arguments_struct).variant_data.field(&name![pieces])?;
|
||||
let pieces_offset = arguments_layout
|
||||
.fields
|
||||
.offset(u32::from(arguments_field_pieces.into_raw()) as usize)
|
||||
.bytes_usize();
|
||||
let ptr_size = self.ptr_size();
|
||||
let arg = args.next()?;
|
||||
let pieces_array_addr =
|
||||
Address::from_bytes(&arg[pieces_offset..pieces_offset + ptr_size]).ok()?;
|
||||
let pieces_array_len = usize::from_le_bytes(
|
||||
(&arg[pieces_offset + ptr_size..pieces_offset + 2 * ptr_size])
|
||||
.try_into()
|
||||
.ok()?,
|
||||
);
|
||||
let mut message = "".to_string();
|
||||
for i in 0..pieces_array_len {
|
||||
let piece_ptr_addr = pieces_array_addr.offset(2 * i * ptr_size);
|
||||
let piece_addr =
|
||||
Address::from_bytes(self.read_memory(piece_ptr_addr, ptr_size).ok()?)
|
||||
.ok()?;
|
||||
let piece_len = usize::from_le_bytes(
|
||||
self.read_memory(piece_ptr_addr.offset(ptr_size), ptr_size)
|
||||
.ok()?
|
||||
.try_into()
|
||||
.ok()?,
|
||||
);
|
||||
let piece_data = self.read_memory(piece_addr, piece_len).ok()?;
|
||||
message += &std::string::String::from_utf8_lossy(piece_data);
|
||||
}
|
||||
Some(message)
|
||||
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(),
|
||||
)),
|
||||
) 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 size = from_bytes!(usize, message_string[2 * self.ptr_size()..]);
|
||||
Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?).into_owned())
|
||||
})()
|
||||
.unwrap_or_else(|| "<format-args-evaluation-failed>".to_string());
|
||||
.unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}"));
|
||||
Err(MirEvalError::Panic(message))
|
||||
}
|
||||
SliceLen => {
|
||||
@ -207,7 +218,7 @@ impl Evaluator<'_> {
|
||||
}
|
||||
DropInPlace => {
|
||||
let ty =
|
||||
generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)).ok_or(
|
||||
generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)).ok_or(
|
||||
MirEvalError::TypeError(
|
||||
"generic argument of drop_in_place is not provided",
|
||||
),
|
||||
@ -224,7 +235,35 @@ impl Evaluator<'_> {
|
||||
)?;
|
||||
Ok(vec![])
|
||||
}
|
||||
x => not_supported!("Executing lang item {x:?}"),
|
||||
it => not_supported!("Executing lang item {it:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_syscall(
|
||||
&mut self,
|
||||
id: i64,
|
||||
args: &[IntervalAndTy],
|
||||
destination: Interval,
|
||||
_locals: &Locals,
|
||||
_span: MirSpan,
|
||||
) -> Result<()> {
|
||||
match id {
|
||||
318 => {
|
||||
// SYS_getrandom
|
||||
let [buf, len, _flags] = args else {
|
||||
return Err(MirEvalError::TypeError("SYS_getrandom args are not provided"));
|
||||
};
|
||||
let addr = Address::from_bytes(buf.get(self)?)?;
|
||||
let size = from_bytes!(usize, len.get(self)?);
|
||||
for i in 0..size {
|
||||
let rand_byte = self.random_state.rand_u64() as u8;
|
||||
self.write_memory(addr.offset(i), &[rand_byte])?;
|
||||
}
|
||||
destination.write_from_interval(self, len.interval)
|
||||
}
|
||||
_ => {
|
||||
not_supported!("Unknown syscall id {id:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,8 +273,8 @@ impl Evaluator<'_> {
|
||||
args: &[IntervalAndTy],
|
||||
_generic_args: &Substitution,
|
||||
destination: Interval,
|
||||
locals: &Locals<'_>,
|
||||
_span: MirSpan,
|
||||
locals: &Locals,
|
||||
span: MirSpan,
|
||||
) -> Result<()> {
|
||||
match as_str {
|
||||
"memcmp" => {
|
||||
@ -299,7 +338,9 @@ impl Evaluator<'_> {
|
||||
}
|
||||
"pthread_getspecific" => {
|
||||
let Some(arg0) = args.get(0) else {
|
||||
return Err(MirEvalError::TypeError("pthread_getspecific arg0 is not provided"));
|
||||
return Err(MirEvalError::TypeError(
|
||||
"pthread_getspecific arg0 is not provided",
|
||||
));
|
||||
};
|
||||
let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]);
|
||||
let value = self.thread_local_storage.get_key(key)?;
|
||||
@ -308,11 +349,15 @@ impl Evaluator<'_> {
|
||||
}
|
||||
"pthread_setspecific" => {
|
||||
let Some(arg0) = args.get(0) else {
|
||||
return Err(MirEvalError::TypeError("pthread_setspecific arg0 is not provided"));
|
||||
return Err(MirEvalError::TypeError(
|
||||
"pthread_setspecific arg0 is not provided",
|
||||
));
|
||||
};
|
||||
let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]);
|
||||
let Some(arg1) = args.get(1) else {
|
||||
return Err(MirEvalError::TypeError("pthread_setspecific arg1 is not provided"));
|
||||
return Err(MirEvalError::TypeError(
|
||||
"pthread_setspecific arg1 is not provided",
|
||||
));
|
||||
};
|
||||
let value = from_bytes!(u128, pad16(arg1.get(self)?, false));
|
||||
self.thread_local_storage.set_key(key, value)?;
|
||||
@ -326,17 +371,52 @@ impl Evaluator<'_> {
|
||||
destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
|
||||
Ok(())
|
||||
}
|
||||
"syscall" => {
|
||||
let Some((id, rest)) = args.split_first() else {
|
||||
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)
|
||||
}
|
||||
"sched_getaffinity" => {
|
||||
let [_pid, _set_size, set] = args else {
|
||||
return Err(MirEvalError::TypeError("libc::write args are not provided"));
|
||||
};
|
||||
let set = Address::from_bytes(set.get(self)?)?;
|
||||
// Only enable core 0 (we are single threaded anyway), which is bitset 0x0000001
|
||||
self.write_memory(set, &[1])?;
|
||||
// return 0 as success
|
||||
self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
|
||||
Ok(())
|
||||
}
|
||||
_ => not_supported!("unknown external function {as_str}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_platform_intrinsic(
|
||||
&mut self,
|
||||
name: &str,
|
||||
args: &[IntervalAndTy],
|
||||
generic_args: &Substitution,
|
||||
destination: Interval,
|
||||
locals: &Locals,
|
||||
span: MirSpan,
|
||||
) -> Result<()> {
|
||||
if let Some(name) = name.strip_prefix("simd_") {
|
||||
return self.exec_simd_intrinsic(name, args, generic_args, destination, locals, span);
|
||||
}
|
||||
not_supported!("unknown platform intrinsic {name}");
|
||||
}
|
||||
|
||||
fn exec_intrinsic(
|
||||
&mut self,
|
||||
name: &str,
|
||||
args: &[IntervalAndTy],
|
||||
generic_args: &Substitution,
|
||||
destination: Interval,
|
||||
locals: &Locals<'_>,
|
||||
locals: &Locals,
|
||||
span: MirSpan,
|
||||
) -> Result<()> {
|
||||
if let Some(name) = name.strip_prefix("atomic_") {
|
||||
@ -347,7 +427,9 @@ impl Evaluator<'_> {
|
||||
"sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs"
|
||||
| "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => {
|
||||
let [arg] = args else {
|
||||
return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64) -> f64"));
|
||||
return Err(MirEvalError::TypeError(
|
||||
"f64 intrinsic signature doesn't match fn (f64) -> f64",
|
||||
));
|
||||
};
|
||||
let arg = from_bytes!(f64, arg.get(self)?);
|
||||
match name {
|
||||
@ -373,7 +455,9 @@ impl Evaluator<'_> {
|
||||
}
|
||||
"pow" | "minnum" | "maxnum" | "copysign" => {
|
||||
let [arg1, arg2] = args else {
|
||||
return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64, f64) -> f64"));
|
||||
return Err(MirEvalError::TypeError(
|
||||
"f64 intrinsic signature doesn't match fn (f64, f64) -> f64",
|
||||
));
|
||||
};
|
||||
let arg1 = from_bytes!(f64, arg1.get(self)?);
|
||||
let arg2 = from_bytes!(f64, arg2.get(self)?);
|
||||
@ -387,7 +471,9 @@ impl Evaluator<'_> {
|
||||
}
|
||||
"powi" => {
|
||||
let [arg1, arg2] = args else {
|
||||
return Err(MirEvalError::TypeError("powif64 signature doesn't match fn (f64, i32) -> f64"));
|
||||
return Err(MirEvalError::TypeError(
|
||||
"powif64 signature doesn't match fn (f64, i32) -> f64",
|
||||
));
|
||||
};
|
||||
let arg1 = from_bytes!(f64, arg1.get(self)?);
|
||||
let arg2 = from_bytes!(i32, arg2.get(self)?);
|
||||
@ -395,7 +481,9 @@ impl Evaluator<'_> {
|
||||
}
|
||||
"fma" => {
|
||||
let [arg1, arg2, arg3] = args else {
|
||||
return Err(MirEvalError::TypeError("fmaf64 signature doesn't match fn (f64, f64, f64) -> f64"));
|
||||
return Err(MirEvalError::TypeError(
|
||||
"fmaf64 signature doesn't match fn (f64, f64, f64) -> f64",
|
||||
));
|
||||
};
|
||||
let arg1 = from_bytes!(f64, arg1.get(self)?);
|
||||
let arg2 = from_bytes!(f64, arg2.get(self)?);
|
||||
@ -411,7 +499,9 @@ impl Evaluator<'_> {
|
||||
"sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs"
|
||||
| "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => {
|
||||
let [arg] = args else {
|
||||
return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32) -> f32"));
|
||||
return Err(MirEvalError::TypeError(
|
||||
"f32 intrinsic signature doesn't match fn (f32) -> f32",
|
||||
));
|
||||
};
|
||||
let arg = from_bytes!(f32, arg.get(self)?);
|
||||
match name {
|
||||
@ -437,7 +527,9 @@ impl Evaluator<'_> {
|
||||
}
|
||||
"pow" | "minnum" | "maxnum" | "copysign" => {
|
||||
let [arg1, arg2] = args else {
|
||||
return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32, f32) -> f32"));
|
||||
return Err(MirEvalError::TypeError(
|
||||
"f32 intrinsic signature doesn't match fn (f32, f32) -> f32",
|
||||
));
|
||||
};
|
||||
let arg1 = from_bytes!(f32, arg1.get(self)?);
|
||||
let arg2 = from_bytes!(f32, arg2.get(self)?);
|
||||
@ -451,7 +543,9 @@ impl Evaluator<'_> {
|
||||
}
|
||||
"powi" => {
|
||||
let [arg1, arg2] = args else {
|
||||
return Err(MirEvalError::TypeError("powif32 signature doesn't match fn (f32, i32) -> f32"));
|
||||
return Err(MirEvalError::TypeError(
|
||||
"powif32 signature doesn't match fn (f32, i32) -> f32",
|
||||
));
|
||||
};
|
||||
let arg1 = from_bytes!(f32, arg1.get(self)?);
|
||||
let arg2 = from_bytes!(i32, arg2.get(self)?);
|
||||
@ -459,7 +553,9 @@ impl Evaluator<'_> {
|
||||
}
|
||||
"fma" => {
|
||||
let [arg1, arg2, arg3] = args else {
|
||||
return Err(MirEvalError::TypeError("fmaf32 signature doesn't match fn (f32, f32, f32) -> f32"));
|
||||
return Err(MirEvalError::TypeError(
|
||||
"fmaf32 signature doesn't match fn (f32, f32, f32) -> f32",
|
||||
));
|
||||
};
|
||||
let arg1 = from_bytes!(f32, arg1.get(self)?);
|
||||
let arg2 = from_bytes!(f32, arg2.get(self)?);
|
||||
@ -472,21 +568,74 @@ impl Evaluator<'_> {
|
||||
}
|
||||
match name {
|
||||
"size_of" => {
|
||||
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
|
||||
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"));
|
||||
};
|
||||
let size = self.size_of_sized(ty, locals, "size_of arg")?;
|
||||
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(|x| x.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))
|
||||
else {
|
||||
return Err(MirEvalError::TypeError("size_of_val generic arg is not provided"));
|
||||
};
|
||||
let [arg] = args else {
|
||||
return Err(MirEvalError::TypeError("size_of_val args are not provided"));
|
||||
};
|
||||
if let Some((size, _)) = self.size_align_of(ty, locals)? {
|
||||
destination.write_from_bytes(self, &size.to_le_bytes())
|
||||
} else {
|
||||
let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2);
|
||||
let (size, _) = self.size_align_of_unsized(ty, metadata, locals)?;
|
||||
destination.write_from_bytes(self, &size.to_le_bytes())
|
||||
}
|
||||
}
|
||||
"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 [arg] = args else {
|
||||
return Err(MirEvalError::TypeError("min_align_of_val args are not provided"));
|
||||
};
|
||||
if let Some((_, align)) = self.size_align_of(ty, locals)? {
|
||||
destination.write_from_bytes(self, &align.to_le_bytes())
|
||||
} else {
|
||||
let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2);
|
||||
let (_, align) = self.size_align_of_unsized(ty, metadata, locals)?;
|
||||
destination.write_from_bytes(self, &align.to_le_bytes())
|
||||
}
|
||||
}
|
||||
"type_name" => {
|
||||
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"));
|
||||
};
|
||||
let Ok(ty_name) = ty.display_source_code(
|
||||
self.db,
|
||||
locals.body.owner.module(self.db.upcast()),
|
||||
true,
|
||||
) else {
|
||||
not_supported!("fail in generating type_name using source code display");
|
||||
};
|
||||
let len = ty_name.len();
|
||||
let addr = self.heap_allocate(len, 1)?;
|
||||
self.write_memory(addr, ty_name.as_bytes())?;
|
||||
destination.slice(0..self.ptr_size()).write_from_bytes(self, &addr.to_bytes())?;
|
||||
destination
|
||||
.slice(self.ptr_size()..2 * self.ptr_size())
|
||||
.write_from_bytes(self, &len.to_le_bytes())
|
||||
}
|
||||
"needs_drop" => {
|
||||
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
|
||||
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"));
|
||||
};
|
||||
let result = !ty.clone().is_copy(self.db, locals.body.owner);
|
||||
@ -501,13 +650,17 @@ impl Evaluator<'_> {
|
||||
let ans = lhs.get(self)? == rhs.get(self)?;
|
||||
destination.write_from_bytes(self, &[u8::from(ans)])
|
||||
}
|
||||
"saturating_add" => {
|
||||
"saturating_add" | "saturating_sub" => {
|
||||
let [lhs, rhs] = args else {
|
||||
return Err(MirEvalError::TypeError("saturating_add args are not provided"));
|
||||
};
|
||||
let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
|
||||
let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
|
||||
let ans = lhs.saturating_add(rhs);
|
||||
let ans = match name {
|
||||
"saturating_add" => lhs.saturating_add(rhs),
|
||||
"saturating_sub" => lhs.saturating_sub(rhs),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let bits = destination.size * 8;
|
||||
// FIXME: signed
|
||||
let is_signed = false;
|
||||
@ -544,6 +697,26 @@ impl Evaluator<'_> {
|
||||
let ans = lhs.wrapping_mul(rhs);
|
||||
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"wrapping_shl" | "unchecked_shl" => {
|
||||
// FIXME: signed
|
||||
let [lhs, rhs] = args else {
|
||||
return Err(MirEvalError::TypeError("unchecked_shl args are not provided"));
|
||||
};
|
||||
let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
|
||||
let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
|
||||
let ans = lhs.wrapping_shl(rhs as u32);
|
||||
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"wrapping_shr" | "unchecked_shr" => {
|
||||
// FIXME: signed
|
||||
let [lhs, rhs] = args else {
|
||||
return Err(MirEvalError::TypeError("unchecked_shr args are not provided"));
|
||||
};
|
||||
let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
|
||||
let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
|
||||
let ans = lhs.wrapping_shr(rhs as u32);
|
||||
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"unchecked_rem" => {
|
||||
// FIXME: signed
|
||||
let [lhs, rhs] = args else {
|
||||
@ -588,7 +761,7 @@ impl Evaluator<'_> {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let is_overflow = u128overflow
|
||||
|| ans.to_le_bytes()[op_size..].iter().any(|&x| x != 0 && x != 255);
|
||||
|| ans.to_le_bytes()[op_size..].iter().any(|&it| it != 0 && it != 255);
|
||||
let is_overflow = vec![u8::from(is_overflow)];
|
||||
let layout = self.layout(&result_ty)?;
|
||||
let result = self.make_by_layout(
|
||||
@ -603,10 +776,15 @@ impl Evaluator<'_> {
|
||||
}
|
||||
"copy" | "copy_nonoverlapping" => {
|
||||
let [src, dst, offset] = args else {
|
||||
return Err(MirEvalError::TypeError("copy_nonoverlapping args are not provided"));
|
||||
return Err(MirEvalError::TypeError(
|
||||
"copy_nonoverlapping args are not provided",
|
||||
));
|
||||
};
|
||||
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
|
||||
return Err(MirEvalError::TypeError("copy_nonoverlapping 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(
|
||||
"copy_nonoverlapping generic arg is not provided",
|
||||
));
|
||||
};
|
||||
let src = Address::from_bytes(src.get(self)?)?;
|
||||
let dst = Address::from_bytes(dst.get(self)?)?;
|
||||
@ -621,7 +799,8 @@ 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(|x| x.ty(Interner)) else {
|
||||
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 ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false));
|
||||
@ -652,20 +831,106 @@ impl Evaluator<'_> {
|
||||
}
|
||||
"ctpop" => {
|
||||
let [arg] = args else {
|
||||
return Err(MirEvalError::TypeError("likely arg is not provided"));
|
||||
return Err(MirEvalError::TypeError("ctpop arg is not provided"));
|
||||
};
|
||||
let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).count_ones();
|
||||
destination
|
||||
.write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"ctlz" | "ctlz_nonzero" => {
|
||||
let [arg] = args else {
|
||||
return Err(MirEvalError::TypeError("cttz arg is not provided"));
|
||||
};
|
||||
let result =
|
||||
u128::from_le_bytes(pad16(arg.get(self)?, false)).leading_zeros() as usize;
|
||||
let result = result - (128 - arg.interval.size * 8);
|
||||
destination
|
||||
.write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"cttz" | "cttz_nonzero" => {
|
||||
let [arg] = args else {
|
||||
return Err(MirEvalError::TypeError("likely arg is not provided"));
|
||||
return Err(MirEvalError::TypeError("cttz arg is not provided"));
|
||||
};
|
||||
let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).trailing_zeros();
|
||||
destination
|
||||
.write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"rotate_left" => {
|
||||
let [lhs, rhs] = args else {
|
||||
return Err(MirEvalError::TypeError("rotate_left args are not provided"));
|
||||
};
|
||||
let lhs = &lhs.get(self)?[0..destination.size];
|
||||
let rhs = rhs.get(self)?[0] as u32;
|
||||
match destination.size {
|
||||
1 => {
|
||||
let r = from_bytes!(u8, lhs).rotate_left(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
2 => {
|
||||
let r = from_bytes!(u16, lhs).rotate_left(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
4 => {
|
||||
let r = from_bytes!(u32, lhs).rotate_left(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
8 => {
|
||||
let r = from_bytes!(u64, lhs).rotate_left(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
16 => {
|
||||
let r = from_bytes!(u128, lhs).rotate_left(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
s => not_supported!("destination with size {s} for rotate_left"),
|
||||
}
|
||||
}
|
||||
"rotate_right" => {
|
||||
let [lhs, rhs] = args else {
|
||||
return Err(MirEvalError::TypeError("rotate_right args are not provided"));
|
||||
};
|
||||
let lhs = &lhs.get(self)?[0..destination.size];
|
||||
let rhs = rhs.get(self)?[0] as u32;
|
||||
match destination.size {
|
||||
1 => {
|
||||
let r = from_bytes!(u8, lhs).rotate_right(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
2 => {
|
||||
let r = from_bytes!(u16, lhs).rotate_right(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
4 => {
|
||||
let r = from_bytes!(u32, lhs).rotate_right(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
8 => {
|
||||
let r = from_bytes!(u64, lhs).rotate_right(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
16 => {
|
||||
let r = from_bytes!(u128, lhs).rotate_right(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
s => not_supported!("destination with size {s} for rotate_right"),
|
||||
}
|
||||
}
|
||||
"discriminant_value" => {
|
||||
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))
|
||||
else {
|
||||
return Err(MirEvalError::TypeError(
|
||||
"discriminant_value generic arg is not provided",
|
||||
));
|
||||
};
|
||||
let addr = Address::from_bytes(arg.get(self)?)?;
|
||||
let size = self.size_of_sized(ty, locals, "discriminant_value ptr type")?;
|
||||
let interval = Interval { addr, size };
|
||||
let r = self.compute_discriminant(ty.clone(), interval.get(self)?)?;
|
||||
destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"const_eval_select" => {
|
||||
let [tuple, const_fn, _] = args else {
|
||||
return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
|
||||
@ -681,24 +946,119 @@ impl Evaluator<'_> {
|
||||
let addr = tuple.interval.addr.offset(offset);
|
||||
args.push(IntervalAndTy::new(addr, field, self, locals)?);
|
||||
}
|
||||
self.exec_fn_trait(&args, destination, locals, span)
|
||||
if let Some(target) = self.db.lang_item(self.crate_id, LangItem::FnOnce) {
|
||||
if let Some(def) = target
|
||||
.as_trait()
|
||||
.and_then(|it| self.db.trait_data(it).method_by_name(&name![call_once]))
|
||||
{
|
||||
self.exec_fn_trait(
|
||||
def,
|
||||
&args,
|
||||
// FIXME: wrong for manual impls of `FnOnce`
|
||||
Substitution::empty(Interner),
|
||||
locals,
|
||||
destination,
|
||||
None,
|
||||
span,
|
||||
)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
not_supported!("FnOnce was not available for executing const_eval_select");
|
||||
}
|
||||
"read_via_copy" | "volatile_load" => {
|
||||
let [arg] = args else {
|
||||
return Err(MirEvalError::TypeError("read_via_copy args are not provided"));
|
||||
};
|
||||
let addr = Address::from_bytes(arg.interval.get(self)?)?;
|
||||
destination.write_from_interval(self, Interval { addr, size: destination.size })
|
||||
}
|
||||
"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))
|
||||
else {
|
||||
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")?;
|
||||
let size = count * size;
|
||||
self.write_memory_using_ref(dst, size)?.fill(val);
|
||||
Ok(())
|
||||
}
|
||||
_ => not_supported!("unknown intrinsic {name}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn size_align_of_unsized(
|
||||
&mut self,
|
||||
ty: &Ty,
|
||||
metadata: Interval,
|
||||
locals: &Locals,
|
||||
) -> Result<(usize, usize)> {
|
||||
Ok(match ty.kind(Interner) {
|
||||
TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1),
|
||||
TyKind::Slice(inner) => {
|
||||
let len = from_bytes!(usize, metadata.get(self)?);
|
||||
let (size, align) = self.size_align_of_sized(inner, locals, "slice inner type")?;
|
||||
(size * len, align)
|
||||
}
|
||||
TyKind::Dyn(_) => self.size_align_of_sized(
|
||||
self.vtable_map.ty_of_bytes(metadata.get(self)?)?,
|
||||
locals,
|
||||
"dyn concrete type",
|
||||
)?,
|
||||
TyKind::Adt(id, subst) => {
|
||||
let id = id.0;
|
||||
let layout = self.layout_adt(id, subst.clone())?;
|
||||
let id = match id {
|
||||
AdtId::StructId(s) => s,
|
||||
_ => not_supported!("unsized enum or union"),
|
||||
};
|
||||
let field_types = &self.db.field_types(id.into());
|
||||
let last_field_ty =
|
||||
field_types.iter().rev().next().unwrap().1.clone().substitute(Interner, subst);
|
||||
let sized_part_size =
|
||||
layout.fields.offset(field_types.iter().count() - 1).bytes_usize();
|
||||
let sized_part_align = layout.align.abi.bytes() as usize;
|
||||
let (unsized_part_size, unsized_part_align) =
|
||||
self.size_align_of_unsized(&last_field_ty, metadata, locals)?;
|
||||
let align = sized_part_align.max(unsized_part_align) as isize;
|
||||
let size = (sized_part_size + unsized_part_size) as isize;
|
||||
// Must add any necessary padding to `size`
|
||||
// (to make it a multiple of `align`) before returning it.
|
||||
//
|
||||
// Namely, the returned size should be, in C notation:
|
||||
//
|
||||
// `size + ((size & (align-1)) ? align : 0)`
|
||||
//
|
||||
// emulated via the semi-standard fast bit trick:
|
||||
//
|
||||
// `(size + (align-1)) & -align`
|
||||
let size = (size + (align - 1)) & (-align);
|
||||
(size as usize, align as usize)
|
||||
}
|
||||
_ => not_supported!("unsized type other than str, slice, struct and dyn"),
|
||||
})
|
||||
}
|
||||
|
||||
fn exec_atomic_intrinsic(
|
||||
&mut self,
|
||||
name: &str,
|
||||
args: &[IntervalAndTy],
|
||||
generic_args: &Substitution,
|
||||
destination: Interval,
|
||||
locals: &Locals<'_>,
|
||||
locals: &Locals,
|
||||
_span: MirSpan,
|
||||
) -> Result<()> {
|
||||
// We are a single threaded runtime with no UB checking and no optimization, so
|
||||
// we can implement these as normal functions.
|
||||
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
|
||||
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else {
|
||||
return Err(MirEvalError::TypeError("atomic intrinsic generic arg is not provided"));
|
||||
};
|
||||
let Some(arg0) = args.get(0) else {
|
||||
|
177
crates/hir-ty/src/mir/eval/shim/simd.rs
Normal file
177
crates/hir-ty/src/mir/eval/shim/simd.rs
Normal file
@ -0,0 +1,177 @@
|
||||
//! Shim implementation for simd intrinsics
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::TyKind;
|
||||
|
||||
use super::*;
|
||||
|
||||
macro_rules! from_bytes {
|
||||
($ty:tt, $value:expr) => {
|
||||
($ty::from_le_bytes(match ($value).try_into() {
|
||||
Ok(it) => it,
|
||||
Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! not_supported {
|
||||
($it: expr) => {
|
||||
return Err(MirEvalError::NotSupported(format!($it)))
|
||||
};
|
||||
}
|
||||
|
||||
impl Evaluator<'_> {
|
||||
fn detect_simd_ty(&self, ty: &Ty) -> Result<(usize, Ty)> {
|
||||
match ty.kind(Interner) {
|
||||
TyKind::Adt(id, subst) => {
|
||||
let len = match subst.as_slice(Interner).get(1).and_then(|it| it.constant(Interner))
|
||||
{
|
||||
Some(len) => len,
|
||||
_ => {
|
||||
if let AdtId::StructId(id) = id.0 {
|
||||
let struct_data = self.db.struct_data(id);
|
||||
let fields = struct_data.variant_data.fields();
|
||||
let Some((first_field, _)) = fields.iter().next() else {
|
||||
not_supported!("simd type with no field");
|
||||
};
|
||||
let field_ty = self.db.field_types(id.into())[first_field]
|
||||
.clone()
|
||||
.substitute(Interner, subst);
|
||||
return Ok((fields.len(), field_ty));
|
||||
}
|
||||
return Err(MirEvalError::TypeError("simd type with no len param"));
|
||||
}
|
||||
};
|
||||
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 {
|
||||
return Err(MirEvalError::TypeError("simd type with no ty param"));
|
||||
};
|
||||
Ok((len as usize, ty.clone()))
|
||||
}
|
||||
None => Err(MirEvalError::TypeError("simd type with unevaluatable len param")),
|
||||
}
|
||||
}
|
||||
_ => Err(MirEvalError::TypeError("simd type which is not a struct")),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn exec_simd_intrinsic(
|
||||
&mut self,
|
||||
name: &str,
|
||||
args: &[IntervalAndTy],
|
||||
_generic_args: &Substitution,
|
||||
destination: Interval,
|
||||
_locals: &Locals,
|
||||
_span: MirSpan,
|
||||
) -> Result<()> {
|
||||
match name {
|
||||
"and" | "or" | "xor" => {
|
||||
let [left, right] = args else {
|
||||
return Err(MirEvalError::TypeError("simd bit op args are not provided"));
|
||||
};
|
||||
let result = left
|
||||
.get(self)?
|
||||
.iter()
|
||||
.zip(right.get(self)?)
|
||||
.map(|(&it, &y)| match name {
|
||||
"and" => it & y,
|
||||
"or" => it | y,
|
||||
"xor" => it ^ y,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
destination.write_from_bytes(self, &result)
|
||||
}
|
||||
"eq" | "ne" | "lt" | "le" | "gt" | "ge" => {
|
||||
let [left, right] = args else {
|
||||
return Err(MirEvalError::TypeError("simd args are not provided"));
|
||||
};
|
||||
let (len, ty) = self.detect_simd_ty(&left.ty)?;
|
||||
let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
|
||||
let size = left.interval.size / len;
|
||||
let dest_size = destination.size / len;
|
||||
let mut destination_bytes = vec![];
|
||||
let vector = left.get(self)?.chunks(size).zip(right.get(self)?.chunks(size));
|
||||
for (l, r) in vector {
|
||||
let mut result = Ordering::Equal;
|
||||
for (l, r) in l.iter().zip(r).rev() {
|
||||
let it = l.cmp(r);
|
||||
if it != Ordering::Equal {
|
||||
result = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if is_signed {
|
||||
if let Some((&l, &r)) = l.iter().zip(r).rev().next() {
|
||||
if l != r {
|
||||
result = (l as i8).cmp(&(r as i8));
|
||||
}
|
||||
}
|
||||
}
|
||||
let result = match result {
|
||||
Ordering::Less => ["lt", "le", "ne"].contains(&name),
|
||||
Ordering::Equal => ["ge", "le", "eq"].contains(&name),
|
||||
Ordering::Greater => ["ge", "gt", "ne"].contains(&name),
|
||||
};
|
||||
let result = if result { 255 } else { 0 };
|
||||
destination_bytes.extend(std::iter::repeat(result).take(dest_size));
|
||||
}
|
||||
|
||||
destination.write_from_bytes(self, &destination_bytes)
|
||||
}
|
||||
"bitmask" => {
|
||||
let [op] = args else {
|
||||
return Err(MirEvalError::TypeError("simd_bitmask args are not provided"));
|
||||
};
|
||||
let (op_len, _) = self.detect_simd_ty(&op.ty)?;
|
||||
let op_count = op.interval.size / op_len;
|
||||
let mut result: u64 = 0;
|
||||
for (i, val) in op.get(self)?.chunks(op_count).enumerate() {
|
||||
if !val.iter().all(|&it| it == 0) {
|
||||
result |= 1 << i;
|
||||
}
|
||||
}
|
||||
destination.write_from_bytes(self, &result.to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"shuffle" => {
|
||||
let [left, right, index] = args else {
|
||||
return Err(MirEvalError::TypeError("simd_shuffle args are not provided"));
|
||||
};
|
||||
let TyKind::Array(_, index_len) = index.ty.kind(Interner) else {
|
||||
return Err(MirEvalError::TypeError(
|
||||
"simd_shuffle index argument has non-array type",
|
||||
));
|
||||
};
|
||||
let index_len = match try_const_usize(self.db, index_len) {
|
||||
Some(it) => it as usize,
|
||||
None => {
|
||||
return Err(MirEvalError::TypeError(
|
||||
"simd type with unevaluatable len param",
|
||||
))
|
||||
}
|
||||
};
|
||||
let (left_len, _) = self.detect_simd_ty(&left.ty)?;
|
||||
let left_size = left.interval.size / left_len;
|
||||
let vector =
|
||||
left.get(self)?.chunks(left_size).chain(right.get(self)?.chunks(left_size));
|
||||
let mut result = vec![];
|
||||
for index in index.get(self)?.chunks(index.interval.size / index_len) {
|
||||
let index = from_bytes!(u32, index) as usize;
|
||||
let val = match vector.clone().nth(index) {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
return Err(MirEvalError::TypeError(
|
||||
"out of bound access in simd shuffle",
|
||||
))
|
||||
}
|
||||
};
|
||||
result.extend(val);
|
||||
}
|
||||
destination.write_from_bytes(self, &result)
|
||||
}
|
||||
_ => not_supported!("unknown simd intrinsic {name}"),
|
||||
}
|
||||
}
|
||||
}
|
@ -30,7 +30,7 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr
|
||||
db.trait_environment(func_id.into()),
|
||||
)
|
||||
.map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?;
|
||||
let (result, stdout, stderr) = interpret_mir(db, &body, false);
|
||||
let (result, stdout, stderr) = interpret_mir(db, body, false);
|
||||
result?;
|
||||
Ok((stdout, stderr))
|
||||
}
|
||||
@ -613,6 +613,34 @@ fn main() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syscalls() {
|
||||
check_pass(
|
||||
r#"
|
||||
//- minicore: option
|
||||
|
||||
extern "C" {
|
||||
pub unsafe extern "C" fn syscall(num: i64, ...) -> i64;
|
||||
}
|
||||
|
||||
const SYS_getrandom: i64 = 318;
|
||||
|
||||
fn should_not_reach() {
|
||||
_ // FIXME: replace this function with panic when that works
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut x: i32 = 0;
|
||||
let r = syscall(SYS_getrandom, &mut x, 4usize, 0);
|
||||
if r != 4 {
|
||||
should_not_reach();
|
||||
}
|
||||
}
|
||||
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn posix_tls() {
|
||||
check_pass(
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,12 @@
|
||||
//! MIR lowering for places
|
||||
|
||||
use super::*;
|
||||
use hir_def::{lang_item::lang_attr, FunctionId};
|
||||
use hir_def::FunctionId;
|
||||
use hir_expand::name;
|
||||
|
||||
macro_rules! not_supported {
|
||||
($x: expr) => {
|
||||
return Err(MirLowerError::NotSupported(format!($x)))
|
||||
($it: expr) => {
|
||||
return Err(MirLowerError::NotSupported(format!($it)))
|
||||
};
|
||||
}
|
||||
|
||||
@ -18,7 +18,9 @@ impl MirLowerCtx<'_> {
|
||||
) -> Result<Option<(Place, BasicBlockId)>> {
|
||||
let ty = self.expr_ty_without_adjust(expr_id);
|
||||
let place = self.temp(ty, prev_block, expr_id.into())?;
|
||||
let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else {
|
||||
let Some(current) =
|
||||
self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some((place.into(), current)))
|
||||
@ -32,10 +34,12 @@ impl MirLowerCtx<'_> {
|
||||
) -> Result<Option<(Place, BasicBlockId)>> {
|
||||
let ty = adjustments
|
||||
.last()
|
||||
.map(|x| x.target.clone())
|
||||
.map(|it| it.target.clone())
|
||||
.unwrap_or_else(|| self.expr_ty_without_adjust(expr_id));
|
||||
let place = self.temp(ty, prev_block, expr_id.into())?;
|
||||
let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else {
|
||||
let Some(current) =
|
||||
self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some((place.into(), current)))
|
||||
@ -57,16 +61,17 @@ impl MirLowerCtx<'_> {
|
||||
if let Some((last, rest)) = adjustments.split_last() {
|
||||
match last.kind {
|
||||
Adjust::Deref(None) => {
|
||||
let Some(mut x) = self.lower_expr_as_place_with_adjust(
|
||||
let Some(mut it) = self.lower_expr_as_place_with_adjust(
|
||||
current,
|
||||
expr_id,
|
||||
upgrade_rvalue,
|
||||
rest,
|
||||
)? else {
|
||||
)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
x.0 = x.0.project(ProjectionElem::Deref);
|
||||
Ok(Some(x))
|
||||
it.0 = it.0.project(ProjectionElem::Deref);
|
||||
Ok(Some(it))
|
||||
}
|
||||
Adjust::Deref(Some(od)) => {
|
||||
let Some((r, current)) = self.lower_expr_as_place_with_adjust(
|
||||
@ -74,14 +79,15 @@ impl MirLowerCtx<'_> {
|
||||
expr_id,
|
||||
upgrade_rvalue,
|
||||
rest,
|
||||
)? else {
|
||||
)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.lower_overloaded_deref(
|
||||
current,
|
||||
r,
|
||||
rest.last()
|
||||
.map(|x| x.target.clone())
|
||||
.map(|it| it.target.clone())
|
||||
.unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)),
|
||||
last.target.clone(),
|
||||
expr_id.into(),
|
||||
@ -156,7 +162,7 @@ impl MirLowerCtx<'_> {
|
||||
let is_builtin = match self.expr_ty_without_adjust(*expr).kind(Interner) {
|
||||
TyKind::Ref(..) | TyKind::Raw(..) => true,
|
||||
TyKind::Adt(id, _) => {
|
||||
if let Some(lang_item) = lang_attr(self.db.upcast(), id.0) {
|
||||
if let Some(lang_item) = self.db.lang_attr(id.0.into()) {
|
||||
lang_item == LangItem::OwnedBox
|
||||
} else {
|
||||
false
|
||||
@ -165,7 +171,8 @@ impl MirLowerCtx<'_> {
|
||||
_ => false,
|
||||
};
|
||||
if !is_builtin {
|
||||
let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||
let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
return self.lower_overloaded_deref(
|
||||
@ -192,7 +199,8 @@ impl MirLowerCtx<'_> {
|
||||
},
|
||||
);
|
||||
}
|
||||
let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||
let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
r = r.project(ProjectionElem::Deref);
|
||||
@ -217,12 +225,18 @@ impl MirLowerCtx<'_> {
|
||||
)
|
||||
{
|
||||
let Some(index_fn) = self.infer.method_resolution(expr_id) else {
|
||||
return Err(MirLowerError::UnresolvedMethod("[overloaded index]".to_string()));
|
||||
return Err(MirLowerError::UnresolvedMethod(
|
||||
"[overloaded index]".to_string(),
|
||||
));
|
||||
};
|
||||
let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
|
||||
let Some((base_place, current)) =
|
||||
self.lower_expr_as_place(current, *base, true)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some((index_operand, current)) = self.lower_expr_to_some_operand(*index, current)? else {
|
||||
let Some((index_operand, current)) =
|
||||
self.lower_expr_to_some_operand(*index, current)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
return self.lower_overloaded_index(
|
||||
@ -239,8 +253,8 @@ impl MirLowerCtx<'_> {
|
||||
.infer
|
||||
.expr_adjustments
|
||||
.get(base)
|
||||
.and_then(|x| x.split_last())
|
||||
.map(|x| x.1)
|
||||
.and_then(|it| it.split_last())
|
||||
.map(|it| it.1)
|
||||
.unwrap_or(&[]);
|
||||
let Some((mut p_base, current)) =
|
||||
self.lower_expr_as_place_with_adjust(current, *base, true, adjusts)?
|
||||
@ -249,7 +263,8 @@ impl MirLowerCtx<'_> {
|
||||
};
|
||||
let l_index =
|
||||
self.temp(self.expr_ty_after_adjustments(*index), current, expr_id.into())?;
|
||||
let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else {
|
||||
let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
p_base = p_base.project(ProjectionElem::Index(l_index));
|
||||
@ -282,7 +297,15 @@ impl MirLowerCtx<'_> {
|
||||
)
|
||||
.intern(Interner),
|
||||
);
|
||||
let Some(current) = self.lower_call(index_fn_op, Box::new([Operand::Copy(place), index_operand]), result.clone(), current, false, span)? else {
|
||||
let Some(current) = self.lower_call(
|
||||
index_fn_op,
|
||||
Box::new([Operand::Copy(place), index_operand]),
|
||||
result.clone(),
|
||||
current,
|
||||
false,
|
||||
span,
|
||||
)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
result = result.project(ProjectionElem::Deref);
|
||||
@ -329,7 +352,15 @@ impl MirLowerCtx<'_> {
|
||||
.intern(Interner),
|
||||
);
|
||||
let mut result: Place = self.temp(target_ty_ref, current, span)?.into();
|
||||
let Some(current) = self.lower_call(deref_fn_op, Box::new([Operand::Copy(ref_place)]), result.clone(), current, false, span)? else {
|
||||
let Some(current) = self.lower_call(
|
||||
deref_fn_op,
|
||||
Box::new([Operand::Copy(ref_place)]),
|
||||
result.clone(),
|
||||
current,
|
||||
false,
|
||||
span,
|
||||
)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
result = result.project(ProjectionElem::Deref);
|
||||
|
@ -307,6 +307,11 @@ impl MirLowerCtx<'_> {
|
||||
mode,
|
||||
)?,
|
||||
None => {
|
||||
// The path is not a variant, so it is a const
|
||||
if mode != MatchingMode::Check {
|
||||
// A const don't bind anything. Only needs check.
|
||||
return Ok((current, current_else));
|
||||
}
|
||||
let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
|
||||
let resolver = self.owner.resolver(self.db.upcast());
|
||||
let pr = resolver
|
||||
@ -362,8 +367,8 @@ impl MirLowerCtx<'_> {
|
||||
},
|
||||
Pat::Lit(l) => match &self.body.exprs[*l] {
|
||||
Expr::Literal(l) => {
|
||||
let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?;
|
||||
if mode == MatchingMode::Check {
|
||||
let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?;
|
||||
self.pattern_match_const(current_else, current, c, cond_place, pattern)?
|
||||
} else {
|
||||
(current, current_else)
|
||||
|
@ -13,15 +13,14 @@ use chalk_ir::{
|
||||
fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
|
||||
ConstData, DebruijnIndex,
|
||||
};
|
||||
use hir_def::{DefWithBodyId, GeneralConstId};
|
||||
use hir_def::DefWithBodyId;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
consteval::unknown_const,
|
||||
consteval::{intern_const_scalar, unknown_const},
|
||||
db::HirDatabase,
|
||||
from_placeholder_idx,
|
||||
infer::normalize,
|
||||
method_resolution::lookup_impl_const,
|
||||
utils::{generics, Generics},
|
||||
ClosureId, Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind,
|
||||
};
|
||||
@ -29,8 +28,8 @@ use crate::{
|
||||
use super::{MirBody, MirLowerError, Operand, Rvalue, StatementKind, TerminatorKind};
|
||||
|
||||
macro_rules! not_supported {
|
||||
($x: expr) => {
|
||||
return Err(MirLowerError::NotSupported(format!($x)))
|
||||
($it: expr) => {
|
||||
return Err(MirLowerError::NotSupported(format!($it)))
|
||||
};
|
||||
}
|
||||
|
||||
@ -97,16 +96,16 @@ impl FallibleTypeFolder<Interner> for Filler<'_> {
|
||||
idx: chalk_ir::PlaceholderIndex,
|
||||
_outer_binder: DebruijnIndex,
|
||||
) -> std::result::Result<chalk_ir::Const<Interner>, Self::Error> {
|
||||
let x = from_placeholder_idx(self.db, idx);
|
||||
let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else {
|
||||
let it = from_placeholder_idx(self.db, idx);
|
||||
let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else {
|
||||
not_supported!("missing idx in generics");
|
||||
};
|
||||
Ok(self
|
||||
.subst
|
||||
.as_slice(Interner)
|
||||
.get(idx)
|
||||
.and_then(|x| x.constant(Interner))
|
||||
.ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))?
|
||||
.and_then(|it| it.constant(Interner))
|
||||
.ok_or_else(|| MirLowerError::GenericArgNotProvided(it, self.subst.clone()))?
|
||||
.clone())
|
||||
}
|
||||
|
||||
@ -115,16 +114,16 @@ impl FallibleTypeFolder<Interner> for Filler<'_> {
|
||||
idx: chalk_ir::PlaceholderIndex,
|
||||
_outer_binder: DebruijnIndex,
|
||||
) -> std::result::Result<Ty, Self::Error> {
|
||||
let x = from_placeholder_idx(self.db, idx);
|
||||
let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else {
|
||||
let it = from_placeholder_idx(self.db, idx);
|
||||
let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else {
|
||||
not_supported!("missing idx in generics");
|
||||
};
|
||||
Ok(self
|
||||
.subst
|
||||
.as_slice(Interner)
|
||||
.get(idx)
|
||||
.and_then(|x| x.ty(Interner))
|
||||
.ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))?
|
||||
.and_then(|it| it.ty(Interner))
|
||||
.ok_or_else(|| MirLowerError::GenericArgNotProvided(it, self.subst.clone()))?
|
||||
.clone())
|
||||
}
|
||||
|
||||
@ -180,7 +179,7 @@ impl Filler<'_> {
|
||||
MirLowerError::GenericArgNotProvided(
|
||||
self.generics
|
||||
.as_ref()
|
||||
.and_then(|x| x.iter().nth(b.index))
|
||||
.and_then(|it| it.iter().nth(b.index))
|
||||
.unwrap()
|
||||
.0,
|
||||
self.subst.clone(),
|
||||
@ -193,25 +192,12 @@ impl Filler<'_> {
|
||||
| chalk_ir::ConstValue::Placeholder(_) => {}
|
||||
chalk_ir::ConstValue::Concrete(cc) => match &cc.interned {
|
||||
crate::ConstScalar::UnevaluatedConst(const_id, subst) => {
|
||||
let mut const_id = *const_id;
|
||||
let mut subst = subst.clone();
|
||||
self.fill_subst(&mut subst)?;
|
||||
if let GeneralConstId::ConstId(c) = const_id {
|
||||
let (c, s) = lookup_impl_const(
|
||||
self.db,
|
||||
self.db.trait_environment_for_body(self.owner),
|
||||
c,
|
||||
subst,
|
||||
);
|
||||
const_id = GeneralConstId::ConstId(c);
|
||||
subst = s;
|
||||
}
|
||||
let result =
|
||||
self.db.const_eval(const_id.into(), subst).map_err(|e| {
|
||||
let name = const_id.name(self.db.upcast());
|
||||
MirLowerError::ConstEvalError(name, Box::new(e))
|
||||
})?;
|
||||
*c = result;
|
||||
*c = intern_const_scalar(
|
||||
crate::ConstScalar::UnevaluatedConst(*const_id, subst),
|
||||
c.data(Interner).ty.clone(),
|
||||
);
|
||||
}
|
||||
crate::ConstScalar::Bytes(_, _) | crate::ConstScalar::Unknown => (),
|
||||
},
|
||||
|
@ -135,7 +135,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
|
||||
fn for_closure(&mut self, closure: ClosureId) {
|
||||
let body = match self.db.mir_body_for_closure(closure) {
|
||||
Ok(x) => x,
|
||||
Ok(it) => it,
|
||||
Err(e) => {
|
||||
wln!(self, "// error in {closure:?}: {e:?}");
|
||||
return;
|
||||
@ -145,7 +145,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
let indent = mem::take(&mut self.indent);
|
||||
let mut ctx = MirPrettyCtx {
|
||||
body: &body,
|
||||
local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(),
|
||||
local_to_binding: body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(),
|
||||
result,
|
||||
indent,
|
||||
..*self
|
||||
@ -167,7 +167,7 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
}
|
||||
|
||||
fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self {
|
||||
let local_to_binding = body.binding_locals.iter().map(|(x, y)| (*y, x)).collect();
|
||||
let local_to_binding = body.binding_locals.iter().map(|(it, y)| (*y, it)).collect();
|
||||
MirPrettyCtx {
|
||||
body,
|
||||
db,
|
||||
@ -315,17 +315,17 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
ProjectionElem::TupleOrClosureField(x) => {
|
||||
ProjectionElem::TupleOrClosureField(it) => {
|
||||
f(this, local, head);
|
||||
w!(this, ".{}", x);
|
||||
w!(this, ".{}", it);
|
||||
}
|
||||
ProjectionElem::Index(l) => {
|
||||
f(this, local, head);
|
||||
w!(this, "[{}]", this.local_name(*l).display(this.db));
|
||||
}
|
||||
x => {
|
||||
it => {
|
||||
f(this, local, head);
|
||||
w!(this, ".{:?}", x);
|
||||
w!(this, ".{:?}", it);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -356,14 +356,14 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
}
|
||||
self.place(p);
|
||||
}
|
||||
Rvalue::Aggregate(AggregateKind::Tuple(_), x) => {
|
||||
Rvalue::Aggregate(AggregateKind::Tuple(_), it) => {
|
||||
w!(self, "(");
|
||||
self.operand_list(x);
|
||||
self.operand_list(it);
|
||||
w!(self, ")");
|
||||
}
|
||||
Rvalue::Aggregate(AggregateKind::Array(_), x) => {
|
||||
Rvalue::Aggregate(AggregateKind::Array(_), it) => {
|
||||
w!(self, "[");
|
||||
self.operand_list(x);
|
||||
self.operand_list(it);
|
||||
w!(self, "]");
|
||||
}
|
||||
Rvalue::Repeat(op, len) => {
|
||||
@ -371,19 +371,19 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
self.operand(op);
|
||||
w!(self, "; {}]", len.display(self.db));
|
||||
}
|
||||
Rvalue::Aggregate(AggregateKind::Adt(_, _), x) => {
|
||||
Rvalue::Aggregate(AggregateKind::Adt(_, _), it) => {
|
||||
w!(self, "Adt(");
|
||||
self.operand_list(x);
|
||||
self.operand_list(it);
|
||||
w!(self, ")");
|
||||
}
|
||||
Rvalue::Aggregate(AggregateKind::Closure(_), x) => {
|
||||
Rvalue::Aggregate(AggregateKind::Closure(_), it) => {
|
||||
w!(self, "Closure(");
|
||||
self.operand_list(x);
|
||||
self.operand_list(it);
|
||||
w!(self, ")");
|
||||
}
|
||||
Rvalue::Aggregate(AggregateKind::Union(_, _), x) => {
|
||||
Rvalue::Aggregate(AggregateKind::Union(_, _), it) => {
|
||||
w!(self, "Union(");
|
||||
self.operand_list(x);
|
||||
self.operand_list(it);
|
||||
w!(self, ")");
|
||||
}
|
||||
Rvalue::Len(p) => {
|
||||
@ -428,8 +428,8 @@ impl<'a> MirPrettyCtx<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn operand_list(&mut self, x: &[Operand]) {
|
||||
let mut it = x.iter();
|
||||
fn operand_list(&mut self, it: &[Operand]) {
|
||||
let mut it = it.iter();
|
||||
if let Some(first) = it.next() {
|
||||
self.operand(first);
|
||||
for op in it {
|
||||
|
@ -30,7 +30,7 @@ use syntax::{
|
||||
ast::{self, AstNode, HasName},
|
||||
SyntaxNode,
|
||||
};
|
||||
use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
|
||||
use tracing_subscriber::{layer::SubscriberExt, Registry};
|
||||
use tracing_tree::HierarchicalLayer;
|
||||
use triomphe::Arc;
|
||||
|
||||
@ -52,7 +52,8 @@ fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let filter = EnvFilter::from_env("CHALK_DEBUG");
|
||||
let filter: tracing_subscriber::filter::Targets =
|
||||
env::var("CHALK_DEBUG").ok().and_then(|it| it.parse().ok()).unwrap_or_default();
|
||||
let layer = HierarchicalLayer::default()
|
||||
.with_indent_lines(true)
|
||||
.with_ansi(false)
|
||||
@ -205,7 +206,9 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
||||
let Some(node) = (match expr_or_pat {
|
||||
hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
|
||||
hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
|
||||
}) else { continue; };
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
let range = node.as_ref().original_file_range(&db);
|
||||
let actual = format!(
|
||||
"expected {}, got {}",
|
||||
|
@ -202,11 +202,11 @@ fn expr_macro_def_expanded_in_various_places() {
|
||||
100..119 'for _ ...!() {}': IntoIterator::IntoIter<isize>
|
||||
100..119 'for _ ...!() {}': &mut IntoIterator::IntoIter<isize>
|
||||
100..119 'for _ ...!() {}': fn next<IntoIterator::IntoIter<isize>>(&mut IntoIterator::IntoIter<isize>) -> Option<<IntoIterator::IntoIter<isize> as Iterator>::Item>
|
||||
100..119 'for _ ...!() {}': Option<Iterator::Item<IntoIterator::IntoIter<isize>>>
|
||||
100..119 'for _ ...!() {}': Option<IntoIterator::Item<isize>>
|
||||
100..119 'for _ ...!() {}': ()
|
||||
100..119 'for _ ...!() {}': ()
|
||||
100..119 'for _ ...!() {}': ()
|
||||
104..105 '_': Iterator::Item<IntoIterator::IntoIter<isize>>
|
||||
104..105 '_': IntoIterator::Item<isize>
|
||||
117..119 '{}': ()
|
||||
124..134 '|| spam!()': impl Fn() -> isize
|
||||
140..156 'while ...!() {}': ()
|
||||
@ -293,11 +293,11 @@ fn expr_macro_rules_expanded_in_various_places() {
|
||||
114..133 'for _ ...!() {}': IntoIterator::IntoIter<isize>
|
||||
114..133 'for _ ...!() {}': &mut IntoIterator::IntoIter<isize>
|
||||
114..133 'for _ ...!() {}': fn next<IntoIterator::IntoIter<isize>>(&mut IntoIterator::IntoIter<isize>) -> Option<<IntoIterator::IntoIter<isize> as Iterator>::Item>
|
||||
114..133 'for _ ...!() {}': Option<Iterator::Item<IntoIterator::IntoIter<isize>>>
|
||||
114..133 'for _ ...!() {}': Option<IntoIterator::Item<isize>>
|
||||
114..133 'for _ ...!() {}': ()
|
||||
114..133 'for _ ...!() {}': ()
|
||||
114..133 'for _ ...!() {}': ()
|
||||
118..119 '_': Iterator::Item<IntoIterator::IntoIter<isize>>
|
||||
118..119 '_': IntoIterator::Item<isize>
|
||||
131..133 '{}': ()
|
||||
138..148 '|| spam!()': impl Fn() -> isize
|
||||
154..170 'while ...!() {}': ()
|
||||
|
@ -1215,6 +1215,52 @@ fn main() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inherent_method_deref_raw() {
|
||||
check_types(
|
||||
r#"
|
||||
struct Val;
|
||||
|
||||
impl Val {
|
||||
pub fn method(self: *const Val) -> u32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let foo: *const Val;
|
||||
foo.method();
|
||||
// ^^^^^^^^^^^^ u32
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trait_method_deref_raw() {
|
||||
check_types(
|
||||
r#"
|
||||
trait Trait {
|
||||
fn method(self: *const Self) -> u32;
|
||||
}
|
||||
|
||||
struct Val;
|
||||
|
||||
impl Trait for Val {
|
||||
fn method(self: *const Self) -> u32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let foo: *const Val;
|
||||
foo.method();
|
||||
// ^^^^^^^^^^^^ u32
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_on_dyn_impl() {
|
||||
check_types(
|
||||
|
@ -1240,11 +1240,11 @@ fn test() {
|
||||
16..66 'for _ ... }': IntoIterator::IntoIter<()>
|
||||
16..66 'for _ ... }': &mut IntoIterator::IntoIter<()>
|
||||
16..66 'for _ ... }': fn next<IntoIterator::IntoIter<()>>(&mut IntoIterator::IntoIter<()>) -> Option<<IntoIterator::IntoIter<()> as Iterator>::Item>
|
||||
16..66 'for _ ... }': Option<Iterator::Item<IntoIterator::IntoIter<()>>>
|
||||
16..66 'for _ ... }': Option<IntoIterator::Item<()>>
|
||||
16..66 'for _ ... }': ()
|
||||
16..66 'for _ ... }': ()
|
||||
16..66 'for _ ... }': ()
|
||||
20..21 '_': Iterator::Item<IntoIterator::IntoIter<()>>
|
||||
20..21 '_': IntoIterator::Item<()>
|
||||
25..39 '{ let x = 0; }': ()
|
||||
31..32 'x': i32
|
||||
35..36 '0': i32
|
||||
|
@ -4148,6 +4148,30 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gats_in_bounds_for_assoc() {
|
||||
check_types(
|
||||
r#"
|
||||
trait Trait {
|
||||
type Assoc: Another<Gat<i32> = usize>;
|
||||
type Assoc2<T>: Another<Gat<T> = T>;
|
||||
}
|
||||
trait Another {
|
||||
type Gat<T>;
|
||||
fn foo(&self) -> Self::Gat<i32>;
|
||||
fn bar<T>(&self) -> Self::Gat<T>;
|
||||
}
|
||||
|
||||
fn test<T: Trait>(a: T::Assoc, b: T::Assoc2<isize>) {
|
||||
let v = a.foo();
|
||||
//^ usize
|
||||
let v = b.bar::<isize>();
|
||||
//^ isize
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bin_op_with_scalar_fallback() {
|
||||
// Extra impls are significant so that chalk doesn't give us definite guidances.
|
||||
|
@ -170,7 +170,7 @@ fn solve(
|
||||
|
||||
struct LoggingRustIrDatabaseLoggingOnDrop<'a>(LoggingRustIrDatabase<Interner, ChalkContext<'a>>);
|
||||
|
||||
impl<'a> Drop for LoggingRustIrDatabaseLoggingOnDrop<'a> {
|
||||
impl Drop for LoggingRustIrDatabaseLoggingOnDrop<'_> {
|
||||
fn drop(&mut self) {
|
||||
eprintln!("chalk program:\n{}", self.0);
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ struct SuperTraits<'a> {
|
||||
seen: FxHashSet<ChalkTraitId>,
|
||||
}
|
||||
|
||||
impl<'a> SuperTraits<'a> {
|
||||
impl SuperTraits<'_> {
|
||||
fn elaborate(&mut self, trait_ref: &TraitRef) {
|
||||
direct_super_trait_refs(self.db, trait_ref, |trait_ref| {
|
||||
if !self.seen.contains(&trait_ref.trait_id) {
|
||||
@ -99,7 +99,7 @@ impl<'a> SuperTraits<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for SuperTraits<'a> {
|
||||
impl Iterator for SuperTraits<'_> {
|
||||
type Item = TraitRef;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
@ -18,7 +18,7 @@ arrayvec = "0.7.2"
|
||||
itertools = "0.10.5"
|
||||
smallvec.workspace = true
|
||||
triomphe.workspace = true
|
||||
once_cell = "1.17.0"
|
||||
once_cell = "1.17.1"
|
||||
|
||||
# local deps
|
||||
base-db.workspace = true
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user