Merge remote-tracking branch 'upstream/master' into sync-from-rust

This commit is contained in:
Laurențiu Nicola 2023-07-17 16:09:39 +03:00
commit 71499fcd22
310 changed files with 13697 additions and 9108 deletions

View File

@ -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

View File

@ -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))

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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" }

View File

@ -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

View File

@ -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;

View File

@ -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 },

View File

@ -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

View File

@ -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,
})

View File

@ -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

View File

@ -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 }

View File

@ -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))

View File

@ -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,
}

View File

@ -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() {

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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(),
));

View File

@ -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(),

View File

@ -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;

View File

@ -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();

View File

@ -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") };

View File

@ -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)
})
});

View File

@ -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,
}

View File

@ -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),
}
}

View File

@ -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#""#]],
);
}
}

View File

@ -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();
}
}

View File

@ -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>;

View File

@ -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 {

View File

@ -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 {

View File

@ -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 }
}

View File

@ -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)))

View File

@ -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 },

View File

@ -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), ]);
}
"##]],
);

View File

@ -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,#4294967295 &#4294967295[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(&#4294967295(::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(&#4294967295[#4294967295""#4294967295,#4294967295 ]#4294967295,#4294967295 &#4294967295[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(&#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(&#4294967295(#4294967295foo#13)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(&#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(

View File

@ -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(&macro_);
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 &macro_ {
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 &macro_ {
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)
}

View File

@ -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>> {

View File

@ -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,

View File

@ -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 {

View File

@ -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 &current_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);
}
}
}

View File

@ -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() };

View File

@ -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();

View File

@ -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) => {

View File

@ -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),
}
}
}

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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 &param {
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,

View File

@ -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)?;

View File

@ -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(&macro_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, &macro_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(())
}
}

View File

@ -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(&macro_call.value));
let expand_to = ExpandTo::from_call_site(&macro_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(&macro_call.value);
let expand_to = ExpandTo::from_call_site(&macro_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 })
}

View File

@ -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 ;}
"#]],
)
}

View File

@ -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(),

View File

@ -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(&macro_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(&macro_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>,

View File

@ -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)
}

View File

@ -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,

View File

@ -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"

View File

@ -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) {

View File

@ -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,

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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(

View File

@ -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,
);
}

View File

@ -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;

View File

@ -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,
},

View File

@ -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 {

View File

@ -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 =

View File

@ -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

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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| {

View File

@ -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()
}

View File

@ -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,
{

View File

@ -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,
}
}

View File

@ -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);
}
}
}

View File

@ -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>);
}
}

View File

@ -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]> {

View File

@ -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;
}

View File

@ -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,

View File

@ -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)]

View File

@ -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

View File

@ -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 {

View 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}"),
}
}
}

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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 => (),
},

View File

@ -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 {

View File

@ -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 {}",

View File

@ -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 ...!() {}': ()

View File

@ -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(

View File

@ -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

View File

@ -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.

View File

@ -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);
}

View File

@ -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> {

View File

@ -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