summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/hir-def
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/hir-def')
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/Cargo.toml3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr.rs241
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs)170
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs40
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs400
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs1072
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs138
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs68
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs246
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/adt.rs)73
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs60
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/keys.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expander.rs211
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/find_path.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/generics.rs48
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/expr.rs)132
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs)131
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/import_map.rs98
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs91
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs293
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/layout.rs97
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs407
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lower.rs45
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs360
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs149
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs131
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs311
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs)85
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres.rs286
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs661
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs141
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs256
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path.rs91
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/pretty.rs80
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs152
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/src.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/test_db.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/visibility.rs3
57 files changed, 4853 insertions, 2214 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
index 31d4018d2..83c705164 100644
--- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
[dependencies]
anymap = "1.0.0-beta.2"
arrayvec = "0.7.2"
-bitflags = "1.3.2"
+bitflags = "2.1.0"
cov-mark = "2.0.0-pre.1"
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
dashmap = { version = "=5.4.0", features = ["raw-api"] }
@@ -29,6 +29,7 @@ once_cell = "1.17.0"
rustc-hash = "1.1.0"
smallvec.workspace = true
tracing = "0.1.35"
+triomphe.workspace = true
rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
index 200072c17..bab3bbc23 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
@@ -1,6 +1,11 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
-use std::{hash::Hash, ops, sync::Arc};
+pub mod builtin;
+
+#[cfg(test)]
+mod tests;
+
+use std::{hash::Hash, ops};
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
@@ -16,14 +21,16 @@ use syntax::{
ast::{self, HasAttrs, IsString},
AstPtr, AstToken, SmolStr, TextRange, TextSize,
};
+use triomphe::Arc;
use crate::{
db::DefDatabase,
item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeNode},
+ lang_item::LangItem,
nameres::{ModuleOrigin, ModuleSource},
src::{HasChildSource, HasSource},
- AdtId, AttrDefId, EnumId, GenericParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroId,
- VariantId,
+ AdtId, AssocItemLoc, AttrDefId, EnumId, GenericParamId, ItemLoc, LocalEnumVariantId,
+ LocalFieldId, Lookup, MacroId, VariantId,
};
/// Holds documentation
@@ -88,6 +95,7 @@ impl Attrs {
db: &dyn DefDatabase,
e: EnumId,
) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> {
+ let _p = profile::span("variants_attrs_query");
// FIXME: There should be some proper form of mapping between item tree enum variant ids and hir enum variant ids
let mut res = ArenaMap::default();
@@ -114,6 +122,7 @@ impl Attrs {
db: &dyn DefDatabase,
v: VariantId,
) -> Arc<ArenaMap<LocalFieldId, Attrs>> {
+ let _p = profile::span("fields_attrs_query");
// FIXME: There should be some proper form of mapping between item tree field ids and hir field ids
let mut res = ArenaMap::default();
@@ -175,13 +184,13 @@ impl Attrs {
Arc::new(res)
}
+}
+impl Attrs {
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
AttrQuery { attrs: self, key }
}
-}
-impl Attrs {
pub fn cfg(&self) -> Option<CfgExpr> {
let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse);
let first = cfgs.next()?;
@@ -193,6 +202,7 @@ impl Attrs {
None => Some(first),
}
}
+
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
match self.cfg() {
None => true,
@@ -204,6 +214,10 @@ impl Attrs {
self.by_key("lang").string_value()
}
+ pub fn lang_item(&self) -> Option<LangItem> {
+ self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
+ }
+
pub fn docs(&self) -> Option<Documentation> {
let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value());
let indent = doc_indent(self);
@@ -238,6 +252,14 @@ impl Attrs {
})
}
+ pub fn doc_exprs(&self) -> impl Iterator<Item = DocExpr> + '_ {
+ self.by_key("doc").tt_values().map(DocExpr::parse)
+ }
+
+ pub fn doc_aliases(&self) -> impl Iterator<Item = SmolStr> + '_ {
+ self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
+ }
+
pub fn is_proc_macro(&self) -> bool {
self.by_key("proc_macro").exists()
}
@@ -249,10 +271,120 @@ impl Attrs {
pub fn is_proc_macro_derive(&self) -> bool {
self.by_key("proc_macro_derive").exists()
}
+
+ pub fn is_unstable(&self) -> bool {
+ self.by_key("unstable").exists()
+ }
+}
+
+use std::slice::Iter as SliceIter;
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
+pub enum DocAtom {
+ /// eg. `#[doc(hidden)]`
+ Flag(SmolStr),
+ /// eg. `#[doc(alias = "x")]`
+ ///
+ /// 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")]`.
+ KeyValue { key: SmolStr, value: SmolStr },
+}
+
+// Adapted from `CfgExpr` parsing code
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+// #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))]
+pub enum DocExpr {
+ Invalid,
+ /// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
+ Atom(DocAtom),
+ /// eg. `#[doc(alias("x", "y"))]`
+ Alias(Vec<SmolStr>),
+}
+
+impl From<DocAtom> for DocExpr {
+ fn from(atom: DocAtom) -> Self {
+ DocExpr::Atom(atom)
+ }
+}
+
+impl DocExpr {
+ fn parse<S>(tt: &tt::Subtree<S>) -> DocExpr {
+ next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid)
+ }
+
+ pub fn aliases(&self) -> &[SmolStr] {
+ match self {
+ DocExpr::Atom(DocAtom::KeyValue { key, value }) if key == "alias" => {
+ std::slice::from_ref(value)
+ }
+ DocExpr::Alias(aliases) => aliases,
+ _ => &[],
+ }
+ }
+}
+
+fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr> {
+ let name = match it.next() {
+ None => return None,
+ Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(),
+ Some(_) => return Some(DocExpr::Invalid),
+ };
+
+ // Peek
+ let ret = match it.as_slice().first() {
+ Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
+ match it.as_slice().get(1) {
+ Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
+ it.next();
+ it.next();
+ // FIXME: escape? raw string?
+ let value =
+ SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"'));
+ DocAtom::KeyValue { key: name, value }.into()
+ }
+ _ => return Some(DocExpr::Invalid),
+ }
+ }
+ Some(tt::TokenTree::Subtree(subtree)) => {
+ it.next();
+ let subs = parse_comma_sep(subtree);
+ match name.as_str() {
+ "alias" => DocExpr::Alias(subs),
+ _ => DocExpr::Invalid,
+ }
+ }
+ _ => DocAtom::Flag(name).into(),
+ };
+
+ // Eat comma separator
+ if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = it.as_slice().first() {
+ if punct.char == ',' {
+ it.next();
+ }
+ }
+ Some(ret)
+}
+
+fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<SmolStr> {
+ subtree
+ .token_trees
+ .iter()
+ .filter_map(|tt| match tt {
+ tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
+ // FIXME: escape? raw string?
+ Some(SmolStr::new(lit.text.trim_start_matches('"').trim_end_matches('"')))
+ }
+ _ => None,
+ })
+ .collect()
}
impl AttrsWithOwner {
- pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Self {
+ pub(crate) fn attrs_with_owner(db: &dyn DefDatabase, owner: AttrDefId) -> Self {
+ Self { attrs: db.attrs(owner), owner }
+ }
+
+ pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
+ let _p = profile::span("attrs_query");
// FIXME: this should use `Trace` to avoid duplication in `source_map` below
let raw_attrs = match def {
AttrDefId::ModuleId(module) => {
@@ -286,31 +418,29 @@ impl AttrsWithOwner {
}
}
AttrDefId::FieldId(it) => {
- return Self { attrs: db.fields_attrs(it.parent)[it.local_id].clone(), owner: def };
+ return db.fields_attrs(it.parent)[it.local_id].clone();
}
AttrDefId::EnumVariantId(it) => {
- return Self {
- attrs: db.variants_attrs(it.parent)[it.local_id].clone(),
- owner: def,
- };
+ return db.variants_attrs(it.parent)[it.local_id].clone();
}
+ // FIXME: DRY this up
AttrDefId::AdtId(it) => match it {
- AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AdtId::StructId(it) => attrs_from_item_tree_loc(db, it),
+ AdtId::EnumId(it) => attrs_from_item_tree_loc(db, it),
+ AdtId::UnionId(it) => attrs_from_item_tree_loc(db, it),
},
- AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::TraitAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AttrDefId::TraitId(it) => attrs_from_item_tree_loc(db, it),
+ AttrDefId::TraitAliasId(it) => attrs_from_item_tree_loc(db, it),
AttrDefId::MacroId(it) => match it {
- MacroId::Macro2Id(it) => attrs_from_item_tree(it.lookup(db).id, db),
- MacroId::MacroRulesId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- MacroId::ProcMacroId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ MacroId::Macro2Id(it) => attrs_from_item_tree(db, it.lookup(db).id),
+ MacroId::MacroRulesId(it) => attrs_from_item_tree(db, it.lookup(db).id),
+ MacroId::ProcMacroId(it) => attrs_from_item_tree(db, it.lookup(db).id),
},
- AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AttrDefId::ImplId(it) => attrs_from_item_tree_loc(db, it),
+ AttrDefId::ConstId(it) => attrs_from_item_tree_assoc(db, it),
+ AttrDefId::StaticId(it) => attrs_from_item_tree_assoc(db, it),
+ AttrDefId::FunctionId(it) => attrs_from_item_tree_assoc(db, it),
+ AttrDefId::TypeAliasId(it) => attrs_from_item_tree_assoc(db, it),
AttrDefId::GenericParamId(it) => match it {
GenericParamId::ConstParamId(it) => {
let src = it.parent().child_source(db);
@@ -331,11 +461,11 @@ impl AttrsWithOwner {
RawAttrs::from_attrs_owner(db.upcast(), src.with_value(&src.value[it.local_id]))
}
},
- AttrDefId::ExternBlockId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
};
let attrs = raw_attrs.filter(db.upcast(), def.krate(db));
- Self { attrs: Attrs(attrs), owner: def }
+ Attrs(attrs)
}
pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap {
@@ -371,7 +501,7 @@ impl AttrsWithOwner {
AttrDefId::FieldId(id) => {
let map = db.fields_attrs_source_map(id.parent);
let file_id = id.parent.file_id(db);
- let root = db.parse_or_expand(file_id).unwrap();
+ let root = db.parse_or_expand(file_id);
let owner = match &map[id.local_id] {
Either::Left(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
Either::Right(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
@@ -379,28 +509,28 @@ impl AttrsWithOwner {
InFile::new(file_id, owner)
}
AttrDefId::AdtId(adt) => match adt {
- AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AdtId::StructId(id) => any_has_attrs(db, id),
+ AdtId::UnionId(id) => any_has_attrs(db, id),
+ AdtId::EnumId(id) => any_has_attrs(db, id),
},
- AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::FunctionId(id) => any_has_attrs(db, id),
AttrDefId::EnumVariantId(id) => {
let map = db.variants_attrs_source_map(id.parent);
let file_id = id.parent.lookup(db).id.file_id();
- let root = db.parse_or_expand(file_id).unwrap();
+ let root = db.parse_or_expand(file_id);
InFile::new(file_id, ast::AnyHasAttrs::new(map[id.local_id].to_node(&root)))
}
- AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::TraitAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::StaticId(id) => any_has_attrs(db, id),
+ AttrDefId::ConstId(id) => any_has_attrs(db, id),
+ AttrDefId::TraitId(id) => any_has_attrs(db, id),
+ AttrDefId::TraitAliasId(id) => any_has_attrs(db, id),
+ AttrDefId::TypeAliasId(id) => any_has_attrs(db, id),
AttrDefId::MacroId(id) => match id {
- MacroId::Macro2Id(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- MacroId::MacroRulesId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- MacroId::ProcMacroId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ MacroId::Macro2Id(id) => any_has_attrs(db, id),
+ MacroId::MacroRulesId(id) => any_has_attrs(db, id),
+ MacroId::ProcMacroId(id) => any_has_attrs(db, id),
},
- AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::ImplId(id) => any_has_attrs(db, id),
AttrDefId::GenericParamId(id) => match id {
GenericParamId::ConstParamId(id) => id
.parent()
@@ -415,7 +545,7 @@ impl AttrsWithOwner {
.child_source(db)
.map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())),
},
- AttrDefId::ExternBlockId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::ExternBlockId(id) => any_has_attrs(db, id),
};
AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs))
@@ -635,19 +765,42 @@ impl<'attr> AttrQuery<'attr> {
.nth(2);
match name {
- Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ref text, ..}))) => Some(text),
+ Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ ref text, ..}))) => Some(text),
_ => None
}
})
}
}
-fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs {
+fn any_has_attrs(
+ db: &dyn DefDatabase,
+ id: impl Lookup<Data = impl HasSource<Value = impl ast::HasAttrs>>,
+) -> InFile<ast::AnyHasAttrs> {
+ id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
+}
+
+fn attrs_from_item_tree<N: ItemTreeNode>(db: &dyn DefDatabase, id: ItemTreeId<N>) -> RawAttrs {
let tree = id.item_tree(db);
let mod_item = N::id_to_mod_item(id.value);
tree.raw_attrs(mod_item.into()).clone()
}
+fn attrs_from_item_tree_loc<N: ItemTreeNode>(
+ db: &dyn DefDatabase,
+ lookup: impl Lookup<Data = ItemLoc<N>>,
+) -> RawAttrs {
+ let id = lookup.lookup(db).id;
+ attrs_from_item_tree(db, id)
+}
+
+fn attrs_from_item_tree_assoc<N: ItemTreeNode>(
+ db: &dyn DefDatabase,
+ lookup: impl Lookup<Data = AssocItemLoc<N>>,
+) -> RawAttrs {
+ let id = lookup.lookup(db).id;
+ attrs_from_item_tree(db, id)
+}
+
pub(crate) fn variants_attrs_source_map(
db: &dyn DefDatabase,
def: EnumId,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
index f7c1e683d..cead64a33 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
@@ -2,7 +2,7 @@
//!
//! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`.
//!
-//! It was last synchronized with upstream commit c1a2db3372a4d6896744919284f3287650a38ab7.
+//! It was last synchronized with upstream commit e29821ff85a2a3000d226f99f62f89464028d5d6.
//!
//! The macros were adjusted to only expand to the attribute name, since that is all we need to do
//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
@@ -108,7 +108,7 @@ macro_rules! experimental {
};
}
-/// "Inert" built-in attributes that have a special meaning to rustc or rustdoc.
+/// Attributes that have a special meaning to rustc or rustdoc.
#[rustfmt::skip]
pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// ==========================================================================
@@ -123,7 +123,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
ungated!(ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing),
ungated!(
should_panic, Normal,
- template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"), FutureWarnFollowing,
+ template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason"), FutureWarnFollowing,
),
// FIXME(Centril): This can be used on stable but shouldn't.
ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing),
@@ -142,20 +142,24 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// Lints:
ungated!(
- warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+ warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+ DuplicatesOk, @only_local: true,
),
ungated!(
- allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+ allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+ DuplicatesOk, @only_local: true,
),
gated!(
expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk,
lint_reasons, experimental!(expect)
),
ungated!(
- forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+ forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+ DuplicatesOk, @only_local: true,
),
ungated!(
- deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+ deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+ DuplicatesOk, @only_local: true,
),
ungated!(must_use, Normal, template!(Word, NameValueStr: "reason"), FutureWarnFollowing),
gated!(
@@ -181,30 +185,28 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// ABI, linking, symbols, and FFI
ungated!(
link, Normal,
- template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#),
+ template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated""#),
DuplicatesOk,
),
ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(no_link, Normal, template!(Word), WarnFollowing),
- ungated!(repr, Normal, template!(List: "C"), DuplicatesOk),
+ ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, @only_local: true),
ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true),
ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, @only_local: true),
+ ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding),
// Limits:
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
gated!(
- const_eval_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
- const_eval_limit, experimental!(const_eval_limit)
- ),
- gated!(
move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
large_assignments, experimental!(move_size_limit)
),
// Entry point:
+ gated!(unix_sigpipe, Normal, template!(Word, NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing, experimental!(unix_sigpipe)),
ungated!(start, Normal, template!(Word), WarnFollowing),
ungated!(no_start, CrateLevel, template!(Word), WarnFollowing),
ungated!(no_main, CrateLevel, template!(Word), WarnFollowing),
@@ -226,11 +228,15 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, @only_local: true),
ungated!(cold, Normal, template!(Word), WarnFollowing, @only_local: true),
ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing),
- ungated!(target_feature, Normal, template!(List: r#"enable = "name""#), DuplicatesOk),
+ ungated!(
+ target_feature, Normal, template!(List: r#"enable = "name""#),
+ DuplicatesOk, @only_local: true,
+ ),
ungated!(track_caller, Normal, template!(Word), WarnFollowing),
+ ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding),
gated!(
no_sanitize, Normal,
- template!(List: "address, memory, thread"), DuplicatesOk,
+ template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
experimental!(no_sanitize)
),
gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)),
@@ -239,25 +245,23 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk
),
+ // Debugging
+ ungated!(
+ debugger_visualizer, Normal,
+ template!(List: r#"natvis_file = "...", gdb_script_file = "...""#), DuplicatesOk
+ ),
+
// ==========================================================================
// Unstable attributes:
// ==========================================================================
- // RFC #3191: #[debugger_visualizer] support
- gated!(
- debugger_visualizer, Normal, template!(List: r#"natvis_file = "...", gdb_script_file = "...""#),
- DuplicatesOk, experimental!(debugger_visualizer)
- ),
-
// Linking:
- gated!(naked, Normal, template!(Word), WarnFollowing, @only_local: true, naked_functions, experimental!(naked)),
gated!(
- link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, raw_dylib,
- experimental!(link_ordinal)
+ naked, Normal, template!(Word), WarnFollowing, @only_local: true,
+ naked_functions, experimental!(naked)
),
// Plugins:
- // XXX Modified for use in rust-analyzer
// BuiltinAttribute {
// name: sym::plugin,
// only_local: false,
@@ -274,10 +278,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// cfg_fn!(plugin)
// ),
// },
- BuiltinAttribute {
- name: "plugin",
- template: template!(List: "name"),
- },
// Testing:
gated!(
@@ -286,7 +286,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
),
// RFC #1268
gated!(
- marker, Normal, template!(Word), WarnFollowing, marker_trait_attr, experimental!(marker)
+ marker, Normal, template!(Word), WarnFollowing, @only_local: true,
+ marker_trait_attr, experimental!(marker)
),
gated!(
thread_local, Normal, template!(Word), WarnFollowing,
@@ -298,11 +299,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
optimize, Normal, template!(List: "size|speed"), ErrorPreceding, optimize_attribute,
experimental!(optimize),
),
- // RFC 2867
- gated!(
- instruction_set, Normal, template!(List: "set"), ErrorPreceding,
- isa_attribute, experimental!(instruction_set)
- ),
gated!(
ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice)
@@ -310,10 +306,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)),
gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)),
gated!(
- register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), DuplicatesOk,
- experimental!(register_attr),
- ),
- gated!(
register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk,
experimental!(register_tool),
),
@@ -325,7 +317,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// RFC 2632
gated!(
const_trait, Normal, template!(Word), WarnFollowing, const_trait_impl,
- "`const` is a temporary placeholder for marking a trait that is suitable for `const` \
+ "`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \
`impls` and all default bodies as `const`, which may be removed or renamed in the \
future."
),
@@ -335,22 +327,47 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
experimental!(deprecated_safe),
),
+ // `#[collapse_debuginfo]`
+ gated!(
+ collapse_debuginfo, Normal, template!(Word), WarnFollowing,
+ experimental!(collapse_debuginfo)
+ ),
+
+ // RFC 2397
+ gated!(do_not_recommend, Normal, template!(Word), WarnFollowing, experimental!(do_not_recommend)),
+
+ // `#[cfi_encoding = ""]`
+ gated!(
+ cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding,
+ experimental!(cfi_encoding)
+ ),
+
// ==========================================================================
// Internal attributes: Stability, deprecation, and unsafe:
// ==========================================================================
- ungated!(feature, CrateLevel, template!(List: "name1, name2, ..."), DuplicatesOk),
+ ungated!(
+ feature, CrateLevel,
+ template!(List: "name1, name2, ..."), DuplicatesOk, @only_local: true,
+ ),
// DuplicatesOk since it has its own validation
ungated!(
- stable, Normal, template!(List: r#"feature = "name", since = "version""#), DuplicatesOk,
+ stable, Normal,
+ template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, @only_local: true,
),
ungated!(
unstable, Normal,
template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk,
),
ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
- ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
- ungated!(rustc_safe_intrinsic, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
+ ungated!(
+ rustc_const_stable, Normal,
+ template!(List: r#"feature = "name""#), DuplicatesOk, @only_local: true,
+ ),
+ ungated!(
+ rustc_default_body_unstable, Normal,
+ template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk
+ ),
gated!(
allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk,
"allow_internal_unstable side-steps feature gating and stability checks",
@@ -364,6 +381,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
"allow_internal_unsafe side-steps the unsafe_code lint",
),
+ ungated!(rustc_safe_intrinsic, Normal, template!(Word), DuplicatesOk),
+ rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing,
+ "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
+ through unstable paths"),
// ==========================================================================
// Internal attributes: Type system related:
@@ -381,10 +402,9 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
- gated!(
- alloc_error_handler, Normal, template!(Word), WarnFollowing,
- experimental!(alloc_error_handler)
- ),
+ rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
+ rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
+ rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
gated!(
default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals,
experimental!(default_lib_allocator),
@@ -465,6 +485,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints
// to assist in changes to diagnostic APIs.
rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+ // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions`
+ // types (as well as any others in future).
+ rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+ // Used by the `rustc::bad_opt_access` lint on fields
+ // types (as well as any others in future).
+ rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE),
// ==========================================================================
// Internal attributes, Const related:
@@ -508,8 +534,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
"language items are subject to change",
),
rustc_attr!(
- rustc_pass_by_value, Normal,
- template!(Word), ErrorFollowing,
+ rustc_pass_by_value, Normal, template!(Word), ErrorFollowing,
"#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
),
rustc_attr!(
@@ -517,10 +542,18 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
"#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`."
),
rustc_attr!(
+ rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, @only_local: true,
+ "#![rustc_coinductive] changes a trait to be coinductive, allowing cycles in the trait solver."
+ ),
+ rustc_attr!(
rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true,
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
),
rustc_attr!(
+ rustc_deny_explicit_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: false,
+ "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls"
+ ),
+ rustc_attr!(
rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing,
"#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \
the given type by annotating all impl items with #[rustc_allow_incoherent_impl]."
@@ -531,24 +564,20 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
and it is only intended to be used in `alloc`."
),
- // modified for r-a
- // BuiltinAttribute {
- // name: sym::rustc_diagnostic_item,
- // // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
- // only_local: false,
- // type_: Normal,
- // template: template!(NameValueStr: "name"),
- // duplicates: ErrorFollowing,
- // gate: Gated(
- // Stability::Unstable,
- // sym::rustc_attrs,
- // "diagnostic items compiler internal support for linting",
- // cfg_fn!(rustc_attrs),
- // ),
- // },
BuiltinAttribute {
+ // name: sym::rustc_diagnostic_item,
name: "rustc_diagnostic_item",
+ // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
+ // only_local: false,
+ // type_: Normal,
template: template!(NameValueStr: "name"),
+ // duplicates: ErrorFollowing,
+ // gate: Gated(
+ // Stability::Unstable,
+ // sym::rustc_attrs,
+ // "diagnostic items compiler internal support for linting",
+ // cfg_fn!(rustc_attrs),
+ // ),
},
gated!(
// Used in resolve:
@@ -572,7 +601,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
for reserving for `for<T> From<!> for T` impl"
),
rustc_attr!(
- rustc_test_marker, Normal, template!(Word), WarnFollowing,
+ rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing,
"the `#[rustc_test_marker]` attribute is used internally to track tests",
),
rustc_attr!(
@@ -598,11 +627,16 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
definition of a trait, it's currently in experimental form and should be changed before \
being exposed outside of the std"
),
+ rustc_attr!(
+ rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing,
+ r#"`rustc_doc_primitive` is a rustc internal attribute"#,
+ ),
// ==========================================================================
// Internal attributes, Testing:
// ==========================================================================
+ rustc_attr!(TEST, rustc_effective_visibility, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_outlives, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing),
@@ -643,6 +677,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_def_path, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk),
+ gated!(
+ custom_mir, Normal, template!(List: r#"dialect = "...", phase = "...""#),
+ ErrorFollowing, "the `#[custom_mir]` attribute is just used for the Rust test suite",
+ ),
rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs
new file mode 100644
index 000000000..e4c8d446a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs
@@ -0,0 +1,40 @@
+//! This module contains tests for doc-expression parsing.
+//! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
+
+use mbe::syntax_node_to_token_tree;
+use syntax::{ast, AstNode};
+
+use crate::attr::{DocAtom, DocExpr};
+
+fn assert_parse_result(input: &str, expected: DocExpr) {
+ let (tt, _) = {
+ let source_file = ast::SourceFile::parse(input).ok().unwrap();
+ let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+ syntax_node_to_token_tree(tt.syntax())
+ };
+ let cfg = DocExpr::parse(&tt);
+ assert_eq!(cfg, expected);
+}
+
+#[test]
+fn test_doc_expr_parser() {
+ assert_parse_result("#![doc(hidden)]", DocAtom::Flag("hidden".into()).into());
+
+ assert_parse_result(
+ r#"#![doc(alias = "foo")]"#,
+ DocAtom::KeyValue { key: "alias".into(), value: "foo".into() }.into(),
+ );
+
+ assert_parse_result(r#"#![doc(alias("foo"))]"#, DocExpr::Alias(["foo".into()].into()));
+ assert_parse_result(
+ r#"#![doc(alias("foo", "bar", "baz"))]"#,
+ DocExpr::Alias(["foo".into(), "bar".into(), "baz".into()].into()),
+ );
+
+ assert_parse_result(
+ r#"
+ #[doc(alias("Bar", "Qux"))]
+ struct Foo;"#,
+ DocExpr::Alias(["Bar".into(), "Qux".into()].into()),
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index b70e658ef..94dc39b11 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -6,267 +6,30 @@ mod tests;
pub mod scope;
mod pretty;
-use std::{ops::Index, sync::Arc};
+use std::ops::Index;
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
-use drop_bomb::DropBomb;
use either::Either;
-use hir_expand::{
- attrs::RawAttrs, hygiene::Hygiene, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId,
-};
+use hir_expand::{name::Name, HirFileId, InFile};
use la_arena::{Arena, ArenaMap};
-use limit::Limit;
use profile::Count;
use rustc_hash::FxHashMap;
-use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
+use syntax::{ast, AstPtr, SyntaxNodePtr};
+use triomphe::Arc;
use crate::{
- attr::Attrs,
db::DefDatabase,
- expr::{
+ expander::Expander,
+ hir::{
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
},
- item_scope::BuiltinShadowMode,
- macro_id_to_def_id,
nameres::DefMap,
path::{ModPath, Path},
src::{HasChildSource, HasSource},
- AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
- UnresolvedMacro,
+ BlockId, DefWithBodyId, HasModule, Lookup,
};
-pub use lower::LowerCtx;
-
-/// 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 {
- cfg_options: CfgOptions,
- hygiene: Hygiene,
- krate: CrateId,
-}
-
-#[derive(Debug)]
-pub struct Expander {
- cfg_expander: CfgExpander,
- def_map: Arc<DefMap>,
- current_file_id: HirFileId,
- module: LocalModuleId,
- /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
- recursion_depth: usize,
-}
-
-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)
- }
-}
-
-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 def_map = module.def_map(db);
- Expander {
- cfg_expander,
- def_map,
- current_file_id,
- module: module.local_id,
- recursion_depth: 0,
- }
- }
-
- pub fn enter_expand<T: ast::AstNode>(
- &mut self,
- db: &dyn DefDatabase,
- macro_call: ast::MacroCall,
- ) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
- let mut unresolved_macro_err = None;
-
- let result = self.within_limit(db, |this| {
- let macro_call = InFile::new(this.current_file_id, &macro_call);
-
- let resolver =
- |path| this.resolve_path_as_macro(db, &path).map(|it| macro_id_to_def_id(db, it));
-
- let mut err = None;
- let call_id = match macro_call.as_call_id_with_errors(
- db,
- this.def_map.krate(),
- resolver,
- &mut |e| {
- err.get_or_insert(e);
- },
- ) {
- Ok(call_id) => call_id,
- Err(resolve_err) => {
- unresolved_macro_err = Some(resolve_err);
- return ExpandResult { value: None, err: None };
- }
- };
- ExpandResult { value: call_id.ok(), err }
- });
-
- if let Some(err) = unresolved_macro_err {
- Err(err)
- } else {
- Ok(result)
- }
- }
-
- pub fn enter_expand_id<T: ast::AstNode>(
- &mut self,
- db: &dyn DefDatabase,
- call_id: MacroCallId,
- ) -> ExpandResult<Option<(Mark, T)>> {
- self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
- }
-
- fn enter_expand_inner(
- db: &dyn DefDatabase,
- call_id: MacroCallId,
- mut err: Option<ExpandError>,
- ) -> ExpandResult<Option<(HirFileId, SyntaxNode)>> {
- if err.is_none() {
- err = db.macro_expand_error(call_id);
- }
-
- let file_id = call_id.as_file();
-
- let raw_node = match db.parse_or_expand(file_id) {
- Some(it) => it,
- None => {
- // Only `None` if the macro expansion produced no usable AST.
- if err.is_none() {
- tracing::warn!("no error despite `parse_or_expand` failing");
- }
-
- return ExpandResult::only_err(err.unwrap_or_else(|| {
- ExpandError::Other("failed to parse macro invocation".into())
- }));
- }
- };
-
- ExpandResult { value: Some((file_id, raw_node)), err }
- }
-
- pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
- self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
- self.current_file_id = mark.file_id;
- if self.recursion_depth == usize::MAX {
- // Recursion limit has been reached somewhere in the macro expansion tree. Reset the
- // depth only when we get out of the tree.
- if !self.current_file_id.is_macro() {
- self.recursion_depth = 0;
- }
- } else {
- self.recursion_depth -= 1;
- }
- mark.bomb.defuse();
- }
-
- pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
- InFile { file_id: self.current_file_id, value }
- }
-
- pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
- self.cfg_expander.parse_attrs(db, owner)
- }
-
- pub(crate) fn cfg_options(&self) -> &CfgOptions {
- &self.cfg_expander.cfg_options
- }
-
- pub fn current_file_id(&self) -> HirFileId {
- self.current_file_id
- }
-
- fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
- let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene);
- Path::from_src(path, &ctx)
- }
-
- fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroId> {
- self.def_map.resolve_path(db, self.module, path, BuiltinShadowMode::Other).0.take_macros()
- }
-
- fn recursion_limit(&self, db: &dyn DefDatabase) -> Limit {
- let limit = db.crate_limits(self.cfg_expander.krate).recursion_limit as _;
-
- #[cfg(not(test))]
- return Limit::new(limit);
-
- // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
- #[cfg(test)]
- return Limit::new(std::cmp::min(32, limit));
- }
-
- fn within_limit<F, T: ast::AstNode>(
- &mut self,
- db: &dyn DefDatabase,
- op: F,
- ) -> ExpandResult<Option<(Mark, T)>>
- where
- F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
- {
- if self.recursion_depth == usize::MAX {
- // Recursion limit has been reached somewhere in the macro expansion tree. We should
- // stop expanding other macro calls in this tree, or else this may result in
- // exponential number of macro expansions, leading to a hang.
- //
- // The overflow error should have been reported when it occurred (see the next branch),
- // so don't return overflow error here to avoid diagnostics duplication.
- cov_mark::hit!(overflow_but_not_me);
- return ExpandResult::only_err(ExpandError::RecursionOverflowPosioned);
- } else if self.recursion_limit(db).check(self.recursion_depth + 1).is_err() {
- self.recursion_depth = usize::MAX;
- cov_mark::hit!(your_stack_belongs_to_me);
- return ExpandResult::only_err(ExpandError::Other(
- "reached recursion limit during macro expansion".into(),
- ));
- }
-
- let ExpandResult { value, err } = op(self);
- let Some(call_id) = value else {
- return ExpandResult { value: None, err };
- };
-
- Self::enter_expand_inner(db, call_id, err).map(|value| {
- value.and_then(|(new_file_id, node)| {
- let node = T::cast(node)?;
-
- self.recursion_depth += 1;
- self.cfg_expander.hygiene = Hygiene::new(db.upcast(), new_file_id);
- let old_file_id = std::mem::replace(&mut self.current_file_id, new_file_id);
- let mark =
- Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") };
- Some((mark, node))
- })
- })
- }
-}
-
-#[derive(Debug)]
-pub struct Mark {
- file_id: HirFileId,
- bomb: DropBomb,
-}
-
/// The body of an item (function, const etc.).
#[derive(Debug, Eq, PartialEq)]
pub struct Body {
@@ -274,6 +37,9 @@ pub struct Body {
pub pats: Arena<Pat>,
pub bindings: Arena<Binding>,
pub labels: Arena<Label>,
+ /// Id of the closure/generator that owns the corresponding binding. If a binding is owned by the
+ /// top level expression, it will not be listed in here.
+ pub binding_owners: FxHashMap<BindingId, ExprId>,
/// The patterns for the function's parameters. While the parameter types are
/// part of the function signature, the patterns are not (they don't change
/// the external type of the function).
@@ -343,6 +109,8 @@ pub enum BodyDiagnostic {
MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId },
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
+ UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
+ UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
}
impl Body {
@@ -353,45 +121,51 @@ impl Body {
let _p = profile::span("body_with_source_map_query");
let mut params = None;
- let (file_id, module, body) = match def {
- DefWithBodyId::FunctionId(f) => {
- let f = f.lookup(db);
- let src = f.source(db);
- params = src.value.param_list().map(|param_list| {
- let item_tree = f.id.item_tree(db);
- let func = &item_tree[f.id.value];
- let krate = f.container.module(db).krate;
- let crate_graph = db.crate_graph();
- (
- param_list,
- func.params.clone().map(move |param| {
- item_tree
- .attrs(db, krate, param.into())
- .is_cfg_enabled(&crate_graph[krate].cfg_options)
- }),
- )
- });
- (src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
- }
- DefWithBodyId::ConstId(c) => {
- let c = c.lookup(db);
- let src = c.source(db);
- (src.file_id, c.module(db), src.value.body())
- }
- DefWithBodyId::StaticId(s) => {
- let s = s.lookup(db);
- let src = s.source(db);
- (src.file_id, s.module(db), src.value.body())
- }
- DefWithBodyId::VariantId(v) => {
- let e = v.parent.lookup(db);
- let src = v.parent.child_source(db);
- let variant = &src.value[v.local_id];
- (src.file_id, e.container, variant.expr())
+ let mut is_async_fn = false;
+ let InFile { file_id, value: body } = {
+ match def {
+ DefWithBodyId::FunctionId(f) => {
+ let data = db.function_data(f);
+ let f = f.lookup(db);
+ let src = f.source(db);
+ params = src.value.param_list().map(|param_list| {
+ let item_tree = f.id.item_tree(db);
+ let func = &item_tree[f.id.value];
+ let krate = f.container.module(db).krate;
+ let crate_graph = db.crate_graph();
+ (
+ param_list,
+ func.params.clone().map(move |param| {
+ item_tree
+ .attrs(db, krate, param.into())
+ .is_cfg_enabled(&crate_graph[krate].cfg_options)
+ }),
+ )
+ });
+ is_async_fn = data.has_async_kw();
+ src.map(|it| it.body().map(ast::Expr::from))
+ }
+ DefWithBodyId::ConstId(c) => {
+ let c = c.lookup(db);
+ let src = c.source(db);
+ src.map(|it| it.body())
+ }
+ DefWithBodyId::StaticId(s) => {
+ let s = s.lookup(db);
+ let src = s.source(db);
+ src.map(|it| it.body())
+ }
+ DefWithBodyId::VariantId(v) => {
+ let src = v.parent.child_source(db);
+ src.map(|it| it[v.local_id].expr())
+ }
+ DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
}
};
+ let module = def.module(db);
let expander = Expander::new(db, file_id, module);
- let (mut body, source_map) = Body::new(db, expander, params, body);
+ let (mut body, source_map) =
+ Body::new(db, def, expander, params, body, module.krate, is_async_fn);
body.shrink_to_fit();
(Arc::new(body), Arc::new(source_map))
@@ -406,46 +180,65 @@ impl Body {
&'a self,
db: &'a dyn DefDatabase,
) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + '_ {
- self.block_scopes
- .iter()
- .map(move |&block| (block, db.block_def_map(block).expect("block ID without DefMap")))
+ self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
}
pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
pretty::print_body_hir(db, self, owner)
}
+ pub fn pretty_print_expr(
+ &self,
+ db: &dyn DefDatabase,
+ owner: DefWithBodyId,
+ expr: ExprId,
+ ) -> String {
+ pretty::print_expr_hir(db, self, owner, expr)
+ }
+
fn new(
db: &dyn DefDatabase,
+ owner: DefWithBodyId,
expander: Expander,
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
+ krate: CrateId,
+ is_async_fn: bool,
) -> (Body, BodySourceMap) {
- lower::lower(db, expander, params, body)
+ lower::lower(db, owner, expander, params, body, krate, is_async_fn)
}
fn shrink_to_fit(&mut self) {
- let Self { _c: _, body_expr: _, block_scopes, exprs, labels, params, pats, bindings } =
- self;
+ let Self {
+ _c: _,
+ body_expr: _,
+ block_scopes,
+ exprs,
+ labels,
+ params,
+ pats,
+ bindings,
+ binding_owners,
+ } = self;
block_scopes.shrink_to_fit();
exprs.shrink_to_fit();
labels.shrink_to_fit();
params.shrink_to_fit();
pats.shrink_to_fit();
bindings.shrink_to_fit();
+ binding_owners.shrink_to_fit();
}
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
self.walk_pats(pat_id, &mut |pat| {
- if let Pat::Bind { id, .. } = pat {
+ if let Pat::Bind { id, .. } = &self[pat] {
f(*id);
}
});
}
- pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(&Pat)) {
+ pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) {
let pat = &self[pat_id];
- f(pat);
match pat {
Pat::Range { .. }
| Pat::Lit(..)
@@ -455,21 +248,37 @@ impl Body {
| Pat::Missing => {}
&Pat::Bind { subpat, .. } => {
if let Some(subpat) = subpat {
- self.walk_pats(subpat, f);
+ f(subpat);
}
}
Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
- args.iter().copied().for_each(|p| self.walk_pats(p, f));
+ args.iter().copied().for_each(|p| f(p));
}
- Pat::Ref { pat, .. } => self.walk_pats(*pat, f),
+ Pat::Ref { pat, .. } => f(*pat),
Pat::Slice { prefix, slice, suffix } => {
let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
- total_iter.copied().for_each(|p| self.walk_pats(p, f));
+ total_iter.copied().for_each(|p| f(p));
}
Pat::Record { args, .. } => {
- args.iter().for_each(|RecordFieldPat { pat, .. }| self.walk_pats(*pat, f));
+ args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat));
+ }
+ Pat::Box { inner } => f(*inner),
+ }
+ }
+
+ pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) {
+ f(pat_id);
+ self.walk_pats_shallow(pat_id, |p| self.walk_pats(p, f));
+ }
+
+ pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool {
+ match self.binding_owners.get(&binding) {
+ Some(x) => {
+ // We assign expression ids in a way that outer closures will receive
+ // a lower id
+ x.into_raw() < relative_to.into_raw()
}
- Pat::Box { inner } => self.walk_pats(*inner, f),
+ None => true,
}
}
}
@@ -484,6 +293,7 @@ impl Default for Body {
labels: Default::default(),
params: Default::default(),
block_scopes: Default::default(),
+ binding_owners: Default::default(),
_c: Default::default(),
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
index fedaf3955..b375ec63a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
@@ -1,120 +1,148 @@
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
//! representation.
-use std::{mem, sync::Arc};
+use std::mem;
+use base_db::CrateId;
use either::Either;
use hir_expand::{
ast_id_map::AstIdMap,
- hygiene::Hygiene,
name::{name, AsName, Name},
- AstId, ExpandError, HirFileId, InFile,
+ AstId, ExpandError, InFile,
};
use intern::Interned;
-use la_arena::Arena;
-use once_cell::unsync::OnceCell;
use profile::Count;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use syntax::{
ast::{
- self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind,
+ self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasLoopBody, HasName,
SlicePatComponents,
},
AstNode, AstPtr, SyntaxNodePtr,
};
+use triomphe::Arc;
use crate::{
- adt::StructKind,
- body::{Body, BodySourceMap, Expander, ExprPtr, LabelPtr, LabelSource, PatPtr},
- body::{BodyDiagnostic, ExprSource, PatSource},
- builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
+ body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
+ data::adt::StructKind,
db::DefDatabase,
- expr::{
- dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId,
- FloatTypeWrapper, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId,
- RecordFieldPat, RecordLitField, Statement,
+ expander::Expander,
+ hir::{
+ dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy,
+ ClosureKind, Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
+ Pat, PatId, RecordFieldPat, RecordLitField, Statement,
},
item_scope::BuiltinShadowMode,
+ lang_item::LangItem,
+ lower::LowerCtx,
+ nameres::{DefMap, MacroSubNs},
path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef},
- AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
+ AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
};
-pub struct LowerCtx<'a> {
- pub db: &'a dyn DefDatabase,
- hygiene: Hygiene,
- ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
-}
-
-impl<'a> LowerCtx<'a> {
- pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
- LowerCtx {
- db,
- hygiene: Hygiene::new(db.upcast(), file_id),
- ast_id_map: Some((file_id, OnceCell::new())),
- }
- }
-
- pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
- LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
- }
-
- pub(crate) fn hygiene(&self) -> &Hygiene {
- &self.hygiene
- }
-
- pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
- Path::from_src(ast, self)
- }
-
- pub(crate) fn ast_id<N: AstNode>(&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)))
- }
-}
-
pub(super) fn lower(
db: &dyn DefDatabase,
+ owner: DefWithBodyId,
expander: Expander,
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
+ krate: CrateId,
+ is_async_fn: bool,
) -> (Body, BodySourceMap) {
ExprCollector {
db,
+ owner,
+ krate,
+ def_map: expander.module.def_map(db),
source_map: BodySourceMap::default(),
ast_id_map: db.ast_id_map(expander.current_file_id),
body: Body {
- exprs: Arena::default(),
- pats: Arena::default(),
- bindings: Arena::default(),
- labels: Arena::default(),
+ exprs: Default::default(),
+ pats: Default::default(),
+ bindings: Default::default(),
+ binding_owners: Default::default(),
+ labels: Default::default(),
params: Vec::new(),
body_expr: dummy_expr_id(),
block_scopes: Vec::new(),
_c: Count::new(),
},
expander,
+ current_try_block_label: None,
is_lowering_assignee_expr: false,
is_lowering_generator: false,
+ label_ribs: Vec::new(),
+ current_binding_owner: None,
}
- .collect(params, body)
+ .collect(params, body, is_async_fn)
}
struct ExprCollector<'a> {
db: &'a dyn DefDatabase,
expander: Expander,
+ owner: DefWithBodyId,
+ def_map: Arc<DefMap>,
ast_id_map: Arc<AstIdMap>,
+ krate: CrateId,
body: Body,
source_map: BodySourceMap,
+
is_lowering_assignee_expr: bool,
is_lowering_generator: bool,
+
+ current_try_block_label: Option<LabelId>,
+ // points to the expression that a try expression will target (replaces current_try_block_label)
+ // catch_scope: Option<ExprId>,
+ // points to the expression that an unlabeled control flow will target
+ // loop_scope: Option<ExprId>,
+ // needed to diagnose non label control flow in while conditions
+ // is_in_loop_condition: bool,
+
+ // resolution
+ label_ribs: Vec<LabelRib>,
+ current_binding_owner: Option<ExprId>,
+}
+
+#[derive(Clone, Debug)]
+struct LabelRib {
+ kind: RibKind,
+ // Once we handle macro hygiene this will need to be a map
+ label: Option<(Name, LabelId)>,
+}
+
+impl LabelRib {
+ fn new(kind: RibKind) -> Self {
+ LabelRib { kind, label: None }
+ }
+ fn new_normal(label: (Name, LabelId)) -> Self {
+ LabelRib { kind: RibKind::Normal, label: Some(label) }
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum RibKind {
+ Normal,
+ Closure,
+ Constant,
+}
+
+impl RibKind {
+ /// This rib forbids referring to labels defined in upwards ribs.
+ fn is_label_barrier(self) -> bool {
+ match self {
+ RibKind::Normal => false,
+ RibKind::Closure | RibKind::Constant => true,
+ }
+ }
}
#[derive(Debug, Default)]
struct BindingList {
map: FxHashMap<Name, BindingId>,
+ is_used: FxHashMap<BindingId, bool>,
+ reject_new: bool,
}
impl BindingList {
@@ -124,7 +152,27 @@ impl BindingList {
name: Name,
mode: BindingAnnotation,
) -> BindingId {
- *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode))
+ let id = *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode));
+ if ec.body.bindings[id].mode != mode {
+ ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
+ }
+ self.check_is_used(ec, id);
+ id
+ }
+
+ fn check_is_used(&mut self, ec: &mut ExprCollector<'_>, id: BindingId) {
+ match self.is_used.get(&id) {
+ None => {
+ if self.reject_new {
+ ec.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll);
+ }
+ }
+ Some(true) => {
+ ec.body.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce);
+ }
+ Some(false) => {}
+ }
+ self.is_used.insert(id, true);
}
}
@@ -133,13 +181,14 @@ impl ExprCollector<'_> {
mut self,
param_list: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
+ is_async_fn: bool,
) -> (Body, BodySourceMap) {
if let Some((param_list, mut attr_enabled)) = param_list {
if let Some(self_param) =
param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
{
let ptr = AstPtr::new(&self_param);
- let binding_id = self.alloc_binding(
+ let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
name![self],
BindingAnnotation::new(
self_param.mut_token().is_some() && self_param.amp_token().is_none(),
@@ -152,72 +201,35 @@ impl ExprCollector<'_> {
self.body.params.push(param_pat);
}
- for pat in param_list
- .params()
- .zip(attr_enabled)
- .filter_map(|(param, enabled)| param.pat().filter(|_| enabled))
+ for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
{
- let param_pat = self.collect_pat(pat);
+ let param_pat = self.collect_pat_top(param.pat());
self.body.params.push(param_pat);
}
};
+ self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
+ if is_async_fn {
+ match body {
+ Some(e) => {
+ let expr = this.collect_expr(e);
+ this.alloc_expr_desugared(Expr::Async {
+ id: None,
+ statements: Box::new([]),
+ tail: Some(expr),
+ })
+ }
+ None => this.missing_expr(),
+ }
+ } else {
+ this.collect_expr_opt(body)
+ }
+ });
- self.body.body_expr = self.collect_expr_opt(body);
(self.body, self.source_map)
}
fn ctx(&self) -> LowerCtx<'_> {
- LowerCtx::new(self.db, self.expander.current_file_id)
- }
-
- fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
- let src = self.expander.to_source(ptr);
- let id = self.make_expr(expr, src.clone());
- self.source_map.expr_map.insert(src, id);
- id
- }
- // desugared exprs don't have ptr, that's wrong and should be fixed
- // somehow.
- fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
- self.body.exprs.alloc(expr)
- }
- fn missing_expr(&mut self) -> ExprId {
- self.alloc_expr_desugared(Expr::Missing)
- }
- fn make_expr(&mut self, expr: Expr, src: ExprSource) -> ExprId {
- let id = self.body.exprs.alloc(expr);
- self.source_map.expr_map_back.insert(id, src);
- id
- }
-
- fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
- self.body.bindings.alloc(Binding { name, mode, definitions: SmallVec::new() })
- }
- fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
- let src = self.expander.to_source(ptr);
- let id = self.make_pat(pat, src.clone());
- self.source_map.pat_map.insert(src, id);
- id
- }
- fn missing_pat(&mut self) -> PatId {
- self.body.pats.alloc(Pat::Missing)
- }
- fn make_pat(&mut self, pat: Pat, src: PatSource) -> PatId {
- let id = self.body.pats.alloc(pat);
- self.source_map.pat_map_back.insert(id, src);
- id
- }
-
- fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
- let src = self.expander.to_source(ptr);
- let id = self.make_label(label, src.clone());
- self.source_map.label_map.insert(src, id);
- id
- }
- fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId {
- let id = self.body.labels.alloc(label);
- self.source_map.label_map_back.insert(id, src);
- id
+ self.expander.ctx(self.db)
}
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@@ -229,6 +241,7 @@ impl ExprCollector<'_> {
let syntax_ptr = AstPtr::new(&expr);
self.check_cfg(&expr)?;
+ // FIXME: Move some of these arms out into separate methods for clarity
Some(match expr {
ast::Expr::IfExpr(e) => {
let then_branch = self.collect_block_opt(e.then_branch());
@@ -246,18 +259,12 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
}
ast::Expr::LetExpr(e) => {
- let pat = self.collect_pat_opt(e.pat());
+ let pat = self.collect_pat_top(e.pat());
let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
}
ast::Expr::BlockExpr(e) => match e.modifier() {
- Some(ast::BlockModifier::Try(_)) => {
- self.collect_block_(e, |id, statements, tail| Expr::TryBlock {
- id,
- statements,
- tail,
- })
- }
+ Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e),
Some(ast::BlockModifier::Unsafe(_)) => {
self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
id,
@@ -267,50 +274,77 @@ impl ExprCollector<'_> {
}
Some(ast::BlockModifier::Label(label)) => {
let label = self.collect_label(label);
- self.collect_block_(e, |id, statements, tail| Expr::Block {
- id,
- statements,
- tail,
- label: Some(label),
+ self.with_labeled_rib(label, |this| {
+ this.collect_block_(e, |id, statements, tail| Expr::Block {
+ id,
+ statements,
+ tail,
+ label: Some(label),
+ })
+ })
+ }
+ Some(ast::BlockModifier::Async(_)) => {
+ self.with_label_rib(RibKind::Closure, |this| {
+ this.collect_block_(e, |id, statements, tail| Expr::Async {
+ id,
+ statements,
+ tail,
+ })
+ })
+ }
+ Some(ast::BlockModifier::Const(_)) => {
+ self.with_label_rib(RibKind::Constant, |this| {
+ 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 {
+ parent: this.owner,
+ root: inner_expr,
+ });
+ this.body.exprs[result_expr_id] = Expr::Const(x);
+ this.current_binding_owner = prev_binding_owner;
+ result_expr_id
})
}
- Some(ast::BlockModifier::Async(_)) => self
- .collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }),
- Some(ast::BlockModifier::Const(_)) => self
- .collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }),
None => self.collect_block(e),
},
ast::Expr::LoopExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
- let body = self.collect_block_opt(e.loop_body());
+ let body = self.collect_labelled_block_opt(label, e.loop_body());
self.alloc_expr(Expr::Loop { body, label }, syntax_ptr)
}
ast::Expr::WhileExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
- let body = self.collect_block_opt(e.loop_body());
-
+ let body = self.collect_labelled_block_opt(label, e.loop_body());
let condition = self.collect_expr_opt(e.condition());
self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
}
- ast::Expr::ForExpr(e) => {
- let label = e.label().map(|label| self.collect_label(label));
- let iterable = self.collect_expr_opt(e.iterable());
- let pat = self.collect_pat_opt(e.pat());
- let body = self.collect_block_opt(e.loop_body());
- self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
- }
+ ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e),
ast::Expr::CallExpr(e) => {
- let callee = self.collect_expr_opt(e.expr());
- let args = if let Some(arg_list) = e.arg_list() {
- arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
- } else {
- Box::default()
+ let is_rustc_box = {
+ let attrs = e.attrs();
+ attrs.filter_map(|x| x.as_simple_atom()).any(|x| x == "rustc_box")
};
- self.alloc_expr(
- Expr::Call { callee, args, is_assignee_expr: self.is_lowering_assignee_expr },
- syntax_ptr,
- )
+ if is_rustc_box {
+ let expr = self.collect_expr_opt(e.arg_list().and_then(|x| x.args().next()));
+ self.alloc_expr(Expr::Box { expr }, syntax_ptr)
+ } else {
+ let callee = self.collect_expr_opt(e.expr());
+ let args = if let Some(arg_list) = e.arg_list() {
+ arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
+ } else {
+ Box::default()
+ };
+ self.alloc_expr(
+ Expr::Call {
+ callee,
+ args,
+ is_assignee_expr: self.is_lowering_assignee_expr,
+ },
+ syntax_ptr,
+ )
+ }
}
ast::Expr::MethodCallExpr(e) => {
let receiver = self.collect_expr_opt(e.receiver());
@@ -336,7 +370,7 @@ impl ExprCollector<'_> {
.arms()
.filter_map(|arm| {
self.check_cfg(&arm).map(|()| MatchArm {
- pat: self.collect_pat_opt(arm.pat()),
+ pat: self.collect_pat_top(arm.pat()),
expr: self.collect_expr_opt(arm.expr()),
guard: arm
.guard()
@@ -357,16 +391,20 @@ impl ExprCollector<'_> {
.unwrap_or(Expr::Missing);
self.alloc_expr(path, syntax_ptr)
}
- ast::Expr::ContinueExpr(e) => self.alloc_expr(
- Expr::Continue { label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
- syntax_ptr,
- ),
+ ast::Expr::ContinueExpr(e) => {
+ let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
+ self.source_map.diagnostics.push(e);
+ None
+ });
+ self.alloc_expr(Expr::Continue { label }, syntax_ptr)
+ }
ast::Expr::BreakExpr(e) => {
+ let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
+ self.source_map.diagnostics.push(e);
+ None
+ });
let expr = e.expr().map(|e| self.collect_expr(e));
- self.alloc_expr(
- Expr::Break { expr, label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
- syntax_ptr,
- )
+ self.alloc_expr(Expr::Break { expr, label }, syntax_ptr)
}
ast::Expr::ParenExpr(e) => {
let inner = self.collect_expr_opt(e.expr());
@@ -437,10 +475,7 @@ impl ExprCollector<'_> {
let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
}
- ast::Expr::TryExpr(e) => {
- let expr = self.collect_expr_opt(e.expr());
- self.alloc_expr(Expr::Try { expr }, syntax_ptr)
- }
+ ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e),
ast::Expr::CastExpr(e) => {
let expr = self.collect_expr_opt(e.expr());
let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
@@ -470,14 +505,16 @@ impl ExprCollector<'_> {
None => self.alloc_expr(Expr::Missing, syntax_ptr),
}
}
- ast::Expr::ClosureExpr(e) => {
+ ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
+ let (result_expr_id, prev_binding_owner) =
+ this.initialize_binding_owner(syntax_ptr);
let mut args = Vec::new();
let mut arg_types = Vec::new();
if let Some(pl) = e.param_list() {
for param in pl.params() {
- let pat = self.collect_pat_opt(param.pat());
+ let pat = this.collect_pat_top(param.pat());
let type_ref =
- param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+ param.ty().map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
args.push(pat);
arg_types.push(type_ref);
}
@@ -485,14 +522,14 @@ impl ExprCollector<'_> {
let ret_type = e
.ret_type()
.and_then(|r| r.ty())
- .map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+ .map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
- let prev_is_lowering_generator = self.is_lowering_generator;
- self.is_lowering_generator = false;
+ let prev_is_lowering_generator = mem::take(&mut this.is_lowering_generator);
+ let prev_try_block_label = this.current_try_block_label.take();
- let body = self.collect_expr_opt(e.body());
+ let body = this.collect_expr_opt(e.body());
- let closure_kind = if self.is_lowering_generator {
+ let closure_kind = if this.is_lowering_generator {
let movability = if e.static_token().is_some() {
Movability::Static
} else {
@@ -504,19 +541,21 @@ impl ExprCollector<'_> {
} else {
ClosureKind::Closure
};
- self.is_lowering_generator = prev_is_lowering_generator;
-
- self.alloc_expr(
- Expr::Closure {
- args: args.into(),
- arg_types: arg_types.into(),
- ret_type,
- body,
- closure_kind,
- },
- syntax_ptr,
- )
- }
+ let capture_by =
+ if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
+ this.is_lowering_generator = prev_is_lowering_generator;
+ this.current_binding_owner = prev_binding_owner;
+ this.current_try_block_label = prev_try_block_label;
+ this.body.exprs[result_expr_id] = Expr::Closure {
+ args: args.into(),
+ arg_types: arg_types.into(),
+ ret_type,
+ body,
+ closure_kind,
+ capture_by,
+ };
+ result_expr_id
+ }),
ast::Expr::BinExpr(e) => {
let op = e.op_kind();
if let Some(ast::BinaryOp::Assignment { op: None }) = op {
@@ -528,9 +567,18 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
}
ast::Expr::TupleExpr(e) => {
- let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect();
+ let mut exprs: Vec<_> = e.fields().map(|expr| self.collect_expr(expr)).collect();
+ // if there is a leading comma, the user is most likely to type out a leading expression
+ // so we insert a missing expression at the beginning for IDE features
+ if comma_follows_token(e.l_paren_token()) {
+ exprs.insert(0, self.missing_expr());
+ }
+
self.alloc_expr(
- Expr::Tuple { exprs, is_assignee_expr: self.is_lowering_assignee_expr },
+ Expr::Tuple {
+ exprs: exprs.into_boxed_slice(),
+ is_assignee_expr: self.is_lowering_assignee_expr,
+ },
syntax_ptr,
)
}
@@ -555,7 +603,17 @@ impl ExprCollector<'_> {
}
ArrayExprKind::Repeat { initializer, repeat } => {
let initializer = self.collect_expr_opt(initializer);
- let repeat = self.collect_expr_opt(repeat);
+ let repeat = self.with_label_rib(RibKind::Constant, |this| {
+ if let Some(repeat) = repeat {
+ let syntax_ptr = AstPtr::new(&repeat);
+ this.collect_as_a_binding_owner_bad(
+ |this| this.collect_expr(repeat),
+ syntax_ptr,
+ )
+ } else {
+ this.missing_expr()
+ }
+ });
self.alloc_expr(
Expr::Array(Array::Repeat { initializer, repeat }),
syntax_ptr,
@@ -601,6 +659,240 @@ impl ExprCollector<'_> {
})
}
+ fn initialize_binding_owner(
+ &mut self,
+ syntax_ptr: AstPtr<ast::Expr>,
+ ) -> (ExprId, Option<ExprId>) {
+ let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
+ let prev_binding_owner = self.current_binding_owner.take();
+ self.current_binding_owner = Some(result_expr_id);
+ (result_expr_id, prev_binding_owner)
+ }
+
+ /// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently
+ /// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have
+ /// their own body. Don't add more usage for this function so that we can remove this function after
+ /// separating those bodies.
+ fn collect_as_a_binding_owner_bad(
+ &mut self,
+ job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId,
+ syntax_ptr: AstPtr<ast::Expr>,
+ ) -> ExprId {
+ let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
+ let tmp = job(self);
+ self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing);
+ self.current_binding_owner = prev_owner;
+ id
+ }
+
+ /// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
+ /// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
+ /// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
+ fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
+ let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else {
+ return self.collect_block(e);
+ };
+ let label = self.alloc_label_desugared(Label { name: Name::generate_new_name() });
+ let old_label = self.current_try_block_label.replace(label);
+
+ let (btail, expr_id) = self.with_labeled_rib(label, |this| {
+ let mut btail = None;
+ let block = this.collect_block_(e, |id, statements, tail| {
+ btail = tail;
+ Expr::Block { id, statements, tail, label: Some(label) }
+ });
+ (btail, block)
+ });
+
+ let callee = self.alloc_expr_desugared(Expr::Path(try_from_output));
+ let next_tail = match btail {
+ Some(tail) => self.alloc_expr_desugared(Expr::Call {
+ callee,
+ args: Box::new([tail]),
+ is_assignee_expr: false,
+ }),
+ None => {
+ let unit = self.alloc_expr_desugared(Expr::Tuple {
+ exprs: Box::new([]),
+ is_assignee_expr: false,
+ });
+ self.alloc_expr_desugared(Expr::Call {
+ callee,
+ args: Box::new([unit]),
+ is_assignee_expr: false,
+ })
+ }
+ };
+ let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
+ unreachable!("block was lowered to non-block");
+ };
+ *tail = Some(next_tail);
+ self.current_try_block_label = old_label;
+ expr_id
+ }
+
+ /// Desugar `ast::ForExpr` from: `[opt_ident]: for <pat> in <head> <body>` into:
+ /// ```ignore (pseudo-rust)
+ /// match IntoIterator::into_iter(<head>) {
+ /// mut iter => {
+ /// [opt_ident]: loop {
+ /// match Iterator::next(&mut iter) {
+ /// None => break,
+ /// Some(<pat>) => <body>,
+ /// };
+ /// }
+ /// }
+ /// }
+ /// ```
+ fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId {
+ let Some((into_iter_fn, iter_next_fn, option_some, option_none)) = (|| {
+ Some((
+ LangItem::IntoIterIntoIter.path(self.db, self.krate)?,
+ LangItem::IteratorNext.path(self.db, self.krate)?,
+ LangItem::OptionSome.path(self.db, self.krate)?,
+ LangItem::OptionNone.path(self.db, self.krate)?,
+ ))
+ })() else {
+ // Some of the needed lang items are missing, so we can't desugar
+ return self.alloc_expr(Expr::Missing, syntax_ptr);
+ };
+ let head = self.collect_expr_opt(e.iterable());
+ let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr.clone());
+ let iterator = self.alloc_expr(
+ Expr::Call {
+ callee: into_iter_fn_expr,
+ args: Box::new([head]),
+ is_assignee_expr: false,
+ },
+ syntax_ptr.clone(),
+ );
+ let none_arm = MatchArm {
+ pat: self.alloc_pat_desugared(Pat::Path(Box::new(option_none))),
+ guard: None,
+ expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone()),
+ };
+ let some_pat = Pat::TupleStruct {
+ path: Some(Box::new(option_some)),
+ args: Box::new([self.collect_pat_top(e.pat())]),
+ ellipsis: None,
+ };
+ let label = e.label().map(|label| self.collect_label(label));
+ let some_arm = MatchArm {
+ 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()))
+ }),
+ };
+ let iter_name = Name::generate_new_name();
+ let iter_expr =
+ self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr.clone());
+ let iter_expr_mut = self.alloc_expr(
+ Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
+ syntax_ptr.clone(),
+ );
+ let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr.clone());
+ let iter_next_expr = self.alloc_expr(
+ Expr::Call {
+ callee: iter_next_fn_expr,
+ args: Box::new([iter_expr_mut]),
+ is_assignee_expr: false,
+ },
+ syntax_ptr.clone(),
+ );
+ let loop_inner = self.alloc_expr(
+ Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) },
+ syntax_ptr.clone(),
+ );
+ let loop_outer =
+ self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr.clone());
+ let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable);
+ let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None });
+ self.add_definition_to_binding(iter_binding, iter_pat);
+ self.alloc_expr(
+ Expr::Match {
+ expr: iterator,
+ arms: Box::new([MatchArm { pat: iter_pat, guard: None, expr: loop_outer }]),
+ },
+ syntax_ptr.clone(),
+ )
+ }
+
+ /// Desugar `ast::TryExpr` from: `<expr>?` into:
+ /// ```ignore (pseudo-rust)
+ /// match Try::branch(<expr>) {
+ /// ControlFlow::Continue(val) => val,
+ /// ControlFlow::Break(residual) =>
+ /// // If there is an enclosing `try {...}`:
+ /// break 'catch_target Try::from_residual(residual),
+ /// // Otherwise:
+ /// return Try::from_residual(residual),
+ /// }
+ /// ```
+ fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId {
+ let Some((try_branch, cf_continue, cf_break, try_from_residual)) = (|| {
+ Some((
+ LangItem::TryTraitBranch.path(self.db, self.krate)?,
+ LangItem::ControlFlowContinue.path(self.db, self.krate)?,
+ LangItem::ControlFlowBreak.path(self.db, self.krate)?,
+ LangItem::TryTraitFromResidual.path(self.db, self.krate)?,
+ ))
+ })() else {
+ // Some of the needed lang items are missing, so we can't desugar
+ return self.alloc_expr(Expr::Missing, syntax_ptr);
+ };
+ let operand = self.collect_expr_opt(e.expr());
+ let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr.clone());
+ let expr = self.alloc_expr(
+ Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false },
+ syntax_ptr.clone(),
+ );
+ let continue_name = Name::generate_new_name();
+ let continue_binding =
+ self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated);
+ let continue_bpat =
+ self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None });
+ self.add_definition_to_binding(continue_binding, continue_bpat);
+ let continue_arm = MatchArm {
+ pat: self.alloc_pat_desugared(Pat::TupleStruct {
+ path: Some(Box::new(cf_continue)),
+ args: Box::new([continue_bpat]),
+ ellipsis: None,
+ }),
+ guard: None,
+ expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr.clone()),
+ };
+ let break_name = Name::generate_new_name();
+ let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated);
+ let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
+ self.add_definition_to_binding(break_binding, break_bpat);
+ let break_arm = MatchArm {
+ pat: self.alloc_pat_desugared(Pat::TupleStruct {
+ path: Some(Box::new(cf_break)),
+ args: Box::new([break_bpat]),
+ ellipsis: None,
+ }),
+ guard: None,
+ expr: {
+ let x = 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 },
+ syntax_ptr.clone(),
+ );
+ self.alloc_expr(
+ match self.current_try_block_label {
+ Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
+ None => Expr::Return { expr: Some(result) },
+ },
+ syntax_ptr.clone(),
+ )
+ },
+ };
+ let arms = Box::new([continue_arm, break_arm]);
+ self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
+ }
+
fn collect_macro_call<F, T, U>(
&mut self,
mcall: ast::MacroCall,
@@ -616,7 +908,19 @@ impl ExprCollector<'_> {
let outer_file = self.expander.current_file_id;
let macro_call_ptr = self.expander.to_source(AstPtr::new(&mcall));
- let res = self.expander.enter_expand(self.db, mcall);
+ let module = self.expander.module.local_id;
+ let res = self.expander.enter_expand(self.db, mcall, |path| {
+ self.def_map
+ .resolve_path(
+ self.db,
+ module,
+ &path,
+ crate::item_scope::BuiltinShadowMode::Other,
+ Some(MacroSubNs::Bang),
+ )
+ .0
+ .take_macros()
+ });
let res = match res {
Ok(res) => res,
@@ -639,7 +943,7 @@ impl ExprCollector<'_> {
krate: *krate,
});
}
- Some(ExpandError::RecursionOverflowPosioned) => {
+ Some(ExpandError::RecursionOverflowPoisoned) => {
// Recursion limit has been reached in the macro expansion tree, but not in
// this very macro call. Don't add diagnostics to avoid duplication.
}
@@ -663,7 +967,11 @@ impl ExprCollector<'_> {
self.db.ast_id_map(self.expander.current_file_id),
);
- let id = collector(self, Some(expansion));
+ if record_diagnostics {
+ // FIXME: Report parse errors here
+ }
+
+ let id = collector(self, Some(expansion.tree()));
self.ast_id_map = prev_ast_id_map;
self.expander.exit(self.db, mark);
id
@@ -720,7 +1028,7 @@ impl ExprCollector<'_> {
if self.check_cfg(&stmt).is_none() {
return;
}
- let pat = self.collect_pat_opt(stmt.pat());
+ let pat = self.collect_pat_top(stmt.pat());
let type_ref =
stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
@@ -763,22 +1071,36 @@ impl ExprCollector<'_> {
fn collect_block_(
&mut self,
block: ast::BlockExpr,
- mk_block: impl FnOnce(BlockId, Box<[Statement]>, Option<ExprId>) -> Expr,
+ mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
) -> ExprId {
- let file_local_id = self.ast_id_map.ast_id(&block);
- let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
- let block_loc =
- BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) };
- let block_id = self.db.intern_block(block_loc);
-
- let (module, def_map) = match self.db.block_def_map(block_id) {
- Some(def_map) => {
- self.body.block_scopes.push(block_id);
- (def_map.root(), def_map)
- }
- None => (self.expander.module, self.expander.def_map.clone()),
+ let block_has_items = {
+ let statement_has_item = block.statements().any(|stmt| match stmt {
+ ast::Stmt::Item(_) => true,
+ // Macro calls can be both items and expressions. The syntax library always treats
+ // them as expressions here, so we undo that.
+ ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))),
+ _ => false,
+ });
+ statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
};
- let prev_def_map = mem::replace(&mut self.expander.def_map, def_map);
+
+ let block_id = if block_has_items {
+ let file_local_id = self.ast_id_map.ast_id(&block);
+ let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
+ Some(self.db.intern_block(BlockLoc { ast_id, module: self.expander.module }))
+ } else {
+ None
+ };
+
+ let (module, def_map) =
+ match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) {
+ Some((def_map, block_id)) => {
+ self.body.block_scopes.push(block_id);
+ (def_map.module_id(DefMap::ROOT), def_map)
+ }
+ None => (self.expander.module, self.def_map.clone()),
+ };
+ let prev_def_map = mem::replace(&mut self.def_map, def_map);
let prev_local_module = mem::replace(&mut self.expander.module, module);
let mut statements = Vec::new();
@@ -800,7 +1122,7 @@ impl ExprCollector<'_> {
let expr_id = self
.alloc_expr(mk_block(block_id, statements.into_boxed_slice(), tail), syntax_node_ptr);
- self.expander.def_map = prev_def_map;
+ self.def_map = prev_def_map;
self.expander.module = prev_local_module;
expr_id
}
@@ -812,43 +1134,46 @@ impl ExprCollector<'_> {
}
}
- fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
- let label = Label {
- name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
- };
- self.alloc_label(label, AstPtr::new(&ast_label))
+ fn collect_labelled_block_opt(
+ &mut self,
+ label: Option<LabelId>,
+ expr: Option<ast::BlockExpr>,
+ ) -> ExprId {
+ match label {
+ Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)),
+ None => self.collect_block_opt(expr),
+ }
}
- fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
- self.collect_pat_(pat, &mut BindingList::default())
- }
+ // region: patterns
- fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
+ fn collect_pat_top(&mut self, pat: Option<ast::Pat>) -> PatId {
match pat {
- Some(pat) => self.collect_pat(pat),
+ Some(pat) => self.collect_pat(pat, &mut BindingList::default()),
None => self.missing_pat(),
}
}
- fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
+ fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
let pattern = match &pat {
ast::Pat::IdentPat(bp) => {
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
let annotation =
BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
- let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat, binding_list));
+ let subpat = bp.pat().map(|subpat| self.collect_pat(subpat, binding_list));
let is_simple_ident_pat =
annotation == BindingAnnotation::Unannotated && subpat.is_none();
let (binding, pattern) = if is_simple_ident_pat {
// This could also be a single-segment path pattern. To
// decide that, we need to try resolving the name.
- let (resolved, _) = self.expander.def_map.resolve_path(
+ let (resolved, _) = self.def_map.resolve_path(
self.db,
- self.expander.module,
+ self.expander.module.local_id,
&name.clone().into(),
BuiltinShadowMode::Other,
+ None,
);
match resolved.take_values() {
Some(ModuleDefId::ConstId(_)) => (None, Pat::Path(name.into())),
@@ -887,11 +1212,15 @@ impl ExprCollector<'_> {
ast::Pat::TupleStructPat(p) => {
let path =
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
- let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
+ let (args, ellipsis) = self.collect_tuple_pat(
+ p.fields(),
+ comma_follows_token(p.l_paren_token()),
+ binding_list,
+ );
Pat::TupleStruct { path, args, ellipsis }
}
ast::Pat::RefPat(p) => {
- let pat = self.collect_pat_opt_(p.pat(), binding_list);
+ let pat = self.collect_pat_opt(p.pat(), binding_list);
let mutability = Mutability::from_mutable(p.mut_token().is_some());
Pat::Ref { pat, mutability }
}
@@ -900,13 +1229,42 @@ impl ExprCollector<'_> {
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
path.map(Pat::Path).unwrap_or(Pat::Missing)
}
- ast::Pat::OrPat(p) => {
- let pats = p.pats().map(|p| self.collect_pat_(p, binding_list)).collect();
- Pat::Or(pats)
+ ast::Pat::OrPat(p) => 'b: {
+ let prev_is_used = mem::take(&mut binding_list.is_used);
+ let prev_reject_new = mem::take(&mut binding_list.reject_new);
+ let mut pats = Vec::with_capacity(p.pats().count());
+ let mut it = p.pats();
+ let Some(first) = it.next() else {
+ break 'b Pat::Or(Box::new([]));
+ };
+ 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;
+ }
+ pats.push(self.collect_pat(rest, binding_list));
+ for (&id, &x) in binding_list.is_used.iter() {
+ if !x {
+ self.body.bindings[id].problems =
+ Some(BindingProblems::NotBoundAcrossAll);
+ }
+ }
+ }
+ binding_list.reject_new = prev_reject_new;
+ let current_is_used = mem::replace(&mut binding_list.is_used, prev_is_used);
+ for (id, _) in current_is_used.into_iter() {
+ binding_list.check_is_used(self, id);
+ }
+ Pat::Or(pats.into())
}
- ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat(), binding_list),
+ ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
ast::Pat::TuplePat(p) => {
- let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
+ let (args, ellipsis) = self.collect_tuple_pat(
+ p.fields(),
+ comma_follows_token(p.l_paren_token()),
+ binding_list,
+ );
Pat::Tuple { args, ellipsis }
}
ast::Pat::WildcardPat(_) => Pat::Wild,
@@ -919,7 +1277,7 @@ impl ExprCollector<'_> {
.fields()
.filter_map(|f| {
let ast_pat = f.pat()?;
- let pat = self.collect_pat_(ast_pat, binding_list);
+ let pat = self.collect_pat(ast_pat, binding_list);
let name = f.field_name()?.as_name();
Some(RecordFieldPat { name, pat })
})
@@ -938,26 +1296,18 @@ impl ExprCollector<'_> {
// FIXME properly handle `RestPat`
Pat::Slice {
- prefix: prefix
- .into_iter()
- .map(|p| self.collect_pat_(p, binding_list))
- .collect(),
- slice: slice.map(|p| self.collect_pat_(p, binding_list)),
- suffix: suffix
- .into_iter()
- .map(|p| self.collect_pat_(p, binding_list))
- .collect(),
+ prefix: prefix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
+ slice: slice.map(|p| self.collect_pat(p, binding_list)),
+ suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
}
}
- ast::Pat::LiteralPat(lit) => {
- if let Some(ast_lit) = lit.literal() {
- let expr = Expr::Literal(ast_lit.kind().into());
- let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
- let expr_id = self.alloc_expr(expr, expr_ptr);
- Pat::Lit(expr_id)
- } else {
- Pat::Missing
- }
+ #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676
+ ast::Pat::LiteralPat(lit) => 'b: {
+ let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { break 'b Pat::Missing };
+ let expr = Expr::Literal(hir_lit);
+ let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
+ let expr_id = self.alloc_expr(expr, expr_ptr);
+ Pat::Lit(expr_id)
}
ast::Pat::RestPat(_) => {
// `RestPat` requires special handling and should not be mapped
@@ -969,12 +1319,18 @@ impl ExprCollector<'_> {
Pat::Missing
}
ast::Pat::BoxPat(boxpat) => {
- let inner = self.collect_pat_opt_(boxpat.pat(), binding_list);
+ let inner = self.collect_pat_opt(boxpat.pat(), binding_list);
Pat::Box { inner }
}
ast::Pat::ConstBlockPat(const_block_pat) => {
- if let Some(expr) = const_block_pat.block_expr() {
- let expr_id = self.collect_block(expr);
+ if let Some(block) = const_block_pat.block_expr() {
+ let expr_id = self.with_label_rib(RibKind::Constant, |this| {
+ let syntax_ptr = AstPtr::new(&block.clone().into());
+ this.collect_as_a_binding_owner_bad(
+ |this| this.collect_block(block),
+ syntax_ptr,
+ )
+ });
Pat::ConstBlock(expr_id)
} else {
Pat::Missing
@@ -986,23 +1342,45 @@ impl ExprCollector<'_> {
let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
let pat =
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
- this.collect_pat_opt_(expanded_pat, binding_list)
+ this.collect_pat_opt(expanded_pat, binding_list)
});
self.source_map.pat_map.insert(src, pat);
return pat;
}
None => Pat::Missing,
},
- // FIXME: implement
- ast::Pat::RangePat(_) => Pat::Missing,
+ // 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)))
+ }
+ ast::Pat::IdentPat(p) => {
+ let name =
+ p.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
+ Some(Box::new(LiteralOrConst::Const(name.into())))
+ }
+ ast::Pat::PathPat(p) => p
+ .path()
+ .and_then(|path| self.expander.parse_path(self.db, path))
+ .map(LiteralOrConst::Const)
+ .map(Box::new),
+ _ => None,
+ })
+ };
+ let start = range_part_lower(p.start());
+ let end = range_part_lower(p.end());
+ Pat::Range { start, end }
+ }
};
let ptr = AstPtr::new(&pat);
self.alloc_pat(pattern, Either::Left(ptr))
}
- fn collect_pat_opt_(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
+ fn collect_pat_opt(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
match pat {
- Some(pat) => self.collect_pat_(pat, binding_list),
+ Some(pat) => self.collect_pat(pat, binding_list),
None => self.missing_pat(),
}
}
@@ -1010,20 +1388,28 @@ impl ExprCollector<'_> {
fn collect_tuple_pat(
&mut self,
args: AstChildren<ast::Pat>,
+ has_leading_comma: bool,
binding_list: &mut BindingList,
) -> (Box<[PatId]>, Option<usize>) {
// Find the location of the `..`, if there is one. Note that we do not
// consider the possibility of there being multiple `..` here.
let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
// We want to skip the `..` pattern here, since we account for it above.
- let args = args
+ let mut args: Vec<_> = args
.filter(|p| !matches!(p, ast::Pat::RestPat(_)))
- .map(|p| self.collect_pat_(p, binding_list))
+ .map(|p| self.collect_pat(p, binding_list))
.collect();
+ // if there is a leading comma, the user is most likely to type out a leading pattern
+ // so we insert a missing pattern at the beginning for IDE features
+ if has_leading_comma {
+ args.insert(0, self.missing_pat());
+ }
- (args, ellipsis)
+ (args.into_boxed_slice(), ellipsis)
}
+ // endregion: patterns
+
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
/// not.
fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> {
@@ -1051,42 +1437,150 @@ impl ExprCollector<'_> {
fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
self.body.bindings[binding_id].definitions.push(pat_id);
}
-}
-impl From<ast::LiteralKind> for Literal {
- fn from(ast_lit_kind: ast::LiteralKind) -> Self {
- match ast_lit_kind {
- // FIXME: these should have actual values filled in, but unsure on perf impact
- LiteralKind::IntNumber(lit) => {
- if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
- Literal::Float(
- FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
- builtin,
- )
- } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinInt::from_suffix) {
- Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
- } else {
- let builtin = lit.suffix().and_then(BuiltinUint::from_suffix);
- Literal::Uint(lit.value().unwrap_or(0), builtin)
+ // region: labels
+
+ fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
+ let label = Label {
+ name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
+ };
+ self.alloc_label(label, AstPtr::new(&ast_label))
+ }
+
+ fn resolve_label(
+ &self,
+ lifetime: Option<ast::Lifetime>,
+ ) -> Result<Option<LabelId>, BodyDiagnostic> {
+ 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() {
+ if let Some((label_name, id)) = &rib.label {
+ if *label_name == name {
+ return if self.is_label_valid_from_rib(rib_idx) {
+ Ok(Some(*id))
+ } else {
+ Err(BodyDiagnostic::UnreachableLabel {
+ name,
+ node: InFile::new(
+ self.expander.current_file_id,
+ AstPtr::new(&lifetime),
+ ),
+ })
+ };
}
}
- LiteralKind::FloatNumber(lit) => {
- let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
- Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
- }
- LiteralKind::ByteString(bs) => {
- let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
- Literal::ByteString(text)
- }
- LiteralKind::String(s) => {
- let text = s.value().map(Box::from).unwrap_or_else(Default::default);
- Literal::String(text)
- }
- LiteralKind::Byte(b) => {
- Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
- }
- LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
- LiteralKind::Bool(val) => Literal::Bool(val),
}
+
+ Err(BodyDiagnostic::UndeclaredLabel {
+ name,
+ node: InFile::new(self.expander.current_file_id, AstPtr::new(&lifetime)),
+ })
+ }
+
+ fn is_label_valid_from_rib(&self, rib_index: usize) -> bool {
+ !self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
+ }
+
+ fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
+ self.label_ribs.push(LabelRib::new(kind));
+ let res = f(self);
+ self.label_ribs.pop();
+ res
+ }
+
+ fn with_labeled_rib<T>(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T {
+ self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label)));
+ let res = f(self);
+ self.label_ribs.pop();
+ res
+ }
+
+ fn with_opt_labeled_rib<T>(
+ &mut self,
+ label: Option<LabelId>,
+ f: impl FnOnce(&mut Self) -> T,
+ ) -> T {
+ match label {
+ None => f(self),
+ Some(label) => self.with_labeled_rib(label, f),
+ }
+ }
+ // endregion: labels
+}
+
+fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> {
+ let ast_lit = lit.literal()?;
+ let mut hir_lit: Literal = ast_lit.kind().into();
+ if lit.minus_token().is_some() {
+ let Some(h) = hir_lit.negate() else {
+ return None;
+ };
+ hir_lit = h;
+ }
+ Some((hir_lit, ast_lit))
+}
+
+impl ExprCollector<'_> {
+ fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
+ let src = self.expander.to_source(ptr);
+ let id = self.body.exprs.alloc(expr);
+ self.source_map.expr_map_back.insert(id, src.clone());
+ self.source_map.expr_map.insert(src, id);
+ id
+ }
+ // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
+ fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
+ self.body.exprs.alloc(expr)
+ }
+ fn missing_expr(&mut self) -> ExprId {
+ self.alloc_expr_desugared(Expr::Missing)
+ }
+
+ fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
+ let binding = self.body.bindings.alloc(Binding {
+ name,
+ mode,
+ definitions: SmallVec::new(),
+ problems: None,
+ });
+ if let Some(owner) = self.current_binding_owner {
+ self.body.binding_owners.insert(binding, owner);
+ }
+ binding
+ }
+
+ fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
+ let src = self.expander.to_source(ptr);
+ let id = self.body.pats.alloc(pat);
+ self.source_map.pat_map_back.insert(id, src.clone());
+ self.source_map.pat_map.insert(src, id);
+ id
+ }
+ // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
+ fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
+ self.body.pats.alloc(pat)
+ }
+ fn missing_pat(&mut self) -> PatId {
+ self.body.pats.alloc(Pat::Missing)
+ }
+
+ fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
+ let src = self.expander.to_source(ptr);
+ let id = self.body.labels.alloc(label);
+ self.source_map.label_map_back.insert(id, src.clone());
+ self.source_map.label_map.insert(src, id);
+ id
+ }
+ // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
+ fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
+ self.body.labels.alloc(label)
}
}
+
+fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
+ (|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
+ .map_or(false, |it| it.kind() == syntax::T![,])
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
index 5a9b825a2..cd6df0e63 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
@@ -2,10 +2,14 @@
use std::fmt::{self, Write};
+use hir_expand::db::ExpandDatabase;
use syntax::ast::HasName;
use crate::{
- expr::{Array, BindingAnnotation, BindingId, ClosureKind, Literal, Movability, Statement},
+ hir::{
+ Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, LiteralOrConst,
+ Movability, Statement,
+ },
pretty::{print_generic_args, print_path, print_type_ref},
type_ref::TypeRef,
};
@@ -13,47 +17,71 @@ use crate::{
use super::*;
pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
- let needs_semi;
let header = match owner {
DefWithBodyId::FunctionId(it) => {
- needs_semi = false;
let item_tree_id = it.lookup(db).id;
- format!("fn {}(…) ", item_tree_id.item_tree(db)[item_tree_id.value].name)
+ format!(
+ "fn {}",
+ item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast())
+ )
}
DefWithBodyId::StaticId(it) => {
- needs_semi = true;
let item_tree_id = it.lookup(db).id;
- format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name)
+ format!(
+ "static {} = ",
+ item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast())
+ )
}
DefWithBodyId::ConstId(it) => {
- needs_semi = true;
let item_tree_id = it.lookup(db).id;
let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name {
- Some(name) => name.to_string(),
+ Some(name) => name.display(db.upcast()).to_string(),
None => "_".to_string(),
};
format!("const {name} = ")
}
+ DefWithBodyId::InTypeConstId(_) => format!("In type const = "),
DefWithBodyId::VariantId(it) => {
- needs_semi = false;
let src = it.parent.child_source(db);
let variant = &src.value[it.local_id];
- let name = match &variant.name() {
+ match &variant.name() {
Some(name) => name.to_string(),
None => "_".to_string(),
- };
- format!("{name}")
+ }
}
};
- let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };
+ let mut p =
+ Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
+ if let DefWithBodyId::FunctionId(it) = owner {
+ p.buf.push('(');
+ body.params.iter().zip(&db.function_data(it).params).for_each(|(&param, ty)| {
+ p.print_pat(param);
+ p.buf.push(':');
+ p.print_type_ref(ty);
+ });
+ p.buf.push(')');
+ p.buf.push(' ');
+ }
p.print_expr(body.body_expr);
- if needs_semi {
+ if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) {
p.buf.push(';');
}
p.buf
}
+pub(super) fn print_expr_hir(
+ db: &dyn DefDatabase,
+ body: &Body,
+ _owner: DefWithBodyId,
+ expr: ExprId,
+) -> String {
+ let mut p =
+ Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
+ p.print_expr(expr);
+ p.buf
+}
+
macro_rules! w {
($dst:expr, $($arg:tt)*) => {
{ let _ = write!($dst, $($arg)*); }
@@ -70,6 +98,7 @@ macro_rules! wln {
}
struct Printer<'a> {
+ db: &'a dyn ExpandDatabase,
body: &'a Body,
buf: String,
indent_level: usize,
@@ -144,29 +173,19 @@ impl<'a> Printer<'a> {
}
Expr::Loop { body, label } => {
if let Some(lbl) = label {
- w!(self, "{}: ", self.body[*lbl].name);
+ w!(self, "{}: ", self.body[*lbl].name.display(self.db));
}
w!(self, "loop ");
self.print_expr(*body);
}
Expr::While { condition, body, label } => {
if let Some(lbl) = label {
- w!(self, "{}: ", self.body[*lbl].name);
+ w!(self, "{}: ", self.body[*lbl].name.display(self.db));
}
w!(self, "while ");
self.print_expr(*condition);
self.print_expr(*body);
}
- Expr::For { iterable, pat, body, label } => {
- if let Some(lbl) = label {
- w!(self, "{}: ", self.body[*lbl].name);
- }
- w!(self, "for ");
- self.print_pat(*pat);
- w!(self, " in ");
- self.print_expr(*iterable);
- self.print_expr(*body);
- }
Expr::Call { callee, args, is_assignee_expr: _ } => {
self.print_expr(*callee);
w!(self, "(");
@@ -182,10 +201,10 @@ impl<'a> Printer<'a> {
}
Expr::MethodCall { receiver, method_name, args, generic_args } => {
self.print_expr(*receiver);
- w!(self, ".{}", method_name);
+ w!(self, ".{}", method_name.display(self.db));
if let Some(args) = generic_args {
w!(self, "::<");
- print_generic_args(args, self).unwrap();
+ print_generic_args(self.db, args, self).unwrap();
w!(self, ">");
}
w!(self, "(");
@@ -219,14 +238,14 @@ impl<'a> Printer<'a> {
}
Expr::Continue { label } => {
w!(self, "continue");
- if let Some(label) = label {
- w!(self, " {}", label);
+ if let Some(lbl) = label {
+ w!(self, " {}", self.body[*lbl].name.display(self.db));
}
}
Expr::Break { expr, label } => {
w!(self, "break");
- if let Some(label) = label {
- w!(self, " {}", label);
+ if let Some(lbl) = label {
+ w!(self, " {}", self.body[*lbl].name.display(self.db));
}
if let Some(expr) = expr {
self.whitespace();
@@ -265,7 +284,7 @@ impl<'a> Printer<'a> {
w!(self, "{{");
self.indented(|p| {
for field in &**fields {
- w!(p, "{}: ", field.name);
+ w!(p, "{}: ", field.name.display(self.db));
p.print_expr(field.expr);
wln!(p, ",");
}
@@ -282,16 +301,12 @@ impl<'a> Printer<'a> {
}
Expr::Field { expr, name } => {
self.print_expr(*expr);
- w!(self, ".{}", name);
+ w!(self, ".{}", name.display(self.db));
}
Expr::Await { expr } => {
self.print_expr(*expr);
w!(self, ".await");
}
- Expr::Try { expr } => {
- self.print_expr(*expr);
- w!(self, "?");
- }
Expr::Cast { expr, type_ref } => {
self.print_expr(*expr);
w!(self, " as ");
@@ -359,7 +374,7 @@ impl<'a> Printer<'a> {
self.print_expr(*index);
w!(self, "]");
}
- Expr::Closure { args, arg_types, ret_type, body, closure_kind } => {
+ Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => {
match closure_kind {
ClosureKind::Generator(Movability::Static) => {
w!(self, "static ");
@@ -369,6 +384,12 @@ impl<'a> Printer<'a> {
}
_ => (),
}
+ match capture_by {
+ CaptureBy::Value => {
+ w!(self, "move ");
+ }
+ CaptureBy::Ref => (),
+ }
w!(self, "|");
for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
if i != 0 {
@@ -418,20 +439,17 @@ impl<'a> Printer<'a> {
}
Expr::Literal(lit) => self.print_literal(lit),
Expr::Block { id: _, statements, tail, label } => {
- let label = label.map(|lbl| format!("{}: ", self.body[lbl].name));
+ let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db)));
self.print_block(label.as_deref(), statements, tail);
}
Expr::Unsafe { id: _, statements, tail } => {
self.print_block(Some("unsafe "), statements, tail);
}
- Expr::TryBlock { id: _, statements, tail } => {
- self.print_block(Some("try "), statements, tail);
- }
Expr::Async { id: _, statements, tail } => {
self.print_block(Some("async "), statements, tail);
}
- Expr::Const { id: _, statements, tail } => {
- self.print_block(Some("const "), statements, tail);
+ Expr::Const(id) => {
+ w!(self, "const {{ /* {id:?} */ }}");
}
}
}
@@ -439,7 +457,7 @@ impl<'a> Printer<'a> {
fn print_block(
&mut self,
label: Option<&str>,
- statements: &Box<[Statement]>,
+ statements: &[Statement],
tail: &Option<la_arena::Idx<Expr>>,
) {
self.whitespace();
@@ -449,7 +467,7 @@ impl<'a> Printer<'a> {
w!(self, "{{");
if !statements.is_empty() || tail.is_some() {
self.indented(|p| {
- for stmt in &**statements {
+ for stmt in statements {
p.print_stmt(stmt);
}
if let Some(tail) = tail {
@@ -497,7 +515,7 @@ impl<'a> Printer<'a> {
w!(self, " {{");
self.indented(|p| {
for arg in args.iter() {
- w!(p, "{}: ", arg.name);
+ w!(p, "{}: ", arg.name.display(self.db));
p.print_pat(arg.pat);
wln!(p, ",");
}
@@ -508,9 +526,13 @@ impl<'a> Printer<'a> {
w!(self, "}}");
}
Pat::Range { start, end } => {
- self.print_expr(*start);
- w!(self, "...");
- self.print_expr(*end);
+ if let Some(start) = start {
+ self.print_literal_or_const(start);
+ }
+ w!(self, "..=");
+ if let Some(end) = end {
+ self.print_literal_or_const(end);
+ }
}
Pat::Slice { prefix, slice, suffix } => {
w!(self, "[");
@@ -601,10 +623,18 @@ impl<'a> Printer<'a> {
}
}
+ fn print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst) {
+ match literal_or_const {
+ LiteralOrConst::Literal(l) => self.print_literal(l),
+ LiteralOrConst::Const(c) => self.print_path(c),
+ }
+ }
+
fn print_literal(&mut self, literal: &Literal) {
match literal {
Literal::String(it) => w!(self, "{:?}", it),
Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()),
+ Literal::CString(it) => w!(self, "\"{}\\0\"", it),
Literal::Char(it) => w!(self, "'{}'", it.escape_debug()),
Literal::Bool(it) => w!(self, "{}", it),
Literal::Int(i, suffix) => {
@@ -629,11 +659,11 @@ impl<'a> Printer<'a> {
}
fn print_type_ref(&mut self, ty: &TypeRef) {
- print_type_ref(ty, self).unwrap();
+ print_type_ref(self.db, ty, self).unwrap();
}
fn print_path(&mut self, path: &Path) {
- print_path(path, self).unwrap();
+ print_path(self.db, path, self).unwrap();
}
fn print_binding(&mut self, id: BindingId) {
@@ -644,6 +674,6 @@ impl<'a> Printer<'a> {
BindingAnnotation::Ref => "ref ",
BindingAnnotation::RefMut => "ref mut ",
};
- w!(self, "{}{}", mode, name);
+ w!(self, "{}{}", mode, name.display(self.db));
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
index 12fc1f116..69741c445 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
@@ -1,14 +1,13 @@
//! Name resolution for expressions.
-use std::sync::Arc;
-
use hir_expand::name::Name;
-use la_arena::{Arena, Idx};
+use la_arena::{Arena, Idx, IdxRange, RawIdx};
use rustc_hash::FxHashMap;
+use triomphe::Arc;
use crate::{
body::Body,
db::DefDatabase,
- expr::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
+ hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
BlockId, DefWithBodyId,
};
@@ -17,6 +16,7 @@ pub type ScopeId = Idx<ScopeData>;
#[derive(Debug, PartialEq, Eq)]
pub struct ExprScopes {
scopes: Arena<ScopeData>,
+ scope_entries: Arena<ScopeEntry>,
scope_by_expr: FxHashMap<ExprId, ScopeId>,
}
@@ -41,7 +41,7 @@ pub struct ScopeData {
parent: Option<ScopeId>,
block: Option<BlockId>,
label: Option<(LabelId, Name)>,
- entries: Vec<ScopeEntry>,
+ entries: IdxRange<ScopeEntry>,
}
impl ExprScopes {
@@ -53,7 +53,7 @@ impl ExprScopes {
}
pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
- &self.scopes[scope].entries
+ &self.scope_entries[self.scopes[scope].entries.clone()]
}
/// If `scope` refers to a block expression scope, returns the corresponding `BlockId`.
@@ -85,10 +85,17 @@ impl ExprScopes {
}
}
+fn empty_entries(idx: usize) -> IdxRange<ScopeEntry> {
+ IdxRange::new(Idx::from_raw(RawIdx::from(idx as u32))..Idx::from_raw(RawIdx::from(idx as u32)))
+}
+
impl ExprScopes {
fn new(body: &Body) -> ExprScopes {
- let mut scopes =
- ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
+ let mut scopes = ExprScopes {
+ scopes: Arena::default(),
+ scope_entries: Arena::default(),
+ scope_by_expr: FxHashMap::default(),
+ };
let mut root = scopes.root_scope();
scopes.add_params_bindings(body, root, &body.params);
compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
@@ -96,7 +103,12 @@ impl ExprScopes {
}
fn root_scope(&mut self) -> ScopeId {
- self.scopes.alloc(ScopeData { parent: None, block: None, label: None, entries: vec![] })
+ self.scopes.alloc(ScopeData {
+ parent: None,
+ block: None,
+ label: None,
+ entries: empty_entries(self.scope_entries.len()),
+ })
}
fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
@@ -104,32 +116,38 @@ impl ExprScopes {
parent: Some(parent),
block: None,
label: None,
- entries: vec![],
+ entries: empty_entries(self.scope_entries.len()),
})
}
fn new_labeled_scope(&mut self, parent: ScopeId, label: Option<(LabelId, Name)>) -> ScopeId {
- self.scopes.alloc(ScopeData { parent: Some(parent), block: None, label, entries: vec![] })
+ self.scopes.alloc(ScopeData {
+ parent: Some(parent),
+ block: None,
+ label,
+ entries: empty_entries(self.scope_entries.len()),
+ })
}
fn new_block_scope(
&mut self,
parent: ScopeId,
- block: BlockId,
+ block: Option<BlockId>,
label: Option<(LabelId, Name)>,
) -> ScopeId {
self.scopes.alloc(ScopeData {
parent: Some(parent),
- block: Some(block),
+ block,
label,
- entries: vec![],
+ entries: empty_entries(self.scope_entries.len()),
})
}
fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) {
let Binding { name, .. } = &body.bindings[binding];
- let entry = ScopeEntry { name: name.clone(), binding };
- self.scopes[scope].entries.push(entry);
+ let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding });
+ self.scopes[scope].entries =
+ IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
}
fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
@@ -150,9 +168,9 @@ impl ExprScopes {
}
fn shrink_to_fit(&mut self) {
- let ExprScopes { scopes, scope_by_expr } = self;
+ let ExprScopes { scopes, scope_entries, scope_by_expr } = self;
scopes.shrink_to_fit();
- scopes.values_mut().for_each(|it| it.entries.shrink_to_fit());
+ scope_entries.shrink_to_fit();
scope_by_expr.shrink_to_fit();
}
}
@@ -200,22 +218,16 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
scopes.set_scope(expr, scope);
compute_block_scopes(statements, *tail, body, scopes, &mut scope);
}
- Expr::Unsafe { id, statements, tail }
- | Expr::Async { id, statements, tail }
- | Expr::Const { id, statements, tail }
- | Expr::TryBlock { id, statements, tail } => {
+ Expr::Const(_) => {
+ // FIXME: This is broken.
+ }
+ Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => {
let mut scope = scopes.new_block_scope(*scope, *id, None);
// Overwrite the old scope for the block expr, so that every block scope can be found
// via the block itself (important for blocks that only contain items, no expressions).
scopes.set_scope(expr, scope);
compute_block_scopes(statements, *tail, body, scopes, &mut scope);
}
- Expr::For { iterable, pat, body: body_expr, label } => {
- compute_expr_scopes(*iterable, body, scopes, scope);
- let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
- scopes.add_pat_bindings(body, scope, *pat);
- compute_expr_scopes(*body_expr, body, scopes, &mut scope);
- }
Expr::While { condition, body: body_expr, label } => {
let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
compute_expr_scopes(*condition, body, scopes, &mut scope);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
index 77ac221e5..6e77744f2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
@@ -148,8 +148,8 @@ fn f() {
}
"#,
expect![[r#"
- BlockId(1) in ModuleId { krate: CrateId(0), block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) }
- BlockId(0) in ModuleId { krate: CrateId(0), block: None, local_id: Idx::<ModuleData>(0) }
+ BlockId(1) in BlockRelativeModuleId { block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) }
+ BlockId(0) in BlockRelativeModuleId { block: None, local_id: Idx::<ModuleData>(0) }
crate scope
"#]],
);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs
index dd69c3ab4..61b248197 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs
@@ -106,8 +106,14 @@ impl AsName for BuiltinType {
impl fmt::Display for BuiltinType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let type_name = self.as_name();
- type_name.fmt(f)
+ match self {
+ BuiltinType::Char => f.write_str("char"),
+ BuiltinType::Bool => f.write_str("bool"),
+ BuiltinType::Str => f.write_str("str"),
+ BuiltinType::Int(it) => it.fmt(f),
+ BuiltinType::Uint(it) => it.fmt(f),
+ BuiltinType::Float(it) => it.fmt(f),
+ }
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
index 68b57acca..bb79e28f2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
@@ -10,9 +10,9 @@ use syntax::ast::HasDocComments;
use crate::{
db::DefDatabase,
- dyn_map::DynMap,
+ dyn_map::{keys, DynMap},
item_scope::ItemScope,
- keys,
+ nameres::DefMap,
src::{HasChildSource, HasSource},
AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId,
ModuleDefId, ModuleId, TraitId, VariantId,
@@ -206,7 +206,7 @@ impl ChildBySource for DefWithBodyId {
for (_, def_map) in body.blocks(db) {
// All block expressions are merged into the same map, because they logically all add
// inner items to the containing `DefWithBodyId`.
- def_map[def_map.root()].scope.child_by_source_to(db, res, file_id);
+ def_map[DefMap::ROOT].scope.child_by_source_to(db, res, file_id);
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index 1633a33be..40e6a4308 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -1,22 +1,28 @@
//! Contains basic data about various HIR declarations.
-use std::sync::Arc;
+pub mod adt;
-use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
+use hir_expand::{
+ name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind,
+};
use intern::Interned;
use smallvec::SmallVec;
-use syntax::ast;
+use syntax::{ast, Parse};
+use triomphe::Arc;
use crate::{
attr::Attrs,
- body::{Expander, Mark},
db::DefDatabase,
- item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId},
+ expander::{Expander, Mark},
+ item_tree::{
+ self, AssocItem, FnFlags, ItemTree, ItemTreeId, MacroCall, ModItem, Param, TreeId,
+ },
+ macro_call_as_call_id, macro_id_to_def_id,
nameres::{
attr_resolution::ResolvedAttr,
diagnostics::DefDiagnostic,
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind},
- DefMap,
+ DefMap, MacroSubNs,
},
type_ref::{TraitRef, TypeBound, TypeRef},
visibility::RawVisibility,
@@ -28,9 +34,8 @@ use crate::{
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunctionData {
pub name: Name,
- pub params: Vec<(Option<Name>, Interned<TypeRef>)>,
+ pub params: Vec<Interned<TypeRef>>,
pub ret_type: Interned<TypeRef>,
- pub async_ret_type: Option<Interned<TypeRef>>,
pub attrs: Attrs,
pub visibility: RawVisibility,
pub abi: Option<Interned<str>>,
@@ -43,16 +48,16 @@ impl FunctionData {
pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> {
let loc = func.lookup(db);
let krate = loc.container.module(db).krate;
- let crate_graph = db.crate_graph();
- let cfg_options = &crate_graph[krate].cfg_options;
let item_tree = loc.id.item_tree(db);
let func = &item_tree[loc.id.value];
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
- db.trait_data(trait_id).visibility.clone()
+ trait_vis(db, trait_id)
} else {
item_tree[func.visibility].clone()
};
+ let crate_graph = db.crate_graph();
+ let cfg_options = &crate_graph[krate].cfg_options;
let enabled_params = func
.params
.clone()
@@ -99,12 +104,11 @@ impl FunctionData {
params: enabled_params
.clone()
.filter_map(|id| match &item_tree[id] {
- Param::Normal(name, ty) => Some((name.clone(), ty.clone())),
+ Param::Normal(ty) => Some(ty.clone()),
Param::Varargs => None,
})
.collect(),
ret_type: func.ret_type.clone(),
- async_ret_type: func.async_ret_type.clone(),
attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
visibility,
abi: func.abi.clone(),
@@ -188,7 +192,7 @@ impl TypeAliasData {
let item_tree = loc.id.item_tree(db);
let typ = &item_tree[loc.id.value];
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
- db.trait_data(trait_id).visibility.clone()
+ trait_vis(db, trait_id)
} else {
item_tree[typ.visibility].clone()
};
@@ -471,7 +475,7 @@ impl ConstData {
let item_tree = loc.id.item_tree(db);
let konst = &item_tree[loc.id.value];
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
- db.trait_data(trait_id).visibility.clone()
+ trait_vis(db, trait_id)
} else {
item_tree[konst.visibility].clone()
};
@@ -519,7 +523,7 @@ struct AssocItemCollector<'a> {
db: &'a dyn DefDatabase,
module_id: ModuleId,
def_map: Arc<DefMap>,
- inactive_diagnostics: Vec<DefDiagnostic>,
+ diagnostics: Vec<DefDiagnostic>,
container: ItemContainerId,
expander: Expander,
@@ -542,7 +546,7 @@ impl<'a> AssocItemCollector<'a> {
expander: Expander::new(db, file_id, module_id),
items: Vec::new(),
attr_calls: Vec::new(),
- inactive_diagnostics: Vec::new(),
+ diagnostics: Vec::new(),
}
}
@@ -556,11 +560,10 @@ impl<'a> AssocItemCollector<'a> {
(
self.items,
if self.attr_calls.is_empty() { None } else { Some(Box::new(self.attr_calls)) },
- self.inactive_diagnostics,
+ self.diagnostics,
)
}
- // FIXME: proc-macro diagnostics
fn collect(&mut self, item_tree: &ItemTree, tree_id: TreeId, assoc_items: &[AssocItem]) {
let container = self.container;
self.items.reserve(assoc_items.len());
@@ -568,7 +571,7 @@ impl<'a> AssocItemCollector<'a> {
'items: for &item in assoc_items {
let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into());
if !attrs.is_cfg_enabled(self.expander.cfg_options()) {
- self.inactive_diagnostics.push(DefDiagnostic::unconfigured_code(
+ self.diagnostics.push(DefDiagnostic::unconfigured_code(
self.module_id.local_id,
InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()),
attrs.cfg().unwrap(),
@@ -582,84 +585,164 @@ impl<'a> AssocItemCollector<'a> {
AstId::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast());
let ast_id_with_path = AstIdWithPath { path: (*attr.path).clone(), ast_id };
- if let Ok(ResolvedAttr::Macro(call_id)) = self.def_map.resolve_attr_macro(
+ match self.def_map.resolve_attr_macro(
self.db,
self.module_id.local_id,
ast_id_with_path,
attr,
) {
- self.attr_calls.push((ast_id, call_id));
- // If proc attribute macro expansion is disabled, skip expanding it here
- if !self.db.enable_proc_attr_macros() {
- continue 'attrs;
- }
- let loc = self.db.lookup_intern_macro_call(call_id);
- if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
- // If there's no expander for the proc macro (e.g. the
- // proc macro is ignored, or building the proc macro
- // crate failed), skip expansion like we would if it was
- // disabled. This is analogous to the handling in
- // `DefCollector::collect_macros`.
- if exp.is_dummy() {
+ Ok(ResolvedAttr::Macro(call_id)) => {
+ self.attr_calls.push((ast_id, call_id));
+ // If proc attribute macro expansion is disabled, skip expanding it here
+ if !self.db.expand_proc_attr_macros() {
continue 'attrs;
}
- }
- match self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id) {
- ExpandResult { value: Some((mark, _)), .. } => {
- self.collect_macro_items(mark);
- continue 'items;
+ let loc = self.db.lookup_intern_macro_call(call_id);
+ if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
+ // If there's no expander for the proc macro (e.g. the
+ // proc macro is ignored, or building the proc macro
+ // crate failed), skip expansion like we would if it was
+ // disabled. This is analogous to the handling in
+ // `DefCollector::collect_macros`.
+ if exp.is_dummy() {
+ continue 'attrs;
+ }
}
- ExpandResult { .. } => {}
+
+ let res =
+ self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
+ self.collect_macro_items(res, &|| loc.kind.clone());
+ continue 'items;
+ }
+ Ok(_) => (),
+ Err(_) => {
+ self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
+ self.module_id.local_id,
+ MacroCallKind::Attr {
+ ast_id,
+ attr_args: Arc::new((tt::Subtree::empty(), Default::default())),
+ invoc_attr_index: attr.id,
+ },
+ attr.path().clone(),
+ ));
}
}
}
- match item {
- AssocItem::Function(id) => {
- let item = &item_tree[id];
+ self.collect_item(item_tree, tree_id, container, item);
+ }
+ }
- let def =
- FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
- self.items.push((item.name.clone(), def.into()));
- }
- AssocItem::Const(id) => {
- let item = &item_tree[id];
-
- let name = match item.name.clone() {
- Some(name) => name,
- None => continue,
- };
- let def =
- ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
- self.items.push((name, def.into()));
- }
- AssocItem::TypeAlias(id) => {
- let item = &item_tree[id];
+ fn collect_item(
+ &mut self,
+ item_tree: &ItemTree,
+ tree_id: TreeId,
+ container: ItemContainerId,
+ item: AssocItem,
+ ) {
+ match item {
+ AssocItem::Function(id) => {
+ let item = &item_tree[id];
- let def = TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }
- .intern(self.db);
- self.items.push((item.name.clone(), def.into()));
- }
- AssocItem::MacroCall(call) => {
- if let Some(root) = self.db.parse_or_expand(self.expander.current_file_id()) {
- let call = &item_tree[call];
-
- let ast_id_map = self.db.ast_id_map(self.expander.current_file_id());
- let call = ast_id_map.get(call.ast_id).to_node(&root);
- let _cx =
- stdx::panic_context::enter(format!("collect_items MacroCall: {call}"));
- let res = self.expander.enter_expand::<ast::MacroItems>(self.db, call);
-
- if let Ok(ExpandResult { value: Some((mark, _)), .. }) = res {
- self.collect_macro_items(mark);
- }
+ let def =
+ FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
+ self.items.push((item.name.clone(), def.into()));
+ }
+ AssocItem::Const(id) => {
+ let item = &item_tree[id];
+ let Some(name) = item.name.clone() else { return };
+ let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
+ self.items.push((name, def.into()));
+ }
+ AssocItem::TypeAlias(id) => {
+ let item = &item_tree[id];
+
+ let def =
+ TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
+ self.items.push((item.name.clone(), def.into()));
+ }
+ AssocItem::MacroCall(call) => {
+ let file_id = self.expander.current_file_id();
+ let MacroCall { ast_id, expand_to, ref path } = item_tree[call];
+ let module = self.expander.module.local_id;
+
+ let resolver = |path| {
+ self.def_map
+ .resolve_path(
+ self.db,
+ module,
+ &path,
+ crate::item_scope::BuiltinShadowMode::Other,
+ Some(MacroSubNs::Bang),
+ )
+ .0
+ .take_macros()
+ .map(|it| macro_id_to_def_id(self.db, it))
+ };
+ match macro_call_as_call_id(
+ self.db.upcast(),
+ &AstIdWithPath::new(file_id, ast_id, Clone::clone(path)),
+ expand_to,
+ self.expander.module.krate(),
+ resolver,
+ ) {
+ Ok(Some(call_id)) => {
+ let res =
+ self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
+ self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike {
+ ast_id: InFile::new(file_id, ast_id),
+ expand_to: hir_expand::ExpandTo::Items,
+ });
+ }
+ Ok(None) => (),
+ Err(_) => {
+ self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
+ self.module_id.local_id,
+ MacroCallKind::FnLike {
+ ast_id: InFile::new(file_id, ast_id),
+ expand_to,
+ },
+ Clone::clone(path),
+ ));
}
}
}
}
}
- fn collect_macro_items(&mut self, mark: Mark) {
+ fn collect_macro_items(
+ &mut self,
+ ExpandResult { value, err }: ExpandResult<Option<(Mark, Parse<ast::MacroItems>)>>,
+ error_call_kind: &dyn Fn() -> hir_expand::MacroCallKind,
+ ) {
+ let Some((mark, parse)) = value else { return };
+
+ if let Some(err) = err {
+ let diag = match err {
+ // why is this reported here?
+ hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
+ DefDiagnostic::unresolved_proc_macro(
+ self.module_id.local_id,
+ error_call_kind(),
+ krate,
+ )
+ }
+ _ => DefDiagnostic::macro_error(
+ self.module_id.local_id,
+ error_call_kind(),
+ err.to_string(),
+ ),
+ };
+ self.diagnostics.push(diag);
+ }
+ if let errors @ [_, ..] = parse.errors() {
+ self.diagnostics.push(DefDiagnostic::macro_expansion_parse_error(
+ self.module_id.local_id,
+ error_call_kind(),
+ errors.into(),
+ ));
+ }
+
let tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None);
let item_tree = tree_id.item_tree(self.db);
let iter: SmallVec<[_; 2]> =
@@ -670,3 +753,10 @@ impl<'a> AssocItemCollector<'a> {
self.expander.exit(self.db, mark);
}
}
+
+fn trait_vis(db: &dyn DefDatabase, trait_id: TraitId) -> RawVisibility {
+ let ItemLoc { id: tree_id, .. } = trait_id.lookup(db);
+ let item_tree = tree_id.item_tree(db);
+ let tr_def = &item_tree[tree_id.value];
+ item_tree[tr_def.visibility].clone()
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
index b336f59ff..6db5abccc 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
@@ -1,8 +1,7 @@
//! Defines hir-level representation of structs, enums and unions
-use std::sync::Arc;
-
use base_db::CrateId;
+use bitflags::bitflags;
use cfg::CfgOptions;
use either::Either;
@@ -12,15 +11,17 @@ use hir_expand::{
};
use intern::Interned;
use la_arena::{Arena, ArenaMap};
-use rustc_abi::{Integer, IntegerType};
+use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
use syntax::ast::{self, HasName, HasVisibility};
+use triomphe::Arc;
use crate::{
- body::{CfgExpander, LowerCtx},
builtin_type::{BuiltinInt, BuiltinUint},
db::DefDatabase,
+ expander::CfgExpander,
item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
- layout::{Align, ReprFlags, ReprOptions},
+ lang_item::LangItem,
+ lower::LowerCtx,
nameres::diagnostics::DefDiagnostic,
src::HasChildSource,
src::HasSource,
@@ -39,8 +40,27 @@ pub struct StructData {
pub variant_data: Arc<VariantData>,
pub repr: Option<ReprOptions>,
pub visibility: RawVisibility,
- pub rustc_has_incoherent_inherent_impls: bool,
- pub fundamental: bool,
+ pub flags: StructFlags,
+}
+
+bitflags! {
+#[derive(Debug, Clone, PartialEq, Eq)]
+ pub struct StructFlags: u8 {
+ const NO_FLAGS = 0;
+ /// Indicates whether the struct is `PhantomData`.
+ const IS_PHANTOM_DATA = 1 << 2;
+ /// Indicates whether the struct has a `#[fundamental]` attribute.
+ const IS_FUNDAMENTAL = 1 << 3;
+ // FIXME: should this be a flag?
+ /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute.
+ const IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL = 1 << 4;
+ /// Indicates whether this struct is `Box`.
+ const IS_BOX = 1 << 5;
+ /// Indicates whether this struct is `ManuallyDrop`.
+ const IS_MANUALLY_DROP = 1 << 6;
+ /// Indicates whether this struct is `UnsafeCell`.
+ const IS_UNSAFE_CELL = 1 << 7;
+ }
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -174,10 +194,25 @@ impl StructData {
let item_tree = loc.id.item_tree(db);
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
+
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
- let rustc_has_incoherent_inherent_impls =
- attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
- let fundamental = attrs.by_key("fundamental").exists();
+
+ let mut flags = StructFlags::NO_FLAGS;
+ if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
+ flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
+ }
+ if attrs.by_key("fundamental").exists() {
+ flags |= StructFlags::IS_FUNDAMENTAL;
+ }
+ if let Some(lang) = attrs.lang_item() {
+ match lang {
+ LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA,
+ LangItem::OwnedBox => flags |= StructFlags::IS_BOX,
+ LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP,
+ LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL,
+ _ => (),
+ }
+ }
let strukt = &item_tree[loc.id.value];
let (variant_data, diagnostics) = lower_fields(
@@ -196,8 +231,7 @@ impl StructData {
variant_data: Arc::new(variant_data),
repr,
visibility: item_tree[strukt.visibility].clone(),
- rustc_has_incoherent_inherent_impls,
- fundamental,
+ flags,
}),
diagnostics.into(),
)
@@ -218,9 +252,13 @@ impl StructData {
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
- let rustc_has_incoherent_inherent_impls =
- attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
- let fundamental = attrs.by_key("fundamental").exists();
+ let mut flags = StructFlags::NO_FLAGS;
+ if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
+ flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
+ }
+ if attrs.by_key("fundamental").exists() {
+ flags |= StructFlags::IS_FUNDAMENTAL;
+ }
let union = &item_tree[loc.id.value];
let (variant_data, diagnostics) = lower_fields(
@@ -239,8 +277,7 @@ impl StructData {
variant_data: Arc::new(variant_data),
repr,
visibility: item_tree[union.visibility].clone(),
- rustc_has_incoherent_inherent_impls,
- fundamental,
+ flags,
}),
diagnostics.into(),
)
@@ -436,7 +473,7 @@ fn lower_struct(
trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
ast: &InFile<ast::StructKind>,
) -> StructKind {
- let ctx = LowerCtx::new(db, ast.file_id);
+ let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id);
match &ast.value {
ast::StructKind::Tuple(fl) => {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
index 9371fc14d..04ec47f84 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -1,18 +1,17 @@
//! Defines database & queries for name resolution.
-use std::sync::Arc;
-
use base_db::{salsa, CrateId, SourceDatabase, Upcast};
use either::Either;
use hir_expand::{db::ExpandDatabase, HirFileId};
use intern::Interned;
use la_arena::ArenaMap;
use syntax::{ast, AstPtr};
+use triomphe::Arc;
use crate::{
- adt::{EnumData, StructData},
attr::{Attrs, AttrsWithOwner},
body::{scope::ExprScopes, Body, BodySourceMap},
data::{
+ adt::{EnumData, StructData},
ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData,
TraitAliasData, TraitData, TypeAliasData,
},
@@ -22,15 +21,17 @@ use crate::{
lang_item::{LangItem, LangItemTarget, LangItems},
nameres::{diagnostics::DefDiagnostic, DefMap},
visibility::{self, Visibility},
- AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId,
- ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId,
- LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc,
- StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc,
- TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
+ 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,
};
#[salsa::query_group(InternDatabaseStorage)]
pub trait InternDatabase: SourceDatabase {
+ // region: items
#[salsa::interned]
fn intern_function(&self, loc: FunctionLoc) -> FunctionId;
#[salsa::interned]
@@ -54,19 +55,25 @@ pub trait InternDatabase: SourceDatabase {
#[salsa::interned]
fn intern_extern_block(&self, loc: ExternBlockLoc) -> ExternBlockId;
#[salsa::interned]
- fn intern_block(&self, loc: BlockLoc) -> BlockId;
- #[salsa::interned]
fn intern_macro2(&self, loc: Macro2Loc) -> Macro2Id;
#[salsa::interned]
fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId;
#[salsa::interned]
fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
+ // endregion: items
+
+ #[salsa::interned]
+ fn intern_block(&self, loc: BlockLoc) -> BlockId;
+ #[salsa::interned]
+ fn intern_anonymous_const(&self, id: ConstBlockLoc) -> ConstBlockId;
+ #[salsa::interned]
+ fn intern_in_type_const(&self, id: InTypeConstLoc) -> InTypeConstId;
}
#[salsa::query_group(DefDatabaseStorage)]
pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDatabase> {
#[salsa::input]
- fn enable_proc_attr_macros(&self) -> bool;
+ fn expand_proc_attr_macros(&self) -> bool;
#[salsa::invoke(ItemTree::file_item_tree_query)]
fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
@@ -94,7 +101,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
/// The `block_def_map` for block 0 would return `None`, while `block_def_map` of block 1 would
/// return a `DefMap` containing `inner`.
#[salsa::invoke(DefMap::block_def_map_query)]
- fn block_def_map(&self, block: BlockId) -> Option<Arc<DefMap>>;
+ fn block_def_map(&self, block: BlockId) -> Arc<DefMap>;
+
+ // region:data
#[salsa::invoke(StructData::struct_data_query)]
fn struct_data(&self, id: StructId) -> Arc<StructData>;
@@ -151,6 +160,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(ProcMacroData::proc_macro_data_query)]
fn proc_macro_data(&self, makro: ProcMacroId) -> Arc<ProcMacroData>;
+ // endregion:data
+
#[salsa::invoke(Body::body_with_source_map_query)]
fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>);
@@ -163,6 +174,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(GenericParams::generic_params_query)]
fn generic_params(&self, def: GenericDefId) -> Interned<GenericParams>;
+ // region:attrs
+
#[salsa::invoke(Attrs::variants_attrs_query)]
fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>;
@@ -182,10 +195,13 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>>;
#[salsa::invoke(AttrsWithOwner::attrs_query)]
- fn attrs(&self, def: AttrDefId) -> AttrsWithOwner;
+ fn attrs(&self, def: AttrDefId) -> Attrs;
- #[salsa::invoke(LangItems::crate_lang_items_query)]
- fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>;
+ #[salsa::transparent]
+ #[salsa::invoke(AttrsWithOwner::attrs_with_owner)]
+ fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner;
+
+ // endregion:attrs
#[salsa::invoke(LangItems::lang_item_query)]
fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option<LangItemTarget>;
@@ -193,6 +209,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(ImportMap::import_map_query)]
fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
+ // region:visibilities
+
#[salsa::invoke(visibility::field_visibilities_query)]
fn field_visibilities(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Visibility>>;
@@ -203,9 +221,17 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(visibility::const_visibility_query)]
fn const_visibility(&self, def: ConstId) -> Visibility;
+ // endregion:visibilities
+
+ #[salsa::invoke(LangItems::crate_lang_items_query)]
+ fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>;
+
#[salsa::transparent]
fn crate_limits(&self, crate_id: CrateId) -> CrateLimits;
+ #[salsa::transparent]
+ fn recursion_limit(&self, crate_id: CrateId) -> u32;
+
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
}
@@ -228,6 +254,10 @@ fn crate_limits(db: &dyn DefDatabase, crate_id: CrateId) -> CrateLimits {
}
}
+fn recursion_limit(db: &dyn DefDatabase, crate_id: CrateId) -> u32 {
+ db.crate_limits(crate_id).recursion_limit
+}
+
fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
let file = db.crate_graph()[crate_id].root_file_id;
let item_tree = db.file_item_tree(file.into());
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
index 166aa04da..63138aa6a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
@@ -21,6 +21,8 @@
//!
//! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
//! a coincidence.
+pub mod keys;
+
use std::{
hash::Hash,
marker::PhantomData,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/keys.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs
index f30be6b64..f30be6b64 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/keys.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
new file mode 100644
index 000000000..a588827c8
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
@@ -0,0 +1,211 @@
+//! Macro expansion utilities.
+
+use base_db::CrateId;
+use cfg::CfgOptions;
+use drop_bomb::DropBomb;
+use hir_expand::{
+ attrs::RawAttrs, hygiene::Hygiene, mod_path::ModPath, ExpandError, ExpandResult, HirFileId,
+ InFile, MacroCallId, UnresolvedMacro,
+};
+use limit::Limit;
+use syntax::{ast, Parse, SyntaxNode};
+
+use crate::{
+ attr::Attrs, db::DefDatabase, lower::LowerCtx, macro_id_to_def_id, path::Path, AsMacroCall,
+ 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 {
+ 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.
+ recursion_depth: u32,
+ 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 }
+ }
+
+ pub fn enter_expand<T: ast::AstNode>(
+ &mut self,
+ db: &dyn DefDatabase,
+ macro_call: ast::MacroCall,
+ resolver: impl Fn(ModPath) -> Option<MacroId>,
+ ) -> Result<ExpandResult<Option<(Mark, Parse<T>)>>, UnresolvedMacro> {
+ // FIXME: within_limit should support this, instead of us having to extract the error
+ let mut unresolved_macro_err = None;
+
+ let result = self.within_limit(db, |this| {
+ let macro_call = InFile::new(this.current_file_id, &macro_call);
+ match macro_call.as_call_id_with_errors(db.upcast(), this.module.krate(), |path| {
+ resolver(path).map(|it| macro_id_to_def_id(db, it))
+ }) {
+ Ok(call_id) => call_id,
+ Err(resolve_err) => {
+ unresolved_macro_err = Some(resolve_err);
+ ExpandResult { value: None, err: None }
+ }
+ }
+ });
+
+ if let Some(err) = unresolved_macro_err {
+ Err(err)
+ } else {
+ Ok(result)
+ }
+ }
+
+ pub fn enter_expand_id<T: ast::AstNode>(
+ &mut self,
+ db: &dyn DefDatabase,
+ call_id: MacroCallId,
+ ) -> ExpandResult<Option<(Mark, Parse<T>)>> {
+ self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
+ }
+
+ fn enter_expand_inner(
+ db: &dyn DefDatabase,
+ call_id: MacroCallId,
+ error: Option<ExpandError>,
+ ) -> ExpandResult<Option<InFile<Parse<SyntaxNode>>>> {
+ let macro_file = call_id.as_macro_file();
+ let ExpandResult { value, err } = db.parse_macro_expansion(macro_file);
+
+ ExpandResult { value: Some(InFile::new(macro_file.into(), value.0)), err: error.or(err) }
+ }
+
+ pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
+ self.cfg_expander.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
+ // depth only when we get out of the tree.
+ if !self.current_file_id.is_macro() {
+ self.recursion_depth = 0;
+ }
+ } else {
+ self.recursion_depth -= 1;
+ }
+ mark.bomb.defuse();
+ }
+
+ pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> {
+ LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id)
+ }
+
+ pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
+ InFile { file_id: self.current_file_id, value }
+ }
+
+ pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
+ self.cfg_expander.parse_attrs(db, owner)
+ }
+
+ pub(crate) fn cfg_options(&self) -> &CfgOptions {
+ &self.cfg_expander.cfg_options
+ }
+
+ pub fn current_file_id(&self) -> HirFileId {
+ self.current_file_id
+ }
+
+ 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);
+ Path::from_src(path, &ctx)
+ }
+
+ fn within_limit<F, T: ast::AstNode>(
+ &mut self,
+ db: &dyn DefDatabase,
+ op: F,
+ ) -> ExpandResult<Option<(Mark, Parse<T>)>>
+ where
+ F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
+ {
+ if self.recursion_depth == u32::MAX {
+ // Recursion limit has been reached somewhere in the macro expansion tree. We should
+ // stop expanding other macro calls in this tree, or else this may result in
+ // exponential number of macro expansions, leading to a hang.
+ //
+ // The overflow error should have been reported when it occurred (see the next branch),
+ // so don't return overflow error here to avoid diagnostics duplication.
+ cov_mark::hit!(overflow_but_not_me);
+ return ExpandResult::only_err(ExpandError::RecursionOverflowPoisoned);
+ } else if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() {
+ self.recursion_depth = u32::MAX;
+ cov_mark::hit!(your_stack_belongs_to_me);
+ return ExpandResult::only_err(ExpandError::other(
+ "reached recursion limit during macro expansion",
+ ));
+ }
+
+ let ExpandResult { value, err } = op(self);
+ let Some(call_id) = value else {
+ return ExpandResult { value: None, err };
+ };
+
+ Self::enter_expand_inner(db, call_id, err).map(|value| {
+ value.and_then(|InFile { file_id, value }| {
+ let parse = value.cast::<T>()?;
+
+ self.recursion_depth += 1;
+ self.cfg_expander.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") };
+ Some((mark, parse))
+ })
+ })
+ }
+}
+
+#[derive(Debug)]
+pub struct Mark {
+ file_id: HirFileId,
+ bomb: DropBomb,
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index 3f4392320..8c49ae1c4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -42,7 +42,7 @@ const MAX_PATH_LEN: usize = 15;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PrefixKind {
/// Causes paths to always start with either `self`, `super`, `crate` or a crate-name.
- /// This is the same as plain, just that paths will start with `self` iprepended f the path
+ /// This is the same as plain, just that paths will start with `self` prepended if the path
/// starts with an identifier that is not a crate.
BySelf,
/// Causes paths to ignore imports in the local module.
@@ -81,7 +81,7 @@ fn find_path_inner(
}
let def_map = from.def_map(db);
- let crate_root = def_map.crate_root(db);
+ let crate_root = def_map.crate_root().into();
// - if the item is a module, jump straight to module search
if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
let mut visited_modules = FxHashSet::default();
@@ -183,7 +183,7 @@ fn find_path_for_module(
// - if the item is the crate root of a dependency crate, return the name from the extern prelude
let root_def_map = crate_root.def_map(db);
- for (name, &def_id) in root_def_map.extern_prelude() {
+ for (name, def_id) in root_def_map.extern_prelude() {
if module_id == def_id {
let name = scope_name.unwrap_or_else(|| name.clone());
@@ -374,7 +374,7 @@ fn calculate_best_path(
}
}
if let Some(module) = item.module(db) {
- if module.def_map(db).block_id().is_some() && prefixed.is_some() {
+ if module.containing_block().is_some() && prefixed.is_some() {
cov_mark::hit!(prefixed_in_block_expression);
prefixed = Some(PrefixKind::Plain);
}
@@ -454,7 +454,7 @@ fn find_local_import_locations(
worklist.push(ancestor);
}
- let def_map = def_map.crate_root(db).def_map(db);
+ let def_map = def_map.crate_root().def_map(db);
let mut seen: FxHashSet<_> = FxHashSet::default();
@@ -543,6 +543,7 @@ mod tests {
module.local_id,
&mod_path,
crate::item_scope::BuiltinShadowMode::Module,
+ None,
)
.0
.take_types()
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
index e4912fa8a..f19c3f028 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
@@ -12,16 +12,17 @@ use hir_expand::{
use intern::Interned;
use la_arena::{Arena, ArenaMap, Idx};
use once_cell::unsync::Lazy;
-use std::ops::DerefMut;
use stdx::impl_from;
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
+use triomphe::Arc;
use crate::{
- body::{Expander, LowerCtx},
child_by_source::ChildBySource,
db::DefDatabase,
- dyn_map::DynMap,
- keys,
+ dyn_map::{keys, DynMap},
+ expander::Expander,
+ lower::LowerCtx,
+ nameres::{DefMap, MacroSubNs},
src::{HasChildSource, HasSource},
type_ref::{LifetimeRef, TypeBound, TypeRef},
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
@@ -153,7 +154,6 @@ impl GenericParams {
def: GenericDefId,
) -> Interned<GenericParams> {
let _p = profile::span("generic_params_query");
-
macro_rules! id_to_generics {
($id:ident) => {{
let id = $id.lookup(db).id;
@@ -176,8 +176,10 @@ impl GenericParams {
// Don't create an `Expander` nor call `loc.source(db)` if not needed since this
// causes a reparse after the `ItemTree` has been created.
- let mut expander = Lazy::new(|| Expander::new(db, loc.source(db).file_id, module));
- for (_, param) in &func_data.params {
+ let mut expander = Lazy::new(|| {
+ (module.def_map(db), Expander::new(db, loc.source(db).file_id, module))
+ });
+ for param in &func_data.params {
generic_params.fill_implicit_impl_trait_args(db, &mut expander, param);
}
@@ -329,7 +331,7 @@ impl GenericParams {
pub(crate) fn fill_implicit_impl_trait_args(
&mut self,
db: &dyn DefDatabase,
- expander: &mut impl DerefMut<Target = Expander>,
+ exp: &mut Lazy<(Arc<DefMap>, Expander), impl FnOnce() -> (Arc<DefMap>, Expander)>,
type_ref: &TypeRef,
) {
type_ref.walk(&mut |type_ref| {
@@ -349,14 +351,28 @@ impl GenericParams {
}
if let TypeRef::Macro(mc) = type_ref {
let macro_call = mc.to_node(db.upcast());
- match expander.enter_expand::<ast::Type>(db, macro_call) {
- Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
- let ctx = LowerCtx::new(db, expander.current_file_id());
- let type_ref = TypeRef::from_ast(&ctx, expanded);
- self.fill_implicit_impl_trait_args(db, expander, &type_ref);
- expander.exit(db, mark);
- }
- _ => {}
+ let (def_map, expander) = &mut **exp;
+
+ let module = expander.module.local_id;
+ let resolver = |path| {
+ def_map
+ .resolve_path(
+ db,
+ module,
+ &path,
+ crate::item_scope::BuiltinShadowMode::Other,
+ Some(MacroSubNs::Bang),
+ )
+ .0
+ .take_macros()
+ };
+ if let Ok(ExpandResult { value: Some((mark, expanded)), .. }) =
+ expander.enter_expand(db, macro_call, resolver)
+ {
+ let ctx = expander.ctx(db);
+ let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
+ self.fill_implicit_impl_trait_args(db, &mut *exp, &type_ref);
+ exp.1.exit(db, mark);
}
}
});
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index 19fa6b254..500e88006 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -12,26 +12,29 @@
//!
//! See also a neighboring `body` module.
+pub mod type_ref;
+
use std::fmt;
use hir_expand::name::Name;
use intern::Interned;
use la_arena::{Idx, RawIdx};
use smallvec::SmallVec;
+use syntax::ast;
use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef},
- BlockId,
+ BlockId, ConstBlockId,
};
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
-pub type ExprId = Idx<Expr>;
-
pub type BindingId = Idx<Binding>;
+pub type ExprId = Idx<Expr>;
+
/// FIXME: this is a hacky function which should be removed
pub(crate) fn dummy_expr_id() -> ExprId {
ExprId::from_raw(RawIdx::from(u32::MAX))
@@ -82,6 +85,7 @@ impl fmt::Display for FloatTypeWrapper {
pub enum Literal {
String(Box<str>),
ByteString(Box<[u8]>),
+ CString(Box<str>),
Char(char),
Bool(bool),
Int(i128, Option<BuiltinInt>),
@@ -93,6 +97,66 @@ pub enum Literal {
}
#[derive(Debug, Clone, Eq, PartialEq)]
+/// Used in range patterns.
+pub enum LiteralOrConst {
+ Literal(Literal),
+ Const(Path),
+}
+
+impl Literal {
+ pub fn negate(self) -> Option<Self> {
+ if let Literal::Int(i, k) = self {
+ Some(Literal::Int(-i, k))
+ } else {
+ None
+ }
+ }
+}
+
+impl From<ast::LiteralKind> for Literal {
+ fn from(ast_lit_kind: ast::LiteralKind) -> Self {
+ use ast::LiteralKind;
+ match ast_lit_kind {
+ // FIXME: these should have actual values filled in, but unsure on perf impact
+ LiteralKind::IntNumber(lit) => {
+ if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
+ Literal::Float(
+ FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
+ builtin,
+ )
+ } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
+ Literal::Uint(lit.value().unwrap_or(0), builtin)
+ } else {
+ let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
+ Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
+ }
+ }
+ LiteralKind::FloatNumber(lit) => {
+ let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
+ Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
+ }
+ LiteralKind::ByteString(bs) => {
+ let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
+ Literal::ByteString(text)
+ }
+ LiteralKind::String(s) => {
+ let text = s.value().map(Box::from).unwrap_or_else(Default::default);
+ Literal::String(text)
+ }
+ LiteralKind::CString(s) => {
+ let text = s.value().map(Box::from).unwrap_or_else(Default::default);
+ Literal::CString(text)
+ }
+ LiteralKind::Byte(b) => {
+ Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
+ }
+ LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
+ LiteralKind::Bool(val) => Literal::Bool(val),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Expr {
/// This is produced if the syntax tree does not have a required expression piece.
Missing,
@@ -107,28 +171,19 @@ pub enum Expr {
expr: ExprId,
},
Block {
- id: BlockId,
+ id: Option<BlockId>,
statements: Box<[Statement]>,
tail: Option<ExprId>,
label: Option<LabelId>,
},
- TryBlock {
- id: BlockId,
- statements: Box<[Statement]>,
- tail: Option<ExprId>,
- },
Async {
- id: BlockId,
- statements: Box<[Statement]>,
- tail: Option<ExprId>,
- },
- Const {
- id: BlockId,
+ id: Option<BlockId>,
statements: Box<[Statement]>,
tail: Option<ExprId>,
},
+ Const(ConstBlockId),
Unsafe {
- id: BlockId,
+ id: Option<BlockId>,
statements: Box<[Statement]>,
tail: Option<ExprId>,
},
@@ -141,12 +196,6 @@ pub enum Expr {
body: ExprId,
label: Option<LabelId>,
},
- For {
- iterable: ExprId,
- pat: PatId,
- body: ExprId,
- label: Option<LabelId>,
- },
Call {
callee: ExprId,
args: Box<[ExprId]>,
@@ -163,11 +212,11 @@ pub enum Expr {
arms: Box<[MatchArm]>,
},
Continue {
- label: Option<Name>,
+ label: Option<LabelId>,
},
Break {
expr: Option<ExprId>,
- label: Option<Name>,
+ label: Option<LabelId>,
},
Return {
expr: Option<ExprId>,
@@ -192,9 +241,6 @@ pub enum Expr {
Await {
expr: ExprId,
},
- Try {
- expr: ExprId,
- },
Cast {
expr: ExprId,
type_ref: Interned<TypeRef>,
@@ -231,6 +277,7 @@ pub enum Expr {
ret_type: Option<Interned<TypeRef>>,
body: ExprId,
closure_kind: ClosureKind,
+ capture_by: CaptureBy,
},
Tuple {
exprs: Box<[ExprId]>,
@@ -249,6 +296,14 @@ pub enum ClosureKind {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CaptureBy {
+ /// `move |x| y + x`.
+ Value,
+ /// `move` keyword was not specified.
+ Ref,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Movability {
Static,
Movable,
@@ -302,11 +357,10 @@ impl Expr {
Expr::Let { expr, .. } => {
f(*expr);
}
+ Expr::Const(_) => (),
Expr::Block { statements, tail, .. }
- | Expr::TryBlock { statements, tail, .. }
| Expr::Unsafe { statements, tail, .. }
- | Expr::Async { statements, tail, .. }
- | Expr::Const { statements, tail, .. } => {
+ | Expr::Async { statements, tail, .. } => {
for stmt in statements.iter() {
match stmt {
Statement::Let { initializer, else_branch, .. } => {
@@ -329,10 +383,6 @@ impl Expr {
f(*condition);
f(*body);
}
- Expr::For { iterable, body, .. } => {
- f(*iterable);
- f(*body);
- }
Expr::Call { callee, args, .. } => {
f(*callee);
args.iter().copied().for_each(f);
@@ -383,7 +433,6 @@ impl Expr {
}
Expr::Field { expr, .. }
| Expr::Await { expr }
- | Expr::Try { expr }
| Expr::Cast { expr, .. }
| Expr::Ref { expr, .. }
| Expr::UnaryOp { expr, .. }
@@ -438,10 +487,21 @@ impl BindingAnnotation {
}
#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum BindingProblems {
+ /// https://doc.rust-lang.org/stable/error_codes/E0416.html
+ BoundMoreThanOnce,
+ /// https://doc.rust-lang.org/stable/error_codes/E0409.html
+ BoundInconsistently,
+ /// https://doc.rust-lang.org/stable/error_codes/E0408.html
+ NotBoundAcrossAll,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Binding {
pub name: Name,
pub mode: BindingAnnotation,
pub definitions: SmallVec<[PatId; 1]>,
+ pub problems: Option<BindingProblems>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -458,7 +518,7 @@ pub enum Pat {
Tuple { args: Box<[PatId]>, ellipsis: Option<usize> },
Or(Box<[PatId]>),
Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool },
- Range { start: ExprId, end: ExprId },
+ Range { start: Option<Box<LiteralOrConst>>, end: Option<Box<LiteralOrConst>> },
Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
Path(Box<Path>),
Lit(ExprId),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
index 8e30f429a..fa1f4933a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
@@ -1,9 +1,11 @@
//! HIR for references to types. Paths in these are not yet resolved. They can
//! be directly created from an ast::TypeRef, without further queries.
+use core::fmt;
use std::fmt::Write;
use hir_expand::{
+ db::ExpandDatabase,
name::{AsName, Name},
AstId,
};
@@ -11,9 +13,9 @@ use intern::Interned;
use syntax::ast::{self, HasName};
use crate::{
- body::LowerCtx,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
- expr::Literal,
+ hir::Literal,
+ lower::LowerCtx,
path::Path,
};
@@ -116,7 +118,7 @@ pub enum TypeRef {
Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
// FIXME: for full const generics, the latter element (length) here is going to have to be an
// expression that is further lowered later in hir_ty.
- Array(Box<TypeRef>, ConstRefOrPath),
+ Array(Box<TypeRef>, ConstRef),
Slice(Box<TypeRef>),
/// A fn pointer. Last element of the vector is the return type.
Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/, bool /*is_unsafe*/),
@@ -184,11 +186,7 @@ impl TypeRef {
TypeRef::RawPtr(Box::new(inner_ty), mutability)
}
ast::Type::ArrayType(inner) => {
- // FIXME: This is a hack. We should probably reuse the machinery of
- // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
- // `hir_ty` level, which would allow knowing the type of:
- // let v: [u8; 2 + 2] = [0u8; 4];
- let len = ConstRefOrPath::from_expr_opt(inner.expr());
+ let len = ConstRef::from_const_arg(ctx, inner.const_arg());
TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
}
ast::Type::SliceType(inner) => {
@@ -378,69 +376,84 @@ impl TypeBound {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ConstRefOrPath {
- Scalar(ConstRef),
+pub enum ConstRef {
+ Scalar(LiteralConstRef),
Path(Name),
+ Complex(AstId<ast::ConstArg>),
}
-impl std::fmt::Display for ConstRefOrPath {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- ConstRefOrPath::Scalar(s) => s.fmt(f),
- ConstRefOrPath::Path(n) => n.fmt(f),
+impl ConstRef {
+ pub(crate) fn from_const_arg(lower_ctx: &LowerCtx<'_>, arg: Option<ast::ConstArg>) -> Self {
+ if let Some(arg) = arg {
+ let ast_id = lower_ctx.ast_id(&arg);
+ if let Some(expr) = arg.expr() {
+ return Self::from_expr(expr, ast_id);
+ }
}
+ Self::Scalar(LiteralConstRef::Unknown)
}
-}
-impl ConstRefOrPath {
- pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
- match expr {
- Some(x) => Self::from_expr(x),
- None => Self::Scalar(ConstRef::Unknown),
+ pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
+ struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef);
+ impl fmt::Display for Display<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.1 {
+ ConstRef::Scalar(s) => s.fmt(f),
+ ConstRef::Path(n) => n.display(self.0).fmt(f),
+ ConstRef::Complex(_) => f.write_str("{const}"),
+ }
+ }
}
+ Display(db, self)
}
- // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
- // parse stage.
- fn from_expr(expr: ast::Expr) -> Self {
+ // We special case literals and single identifiers, to speed up things.
+ fn from_expr(expr: ast::Expr, ast_id: Option<AstId<ast::ConstArg>>) -> Self {
+ fn is_path_ident(p: &ast::PathExpr) -> bool {
+ let Some(path) = p.path() else {
+ return false;
+ };
+ if path.coloncolon_token().is_some() {
+ return false;
+ }
+ if let Some(s) = path.segment() {
+ if s.coloncolon_token().is_some() || s.generic_arg_list().is_some() {
+ return false;
+ }
+ }
+ true
+ }
match expr {
- ast::Expr::PathExpr(p) => {
+ 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()),
- None => Self::Scalar(ConstRef::Unknown),
+ None => Self::Scalar(LiteralConstRef::Unknown),
}
}
- ast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() {
- Some(ast::UnaryOp::Neg) => {
- let unsigned = Self::from_expr_opt(prefix_expr.expr());
- // Add sign
- match unsigned {
- Self::Scalar(ConstRef::UInt(num)) => {
- Self::Scalar(ConstRef::Int(-(num as i128)))
- }
- other => other,
- }
- }
- _ => Self::from_expr_opt(prefix_expr.expr()),
- },
ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
ast::LiteralKind::IntNumber(num) => {
- num.value().map(ConstRef::UInt).unwrap_or(ConstRef::Unknown)
+ num.value().map(LiteralConstRef::UInt).unwrap_or(LiteralConstRef::Unknown)
}
ast::LiteralKind::Char(c) => {
- c.value().map(ConstRef::Char).unwrap_or(ConstRef::Unknown)
+ c.value().map(LiteralConstRef::Char).unwrap_or(LiteralConstRef::Unknown)
}
- ast::LiteralKind::Bool(f) => ConstRef::Bool(f),
- _ => ConstRef::Unknown,
+ ast::LiteralKind::Bool(f) => LiteralConstRef::Bool(f),
+ _ => LiteralConstRef::Unknown,
}),
- _ => Self::Scalar(ConstRef::Unknown),
+ _ => {
+ if let Some(ast_id) = ast_id {
+ Self::Complex(ast_id)
+ } else {
+ Self::Scalar(LiteralConstRef::Unknown)
+ }
+ }
}
}
}
-/// A concrete constant value
+/// A literal constant value
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ConstRef {
+pub enum LiteralConstRef {
Int(i128),
UInt(u128),
Bool(bool),
@@ -454,18 +467,20 @@ pub enum ConstRef {
Unknown,
}
-impl ConstRef {
+impl LiteralConstRef {
pub fn builtin_type(&self) -> BuiltinType {
match self {
- ConstRef::UInt(_) | ConstRef::Unknown => BuiltinType::Uint(BuiltinUint::U128),
- ConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128),
- ConstRef::Char(_) => BuiltinType::Char,
- ConstRef::Bool(_) => BuiltinType::Bool,
+ LiteralConstRef::UInt(_) | LiteralConstRef::Unknown => {
+ BuiltinType::Uint(BuiltinUint::U128)
+ }
+ LiteralConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128),
+ LiteralConstRef::Char(_) => BuiltinType::Char,
+ LiteralConstRef::Bool(_) => BuiltinType::Bool,
}
}
}
-impl From<Literal> for ConstRef {
+impl From<Literal> for LiteralConstRef {
fn from(literal: Literal) -> Self {
match literal {
Literal::Char(c) => Self::Char(c),
@@ -477,14 +492,14 @@ impl From<Literal> for ConstRef {
}
}
-impl std::fmt::Display for ConstRef {
+impl std::fmt::Display for LiteralConstRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
- ConstRef::Int(num) => num.fmt(f),
- ConstRef::UInt(num) => num.fmt(f),
- ConstRef::Bool(flag) => flag.fmt(f),
- ConstRef::Char(c) => write!(f, "'{c}'"),
- ConstRef::Unknown => f.write_char('_'),
+ LiteralConstRef::Int(num) => num.fmt(f),
+ LiteralConstRef::UInt(num) => num.fmt(f),
+ LiteralConstRef::Bool(flag) => flag.fmt(f),
+ LiteralConstRef::Char(c) => write!(f, "'{c}'"),
+ LiteralConstRef::Unknown => f.write_char('_'),
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index 4f1f6000d..48532655e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -1,6 +1,6 @@
//! A map of all publicly exported items in a crate.
-use std::{fmt, hash::BuildHasherDefault, sync::Arc};
+use std::{fmt, hash::BuildHasherDefault};
use base_db::CrateId;
use fst::{self, Streamer};
@@ -8,10 +8,11 @@ use hir_expand::name::Name;
use indexmap::{map::Entry, IndexMap};
use itertools::Itertools;
use rustc_hash::{FxHashSet, FxHasher};
+use triomphe::Arc;
use crate::{
- db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
- ModuleId, TraitId,
+ db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId,
+ ModuleDefId, ModuleId, TraitId,
};
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
@@ -32,13 +33,23 @@ pub struct ImportPath {
pub segments: Vec<Name>,
}
-impl fmt::Display for ImportPath {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(&self.segments.iter().format("::"), f)
+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 }
}
-}
-impl ImportPath {
fn len(&self) -> usize {
self.segments.len()
}
@@ -75,7 +86,7 @@ impl ImportMap {
let mut importables = import_map
.map
.iter()
- .map(|(item, info)| (item, fst_path(&info.path)))
+ .map(|(item, info)| (item, fst_path(db, &info.path)))
.collect::<Vec<_>>();
importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2));
@@ -112,6 +123,25 @@ impl ImportMap {
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,
@@ -153,7 +183,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap {
// 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(def_map.root());
+ let root = def_map.module_id(DefMap::ROOT);
let mut worklist = vec![(root, empty)];
while let Some((module, mod_path)) = worklist.pop() {
let ext_def_map;
@@ -233,13 +263,10 @@ impl fmt::Debug for ImportMap {
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)
+ .map(|(item, _)| match item {
+ ItemInNs::Types(it) => format!("- {it:?} (t)",),
+ ItemInNs::Values(it) => format!("- {it:?} (v)",),
+ ItemInNs::Macros(it) => format!("- {it:?} (m)",),
})
.collect();
@@ -248,9 +275,9 @@ impl fmt::Debug for ImportMap {
}
}
-fn fst_path(path: &ImportPath) -> String {
+fn fst_path(db: &dyn DefDatabase, path: &ImportPath) -> String {
let _p = profile::span("fst_path");
- let mut s = path.to_string();
+ let mut s = path.display(db).to_string();
s.make_ascii_lowercase();
s
}
@@ -343,7 +370,12 @@ impl Query {
self
}
- fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
+ fn import_matches(
+ &self,
+ db: &dyn DefDatabase,
+ import: &ImportInfo,
+ 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) {
@@ -354,9 +386,9 @@ impl Query {
}
let mut input = if import.is_trait_assoc_item || self.name_only {
- import.path.segments.last().unwrap().to_string()
+ import.path.segments.last().unwrap().display(db.upcast()).to_string()
} else {
- import.path.to_string()
+ import.path.display(db).to_string()
};
if enforce_lowercase || !self.case_sensitive {
input.make_ascii_lowercase();
@@ -421,25 +453,27 @@ pub fn search_dependencies(
let importables = &import_map.importables[indexed_value.value as usize..];
let common_importable_data = &import_map.map[&importables[0]];
- if !query.import_matches(common_importable_data, true) {
+ 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(&common_importable_data.path);
+ 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(&import_map.map[item].path))
+ .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
- || query.import_matches(&import_map.map[item], false)
+ || query.import_matches(db, &import_map.map[item], false)
});
res.extend(iter);
@@ -472,7 +506,7 @@ mod tests {
use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
use expect_test::{expect, Expect};
- use crate::{test_db::TestDB, ItemContainerId, Lookup};
+ use crate::{db::DefDatabase, test_db::TestDB, ItemContainerId, Lookup};
use super::*;
@@ -496,7 +530,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)?.to_string(),
+ dependency_imports.path_of(dependency)?.display(&db).to_string(),
match dependency {
ItemInNs::Types(ModuleDefId::FunctionId(_))
| ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
@@ -547,7 +581,11 @@ mod tests {
None
}
})?;
- return Some(format!("{}::{assoc_item_name}", dependency_imports.path_of(trait_)?));
+ return Some(format!(
+ "{}::{}",
+ dependency_imports.path_of(trait_)?.display(db),
+ assoc_item_name.display(db.upcast())
+ ));
}
None
}
@@ -587,7 +625,7 @@ mod tests {
let map = db.import_map(krate);
- Some(format!("{name}:\n{map:?}\n"))
+ Some(format!("{name}:\n{}\n", map.fmt_for_test(db.upcast())))
})
.sorted()
.collect::<String>();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index 991e44703..2001fb29a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -4,7 +4,7 @@
use std::collections::hash_map::Entry;
use base_db::CrateId;
-use hir_expand::{attrs::AttrId, name::Name, AstId, MacroCallId};
+use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId};
use itertools::Itertools;
use once_cell::sync::Lazy;
use profile::Count;
@@ -334,10 +334,6 @@ impl ItemScope {
)
}
- pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, SmallVec<[MacroId; 1]>> {
- self.legacy_macros.clone()
- }
-
/// Marks everything that is not a procedural macro as private to `this_module`.
pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) {
self.types
@@ -358,12 +354,16 @@ impl ItemScope {
}
}
- pub(crate) fn dump(&self, buf: &mut String) {
+ pub(crate) fn dump(&self, db: &dyn ExpandDatabase, buf: &mut String) {
let mut entries: Vec<_> = self.resolutions().collect();
entries.sort_by_key(|(name, _)| name.clone());
for (name, def) in entries {
- format_to!(buf, "{}:", name.map_or("_".to_string(), |name| name.to_string()));
+ format_to!(
+ buf,
+ "{}:",
+ name.map_or("_".to_string(), |name| name.display(db).to_string())
+ );
if def.types.is_some() {
buf.push_str(" t");
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 9da5b2d47..e74b71888 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -40,7 +40,6 @@ use std::{
hash::{Hash, Hasher},
marker::PhantomData,
ops::Index,
- sync::Arc,
};
use ast::{AstNode, HasName, StructKind};
@@ -60,6 +59,7 @@ use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use stdx::never;
use syntax::{ast, match_ast, SyntaxKind};
+use triomphe::Arc;
use crate::{
attr::Attrs,
@@ -107,10 +107,7 @@ pub struct ItemTree {
impl ItemTree {
pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
let _p = profile::span("file_item_tree_query").detail(|| format!("{file_id:?}"));
- let syntax = match db.parse_or_expand(file_id) {
- Some(node) => node,
- None => return Default::default(),
- };
+ let syntax = db.parse_or_expand(file_id);
if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax)
{
// FIXME: not 100% sure why these crop up, but return an empty tree to avoid a panic
@@ -169,8 +166,8 @@ impl ItemTree {
Attrs::filter(db, krate, self.raw_attrs(of).clone())
}
- pub fn pretty_print(&self) -> String {
- pretty::print_item_tree(self)
+ pub fn pretty_print(&self, db: &dyn DefDatabase) -> String {
+ pretty::print_item_tree(db.upcast(), self)
}
fn data(&self) -> &ItemTreeData {
@@ -600,19 +597,18 @@ pub struct Function {
pub abi: Option<Interned<str>>,
pub params: IdxRange<Param>,
pub ret_type: Interned<TypeRef>,
- pub async_ret_type: Option<Interned<TypeRef>>,
pub ast_id: FileAstId<ast::Fn>,
pub(crate) flags: FnFlags,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Param {
- Normal(Option<Name>, Interned<TypeRef>),
+ Normal(Interned<TypeRef>),
Varargs,
}
bitflags::bitflags! {
- #[derive(Default)]
+ #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
pub(crate) struct FnFlags: u8 {
const HAS_SELF_PARAM = 1 << 0;
const HAS_BODY = 1 << 1;
@@ -721,7 +717,6 @@ pub struct Mod {
pub enum ModKind {
/// `mod m { ... }`
Inline { items: Box<[ModItem]> },
-
/// `mod m;`
Outline,
}
@@ -895,10 +890,6 @@ impl ModItem {
}
}
- pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
- N::id_from_mod_item(self)
- }
-
pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
match self {
ModItem::Import(it) => tree[it.index].ast_id().upcast(),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
index 77b186f8e..46633667e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
@@ -1,6 +1,6 @@
//! AST -> `ItemTree` lowering code.
-use std::{collections::hash_map::Entry, sync::Arc};
+use std::collections::hash_map::Entry;
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
use syntax::ast::{self, HasModuleItem, HasTypeBounds};
@@ -20,7 +20,7 @@ pub(super) struct Ctx<'a> {
db: &'a dyn DefDatabase,
tree: ItemTree,
source_ast_id_map: Arc<AstIdMap>,
- body_ctx: crate::body::LowerCtx<'a>,
+ body_ctx: crate::lower::LowerCtx<'a>,
}
impl<'a> Ctx<'a> {
@@ -29,7 +29,7 @@ impl<'a> Ctx<'a> {
db,
tree: ItemTree::default(),
source_ast_id_map: db.ast_id_map(file),
- body_ctx: crate::body::LowerCtx::new(db, file),
+ body_ctx: crate::lower::LowerCtx::with_file_id(db, file),
}
}
@@ -293,7 +293,7 @@ impl<'a> Ctx<'a> {
}
};
let ty = Interned::new(self_type);
- let idx = self.data().params.alloc(Param::Normal(None, ty));
+ let idx = self.data().params.alloc(Param::Normal(ty));
self.add_attrs(
idx.into(),
RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()),
@@ -306,19 +306,7 @@ impl<'a> Ctx<'a> {
None => {
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
let ty = Interned::new(type_ref);
- let mut pat = param.pat();
- // FIXME: This really shouldn't be here, in fact FunctionData/ItemTree's function shouldn't know about
- // pattern names at all
- let name = 'name: loop {
- match pat {
- Some(ast::Pat::RefPat(ref_pat)) => pat = ref_pat.pat(),
- Some(ast::Pat::IdentPat(ident)) => {
- break 'name ident.name().map(|it| it.as_name())
- }
- _ => break 'name None,
- }
- };
- self.data().params.alloc(Param::Normal(name, ty))
+ self.data().params.alloc(Param::Normal(ty))
}
};
self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &param, self.hygiene()));
@@ -336,13 +324,12 @@ impl<'a> Ctx<'a> {
None => TypeRef::unit(),
};
- let (ret_type, async_ret_type) = if func.async_token().is_some() {
- let async_ret_type = ret_type.clone();
+ let ret_type = if func.async_token().is_some() {
let future_impl = desugar_future_path(ret_type);
let ty_bound = Interned::new(TypeBound::Path(future_impl, TraitBoundModifier::None));
- (TypeRef::ImplTrait(vec![ty_bound]), Some(async_ret_type))
+ TypeRef::ImplTrait(vec![ty_bound])
} else {
- (ret_type, None)
+ ret_type
};
let abi = func.abi().map(lower_abi);
@@ -376,7 +363,6 @@ impl<'a> Ctx<'a> {
abi,
params,
ret_type: Interned::new(ret_type),
- async_ret_type: async_ret_type.map(Interned::new),
ast_id,
flags,
};
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
index 5f2999796..e873316a5 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
@@ -2,6 +2,8 @@
use std::fmt::{self, Write};
+use hir_expand::db::ExpandDatabase;
+
use crate::{
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
pretty::{print_path, print_type_bounds, print_type_ref},
@@ -10,8 +12,8 @@ use crate::{
use super::*;
-pub(super) fn print_item_tree(tree: &ItemTree) -> String {
- let mut p = Printer { tree, buf: String::new(), indent_level: 0, needs_indent: true };
+pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String {
+ let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
p.print_attrs(attrs, true);
@@ -43,6 +45,7 @@ macro_rules! wln {
}
struct Printer<'a> {
+ db: &'a dyn ExpandDatabase,
tree: &'a ItemTree,
buf: String,
indent_level: usize,
@@ -88,7 +91,7 @@ impl<'a> Printer<'a> {
self,
"#{}[{}{}]",
inner,
- attr.path,
+ attr.path.display(self.db),
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
);
}
@@ -102,7 +105,7 @@ impl<'a> Printer<'a> {
fn print_visibility(&mut self, vis: RawVisibilityId) {
match &self.tree[vis] {
- RawVisibility::Module(path) => w!(self, "pub({}) ", path),
+ RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)),
RawVisibility::Public => w!(self, "pub "),
};
}
@@ -117,7 +120,7 @@ impl<'a> Printer<'a> {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field);
this.print_visibility(*visibility);
- w!(this, "{}: ", name);
+ w!(this, "{}: ", name.display(self.db));
this.print_type_ref(type_ref);
wln!(this, ",");
}
@@ -131,7 +134,7 @@ impl<'a> Printer<'a> {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field);
this.print_visibility(*visibility);
- w!(this, "{}: ", name);
+ w!(this, "{}: ", name.display(self.db));
this.print_type_ref(type_ref);
wln!(this, ",");
}
@@ -164,20 +167,20 @@ impl<'a> Printer<'a> {
fn print_use_tree(&mut self, use_tree: &UseTree) {
match &use_tree.kind {
UseTreeKind::Single { path, alias } => {
- w!(self, "{}", path);
+ w!(self, "{}", path.display(self.db));
if let Some(alias) = alias {
w!(self, " as {}", alias);
}
}
UseTreeKind::Glob { path } => {
if let Some(path) = path {
- w!(self, "{}::", path);
+ w!(self, "{}::", path.display(self.db));
}
w!(self, "*");
}
UseTreeKind::Prefixed { prefix, list } => {
if let Some(prefix) = prefix {
- w!(self, "{}::", prefix);
+ w!(self, "{}::", prefix.display(self.db));
}
w!(self, "{{");
for (i, tree) in list.iter().enumerate() {
@@ -205,7 +208,7 @@ impl<'a> Printer<'a> {
ModItem::ExternCrate(it) => {
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "extern crate {}", name);
+ w!(self, "extern crate {}", name.display(self.db));
if let Some(alias) = alias {
w!(self, " as {}", alias);
}
@@ -233,7 +236,6 @@ impl<'a> Printer<'a> {
abi,
params,
ret_type,
- async_ret_type: _,
ast_id: _,
flags,
} = &self.tree[it];
@@ -253,26 +255,20 @@ impl<'a> Printer<'a> {
if let Some(abi) = abi {
w!(self, "extern \"{}\" ", abi);
}
- w!(self, "fn {}", name);
+ w!(self, "fn {}", name.display(self.db));
self.print_generic_params(explicit_generic_params);
w!(self, "(");
if !params.is_empty() {
self.indented(|this| {
- for (i, param) in params.clone().enumerate() {
+ for param in params.clone() {
this.print_attrs_of(param);
match &this.tree[param] {
- Param::Normal(name, ty) => {
- match name {
- Some(name) => w!(this, "{}: ", name),
- None => w!(this, "_: "),
+ Param::Normal(ty) => {
+ if flags.contains(FnFlags::HAS_SELF_PARAM) {
+ w!(this, "self: ");
}
this.print_type_ref(ty);
- w!(this, ",");
- if flags.contains(FnFlags::HAS_SELF_PARAM) && i == 0 {
- wln!(this, " // self");
- } else {
- wln!(this);
- }
+ wln!(this, ",");
}
Param::Varargs => {
wln!(this, "...");
@@ -293,7 +289,7 @@ impl<'a> Printer<'a> {
ModItem::Struct(it) => {
let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "struct {}", name);
+ w!(self, "struct {}", name.display(self.db));
self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) {
@@ -305,7 +301,7 @@ impl<'a> Printer<'a> {
ModItem::Union(it) => {
let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "union {}", name);
+ w!(self, "union {}", name.display(self.db));
self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) {
@@ -317,14 +313,14 @@ impl<'a> Printer<'a> {
ModItem::Enum(it) => {
let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "enum {}", name);
+ w!(self, "enum {}", name.display(self.db));
self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
for variant in variants.clone() {
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
this.print_attrs_of(variant);
- w!(this, "{}", name);
+ w!(this, "{}", name.display(self.db));
this.print_fields(fields);
wln!(this, ",");
}
@@ -336,7 +332,7 @@ impl<'a> Printer<'a> {
self.print_visibility(*visibility);
w!(self, "const ");
match name {
- Some(name) => w!(self, "{}", name),
+ Some(name) => w!(self, "{}", name.display(self.db)),
None => w!(self, "_"),
}
w!(self, ": ");
@@ -350,7 +346,7 @@ impl<'a> Printer<'a> {
if *mutable {
w!(self, "mut ");
}
- w!(self, "{}: ", name);
+ w!(self, "{}: ", name.display(self.db));
self.print_type_ref(type_ref);
w!(self, " = _;");
wln!(self);
@@ -372,7 +368,7 @@ impl<'a> Printer<'a> {
if *is_auto {
w!(self, "auto ");
}
- w!(self, "trait {}", name);
+ w!(self, "trait {}", name.display(self.db));
self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
@@ -385,7 +381,7 @@ impl<'a> Printer<'a> {
ModItem::TraitAlias(it) => {
let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "trait {}", name);
+ w!(self, "trait {}", name.display(self.db));
self.print_generic_params(generic_params);
w!(self, " = ");
self.print_where_clause(generic_params);
@@ -418,7 +414,7 @@ impl<'a> Printer<'a> {
let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
&self.tree[it];
self.print_visibility(*visibility);
- w!(self, "type {}", name);
+ w!(self, "type {}", name.display(self.db));
self.print_generic_params(generic_params);
if !bounds.is_empty() {
w!(self, ": ");
@@ -435,7 +431,7 @@ impl<'a> Printer<'a> {
ModItem::Mod(it) => {
let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "mod {}", name);
+ w!(self, "mod {}", name.display(self.db));
match kind {
ModKind::Inline { items } => {
w!(self, " {{");
@@ -453,16 +449,16 @@ impl<'a> Printer<'a> {
}
ModItem::MacroCall(it) => {
let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it];
- wln!(self, "{}!(...);", path);
+ wln!(self, "{}!(...);", path.display(self.db));
}
ModItem::MacroRules(it) => {
let MacroRules { name, ast_id: _ } = &self.tree[it];
- wln!(self, "macro_rules! {} {{ ... }}", name);
+ wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db));
}
ModItem::MacroDef(it) => {
let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- wln!(self, "macro {} {{ ... }}", name);
+ wln!(self, "macro {} {{ ... }}", name.display(self.db));
}
}
@@ -470,15 +466,15 @@ impl<'a> Printer<'a> {
}
fn print_type_ref(&mut self, type_ref: &TypeRef) {
- print_type_ref(type_ref, self).unwrap();
+ print_type_ref(self.db, type_ref, self).unwrap();
}
fn print_type_bounds(&mut self, bounds: &[Interned<TypeBound>]) {
- print_type_bounds(bounds, self).unwrap();
+ print_type_bounds(self.db, bounds, self).unwrap();
}
fn print_path(&mut self, path: &Path) {
- print_path(path, self).unwrap();
+ print_path(self.db, path, self).unwrap();
}
fn print_generic_params(&mut self, params: &GenericParams) {
@@ -493,7 +489,7 @@ impl<'a> Printer<'a> {
w!(self, ", ");
}
first = false;
- w!(self, "{}", lt.name);
+ w!(self, "{}", lt.name.display(self.db));
}
for (idx, x) in params.type_or_consts.iter() {
if !first {
@@ -502,11 +498,11 @@ impl<'a> Printer<'a> {
first = false;
match x {
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
- Some(name) => w!(self, "{}", name),
+ Some(name) => w!(self, "{}", name.display(self.db)),
None => w!(self, "_anon_{}", idx.into_raw()),
},
TypeOrConstParamData::ConstParamData(konst) => {
- w!(self, "const {}: ", konst.name);
+ w!(self, "const {}: ", konst.name.display(self.db));
self.print_type_ref(&konst.ty);
}
}
@@ -538,7 +534,12 @@ impl<'a> Printer<'a> {
let (target, bound) = match pred {
WherePredicate::TypeBound { target, bound } => (target, bound),
WherePredicate::Lifetime { target, bound } => {
- wln!(this, "{}: {},", target.name, bound.name);
+ wln!(
+ this,
+ "{}: {},",
+ target.name.display(self.db),
+ bound.name.display(self.db)
+ );
continue;
}
WherePredicate::ForLifetime { lifetimes, target, bound } => {
@@ -547,7 +548,7 @@ impl<'a> Printer<'a> {
if i != 0 {
w!(this, ", ");
}
- w!(this, "{}", lt);
+ w!(this, "{}", lt.display(self.db));
}
w!(this, "> ");
(target, bound)
@@ -558,7 +559,7 @@ impl<'a> Printer<'a> {
WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
WherePredicateTypeTarget::TypeOrConstParam(id) => {
match &params.type_or_consts[*id].name() {
- Some(name) => w!(this, "{}", name),
+ Some(name) => w!(this, "{}", name.display(self.db)),
None => w!(this, "_anon_{}", id.into_raw()),
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
index e30d9652b..5ded4b6b2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
@@ -6,7 +6,7 @@ use crate::{db::DefDatabase, test_db::TestDB};
fn check(ra_fixture: &str, expect: Expect) {
let (db, file_id) = TestDB::with_single_file(ra_fixture);
let item_tree = db.file_item_tree(file_id.into());
- let pretty = item_tree.pretty_print();
+ let pretty = item_tree.pretty_print(&db);
expect.assert_eq(&pretty);
}
@@ -165,7 +165,7 @@ trait Tr: SuperTrait + 'lifetime {
fn method(&self);
}
"#,
- expect![[r##"
+ expect![[r#"
pub static mut ST: () = _;
pub(self) const _: Anon = _;
@@ -174,8 +174,8 @@ trait Tr: SuperTrait + 'lifetime {
#[inner_attr_in_fn]
pub(self) fn f(
#[attr]
- arg: u8,
- _: (),
+ u8,
+ (),
) -> () { ... }
pub(self) trait Tr<Self>
@@ -186,10 +186,10 @@ trait Tr: SuperTrait + 'lifetime {
pub(self) type Assoc: AssocBound = Default;
pub(self) fn method(
- _: &Self, // self
+ self: &Self,
) -> ();
}
- "##]],
+ "#]],
);
}
@@ -336,7 +336,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
T: 'b
{
pub(self) fn f<G>(
- arg: impl Copy,
+ impl Copy,
) -> impl Copy
where
G: 'a { ... }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index d338bb412..0e9ac58fb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -2,14 +2,13 @@
//!
//! This attribute to tell the compiler about semi built-in std library
//! features, such as Fn family of traits.
-use std::sync::Arc;
-
use rustc_hash::FxHashMap;
use syntax::SmolStr;
+use triomphe::Arc;
use crate::{
- db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId,
- ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
+ db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
+ FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -200,7 +199,7 @@ pub enum GenericRequirement {
macro_rules! language_item_table {
(
- $( $(#[$attr:meta])* $variant:ident, $name:ident, $method:ident, $target:expr, $generics:expr; )*
+ $( $(#[$attr:meta])* $variant:ident, $module:ident :: $name:ident, $method:ident, $target:expr, $generics:expr; )*
) => {
/// A representation of all the valid language items in Rust.
@@ -221,11 +220,6 @@ macro_rules! language_item_table {
}
/// Opposite of [`LangItem::name`]
- pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
- Self::from_str(name.as_str()?)
- }
-
- /// Opposite of [`LangItem::name`]
pub fn from_str(name: &str) -> Option<Self> {
match name {
$( stringify!($name) => Some(LangItem::$variant), )*
@@ -236,84 +230,100 @@ macro_rules! language_item_table {
}
}
+impl LangItem {
+ /// Opposite of [`LangItem::name`]
+ pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
+ Self::from_str(name.as_str()?)
+ }
+
+ pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
+ let t = db.lang_item(start_crate, *self)?;
+ Some(Path::LangItem(t))
+ }
+}
+
language_item_table! {
// Variant name, Name, Getter method name, Target Generic requirements;
- Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
- Unsize, unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
+ Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
+ Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
/// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ").
- StructuralPeq, structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;
+ StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;
/// Trait injected by `#[derive(Eq)]`, (i.e. "Total EQ"; no, I will not apologize).
- StructuralTeq, structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None;
- Copy, copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);
- Clone, clone, clone_trait, Target::Trait, GenericRequirement::None;
- Sync, sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
- DiscriminantKind, discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
+ StructuralTeq, sym::structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None;
+ Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);
+ Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None;
+ Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
+ DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
/// The associated item of the [`DiscriminantKind`] trait.
- Discriminant, discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None;
+ Discriminant, sym::discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None;
+
+ PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
+ Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
+ DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
- PointeeTrait, pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
- Metadata, metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
- DynMetadata, dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
+ Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
- Freeze, freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
+ FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0);
+ FnPtrAddr, sym::fn_ptr_addr, fn_ptr_addr, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- Drop, drop, drop_trait, Target::Trait, GenericRequirement::None;
- Destruct, destruct, destruct_trait, Target::Trait, GenericRequirement::None;
+ Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None;
+ Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
- CoerceUnsized, coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
- DispatchFromDyn, dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
+ CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
+ DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
// language items relating to transmutability
- TransmuteOpts, transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
- TransmuteTrait, transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3);
-
- Add, add, add_trait, Target::Trait, GenericRequirement::Exact(1);
- Sub, sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
- Mul, mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);
- Div, div, div_trait, Target::Trait, GenericRequirement::Exact(1);
- Rem, rem, rem_trait, Target::Trait, GenericRequirement::Exact(1);
- Neg, neg, neg_trait, Target::Trait, GenericRequirement::Exact(0);
- Not, not, not_trait, Target::Trait, GenericRequirement::Exact(0);
- BitXor, bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1);
- BitAnd, bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1);
- BitOr, bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1);
- Shl, shl, shl_trait, Target::Trait, GenericRequirement::Exact(1);
- Shr, shr, shr_trait, Target::Trait, GenericRequirement::Exact(1);
- AddAssign, add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- SubAssign, sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- MulAssign, mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- DivAssign, div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- RemAssign, rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- BitXorAssign, bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- BitAndAssign, bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- BitOrAssign, bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- ShlAssign, shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- ShrAssign, shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- Index, index, index_trait, Target::Trait, GenericRequirement::Exact(1);
- IndexMut, index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1);
-
- UnsafeCell, unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
- VaList, va_list, va_list, Target::Struct, GenericRequirement::None;
-
- Deref, deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
- DerefMut, deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
- DerefTarget, deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
- Receiver, receiver, receiver_trait, Target::Trait, GenericRequirement::None;
-
- Fn, fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
- FnMut, fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
- FnOnce, fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
-
- FnOnceOutput, fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
-
- Future, future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
- GeneratorState, generator_state, gen_state, Target::Enum, GenericRequirement::None;
- Generator, generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1);
- Unpin, unpin, unpin_trait, Target::Trait, GenericRequirement::None;
- Pin, pin, pin_type, Target::Struct, GenericRequirement::None;
-
- PartialEq, eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
- PartialOrd, partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
+ TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
+ TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3);
+
+ Add, sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1);
+ Sub, sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
+ Mul, sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);
+ Div, sym::div, div_trait, Target::Trait, GenericRequirement::Exact(1);
+ Rem, sym::rem, rem_trait, Target::Trait, GenericRequirement::Exact(1);
+ Neg, sym::neg, neg_trait, Target::Trait, GenericRequirement::Exact(0);
+ Not, sym::not, not_trait, Target::Trait, GenericRequirement::Exact(0);
+ BitXor, sym::bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitAnd, sym::bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitOr, sym::bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1);
+ Shl, sym::shl, shl_trait, Target::Trait, GenericRequirement::Exact(1);
+ Shr, sym::shr, shr_trait, Target::Trait, GenericRequirement::Exact(1);
+ AddAssign, sym::add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ SubAssign, sym::sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ MulAssign, sym::mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ DivAssign, sym::div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ RemAssign, sym::rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitXorAssign, sym::bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitAndAssign, sym::bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitOrAssign, sym::bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ ShlAssign, sym::shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ ShrAssign, sym::shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ Index, sym::index, index_trait, Target::Trait, GenericRequirement::Exact(1);
+ IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1);
+
+ UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
+ VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None;
+
+ Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
+ DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
+ DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
+ Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None;
+
+ Fn, kw::fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
+ FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
+ FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
+
+ FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
+
+ Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
+ GeneratorState, sym::generator_state, gen_state, Target::Enum, GenericRequirement::None;
+ Generator, sym::generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1);
+ Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None;
+ Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None;
+
+ PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
+ PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
+ CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None;
// A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and
// various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays.
@@ -322,92 +332,103 @@ language_item_table! {
// in the sense that a crate is not required to have it defined to use it, but a final product
// is required to define it somewhere. Additionally, there are restrictions on crates that use
// a weak lang item, but do not have it defined.
- Panic, panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
- PanicNounwind, panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0);
- PanicFmt, panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
- PanicDisplay, panic_display, panic_display, Target::Fn, GenericRequirement::None;
- ConstPanicFmt, const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
- PanicBoundsCheck, panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
- PanicInfo, panic_info, panic_info, Target::Struct, GenericRequirement::None;
- PanicLocation, panic_location, panic_location, Target::Struct, GenericRequirement::None;
- PanicImpl, panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
- PanicCannotUnwind, panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
+ Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
+ PanicNounwind, sym::panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0);
+ PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
+ PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None;
+ ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
+ PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
+ PanicMisalignedPointerDereference, sym::panic_misaligned_pointer_dereference, panic_misaligned_pointer_dereference_fn, Target::Fn, GenericRequirement::Exact(0);
+ PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None;
+ PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None;
+ PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
+ PanicCannotUnwind, sym::panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
/// libstd panic entry point. Necessary for const eval to be able to catch it
- BeginPanic, begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;
+ BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;
- ExchangeMalloc, exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
- BoxFree, box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
- DropInPlace, drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
- AllocLayout, alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
+ // Lang items needed for `format_args!()`.
+ FormatAlignment, sym::format_alignment, format_alignment, Target::Enum, GenericRequirement::None;
+ FormatArgument, sym::format_argument, format_argument, Target::Struct, GenericRequirement::None;
+ FormatArguments, sym::format_arguments, format_arguments, Target::Struct, GenericRequirement::None;
+ FormatCount, sym::format_count, format_count, Target::Enum, GenericRequirement::None;
+ FormatPlaceholder, sym::format_placeholder, format_placeholder, Target::Struct, GenericRequirement::None;
+ FormatUnsafeArg, sym::format_unsafe_arg, format_unsafe_arg, Target::Struct, GenericRequirement::None;
- Start, start, start_fn, Target::Fn, GenericRequirement::Exact(1);
+ ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
+ BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
+ DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
+ AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
- EhPersonality, eh_personality, eh_personality, Target::Fn, GenericRequirement::None;
- EhCatchTypeinfo, eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None;
+ Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
- OwnedBox, owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1);
+ EhPersonality, sym::eh_personality, eh_personality, Target::Fn, GenericRequirement::None;
+ EhCatchTypeinfo, sym::eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None;
- PhantomData, phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
+ OwnedBox, sym::owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1);
- ManuallyDrop, manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
+ PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
- MaybeUninit, maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
+ ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
+
+ MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
/// Align offset for stride != 1; must not panic.
- AlignOffset, align_offset, align_offset_fn, Target::Fn, GenericRequirement::None;
+ AlignOffset, sym::align_offset, align_offset_fn, Target::Fn, GenericRequirement::None;
- Termination, termination, termination, Target::Trait, GenericRequirement::None;
+ Termination, sym::termination, termination, Target::Trait, GenericRequirement::None;
- Try, Try, try_trait, Target::Trait, GenericRequirement::None;
+ Try, sym::Try, try_trait, Target::Trait, GenericRequirement::None;
- Tuple, tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0);
+ Tuple, sym::tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0);
- SliceLen, slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
+ SliceLen, sym::slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
// Language items from AST lowering
- TryTraitFromResidual, from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- TryTraitFromOutput, from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- TryTraitBranch, branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- TryTraitFromYeet, from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
+ TryTraitFromResidual, sym::from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
+
+ PointerLike, sym::pointer_like, pointer_like, Target::Trait, GenericRequirement::Exact(0);
- PointerSized, pointer_sized, pointer_sized, Target::Trait, GenericRequirement::Exact(0);
+ ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
- Poll, Poll, poll, Target::Enum, GenericRequirement::None;
- PollReady, Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
- PollPending, Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
+ Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None;
+ PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
+ PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
// FIXME(swatinem): the following lang items are used for async lowering and
// should become obsolete eventually.
- ResumeTy, ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
- GetContext, get_context, get_context_fn, Target::Fn, GenericRequirement::None;
-
- Context, Context, context, Target::Struct, GenericRequirement::None;
- FuturePoll, poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
+ GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None;
- FromFrom, from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ Context, sym::Context, context, Target::Struct, GenericRequirement::None;
+ FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- OptionSome, Some, option_some_variant, Target::Variant, GenericRequirement::None;
- OptionNone, None, option_none_variant, Target::Variant, GenericRequirement::None;
+ Option, sym::Option, option_type, Target::Enum, GenericRequirement::None;
+ OptionSome, sym::Some, option_some_variant, Target::Variant, GenericRequirement::None;
+ OptionNone, sym::None, option_none_variant, Target::Variant, GenericRequirement::None;
- ResultOk, Ok, result_ok_variant, Target::Variant, GenericRequirement::None;
- ResultErr, Err, result_err_variant, Target::Variant, GenericRequirement::None;
+ ResultOk, sym::Ok, result_ok_variant, Target::Variant, GenericRequirement::None;
+ ResultErr, sym::Err, result_err_variant, Target::Variant, GenericRequirement::None;
- ControlFlowContinue, Continue, cf_continue_variant, Target::Variant, GenericRequirement::None;
- ControlFlowBreak, Break, cf_break_variant, Target::Variant, GenericRequirement::None;
+ ControlFlowContinue, sym::Continue, cf_continue_variant, Target::Variant, GenericRequirement::None;
+ ControlFlowBreak, sym::Break, cf_break_variant, Target::Variant, GenericRequirement::None;
- IntoFutureIntoFuture, into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- IntoIterIntoIter, into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- IteratorNext, next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
+ IntoFutureIntoFuture, sym::into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
- PinNewUnchecked, new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
+ PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
- RangeFrom, RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None;
- RangeFull, RangeFull, range_full_struct, Target::Struct, GenericRequirement::None;
- RangeInclusiveStruct, RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None;
- RangeInclusiveNew, range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None;
- Range, Range, range_struct, Target::Struct, GenericRequirement::None;
- RangeToInclusive, RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
- RangeTo, RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
+ RangeFrom, sym::RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None;
+ RangeFull, sym::RangeFull, range_full_struct, Target::Struct, GenericRequirement::None;
+ RangeInclusiveStruct, sym::RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None;
+ RangeInclusiveNew, sym::range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None;
+ Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
+ RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
+ RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
- String, String, string, Target::Struct, GenericRequirement::None;
+ String, sym::String, string, Target::Struct, GenericRequirement::None;
+ CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/layout.rs b/src/tools/rust-analyzer/crates/hir-def/src/layout.rs
deleted file mode 100644
index 49b1190ad..000000000
--- a/src/tools/rust-analyzer/crates/hir-def/src/layout.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-//! Definitions needed for computing data layout of types.
-
-use std::cmp;
-
-use la_arena::{Idx, RawIdx};
-pub use rustc_abi::{
- Abi, AbiAndPrefAlign, AddressSpace, Align, Endian, FieldsShape, Integer, IntegerType,
- LayoutCalculator, Niche, Primitive, ReprFlags, ReprOptions, Scalar, Size, StructKind,
- TargetDataLayout, TargetDataLayoutErrors, WrappingRange,
-};
-
-use crate::LocalEnumVariantId;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct RustcEnumVariantIdx(pub LocalEnumVariantId);
-
-impl rustc_index::vec::Idx for RustcEnumVariantIdx {
- fn new(idx: usize) -> Self {
- RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32)))
- }
-
- fn index(self) -> usize {
- u32::from(self.0.into_raw()) as usize
- }
-}
-
-pub type Layout = rustc_abi::LayoutS<RustcEnumVariantIdx>;
-pub type TagEncoding = rustc_abi::TagEncoding<RustcEnumVariantIdx>;
-pub type Variants = rustc_abi::Variants<RustcEnumVariantIdx>;
-
-pub trait IntegerExt {
- fn repr_discr(
- dl: &TargetDataLayout,
- repr: &ReprOptions,
- min: i128,
- max: i128,
- ) -> Result<(Integer, bool), LayoutError>;
-}
-
-impl IntegerExt for Integer {
- /// Finds the appropriate Integer type and signedness for the given
- /// signed discriminant range and `#[repr]` attribute.
- /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
- /// that shouldn't affect anything, other than maybe debuginfo.
- fn repr_discr(
- dl: &TargetDataLayout,
- repr: &ReprOptions,
- min: i128,
- max: i128,
- ) -> Result<(Integer, bool), LayoutError> {
- // Theoretically, negative values could be larger in unsigned representation
- // than the unsigned representation of the signed minimum. However, if there
- // are any negative values, the only valid unsigned representation is u128
- // which can fit all i128 values, so the result remains unaffected.
- let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
- let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
-
- if let Some(ity) = repr.int {
- let discr = Integer::from_attr(dl, ity);
- let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
- if discr < fit {
- return Err(LayoutError::UserError(
- "Integer::repr_discr: `#[repr]` hint too small for \
- discriminant range of enum "
- .to_string(),
- ));
- }
- return Ok((discr, ity.is_signed()));
- }
-
- let at_least = if repr.c() {
- // This is usually I32, however it can be different on some platforms,
- // notably hexagon and arm-none/thumb-none
- dl.c_enum_min_size
- } else {
- // repr(Rust) enums try to be as small as possible
- Integer::I8
- };
-
- // If there are no negative values, we can use the unsigned fit.
- Ok(if min >= 0 {
- (cmp::max(unsigned_fit, at_least), false)
- } else {
- (cmp::max(signed_fit, at_least), true)
- })
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum LayoutError {
- UserError(String),
- SizeOverflow,
- TargetLayoutNotAvailable,
- HasPlaceholder,
- NotImplemented,
- Unknown,
-}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 8c2e93f09..9d8b57a0d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -18,24 +18,23 @@ pub mod db;
pub mod attr;
pub mod path;
-pub mod type_ref;
pub mod builtin_type;
-pub mod builtin_attr;
pub mod per_ns;
pub mod item_scope;
+pub mod lower;
+pub mod expander;
+
pub mod dyn_map;
-pub mod keys;
pub mod item_tree;
-pub mod adt;
pub mod data;
pub mod generics;
pub mod lang_item;
-pub mod layout;
-pub mod expr;
+pub mod hir;
+pub use self::hir::type_ref;
pub mod body;
pub mod resolver;
@@ -49,6 +48,9 @@ pub mod visibility;
pub mod find_path;
pub mod import_map;
+pub use rustc_abi as layout;
+use triomphe::Arc;
+
#[cfg(test)]
mod test_db;
#[cfg(test)]
@@ -57,7 +59,7 @@ mod pretty;
use std::{
hash::{Hash, Hasher},
- sync::Arc,
+ panic::{RefUnwindSafe, UnwindSafe},
};
use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind};
@@ -67,11 +69,12 @@ use hir_expand::{
builtin_attr_macro::BuiltinAttrExpander,
builtin_derive_macro::BuiltinDeriveExpander,
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
- eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
+ db::ExpandDatabase,
+ eager::expand_eager_macro_input,
hygiene::Hygiene,
proc_macro::ProcMacroExpander,
- AstId, ExpandError, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
- MacroDefKind, UnresolvedMacro,
+ AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
+ MacroDefId, MacroDefKind, UnresolvedMacro,
};
use item_tree::ExternBlock;
use la_arena::Idx;
@@ -82,14 +85,54 @@ use syntax::ast;
use ::tt::token_id as tt;
use crate::{
- adt::VariantData,
builtin_type::BuiltinType,
+ data::adt::VariantData,
item_tree::{
- Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
- Static, Struct, Trait, TraitAlias, TypeAlias, Union,
+ Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, Static,
+ Struct, Trait, TraitAlias, TypeAlias, Union,
},
};
+/// A `ModuleId` that is always a crate's root module.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct CrateRootModuleId {
+ krate: CrateId,
+}
+
+impl CrateRootModuleId {
+ pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
+ db.crate_def_map(self.krate)
+ }
+
+ pub fn krate(self) -> CrateId {
+ self.krate
+ }
+}
+
+impl From<CrateRootModuleId> for ModuleId {
+ fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
+ ModuleId { krate, block: None, local_id: DefMap::ROOT }
+ }
+}
+
+impl From<CrateRootModuleId> for ModuleDefId {
+ fn from(value: CrateRootModuleId) -> Self {
+ ModuleDefId::ModuleId(value.into())
+ }
+}
+
+impl TryFrom<ModuleId> for CrateRootModuleId {
+ type Error = ();
+
+ fn try_from(ModuleId { krate, block, local_id }: ModuleId) -> Result<Self, Self::Error> {
+ if block.is_none() && local_id == DefMap::ROOT {
+ Ok(CrateRootModuleId { krate })
+ } else {
+ Err(())
+ }
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ModuleId {
krate: CrateId,
@@ -104,13 +147,7 @@ pub struct ModuleId {
impl ModuleId {
pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
match self.block {
- Some(block) => {
- db.block_def_map(block).unwrap_or_else(|| {
- // NOTE: This should be unreachable - all `ModuleId`s come from their `DefMap`s,
- // so the `DefMap` here must exist.
- unreachable!("no `block_def_map` for `ModuleId` {:?}", self);
- })
- }
+ Some(block) => db.block_def_map(block),
None => db.crate_def_map(self.krate),
}
}
@@ -236,7 +273,7 @@ pub struct EnumVariantId {
pub local_id: LocalEnumVariantId,
}
-pub type LocalEnumVariantId = Idx<adt::EnumVariantData>;
+pub type LocalEnumVariantId = Idx<data::adt::EnumVariantData>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FieldId {
@@ -244,7 +281,7 @@ pub struct FieldId {
pub local_id: LocalFieldId,
}
-pub type LocalFieldId = Idx<adt::FieldData>;
+pub type LocalFieldId = Idx<data::adt::FieldData>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ConstId(salsa::InternId);
@@ -317,8 +354,7 @@ impl_intern!(MacroRulesId, MacroRulesLoc, intern_macro_rules, lookup_intern_macr
pub struct ProcMacroId(salsa::InternId);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ProcMacroLoc {
- // FIXME: this should be a crate? or just a crate-root module
- pub container: ModuleId,
+ pub container: CrateRootModuleId,
pub id: ItemTreeId<Function>,
pub expander: ProcMacroExpander,
pub kind: ProcMacroKind,
@@ -478,16 +514,228 @@ impl_from!(
for ModuleDefId
);
+/// Id of the anonymous const block expression and patterns. This is very similar to `ClosureId` and
+/// shouldn't be a `DefWithBodyId` since its type inference is dependent on its parent.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub struct ConstBlockId(salsa::InternId);
+impl_intern!(ConstBlockId, ConstBlockLoc, intern_anonymous_const, lookup_intern_anonymous_const);
+
+#[derive(Debug, Hash, PartialEq, Eq, Clone)]
+pub struct ConstBlockLoc {
+ /// The parent of the anonymous const block.
+ pub parent: DefWithBodyId,
+ /// The root expression of this const block in the parent body.
+ pub root: hir::ExprId,
+}
+
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub enum TypeOwnerId {
+ FunctionId(FunctionId),
+ StaticId(StaticId),
+ ConstId(ConstId),
+ InTypeConstId(InTypeConstId),
+ AdtId(AdtId),
+ TraitId(TraitId),
+ TraitAliasId(TraitAliasId),
+ TypeAliasId(TypeAliasId),
+ ImplId(ImplId),
+ EnumVariantId(EnumVariantId),
+ // FIXME(const-generic-body): ModuleId should not be a type owner. This needs to be fixed to make `TypeOwnerId` actually
+ // useful for assigning ids to in type consts.
+ ModuleId(ModuleId),
+}
+
+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::InTypeConstId(_) | TypeOwnerId::ModuleId(_) | TypeOwnerId::StaticId(_) => {
+ return None
+ }
+ })
+ }
+}
+
+impl_from!(
+ FunctionId,
+ StaticId,
+ ConstId,
+ InTypeConstId,
+ AdtId,
+ TraitId,
+ TraitAliasId,
+ TypeAliasId,
+ ImplId,
+ EnumVariantId,
+ ModuleId
+ for TypeOwnerId
+);
+
+// Every `DefWithBodyId` is a type owner, since bodies can contain type (e.g. `{ let x: 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(),
+ }
+ }
+}
+
+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(),
+ }
+ }
+}
+
+// FIXME: This should not be a thing
+/// A thing that we want to store in interned ids, but we don't know its type in `hir-def`. This is
+/// currently only used in `InTypeConstId` for storing the type (which has type `Ty` defined in
+/// the `hir-ty` crate) of the constant in its id, which is a temporary hack so we may want
+/// to remove this after removing that.
+pub trait OpaqueInternableThing:
+ std::any::Any + std::fmt::Debug + Sync + Send + UnwindSafe + RefUnwindSafe
+{
+ fn as_any(&self) -> &dyn std::any::Any;
+ fn box_any(&self) -> Box<dyn std::any::Any>;
+ fn dyn_hash(&self, state: &mut dyn Hasher);
+ fn dyn_eq(&self, other: &dyn OpaqueInternableThing) -> bool;
+ fn dyn_clone(&self) -> Box<dyn OpaqueInternableThing>;
+}
+
+impl Hash for dyn OpaqueInternableThing {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.dyn_hash(state);
+ }
+}
+
+impl PartialEq for dyn OpaqueInternableThing {
+ fn eq(&self, other: &Self) -> bool {
+ self.dyn_eq(other)
+ }
+}
+
+impl Eq for dyn OpaqueInternableThing {}
+
+impl Clone for Box<dyn OpaqueInternableThing> {
+ fn clone(&self) -> Self {
+ self.dyn_clone()
+ }
+}
+
+// FIXME(const-generic-body): Use an stable id for in type consts.
+//
+// The current id uses `AstId<ast::ConstArg>` which will be changed by every change in the code. Ideally
+// we should use an id which is relative to the type owner, so that every change will only invalidate the
+// id if it happens inside of the type owner.
+//
+// The solution probably is to have some query on `TypeOwnerId` to traverse its constant children and store
+// their `AstId` in a list (vector or arena), and use the index of that list in the id here. That query probably
+// needs name resolution, and might go far and handles the whole path lowering or type lowering for a `TypeOwnerId`.
+//
+// Whatever path the solution takes, it should answer 3 questions at the same time:
+// * Is the id stable enough?
+// * How to find a constant id using an ast node / position in the source code? This is needed when we want to
+// provide ide functionalities inside an in type const (which we currently don't support) e.g. go to definition
+// for a local defined there. A complex id might have some trouble in this reverse mapping.
+// * How to find the return type of a constant using its id? We have this data when we are doing type lowering
+// and the name of the struct that contains this constant is resolved, so a query that only traverses the
+// type owner by its syntax tree might have a hard time here.
+
+/// A constant in a type as a substitution for const generics (like `Foo<{ 2 + 2 }>`) or as an array
+/// length (like `[u8; 2 + 2]`). These constants are body owner and are a variant of `DefWithBodyId`. These
+/// are not called `AnonymousConstId` to prevent confusion with [`ConstBlockId`].
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub struct InTypeConstId(salsa::InternId);
+impl_intern!(InTypeConstId, InTypeConstLoc, intern_in_type_const, lookup_intern_in_type_const);
+
+#[derive(Debug, Hash, Eq, Clone)]
+pub struct InTypeConstLoc {
+ pub id: AstId<ast::ConstArg>,
+ /// The thing this const arg appears in
+ pub owner: TypeOwnerId,
+ pub thing: Box<dyn OpaqueInternableThing>,
+}
+
+impl PartialEq for InTypeConstLoc {
+ fn eq(&self, other: &Self) -> bool {
+ self.id == other.id && self.owner == other.owner && &*self.thing == &*other.thing
+ }
+}
+
+impl InTypeConstId {
+ pub fn source(&self, db: &dyn db::DefDatabase) -> ast::ConstArg {
+ let src = self.lookup(db).id;
+ let file_id = src.file_id;
+ let root = &db.parse_or_expand(file_id);
+ db.ast_id_map(file_id).get(src.value).to_node(root)
+ }
+}
+
+/// A constant, which might appears as a const item, an annonymous const block in expressions
+/// or patterns, or as a constant in types with const generics.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum GeneralConstId {
+ ConstId(ConstId),
+ ConstBlockId(ConstBlockId),
+ InTypeConstId(InTypeConstId),
+}
+
+impl_from!(ConstId, ConstBlockId, InTypeConstId for GeneralConstId);
+
+impl GeneralConstId {
+ pub fn generic_def(self, db: &dyn db::DefDatabase) -> Option<GenericDefId> {
+ match self {
+ GeneralConstId::ConstId(it) => Some(it.into()),
+ GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(),
+ GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(),
+ }
+ }
+
+ pub fn name(self, db: &dyn db::DefDatabase) -> String {
+ match self {
+ GeneralConstId::ConstId(const_id) => db
+ .const_data(const_id)
+ .name
+ .as_ref()
+ .and_then(|x| x.as_str())
+ .unwrap_or("_")
+ .to_owned(),
+ GeneralConstId::ConstBlockId(id) => format!("{{anonymous const {id:?}}}"),
+ GeneralConstId::InTypeConstId(id) => format!("{{in type const {id:?}}}"),
+ }
+ }
+}
+
/// The defs which have a body.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DefWithBodyId {
FunctionId(FunctionId),
StaticId(StaticId),
ConstId(ConstId),
+ InTypeConstId(InTypeConstId),
VariantId(EnumVariantId),
}
-impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId);
+impl_from!(FunctionId, ConstId, StaticId, InTypeConstId for DefWithBodyId);
impl From<EnumVariantId> for DefWithBodyId {
fn from(id: EnumVariantId) -> Self {
@@ -502,6 +750,9 @@ impl DefWithBodyId {
DefWithBodyId::StaticId(_) => None,
DefWithBodyId::ConstId(c) => Some(c.into()),
DefWithBodyId::VariantId(c) => Some(c.into()),
+ // FIXME: stable rust doesn't allow generics in constants, but we should
+ // use `TypeOwnerId::as_generic_def_id` when it does.
+ DefWithBodyId::InTypeConstId(_) => None,
}
}
}
@@ -691,29 +942,37 @@ impl HasModule for MacroId {
match self {
MacroId::MacroRulesId(it) => it.lookup(db).container,
MacroId::Macro2Id(it) => it.lookup(db).container,
- MacroId::ProcMacroId(it) => it.lookup(db).container,
+ MacroId::ProcMacroId(it) => it.lookup(db).container.into(),
}
}
}
-impl HasModule for DefWithBodyId {
+impl HasModule for TypeOwnerId {
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
match self {
- DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
- DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
- DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
- DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
+ 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,
}
}
}
-impl DefWithBodyId {
- pub fn as_mod_item(self, db: &dyn db::DefDatabase) -> ModItem {
+impl HasModule for DefWithBodyId {
+ fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
match self {
- DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
- DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
- DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
- DefWithBodyId::VariantId(it) => it.parent.lookup(db).id.value.into(),
+ DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
+ DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
+ DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
+ DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
+ DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db),
}
}
}
@@ -799,52 +1058,43 @@ impl AttrDefId {
pub trait AsMacroCall {
fn as_call_id(
&self,
- db: &dyn db::DefDatabase,
+ db: &dyn ExpandDatabase,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
) -> Option<MacroCallId> {
- self.as_call_id_with_errors(db, krate, resolver, &mut |_| ()).ok()?.ok()
+ self.as_call_id_with_errors(db, krate, resolver).ok()?.value
}
fn as_call_id_with_errors(
&self,
- db: &dyn db::DefDatabase,
+ db: &dyn ExpandDatabase,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
- error_sink: &mut dyn FnMut(ExpandError),
- ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro>;
+ ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
}
impl AsMacroCall for InFile<&ast::MacroCall> {
fn as_call_id_with_errors(
&self,
- db: &dyn db::DefDatabase,
+ db: &dyn ExpandDatabase,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
- mut error_sink: &mut dyn FnMut(ExpandError),
- ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
+ ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
- let h = Hygiene::new(db.upcast(), self.file_id);
- let path =
- self.value.path().and_then(|path| path::ModPath::from_src(db.upcast(), path, &h));
-
- let path = match error_sink
- .option(path, || ExpandError::Other("malformed macro invocation".into()))
- {
- Ok(path) => path,
- Err(error) => {
- return Ok(Err(error));
- }
+ let h = Hygiene::new(db, self.file_id);
+ let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
+
+ let Some(path) = path else {
+ return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation")));
};
- macro_call_as_call_id(
+ macro_call_as_call_id_(
db,
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
expands_to,
krate,
resolver,
- error_sink,
)
}
}
@@ -863,26 +1113,37 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
}
fn macro_call_as_call_id(
- db: &dyn db::DefDatabase,
+ db: &dyn ExpandDatabase,
call: &AstIdWithPath<ast::MacroCall>,
expand_to: ExpandTo,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
- error_sink: &mut dyn FnMut(ExpandError),
-) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
+) -> Result<Option<MacroCallId>, UnresolvedMacro> {
+ macro_call_as_call_id_(db, call, expand_to, krate, resolver).map(|res| res.value)
+}
+
+fn macro_call_as_call_id_(
+ db: &dyn ExpandDatabase,
+ call: &AstIdWithPath<ast::MacroCall>,
+ expand_to: ExpandTo,
+ krate: CrateId,
+ resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
+) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
let def =
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
let res = if let MacroDefKind::BuiltInEager(..) = def.kind {
- let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast()));
-
- expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver, error_sink)?
+ let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db));
+ expand_eager_macro_input(db, krate, macro_call, def, &resolver)?
} else {
- Ok(def.as_lazy_macro(
- db.upcast(),
- krate,
- MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
- ))
+ ExpandResult {
+ value: Some(def.as_lazy_macro(
+ db,
+ krate,
+ MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
+ )),
+ err: None,
+ }
};
Ok(res)
}
@@ -986,16 +1247,15 @@ fn attr_macro_as_call_id(
macro_attr: &Attr,
krate: CrateId,
def: MacroDefId,
- is_derive: bool,
) -> MacroCallId {
let arg = match macro_attr.input.as_deref() {
- Some(AttrInput::TokenTree(tt, map)) => (
+ Some(AttrInput::TokenTree(tt)) => (
{
- let mut tt = tt.clone();
+ let mut tt = tt.0.clone();
tt.delimiter = tt::Delimiter::UNSPECIFIED;
tt
},
- map.clone(),
+ tt.1.clone(),
),
_ => (tt::Subtree::empty(), Default::default()),
};
@@ -1007,7 +1267,6 @@ fn attr_macro_as_call_id(
ast_id: item_attr.ast_id,
attr_args: Arc::new(arg),
invoc_attr_index: macro_attr.id,
- is_derive,
},
)
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
new file mode 100644
index 000000000..af623fd0e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
@@ -0,0 +1,45 @@
+//! Context for lowering paths.
+use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile};
+use once_cell::unsync::OnceCell;
+use syntax::ast;
+use triomphe::Arc;
+
+use crate::{db::DefDatabase, path::Path};
+
+pub struct LowerCtx<'a> {
+ pub db: &'a dyn DefDatabase,
+ hygiene: Hygiene,
+ ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
+}
+
+impl<'a> LowerCtx<'a> {
+ pub fn new(db: &'a dyn DefDatabase, hygiene: &Hygiene, file_id: HirFileId) -> Self {
+ LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: Some((file_id, OnceCell::new())) }
+ }
+
+ pub fn with_file_id(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
+ LowerCtx {
+ db,
+ hygiene: Hygiene::new(db.upcast(), file_id),
+ ast_id_map: Some((file_id, OnceCell::new())),
+ }
+ }
+
+ pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
+ LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
+ }
+
+ pub(crate) fn hygiene(&self) -> &Hygiene {
+ &self.hygiene
+ }
+
+ pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
+ Path::from_src(ast, self)
+ }
+
+ pub(crate) fn ast_id<N: syntax::AstNode>(&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)))
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
index fafcde25a..f41f97190 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -16,7 +16,7 @@ struct Foo;
#[derive(Copy)]
struct Foo;
-impl < > core::marker::Copy for Foo< > {}"#]],
+impl < > core::marker::Copy for Foo< > where {}"#]],
);
}
@@ -41,7 +41,7 @@ macro Copy {}
#[derive(Copy)]
struct Foo;
-impl < > crate ::marker::Copy for Foo< > {}"#]],
+impl < > crate ::marker::Copy for Foo< > where {}"#]],
);
}
@@ -57,7 +57,7 @@ struct Foo<A, B>;
#[derive(Copy)]
struct Foo<A, B>;
-impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
+impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
);
}
@@ -74,7 +74,7 @@ struct Foo<A, B, 'a, 'b>;
#[derive(Copy)]
struct Foo<A, B, 'a, 'b>;
-impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
+impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
);
}
@@ -84,13 +84,93 @@ fn test_clone_expand() {
r#"
//- minicore: derive, clone
#[derive(Clone)]
-struct Foo<A, B>;
+enum Command<A, B> {
+ Move { x: A, y: B },
+ Do(&'static str),
+ Jump,
+}
"#,
expect![[r#"
#[derive(Clone)]
-struct Foo<A, B>;
+enum Command<A, B> {
+ Move { x: A, y: B },
+ Do(&'static str),
+ Jump,
+}
+
+impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Command<A, B, > where {
+ fn clone(&self ) -> Self {
+ match self {
+ Command::Move {
+ x: x, y: y,
+ }
+ =>Command::Move {
+ x: x.clone(), y: y.clone(),
+ }
+ , Command::Do(f0, )=>Command::Do(f0.clone(), ), Command::Jump=>Command::Jump,
+ }
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_clone_expand_with_associated_types() {
+ check(
+ r#"
+//- minicore: derive, clone
+trait Trait {
+ type InWc;
+ type InFieldQualified;
+ type InFieldShorthand;
+ type InGenericArg;
+}
+trait Marker {}
+struct Vec<T>(T);
+
+#[derive(Clone)]
+struct Foo<T: Trait>
+where
+ <T as Trait>::InWc: Marker,
+{
+ qualified: <T as Trait>::InFieldQualified,
+ shorthand: T::InFieldShorthand,
+ generic: Vec<T::InGenericArg>,
+}
+"#,
+ expect![[r#"
+trait Trait {
+ type InWc;
+ type InFieldQualified;
+ type InFieldShorthand;
+ type InGenericArg;
+}
+trait Marker {}
+struct Vec<T>(T);
+
+#[derive(Clone)]
+struct Foo<T: Trait>
+where
+ <T as Trait>::InWc: Marker,
+{
+ qualified: <T as Trait>::InFieldQualified,
+ shorthand: T::InFieldShorthand,
+ generic: Vec<T::InGenericArg>,
+}
-impl <T0: core::clone::Clone, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
+impl <T: core::clone::Clone, > core::clone::Clone for Foo<T, > where T: Trait, T::InFieldShorthand: core::clone::Clone, T::InGenericArg: core::clone::Clone, {
+ fn clone(&self ) -> Self {
+ match self {
+ Foo {
+ qualified: qualified, shorthand: shorthand, generic: generic,
+ }
+ =>Foo {
+ qualified: qualified.clone(), shorthand: shorthand.clone(), generic: generic.clone(),
+ }
+ ,
+ }
+ }
+}"#]],
);
}
@@ -106,6 +186,270 @@ struct Foo<const X: usize, T>(u32);
#[derive(Clone)]
struct Foo<const X: usize, T>(u32);
-impl <const T0: usize, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
+impl <const X: usize, T: core::clone::Clone, > core::clone::Clone for Foo<X, T, > where {
+ fn clone(&self ) -> Self {
+ match self {
+ Foo(f0, )=>Foo(f0.clone(), ),
+ }
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_default_expand() {
+ check(
+ r#"
+//- minicore: derive, default
+#[derive(Default)]
+struct Foo {
+ field1: i32,
+ field2: (),
+}
+#[derive(Default)]
+enum Bar {
+ Foo(u8),
+ #[default]
+ Bar,
+}
+"#,
+ expect![[r#"
+#[derive(Default)]
+struct Foo {
+ field1: i32,
+ field2: (),
+}
+#[derive(Default)]
+enum Bar {
+ Foo(u8),
+ #[default]
+ Bar,
+}
+
+impl < > core::default::Default for Foo< > where {
+ fn default() -> Self {
+ Foo {
+ field1: core::default::Default::default(), field2: core::default::Default::default(),
+ }
+ }
+}
+impl < > core::default::Default for Bar< > where {
+ fn default() -> Self {
+ Bar::Bar
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_partial_eq_expand() {
+ check(
+ r#"
+//- minicore: derive, eq
+#[derive(PartialEq, Eq)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+"#,
+ expect![[r#"
+#[derive(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(
+ r#"
+//- minicore: derive, ord
+#[derive(PartialOrd, Ord)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+"#,
+ expect![[r#"
+#[derive(PartialOrd, Ord)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+
+impl < > core::cmp::PartialOrd for Command< > where {
+ fn partial_cmp(&self , other: &Self ) -> core::option::Option::Option<core::cmp::Ordering> {
+ match core::intrinsics::discriminant_value(self ).partial_cmp(&core::intrinsics::discriminant_value(other)) {
+ core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ match (self , other) {
+ (Command::Move {
+ x: x_self, y: y_self,
+ }
+ , Command::Move {
+ x: x_other, y: y_other,
+ }
+ )=>match x_self.partial_cmp(&x_other) {
+ core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ match y_self.partial_cmp(&y_other) {
+ core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ core::option::Option::Some(core::cmp::Ordering::Equal)
+ }
+ c=>return c,
+ }
+ }
+ c=>return c,
+ }
+ , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) {
+ core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ core::option::Option::Some(core::cmp::Ordering::Equal)
+ }
+ c=>return c,
+ }
+ , (Command::Jump, Command::Jump)=>core::option::Option::Some(core::cmp::Ordering::Equal), _unused=>core::option::Option::Some(core::cmp::Ordering::Equal)
+ }
+ }
+ c=>return c,
+ }
+ }
+}
+impl < > core::cmp::Ord for Command< > where {
+ fn cmp(&self , other: &Self ) -> core::cmp::Ordering {
+ match core::intrinsics::discriminant_value(self ).cmp(&core::intrinsics::discriminant_value(other)) {
+ core::cmp::Ordering::Equal=> {
+ match (self , other) {
+ (Command::Move {
+ x: x_self, y: y_self,
+ }
+ , Command::Move {
+ x: x_other, y: y_other,
+ }
+ )=>match x_self.cmp(&x_other) {
+ core::cmp::Ordering::Equal=> {
+ match y_self.cmp(&y_other) {
+ core::cmp::Ordering::Equal=> {
+ core::cmp::Ordering::Equal
+ }
+ c=>return c,
+ }
+ }
+ c=>return c,
+ }
+ , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) {
+ core::cmp::Ordering::Equal=> {
+ core::cmp::Ordering::Equal
+ }
+ c=>return c,
+ }
+ , (Command::Jump, Command::Jump)=>core::cmp::Ordering::Equal, _unused=>core::cmp::Ordering::Equal
+ }
+ }
+ c=>return c,
+ }
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_hash_expand() {
+ check(
+ r#"
+//- minicore: derive, hash
+use core::hash::Hash;
+
+#[derive(Hash)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+"#,
+ expect![[r#"
+use core::hash::Hash;
+
+#[derive(Hash)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+
+impl < > core::hash::Hash for Command< > where {
+ fn hash<H: core::hash::Hasher>(&self , ra_expand_state: &mut H) {
+ core::mem::discriminant(self ).hash(ra_expand_state);
+ match self {
+ Command::Move {
+ x: x, y: y,
+ }
+ => {
+ x.hash(ra_expand_state);
+ y.hash(ra_expand_state);
+ }
+ , Command::Do(f0, )=> {
+ f0.hash(ra_expand_state);
+ }
+ , Command::Jump=> {}
+ ,
+ }
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_debug_expand() {
+ check(
+ r#"
+//- minicore: derive, fmt
+use core::fmt::Debug;
+
+#[derive(Debug)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+"#,
+ expect![[r#"
+use core::fmt::Debug;
+
+#[derive(Debug)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+
+impl < > core::fmt::Debug for Command< > where {
+ fn fmt(&self , f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ match self {
+ Command::Move {
+ x: x, y: y,
+ }
+ =>f.debug_struct("Move").field("x", &x).field("y", &y).finish(), Command::Do(f0, )=>f.debug_tuple("Do").field(&f0).finish(), Command::Jump=>f.write_str("Jump"),
+ }
+ }
+}"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 5fbd1789b..07d9baa58 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -13,12 +13,12 @@ macro_rules! column {() => {}}
fn main() { column!(); }
"#,
- expect![[r##"
+ expect![[r#"
#[rustc_builtin_macro]
macro_rules! column {() => {}}
-fn main() { 0; }
-"##]],
+fn main() { 0 as u32; }
+"#]],
);
}
@@ -31,12 +31,12 @@ macro_rules! line {() => {}}
fn main() { line!() }
"#,
- expect![[r##"
+ expect![[r#"
#[rustc_builtin_macro]
macro_rules! line {() => {}}
-fn main() { 0 }
-"##]],
+fn main() { 0 as u32 }
+"#]],
);
}
@@ -79,7 +79,7 @@ fn main() { env!("TEST_ENV_VAR"); }
#[rustc_builtin_macro]
macro_rules! env {() => {}}
-fn main() { "__RA_UNIMPLEMENTED__"; }
+fn main() { "UNRESOLVED_ENV_VAR"; }
"##]],
);
}
@@ -97,7 +97,7 @@ fn main() { option_env!("TEST_ENV_VAR"); }
#[rustc_builtin_macro]
macro_rules! option_env {() => {}}
-fn main() { $crate::option::Option::None:: < &str>; }
+fn main() { ::core::option::Option::None:: < &str>; }
"#]],
);
}
@@ -193,7 +193,7 @@ fn main() {
format_args!("{} {:?}", arg1(a, b, c), arg2);
}
"#,
- expect![[r#"
+ expect![[r##"
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ });
@@ -201,9 +201,47 @@ macro_rules! format_args {
}
fn main() {
- $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(arg2), $crate::fmt::Display::fmt), ]);
+ ::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), ]);
}
-"#]],
+"##]],
+ );
+}
+
+#[test]
+fn regression_15002() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ format_args!(x = 2);
+ format_args!(x =);
+ format_args!(x =, x = 2);
+ format_args!("{}", x =);
+ format_args!(=, "{}", x =);
+ format_args!(x = 2, "{}", 5);
+}
+"#,
+ expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+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), ]);
+}
+"##]],
);
}
@@ -221,7 +259,7 @@ fn main() {
format_args!("{} {:?}", a::<A,B>(), b);
}
"#,
- expect![[r#"
+ expect![[r##"
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ });
@@ -229,9 +267,76 @@ macro_rules! format_args {
}
fn main() {
- $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a::<A, B>()), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(b), $crate::fmt::Display::fmt), ]);
+ ::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), ]);
}
-"#]],
+"##]],
+ );
+}
+
+#[test]
+fn test_format_args_expand_with_raw_strings() {
+ check(
+ r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ format_args!(
+ r#"{},mismatch,"{}","{}""#,
+ location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+ mismatch.expected.display(db),
+ mismatch.actual.display(db)
+ );
+}
+"##,
+ expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+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), ]);
+}
+"##]],
+ );
+}
+
+#[test]
+fn test_format_args_expand_eager() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ format_args!(concat!("xxx{}y", "{:?}zzz"), 2, b);
+}
+"#,
+ expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+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), ]);
+}
+"##]],
);
}
@@ -250,7 +355,7 @@ fn main() {
format_args!/*+errors*/("{} {:?}", a.);
}
"#,
- expect![[r#"
+ expect![[r##"
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ });
@@ -259,10 +364,10 @@ macro_rules! format_args {
fn main() {
let _ =
- /* parse error: expected field name or number */
-$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a.), $crate::fmt::Display::fmt), ]);
+ /* 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), ]);
}
-"#]],
+"##]],
);
}
@@ -337,10 +442,6 @@ macro_rules! surprise {
() => { "s" };
}
-macro_rules! stuff {
- ($string:expr) => { concat!($string) };
-}
-
fn main() { concat!(surprise!()); }
"##,
expect![[r##"
@@ -351,10 +452,6 @@ macro_rules! surprise {
() => { "s" };
}
-macro_rules! stuff {
- ($string:expr) => { concat!($string) };
-}
-
fn main() { "s"; }
"##]],
);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 7a3e8c3b0..553ffe3d0 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -4,6 +4,7 @@
mod tt_conversion;
mod matching;
mod meta_syntax;
+mod metavar_expr;
mod regression;
use expect_test::expect;
@@ -98,7 +99,7 @@ fn#19 main#20(#21)#21 {#22
);
}
#[test]
-fn float_field_acces_macro_input() {
+fn float_field_access_macro_input() {
check(
r#"
macro_rules! foo {
@@ -922,7 +923,7 @@ macro_rules! m {
fn bar() -> &'a Baz<u8> {}
-fn bar() -> extern "Rust"fn() -> Ret {}
+fn bar() -> extern "Rust" fn() -> Ret {}
"#]],
);
}
@@ -1293,20 +1294,54 @@ ok!();
}
#[test]
-fn test_vertical_bar_with_pat() {
+fn test_vertical_bar_with_pat_param() {
check(
r#"
-macro_rules! m { (|$pat:pat| ) => { ok!(); } }
+macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
m! { |x| }
"#,
expect![[r#"
-macro_rules! m { (|$pat:pat| ) => { ok!(); } }
+macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
ok!();
"#]],
);
}
#[test]
+fn test_new_std_matches() {
+ check(
+ r#"
+macro_rules! matches {
+ ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
+ match $expression {
+ $pattern $(if $guard)? => true,
+ _ => false
+ }
+ };
+}
+fn main() {
+ matches!(0, 0 | 1 if true);
+}
+ "#,
+ expect![[r#"
+macro_rules! matches {
+ ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
+ match $expression {
+ $pattern $(if $guard)? => true,
+ _ => false
+ }
+ };
+}
+fn main() {
+ match 0 {
+ 0|1 if true =>true , _=>false
+ };
+}
+ "#]],
+ );
+}
+
+#[test]
fn test_dollar_crate_lhs_is_not_meta() {
check(
r#"
@@ -1581,92 +1616,6 @@ struct Foo;
}
#[test]
-fn test_dollar_dollar() {
- check(
- r#"
-macro_rules! register_struct { ($Struct:ident) => {
- macro_rules! register_methods { ($$($method:ident),*) => {
- macro_rules! implement_methods { ($$$$($$val:expr),*) => {
- struct $Struct;
- impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
- }}
- }}
-}}
-
-register_struct!(Foo);
-register_methods!(alpha, beta);
-implement_methods!(1, 2, 3);
-"#,
- expect![[r#"
-macro_rules! register_struct { ($Struct:ident) => {
- macro_rules! register_methods { ($$($method:ident),*) => {
- macro_rules! implement_methods { ($$$$($$val:expr),*) => {
- struct $Struct;
- impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
- }}
- }}
-}}
-
-macro_rules !register_methods {
- ($($method: ident), *) = > {
- macro_rules!implement_methods {
- ($$($val: expr), *) = > {
- struct Foo;
- impl Foo {
- $(fn $method()-> &'static[u32] {
- &[$$($$val), *]
- }
- )*
- }
- }
- }
- }
-}
-macro_rules !implement_methods {
- ($($val: expr), *) = > {
- struct Foo;
- impl Foo {
- fn alpha()-> &'static[u32] {
- &[$($val), *]
- }
- fn beta()-> &'static[u32] {
- &[$($val), *]
- }
- }
- }
-}
-struct Foo;
-impl Foo {
- fn alpha() -> &'static[u32] {
- &[1, 2, 3]
- }
- fn beta() -> &'static[u32] {
- &[1, 2, 3]
- }
-}
-"#]],
- )
-}
-
-#[test]
-fn test_metavar_exprs() {
- check(
- r#"
-macro_rules! m {
- ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
-}
-const _: i32 = m!(a b c);
- "#,
- expect![[r#"
-macro_rules! m {
- ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
-}
-const _: i32 = -0--1--2;
- "#]],
- );
-}
-
-#[test]
fn test_punct_without_space() {
// Puncts are "glued" greedily.
check(
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
index 26f16542c..0909d8c83 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
@@ -33,7 +33,7 @@ m!(&k");
"#,
expect![[r#"
macro_rules! m { ($i:literal) => {}; }
-/* error: Failed to lower macro args to token tree */"#]],
+/* error: invalid token tree */"#]],
);
}
@@ -73,7 +73,7 @@ fn main() {
macro_rules! asi { ($($stmt:stmt)*) => ($($stmt)*); }
fn main() {
- let a = 2let b = 5drop(b-a)println!("{}", a+b)
+ let a = 2 let b = 5 drop(b-a)println!("{}", a+b)
}
"#]],
)
@@ -106,7 +106,6 @@ stringify!(;
#[test]
fn range_patterns() {
- // FIXME: rustc thinks there are three patterns here, not one.
check(
r#"
macro_rules! m {
@@ -118,7 +117,7 @@ m!(.. .. ..);
macro_rules! m {
($($p:pat)*) => (stringify!($($p |)*);)
}
-stringify!(.. .. .. |);
+stringify!(.. | .. | .. |);
"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
new file mode 100644
index 000000000..967b5ad36
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
@@ -0,0 +1,311 @@
+//! Tests for RFC 3086 metavariable expressions.
+
+use expect_test::expect;
+
+use crate::macro_expansion_tests::check;
+
+#[test]
+fn test_dollar_dollar() {
+ check(
+ r#"
+macro_rules! register_struct { ($Struct:ident) => {
+ macro_rules! register_methods { ($$($method:ident),*) => {
+ macro_rules! implement_methods { ($$$$($$val:expr),*) => {
+ struct $Struct;
+ impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
+ }}
+ }}
+}}
+
+register_struct!(Foo);
+register_methods!(alpha, beta);
+implement_methods!(1, 2, 3);
+"#,
+ expect![[r#"
+macro_rules! register_struct { ($Struct:ident) => {
+ macro_rules! register_methods { ($$($method:ident),*) => {
+ macro_rules! implement_methods { ($$$$($$val:expr),*) => {
+ struct $Struct;
+ impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
+ }}
+ }}
+}}
+
+macro_rules !register_methods {
+ ($($method: ident), *) = > {
+ macro_rules!implement_methods {
+ ($$($val: expr), *) = > {
+ struct Foo;
+ impl Foo {
+ $(fn $method()-> &'static[u32] {
+ &[$$($$val), *]
+ }
+ )*
+ }
+ }
+ }
+ }
+}
+macro_rules !implement_methods {
+ ($($val: expr), *) = > {
+ struct Foo;
+ impl Foo {
+ fn alpha()-> &'static[u32] {
+ &[$($val), *]
+ }
+ fn beta()-> &'static[u32] {
+ &[$($val), *]
+ }
+ }
+ }
+}
+struct Foo;
+impl Foo {
+ fn alpha() -> &'static[u32] {
+ &[1, 2, 3]
+ }
+ fn beta() -> &'static[u32] {
+ &[1, 2, 3]
+ }
+}
+"#]],
+ )
+}
+
+#[test]
+fn test_metavar_exprs() {
+ check(
+ r#"
+macro_rules! m {
+ ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
+}
+const _: i32 = m!(a b c);
+ "#,
+ expect![[r#"
+macro_rules! m {
+ ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
+}
+const _: i32 = -0--1--2;
+ "#]],
+ );
+}
+
+#[test]
+fn count_basic() {
+ check(
+ r#"
+macro_rules! m {
+ ($($t:ident),*) => {
+ ${count(t)}
+ }
+}
+
+fn test() {
+ m!();
+ m!(a);
+ m!(a, a);
+}
+"#,
+ expect![[r#"
+macro_rules! m {
+ ($($t:ident),*) => {
+ ${count(t)}
+ }
+}
+
+fn test() {
+ 0;
+ 1;
+ 2;
+}
+"#]],
+ );
+}
+
+#[test]
+fn count_with_depth() {
+ check(
+ r#"
+macro_rules! foo {
+ ($( $( $($t:ident)* ),* );*) => {
+ $(
+ {
+ let depth_none = ${count(t)};
+ let depth_zero = ${count(t, 0)};
+ let depth_one = ${count(t, 1)};
+ }
+ )*
+ }
+}
+
+fn bar() {
+ foo!(
+ a a a, a, a a;
+ a a a
+ )
+}
+"#,
+ expect![[r#"
+macro_rules! foo {
+ ($( $( $($t:ident)* ),* );*) => {
+ $(
+ {
+ let depth_none = ${count(t)};
+ let depth_zero = ${count(t, 0)};
+ let depth_one = ${count(t, 1)};
+ }
+ )*
+ }
+}
+
+fn bar() {
+ {
+ let depth_none = 6;
+ let depth_zero = 3;
+ let depth_one = 6;
+ } {
+ let depth_none = 3;
+ let depth_zero = 1;
+ let depth_one = 3;
+ }
+}
+"#]],
+ );
+}
+
+#[test]
+fn count_depth_out_of_bounds() {
+ check(
+ r#"
+macro_rules! foo {
+ ($($t:ident)*) => { ${count(t, 1)} };
+ ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
+}
+macro_rules! bar {
+ ($($t:ident)*) => { ${count(t, 1024)} };
+ ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
+}
+
+fn test() {
+ foo!(a b);
+ foo!(1 2; 3);
+ bar!(a b);
+ bar!(1 2; 3);
+}
+"#,
+ expect![[r#"
+macro_rules! foo {
+ ($($t:ident)*) => { ${count(t, 1)} };
+ ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
+}
+macro_rules! bar {
+ ($($t:ident)*) => { ${count(t, 1024)} };
+ ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
+}
+
+fn test() {
+ /* error: ${count} out of bounds */;
+ /* error: ${count} out of bounds */;
+ /* error: ${count} out of bounds */;
+ /* error: ${count} out of bounds */;
+}
+"#]],
+ );
+}
+
+#[test]
+fn misplaced_count() {
+ check(
+ r#"
+macro_rules! foo {
+ ($($t:ident)*) => { $(${count(t)})* };
+ ($l:literal) => { ${count(l)} }
+}
+
+fn test() {
+ foo!(a b c);
+ foo!(1);
+}
+"#,
+ expect![[r#"
+macro_rules! foo {
+ ($($t:ident)*) => { $(${count(t)})* };
+ ($l:literal) => { ${count(l)} }
+}
+
+fn test() {
+ /* error: ${count} misplaced */;
+ /* error: ${count} misplaced */;
+}
+"#]],
+ );
+}
+
+#[test]
+fn malformed_count() {
+ check(
+ r#"
+macro_rules! too_many_args {
+ ($($t:ident)*) => { ${count(t, 1, leftover)} }
+}
+macro_rules! depth_suffixed {
+ ($($t:ident)*) => { ${count(t, 0usize)} }
+}
+macro_rules! depth_too_large {
+ ($($t:ident)*) => { ${count(t, 18446744073709551616)} }
+}
+
+fn test() {
+ too_many_args!();
+ depth_suffixed!();
+ depth_too_large!();
+}
+"#,
+ expect![[r#"
+macro_rules! too_many_args {
+ ($($t:ident)*) => { ${count(t, 1, leftover)} }
+}
+macro_rules! depth_suffixed {
+ ($($t:ident)*) => { ${count(t, 0usize)} }
+}
+macro_rules! depth_too_large {
+ ($($t:ident)*) => { ${count(t, 18446744073709551616)} }
+}
+
+fn test() {
+ /* error: invalid macro definition: invalid metavariable expression */;
+ /* error: invalid macro definition: invalid metavariable expression */;
+ /* error: invalid macro definition: invalid metavariable expression */;
+}
+"#]],
+ );
+}
+
+#[test]
+fn count_interaction_with_empty_binding() {
+ // FIXME: Should this error? rustc currently accepts it.
+ check(
+ r#"
+macro_rules! m {
+ ($($t:ident),*) => {
+ ${count(t, 100)}
+ }
+}
+
+fn test() {
+ m!();
+}
+"#,
+ expect![[r#"
+macro_rules! m {
+ ($($t:ident),*) => {
+ ${count(t, 100)}
+ }
+}
+
+fn test() {
+ 0;
+}
+"#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index b663a2917..d8e4a4dcc 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -297,55 +297,55 @@ macro_rules! impl_fn_for_zst {
#[derive(Clone)] struct CharEscapeDebugContinue;
impl Fn<(char, )> for CharEscapeDebugContinue {
- #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeDebug { {
+ #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDebug { {
c.escape_debug_ext(false )
}
}
}
impl FnMut<(char, )> for CharEscapeDebugContinue {
- #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug {
+ #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug {
Fn::call(&*self , (c, ))
}
}
impl FnOnce<(char, )> for CharEscapeDebugContinue {
type Output = char::EscapeDebug;
- #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeDebug {
+ #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDebug {
Fn::call(&self , (c, ))
}
}
#[derive(Clone)] struct CharEscapeUnicode;
impl Fn<(char, )> for CharEscapeUnicode {
- #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { {
+ #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { {
c.escape_unicode()
}
}
}
impl FnMut<(char, )> for CharEscapeUnicode {
- #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode {
+ #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode {
Fn::call(&*self , (c, ))
}
}
impl FnOnce<(char, )> for CharEscapeUnicode {
type Output = char::EscapeUnicode;
- #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode {
+ #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode {
Fn::call(&self , (c, ))
}
}
#[derive(Clone)] struct CharEscapeDefault;
impl Fn<(char, )> for CharEscapeDefault {
- #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeDefault { {
+ #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDefault { {
c.escape_default()
}
}
}
impl FnMut<(char, )> for CharEscapeDefault {
- #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault {
+ #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault {
Fn::call(&*self , (c, ))
}
}
impl FnOnce<(char, )> for CharEscapeDefault {
type Output = char::EscapeDefault;
- #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeDefault {
+ #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDefault {
Fn::call(&self , (c, ))
}
}
@@ -833,7 +833,7 @@ macro_rules! rgb_color {
/* parse error: expected SEMICOLON */
/* parse error: expected expression, item or let statement */
pub fn new() {
- let _ = 0as u32<<(8+8);
+ let _ = 0 as u32<<(8+8);
}
// MACRO_ITEMS@0..31
// FN@0..31
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
index b8d2ca687..ae56934f6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
@@ -98,7 +98,7 @@ macro_rules! m1 { ($x:ident) => { ($x } }
macro_rules! m2 { ($x:ident) => {} }
/* error: invalid macro definition: expected subtree */
-/* error: Failed to lower macro args to token tree */
+/* error: invalid token tree */
"#]],
)
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
index 314bf22b9..4a62696df 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -14,7 +14,7 @@ mod builtin_fn_macro;
mod builtin_derive_macro;
mod proc_macros;
-use std::{iter, ops::Range, sync::Arc};
+use std::{iter, ops::Range, sync};
use ::mbe::TokenMap;
use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase};
@@ -33,8 +33,13 @@ use syntax::{
use tt::token_id::{Subtree, TokenId};
use crate::{
- db::DefDatabase, macro_id_to_def_id, nameres::ModuleSource, resolver::HasResolver,
- src::HasSource, test_db::TestDB, AdtId, AsMacroCall, Lookup, ModuleDefId,
+ db::DefDatabase,
+ macro_id_to_def_id,
+ nameres::{DefMap, MacroSubNs, ModuleSource},
+ resolver::HasResolver,
+ src::HasSource,
+ test_db::TestDB,
+ AdtId, AsMacroCall, Lookup, ModuleDefId,
};
#[track_caller]
@@ -50,13 +55,13 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
ProcMacro {
name: "identity_when_valid".into(),
kind: base_db::ProcMacroKind::Attr,
- expander: Arc::new(IdentityWhenValidProcMacroExpander),
+ expander: sync::Arc::new(IdentityWhenValidProcMacroExpander),
},
)];
let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);
let krate = db.crate_graph().iter().next().unwrap();
let def_map = db.crate_def_map(krate);
- let local_id = def_map.root();
+ let local_id = DefMap::ROOT;
let module = def_map.module_id(local_id);
let resolver = module.resolver(&db);
let source = def_map[local_id].definition_source(&db);
@@ -125,21 +130,17 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
let macro_call = InFile::new(source.file_id, &macro_call);
- let mut error = None;
- let macro_call_id = macro_call
- .as_call_id_with_errors(
- &db,
- krate,
- |path| {
- resolver.resolve_path_as_macro(&db, &path).map(|it| macro_id_to_def_id(&db, it))
- },
- &mut |err| error = Some(err),
- )
- .unwrap()
+ let res = macro_call
+ .as_call_id_with_errors(&db, krate, |path| {
+ resolver
+ .resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang))
+ .map(|it| macro_id_to_def_id(&db, it))
+ })
.unwrap();
+ let macro_call_id = res.value.unwrap();
let macro_file = MacroFile { macro_call_id };
let mut expansion_result = db.parse_macro_expansion(macro_file);
- expansion_result.err = expansion_result.err.or(error);
+ expansion_result.err = expansion_result.err.or(res.err);
expansions.push((macro_call.value.clone(), expansion_result, db.macro_arg(macro_call_id)));
}
@@ -157,34 +158,33 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
if let Some(err) = exp.err {
format_to!(expn_text, "/* error: {} */", err);
}
- if let Some((parse, token_map)) = exp.value {
- if expect_errors {
- assert!(!parse.errors().is_empty(), "no parse errors in expansion");
- for e in parse.errors() {
- format_to!(expn_text, "/* parse error: {} */\n", e);
- }
- } else {
- assert!(
- parse.errors().is_empty(),
- "parse errors in expansion: \n{:#?}",
- parse.errors()
- );
+ let (parse, token_map) = exp.value;
+ if expect_errors {
+ assert!(!parse.errors().is_empty(), "no parse errors in expansion");
+ for e in parse.errors() {
+ format_to!(expn_text, "/* parse error: {} */\n", e);
}
- let pp = pretty_print_macro_expansion(
- parse.syntax_node(),
- show_token_ids.then_some(&*token_map),
+ } else {
+ assert!(
+ parse.errors().is_empty(),
+ "parse errors in expansion: \n{:#?}",
+ parse.errors()
);
- let indent = IndentLevel::from_node(call.syntax());
- let pp = reindent(indent, pp);
- format_to!(expn_text, "{}", pp);
+ }
+ let pp = pretty_print_macro_expansion(
+ parse.syntax_node(),
+ show_token_ids.then_some(&*token_map),
+ );
+ let indent = IndentLevel::from_node(call.syntax());
+ let pp = reindent(indent, pp);
+ format_to!(expn_text, "{}", pp);
- if tree {
- let tree = format!("{:#?}", parse.syntax_node())
- .split_inclusive('\n')
- .map(|line| format!("// {line}"))
- .collect::<String>();
- format_to!(expn_text, "\n{}", tree)
- }
+ if tree {
+ let tree = format!("{:#?}", parse.syntax_node())
+ .split_inclusive('\n')
+ .map(|line| format!("// {line}"))
+ .collect::<String>();
+ format_to!(expn_text, "\n{}", tree)
}
let range = call.syntax().text_range();
let range: Range<usize> = range.into();
@@ -287,6 +287,7 @@ fn pretty_print_macro_expansion(expn: SyntaxNode, map: Option<&TokenMap>) -> Str
let curr_kind = token.kind();
let space = match (prev_kind, curr_kind) {
_ if prev_kind.is_trivia() || curr_kind.is_trivia() => "",
+ _ if prev_kind.is_literal() && !curr_kind.is_punct() => " ",
(T!['{'], T!['}']) => "",
(T![=], _) | (_, T![=]) => " ",
(_, T!['{']) => " ",
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index 4efe8c58a..0ab1bd849 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -57,9 +57,9 @@ mod path_resolution;
#[cfg(test)]
mod tests;
-use std::{cmp::Ord, ops::Deref, sync::Arc};
+use std::{cmp::Ord, ops::Deref};
-use base_db::{CrateId, Edition, FileId};
+use base_db::{CrateId, Edition, FileId, ProcMacroKind};
use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId};
use itertools::Itertools;
use la_arena::Arena;
@@ -67,6 +67,7 @@ use profile::Count;
use rustc_hash::{FxHashMap, FxHashSet};
use stdx::format_to;
use syntax::{ast, SmolStr};
+use triomphe::Arc;
use crate::{
db::DefDatabase,
@@ -76,7 +77,8 @@ use crate::{
path::ModPath,
per_ns::PerNs,
visibility::Visibility,
- AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, MacroId, ModuleId, ProcMacroId,
+ AstId, BlockId, BlockLoc, CrateRootModuleId, FunctionId, LocalModuleId, Lookup, MacroExpander,
+ MacroId, ModuleId, ProcMacroId,
};
/// Contains the results of (early) name resolution.
@@ -91,8 +93,10 @@ use crate::{
#[derive(Debug, PartialEq, Eq)]
pub struct DefMap {
_c: Count<Self>,
+ /// When this is a block def map, this will hold the block id of the the block and module that
+ /// contains this block.
block: Option<BlockInfo>,
- root: LocalModuleId,
+ /// The modules and their data declared in this crate.
modules: Arena<ModuleData>,
krate: CrateId,
/// The prelude module for this crate. This either comes from an import
@@ -102,17 +106,32 @@ pub struct DefMap {
/// but that attribute is nightly and when used in a block, it affects resolution globally
/// so we aren't handling this correctly anyways).
prelude: Option<ModuleId>,
- /// The extern prelude is only populated for non-block DefMaps
- extern_prelude: FxHashMap<Name, ModuleId>,
+ /// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that
+ /// this contains all kinds of macro, not just `macro_rules!` macro.
+ macro_use_prelude: FxHashMap<Name, MacroId>,
+
+ /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
+ /// attributes.
+ derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
+
+ /// The diagnostics that need to be emitted for this crate.
+ diagnostics: Vec<DefDiagnostic>,
+
+ /// The crate data that is shared between a crate's def map and all its block def maps.
+ data: Arc<DefMapCrateData>,
+}
+
+/// Data that belongs to a crate which is shared between a crate's def map and all its block def maps.
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct DefMapCrateData {
+ /// The extern prelude which contains all root modules of external crates that are in scope.
+ extern_prelude: FxHashMap<Name, CrateRootModuleId>,
/// Side table for resolving derive helpers.
exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
/// The error that occurred when failing to load the proc-macro dll.
proc_macro_loading_error: Option<Box<str>>,
- /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
- /// attributes.
- derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
/// Custom attributes registered with `#![register_attr]`.
registered_attrs: Vec<SmolStr>,
@@ -122,10 +141,36 @@ pub struct DefMap {
unstable_features: FxHashSet<SmolStr>,
/// #[rustc_coherence_is_core]
rustc_coherence_is_core: bool,
+ no_core: bool,
+ no_std: bool,
edition: Edition,
recursion_limit: Option<u32>,
- diagnostics: Vec<DefDiagnostic>,
+}
+
+impl DefMapCrateData {
+ fn shrink_to_fit(&mut self) {
+ let Self {
+ extern_prelude,
+ exported_derives,
+ fn_proc_macro_mapping,
+ registered_attrs,
+ registered_tools,
+ unstable_features,
+ proc_macro_loading_error: _,
+ rustc_coherence_is_core: _,
+ no_core: _,
+ no_std: _,
+ edition: _,
+ recursion_limit: _,
+ } = self;
+ extern_prelude.shrink_to_fit();
+ exported_derives.shrink_to_fit();
+ fn_proc_macro_mapping.shrink_to_fit();
+ registered_attrs.shrink_to_fit();
+ registered_tools.shrink_to_fit();
+ unstable_features.shrink_to_fit();
+ }
}
/// For `DefMap`s computed for a block expression, this stores its location in the parent map.
@@ -134,7 +179,23 @@ struct BlockInfo {
/// The `BlockId` this `DefMap` was created from.
block: BlockId,
/// The containing module.
- parent: ModuleId,
+ parent: BlockRelativeModuleId,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+struct BlockRelativeModuleId {
+ block: Option<BlockId>,
+ local_id: LocalModuleId,
+}
+
+impl BlockRelativeModuleId {
+ fn def_map(self, db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
+ self.into_module(krate).def_map(db)
+ }
+
+ fn into_module(self, krate: CrateId) -> ModuleId {
+ ModuleId { krate, block: self.block, local_id: self.local_id }
+ }
}
impl std::ops::Index<LocalModuleId> for DefMap {
@@ -224,6 +285,9 @@ pub struct ModuleData {
}
impl DefMap {
+ /// The module id of a crate or block root.
+ pub const ROOT: LocalModuleId = LocalModuleId::from_raw(la_arena::RawIdx::from_u32(0));
+
pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
let _p = profile::span("crate_def_map_query").detail(|| {
db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
@@ -243,17 +307,10 @@ impl DefMap {
Arc::new(def_map)
}
- pub(crate) fn block_def_map_query(
- db: &dyn DefDatabase,
- block_id: BlockId,
- ) -> Option<Arc<DefMap>> {
+ pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> {
let block: BlockLoc = db.lookup_intern_block(block_id);
let tree_id = TreeId::new(block.ast_id.file_id, Some(block_id));
- let item_tree = tree_id.item_tree(db);
- if item_tree.top_level_items().is_empty() {
- return None;
- }
let parent_map = block.module.def_map(db);
let krate = block.module.krate;
@@ -265,36 +322,48 @@ impl DefMap {
let module_data =
ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility);
- let mut def_map = DefMap::empty(krate, parent_map.edition, module_data);
- def_map.block = Some(BlockInfo { block: block_id, parent: block.module });
+ let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data);
+ def_map.data = parent_map.data.clone();
+ def_map.block = Some(BlockInfo {
+ block: block_id,
+ parent: BlockRelativeModuleId {
+ block: block.module.block,
+ local_id: block.module.local_id,
+ },
+ });
let def_map = collector::collect_defs(db, def_map, tree_id);
- Some(Arc::new(def_map))
+ Arc::new(def_map)
}
fn empty(krate: CrateId, edition: Edition, module_data: ModuleData) -> DefMap {
let mut modules: Arena<ModuleData> = Arena::default();
let root = modules.alloc(module_data);
+ assert_eq!(root, Self::ROOT);
DefMap {
_c: Count::new(),
block: None,
+ modules,
krate,
- edition,
- recursion_limit: None,
- extern_prelude: FxHashMap::default(),
- exported_derives: FxHashMap::default(),
- fn_proc_macro_mapping: FxHashMap::default(),
- proc_macro_loading_error: None,
- derive_helpers_in_scope: FxHashMap::default(),
prelude: None,
- root,
- modules,
- registered_attrs: Vec::new(),
- registered_tools: Vec::new(),
- unstable_features: FxHashSet::default(),
+ macro_use_prelude: FxHashMap::default(),
+ derive_helpers_in_scope: FxHashMap::default(),
diagnostics: Vec::new(),
- rustc_coherence_is_core: false,
+ data: Arc::new(DefMapCrateData {
+ extern_prelude: FxHashMap::default(),
+ exported_derives: FxHashMap::default(),
+ fn_proc_macro_mapping: FxHashMap::default(),
+ proc_macro_loading_error: None,
+ registered_attrs: Vec::new(),
+ registered_tools: Vec::new(),
+ unstable_features: FxHashSet::default(),
+ rustc_coherence_is_core: false,
+ no_core: false,
+ no_std: false,
+ edition,
+ recursion_limit: None,
+ }),
}
}
@@ -317,31 +386,31 @@ impl DefMap {
}
pub fn registered_tools(&self) -> &[SmolStr] {
- &self.registered_tools
+ &self.data.registered_tools
}
pub fn registered_attrs(&self) -> &[SmolStr] {
- &self.registered_attrs
+ &self.data.registered_attrs
}
pub fn is_unstable_feature_enabled(&self, feature: &str) -> bool {
- self.unstable_features.contains(feature)
+ self.data.unstable_features.contains(feature)
}
pub fn is_rustc_coherence_is_core(&self) -> bool {
- self.rustc_coherence_is_core
+ self.data.rustc_coherence_is_core
}
- pub fn root(&self) -> LocalModuleId {
- self.root
+ pub fn is_no_std(&self) -> bool {
+ self.data.no_std || self.data.no_core
}
pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option<ProcMacroId> {
- self.fn_proc_macro_mapping.get(&id).copied()
+ self.data.fn_proc_macro_mapping.get(&id).copied()
}
pub fn proc_macro_loading_error(&self) -> Option<&str> {
- self.proc_macro_loading_error.as_deref()
+ self.data.proc_macro_loading_error.as_deref()
}
pub fn krate(&self) -> CrateId {
@@ -356,8 +425,12 @@ impl DefMap {
self.prelude
}
- pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, &ModuleId)> + '_ {
- self.extern_prelude.iter()
+ pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, ModuleId)> + '_ {
+ self.data.extern_prelude.iter().map(|(name, &def)| (name, def.into()))
+ }
+
+ pub(crate) fn macro_use_prelude(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ {
+ self.macro_use_prelude.iter().map(|(name, &def)| (name, def))
}
pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId {
@@ -365,11 +438,8 @@ impl DefMap {
ModuleId { krate: self.krate, local_id, block }
}
- pub(crate) fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId {
- self.with_ancestor_maps(db, self.root, &mut |def_map, _module| {
- if def_map.block.is_none() { Some(def_map.module_id(def_map.root)) } else { None }
- })
- .expect("DefMap chain without root")
+ pub fn crate_root(&self) -> CrateRootModuleId {
+ CrateRootModuleId { krate: self.krate }
}
pub(crate) fn resolve_path(
@@ -378,9 +448,16 @@ impl DefMap {
original_module: LocalModuleId,
path: &ModPath,
shadow: BuiltinShadowMode,
+ expected_macro_subns: Option<MacroSubNs>,
) -> (PerNs, Option<usize>) {
- let res =
- self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow);
+ let res = self.resolve_path_fp_with_macro(
+ db,
+ ResolveMode::Other,
+ original_module,
+ path,
+ shadow,
+ expected_macro_subns,
+ );
(res.resolved_def, res.segment_index)
}
@@ -397,6 +474,7 @@ impl DefMap {
original_module,
path,
shadow,
+ None, // Currently this function isn't used for macro resolution.
);
(res.resolved_def, res.segment_index)
}
@@ -405,7 +483,7 @@ impl DefMap {
///
/// If `f` returns `Some(val)`, iteration is stopped and `Some(val)` is returned. If `f` returns
/// `None`, iteration continues.
- pub fn with_ancestor_maps<T>(
+ pub(crate) fn with_ancestor_maps<T>(
&self,
db: &dyn DefDatabase,
local_mod: LocalModuleId,
@@ -416,7 +494,7 @@ impl DefMap {
}
let mut block = self.block;
while let Some(block_info) = block {
- let parent = block_info.parent.def_map(db);
+ let parent = block_info.parent.def_map(db, self.krate);
if let Some(it) = f(&parent, block_info.parent.local_id) {
return Some(it);
}
@@ -429,7 +507,8 @@ impl DefMap {
/// If this `DefMap` is for a block expression, returns the module containing the block (which
/// might again be a block, or a module inside a block).
pub fn parent(&self) -> Option<ModuleId> {
- Some(self.block?.parent)
+ let BlockRelativeModuleId { block, local_id } = self.block?.parent;
+ Some(ModuleId { krate: self.krate, block, local_id })
}
/// Returns the module containing `local_mod`, either the parent `mod`, or the module (or block) containing
@@ -437,7 +516,13 @@ impl DefMap {
pub fn containing_module(&self, local_mod: LocalModuleId) -> Option<ModuleId> {
match self[local_mod].parent {
Some(parent) => Some(self.module_id(parent)),
- None => self.block.map(|block| block.parent),
+ None => {
+ self.block.map(
+ |BlockInfo { parent: BlockRelativeModuleId { block, local_id }, .. }| {
+ ModuleId { krate: self.krate, block, local_id }
+ },
+ )
+ }
}
}
@@ -448,25 +533,31 @@ impl DefMap {
let mut arc;
let mut current_map = self;
while let Some(block) = current_map.block {
- go(&mut buf, current_map, "block scope", current_map.root);
+ go(&mut buf, db, current_map, "block scope", Self::ROOT);
buf.push('\n');
- arc = block.parent.def_map(db);
+ arc = block.parent.def_map(db, self.krate);
current_map = &arc;
}
- go(&mut buf, current_map, "crate", current_map.root);
+ go(&mut buf, db, current_map, "crate", Self::ROOT);
return buf;
- fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) {
+ fn go(
+ buf: &mut String,
+ db: &dyn DefDatabase,
+ map: &DefMap,
+ path: &str,
+ module: LocalModuleId,
+ ) {
format_to!(buf, "{}\n", path);
- map.modules[module].scope.dump(buf);
+ map.modules[module].scope.dump(db.upcast(), buf);
for (name, child) in
map.modules[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
{
- let path = format!("{path}::{name}");
+ let path = format!("{path}::{}", name.display(db.upcast()));
buf.push('\n');
- go(buf, map, &path, *child);
+ go(buf, db, map, &path, *child);
}
}
}
@@ -477,7 +568,7 @@ impl DefMap {
let mut current_map = self;
while let Some(block) = current_map.block {
format_to!(buf, "{:?} in {:?}\n", block.block, block.parent);
- arc = block.parent.def_map(db);
+ arc = block.parent.def_map(db, self.krate);
current_map = &arc;
}
@@ -489,34 +580,20 @@ impl DefMap {
// Exhaustive match to require handling new fields.
let Self {
_c: _,
- exported_derives,
- extern_prelude,
+ macro_use_prelude,
diagnostics,
modules,
- registered_attrs,
- registered_tools,
- fn_proc_macro_mapping,
derive_helpers_in_scope,
- unstable_features,
- proc_macro_loading_error: _,
block: _,
- edition: _,
- recursion_limit: _,
krate: _,
prelude: _,
- root: _,
- rustc_coherence_is_core: _,
+ data: _,
} = self;
- extern_prelude.shrink_to_fit();
- exported_derives.shrink_to_fit();
+ macro_use_prelude.shrink_to_fit();
diagnostics.shrink_to_fit();
modules.shrink_to_fit();
- registered_attrs.shrink_to_fit();
- registered_tools.shrink_to_fit();
- fn_proc_macro_mapping.shrink_to_fit();
derive_helpers_in_scope.shrink_to_fit();
- unstable_features.shrink_to_fit();
for (_, module) in modules.iter_mut() {
module.children.shrink_to_fit();
module.scope.shrink_to_fit();
@@ -529,7 +606,7 @@ impl DefMap {
}
pub fn recursion_limit(&self) -> Option<u32> {
- self.recursion_limit
+ self.data.recursion_limit
}
}
@@ -564,3 +641,48 @@ pub enum ModuleSource {
Module(ast::Module),
BlockExpr(ast::BlockExpr),
}
+
+/// See `sub_namespace_match()`.
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum MacroSubNs {
+ /// Function-like macros, suffixed with `!`.
+ Bang,
+ /// Macros inside attributes, i.e. attribute macros and derive macros.
+ Attr,
+}
+
+impl MacroSubNs {
+ fn from_id(db: &dyn DefDatabase, macro_id: MacroId) -> Self {
+ let expander = match macro_id {
+ MacroId::Macro2Id(it) => it.lookup(db).expander,
+ MacroId::MacroRulesId(it) => it.lookup(db).expander,
+ MacroId::ProcMacroId(it) => {
+ return match it.lookup(db).kind {
+ ProcMacroKind::CustomDerive | ProcMacroKind::Attr => Self::Attr,
+ ProcMacroKind::FuncLike => Self::Bang,
+ };
+ }
+ };
+
+ // Eager macros aren't *guaranteed* to be bang macros, but they *are* all bang macros currently.
+ match expander {
+ MacroExpander::Declarative
+ | MacroExpander::BuiltIn(_)
+ | MacroExpander::BuiltInEager(_) => Self::Bang,
+ MacroExpander::BuiltInAttr(_) | MacroExpander::BuiltInDerive(_) => Self::Attr,
+ }
+ }
+}
+
+/// Quoted from [rustc]:
+/// Macro namespace is separated into two sub-namespaces, one for bang macros and
+/// one for attribute-like macros (attributes, derives).
+/// We ignore resolutions from one sub-namespace when searching names in scope for another.
+///
+/// [rustc]: https://github.com/rust-lang/rust/blob/1.69.0/compiler/rustc_resolve/src/macros.rs#L75
+fn sub_namespace_match(candidate: Option<MacroSubNs>, expected: Option<MacroSubNs>) -> bool {
+ match (candidate, expected) {
+ (Some(candidate), Some(expected)) => candidate == expected,
+ _ => true,
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
index 79cabeb0f..a7abf4459 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
@@ -4,7 +4,8 @@ use hir_expand::{attrs::Attr, MacroCallId};
use syntax::{ast, SmolStr};
use crate::{
- attr_macro_as_call_id, builtin_attr,
+ attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
+ attr_macro_as_call_id,
db::DefDatabase,
item_scope::BuiltinShadowMode,
macro_id_to_def_id,
@@ -13,7 +14,7 @@ use crate::{
AstIdWithPath, LocalModuleId, UnresolvedMacro,
};
-use super::DefMap;
+use super::{DefMap, MacroSubNs};
pub enum ResolvedAttr {
/// Attribute resolved to an attribute macro.
@@ -42,9 +43,12 @@ impl DefMap {
original_module,
&ast_id.path,
BuiltinShadowMode::Module,
+ Some(MacroSubNs::Attr),
);
let def = match resolved_res.resolved_def.take_macros() {
Some(def) => {
+ // `MacroSubNs` is just a hint, so the path may still resolve to a custom derive
+ // macro, or even function-like macro when the path is qualified.
if def.is_attribute(db) {
def
} else {
@@ -60,7 +64,6 @@ impl DefMap {
attr,
self.krate,
macro_id_to_def_id(db, def),
- false,
)))
}
@@ -75,20 +78,16 @@ impl DefMap {
let name = name.to_smol_str();
let pred = |n: &_| *n == name;
- let registered = self.registered_tools.iter().map(SmolStr::as_str);
- let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
+ let registered = self.data.registered_tools.iter().map(SmolStr::as_str);
+ let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred);
// FIXME: tool modules can be shadowed by actual modules
if is_tool {
return true;
}
if segments.len() == 1 {
- let registered = self.registered_attrs.iter().map(SmolStr::as_str);
- let is_inert = builtin_attr::INERT_ATTRIBUTES
- .iter()
- .map(|it| it.name)
- .chain(registered)
- .any(pred);
+ let mut registered = self.data.registered_attrs.iter().map(SmolStr::as_str);
+ let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred);
return is_inert;
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index ddcee77ec..62fb3c788 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -3,9 +3,9 @@
//! `DefCollector::collect` contains the fixed-point iteration loop which
//! resolves imports and expands macros.
-use std::{iter, mem};
+use std::{cmp::Ordering, iter, mem};
-use base_db::{CrateId, Edition, FileId};
+use base_db::{CrateId, Dependency, Edition, FileId};
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{
@@ -14,10 +14,11 @@ use hir_expand::{
builtin_attr_macro::find_builtin_attr,
builtin_derive_macro::find_builtin_derive,
builtin_fn_macro::find_builtin_macro,
+ hygiene::Hygiene,
name::{name, AsName, Name},
proc_macro::ProcMacroExpander,
- ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
- MacroDefKind,
+ ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc,
+ MacroDefId, MacroDefKind,
};
use itertools::{izip, Itertools};
use la_arena::Idx;
@@ -25,6 +26,7 @@ use limit::Limit;
use rustc_hash::{FxHashMap, FxHashSet};
use stdx::always;
use syntax::{ast, SmolStr};
+use triomphe::Arc;
use crate::{
attr::Attrs,
@@ -33,8 +35,8 @@ use crate::{
derive_macro_as_call_id,
item_scope::{ImportType, PerNsGlobImports},
item_tree::{
- self, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, MacroCall,
- MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
+ self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
+ MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
},
macro_call_as_call_id, macro_id_to_def_id,
nameres::{
@@ -42,24 +44,25 @@ use crate::{
mod_resolution::ModDir,
path_resolution::ReachedFixedPoint,
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
- BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode,
+ sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin,
+ ResolveMode,
},
path::{ImportAlias, ModPath, PathKind},
per_ns::PerNs,
tt,
visibility::{RawVisibility, Visibility},
- AdtId, AstId, AstIdWithPath, ConstLoc, 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,
+ 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,
};
static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
static EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128);
static FIXED_POINT_LIMIT: Limit = Limit::new(8192);
-pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: TreeId) -> DefMap {
+pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap {
let crate_graph = db.crate_graph();
let mut deps = FxHashMap::default();
@@ -67,36 +70,33 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
let krate = &crate_graph[def_map.krate];
for dep in &krate.dependencies {
tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
- let dep_def_map = db.crate_def_map(dep.crate_id);
- let dep_root = dep_def_map.module_id(dep_def_map.root);
- deps.insert(dep.as_name(), dep_root);
-
- if dep.is_prelude() && !tree_id.is_block() {
- def_map.extern_prelude.insert(dep.as_name(), dep_root);
- }
+ deps.insert(dep.as_name(), dep.clone());
}
let cfg_options = &krate.cfg_options;
- let proc_macros = match &krate.proc_macro {
- Ok(proc_macros) => {
- proc_macros
- .iter()
- .enumerate()
- .map(|(idx, it)| {
- // FIXME: a hacky way to create a Name from string.
- let name =
- tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() };
- (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32)))
- })
- .collect()
- }
- Err(e) => {
- def_map.proc_macro_loading_error = Some(e.clone().into_boxed_str());
- Vec::new()
+
+ let is_proc_macro = krate.is_proc_macro;
+ let proc_macros = if is_proc_macro {
+ match db.proc_macros().get(&def_map.krate) {
+ Some(Ok(proc_macros)) => {
+ Ok(proc_macros
+ .iter()
+ .enumerate()
+ .map(|(idx, it)| {
+ // FIXME: a hacky way to create a Name from string.
+ let name =
+ tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() };
+ (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32)))
+ })
+ .collect())
+ }
+ Some(Err(e)) => Err(e.clone().into_boxed_str()),
+ None => Err("No proc-macros present for crate".to_owned().into_boxed_str()),
}
+ } else {
+ Ok(vec![])
};
- let is_proc_macro = krate.is_proc_macro;
let mut collector = DefCollector {
db,
@@ -112,6 +112,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
from_glob_import: Default::default(),
skip_attrs: Default::default(),
is_proc_macro,
+ hygienes: FxHashMap::default(),
};
if tree_id.is_block() {
collector.seed_with_inner(tree_id);
@@ -238,7 +239,7 @@ enum MacroDirectiveKind {
struct DefCollector<'a> {
db: &'a dyn DefDatabase,
def_map: DefMap,
- deps: FxHashMap<Name, ModuleId>,
+ deps: FxHashMap<Name, Dependency>,
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
unresolved_imports: Vec<ImportDirective>,
indeterminate_imports: Vec<ImportDirective>,
@@ -249,7 +250,7 @@ struct DefCollector<'a> {
/// built by the build system, and is the list of proc. macros we can actually expand. It is
/// empty when proc. macro support is disabled (in which case we still do name resolution for
/// them).
- proc_macros: Vec<(Name, ProcMacroExpander)>,
+ proc_macros: Result<Vec<(Name, ProcMacroExpander)>, Box<str>>,
is_proc_macro: bool,
from_glob_import: PerNsGlobImports,
/// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute.
@@ -259,6 +260,12 @@ struct DefCollector<'a> {
/// This also stores the attributes to skip when we resolve derive helpers and non-macro
/// non-builtin attributes in general.
skip_attrs: FxHashMap<InFile<ModItem>, AttrId>,
+ /// `Hygiene` cache, because `Hygiene` construction is expensive.
+ ///
+ /// Almost all paths should have been lowered to `ModPath` during `ItemTree` construction.
+ /// However, `DefCollector` still needs to lower paths in attributes, in particular those in
+ /// derive meta item list.
+ hygienes: FxHashMap<HirFileId, Hygiene>,
}
impl DefCollector<'_> {
@@ -267,87 +274,113 @@ impl DefCollector<'_> {
let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
let item_tree = self.db.file_item_tree(file_id.into());
- let module_id = self.def_map.root;
-
let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
- if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) {
- self.inject_prelude(&attrs);
-
- // Process other crate-level attributes.
- for attr in &*attrs {
- let attr_name = match attr.path.as_ident() {
- Some(name) => name,
- None => continue,
- };
+ let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
- if *attr_name == hir_expand::name![recursion_limit] {
- if let Some(limit) = attr.string_value() {
- if let Ok(limit) = limit.parse() {
- self.def_map.recursion_limit = Some(limit);
- }
- }
- continue;
+ if let Err(e) = &self.proc_macros {
+ crate_data.proc_macro_loading_error = Some(e.clone());
+ }
+
+ for (name, dep) in &self.deps {
+ if dep.is_prelude() {
+ crate_data
+ .extern_prelude
+ .insert(name.clone(), CrateRootModuleId { krate: dep.crate_id });
+ }
+ }
+
+ // Process other crate-level attributes.
+ for attr in &*attrs {
+ if let Some(cfg) = attr.cfg() {
+ if self.cfg_options.check(&cfg) == Some(false) {
+ return;
}
+ }
+ let attr_name = match attr.path.as_ident() {
+ Some(name) => name,
+ None => continue,
+ };
- if *attr_name == hir_expand::name![crate_type] {
- if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) {
- self.is_proc_macro = true;
+ if *attr_name == hir_expand::name![recursion_limit] {
+ if let Some(limit) = attr.string_value() {
+ if let Ok(limit) = limit.parse() {
+ crate_data.recursion_limit = Some(limit);
}
- continue;
}
+ continue;
+ }
- if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") {
- self.def_map.rustc_coherence_is_core = true;
- continue;
+ if *attr_name == hir_expand::name![crate_type] {
+ if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) {
+ self.is_proc_macro = true;
}
+ continue;
+ }
- if *attr_name == hir_expand::name![feature] {
- let features =
- attr.parse_path_comma_token_tree().into_iter().flatten().filter_map(
- |feat| match feat.segments() {
- [name] => Some(name.to_smol_str()),
- _ => None,
- },
- );
- self.def_map.unstable_features.extend(features);
- }
+ if *attr_name == hir_expand::name![no_core] {
+ crate_data.no_core = true;
+ continue;
+ }
- let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
- || *attr_name == hir_expand::name![register_tool];
- if !attr_is_register_like {
- continue;
- }
+ if *attr_name == hir_expand::name![no_std] {
+ crate_data.no_std = true;
+ continue;
+ }
- let registered_name = match attr.single_ident_value() {
- Some(ident) => ident.as_name(),
- _ => continue,
- };
+ if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") {
+ crate_data.rustc_coherence_is_core = true;
+ continue;
+ }
- if *attr_name == hir_expand::name![register_attr] {
- self.def_map.registered_attrs.push(registered_name.to_smol_str());
- cov_mark::hit!(register_attr);
- } else {
- self.def_map.registered_tools.push(registered_name.to_smol_str());
- cov_mark::hit!(register_tool);
- }
+ if *attr_name == hir_expand::name![feature] {
+ let hygiene = &Hygiene::new_unhygienic();
+ let features = attr
+ .parse_path_comma_token_tree(self.db.upcast(), hygiene)
+ .into_iter()
+ .flatten()
+ .filter_map(|feat| match feat.segments() {
+ [name] => Some(name.to_smol_str()),
+ _ => None,
+ });
+ crate_data.unstable_features.extend(features);
}
- ModCollector {
- def_collector: self,
- macro_depth: 0,
- module_id,
- tree_id: TreeId::new(file_id.into(), None),
- item_tree: &item_tree,
- mod_dir: ModDir::root(),
+ let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
+ || *attr_name == hir_expand::name![register_tool];
+ if !attr_is_register_like {
+ continue;
}
- .collect_in_top_module(item_tree.top_level_items());
+
+ let registered_name = match attr.single_ident_value() {
+ Some(ident) => ident.as_name(),
+ _ => continue,
+ };
+
+ if *attr_name == hir_expand::name![register_attr] {
+ crate_data.registered_attrs.push(registered_name.to_smol_str());
+ cov_mark::hit!(register_attr);
+ } else {
+ crate_data.registered_tools.push(registered_name.to_smol_str());
+ cov_mark::hit!(register_tool);
+ }
+ }
+
+ crate_data.shrink_to_fit();
+ self.inject_prelude();
+
+ ModCollector {
+ def_collector: self,
+ macro_depth: 0,
+ module_id: DefMap::ROOT,
+ tree_id: TreeId::new(file_id.into(), None),
+ item_tree: &item_tree,
+ mod_dir: ModDir::root(),
}
+ .collect_in_top_module(item_tree.top_level_items());
}
fn seed_with_inner(&mut self, tree_id: TreeId) {
let item_tree = tree_id.item_tree(self.db);
- let module_id = self.def_map.root;
-
let is_cfg_enabled = item_tree
.top_level_attrs(self.db, self.def_map.krate)
.cfg()
@@ -356,7 +389,7 @@ impl DefCollector<'_> {
ModCollector {
def_collector: self,
macro_depth: 0,
- module_id,
+ module_id: DefMap::ROOT,
tree_id,
item_tree: &item_tree,
mod_dir: ModDir::root(),
@@ -428,7 +461,7 @@ impl DefCollector<'_> {
// Additionally, while the proc macro entry points must be `pub`, they are not publicly
// exported in type/value namespace. This function reduces the visibility of all items
// in the crate root that aren't proc macros.
- let root = self.def_map.root;
+ let root = DefMap::ROOT;
let module_id = self.def_map.module_id(root);
let root = &mut self.def_map.modules[root];
root.scope.censor_non_proc_macros(module_id);
@@ -456,12 +489,8 @@ impl DefCollector<'_> {
directive.module_id,
MacroCallKind::Attr {
ast_id: ast_id.ast_id,
- attr_args: std::sync::Arc::new((
- tt::Subtree::empty(),
- Default::default(),
- )),
+ attr_args: Arc::new((tt::Subtree::empty(), Default::default())),
invoc_attr_index: attr.id,
- is_derive: false,
},
attr.path().clone(),
));
@@ -495,15 +524,15 @@ impl DefCollector<'_> {
}
}
- fn inject_prelude(&mut self, crate_attrs: &Attrs) {
+ fn inject_prelude(&mut self) {
// See compiler/rustc_builtin_macros/src/standard_library_imports.rs
- if crate_attrs.by_key("no_core").exists() {
+ if self.def_map.data.no_core {
// libcore does not get a prelude.
return;
}
- let krate = if crate_attrs.by_key("no_std").exists() {
+ let krate = if self.def_map.data.no_std {
name![core]
} else {
let std = name![std];
@@ -516,43 +545,31 @@ impl DefCollector<'_> {
}
};
- let edition = match self.def_map.edition {
+ let edition = match self.def_map.data.edition {
Edition::Edition2015 => name![rust_2015],
Edition::Edition2018 => name![rust_2018],
Edition::Edition2021 => name![rust_2021],
};
- let path_kind = match self.def_map.edition {
+ let path_kind = match self.def_map.data.edition {
Edition::Edition2015 => PathKind::Plain,
_ => PathKind::Abs,
};
- let path =
- ModPath::from_segments(path_kind, [krate.clone(), name![prelude], edition].into_iter());
- // Fall back to the older `std::prelude::v1` for compatibility with Rust <1.52.0
- // FIXME remove this fallback
- let fallback_path =
- ModPath::from_segments(path_kind, [krate, name![prelude], name![v1]].into_iter());
-
- for path in &[path, fallback_path] {
- let (per_ns, _) = self.def_map.resolve_path(
- self.db,
- self.def_map.root,
- path,
- BuiltinShadowMode::Other,
- );
+ let path = ModPath::from_segments(path_kind, [krate, name![prelude], edition]);
- match per_ns.types {
- Some((ModuleDefId::ModuleId(m), _)) => {
- self.def_map.prelude = Some(m);
- break;
- }
- types => {
- tracing::debug!(
- "could not resolve prelude path `{}` to module (resolved to {:?})",
- path,
- types
- );
- }
+ let (per_ns, _) =
+ self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None);
+
+ match per_ns.types {
+ Some((ModuleDefId::ModuleId(m), _)) => {
+ self.def_map.prelude = Some(m);
+ }
+ types => {
+ tracing::debug!(
+ "could not resolve prelude path `{}` to module (resolved to {:?})",
+ path.display(self.db.upcast()),
+ types
+ );
}
}
}
@@ -578,23 +595,28 @@ impl DefCollector<'_> {
def: ProcMacroDef,
id: ItemTreeId<item_tree::Function>,
fn_id: FunctionId,
- module_id: ModuleId,
) {
+ if self.def_map.block.is_some() {
+ return;
+ }
let kind = def.kind.to_basedb_kind();
- let (expander, kind) = match self.proc_macros.iter().find(|(n, _)| n == &def.name) {
- Some(&(_, expander)) => (expander, kind),
- None => (ProcMacroExpander::dummy(), kind),
- };
+ let (expander, kind) =
+ match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) {
+ Ok(Some(&(_, expander))) => (expander, kind),
+ _ => (ProcMacroExpander::dummy(), kind),
+ };
let proc_macro_id =
- ProcMacroLoc { container: module_id, id, expander, kind }.intern(self.db);
+ ProcMacroLoc { container: self.def_map.crate_root(), id, expander, kind }
+ .intern(self.db);
self.define_proc_macro(def.name.clone(), proc_macro_id);
+ let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
if let ProcMacroKind::CustomDerive { helpers } = def.kind {
- self.def_map
+ crate_data
.exported_derives
.insert(macro_id_to_def_id(self.db, proc_macro_id.into()), helpers);
}
- self.def_map.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
+ crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
}
/// Define a macro with `macro_rules`.
@@ -636,7 +658,7 @@ impl DefCollector<'_> {
// In Rust, `#[macro_export]` macros are unconditionally visible at the
// crate root, even if the parent modules is **not** visible.
if export {
- let module_id = self.def_map.root;
+ let module_id = DefMap::ROOT;
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
@@ -687,7 +709,7 @@ impl DefCollector<'_> {
/// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped.
/// And unconditionally exported.
fn define_proc_macro(&mut self, name: Name, macro_: ProcMacroId) {
- let module_id = self.def_map.root;
+ let module_id = DefMap::ROOT;
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
@@ -697,39 +719,28 @@ impl DefCollector<'_> {
);
}
- /// Import macros from `#[macro_use] extern crate`.
- fn import_macros_from_extern_crate(
- &mut self,
- current_module_id: LocalModuleId,
- extern_crate: &item_tree::ExternCrate,
- ) {
- tracing::debug!(
- "importing macros from extern crate: {:?} ({:?})",
- extern_crate,
- self.def_map.edition,
- );
-
- if let Some(m) = self.resolve_extern_crate(&extern_crate.name) {
- if m == self.def_map.module_id(current_module_id) {
- cov_mark::hit!(ignore_macro_use_extern_crate_self);
- return;
- }
-
- cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
- self.import_all_macros_exported(current_module_id, m.krate);
- }
- }
-
- /// Import all exported macros from another crate
+ /// Import exported macros from another crate. `names`, if `Some(_)`, specifies the name of
+ /// macros to be imported. Otherwise this method imports all exported macros.
///
/// Exported macros are just all macros in the root module scope.
/// Note that it contains not only all `#[macro_export]` macros, but also all aliases
/// created by `use` in the root module, ignoring the visibility of `use`.
- fn import_all_macros_exported(&mut self, current_module_id: LocalModuleId, krate: CrateId) {
+ fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option<Vec<Name>>) {
let def_map = self.db.crate_def_map(krate);
- for (name, def) in def_map[def_map.root].scope.macros() {
- // `#[macro_use]` brings macros into legacy scope. Yes, even non-`macro_rules!` macros.
- self.define_legacy_macro(current_module_id, name.clone(), def);
+ // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
+ // macros.
+ let root_scope = &def_map[DefMap::ROOT].scope;
+ if let Some(names) = names {
+ for name in names {
+ // FIXME: Report diagnostic on 404.
+ if let Some(def) = root_scope.get(&name).take_macros() {
+ self.def_map.macro_use_prelude.insert(name, def);
+ }
+ }
+ } else {
+ for (name, def) in root_scope.macros() {
+ self.def_map.macro_use_prelude.insert(name.clone(), def);
+ }
}
}
@@ -762,8 +773,9 @@ impl DefCollector<'_> {
}
fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
- let _p = profile::span("resolve_import").detail(|| format!("{}", import.path));
- tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
+ 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 {
let name = import
.path
@@ -785,6 +797,7 @@ impl DefCollector<'_> {
module_id,
&import.path,
BuiltinShadowMode::Module,
+ None, // An import may resolve to any kind of macro.
);
let def = res.resolved_def;
@@ -812,19 +825,12 @@ impl DefCollector<'_> {
}
}
- fn resolve_extern_crate(&self, name: &Name) -> Option<ModuleId> {
+ fn resolve_extern_crate(&self, name: &Name) -> Option<CrateRootModuleId> {
if *name == name!(self) {
cov_mark::hit!(extern_crate_self_as);
- let root = match self.def_map.block {
- Some(_) => {
- let def_map = self.def_map.crate_root(self.db).def_map(self.db);
- def_map.module_id(def_map.root())
- }
- None => self.def_map.module_id(self.def_map.root()),
- };
- Some(root)
+ Some(self.def_map.crate_root())
} else {
- self.deps.get(name).copied()
+ self.deps.get(name).map(|dep| CrateRootModuleId { krate: dep.crate_id })
}
}
@@ -863,11 +869,16 @@ impl DefCollector<'_> {
// 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
&& self.def_map.block.is_none()
- && module_id == self.def_map.root
+ && module_id == DefMap::ROOT
{
if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name)
{
- self.def_map.extern_prelude.insert(name.clone(), def);
+ if let Ok(def) = def.try_into() {
+ Arc::get_mut(&mut self.def_map.data)
+ .unwrap()
+ .extern_prelude
+ .insert(name.clone(), def);
+ }
}
}
@@ -1082,7 +1093,14 @@ impl DefCollector<'_> {
resolved.push((directive.module_id, directive.depth, directive.container, call_id));
};
let mut res = ReachedFixedPoint::Yes;
+ // Retain unresolved macros after this round of resolution.
macros.retain(|directive| {
+ let subns = match &directive.kind {
+ MacroDirectiveKind::FnLike { .. } => MacroSubNs::Bang,
+ MacroDirectiveKind::Attr { .. } | MacroDirectiveKind::Derive { .. } => {
+ MacroSubNs::Attr
+ }
+ };
let resolver = |path| {
let resolved_res = self.def_map.resolve_path_fp_with_macro(
self.db,
@@ -1090,6 +1108,7 @@ impl DefCollector<'_> {
directive.module_id,
&path,
BuiltinShadowMode::Module,
+ Some(subns),
);
resolved_res
.resolved_def
@@ -1101,15 +1120,15 @@ impl DefCollector<'_> {
match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
let call_id = macro_call_as_call_id(
- self.db,
+ self.db.upcast(),
ast_id,
*expand_to,
self.def_map.krate,
resolver_def_id,
- &mut |_err| (),
);
- if let Ok(Ok(call_id)) = call_id {
+ if let Ok(Some(call_id)) = call_id {
push_resolved(directive, call_id);
+
res = ReachedFixedPoint::No;
return false;
}
@@ -1134,7 +1153,7 @@ impl DefCollector<'_> {
// Record its helper attributes.
if def_id.krate != self.def_map.krate {
let def_map = self.db.crate_def_map(def_id.krate);
- if let Some(helpers) = def_map.exported_derives.get(&def_id) {
+ if let Some(helpers) = def_map.data.exported_derives.get(&def_id) {
self.def_map
.derive_helpers_in_scope
.entry(ast_id.ast_id.map(|it| it.upcast()))
@@ -1214,7 +1233,19 @@ impl DefCollector<'_> {
};
let ast_id = ast_id.with_value(ast_adt_id);
- match attr.parse_path_comma_token_tree() {
+ let extend_unhygenic;
+ let hygiene = if file_id.is_macro() {
+ self.hygienes
+ .entry(file_id)
+ .or_insert_with(|| Hygiene::new(self.db.upcast(), file_id))
+ } else {
+ // Avoid heap allocation (`Hygiene` embraces `Arc`) and hash map entry
+ // when we're in an oridinary (non-macro) file.
+ extend_unhygenic = Hygiene::new_unhygienic();
+ &extend_unhygenic
+ };
+
+ match attr.parse_path_comma_token_tree(self.db.upcast(), hygiene) {
Some(derive_macros) => {
let mut len = 0;
for (idx, path) in derive_macros.enumerate() {
@@ -1241,7 +1272,6 @@ impl DefCollector<'_> {
attr,
self.def_map.krate,
def,
- true,
);
self.def_map.modules[directive.module_id]
.scope
@@ -1261,18 +1291,12 @@ impl DefCollector<'_> {
}
// Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute.
- let call_id = attr_macro_as_call_id(
- self.db,
- file_ast_id,
- attr,
- self.def_map.krate,
- def,
- false,
- );
+ let call_id =
+ attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def);
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
// If proc attribute macro expansion is disabled, skip expanding it here
- if !self.db.enable_proc_attr_macros() {
+ if !self.db.expand_proc_attr_macros() {
self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
directive.module_id,
loc.kind,
@@ -1345,25 +1369,31 @@ impl DefCollector<'_> {
let file_id = macro_call_id.as_file();
// First, fetch the raw expansion result for purposes of error reporting. This goes through
- // `macro_expand_error` to avoid depending on the full expansion result (to improve
+ // `parse_macro_expansion_error` to avoid depending on the full expansion result (to improve
// incrementality).
- let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
- let err = self.db.macro_expand_error(macro_call_id);
+ let ExpandResult { value, err } = self.db.parse_macro_expansion_error(macro_call_id);
if let Some(err) = err {
+ let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
let diag = match err {
+ // why is this reported here?
hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
always!(krate == loc.def.krate);
- // Missing proc macros are non-fatal, so they are handled specially.
DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate)
}
- _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()),
+ _ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()),
};
self.def_map.diagnostics.push(diag);
}
+ if let errors @ [_, ..] = &*value {
+ let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
+ let diag = DefDiagnostic::macro_expansion_parse_error(module_id, loc.kind, &errors);
+ self.def_map.diagnostics.push(diag);
+ }
// Then, fetch and process the item tree. This will reuse the expansion result from above.
let item_tree = self.db.file_item_tree(file_id);
+
let mod_dir = self.mod_dirs[&module_id].clone();
ModCollector {
def_collector: &mut *self,
@@ -1384,8 +1414,9 @@ impl DefCollector<'_> {
for directive in &self.unresolved_macros {
match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
+ // FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error!
let macro_call_as_call_id = macro_call_as_call_id(
- self.db,
+ self.db.upcast(),
ast_id,
*expand_to,
self.def_map.krate,
@@ -1396,13 +1427,13 @@ impl DefCollector<'_> {
directive.module_id,
&path,
BuiltinShadowMode::Module,
+ Some(MacroSubNs::Bang),
);
resolved_res
.resolved_def
.take_macros()
.map(|it| macro_id_to_def_id(self.db, it))
},
- &mut |_| (),
);
if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
@@ -1489,6 +1520,7 @@ impl ModCollector<'_, '_> {
fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
let krate = self.def_collector.def_map.krate;
+ let is_crate_root = self.module_id == DefMap::ROOT;
// Note: don't assert that inserted value is fresh: it's simply not true
// for macros.
@@ -1496,28 +1528,22 @@ impl ModCollector<'_, '_> {
// Prelude module is always considered to be `#[macro_use]`.
if let Some(prelude_module) = self.def_collector.def_map.prelude {
- if prelude_module.krate != krate {
+ if prelude_module.krate != krate && is_crate_root {
cov_mark::hit!(prelude_is_macro_use);
- self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
+ self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None);
}
}
// This should be processed eagerly instead of deferred to resolving.
// `#[macro_use] extern crate` is hoisted to imports macros before collecting
// any other items.
- for &item in items {
- let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
- if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
+ //
+ // If we're not at the crate root, `macro_use`d extern crates are an error so let's just
+ // ignore them.
+ if is_crate_root {
+ for &item in items {
if let ModItem::ExternCrate(id) = item {
- let import = &self.item_tree[id];
- let attrs = self.item_tree.attrs(
- self.def_collector.db,
- krate,
- ModItem::from(id).into(),
- );
- if attrs.by_key("macro_use").exists() {
- self.def_collector.import_macros_from_extern_crate(self.module_id, import);
- }
+ self.process_macro_use_extern_crate(id);
}
}
}
@@ -1610,14 +1636,12 @@ impl ModCollector<'_, '_> {
FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db);
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
- if self.def_collector.is_proc_macro && self.module_id == def_map.root {
+ if self.def_collector.is_proc_macro && self.module_id == DefMap::ROOT {
if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) {
- let crate_root = def_map.module_id(def_map.root);
self.def_collector.export_proc_macro(
proc_macro,
ItemTreeId::new(self.tree_id, id),
fn_id,
- crate_root,
);
}
}
@@ -1744,6 +1768,50 @@ impl ModCollector<'_, '_> {
}
}
+ fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId<ExternCrate>) {
+ let db = self.def_collector.db;
+ let attrs = self.item_tree.attrs(
+ db,
+ self.def_collector.def_map.krate,
+ ModItem::from(extern_crate).into(),
+ );
+ if let Some(cfg) = attrs.cfg() {
+ if !self.is_cfg_enabled(&cfg) {
+ return;
+ }
+ }
+
+ let target_crate =
+ match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) {
+ Some(m) if m.krate == self.def_collector.def_map.krate => {
+ cov_mark::hit!(ignore_macro_use_extern_crate_self);
+ return;
+ }
+ Some(m) => m.krate,
+ None => return,
+ };
+
+ cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
+
+ let mut single_imports = Vec::new();
+ let hygiene = Hygiene::new_unhygienic();
+ for attr in attrs.by_key("macro_use").attrs() {
+ let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else {
+ // `#[macro_use]` (without any paths) found, forget collected names and just import
+ // all visible macros.
+ self.def_collector.import_macros_from_extern_crate(target_crate, None);
+ return;
+ };
+ for path in paths {
+ if let Some(name) = path.as_ident() {
+ single_imports.push(name.clone());
+ }
+ }
+ }
+
+ self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports));
+ }
+
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
let path_attr = attrs.by_key("path").string_value();
let is_macro_use = attrs.by_key("macro_use").exists();
@@ -1844,7 +1912,6 @@ impl ModCollector<'_, '_> {
let vis = def_map
.resolve_visibility(self.def_collector.db, self.module_id, visibility, false)
.unwrap_or(Visibility::Public);
- let modules = &mut def_map.modules;
let origin = match definition {
None => ModuleOrigin::Inline {
definition: declaration,
@@ -1858,11 +1925,16 @@ impl ModCollector<'_, '_> {
},
};
+ let modules = &mut def_map.modules;
let res = modules.alloc(ModuleData::new(origin, vis));
modules[res].parent = Some(self.module_id);
- for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() {
- for &mac in &mac {
- modules[res].scope.define_legacy_macro(name.clone(), mac);
+
+ if let Some((target, source)) = Self::borrow_modules(modules.as_mut(), res, self.module_id)
+ {
+ for (name, macs) in source.scope.legacy_macros() {
+ for &mac in macs {
+ target.scope.define_legacy_macro(name.clone(), mac);
+ }
}
}
modules[self.module_id].children.insert(name.clone(), res);
@@ -1919,7 +1991,10 @@ impl ModCollector<'_, '_> {
if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) {
continue;
}
- tracing::debug!("non-builtin attribute {}", attr.path);
+ tracing::debug!(
+ "non-builtin attribute {}",
+ attr.path.display(self.def_collector.db.upcast())
+ );
let ast_id = AstIdWithPath::new(
self.file_id(),
@@ -2053,8 +2128,8 @@ impl ModCollector<'_, '_> {
stdx::always!(
name == mac.name,
"built-in macro {} has #[rustc_builtin_macro] which declares different name {}",
- mac.name,
- name
+ mac.name.display(self.def_collector.db.upcast()),
+ name.display(self.def_collector.db.upcast())
);
helpers_opt = Some(helpers);
}
@@ -2089,73 +2164,60 @@ impl ModCollector<'_, '_> {
&self.item_tree[mac.visibility],
);
if let Some(helpers) = helpers_opt {
- self.def_collector
- .def_map
- .exported_derives
- .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers);
+ if self.def_collector.def_map.block.is_none() {
+ Arc::get_mut(&mut self.def_collector.def_map.data)
+ .unwrap()
+ .exported_derives
+ .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers);
+ }
}
}
fn collect_macro_call(&mut self, mac: &MacroCall, container: ItemContainerId) {
let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path));
+ let db = self.def_collector.db;
+
+ // FIXME: Immediately expanding in "Case 1" is insufficient since "Case 2" may also define
+ // new legacy macros that create textual scopes. We need a way to resolve names in textual
+ // scopes without eager expansion.
- // Case 1: try to resolve in legacy scope and expand macro_rules
- let mut error = None;
- match macro_call_as_call_id(
- self.def_collector.db,
+ // Case 1: try to resolve macro calls with single-segment name and expand macro_rules
+ if let Ok(res) = macro_call_as_call_id(
+ db.upcast(),
&ast_id,
mac.expand_to,
self.def_collector.def_map.krate,
|path| {
path.as_ident().and_then(|name| {
- self.def_collector.def_map.with_ancestor_maps(
- self.def_collector.db,
- self.module_id,
- &mut |map, module| {
- map[module]
- .scope
- .get_legacy_macro(name)
- .and_then(|it| it.last())
- .map(|&it| macro_id_to_def_id(self.def_collector.db, it))
- },
- )
+ let def_map = &self.def_collector.def_map;
+ def_map
+ .with_ancestor_maps(db, self.module_id, &mut |map, module| {
+ map[module].scope.get_legacy_macro(name)?.last().copied()
+ })
+ .or_else(|| def_map[self.module_id].scope.get(name).take_macros())
+ .or_else(|| def_map.macro_use_prelude.get(name).copied())
+ .filter(|&id| {
+ sub_namespace_match(
+ Some(MacroSubNs::from_id(db, id)),
+ Some(MacroSubNs::Bang),
+ )
+ })
+ .map(|it| macro_id_to_def_id(self.def_collector.db, it))
})
},
- &mut |err| {
- error.get_or_insert(err);
- },
) {
- Ok(Ok(macro_call_id)) => {
- // Legacy macros need to be expanded immediately, so that any macros they produce
- // are in scope.
+ // Legacy macros need to be expanded immediately, so that any macros they produce
+ // are in scope.
+ if let Some(val) = res {
self.def_collector.collect_macro_expansion(
self.module_id,
- macro_call_id,
+ val,
self.macro_depth + 1,
container,
);
-
- if let Some(err) = error {
- self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
- self.module_id,
- MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
- err.to_string(),
- ));
- }
-
- return;
}
- Ok(Err(_)) => {
- // Built-in macro failed eager expansion.
- self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
- self.module_id,
- MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
- error.unwrap().to_string(),
- ));
- return;
- }
- Err(UnresolvedMacro { .. }) => (),
+ return;
}
// Case 2: resolve in module scope, expand during name resolution.
@@ -2168,14 +2230,40 @@ impl ModCollector<'_, '_> {
}
fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) {
- let macros = self.def_collector.def_map[module_id].scope.collect_legacy_macros();
- for (name, macs) in macros {
+ 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() {
macs.last().map(|&mac| {
- self.def_collector.define_legacy_macro(self.module_id, name.clone(), mac)
+ target.scope.define_legacy_macro(name.clone(), mac);
});
}
}
+ /// Mutably borrow two modules at once, retu
+ fn borrow_modules(
+ modules: &mut [ModuleData],
+ a: LocalModuleId,
+ b: LocalModuleId,
+ ) -> Option<(&mut ModuleData, &mut ModuleData)> {
+ let a = a.into_raw().into_u32() as usize;
+ let b = b.into_raw().into_u32() as usize;
+
+ let (a, b) = match a.cmp(&b) {
+ Ordering::Equal => return None,
+ Ordering::Less => {
+ let (prefix, b) = modules.split_at_mut(b);
+ (&mut prefix[a], &mut b[0])
+ }
+ Ordering::Greater => {
+ let (prefix, a) = modules.split_at_mut(a);
+ (&mut a[0], &mut prefix[b])
+ }
+ };
+ Some((a, b))
+ }
+
fn is_cfg_enabled(&self, cfg: &CfgExpr) -> bool {
self.def_collector.cfg_options.check(cfg) != Some(false)
}
@@ -2215,10 +2303,11 @@ mod tests {
unresolved_macros: Vec::new(),
mod_dirs: FxHashMap::default(),
cfg_options: &CfgOptions::default(),
- proc_macros: Default::default(),
+ proc_macros: Ok(vec![]),
from_glob_import: Default::default(),
skip_attrs: Default::default(),
is_proc_macro: false,
+ hygienes: FxHashMap::default(),
};
collector.seed_with_top_level();
collector.collect();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
index b024d7c67..18b424255 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
@@ -4,7 +4,10 @@ use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
use hir_expand::{attrs::AttrId, MacroCallKind};
use la_arena::Idx;
-use syntax::ast::{self, AnyHasAttrs};
+use syntax::{
+ ast::{self, AnyHasAttrs},
+ SyntaxError,
+};
use crate::{
item_tree::{self, ItemTreeId},
@@ -29,11 +32,15 @@ pub enum DefDiagnosticKind {
MacroError { ast: MacroCallKind, message: String },
+ MacroExpansionParseError { ast: MacroCallKind, errors: Box<[SyntaxError]> },
+
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
MalformedDerive { ast: AstId<ast::Adt>, id: usize },
+
+ MacroDefError { ast: AstId<ast::Macro>, message: String },
}
#[derive(Debug, PartialEq, Eq)]
@@ -81,7 +88,8 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
}
- pub(super) fn unresolved_proc_macro(
+ // FIXME: Whats the difference between this and unresolved_macro_call
+ pub(crate) fn unresolved_proc_macro(
container: LocalModuleId,
ast: MacroCallKind,
krate: CrateId,
@@ -89,7 +97,7 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } }
}
- pub(super) fn macro_error(
+ pub(crate) fn macro_error(
container: LocalModuleId,
ast: MacroCallKind,
message: String,
@@ -97,7 +105,22 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } }
}
- pub(super) fn unresolved_macro_call(
+ pub(crate) fn macro_expansion_parse_error(
+ container: LocalModuleId,
+ ast: MacroCallKind,
+ errors: &[SyntaxError],
+ ) -> Self {
+ Self {
+ in_module: container,
+ kind: DefDiagnosticKind::MacroExpansionParseError {
+ ast,
+ errors: errors.to_vec().into_boxed_slice(),
+ },
+ }
+ }
+
+ // FIXME: Whats the difference between this and unresolved_proc_macro
+ pub(crate) fn unresolved_macro_call(
container: LocalModuleId,
ast: MacroCallKind,
path: ModPath,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
index 51c565fe1..2dcc2c30f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
@@ -74,12 +74,20 @@ impl ModDir {
candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
}
None if file_id.is_include_macro(db.upcast()) => {
- candidate_files.push(format!("{name}.rs"));
- candidate_files.push(format!("{name}/mod.rs"));
+ candidate_files.push(format!("{}.rs", name.display(db.upcast())));
+ candidate_files.push(format!("{}/mod.rs", name.display(db.upcast())));
}
None => {
- candidate_files.push(format!("{}{name}.rs", self.dir_path.0));
- candidate_files.push(format!("{}{name}/mod.rs", self.dir_path.0));
+ candidate_files.push(format!(
+ "{}{}.rs",
+ self.dir_path.0,
+ name.display(db.upcast())
+ ));
+ candidate_files.push(format!(
+ "{}{}/mod.rs",
+ self.dir_path.0,
+ name.display(db.upcast())
+ ));
}
};
@@ -91,7 +99,7 @@ impl ModDir {
let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
(DirPath::empty(), false)
} else {
- (DirPath::new(format!("{name}/")), true)
+ (DirPath::new(format!("{}/", name.display(db.upcast()))), true)
};
if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) {
return Ok((file_id, is_mod_rs, mod_dir));
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
index 25478481d..5f6163175 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
@@ -16,11 +16,11 @@ use hir_expand::name::Name;
use crate::{
db::DefDatabase,
item_scope::BUILTIN_SCOPE,
- nameres::{BuiltinShadowMode, DefMap},
+ nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs},
path::{ModPath, PathKind},
per_ns::PerNs,
visibility::{RawVisibility, Visibility},
- AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
+ AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -58,18 +58,22 @@ impl ResolvePathResult {
}
}
-impl DefMap {
- pub(super) fn resolve_name_in_extern_prelude(
- &self,
+impl PerNs {
+ pub(super) fn filter_macro(
+ mut self,
db: &dyn DefDatabase,
- name: &Name,
- ) -> Option<ModuleId> {
- match self.block {
- Some(_) => self.crate_root(db).def_map(db).extern_prelude.get(name).copied(),
- None => self.extern_prelude.get(name).copied(),
- }
+ expected: Option<MacroSubNs>,
+ ) -> Self {
+ self.macros = self.macros.filter(|&(id, _)| {
+ let this = MacroSubNs::from_id(db, id);
+ sub_namespace_match(Some(this), expected)
+ });
+
+ self
}
+}
+impl DefMap {
pub(crate) fn resolve_visibility(
&self,
db: &dyn DefDatabase,
@@ -83,7 +87,7 @@ impl DefMap {
let mut vis = match visibility {
RawVisibility::Module(path) => {
let (result, remaining) =
- self.resolve_path(db, original_module, path, BuiltinShadowMode::Module);
+ self.resolve_path(db, original_module, path, BuiltinShadowMode::Module, None);
if remaining.is_some() {
return None;
}
@@ -106,7 +110,7 @@ impl DefMap {
// ...unless we're resolving visibility for an associated item in an impl.
if self.block_id() != m.block && !within_impl {
cov_mark::hit!(adjust_vis_in_block_def_map);
- vis = Visibility::Module(self.module_id(self.root()));
+ vis = Visibility::Module(self.module_id(Self::ROOT));
tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
}
}
@@ -124,6 +128,9 @@ impl DefMap {
mut original_module: LocalModuleId,
path: &ModPath,
shadow: BuiltinShadowMode,
+ // Pass `MacroSubNs` if we know we're resolving macro names and which kind of macro we're
+ // resolving them to. Pass `None` otherwise, e.g. when we're resolving import paths.
+ expected_macro_subns: Option<MacroSubNs>,
) -> ResolvePathResult {
let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
@@ -136,6 +143,7 @@ impl DefMap {
original_module,
path,
shadow,
+ expected_macro_subns,
);
// Merge `new` into `result`.
@@ -154,7 +162,7 @@ impl DefMap {
match &current_map.block {
Some(block) => {
original_module = block.parent.local_id;
- arc = block.parent.def_map(db);
+ arc = block.parent.def_map(db, current_map.krate);
current_map = &*arc;
}
None => return result,
@@ -169,11 +177,15 @@ impl DefMap {
original_module: LocalModuleId,
path: &ModPath,
shadow: BuiltinShadowMode,
+ expected_macro_subns: Option<MacroSubNs>,
) -> ResolvePathResult {
let graph = db.crate_graph();
let _cx = stdx::panic_context::enter(format!(
- "DefMap {:?} crate_name={:?} block={:?} path={path}",
- self.krate, graph[self.krate].display_name, self.block
+ "DefMap {:?} crate_name={:?} block={:?} path={}",
+ self.krate,
+ graph[self.krate].display_name,
+ self.block,
+ path.display(db.upcast())
));
let mut segments = path.segments().iter().enumerate();
@@ -181,21 +193,21 @@ impl DefMap {
PathKind::DollarCrate(krate) => {
if krate == self.krate {
cov_mark::hit!(macro_dollar_crate_self);
- PerNs::types(self.crate_root(db).into(), Visibility::Public)
+ PerNs::types(self.crate_root().into(), Visibility::Public)
} else {
let def_map = db.crate_def_map(krate);
- let module = def_map.module_id(def_map.root);
+ let module = def_map.module_id(Self::ROOT);
cov_mark::hit!(macro_dollar_crate_other);
PerNs::types(module.into(), Visibility::Public)
}
}
- PathKind::Crate => PerNs::types(self.crate_root(db).into(), Visibility::Public),
+ PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public),
// plain import or absolute path in 2015: crate-relative with
// fallback to extern prelude (with the simplification in
// rust-lang/rust#57745)
// FIXME there must be a nicer way to write this condition
PathKind::Plain | PathKind::Abs
- if self.edition == Edition::Edition2015
+ if self.data.edition == Edition::Edition2015
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
{
let (_, segment) = match segments.next() {
@@ -220,7 +232,13 @@ impl DefMap {
if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };
tracing::debug!("resolving {:?} in module", segment);
- self.resolve_name_in_module(db, original_module, segment, prefer_module)
+ self.resolve_name_in_module(
+ db,
+ original_module,
+ segment,
+ prefer_module,
+ expected_macro_subns,
+ )
}
PathKind::Super(lvl) => {
let mut module = original_module;
@@ -236,16 +254,20 @@ impl DefMap {
);
tracing::debug!(
"`super` path: {} -> {} in parent map",
- path,
- new_path
- );
- return block.parent.def_map(db).resolve_path_fp_with_macro(
- db,
- mode,
- block.parent.local_id,
- &new_path,
- shadow,
+ 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");
@@ -271,7 +293,7 @@ impl DefMap {
Some((_, segment)) => segment,
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
};
- if let Some(&def) = self.extern_prelude.get(segment) {
+ if let Some(&def) = self.data.extern_prelude.get(segment) {
tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
PerNs::types(def.into(), Visibility::Public)
} else {
@@ -303,7 +325,12 @@ impl DefMap {
);
tracing::debug!("resolving {:?} in other crate", path);
let defp_map = module.def_map(db);
- let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
+ // Macro sub-namespaces only matter when resolving single-segment paths
+ // because `macro_use` and other preludes should be taken into account. At
+ // this point, we know we're resolving a multi-segment path so macro kind
+ // expectation is discarded.
+ let (def, s) =
+ defp_map.resolve_path(db, module.local_id, &path, shadow, None);
return ResolvePathResult::with(
def,
ReachedFixedPoint::Yes,
@@ -331,11 +358,11 @@ impl DefMap {
Some(local_id) => {
let variant = EnumVariantId { parent: e, local_id };
match &*enum_data.variants[local_id].variant_data {
- crate::adt::VariantData::Record(_) => {
+ crate::data::adt::VariantData::Record(_) => {
PerNs::types(variant.into(), Visibility::Public)
}
- crate::adt::VariantData::Tuple(_)
- | crate::adt::VariantData::Unit => {
+ crate::data::adt::VariantData::Tuple(_)
+ | crate::data::adt::VariantData::Unit => {
PerNs::both(variant.into(), variant.into(), Visibility::Public)
}
}
@@ -381,19 +408,24 @@ impl DefMap {
module: LocalModuleId,
name: &Name,
shadow: BuiltinShadowMode,
+ expected_macro_subns: Option<MacroSubNs>,
) -> PerNs {
// Resolve in:
// - legacy scope of macro
// - current module / scope
- // - extern prelude
+ // - extern prelude / macro_use prelude
// - std prelude
let from_legacy_macro = self[module]
.scope
.get_legacy_macro(name)
// FIXME: shadowing
.and_then(|it| it.last())
- .map_or_else(PerNs::none, |&m| PerNs::macros(m, Visibility::Public));
- let from_scope = self[module].scope.get(name);
+ .copied()
+ .filter(|&id| {
+ sub_namespace_match(Some(MacroSubNs::from_id(db, id)), expected_macro_subns)
+ })
+ .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
+ let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns);
let from_builtin = match self.block {
Some(_) => {
// Only resolve to builtins in the root `DefMap`.
@@ -410,13 +442,27 @@ impl DefMap {
};
let extern_prelude = || {
- self.extern_prelude
+ if self.block.is_some() {
+ // Don't resolve extern prelude in block `DefMap`s.
+ return PerNs::none();
+ }
+ self.data
+ .extern_prelude
.get(name)
.map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
};
+ let macro_use_prelude = || {
+ self.macro_use_prelude
+ .get(name)
+ .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public))
+ };
let prelude = || self.resolve_in_prelude(db, name);
- from_legacy_macro.or(from_scope_or_builtin).or_else(extern_prelude).or_else(prelude)
+ from_legacy_macro
+ .or(from_scope_or_builtin)
+ .or_else(extern_prelude)
+ .or_else(macro_use_prelude)
+ .or_else(prelude)
}
fn resolve_name_in_crate_root_or_extern_prelude(
@@ -426,13 +472,20 @@ impl DefMap {
) -> PerNs {
let from_crate_root = match self.block {
Some(_) => {
- let def_map = self.crate_root(db).def_map(db);
- def_map[def_map.root].scope.get(name)
+ let def_map = self.crate_root().def_map(db);
+ def_map[Self::ROOT].scope.get(name)
}
- None => self[self.root].scope.get(name),
+ None => self[Self::ROOT].scope.get(name),
};
let from_extern_prelude = || {
- self.resolve_name_in_extern_prelude(db, name)
+ if self.block.is_some() {
+ // Don't resolve extern prelude in block `DefMap`s.
+ return PerNs::none();
+ }
+ self.data
+ .extern_prelude
+ .get(name)
+ .copied()
.map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public))
};
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
index caad4a1f3..751b7beaa 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
@@ -52,7 +52,7 @@ impl Attrs {
}
// This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have
-// the same strucuture.
+// the same structure.
#[rustfmt::skip]
pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> {
match tt {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
index 8a27c60df..dd7c3c363 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
@@ -4,10 +4,9 @@ mod macros;
mod mod_resolution;
mod primitives;
-use std::sync::Arc;
-
use base_db::{fixture::WithFixture, SourceDatabase};
use expect_test::{expect, Expect};
+use triomphe::Arc;
use crate::{db::DefDatabase, test_db::TestDB};
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
index 13e6825f8..4931c36bb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
@@ -1,8 +1,7 @@
-use std::sync::Arc;
-
use base_db::SourceDatabaseExt;
+use triomphe::Arc;
-use crate::{AdtId, ModuleDefId};
+use crate::{db::DefDatabase, AdtId, ModuleDefId};
use super::*;
@@ -15,7 +14,7 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change:
});
assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
}
- db.set_file_text(pos.file_id, Arc::new(ra_fixture_change.to_string()));
+ db.set_file_text(pos.file_id, Arc::from(ra_fixture_change));
{
let events = db.log_executed(|| {
@@ -96,7 +95,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
});
assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
}
- db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string()));
+ db.set_file_text(pos.file_id, Arc::from("m!(Y);"));
{
let events = db.log_executed(|| {
@@ -109,7 +108,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
}
#[test]
-fn typing_inside_a_function_should_not_invalidate_expansions() {
+fn typing_inside_a_function_should_not_invalidate_item_expansions() {
let (mut db, pos) = TestDB::with_position(
r#"
//- /lib.rs
@@ -140,7 +139,7 @@ m!(Z);
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
assert_eq!(n_recalculated_item_trees, 6);
let n_reparsed_macros =
- events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
+ events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
assert_eq!(n_reparsed_macros, 3);
}
@@ -150,7 +149,7 @@ fn quux() { 92 }
m!(Y);
m!(Z);
"#;
- db.set_file_text(pos.file_id, Arc::new(new_text.to_string()));
+ db.set_file_text(pos.file_id, Arc::from(new_text));
{
let events = db.log_executed(|| {
@@ -161,7 +160,7 @@ m!(Z);
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
assert_eq!(n_recalculated_item_trees, 1);
let n_reparsed_macros =
- events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
+ events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
assert_eq!(n_reparsed_macros, 0);
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
index a4ccd14cb..f4cca8d68 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
@@ -260,6 +260,72 @@ mod priv_mod {
}
#[test]
+fn macro_use_filter() {
+ check(
+ r#"
+//- /main.rs crate:main deps:empty,multiple,all
+#[macro_use()]
+extern crate empty;
+
+foo_not_imported!();
+
+#[macro_use(bar1)]
+#[macro_use()]
+#[macro_use(bar2, bar3)]
+extern crate multiple;
+
+bar1!();
+bar2!();
+bar3!();
+bar_not_imported!();
+
+#[macro_use(baz1)]
+#[macro_use]
+#[macro_use(baz2)]
+extern crate all;
+
+baz1!();
+baz2!();
+baz3!();
+
+//- /empty.rs crate:empty
+#[macro_export]
+macro_rules! foo_not_imported { () => { struct NotOkFoo; } }
+
+//- /multiple.rs crate:multiple
+#[macro_export]
+macro_rules! bar1 { () => { struct OkBar1; } }
+#[macro_export]
+macro_rules! bar2 { () => { struct OkBar2; } }
+#[macro_export]
+macro_rules! bar3 { () => { struct OkBar3; } }
+#[macro_export]
+macro_rules! bar_not_imported { () => { struct NotOkBar; } }
+
+//- /all.rs crate:all
+#[macro_export]
+macro_rules! baz1 { () => { struct OkBaz1; } }
+#[macro_export]
+macro_rules! baz2 { () => { struct OkBaz2; } }
+#[macro_export]
+macro_rules! baz3 { () => { struct OkBaz3; } }
+"#,
+ expect![[r#"
+ crate
+ OkBar1: t v
+ OkBar2: t v
+ OkBar3: t v
+ OkBaz1: t v
+ OkBaz2: t v
+ OkBaz3: t v
+ all: t
+ empty: t
+ multiple: t
+ "#]],
+ );
+}
+
+#[test]
fn prelude_is_macro_use() {
cov_mark::check!(prelude_is_macro_use);
check(
@@ -665,6 +731,29 @@ pub struct bar;
}
#[test]
+fn macro_dollar_crate_is_correct_in_derive_meta() {
+ let map = compute_crate_def_map(
+ r#"
+//- minicore: derive, clone
+//- /main.rs crate:main deps:lib
+lib::foo!();
+
+//- /lib.rs crate:lib
+#[macro_export]
+macro_rules! foo {
+ () => {
+ #[derive($crate::Clone)]
+ struct S;
+ }
+}
+
+pub use core::clone::Clone;
+"#,
+ );
+ assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
+}
+
+#[test]
fn expand_derive() {
let map = compute_crate_def_map(
r#"
@@ -683,7 +772,7 @@ pub macro Copy {}
pub macro Clone {}
"#,
);
- assert_eq!(map.modules[map.root].scope.impls().len(), 2);
+ assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2);
}
#[test]
@@ -726,7 +815,7 @@ pub macro derive($item:item) {}
pub macro Clone {}
"#,
);
- assert_eq!(map.modules[map.root].scope.impls().len(), 1);
+ assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
}
#[test]
@@ -991,7 +1080,7 @@ macro_rules! mbe {
#[test]
fn collects_derive_helpers() {
- let def_map = compute_crate_def_map(
+ let db = TestDB::with_files(
r#"
#![crate_type="proc-macro"]
struct TokenStream;
@@ -1002,11 +1091,13 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
}
"#,
);
+ let krate = db.crate_graph().iter().next().unwrap();
+ let def_map = db.crate_def_map(krate);
- assert_eq!(def_map.exported_derives.len(), 1);
- match def_map.exported_derives.values().next() {
+ assert_eq!(def_map.data.exported_derives.len(), 1);
+ match def_map.data.exported_derives.values().next() {
Some(helpers) => match &**helpers {
- [attr] => assert_eq!(attr.to_string(), "helper_attr"),
+ [attr] => assert_eq!(attr.display(&db).to_string(), "helper_attr"),
_ => unreachable!(),
},
_ => unreachable!(),
@@ -1169,7 +1260,7 @@ struct A;
#[test]
fn macro_use_imports_all_macro_types() {
- let def_map = compute_crate_def_map(
+ let db = TestDB::with_files(
r#"
//- /main.rs crate:main deps:lib
#[macro_use]
@@ -1192,18 +1283,153 @@ struct TokenStream;
fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
"#,
);
+ let krate = db.crate_graph().iter().next().unwrap();
+ let def_map = db.crate_def_map(krate);
+
+ let root_module = &def_map[DefMap::ROOT].scope;
+ assert!(
+ root_module.legacy_macros().count() == 0,
+ "`#[macro_use]` shouldn't bring macros into textual macro scope",
+ );
- let root = &def_map[def_map.root()].scope;
- let actual = root
- .legacy_macros()
- .sorted_by(|a, b| std::cmp::Ord::cmp(&a.0, &b.0))
- .map(|(name, _)| format!("{name}\n"))
- .collect::<String>();
+ let actual = def_map
+ .macro_use_prelude
+ .iter()
+ .map(|(name, _)| name.display(&db).to_string())
+ .sorted()
+ .join("\n");
expect![[r#"
legacy
macro20
- proc_attr
- "#]]
+ proc_attr"#]]
.assert_eq(&actual);
}
+
+#[test]
+fn non_prelude_macros_take_precedence_over_macro_use_prelude() {
+ check(
+ r#"
+//- /lib.rs edition:2021 crate:lib deps:dep,core
+#[macro_use]
+extern crate dep;
+
+macro foo() { struct Ok; }
+macro bar() { fn ok() {} }
+
+foo!();
+bar!();
+
+//- /dep.rs crate:dep
+#[macro_export]
+macro_rules! foo {
+ () => { struct NotOk; }
+}
+
+//- /core.rs crate:core
+pub mod prelude {
+ pub mod rust_2021 {
+ #[macro_export]
+ macro_rules! bar {
+ () => { fn not_ok() {} }
+ }
+ }
+}
+ "#,
+ expect![[r#"
+ crate
+ Ok: t v
+ bar: m
+ dep: t
+ foo: m
+ ok: v
+ "#]],
+ );
+}
+
+#[test]
+fn macro_use_prelude_is_eagerly_expanded() {
+ // See FIXME in `ModCollector::collect_macro_call()`.
+ check(
+ r#"
+//- /main.rs crate:main deps:lib
+#[macro_use]
+extern crate lib;
+mk_foo!();
+mod a {
+ foo!();
+}
+//- /lib.rs crate:lib
+#[macro_export]
+macro_rules! mk_foo {
+ () => {
+ macro_rules! foo {
+ () => { struct Ok; }
+ }
+ }
+}
+ "#,
+ expect![[r#"
+ crate
+ a: t
+ lib: t
+
+ crate::a
+ Ok: t v
+ "#]],
+ );
+}
+
+#[test]
+fn macro_sub_namespace() {
+ let map = compute_crate_def_map(
+ r#"
+//- minicore: derive, clone
+macro_rules! Clone { () => {} }
+macro_rules! derive { () => {} }
+
+#[derive(Clone)]
+struct S;
+ "#,
+ );
+ assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
+}
+
+#[test]
+fn macro_sub_namespace2() {
+ check(
+ r#"
+//- /main.rs edition:2021 crate:main deps:proc,core
+use proc::{foo, bar};
+
+foo!();
+bar!();
+
+//- /proc.rs crate:proc
+#![crate_type="proc-macro"]
+#[proc_macro_derive(foo)]
+pub fn foo() {}
+#[proc_macro_attribute]
+pub fn bar() {}
+
+//- /core.rs crate:core
+pub mod prelude {
+ pub mod rust_2021 {
+ pub macro foo() {
+ struct Ok;
+ }
+ pub macro bar() {
+ fn ok() {}
+ }
+ }
+}
+ "#,
+ expect![[r#"
+ crate
+ Ok: t v
+ bar: m
+ foo: m
+ ok: v
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
index a01931288..81bc0ff91 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
@@ -887,7 +887,7 @@ mod module;
//- /module.rs
#![cfg(NEVER)]
-struct AlsoShoulntAppear;
+struct AlsoShouldNotAppear;
"#,
expect![[r#"
crate
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
index f3197d180..ff4ae6954 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -7,15 +7,14 @@ use std::{
};
use crate::{
- body::LowerCtx,
- type_ref::{ConstRefOrPath, LifetimeRef},
+ lang_item::LangItemTarget,
+ lower::LowerCtx,
+ type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
};
use hir_expand::name::Name;
use intern::Interned;
use syntax::ast;
-use crate::type_ref::{TypeBound, TypeRef};
-
pub use hir_expand::mod_path::{path, ModPath, PathKind};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -36,13 +35,19 @@ impl Display for ImportAlias {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Path {
- /// Type based path like `<T>::foo`.
- /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
- type_anchor: Option<Interned<TypeRef>>,
- mod_path: Interned<ModPath>,
- /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
- generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
+pub enum Path {
+ /// A normal path
+ Normal {
+ /// Type based path like `<T>::foo`.
+ /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
+ type_anchor: Option<Interned<TypeRef>>,
+ mod_path: Interned<ModPath>,
+ /// 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
+ /// links via a normal path since they might be private and not accessible in the usage place.
+ LangItem(LangItemTarget),
}
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
@@ -85,7 +90,7 @@ pub struct AssociatedTypeBinding {
pub enum GenericArg {
Type(TypeRef),
Lifetime(LifetimeRef),
- Const(ConstRefOrPath),
+ Const(ConstRef),
}
impl Path {
@@ -102,51 +107,77 @@ impl Path {
) -> Path {
let generic_args = generic_args.into();
assert_eq!(path.len(), generic_args.len());
- Path { type_anchor: None, mod_path: Interned::new(path), generic_args: Some(generic_args) }
+ Path::Normal {
+ type_anchor: None,
+ mod_path: Interned::new(path),
+ generic_args: Some(generic_args),
+ }
+ }
+
+ /// Converts a known mod path to `Path`.
+ pub fn from_known_path_with_no_generic(path: ModPath) -> Path {
+ Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None }
}
pub fn kind(&self) -> &PathKind {
- &self.mod_path.kind
+ match self {
+ Path::Normal { mod_path, .. } => &mod_path.kind,
+ Path::LangItem(_) => &PathKind::Abs,
+ }
}
pub fn type_anchor(&self) -> Option<&TypeRef> {
- self.type_anchor.as_deref()
+ match self {
+ Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
+ Path::LangItem(_) => None,
+ }
}
pub fn segments(&self) -> PathSegments<'_> {
- let s = PathSegments {
- segments: self.mod_path.segments(),
- generic_args: self.generic_args.as_deref(),
+ let Path::Normal { mod_path, generic_args, .. } = self else {
+ return PathSegments {
+ segments: &[],
+ generic_args: None,
+ };
};
+ let s =
+ PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
if let Some(generic_args) = s.generic_args {
assert_eq!(s.segments.len(), generic_args.len());
}
s
}
- pub fn mod_path(&self) -> &ModPath {
- &self.mod_path
+ pub fn mod_path(&self) -> Option<&ModPath> {
+ match self {
+ Path::Normal { mod_path, .. } => Some(&mod_path),
+ Path::LangItem(_) => None,
+ }
}
pub fn qualifier(&self) -> Option<Path> {
- if self.mod_path.is_ident() {
+ let Path::Normal { mod_path, generic_args, type_anchor } = self else {
+ return None;
+ };
+ if mod_path.is_ident() {
return None;
}
- let res = Path {
- type_anchor: self.type_anchor.clone(),
+ let res = Path::Normal {
+ type_anchor: type_anchor.clone(),
mod_path: Interned::new(ModPath::from_segments(
- self.mod_path.kind,
- self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(),
+ mod_path.kind,
+ mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
)),
- generic_args: self.generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
+ generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
};
Some(res)
}
pub fn is_self_type(&self) -> bool {
- self.type_anchor.is_none()
- && self.generic_args.as_deref().is_none()
- && self.mod_path.is_Self()
+ let Path::Normal { mod_path, generic_args, type_anchor } = self else {
+ return false;
+ };
+ type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self()
}
}
@@ -222,7 +253,7 @@ impl GenericArgs {
impl From<Name> for Path {
fn from(name: Name) -> Path {
- Path {
+ Path::Normal {
type_anchor: None,
mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
generic_args: None,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
index b7542bd77..1cb17ff0d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
@@ -2,17 +2,15 @@
use std::iter;
-use crate::type_ref::ConstRefOrPath;
+use crate::{lower::LowerCtx, type_ref::ConstRef};
use either::Either;
use hir_expand::name::{name, AsName};
use intern::Interned;
use syntax::ast::{self, AstNode, HasTypeBounds};
-use super::AssociatedTypeBinding;
use crate::{
- body::LowerCtx,
- path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
+ path::{AssociatedTypeBinding, GenericArg, GenericArgs, ModPath, Path, PathKind},
type_ref::{LifetimeRef, TypeBound, TypeRef},
};
@@ -75,8 +73,11 @@ 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 { mod_path, generic_args: path_generic_args, .. } =
- Path::from_src(trait_ref.path()?, ctx)?;
+ let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
+ Path::from_src(trait_ref.path()?, ctx)? else
+ {
+ return None;
+ };
let num_segments = mod_path.segments().len();
kind = mod_path.kind;
@@ -157,7 +158,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
}
let mod_path = Interned::new(ModPath::from_segments(kind, segments));
- return Some(Path {
+ return Some(Path::Normal {
type_anchor,
mod_path,
generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },
@@ -188,6 +189,10 @@ pub(super) fn lower_generic_args(
args.push(GenericArg::Type(type_ref));
}
ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
+ if assoc_type_arg.param_list().is_some() {
+ // We currently ignore associated return type bounds.
+ continue;
+ }
if let Some(name_ref) = assoc_type_arg.name_ref() {
let name = name_ref.as_name();
let args = assoc_type_arg
@@ -212,7 +217,7 @@ pub(super) fn lower_generic_args(
}
}
ast::GenericArg::ConstArg(arg) => {
- let arg = ConstRefOrPath::from_expr_opt(arg.expr());
+ let arg = ConstRef::from_const_arg(lower_ctx, Some(arg));
args.push(GenericArg::Const(arg))
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
index 2d45c8c8d..0aead6f37 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
@@ -2,7 +2,7 @@
use std::fmt::{self, Write};
-use hir_expand::mod_path::PathKind;
+use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
use intern::Interned;
use itertools::Itertools;
@@ -11,11 +11,14 @@ use crate::{
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
};
-pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
+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:?}");
+ }
match path.type_anchor() {
Some(anchor) => {
write!(buf, "<")?;
- print_type_ref(anchor, buf)?;
+ print_type_ref(db, anchor, buf)?;
write!(buf, ">::")?;
}
None => match path.kind() {
@@ -41,10 +44,10 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
write!(buf, "::")?;
}
- write!(buf, "{}", segment.name)?;
+ write!(buf, "{}", segment.name.display(db))?;
if let Some(generics) = segment.args_and_bindings {
write!(buf, "::<")?;
- print_generic_args(generics, buf)?;
+ print_generic_args(db, generics, buf)?;
write!(buf, ">")?;
}
@@ -53,12 +56,16 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
Ok(())
}
-pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_generic_args(
+ db: &dyn ExpandDatabase,
+ generics: &GenericArgs,
+ buf: &mut dyn Write,
+) -> fmt::Result {
let mut first = true;
let args = if generics.has_self_type {
let (self_ty, args) = generics.args.split_first().unwrap();
write!(buf, "Self=")?;
- print_generic_arg(self_ty, buf)?;
+ print_generic_arg(db, self_ty, buf)?;
first = false;
args
} else {
@@ -69,35 +76,43 @@ pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) ->
write!(buf, ", ")?;
}
first = false;
- print_generic_arg(arg, buf)?;
+ print_generic_arg(db, arg, buf)?;
}
for binding in generics.bindings.iter() {
if !first {
write!(buf, ", ")?;
}
first = false;
- write!(buf, "{}", binding.name)?;
+ write!(buf, "{}", binding.name.display(db))?;
if !binding.bounds.is_empty() {
write!(buf, ": ")?;
- print_type_bounds(&binding.bounds, buf)?;
+ print_type_bounds(db, &binding.bounds, buf)?;
}
if let Some(ty) = &binding.type_ref {
write!(buf, " = ")?;
- print_type_ref(ty, buf)?;
+ print_type_ref(db, ty, buf)?;
}
}
Ok(())
}
-pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_generic_arg(
+ db: &dyn ExpandDatabase,
+ arg: &GenericArg,
+ buf: &mut dyn Write,
+) -> fmt::Result {
match arg {
- GenericArg::Type(ty) => print_type_ref(ty, buf),
- GenericArg::Const(c) => write!(buf, "{c}"),
- GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name),
+ GenericArg::Type(ty) => print_type_ref(db, ty, buf),
+ GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
+ GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)),
}
}
-pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_type_ref(
+ db: &dyn ExpandDatabase,
+ type_ref: &TypeRef,
+ buf: &mut dyn Write,
+) -> fmt::Result {
// FIXME: deduplicate with `HirDisplay` impl
match type_ref {
TypeRef::Never => write!(buf, "!")?,
@@ -108,18 +123,18 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
if i != 0 {
write!(buf, ", ")?;
}
- print_type_ref(field, buf)?;
+ print_type_ref(db, field, buf)?;
}
write!(buf, ")")?;
}
- TypeRef::Path(path) => print_path(path, buf)?,
+ TypeRef::Path(path) => print_path(db, path, buf)?,
TypeRef::RawPtr(pointee, mtbl) => {
let mtbl = match mtbl {
Mutability::Shared => "*const",
Mutability::Mut => "*mut",
};
write!(buf, "{mtbl} ")?;
- print_type_ref(pointee, buf)?;
+ print_type_ref(db, pointee, buf)?;
}
TypeRef::Reference(pointee, lt, mtbl) => {
let mtbl = match mtbl {
@@ -128,19 +143,19 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
};
write!(buf, "&")?;
if let Some(lt) = lt {
- write!(buf, "{} ", lt.name)?;
+ write!(buf, "{} ", lt.name.display(db))?;
}
write!(buf, "{mtbl}")?;
- print_type_ref(pointee, buf)?;
+ print_type_ref(db, pointee, buf)?;
}
TypeRef::Array(elem, len) => {
write!(buf, "[")?;
- print_type_ref(elem, buf)?;
- write!(buf, "; {len}]")?;
+ print_type_ref(db, elem, buf)?;
+ write!(buf, "; {}]", len.display(db))?;
}
TypeRef::Slice(elem) => {
write!(buf, "[")?;
- print_type_ref(elem, buf)?;
+ print_type_ref(db, elem, buf)?;
write!(buf, "]")?;
}
TypeRef::Fn(args_and_ret, varargs, is_unsafe) => {
@@ -154,7 +169,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
if i != 0 {
write!(buf, ", ")?;
}
- print_type_ref(typeref, buf)?;
+ print_type_ref(db, typeref, buf)?;
}
if *varargs {
if !args.is_empty() {
@@ -163,7 +178,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
write!(buf, "...")?;
}
write!(buf, ") -> ")?;
- print_type_ref(return_type, buf)?;
+ print_type_ref(db, return_type, buf)?;
}
TypeRef::Macro(_ast_id) => {
write!(buf, "<macro>")?;
@@ -171,11 +186,11 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
TypeRef::Error => write!(buf, "{{unknown}}")?,
TypeRef::ImplTrait(bounds) => {
write!(buf, "impl ")?;
- print_type_bounds(bounds, buf)?;
+ print_type_bounds(db, bounds, buf)?;
}
TypeRef::DynTrait(bounds) => {
write!(buf, "dyn ")?;
- print_type_bounds(bounds, buf)?;
+ print_type_bounds(db, bounds, buf)?;
}
}
@@ -183,6 +198,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
}
pub(crate) fn print_type_bounds(
+ db: &dyn ExpandDatabase,
bounds: &[Interned<TypeBound>],
buf: &mut dyn Write,
) -> fmt::Result {
@@ -197,13 +213,13 @@ pub(crate) fn print_type_bounds(
TraitBoundModifier::None => (),
TraitBoundModifier::Maybe => write!(buf, "?")?,
}
- print_path(path, buf)?;
+ print_path(db, path, buf)?;
}
TypeBound::ForLifetime(lifetimes, path) => {
- write!(buf, "for<{}> ", lifetimes.iter().format(", "))?;
- print_path(path, buf)?;
+ write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?;
+ print_path(db, path, buf)?;
}
- TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name)?,
+ TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?,
TypeBound::Error => write!(buf, "{{unknown}}")?,
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index 61e64fc10..0d6f55411 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -1,5 +1,5 @@
//! Name resolution façade.
-use std::{fmt, hash::BuildHasherDefault, sync::Arc};
+use std::{fmt, hash::BuildHasherDefault};
use base_db::CrateId;
use hir_expand::name::{name, Name};
@@ -7,23 +7,25 @@ use indexmap::IndexMap;
use intern::Interned;
use rustc_hash::FxHashSet;
use smallvec::{smallvec, SmallVec};
+use triomphe::Arc;
use crate::{
body::scope::{ExprScopes, ScopeId},
builtin_type::BuiltinType,
db::DefDatabase,
- expr::{BindingId, ExprId, LabelId},
generics::{GenericParams, TypeOrConstParamData},
+ hir::{BindingId, ExprId, LabelId},
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
- nameres::DefMap,
- path::{ModPath, PathKind},
+ lang_item::LangItemTarget,
+ nameres::{DefMap, MacroSubNs},
+ path::{ModPath, Path, PathKind},
per_ns::PerNs,
visibility::{RawVisibility, Visibility},
- AdtId, AssocItemId, ConstId, ConstParamId, 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, TypeParamId,
- VariantId,
+ 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,
};
#[derive(Debug, Clone)]
@@ -78,7 +80,7 @@ enum Scope {
ExprScope(ExprScope),
}
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TypeNs {
SelfType(ImplId),
GenericParam(TypeParamId),
@@ -153,7 +155,8 @@ impl Resolver {
path: &ModPath,
) -> Option<PerNs> {
let (item_map, module) = self.item_scope();
- let (module_res, idx) = item_map.resolve_path(db, module, path, BuiltinShadowMode::Module);
+ let (module_res, idx) =
+ item_map.resolve_path(db, module, path, BuiltinShadowMode::Module, None);
match module_res.take_types()? {
ModuleDefId::TraitId(it) => {
let idx = idx?;
@@ -176,8 +179,27 @@ impl Resolver {
pub fn resolve_path_in_type_ns(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<(TypeNs, Option<usize>)> {
+ let path = match path {
+ Path::Normal { mod_path, .. } => mod_path,
+ 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::Function(_)
+ | LangItemTarget::ImplDef(_)
+ | LangItemTarget::Static(_) => return None,
+ },
+ None,
+ ))
+ }
+ };
let first_name = path.segments().first()?;
let skip_to_mod = path.kind != PathKind::Plain;
if skip_to_mod {
@@ -217,7 +239,7 @@ impl Resolver {
pub fn resolve_path_in_type_ns_fully(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<TypeNs> {
let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
if unresolved.is_some() {
@@ -245,8 +267,24 @@ impl Resolver {
pub fn resolve_path_in_value_ns(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<ResolveValueResult> {
+ let path = match path {
+ 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::Union(_)
+ | LangItemTarget::ImplDef(_)
+ | LangItemTarget::TypeAlias(_)
+ | LangItemTarget::Trait(_)
+ | LangItemTarget::EnumId(_) => return None,
+ }))
+ }
+ };
let n_segments = path.segments().len();
let tmp = name![self];
let first_name = if path.is_self() { &tmp } else { path.segments().first()? };
@@ -340,7 +378,7 @@ impl Resolver {
pub fn resolve_path_in_value_ns_fully(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<ValueNs> {
match self.resolve_path_in_value_ns(db, path)? {
ResolveValueResult::ValueNs(it) => Some(it),
@@ -348,9 +386,17 @@ impl Resolver {
}
}
- pub fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroId> {
+ pub fn resolve_path_as_macro(
+ &self,
+ db: &dyn DefDatabase,
+ path: &ModPath,
+ expected_macro_kind: Option<MacroSubNs>,
+ ) -> Option<MacroId> {
let (item_map, module) = self.item_scope();
- item_map.resolve_path(db, module, path, BuiltinShadowMode::Other).0.take_macros()
+ item_map
+ .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind)
+ .0
+ .take_macros()
}
/// Returns a set of names available in the current scope.
@@ -415,7 +461,10 @@ impl Resolver {
res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
})
});
- def_map.extern_prelude().for_each(|(name, &def)| {
+ def_map.macro_use_prelude().for_each(|(name, def)| {
+ res.add(name, ScopeDef::ModuleDef(def.into()));
+ });
+ def_map.extern_prelude().for_each(|(name, def)| {
res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def)));
});
BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
@@ -441,7 +490,7 @@ impl Resolver {
&Scope::ImplDefScope(impl_) => {
if let Some(target_trait) = &db.impl_data(impl_).target_trait {
if let Some(TypeNs::TraitId(trait_)) =
- self.resolve_path_in_type_ns_fully(db, target_trait.path.mod_path())
+ self.resolve_path_in_type_ns_fully(db, &target_trait.path)
{
traits.insert(trait_);
}
@@ -536,15 +585,13 @@ impl Resolver {
scope_id,
}));
if let Some(block) = expr_scopes.block(scope_id) {
- if let Some(def_map) = db.block_def_map(block) {
- let root = def_map.root();
- resolver
- .scopes
- .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: root }));
- // FIXME: This adds as many module scopes as there are blocks, but resolving in each
- // already traverses all parents, so this is O(n²). I think we could only store the
- // innermost module scope instead?
- }
+ let def_map = db.block_def_map(block);
+ resolver
+ .scopes
+ .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: DefMap::ROOT }));
+ // FIXME: This adds as many module scopes as there are blocks, but resolving in each
+ // already traverses all parents, so this is O(n²). I think we could only store the
+ // innermost module scope instead?
}
}
@@ -592,7 +639,8 @@ impl Resolver {
shadow: BuiltinShadowMode,
) -> PerNs {
let (item_map, module) = self.item_scope();
- let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow);
+ // This method resolves `path` just like import paths, so no expected macro subns is given.
+ let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow, None);
if segment_index.is_some() {
return PerNs::none();
}
@@ -705,13 +753,11 @@ fn resolver_for_scope_(
for scope in scope_chain.into_iter().rev() {
if let Some(block) = scopes.block(scope) {
- if let Some(def_map) = db.block_def_map(block) {
- let root = def_map.root();
- r = r.push_block_scope(def_map, root);
- // FIXME: This adds as many module scopes as there are blocks, but resolving in each
- // already traverses all parents, so this is O(n²). I think we could only store the
- // innermost module scope instead?
- }
+ let def_map = db.block_def_map(block);
+ r = r.push_block_scope(def_map, DefMap::ROOT);
+ // FIXME: This adds as many module scopes as there are blocks, but resolving in each
+ // already traverses all parents, so this is O(n²). I think we could only store the
+ // innermost module scope instead?
}
r = r.push_expr_scope(owner, Arc::clone(&scopes), scope);
@@ -900,6 +946,15 @@ impl HasResolver for ModuleId {
}
}
+impl HasResolver for CrateRootModuleId {
+ fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+ Resolver {
+ scopes: vec![],
+ module_scope: ModuleItemMap { def_map: self.def_map(db), module_id: DefMap::ROOT },
+ }
+ }
+}
+
impl HasResolver for TraitId {
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
@@ -963,6 +1018,24 @@ impl HasResolver for ExternBlockId {
}
}
+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),
+ }
+ }
+}
+
impl HasResolver for DefWithBodyId {
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
match self {
@@ -970,6 +1043,7 @@ impl HasResolver for DefWithBodyId {
DefWithBodyId::FunctionId(f) => f.resolver(db),
DefWithBodyId::StaticId(s) => s.resolver(db),
DefWithBodyId::VariantId(v) => v.parent.resolver(db),
+ DefWithBodyId::InTypeConstId(c) => c.lookup(db).owner.resolver(db),
}
}
}
@@ -1000,6 +1074,12 @@ impl HasResolver for GenericDefId {
}
}
+impl HasResolver for EnumVariantId {
+ fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+ self.parent.resolver(db)
+ }
+}
+
impl HasResolver for VariantId {
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
match self {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
index f69356cac..6047f770d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
@@ -20,7 +20,7 @@ impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> {
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -33,7 +33,7 @@ impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -46,7 +46,7 @@ impl HasSource for Macro2Loc {
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -59,7 +59,7 @@ impl HasSource for MacroRulesLoc {
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -72,7 +72,7 @@ impl HasSource for ProcMacroLoc {
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
index ee143b19a..a6befc8a8 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
@@ -1,17 +1,16 @@
//! Database used for testing `hir_def`.
-use std::{
- fmt, panic,
- sync::{Arc, Mutex},
-};
+use std::{fmt, panic, sync::Mutex};
use base_db::{
- salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition,
- SourceDatabase, Upcast,
+ salsa::{self, Durability},
+ AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, SourceDatabase,
+ Upcast,
};
use hir_expand::{db::ExpandDatabase, InFile};
-use stdx::hash::NoHashHashSet;
+use rustc_hash::FxHashSet;
use syntax::{algo, ast, AstNode};
+use triomphe::Arc;
use crate::{
db::DefDatabase,
@@ -35,7 +34,7 @@ pub(crate) struct TestDB {
impl Default for TestDB {
fn default() -> Self {
let mut this = Self { storage: Default::default(), events: Default::default() };
- this.set_enable_proc_attr_macros(true);
+ this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
this
}
}
@@ -70,13 +69,13 @@ impl fmt::Debug for TestDB {
impl panic::RefUnwindSafe for TestDB {}
impl FileLoader for TestDB {
- fn file_text(&self, file_id: FileId) -> Arc<String> {
+ fn file_text(&self, file_id: FileId) -> Arc<str> {
FileLoaderDelegate(self).file_text(file_id)
}
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
FileLoaderDelegate(self).relevant_crates(file_id)
}
}
@@ -111,7 +110,7 @@ impl TestDB {
}
_ => {
// FIXME: handle `mod` inside block expression
- return def_map.module_id(def_map.root());
+ return def_map.module_id(DefMap::ROOT);
}
}
}
@@ -120,7 +119,7 @@ impl TestDB {
/// Finds the smallest/innermost module in `def_map` containing `position`.
fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId {
let mut size = None;
- let mut res = def_map.root();
+ let mut res = DefMap::ROOT;
for (module, data) in def_map.modules() {
let src = data.definition_source(self);
if src.file_id != position.file_id.into() {
@@ -209,13 +208,11 @@ impl TestDB {
});
for scope in scope_iter {
- let containing_blocks =
+ let mut containing_blocks =
scopes.scope_chain(Some(scope)).filter_map(|scope| scopes.block(scope));
- for block in containing_blocks {
- if let Some(def_map) = self.block_def_map(block) {
- return Some(def_map);
- }
+ if let Some(block) = containing_blocks.next().map(|block| self.block_def_map(block)) {
+ return Some(block);
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
index ab76ed43d..30f48de61 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
@@ -1,10 +1,11 @@
//! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
-use std::{iter, sync::Arc};
+use std::iter;
use hir_expand::{hygiene::Hygiene, InFile};
use la_arena::ArenaMap;
use syntax::ast;
+use triomphe::Arc;
use crate::{
db::DefDatabase,