aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src')
-rw-r--r--crates/hir_def/src/adt.rs329
-rw-r--r--crates/hir_def/src/attr.rs212
-rw-r--r--crates/hir_def/src/body.rs360
-rw-r--r--crates/hir_def/src/body/lower.rs931
-rw-r--r--crates/hir_def/src/body/scope.rs456
-rw-r--r--crates/hir_def/src/builtin_type.rs166
-rw-r--r--crates/hir_def/src/child_by_source.rs177
-rw-r--r--crates/hir_def/src/data.rs278
-rw-r--r--crates/hir_def/src/db.rs120
-rw-r--r--crates/hir_def/src/diagnostics.rs30
-rw-r--r--crates/hir_def/src/docs.rs121
-rw-r--r--crates/hir_def/src/dyn_map.rs108
-rw-r--r--crates/hir_def/src/expr.rs420
-rw-r--r--crates/hir_def/src/find_path.rs687
-rw-r--r--crates/hir_def/src/generics.rs339
-rw-r--r--crates/hir_def/src/import_map.rs745
-rw-r--r--crates/hir_def/src/item_scope.rs341
-rw-r--r--crates/hir_def/src/item_tree.rs754
-rw-r--r--crates/hir_def/src/item_tree/lower.rs705
-rw-r--r--crates/hir_def/src/item_tree/tests.rs439
-rw-r--r--crates/hir_def/src/keys.rs58
-rw-r--r--crates/hir_def/src/lang_item.rs174
-rw-r--r--crates/hir_def/src/lib.rs541
-rw-r--r--crates/hir_def/src/nameres.rs326
-rw-r--r--crates/hir_def/src/nameres/collector.rs1279
-rw-r--r--crates/hir_def/src/nameres/mod_resolution.rs139
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs330
-rw-r--r--crates/hir_def/src/nameres/tests.rs690
-rw-r--r--crates/hir_def/src/nameres/tests/globs.rs338
-rw-r--r--crates/hir_def/src/nameres/tests/incremental.rs101
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs669
-rw-r--r--crates/hir_def/src/nameres/tests/mod_resolution.rs796
-rw-r--r--crates/hir_def/src/nameres/tests/primitives.rs23
-rw-r--r--crates/hir_def/src/path.rs345
-rw-r--r--crates/hir_def/src/path/lower.rs215
-rw-r--r--crates/hir_def/src/path/lower/lower_use.rs120
-rw-r--r--crates/hir_def/src/per_ns.rs95
-rw-r--r--crates/hir_def/src/resolver.rs713
-rw-r--r--crates/hir_def/src/src.rs43
-rw-r--r--crates/hir_def/src/test_db.rs101
-rw-r--r--crates/hir_def/src/trace.rs51
-rw-r--r--crates/hir_def/src/type_ref.rs245
-rw-r--r--crates/hir_def/src/visibility.rs171
43 files changed, 15281 insertions, 0 deletions
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs
new file mode 100644
index 000000000..d69ff2fc7
--- /dev/null
+++ b/crates/hir_def/src/adt.rs
@@ -0,0 +1,329 @@
1//! Defines hir-level representation of structs, enums and unions
2
3use std::sync::Arc;
4
5use arena::{map::ArenaMap, Arena};
6use either::Either;
7use hir_expand::{
8 name::{AsName, Name},
9 InFile,
10};
11use syntax::ast::{self, NameOwner, VisibilityOwner};
12use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
13
14use crate::{
15 body::{CfgExpander, LowerCtx},
16 db::DefDatabase,
17 item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem},
18 src::HasChildSource,
19 src::HasSource,
20 trace::Trace,
21 type_ref::TypeRef,
22 visibility::RawVisibility,
23 EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId,
24 VariantId,
25};
26use cfg::CfgOptions;
27
28/// Note that we use `StructData` for unions as well!
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct StructData {
31 pub name: Name,
32 pub variant_data: Arc<VariantData>,
33 pub repr: Option<ReprKind>,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct EnumData {
38 pub name: Name,
39 pub variants: Arena<EnumVariantData>,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct EnumVariantData {
44 pub name: Name,
45 pub variant_data: Arc<VariantData>,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum VariantData {
50 Record(Arena<FieldData>),
51 Tuple(Arena<FieldData>),
52 Unit,
53}
54
55/// A single field of an enum variant or struct
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct FieldData {
58 pub name: Name,
59 pub type_ref: TypeRef,
60 pub visibility: RawVisibility,
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub enum ReprKind {
65 Packed,
66 Other,
67}
68
69fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option<ReprKind> {
70 item_tree.attrs(of).by_key("repr").tt_values().find_map(parse_repr_tt)
71}
72
73fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
74 match tt.delimiter {
75 Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
76 _ => return None,
77 }
78
79 let mut it = tt.token_trees.iter();
80 match it.next()? {
81 TokenTree::Leaf(Leaf::Ident(ident)) if ident.text == "packed" => Some(ReprKind::Packed),
82 _ => Some(ReprKind::Other),
83 }
84}
85
86impl StructData {
87 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
88 let loc = id.lookup(db);
89 let item_tree = db.item_tree(loc.id.file_id);
90 let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into());
91 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
92
93 let strukt = &item_tree[loc.id.value];
94 let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields);
95 Arc::new(StructData {
96 name: strukt.name.clone(),
97 variant_data: Arc::new(variant_data),
98 repr,
99 })
100 }
101 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
102 let loc = id.lookup(db);
103 let item_tree = db.item_tree(loc.id.file_id);
104 let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into());
105 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
106
107 let union = &item_tree[loc.id.value];
108 let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields);
109
110 Arc::new(StructData {
111 name: union.name.clone(),
112 variant_data: Arc::new(variant_data),
113 repr,
114 })
115 }
116}
117
118impl EnumData {
119 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
120 let loc = e.lookup(db);
121 let item_tree = db.item_tree(loc.id.file_id);
122 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
123
124 let enum_ = &item_tree[loc.id.value];
125 let mut variants = Arena::new();
126 for var_id in enum_.variants.clone() {
127 if item_tree.attrs(var_id.into()).is_cfg_enabled(&cfg_options) {
128 let var = &item_tree[var_id];
129 let var_data = lower_fields(&item_tree, &cfg_options, &var.fields);
130
131 variants.alloc(EnumVariantData {
132 name: var.name.clone(),
133 variant_data: Arc::new(var_data),
134 });
135 }
136 }
137
138 Arc::new(EnumData { name: enum_.name.clone(), variants })
139 }
140
141 pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
142 let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?;
143 Some(id)
144 }
145}
146
147impl HasChildSource for EnumId {
148 type ChildId = LocalEnumVariantId;
149 type Value = ast::Variant;
150 fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
151 let src = self.lookup(db).source(db);
152 let mut trace = Trace::new_for_map();
153 lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db));
154 src.with_value(trace.into_map())
155 }
156}
157
158fn lower_enum(
159 db: &dyn DefDatabase,
160 trace: &mut Trace<EnumVariantData, ast::Variant>,
161 ast: &InFile<ast::Enum>,
162 module_id: ModuleId,
163) {
164 let expander = CfgExpander::new(db, ast.file_id, module_id.krate);
165 let variants = ast
166 .value
167 .variant_list()
168 .into_iter()
169 .flat_map(|it| it.variants())
170 .filter(|var| expander.is_cfg_enabled(var));
171 for var in variants {
172 trace.alloc(
173 || var.clone(),
174 || EnumVariantData {
175 name: var.name().map_or_else(Name::missing, |it| it.as_name()),
176 variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()), module_id)),
177 },
178 );
179 }
180}
181
182impl VariantData {
183 fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self {
184 let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate);
185 let mut trace = Trace::new_for_arena();
186 match lower_struct(db, &mut expander, &mut trace, &flavor) {
187 StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
188 StructKind::Record => VariantData::Record(trace.into_arena()),
189 StructKind::Unit => VariantData::Unit,
190 }
191 }
192
193 pub fn fields(&self) -> &Arena<FieldData> {
194 const EMPTY: &Arena<FieldData> = &Arena::new();
195 match &self {
196 VariantData::Record(fields) | VariantData::Tuple(fields) => fields,
197 _ => EMPTY,
198 }
199 }
200
201 pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
202 self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
203 }
204
205 pub fn kind(&self) -> StructKind {
206 match self {
207 VariantData::Record(_) => StructKind::Record,
208 VariantData::Tuple(_) => StructKind::Tuple,
209 VariantData::Unit => StructKind::Unit,
210 }
211 }
212}
213
214impl HasChildSource for VariantId {
215 type ChildId = LocalFieldId;
216 type Value = Either<ast::TupleField, ast::RecordField>;
217
218 fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
219 let (src, module_id) = match self {
220 VariantId::EnumVariantId(it) => {
221 // I don't really like the fact that we call into parent source
222 // here, this might add to more queries then necessary.
223 let src = it.parent.child_source(db);
224 (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container.module(db))
225 }
226 VariantId::StructId(it) => {
227 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container.module(db))
228 }
229 VariantId::UnionId(it) => (
230 it.lookup(db).source(db).map(|it| {
231 it.record_field_list()
232 .map(ast::StructKind::Record)
233 .unwrap_or(ast::StructKind::Unit)
234 }),
235 it.lookup(db).container.module(db),
236 ),
237 };
238 let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
239 let mut trace = Trace::new_for_map();
240 lower_struct(db, &mut expander, &mut trace, &src);
241 src.with_value(trace.into_map())
242 }
243}
244
245#[derive(Debug, Copy, Clone, PartialEq, Eq)]
246pub enum StructKind {
247 Tuple,
248 Record,
249 Unit,
250}
251
252fn lower_struct(
253 db: &dyn DefDatabase,
254 expander: &mut CfgExpander,
255 trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
256 ast: &InFile<ast::StructKind>,
257) -> StructKind {
258 let ctx = LowerCtx::new(db, ast.file_id);
259
260 match &ast.value {
261 ast::StructKind::Tuple(fl) => {
262 for (i, fd) in fl.fields().enumerate() {
263 if !expander.is_cfg_enabled(&fd) {
264 continue;
265 }
266
267 trace.alloc(
268 || Either::Left(fd.clone()),
269 || FieldData {
270 name: Name::new_tuple_field(i),
271 type_ref: TypeRef::from_ast_opt(&ctx, fd.ty()),
272 visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
273 },
274 );
275 }
276 StructKind::Tuple
277 }
278 ast::StructKind::Record(fl) => {
279 for fd in fl.fields() {
280 if !expander.is_cfg_enabled(&fd) {
281 continue;
282 }
283
284 trace.alloc(
285 || Either::Right(fd.clone()),
286 || FieldData {
287 name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
288 type_ref: TypeRef::from_ast_opt(&ctx, fd.ty()),
289 visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
290 },
291 );
292 }
293 StructKind::Record
294 }
295 ast::StructKind::Unit => StructKind::Unit,
296 }
297}
298
299fn lower_fields(item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields) -> VariantData {
300 match fields {
301 Fields::Record(flds) => {
302 let mut arena = Arena::new();
303 for field_id in flds.clone() {
304 if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) {
305 arena.alloc(lower_field(item_tree, &item_tree[field_id]));
306 }
307 }
308 VariantData::Record(arena)
309 }
310 Fields::Tuple(flds) => {
311 let mut arena = Arena::new();
312 for field_id in flds.clone() {
313 if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) {
314 arena.alloc(lower_field(item_tree, &item_tree[field_id]));
315 }
316 }
317 VariantData::Tuple(arena)
318 }
319 Fields::Unit => VariantData::Unit,
320 }
321}
322
323fn lower_field(item_tree: &ItemTree, field: &Field) -> FieldData {
324 FieldData {
325 name: field.name.clone(),
326 type_ref: field.type_ref.clone(),
327 visibility: item_tree[field.visibility].clone(),
328 }
329}
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
new file mode 100644
index 000000000..dea552a60
--- /dev/null
+++ b/crates/hir_def/src/attr.rs
@@ -0,0 +1,212 @@
1//! A higher level attributes based on TokenTree, with also some shortcuts.
2
3use std::{ops, sync::Arc};
4
5use cfg::{CfgExpr, CfgOptions};
6use either::Either;
7use hir_expand::{hygiene::Hygiene, AstId, InFile};
8use mbe::ast_to_token_tree;
9use syntax::{
10 ast::{self, AstNode, AttrsOwner},
11 SmolStr,
12};
13use tt::Subtree;
14
15use crate::{
16 db::DefDatabase,
17 item_tree::{ItemTreeId, ItemTreeNode},
18 nameres::ModuleSource,
19 path::ModPath,
20 src::HasChildSource,
21 AdtId, AttrDefId, Lookup,
22};
23
24#[derive(Default, Debug, Clone, PartialEq, Eq)]
25pub struct Attrs {
26 entries: Option<Arc<[Attr]>>,
27}
28
29impl ops::Deref for Attrs {
30 type Target = [Attr];
31
32 fn deref(&self) -> &[Attr] {
33 match &self.entries {
34 Some(it) => &*it,
35 None => &[],
36 }
37 }
38}
39
40impl Attrs {
41 pub const EMPTY: Attrs = Attrs { entries: None };
42
43 pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
44 match def {
45 AttrDefId::ModuleId(module) => {
46 let def_map = db.crate_def_map(module.krate);
47 let mod_data = &def_map[module.local_id];
48 match mod_data.declaration_source(db) {
49 Some(it) => {
50 Attrs::from_attrs_owner(db, it.as_ref().map(|it| it as &dyn AttrsOwner))
51 }
52 None => Attrs::from_attrs_owner(
53 db,
54 mod_data.definition_source(db).as_ref().map(|src| match src {
55 ModuleSource::SourceFile(file) => file as &dyn AttrsOwner,
56 ModuleSource::Module(module) => module as &dyn AttrsOwner,
57 }),
58 ),
59 }
60 }
61 AttrDefId::FieldId(it) => {
62 let src = it.parent.child_source(db);
63 match &src.value[it.local_id] {
64 Either::Left(_tuple) => Attrs::default(),
65 Either::Right(record) => Attrs::from_attrs_owner(db, src.with_value(record)),
66 }
67 }
68 AttrDefId::EnumVariantId(var_id) => {
69 let src = var_id.parent.child_source(db);
70 let src = src.as_ref().map(|it| &it[var_id.local_id]);
71 Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
72 }
73 AttrDefId::AdtId(it) => match it {
74 AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
75 AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
76 AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
77 },
78 AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
79 AttrDefId::MacroDefId(it) => {
80 it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db))
81 }
82 AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
83 AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
84 AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
85 AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
86 AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
87 }
88 }
89
90 pub fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs {
91 let hygiene = Hygiene::new(db.upcast(), owner.file_id);
92 Attrs::new(owner.value, &hygiene)
93 }
94
95 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
96 let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map(
97 |docs_text| Attr {
98 input: Some(AttrInput::Literal(SmolStr::new(docs_text))),
99 path: ModPath::from(hir_expand::name!(doc)),
100 },
101 );
102 let mut attrs = owner.attrs().peekable();
103 let entries = if attrs.peek().is_none() {
104 // Avoid heap allocation
105 None
106 } else {
107 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect())
108 };
109 Attrs { entries }
110 }
111
112 pub fn merge(&self, other: Attrs) -> Attrs {
113 match (&self.entries, &other.entries) {
114 (None, None) => Attrs { entries: None },
115 (Some(entries), None) | (None, Some(entries)) => {
116 Attrs { entries: Some(entries.clone()) }
117 }
118 (Some(a), Some(b)) => {
119 Attrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }
120 }
121 }
122 }
123
124 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
125 AttrQuery { attrs: self, key }
126 }
127
128 pub fn cfg(&self) -> impl Iterator<Item = CfgExpr> + '_ {
129 // FIXME: handle cfg_attr :-)
130 self.by_key("cfg").tt_values().map(CfgExpr::parse)
131 }
132 pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
133 self.cfg().all(|cfg| cfg_options.check(&cfg) != Some(false))
134 }
135}
136
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct Attr {
139 pub(crate) path: ModPath,
140 pub(crate) input: Option<AttrInput>,
141}
142
143#[derive(Debug, Clone, PartialEq, Eq)]
144pub enum AttrInput {
145 /// `#[attr = "string"]`
146 Literal(SmolStr),
147 /// `#[attr(subtree)]`
148 TokenTree(Subtree),
149}
150
151impl Attr {
152 fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
153 let path = ModPath::from_src(ast.path()?, hygiene)?;
154 let input = if let Some(lit) = ast.literal() {
155 // FIXME: escape? raw string?
156 let value = lit.syntax().first_token()?.text().trim_matches('"').into();
157 Some(AttrInput::Literal(value))
158 } else if let Some(tt) = ast.token_tree() {
159 Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0))
160 } else {
161 None
162 };
163 Some(Attr { path, input })
164 }
165}
166
167#[derive(Debug, Clone, Copy)]
168pub struct AttrQuery<'a> {
169 attrs: &'a Attrs,
170 key: &'static str,
171}
172
173impl<'a> AttrQuery<'a> {
174 pub fn tt_values(self) -> impl Iterator<Item = &'a Subtree> {
175 self.attrs().filter_map(|attr| match attr.input.as_ref()? {
176 AttrInput::TokenTree(it) => Some(it),
177 _ => None,
178 })
179 }
180
181 pub fn string_value(self) -> Option<&'a SmolStr> {
182 self.attrs().find_map(|attr| match attr.input.as_ref()? {
183 AttrInput::Literal(it) => Some(it),
184 _ => None,
185 })
186 }
187
188 pub fn exists(self) -> bool {
189 self.attrs().next().is_some()
190 }
191
192 fn attrs(self) -> impl Iterator<Item = &'a Attr> {
193 let key = self.key;
194 self.attrs
195 .iter()
196 .filter(move |attr| attr.path.as_ident().map_or(false, |s| s.to_string() == key))
197 }
198}
199
200fn attrs_from_ast<N>(src: AstId<N>, db: &dyn DefDatabase) -> Attrs
201where
202 N: ast::AttrsOwner,
203{
204 let src = InFile::new(src.file_id, src.to_node(db.upcast()));
205 Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
206}
207
208fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> Attrs {
209 let tree = db.item_tree(id.file_id);
210 let mod_item = N::id_to_mod_item(id.value);
211 tree.attrs(mod_item.into()).clone()
212}
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
new file mode 100644
index 000000000..9a9a605dd
--- /dev/null
+++ b/crates/hir_def/src/body.rs
@@ -0,0 +1,360 @@
1//! Defines `Body`: a lowered representation of bodies of functions, statics and
2//! consts.
3mod lower;
4pub mod scope;
5
6use std::{mem, ops::Index, sync::Arc};
7
8use arena::{map::ArenaMap, Arena};
9use base_db::CrateId;
10use cfg::CfgOptions;
11use drop_bomb::DropBomb;
12use either::Either;
13use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId};
14use rustc_hash::FxHashMap;
15use syntax::{ast, AstNode, AstPtr};
16use test_utils::mark;
17
18pub(crate) use lower::LowerCtx;
19
20use crate::{
21 attr::Attrs,
22 db::DefDatabase,
23 expr::{Expr, ExprId, Pat, PatId},
24 item_scope::BuiltinShadowMode,
25 item_scope::ItemScope,
26 nameres::CrateDefMap,
27 path::{ModPath, Path},
28 src::HasSource,
29 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
30};
31
32/// A subset of Expander that only deals with cfg attributes. We only need it to
33/// avoid cyclic queries in crate def map during enum processing.
34pub(crate) struct CfgExpander {
35 cfg_options: CfgOptions,
36 hygiene: Hygiene,
37}
38
39pub(crate) struct Expander {
40 cfg_expander: CfgExpander,
41 crate_def_map: Arc<CrateDefMap>,
42 current_file_id: HirFileId,
43 ast_id_map: Arc<AstIdMap>,
44 module: ModuleId,
45 recursion_limit: usize,
46}
47
48#[cfg(test)]
49const EXPANSION_RECURSION_LIMIT: usize = 32;
50
51#[cfg(not(test))]
52const EXPANSION_RECURSION_LIMIT: usize = 128;
53
54impl CfgExpander {
55 pub(crate) fn new(
56 db: &dyn DefDatabase,
57 current_file_id: HirFileId,
58 krate: CrateId,
59 ) -> CfgExpander {
60 let hygiene = Hygiene::new(db.upcast(), current_file_id);
61 let cfg_options = db.crate_graph()[krate].cfg_options.clone();
62 CfgExpander { cfg_options, hygiene }
63 }
64
65 pub(crate) fn parse_attrs(&self, owner: &dyn ast::AttrsOwner) -> Attrs {
66 Attrs::new(owner, &self.hygiene)
67 }
68
69 pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool {
70 let attrs = self.parse_attrs(owner);
71 attrs.is_cfg_enabled(&self.cfg_options)
72 }
73}
74
75impl Expander {
76 pub(crate) fn new(
77 db: &dyn DefDatabase,
78 current_file_id: HirFileId,
79 module: ModuleId,
80 ) -> Expander {
81 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
82 let crate_def_map = db.crate_def_map(module.krate);
83 let ast_id_map = db.ast_id_map(current_file_id);
84 Expander {
85 cfg_expander,
86 crate_def_map,
87 current_file_id,
88 ast_id_map,
89 module,
90 recursion_limit: 0,
91 }
92 }
93
94 pub(crate) fn enter_expand<T: ast::AstNode>(
95 &mut self,
96 db: &dyn DefDatabase,
97 local_scope: Option<&ItemScope>,
98 macro_call: ast::MacroCall,
99 ) -> Option<(Mark, T)> {
100 self.recursion_limit += 1;
101 if self.recursion_limit > EXPANSION_RECURSION_LIMIT {
102 mark::hit!(your_stack_belongs_to_me);
103 return None;
104 }
105
106 let macro_call = InFile::new(self.current_file_id, &macro_call);
107
108 if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| {
109 if let Some(local_scope) = local_scope {
110 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
111 return Some(def);
112 }
113 }
114 self.resolve_path_as_macro(db, &path)
115 }) {
116 let file_id = call_id.as_file();
117 if let Some(node) = db.parse_or_expand(file_id) {
118 if let Some(expr) = T::cast(node) {
119 log::debug!("macro expansion {:#?}", expr.syntax());
120
121 let mark = Mark {
122 file_id: self.current_file_id,
123 ast_id_map: mem::take(&mut self.ast_id_map),
124 bomb: DropBomb::new("expansion mark dropped"),
125 };
126 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
127 self.current_file_id = file_id;
128 self.ast_id_map = db.ast_id_map(file_id);
129 return Some((mark, expr));
130 }
131 }
132 }
133
134 // FIXME: Instead of just dropping the error from expansion
135 // report it
136 None
137 }
138
139 pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
140 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
141 self.current_file_id = mark.file_id;
142 self.ast_id_map = mem::take(&mut mark.ast_id_map);
143 self.recursion_limit -= 1;
144 mark.bomb.defuse();
145 }
146
147 pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
148 InFile { file_id: self.current_file_id, value }
149 }
150
151 pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool {
152 self.cfg_expander.is_cfg_enabled(owner)
153 }
154
155 fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
156 Path::from_src(path, &self.cfg_expander.hygiene)
157 }
158
159 fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> {
160 self.crate_def_map
161 .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other)
162 .0
163 .take_macros()
164 }
165
166 fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> {
167 let file_local_id = self.ast_id_map.ast_id(item);
168 AstId::new(self.current_file_id, file_local_id)
169 }
170}
171
172pub(crate) struct Mark {
173 file_id: HirFileId,
174 ast_id_map: Arc<AstIdMap>,
175 bomb: DropBomb,
176}
177
178/// The body of an item (function, const etc.).
179#[derive(Debug, Eq, PartialEq)]
180pub struct Body {
181 pub exprs: Arena<Expr>,
182 pub pats: Arena<Pat>,
183 /// The patterns for the function's parameters. While the parameter types are
184 /// part of the function signature, the patterns are not (they don't change
185 /// the external type of the function).
186 ///
187 /// If this `Body` is for the body of a constant, this will just be
188 /// empty.
189 pub params: Vec<PatId>,
190 /// The `ExprId` of the actual body expression.
191 pub body_expr: ExprId,
192 pub item_scope: ItemScope,
193}
194
195pub type ExprPtr = AstPtr<ast::Expr>;
196pub type ExprSource = InFile<ExprPtr>;
197
198pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
199pub type PatSource = InFile<PatPtr>;
200
201/// An item body together with the mapping from syntax nodes to HIR expression
202/// IDs. This is needed to go from e.g. a position in a file to the HIR
203/// expression containing it; but for type inference etc., we want to operate on
204/// a structure that is agnostic to the actual positions of expressions in the
205/// file, so that we don't recompute types whenever some whitespace is typed.
206///
207/// One complication here is that, due to macro expansion, a single `Body` might
208/// be spread across several files. So, for each ExprId and PatId, we record
209/// both the HirFileId and the position inside the file. However, we only store
210/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
211/// this properly for macros.
212#[derive(Default, Debug, Eq, PartialEq)]
213pub struct BodySourceMap {
214 expr_map: FxHashMap<ExprSource, ExprId>,
215 expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
216 pat_map: FxHashMap<PatSource, PatId>,
217 pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
218 field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordExprField>>>,
219 expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
220}
221
222#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
223pub struct SyntheticSyntax;
224
225impl Body {
226 pub(crate) fn body_with_source_map_query(
227 db: &dyn DefDatabase,
228 def: DefWithBodyId,
229 ) -> (Arc<Body>, Arc<BodySourceMap>) {
230 let _p = profile::span("body_with_source_map_query");
231 let mut params = None;
232
233 let (file_id, module, body) = match def {
234 DefWithBodyId::FunctionId(f) => {
235 let f = f.lookup(db);
236 let src = f.source(db);
237 params = src.value.param_list();
238 (src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
239 }
240 DefWithBodyId::ConstId(c) => {
241 let c = c.lookup(db);
242 let src = c.source(db);
243 (src.file_id, c.module(db), src.value.body())
244 }
245 DefWithBodyId::StaticId(s) => {
246 let s = s.lookup(db);
247 let src = s.source(db);
248 (src.file_id, s.module(db), src.value.body())
249 }
250 };
251 let expander = Expander::new(db, file_id, module);
252 let (body, source_map) = Body::new(db, def, expander, params, body);
253 (Arc::new(body), Arc::new(source_map))
254 }
255
256 pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
257 db.body_with_source_map(def).0
258 }
259
260 fn new(
261 db: &dyn DefDatabase,
262 def: DefWithBodyId,
263 expander: Expander,
264 params: Option<ast::ParamList>,
265 body: Option<ast::Expr>,
266 ) -> (Body, BodySourceMap) {
267 lower::lower(db, def, expander, params, body)
268 }
269}
270
271impl Index<ExprId> for Body {
272 type Output = Expr;
273
274 fn index(&self, expr: ExprId) -> &Expr {
275 &self.exprs[expr]
276 }
277}
278
279impl Index<PatId> for Body {
280 type Output = Pat;
281
282 fn index(&self, pat: PatId) -> &Pat {
283 &self.pats[pat]
284 }
285}
286
287impl BodySourceMap {
288 pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
289 self.expr_map_back[expr].clone()
290 }
291
292 pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
293 let src = node.map(|it| AstPtr::new(it));
294 self.expr_map.get(&src).cloned()
295 }
296
297 pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> {
298 let src = node.map(|it| AstPtr::new(it));
299 self.expansions.get(&src).cloned()
300 }
301
302 pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
303 self.pat_map_back[pat].clone()
304 }
305
306 pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
307 let src = node.map(|it| Either::Left(AstPtr::new(it)));
308 self.pat_map.get(&src).cloned()
309 }
310
311 pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId> {
312 let src = node.map(|it| Either::Right(AstPtr::new(it)));
313 self.pat_map.get(&src).cloned()
314 }
315
316 pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordExprField>> {
317 self.field_map[&(expr, field)].clone()
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use base_db::{fixture::WithFixture, SourceDatabase};
324 use test_utils::mark;
325
326 use crate::ModuleDefId;
327
328 use super::*;
329
330 fn lower(ra_fixture: &str) -> Arc<Body> {
331 let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture);
332
333 let krate = db.crate_graph().iter().next().unwrap();
334 let def_map = db.crate_def_map(krate);
335 let module = def_map.modules_for_file(file_id).next().unwrap();
336 let module = &def_map[module];
337 let fn_def = match module.scope.declarations().next().unwrap() {
338 ModuleDefId::FunctionId(it) => it,
339 _ => panic!(),
340 };
341
342 db.body(fn_def.into())
343 }
344
345 #[test]
346 fn your_stack_belongs_to_me() {
347 mark::check!(your_stack_belongs_to_me);
348 lower(
349 "
350macro_rules! n_nuple {
351 ($e:tt) => ();
352 ($($rest:tt)*) => {{
353 (n_nuple!($($rest)*)None,)
354 }};
355}
356fn main() { n_nuple!(1,2,3); }
357",
358 );
359 }
360}
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
new file mode 100644
index 000000000..30ac12a12
--- /dev/null
+++ b/crates/hir_def/src/body/lower.rs
@@ -0,0 +1,931 @@
1//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
2//! representation.
3
4use std::{any::type_name, sync::Arc};
5
6use arena::Arena;
7use either::Either;
8use hir_expand::{
9 hygiene::Hygiene,
10 name::{name, AsName, Name},
11 HirFileId, MacroDefId, MacroDefKind,
12};
13use rustc_hash::FxHashMap;
14use syntax::{
15 ast::{
16 self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner,
17 SlicePatComponents,
18 },
19 AstNode, AstPtr,
20};
21use test_utils::mark;
22
23use crate::{
24 adt::StructKind,
25 body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax},
26 builtin_type::{BuiltinFloat, BuiltinInt},
27 db::DefDatabase,
28 expr::{
29 dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal,
30 LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
31 },
32 item_scope::BuiltinShadowMode,
33 item_tree::{ItemTree, ItemTreeId, ItemTreeNode},
34 path::{GenericArgs, Path},
35 type_ref::{Mutability, Rawness, TypeRef},
36 AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
37 StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
38};
39
40use super::{ExprSource, PatSource};
41
42pub(crate) struct LowerCtx {
43 hygiene: Hygiene,
44}
45
46impl LowerCtx {
47 pub fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self {
48 LowerCtx { hygiene: Hygiene::new(db.upcast(), file_id) }
49 }
50 pub fn with_hygiene(hygiene: &Hygiene) -> Self {
51 LowerCtx { hygiene: hygiene.clone() }
52 }
53
54 pub fn lower_path(&self, ast: ast::Path) -> Option<Path> {
55 Path::from_src(ast, &self.hygiene)
56 }
57}
58
59pub(super) fn lower(
60 db: &dyn DefDatabase,
61 def: DefWithBodyId,
62 expander: Expander,
63 params: Option<ast::ParamList>,
64 body: Option<ast::Expr>,
65) -> (Body, BodySourceMap) {
66 let item_tree = db.item_tree(expander.current_file_id);
67 ExprCollector {
68 db,
69 def,
70 source_map: BodySourceMap::default(),
71 body: Body {
72 exprs: Arena::default(),
73 pats: Arena::default(),
74 params: Vec::new(),
75 body_expr: dummy_expr_id(),
76 item_scope: Default::default(),
77 },
78 item_trees: {
79 let mut map = FxHashMap::default();
80 map.insert(expander.current_file_id, item_tree);
81 map
82 },
83 expander,
84 }
85 .collect(params, body)
86}
87
88struct ExprCollector<'a> {
89 db: &'a dyn DefDatabase,
90 def: DefWithBodyId,
91 expander: Expander,
92 body: Body,
93 source_map: BodySourceMap,
94
95 item_trees: FxHashMap<HirFileId, Arc<ItemTree>>,
96}
97
98impl ExprCollector<'_> {
99 fn collect(
100 mut self,
101 param_list: Option<ast::ParamList>,
102 body: Option<ast::Expr>,
103 ) -> (Body, BodySourceMap) {
104 if let Some(param_list) = param_list {
105 if let Some(self_param) = param_list.self_param() {
106 let ptr = AstPtr::new(&self_param);
107 let param_pat = self.alloc_pat(
108 Pat::Bind {
109 name: name![self],
110 mode: BindingAnnotation::Unannotated,
111 subpat: None,
112 },
113 Either::Right(ptr),
114 );
115 self.body.params.push(param_pat);
116 }
117
118 for param in param_list.params() {
119 let pat = match param.pat() {
120 None => continue,
121 Some(pat) => pat,
122 };
123 let param_pat = self.collect_pat(pat);
124 self.body.params.push(param_pat);
125 }
126 };
127
128 self.body.body_expr = self.collect_expr_opt(body);
129 (self.body, self.source_map)
130 }
131
132 fn ctx(&self) -> LowerCtx {
133 LowerCtx::new(self.db, self.expander.current_file_id)
134 }
135
136 fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId {
137 let src = self.expander.to_source(ptr);
138 let id = self.make_expr(expr, Ok(src.clone()));
139 self.source_map.expr_map.insert(src, id);
140 id
141 }
142 // desugared exprs don't have ptr, that's wrong and should be fixed
143 // somehow.
144 fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
145 self.make_expr(expr, Err(SyntheticSyntax))
146 }
147 fn empty_block(&mut self) -> ExprId {
148 self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None, label: None })
149 }
150 fn missing_expr(&mut self) -> ExprId {
151 self.alloc_expr_desugared(Expr::Missing)
152 }
153 fn make_expr(&mut self, expr: Expr, src: Result<ExprSource, SyntheticSyntax>) -> ExprId {
154 let id = self.body.exprs.alloc(expr);
155 self.source_map.expr_map_back.insert(id, src);
156 id
157 }
158
159 fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
160 let src = self.expander.to_source(ptr);
161 let id = self.make_pat(pat, Ok(src.clone()));
162 self.source_map.pat_map.insert(src, id);
163 id
164 }
165 fn missing_pat(&mut self) -> PatId {
166 self.make_pat(Pat::Missing, Err(SyntheticSyntax))
167 }
168 fn make_pat(&mut self, pat: Pat, src: Result<PatSource, SyntheticSyntax>) -> PatId {
169 let id = self.body.pats.alloc(pat);
170 self.source_map.pat_map_back.insert(id, src);
171 id
172 }
173
174 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
175 let syntax_ptr = AstPtr::new(&expr);
176 if !self.expander.is_cfg_enabled(&expr) {
177 return self.missing_expr();
178 }
179
180 match expr {
181 ast::Expr::IfExpr(e) => {
182 let then_branch = self.collect_block_opt(e.then_branch());
183
184 let else_branch = e.else_branch().map(|b| match b {
185 ast::ElseBranch::Block(it) => self.collect_block(it),
186 ast::ElseBranch::IfExpr(elif) => {
187 let expr: ast::Expr = ast::Expr::cast(elif.syntax().clone()).unwrap();
188 self.collect_expr(expr)
189 }
190 });
191
192 let condition = match e.condition() {
193 None => self.missing_expr(),
194 Some(condition) => match condition.pat() {
195 None => self.collect_expr_opt(condition.expr()),
196 // if let -- desugar to match
197 Some(pat) => {
198 let pat = self.collect_pat(pat);
199 let match_expr = self.collect_expr_opt(condition.expr());
200 let placeholder_pat = self.missing_pat();
201 let arms = vec![
202 MatchArm { pat, expr: then_branch, guard: None },
203 MatchArm {
204 pat: placeholder_pat,
205 expr: else_branch.unwrap_or_else(|| self.empty_block()),
206 guard: None,
207 },
208 ];
209 return self
210 .alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr);
211 }
212 },
213 };
214
215 self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
216 }
217 ast::Expr::EffectExpr(e) => match e.effect() {
218 ast::Effect::Try(_) => {
219 let body = self.collect_block_opt(e.block_expr());
220 self.alloc_expr(Expr::TryBlock { body }, syntax_ptr)
221 }
222 ast::Effect::Unsafe(_) => {
223 let body = self.collect_block_opt(e.block_expr());
224 self.alloc_expr(Expr::Unsafe { body }, syntax_ptr)
225 }
226 // FIXME: we need to record these effects somewhere...
227 ast::Effect::Label(label) => match e.block_expr() {
228 Some(block) => {
229 let res = self.collect_block(block);
230 match &mut self.body.exprs[res] {
231 Expr::Block { label: block_label, .. } => {
232 *block_label =
233 label.lifetime_token().map(|t| Name::new_lifetime(&t))
234 }
235 _ => unreachable!(),
236 }
237 res
238 }
239 None => self.missing_expr(),
240 },
241 // FIXME: we need to record these effects somewhere...
242 ast::Effect::Async(_) => self.collect_block_opt(e.block_expr()),
243 },
244 ast::Expr::BlockExpr(e) => self.collect_block(e),
245 ast::Expr::LoopExpr(e) => {
246 let body = self.collect_block_opt(e.loop_body());
247 self.alloc_expr(
248 Expr::Loop {
249 body,
250 label: e
251 .label()
252 .and_then(|l| l.lifetime_token())
253 .map(|l| Name::new_lifetime(&l)),
254 },
255 syntax_ptr,
256 )
257 }
258 ast::Expr::WhileExpr(e) => {
259 let body = self.collect_block_opt(e.loop_body());
260
261 let condition = match e.condition() {
262 None => self.missing_expr(),
263 Some(condition) => match condition.pat() {
264 None => self.collect_expr_opt(condition.expr()),
265 // if let -- desugar to match
266 Some(pat) => {
267 mark::hit!(infer_resolve_while_let);
268 let pat = self.collect_pat(pat);
269 let match_expr = self.collect_expr_opt(condition.expr());
270 let placeholder_pat = self.missing_pat();
271 let break_ =
272 self.alloc_expr_desugared(Expr::Break { expr: None, label: None });
273 let arms = vec![
274 MatchArm { pat, expr: body, guard: None },
275 MatchArm { pat: placeholder_pat, expr: break_, guard: None },
276 ];
277 let match_expr =
278 self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms });
279 return self.alloc_expr(
280 Expr::Loop {
281 body: match_expr,
282 label: e
283 .label()
284 .and_then(|l| l.lifetime_token())
285 .map(|l| Name::new_lifetime(&l)),
286 },
287 syntax_ptr,
288 );
289 }
290 },
291 };
292
293 self.alloc_expr(
294 Expr::While {
295 condition,
296 body,
297 label: e
298 .label()
299 .and_then(|l| l.lifetime_token())
300 .map(|l| Name::new_lifetime(&l)),
301 },
302 syntax_ptr,
303 )
304 }
305 ast::Expr::ForExpr(e) => {
306 let iterable = self.collect_expr_opt(e.iterable());
307 let pat = self.collect_pat_opt(e.pat());
308 let body = self.collect_block_opt(e.loop_body());
309 self.alloc_expr(
310 Expr::For {
311 iterable,
312 pat,
313 body,
314 label: e
315 .label()
316 .and_then(|l| l.lifetime_token())
317 .map(|l| Name::new_lifetime(&l)),
318 },
319 syntax_ptr,
320 )
321 }
322 ast::Expr::CallExpr(e) => {
323 let callee = self.collect_expr_opt(e.expr());
324 let args = if let Some(arg_list) = e.arg_list() {
325 arg_list.args().map(|e| self.collect_expr(e)).collect()
326 } else {
327 Vec::new()
328 };
329 self.alloc_expr(Expr::Call { callee, args }, syntax_ptr)
330 }
331 ast::Expr::MethodCallExpr(e) => {
332 let receiver = self.collect_expr_opt(e.receiver());
333 let args = if let Some(arg_list) = e.arg_list() {
334 arg_list.args().map(|e| self.collect_expr(e)).collect()
335 } else {
336 Vec::new()
337 };
338 let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
339 let generic_args =
340 e.generic_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx(), it));
341 self.alloc_expr(
342 Expr::MethodCall { receiver, method_name, args, generic_args },
343 syntax_ptr,
344 )
345 }
346 ast::Expr::MatchExpr(e) => {
347 let expr = self.collect_expr_opt(e.expr());
348 let arms = if let Some(match_arm_list) = e.match_arm_list() {
349 match_arm_list
350 .arms()
351 .map(|arm| MatchArm {
352 pat: self.collect_pat_opt(arm.pat()),
353 expr: self.collect_expr_opt(arm.expr()),
354 guard: arm
355 .guard()
356 .and_then(|guard| guard.expr())
357 .map(|e| self.collect_expr(e)),
358 })
359 .collect()
360 } else {
361 Vec::new()
362 };
363 self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
364 }
365 ast::Expr::PathExpr(e) => {
366 let path = e
367 .path()
368 .and_then(|path| self.expander.parse_path(path))
369 .map(Expr::Path)
370 .unwrap_or(Expr::Missing);
371 self.alloc_expr(path, syntax_ptr)
372 }
373 ast::Expr::ContinueExpr(e) => self.alloc_expr(
374 Expr::Continue { label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
375 syntax_ptr,
376 ),
377 ast::Expr::BreakExpr(e) => {
378 let expr = e.expr().map(|e| self.collect_expr(e));
379 self.alloc_expr(
380 Expr::Break { expr, label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
381 syntax_ptr,
382 )
383 }
384 ast::Expr::ParenExpr(e) => {
385 let inner = self.collect_expr_opt(e.expr());
386 // make the paren expr point to the inner expression as well
387 let src = self.expander.to_source(syntax_ptr);
388 self.source_map.expr_map.insert(src, inner);
389 inner
390 }
391 ast::Expr::ReturnExpr(e) => {
392 let expr = e.expr().map(|e| self.collect_expr(e));
393 self.alloc_expr(Expr::Return { expr }, syntax_ptr)
394 }
395 ast::Expr::RecordExpr(e) => {
396 let path = e.path().and_then(|path| self.expander.parse_path(path));
397 let mut field_ptrs = Vec::new();
398 let record_lit = if let Some(nfl) = e.record_expr_field_list() {
399 let fields = nfl
400 .fields()
401 .inspect(|field| field_ptrs.push(AstPtr::new(field)))
402 .filter_map(|field| {
403 if !self.expander.is_cfg_enabled(&field) {
404 return None;
405 }
406 let name = field.field_name()?.as_name();
407
408 Some(RecordLitField {
409 name,
410 expr: match field.expr() {
411 Some(e) => self.collect_expr(e),
412 None => self.missing_expr(),
413 },
414 })
415 })
416 .collect();
417 let spread = nfl.spread().map(|s| self.collect_expr(s));
418 Expr::RecordLit { path, fields, spread }
419 } else {
420 Expr::RecordLit { path, fields: Vec::new(), spread: None }
421 };
422
423 let res = self.alloc_expr(record_lit, syntax_ptr);
424 for (i, ptr) in field_ptrs.into_iter().enumerate() {
425 let src = self.expander.to_source(ptr);
426 self.source_map.field_map.insert((res, i), src);
427 }
428 res
429 }
430 ast::Expr::FieldExpr(e) => {
431 let expr = self.collect_expr_opt(e.expr());
432 let name = match e.field_access() {
433 Some(kind) => kind.as_name(),
434 _ => Name::missing(),
435 };
436 self.alloc_expr(Expr::Field { expr, name }, syntax_ptr)
437 }
438 ast::Expr::AwaitExpr(e) => {
439 let expr = self.collect_expr_opt(e.expr());
440 self.alloc_expr(Expr::Await { expr }, syntax_ptr)
441 }
442 ast::Expr::TryExpr(e) => {
443 let expr = self.collect_expr_opt(e.expr());
444 self.alloc_expr(Expr::Try { expr }, syntax_ptr)
445 }
446 ast::Expr::CastExpr(e) => {
447 let expr = self.collect_expr_opt(e.expr());
448 let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.ty());
449 self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
450 }
451 ast::Expr::RefExpr(e) => {
452 let expr = self.collect_expr_opt(e.expr());
453 let raw_tok = e.raw_token().is_some();
454 let mutability = if raw_tok {
455 if e.mut_token().is_some() {
456 Mutability::Mut
457 } else if e.const_token().is_some() {
458 Mutability::Shared
459 } else {
460 unreachable!("parser only remaps to raw_token() if matching mutability token follows")
461 }
462 } else {
463 Mutability::from_mutable(e.mut_token().is_some())
464 };
465 let rawness = Rawness::from_raw(raw_tok);
466 self.alloc_expr(Expr::Ref { expr, rawness, mutability }, syntax_ptr)
467 }
468 ast::Expr::PrefixExpr(e) => {
469 let expr = self.collect_expr_opt(e.expr());
470 if let Some(op) = e.op_kind() {
471 self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr)
472 } else {
473 self.alloc_expr(Expr::Missing, syntax_ptr)
474 }
475 }
476 ast::Expr::ClosureExpr(e) => {
477 let mut args = Vec::new();
478 let mut arg_types = Vec::new();
479 if let Some(pl) = e.param_list() {
480 for param in pl.params() {
481 let pat = self.collect_pat_opt(param.pat());
482 let type_ref = param.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
483 args.push(pat);
484 arg_types.push(type_ref);
485 }
486 }
487 let ret_type =
488 e.ret_type().and_then(|r| r.ty()).map(|it| TypeRef::from_ast(&self.ctx(), it));
489 let body = self.collect_expr_opt(e.body());
490 self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr)
491 }
492 ast::Expr::BinExpr(e) => {
493 let lhs = self.collect_expr_opt(e.lhs());
494 let rhs = self.collect_expr_opt(e.rhs());
495 let op = e.op_kind().map(BinaryOp::from);
496 self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
497 }
498 ast::Expr::TupleExpr(e) => {
499 let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect();
500 self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr)
501 }
502 ast::Expr::BoxExpr(e) => {
503 let expr = self.collect_expr_opt(e.expr());
504 self.alloc_expr(Expr::Box { expr }, syntax_ptr)
505 }
506
507 ast::Expr::ArrayExpr(e) => {
508 let kind = e.kind();
509
510 match kind {
511 ArrayExprKind::ElementList(e) => {
512 let exprs = e.map(|expr| self.collect_expr(expr)).collect();
513 self.alloc_expr(Expr::Array(Array::ElementList(exprs)), syntax_ptr)
514 }
515 ArrayExprKind::Repeat { initializer, repeat } => {
516 let initializer = self.collect_expr_opt(initializer);
517 let repeat = self.collect_expr_opt(repeat);
518 self.alloc_expr(
519 Expr::Array(Array::Repeat { initializer, repeat }),
520 syntax_ptr,
521 )
522 }
523 }
524 }
525
526 ast::Expr::Literal(e) => self.alloc_expr(Expr::Literal(e.kind().into()), syntax_ptr),
527 ast::Expr::IndexExpr(e) => {
528 let base = self.collect_expr_opt(e.base());
529 let index = self.collect_expr_opt(e.index());
530 self.alloc_expr(Expr::Index { base, index }, syntax_ptr)
531 }
532 ast::Expr::RangeExpr(e) => {
533 let lhs = e.start().map(|lhs| self.collect_expr(lhs));
534 let rhs = e.end().map(|rhs| self.collect_expr(rhs));
535 match e.op_kind() {
536 Some(range_type) => {
537 self.alloc_expr(Expr::Range { lhs, rhs, range_type }, syntax_ptr)
538 }
539 None => self.alloc_expr(Expr::Missing, syntax_ptr),
540 }
541 }
542 ast::Expr::MacroCall(e) => {
543 if let Some(name) = e.is_macro_rules().map(|it| it.as_name()) {
544 let mac = MacroDefId {
545 krate: Some(self.expander.module.krate),
546 ast_id: Some(self.expander.ast_id(&e)),
547 kind: MacroDefKind::Declarative,
548 local_inner: false,
549 };
550 self.body.item_scope.define_legacy_macro(name, mac);
551
552 // FIXME: do we still need to allocate this as missing ?
553 self.alloc_expr(Expr::Missing, syntax_ptr)
554 } else {
555 let macro_call = self.expander.to_source(AstPtr::new(&e));
556 match self.expander.enter_expand(self.db, Some(&self.body.item_scope), e) {
557 Some((mark, expansion)) => {
558 self.source_map
559 .expansions
560 .insert(macro_call, self.expander.current_file_id);
561
562 let item_tree = self.db.item_tree(self.expander.current_file_id);
563 self.item_trees.insert(self.expander.current_file_id, item_tree);
564 let id = self.collect_expr(expansion);
565 self.expander.exit(self.db, mark);
566 id
567 }
568 None => self.alloc_expr(Expr::Missing, syntax_ptr),
569 }
570 }
571 }
572 }
573 }
574
575 fn find_inner_item<N: ItemTreeNode>(&self, ast: &N::Source) -> Option<ItemTreeId<N>> {
576 let id = self.expander.ast_id(ast);
577 let tree = &self.item_trees[&id.file_id];
578
579 // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes
580
581 // Root file (non-macro).
582 let item_tree_id = tree
583 .all_inner_items()
584 .chain(tree.top_level_items().iter().copied())
585 .filter_map(|mod_item| mod_item.downcast::<N>())
586 .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value.upcast())
587 .or_else(|| {
588 log::debug!(
589 "couldn't find inner {} item for {:?} (AST: `{}` - {:?})",
590 type_name::<N>(),
591 id,
592 ast.syntax(),
593 ast.syntax(),
594 );
595 None
596 })?;
597
598 Some(ItemTreeId::new(id.file_id, item_tree_id))
599 }
600
601 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
602 if let Some(expr) = expr {
603 self.collect_expr(expr)
604 } else {
605 self.missing_expr()
606 }
607 }
608
609 fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
610 let syntax_node_ptr = AstPtr::new(&block.clone().into());
611 self.collect_block_items(&block);
612 let statements = block
613 .statements()
614 .filter_map(|s| {
615 let stmt = match s {
616 ast::Stmt::LetStmt(stmt) => {
617 let pat = self.collect_pat_opt(stmt.pat());
618 let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
619 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
620 Statement::Let { pat, type_ref, initializer }
621 }
622 ast::Stmt::ExprStmt(stmt) => {
623 Statement::Expr(self.collect_expr_opt(stmt.expr()))
624 }
625 ast::Stmt::Item(_) => return None,
626 };
627 Some(stmt)
628 })
629 .collect();
630 let tail = block.expr().map(|e| self.collect_expr(e));
631 self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr)
632 }
633
634 fn collect_block_items(&mut self, block: &ast::BlockExpr) {
635 let container = ContainerId::DefWithBodyId(self.def);
636
637 let items = block
638 .statements()
639 .filter_map(|stmt| match stmt {
640 ast::Stmt::Item(it) => Some(it),
641 ast::Stmt::LetStmt(_) | ast::Stmt::ExprStmt(_) => None,
642 })
643 .filter_map(|item| {
644 let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
645 ast::Item::Fn(def) => {
646 let id = self.find_inner_item(&def)?;
647 (
648 FunctionLoc { container: container.into(), id }.intern(self.db).into(),
649 def.name(),
650 )
651 }
652 ast::Item::TypeAlias(def) => {
653 let id = self.find_inner_item(&def)?;
654 (
655 TypeAliasLoc { container: container.into(), id }.intern(self.db).into(),
656 def.name(),
657 )
658 }
659 ast::Item::Const(def) => {
660 let id = self.find_inner_item(&def)?;
661 (
662 ConstLoc { container: container.into(), id }.intern(self.db).into(),
663 def.name(),
664 )
665 }
666 ast::Item::Static(def) => {
667 let id = self.find_inner_item(&def)?;
668 (StaticLoc { container, id }.intern(self.db).into(), def.name())
669 }
670 ast::Item::Struct(def) => {
671 let id = self.find_inner_item(&def)?;
672 (StructLoc { container, id }.intern(self.db).into(), def.name())
673 }
674 ast::Item::Enum(def) => {
675 let id = self.find_inner_item(&def)?;
676 (EnumLoc { container, id }.intern(self.db).into(), def.name())
677 }
678 ast::Item::Union(def) => {
679 let id = self.find_inner_item(&def)?;
680 (UnionLoc { container, id }.intern(self.db).into(), def.name())
681 }
682 ast::Item::Trait(def) => {
683 let id = self.find_inner_item(&def)?;
684 (TraitLoc { container, id }.intern(self.db).into(), def.name())
685 }
686 ast::Item::ExternBlock(_) => return None, // FIXME: collect from extern blocks
687 ast::Item::Impl(_)
688 | ast::Item::Use(_)
689 | ast::Item::ExternCrate(_)
690 | ast::Item::Module(_)
691 | ast::Item::MacroCall(_) => return None,
692 };
693
694 Some((def, name))
695 })
696 .collect::<Vec<_>>();
697
698 for (def, name) in items {
699 self.body.item_scope.define_def(def);
700 if let Some(name) = name {
701 let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
702 let has_constructor = match def {
703 ModuleDefId::AdtId(AdtId::StructId(s)) => {
704 self.db.struct_data(s).variant_data.kind() != StructKind::Record
705 }
706 _ => true,
707 };
708 self.body.item_scope.push_res(
709 name.as_name(),
710 crate::per_ns::PerNs::from_def(def, vis, has_constructor),
711 );
712 }
713 }
714 }
715
716 fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId {
717 if let Some(block) = expr {
718 self.collect_block(block)
719 } else {
720 self.missing_expr()
721 }
722 }
723
724 fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
725 let pattern = match &pat {
726 ast::Pat::IdentPat(bp) => {
727 let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
728 let annotation =
729 BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
730 let subpat = bp.pat().map(|subpat| self.collect_pat(subpat));
731 if annotation == BindingAnnotation::Unannotated && subpat.is_none() {
732 // This could also be a single-segment path pattern. To
733 // decide that, we need to try resolving the name.
734 let (resolved, _) = self.expander.crate_def_map.resolve_path(
735 self.db,
736 self.expander.module.local_id,
737 &name.clone().into(),
738 BuiltinShadowMode::Other,
739 );
740 match resolved.take_values() {
741 Some(ModuleDefId::ConstId(_)) => Pat::Path(name.into()),
742 Some(ModuleDefId::EnumVariantId(_)) => {
743 // this is only really valid for unit variants, but
744 // shadowing other enum variants with a pattern is
745 // an error anyway
746 Pat::Path(name.into())
747 }
748 Some(ModuleDefId::AdtId(AdtId::StructId(s)))
749 if self.db.struct_data(s).variant_data.kind() != StructKind::Record =>
750 {
751 // Funnily enough, record structs *can* be shadowed
752 // by pattern bindings (but unit or tuple structs
753 // can't).
754 Pat::Path(name.into())
755 }
756 // shadowing statics is an error as well, so we just ignore that case here
757 _ => Pat::Bind { name, mode: annotation, subpat },
758 }
759 } else {
760 Pat::Bind { name, mode: annotation, subpat }
761 }
762 }
763 ast::Pat::TupleStructPat(p) => {
764 let path = p.path().and_then(|path| self.expander.parse_path(path));
765 let (args, ellipsis) = self.collect_tuple_pat(p.fields());
766 Pat::TupleStruct { path, args, ellipsis }
767 }
768 ast::Pat::RefPat(p) => {
769 let pat = self.collect_pat_opt(p.pat());
770 let mutability = Mutability::from_mutable(p.mut_token().is_some());
771 Pat::Ref { pat, mutability }
772 }
773 ast::Pat::PathPat(p) => {
774 let path = p.path().and_then(|path| self.expander.parse_path(path));
775 path.map(Pat::Path).unwrap_or(Pat::Missing)
776 }
777 ast::Pat::OrPat(p) => {
778 let pats = p.pats().map(|p| self.collect_pat(p)).collect();
779 Pat::Or(pats)
780 }
781 ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat()),
782 ast::Pat::TuplePat(p) => {
783 let (args, ellipsis) = self.collect_tuple_pat(p.fields());
784 Pat::Tuple { args, ellipsis }
785 }
786 ast::Pat::WildcardPat(_) => Pat::Wild,
787 ast::Pat::RecordPat(p) => {
788 let path = p.path().and_then(|path| self.expander.parse_path(path));
789 let args: Vec<_> = p
790 .record_pat_field_list()
791 .expect("every struct should have a field list")
792 .fields()
793 .filter_map(|f| {
794 let ast_pat = f.pat()?;
795 let pat = self.collect_pat(ast_pat);
796 let name = f.field_name()?.as_name();
797 Some(RecordFieldPat { name, pat })
798 })
799 .collect();
800
801 let ellipsis = p
802 .record_pat_field_list()
803 .expect("every struct should have a field list")
804 .dotdot_token()
805 .is_some();
806
807 Pat::Record { path, args, ellipsis }
808 }
809 ast::Pat::SlicePat(p) => {
810 let SlicePatComponents { prefix, slice, suffix } = p.components();
811
812 // FIXME properly handle `RestPat`
813 Pat::Slice {
814 prefix: prefix.into_iter().map(|p| self.collect_pat(p)).collect(),
815 slice: slice.map(|p| self.collect_pat(p)),
816 suffix: suffix.into_iter().map(|p| self.collect_pat(p)).collect(),
817 }
818 }
819 ast::Pat::LiteralPat(lit) => {
820 if let Some(ast_lit) = lit.literal() {
821 let expr = Expr::Literal(ast_lit.kind().into());
822 let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
823 let expr_id = self.alloc_expr(expr, expr_ptr);
824 Pat::Lit(expr_id)
825 } else {
826 Pat::Missing
827 }
828 }
829 ast::Pat::RestPat(_) => {
830 // `RestPat` requires special handling and should not be mapped
831 // to a Pat. Here we are using `Pat::Missing` as a fallback for
832 // when `RestPat` is mapped to `Pat`, which can easily happen
833 // when the source code being analyzed has a malformed pattern
834 // which includes `..` in a place where it isn't valid.
835
836 Pat::Missing
837 }
838 // FIXME: implement
839 ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing,
840 };
841 let ptr = AstPtr::new(&pat);
842 self.alloc_pat(pattern, Either::Left(ptr))
843 }
844
845 fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
846 if let Some(pat) = pat {
847 self.collect_pat(pat)
848 } else {
849 self.missing_pat()
850 }
851 }
852
853 fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) {
854 // Find the location of the `..`, if there is one. Note that we do not
855 // consider the possiblity of there being multiple `..` here.
856 let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
857 // We want to skip the `..` pattern here, since we account for it above.
858 let args = args
859 .filter(|p| !matches!(p, ast::Pat::RestPat(_)))
860 .map(|p| self.collect_pat(p))
861 .collect();
862
863 (args, ellipsis)
864 }
865}
866
867impl From<ast::BinOp> for BinaryOp {
868 fn from(ast_op: ast::BinOp) -> Self {
869 match ast_op {
870 ast::BinOp::BooleanOr => BinaryOp::LogicOp(LogicOp::Or),
871 ast::BinOp::BooleanAnd => BinaryOp::LogicOp(LogicOp::And),
872 ast::BinOp::EqualityTest => BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
873 ast::BinOp::NegatedEqualityTest => BinaryOp::CmpOp(CmpOp::Eq { negated: true }),
874 ast::BinOp::LesserEqualTest => {
875 BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: false })
876 }
877 ast::BinOp::GreaterEqualTest => {
878 BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: false })
879 }
880 ast::BinOp::LesserTest => {
881 BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: true })
882 }
883 ast::BinOp::GreaterTest => {
884 BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: true })
885 }
886 ast::BinOp::Addition => BinaryOp::ArithOp(ArithOp::Add),
887 ast::BinOp::Multiplication => BinaryOp::ArithOp(ArithOp::Mul),
888 ast::BinOp::Subtraction => BinaryOp::ArithOp(ArithOp::Sub),
889 ast::BinOp::Division => BinaryOp::ArithOp(ArithOp::Div),
890 ast::BinOp::Remainder => BinaryOp::ArithOp(ArithOp::Rem),
891 ast::BinOp::LeftShift => BinaryOp::ArithOp(ArithOp::Shl),
892 ast::BinOp::RightShift => BinaryOp::ArithOp(ArithOp::Shr),
893 ast::BinOp::BitwiseXor => BinaryOp::ArithOp(ArithOp::BitXor),
894 ast::BinOp::BitwiseOr => BinaryOp::ArithOp(ArithOp::BitOr),
895 ast::BinOp::BitwiseAnd => BinaryOp::ArithOp(ArithOp::BitAnd),
896 ast::BinOp::Assignment => BinaryOp::Assignment { op: None },
897 ast::BinOp::AddAssign => BinaryOp::Assignment { op: Some(ArithOp::Add) },
898 ast::BinOp::DivAssign => BinaryOp::Assignment { op: Some(ArithOp::Div) },
899 ast::BinOp::MulAssign => BinaryOp::Assignment { op: Some(ArithOp::Mul) },
900 ast::BinOp::RemAssign => BinaryOp::Assignment { op: Some(ArithOp::Rem) },
901 ast::BinOp::ShlAssign => BinaryOp::Assignment { op: Some(ArithOp::Shl) },
902 ast::BinOp::ShrAssign => BinaryOp::Assignment { op: Some(ArithOp::Shr) },
903 ast::BinOp::SubAssign => BinaryOp::Assignment { op: Some(ArithOp::Sub) },
904 ast::BinOp::BitOrAssign => BinaryOp::Assignment { op: Some(ArithOp::BitOr) },
905 ast::BinOp::BitAndAssign => BinaryOp::Assignment { op: Some(ArithOp::BitAnd) },
906 ast::BinOp::BitXorAssign => BinaryOp::Assignment { op: Some(ArithOp::BitXor) },
907 }
908 }
909}
910
911impl From<ast::LiteralKind> for Literal {
912 fn from(ast_lit_kind: ast::LiteralKind) -> Self {
913 match ast_lit_kind {
914 LiteralKind::IntNumber { suffix } => {
915 let known_name = suffix.and_then(|it| BuiltinInt::from_suffix(&it));
916
917 Literal::Int(Default::default(), known_name)
918 }
919 LiteralKind::FloatNumber { suffix } => {
920 let known_name = suffix.and_then(|it| BuiltinFloat::from_suffix(&it));
921
922 Literal::Float(Default::default(), known_name)
923 }
924 LiteralKind::ByteString => Literal::ByteString(Default::default()),
925 LiteralKind::String => Literal::String(Default::default()),
926 LiteralKind::Byte => Literal::Int(Default::default(), Some(BuiltinInt::U8)),
927 LiteralKind::Bool(val) => Literal::Bool(val),
928 LiteralKind::Char => Literal::Char(Default::default()),
929 }
930 }
931}
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
new file mode 100644
index 000000000..9142bc05b
--- /dev/null
+++ b/crates/hir_def/src/body/scope.rs
@@ -0,0 +1,456 @@
1//! Name resolution for expressions.
2use std::sync::Arc;
3
4use arena::{Arena, Idx};
5use hir_expand::name::Name;
6use rustc_hash::FxHashMap;
7
8use crate::{
9 body::Body,
10 db::DefDatabase,
11 expr::{Expr, ExprId, Pat, PatId, Statement},
12 DefWithBodyId,
13};
14
15pub type ScopeId = Idx<ScopeData>;
16
17#[derive(Debug, PartialEq, Eq)]
18pub struct ExprScopes {
19 scopes: Arena<ScopeData>,
20 scope_by_expr: FxHashMap<ExprId, ScopeId>,
21}
22
23#[derive(Debug, PartialEq, Eq)]
24pub struct ScopeEntry {
25 name: Name,
26 pat: PatId,
27}
28
29impl ScopeEntry {
30 pub fn name(&self) -> &Name {
31 &self.name
32 }
33
34 pub fn pat(&self) -> PatId {
35 self.pat
36 }
37}
38
39#[derive(Debug, PartialEq, Eq)]
40pub struct ScopeData {
41 parent: Option<ScopeId>,
42 entries: Vec<ScopeEntry>,
43}
44
45impl ExprScopes {
46 pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> {
47 let body = db.body(def);
48 Arc::new(ExprScopes::new(&*body))
49 }
50
51 fn new(body: &Body) -> ExprScopes {
52 let mut scopes =
53 ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
54 let root = scopes.root_scope();
55 scopes.add_params_bindings(body, root, &body.params);
56 compute_expr_scopes(body.body_expr, body, &mut scopes, root);
57 scopes
58 }
59
60 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
61 &self.scopes[scope].entries
62 }
63
64 pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ {
65 std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
66 }
67
68 pub fn resolve_name_in_scope(&self, scope: ScopeId, name: &Name) -> Option<&ScopeEntry> {
69 self.scope_chain(Some(scope))
70 .find_map(|scope| self.entries(scope).iter().find(|it| it.name == *name))
71 }
72
73 pub fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
74 self.scope_by_expr.get(&expr).copied()
75 }
76
77 pub fn scope_by_expr(&self) -> &FxHashMap<ExprId, ScopeId> {
78 &self.scope_by_expr
79 }
80
81 fn root_scope(&mut self) -> ScopeId {
82 self.scopes.alloc(ScopeData { parent: None, entries: vec![] })
83 }
84
85 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
86 self.scopes.alloc(ScopeData { parent: Some(parent), entries: vec![] })
87 }
88
89 fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
90 let pattern = &body[pat];
91 if let Pat::Bind { name, .. } = pattern {
92 let entry = ScopeEntry { name: name.clone(), pat };
93 self.scopes[scope].entries.push(entry);
94 }
95
96 pattern.walk_child_pats(|pat| self.add_bindings(body, scope, pat));
97 }
98
99 fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) {
100 params.iter().for_each(|pat| self.add_bindings(body, scope, *pat));
101 }
102
103 fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
104 self.scope_by_expr.insert(node, scope);
105 }
106}
107
108fn compute_block_scopes(
109 statements: &[Statement],
110 tail: Option<ExprId>,
111 body: &Body,
112 scopes: &mut ExprScopes,
113 mut scope: ScopeId,
114) {
115 for stmt in statements {
116 match stmt {
117 Statement::Let { pat, initializer, .. } => {
118 if let Some(expr) = initializer {
119 scopes.set_scope(*expr, scope);
120 compute_expr_scopes(*expr, body, scopes, scope);
121 }
122 scope = scopes.new_scope(scope);
123 scopes.add_bindings(body, scope, *pat);
124 }
125 Statement::Expr(expr) => {
126 scopes.set_scope(*expr, scope);
127 compute_expr_scopes(*expr, body, scopes, scope);
128 }
129 }
130 }
131 if let Some(expr) = tail {
132 compute_expr_scopes(expr, body, scopes, scope);
133 }
134}
135
136fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
137 scopes.set_scope(expr, scope);
138 match &body[expr] {
139 Expr::Block { statements, tail, .. } => {
140 compute_block_scopes(&statements, *tail, body, scopes, scope);
141 }
142 Expr::For { iterable, pat, body: body_expr, .. } => {
143 compute_expr_scopes(*iterable, body, scopes, scope);
144 let scope = scopes.new_scope(scope);
145 scopes.add_bindings(body, scope, *pat);
146 compute_expr_scopes(*body_expr, body, scopes, scope);
147 }
148 Expr::Lambda { args, body: body_expr, .. } => {
149 let scope = scopes.new_scope(scope);
150 scopes.add_params_bindings(body, scope, &args);
151 compute_expr_scopes(*body_expr, body, scopes, scope);
152 }
153 Expr::Match { expr, arms } => {
154 compute_expr_scopes(*expr, body, scopes, scope);
155 for arm in arms {
156 let scope = scopes.new_scope(scope);
157 scopes.add_bindings(body, scope, arm.pat);
158 if let Some(guard) = arm.guard {
159 scopes.set_scope(guard, scope);
160 compute_expr_scopes(guard, body, scopes, scope);
161 }
162 scopes.set_scope(arm.expr, scope);
163 compute_expr_scopes(arm.expr, body, scopes, scope);
164 }
165 }
166 e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
167 };
168}
169
170#[cfg(test)]
171mod tests {
172 use base_db::{fixture::WithFixture, FileId, SourceDatabase};
173 use hir_expand::{name::AsName, InFile};
174 use syntax::{algo::find_node_at_offset, ast, AstNode};
175 use test_utils::{assert_eq_text, extract_offset, mark};
176
177 use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId};
178
179 fn find_function(db: &TestDB, file_id: FileId) -> FunctionId {
180 let krate = db.test_crate();
181 let crate_def_map = db.crate_def_map(krate);
182
183 let module = crate_def_map.modules_for_file(file_id).next().unwrap();
184 let (_, def) = crate_def_map[module].scope.entries().next().unwrap();
185 match def.take_values().unwrap() {
186 ModuleDefId::FunctionId(it) => it,
187 _ => panic!(),
188 }
189 }
190
191 fn do_check(ra_fixture: &str, expected: &[&str]) {
192 let (offset, code) = extract_offset(ra_fixture);
193 let code = {
194 let mut buf = String::new();
195 let off: usize = offset.into();
196 buf.push_str(&code[..off]);
197 buf.push_str("<|>marker");
198 buf.push_str(&code[off..]);
199 buf
200 };
201
202 let (db, position) = TestDB::with_position(&code);
203 let file_id = position.file_id;
204 let offset = position.offset;
205
206 let file_syntax = db.parse(file_id).syntax_node();
207 let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap();
208 let function = find_function(&db, file_id);
209
210 let scopes = db.expr_scopes(function.into());
211 let (_body, source_map) = db.body_with_source_map(function.into());
212
213 let expr_id = source_map
214 .node_expr(InFile { file_id: file_id.into(), value: &marker.into() })
215 .unwrap();
216 let scope = scopes.scope_for(expr_id);
217
218 let actual = scopes
219 .scope_chain(scope)
220 .flat_map(|scope| scopes.entries(scope))
221 .map(|it| it.name().to_string())
222 .collect::<Vec<_>>()
223 .join("\n");
224 let expected = expected.join("\n");
225 assert_eq_text!(&expected, &actual);
226 }
227
228 #[test]
229 fn test_lambda_scope() {
230 do_check(
231 r"
232 fn quux(foo: i32) {
233 let f = |bar, baz: i32| {
234 <|>
235 };
236 }",
237 &["bar", "baz", "foo"],
238 );
239 }
240
241 #[test]
242 fn test_call_scope() {
243 do_check(
244 r"
245 fn quux() {
246 f(|x| <|> );
247 }",
248 &["x"],
249 );
250 }
251
252 #[test]
253 fn test_method_call_scope() {
254 do_check(
255 r"
256 fn quux() {
257 z.f(|x| <|> );
258 }",
259 &["x"],
260 );
261 }
262
263 #[test]
264 fn test_loop_scope() {
265 do_check(
266 r"
267 fn quux() {
268 loop {
269 let x = ();
270 <|>
271 };
272 }",
273 &["x"],
274 );
275 }
276
277 #[test]
278 fn test_match() {
279 do_check(
280 r"
281 fn quux() {
282 match () {
283 Some(x) => {
284 <|>
285 }
286 };
287 }",
288 &["x"],
289 );
290 }
291
292 #[test]
293 fn test_shadow_variable() {
294 do_check(
295 r"
296 fn foo(x: String) {
297 let x : &str = &x<|>;
298 }",
299 &["x"],
300 );
301 }
302
303 #[test]
304 fn test_bindings_after_at() {
305 do_check(
306 r"
307fn foo() {
308 match Some(()) {
309 opt @ Some(unit) => {
310 <|>
311 }
312 _ => {}
313 }
314}
315",
316 &["opt", "unit"],
317 );
318 }
319
320 #[test]
321 fn macro_inner_item() {
322 do_check(
323 r"
324 macro_rules! mac {
325 () => {{
326 fn inner() {}
327 inner();
328 }};
329 }
330
331 fn foo() {
332 mac!();
333 <|>
334 }
335 ",
336 &[],
337 );
338 }
339
340 #[test]
341 fn broken_inner_item() {
342 do_check(
343 r"
344 fn foo() {
345 trait {}
346 <|>
347 }
348 ",
349 &[],
350 );
351 }
352
353 fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
354 let (db, position) = TestDB::with_position(ra_fixture);
355 let file_id = position.file_id;
356 let offset = position.offset;
357
358 let file = db.parse(file_id).ok().unwrap();
359 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
360 .expect("failed to find a name at the target offset");
361 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset).unwrap();
362
363 let function = find_function(&db, file_id);
364
365 let scopes = db.expr_scopes(function.into());
366 let (_body, source_map) = db.body_with_source_map(function.into());
367
368 let expr_scope = {
369 let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap();
370 let expr_id =
371 source_map.node_expr(InFile { file_id: file_id.into(), value: &expr_ast }).unwrap();
372 scopes.scope_for(expr_id).unwrap()
373 };
374
375 let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
376 let pat_src = source_map.pat_syntax(resolved.pat()).unwrap();
377
378 let local_name = pat_src.value.either(
379 |it| it.syntax_node_ptr().to_node(file.syntax()),
380 |it| it.syntax_node_ptr().to_node(file.syntax()),
381 );
382 assert_eq!(local_name.text_range(), expected_name.syntax().text_range());
383 }
384
385 #[test]
386 fn test_resolve_local_name() {
387 do_check_local_name(
388 r#"
389fn foo(x: i32, y: u32) {
390 {
391 let z = x * 2;
392 }
393 {
394 let t = x<|> * 3;
395 }
396}
397"#,
398 7,
399 );
400 }
401
402 #[test]
403 fn test_resolve_local_name_declaration() {
404 do_check_local_name(
405 r#"
406fn foo(x: String) {
407 let x : &str = &x<|>;
408}
409"#,
410 7,
411 );
412 }
413
414 #[test]
415 fn test_resolve_local_name_shadow() {
416 do_check_local_name(
417 r"
418fn foo(x: String) {
419 let x : &str = &x;
420 x<|>
421}
422",
423 28,
424 );
425 }
426
427 #[test]
428 fn ref_patterns_contribute_bindings() {
429 do_check_local_name(
430 r"
431fn foo() {
432 if let Some(&from) = bar() {
433 from<|>;
434 }
435}
436",
437 28,
438 );
439 }
440
441 #[test]
442 fn while_let_desugaring() {
443 mark::check!(infer_resolve_while_let);
444 do_check_local_name(
445 r#"
446fn test() {
447 let foo: Option<f32> = None;
448 while let Option::Some(spam) = foo {
449 spam<|>
450 }
451}
452"#,
453 75,
454 );
455 }
456}
diff --git a/crates/hir_def/src/builtin_type.rs b/crates/hir_def/src/builtin_type.rs
new file mode 100644
index 000000000..0f872b5c0
--- /dev/null
+++ b/crates/hir_def/src/builtin_type.rs
@@ -0,0 +1,166 @@
1//! This module defines built-in types.
2//!
3//! A peculiarity of built-in types is that they are always available and are
4//! not associated with any particular crate.
5
6use std::fmt;
7
8use hir_expand::name::{name, AsName, Name};
9
10#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
11pub enum Signedness {
12 Signed,
13 Unsigned,
14}
15
16#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
17pub enum IntBitness {
18 Xsize,
19 X8,
20 X16,
21 X32,
22 X64,
23 X128,
24}
25
26#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
27pub enum FloatBitness {
28 X32,
29 X64,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33pub struct BuiltinInt {
34 pub signedness: Signedness,
35 pub bitness: IntBitness,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
39pub struct BuiltinFloat {
40 pub bitness: FloatBitness,
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
44pub enum BuiltinType {
45 Char,
46 Bool,
47 Str,
48 Int(BuiltinInt),
49 Float(BuiltinFloat),
50}
51
52impl BuiltinType {
53 #[rustfmt::skip]
54 pub const ALL: &'static [(Name, BuiltinType)] = &[
55 (name![char], BuiltinType::Char),
56 (name![bool], BuiltinType::Bool),
57 (name![str], BuiltinType::Str),
58
59 (name![isize], BuiltinType::Int(BuiltinInt::ISIZE)),
60 (name![i8], BuiltinType::Int(BuiltinInt::I8)),
61 (name![i16], BuiltinType::Int(BuiltinInt::I16)),
62 (name![i32], BuiltinType::Int(BuiltinInt::I32)),
63 (name![i64], BuiltinType::Int(BuiltinInt::I64)),
64 (name![i128], BuiltinType::Int(BuiltinInt::I128)),
65
66 (name![usize], BuiltinType::Int(BuiltinInt::USIZE)),
67 (name![u8], BuiltinType::Int(BuiltinInt::U8)),
68 (name![u16], BuiltinType::Int(BuiltinInt::U16)),
69 (name![u32], BuiltinType::Int(BuiltinInt::U32)),
70 (name![u64], BuiltinType::Int(BuiltinInt::U64)),
71 (name![u128], BuiltinType::Int(BuiltinInt::U128)),
72
73 (name![f32], BuiltinType::Float(BuiltinFloat::F32)),
74 (name![f64], BuiltinType::Float(BuiltinFloat::F64)),
75 ];
76}
77
78impl AsName for BuiltinType {
79 fn as_name(&self) -> Name {
80 match self {
81 BuiltinType::Char => name![char],
82 BuiltinType::Bool => name![bool],
83 BuiltinType::Str => name![str],
84 BuiltinType::Int(BuiltinInt { signedness, bitness }) => match (signedness, bitness) {
85 (Signedness::Signed, IntBitness::Xsize) => name![isize],
86 (Signedness::Signed, IntBitness::X8) => name![i8],
87 (Signedness::Signed, IntBitness::X16) => name![i16],
88 (Signedness::Signed, IntBitness::X32) => name![i32],
89 (Signedness::Signed, IntBitness::X64) => name![i64],
90 (Signedness::Signed, IntBitness::X128) => name![i128],
91
92 (Signedness::Unsigned, IntBitness::Xsize) => name![usize],
93 (Signedness::Unsigned, IntBitness::X8) => name![u8],
94 (Signedness::Unsigned, IntBitness::X16) => name![u16],
95 (Signedness::Unsigned, IntBitness::X32) => name![u32],
96 (Signedness::Unsigned, IntBitness::X64) => name![u64],
97 (Signedness::Unsigned, IntBitness::X128) => name![u128],
98 },
99 BuiltinType::Float(BuiltinFloat { bitness }) => match bitness {
100 FloatBitness::X32 => name![f32],
101 FloatBitness::X64 => name![f64],
102 },
103 }
104 }
105}
106
107impl fmt::Display for BuiltinType {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 let type_name = self.as_name();
110 type_name.fmt(f)
111 }
112}
113
114#[rustfmt::skip]
115impl BuiltinInt {
116 pub const ISIZE: BuiltinInt = BuiltinInt { signedness: Signedness::Signed, bitness: IntBitness::Xsize };
117 pub const I8 : BuiltinInt = BuiltinInt { signedness: Signedness::Signed, bitness: IntBitness::X8 };
118 pub const I16 : BuiltinInt = BuiltinInt { signedness: Signedness::Signed, bitness: IntBitness::X16 };
119 pub const I32 : BuiltinInt = BuiltinInt { signedness: Signedness::Signed, bitness: IntBitness::X32 };
120 pub const I64 : BuiltinInt = BuiltinInt { signedness: Signedness::Signed, bitness: IntBitness::X64 };
121 pub const I128 : BuiltinInt = BuiltinInt { signedness: Signedness::Signed, bitness: IntBitness::X128 };
122
123 pub const USIZE: BuiltinInt = BuiltinInt { signedness: Signedness::Unsigned, bitness: IntBitness::Xsize };
124 pub const U8 : BuiltinInt = BuiltinInt { signedness: Signedness::Unsigned, bitness: IntBitness::X8 };
125 pub const U16 : BuiltinInt = BuiltinInt { signedness: Signedness::Unsigned, bitness: IntBitness::X16 };
126 pub const U32 : BuiltinInt = BuiltinInt { signedness: Signedness::Unsigned, bitness: IntBitness::X32 };
127 pub const U64 : BuiltinInt = BuiltinInt { signedness: Signedness::Unsigned, bitness: IntBitness::X64 };
128 pub const U128 : BuiltinInt = BuiltinInt { signedness: Signedness::Unsigned, bitness: IntBitness::X128 };
129
130
131 pub fn from_suffix(suffix: &str) -> Option<BuiltinInt> {
132 let res = match suffix {
133 "isize" => Self::ISIZE,
134 "i8" => Self::I8,
135 "i16" => Self::I16,
136 "i32" => Self::I32,
137 "i64" => Self::I64,
138 "i128" => Self::I128,
139
140 "usize" => Self::USIZE,
141 "u8" => Self::U8,
142 "u16" => Self::U16,
143 "u32" => Self::U32,
144 "u64" => Self::U64,
145 "u128" => Self::U128,
146
147 _ => return None,
148 };
149 Some(res)
150 }
151}
152
153#[rustfmt::skip]
154impl BuiltinFloat {
155 pub const F32: BuiltinFloat = BuiltinFloat { bitness: FloatBitness::X32 };
156 pub const F64: BuiltinFloat = BuiltinFloat { bitness: FloatBitness::X64 };
157
158 pub fn from_suffix(suffix: &str) -> Option<BuiltinFloat> {
159 let res = match suffix {
160 "f32" => BuiltinFloat::F32,
161 "f64" => BuiltinFloat::F64,
162 _ => return None,
163 };
164 Some(res)
165 }
166}
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs
new file mode 100644
index 000000000..dcb00a1d9
--- /dev/null
+++ b/crates/hir_def/src/child_by_source.rs
@@ -0,0 +1,177 @@
1//! When *constructing* `hir`, we start at some parent syntax node and recursively
2//! lower the children.
3//!
4//! This modules allows one to go in the opposite direction: start with a syntax
5//! node for a *child*, and get its hir.
6
7use either::Either;
8
9use crate::{
10 db::DefDatabase,
11 dyn_map::DynMap,
12 item_scope::ItemScope,
13 keys,
14 src::{HasChildSource, HasSource},
15 AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, ModuleDefId,
16 ModuleId, TraitId, VariantId,
17};
18
19pub trait ChildBySource {
20 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap;
21}
22
23impl ChildBySource for TraitId {
24 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
25 let mut res = DynMap::default();
26
27 let data = db.trait_data(*self);
28 for (_name, item) in data.items.iter() {
29 match *item {
30 AssocItemId::FunctionId(func) => {
31 let src = func.lookup(db).source(db);
32 res[keys::FUNCTION].insert(src, func)
33 }
34 AssocItemId::ConstId(konst) => {
35 let src = konst.lookup(db).source(db);
36 res[keys::CONST].insert(src, konst)
37 }
38 AssocItemId::TypeAliasId(ty) => {
39 let src = ty.lookup(db).source(db);
40 res[keys::TYPE_ALIAS].insert(src, ty)
41 }
42 }
43 }
44
45 res
46 }
47}
48
49impl ChildBySource for ImplId {
50 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
51 let mut res = DynMap::default();
52
53 let data = db.impl_data(*self);
54 for &item in data.items.iter() {
55 match item {
56 AssocItemId::FunctionId(func) => {
57 let src = func.lookup(db).source(db);
58 res[keys::FUNCTION].insert(src, func)
59 }
60 AssocItemId::ConstId(konst) => {
61 let src = konst.lookup(db).source(db);
62 res[keys::CONST].insert(src, konst)
63 }
64 AssocItemId::TypeAliasId(ty) => {
65 let src = ty.lookup(db).source(db);
66 res[keys::TYPE_ALIAS].insert(src, ty)
67 }
68 }
69 }
70
71 res
72 }
73}
74
75impl ChildBySource for ModuleId {
76 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
77 let crate_def_map = db.crate_def_map(self.krate);
78 let module_data = &crate_def_map[self.local_id];
79 module_data.scope.child_by_source(db)
80 }
81}
82
83impl ChildBySource for ItemScope {
84 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
85 let mut res = DynMap::default();
86 self.declarations().for_each(|item| add_module_def(db, &mut res, item));
87 self.impls().for_each(|imp| add_impl(db, &mut res, imp));
88 return res;
89
90 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) {
91 match item {
92 ModuleDefId::FunctionId(func) => {
93 let src = func.lookup(db).source(db);
94 map[keys::FUNCTION].insert(src, func)
95 }
96 ModuleDefId::ConstId(konst) => {
97 let src = konst.lookup(db).source(db);
98 map[keys::CONST].insert(src, konst)
99 }
100 ModuleDefId::StaticId(statik) => {
101 let src = statik.lookup(db).source(db);
102 map[keys::STATIC].insert(src, statik)
103 }
104 ModuleDefId::TypeAliasId(ty) => {
105 let src = ty.lookup(db).source(db);
106 map[keys::TYPE_ALIAS].insert(src, ty)
107 }
108 ModuleDefId::TraitId(trait_) => {
109 let src = trait_.lookup(db).source(db);
110 map[keys::TRAIT].insert(src, trait_)
111 }
112 ModuleDefId::AdtId(adt) => match adt {
113 AdtId::StructId(strukt) => {
114 let src = strukt.lookup(db).source(db);
115 map[keys::STRUCT].insert(src, strukt)
116 }
117 AdtId::UnionId(union_) => {
118 let src = union_.lookup(db).source(db);
119 map[keys::UNION].insert(src, union_)
120 }
121 AdtId::EnumId(enum_) => {
122 let src = enum_.lookup(db).source(db);
123 map[keys::ENUM].insert(src, enum_)
124 }
125 },
126 _ => (),
127 }
128 }
129 fn add_impl(db: &dyn DefDatabase, map: &mut DynMap, imp: ImplId) {
130 let src = imp.lookup(db).source(db);
131 map[keys::IMPL].insert(src, imp)
132 }
133 }
134}
135
136impl ChildBySource for VariantId {
137 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
138 let mut res = DynMap::default();
139
140 let arena_map = self.child_source(db);
141 let arena_map = arena_map.as_ref();
142 for (local_id, source) in arena_map.value.iter() {
143 let id = FieldId { parent: *self, local_id };
144 match source {
145 Either::Left(source) => {
146 res[keys::TUPLE_FIELD].insert(arena_map.with_value(source.clone()), id)
147 }
148 Either::Right(source) => {
149 res[keys::RECORD_FIELD].insert(arena_map.with_value(source.clone()), id)
150 }
151 }
152 }
153 res
154 }
155}
156
157impl ChildBySource for EnumId {
158 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
159 let mut res = DynMap::default();
160
161 let arena_map = self.child_source(db);
162 let arena_map = arena_map.as_ref();
163 for (local_id, source) in arena_map.value.iter() {
164 let id = EnumVariantId { parent: *self, local_id };
165 res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id)
166 }
167
168 res
169 }
170}
171
172impl ChildBySource for DefWithBodyId {
173 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
174 let body = db.body(*self);
175 body.item_scope.child_by_source(db)
176 }
177}
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
new file mode 100644
index 000000000..9a8eb4ede
--- /dev/null
+++ b/crates/hir_def/src/data.rs
@@ -0,0 +1,278 @@
1//! Contains basic data about various HIR declarations.
2
3use std::sync::Arc;
4
5use hir_expand::{name::Name, InFile};
6use syntax::ast;
7
8use crate::{
9 attr::Attrs,
10 body::Expander,
11 db::DefDatabase,
12 item_tree::{AssocItem, ItemTreeId, ModItem},
13 type_ref::{TypeBound, TypeRef},
14 visibility::RawVisibility,
15 AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
16 Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
17};
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct FunctionData {
21 pub name: Name,
22 pub params: Vec<TypeRef>,
23 pub ret_type: TypeRef,
24 pub attrs: Attrs,
25 /// True if the first param is `self`. This is relevant to decide whether this
26 /// can be called as a method.
27 pub has_self_param: bool,
28 pub is_unsafe: bool,
29 pub is_varargs: bool,
30 pub visibility: RawVisibility,
31}
32
33impl FunctionData {
34 pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> {
35 let loc = func.lookup(db);
36 let item_tree = db.item_tree(loc.id.file_id);
37 let func = &item_tree[loc.id.value];
38
39 Arc::new(FunctionData {
40 name: func.name.clone(),
41 params: func.params.to_vec(),
42 ret_type: func.ret_type.clone(),
43 attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(),
44 has_self_param: func.has_self_param,
45 is_unsafe: func.is_unsafe,
46 is_varargs: func.is_varargs,
47 visibility: item_tree[func.visibility].clone(),
48 })
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct TypeAliasData {
54 pub name: Name,
55 pub type_ref: Option<TypeRef>,
56 pub visibility: RawVisibility,
57 /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
58 pub bounds: Vec<TypeBound>,
59}
60
61impl TypeAliasData {
62 pub(crate) fn type_alias_data_query(
63 db: &dyn DefDatabase,
64 typ: TypeAliasId,
65 ) -> Arc<TypeAliasData> {
66 let loc = typ.lookup(db);
67 let item_tree = db.item_tree(loc.id.file_id);
68 let typ = &item_tree[loc.id.value];
69
70 Arc::new(TypeAliasData {
71 name: typ.name.clone(),
72 type_ref: typ.type_ref.clone(),
73 visibility: item_tree[typ.visibility].clone(),
74 bounds: typ.bounds.to_vec(),
75 })
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct TraitData {
81 pub name: Name,
82 pub items: Vec<(Name, AssocItemId)>,
83 pub auto: bool,
84}
85
86impl TraitData {
87 pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
88 let tr_loc = tr.lookup(db);
89 let item_tree = db.item_tree(tr_loc.id.file_id);
90 let tr_def = &item_tree[tr_loc.id.value];
91 let name = tr_def.name.clone();
92 let auto = tr_def.auto;
93 let module_id = tr_loc.container.module(db);
94 let container = AssocContainerId::TraitId(tr);
95 let mut expander = Expander::new(db, tr_loc.id.file_id, module_id);
96
97 let items = collect_items(
98 db,
99 module_id,
100 &mut expander,
101 tr_def.items.iter().copied(),
102 tr_loc.id.file_id,
103 container,
104 100,
105 );
106
107 Arc::new(TraitData { name, items, auto })
108 }
109
110 pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {
111 self.items.iter().filter_map(|(_name, item)| match item {
112 AssocItemId::TypeAliasId(t) => Some(*t),
113 _ => None,
114 })
115 }
116
117 pub fn associated_type_by_name(&self, name: &Name) -> Option<TypeAliasId> {
118 self.items.iter().find_map(|(item_name, item)| match item {
119 AssocItemId::TypeAliasId(t) if item_name == name => Some(*t),
120 _ => None,
121 })
122 }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
126pub struct ImplData {
127 pub target_trait: Option<TypeRef>,
128 pub target_type: TypeRef,
129 pub items: Vec<AssocItemId>,
130 pub is_negative: bool,
131}
132
133impl ImplData {
134 pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
135 let _p = profile::span("impl_data_query");
136 let impl_loc = id.lookup(db);
137
138 let item_tree = db.item_tree(impl_loc.id.file_id);
139 let impl_def = &item_tree[impl_loc.id.value];
140 let target_trait = impl_def.target_trait.clone();
141 let target_type = impl_def.target_type.clone();
142 let is_negative = impl_def.is_negative;
143 let module_id = impl_loc.container.module(db);
144 let container = AssocContainerId::ImplId(id);
145 let mut expander = Expander::new(db, impl_loc.id.file_id, module_id);
146
147 let items = collect_items(
148 db,
149 module_id,
150 &mut expander,
151 impl_def.items.iter().copied(),
152 impl_loc.id.file_id,
153 container,
154 100,
155 );
156 let items = items.into_iter().map(|(_, item)| item).collect();
157
158 Arc::new(ImplData { target_trait, target_type, items, is_negative })
159 }
160}
161
162#[derive(Debug, Clone, PartialEq, Eq)]
163pub struct ConstData {
164 /// const _: () = ();
165 pub name: Option<Name>,
166 pub type_ref: TypeRef,
167 pub visibility: RawVisibility,
168}
169
170impl ConstData {
171 pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
172 let loc = konst.lookup(db);
173 let item_tree = db.item_tree(loc.id.file_id);
174 let konst = &item_tree[loc.id.value];
175
176 Arc::new(ConstData {
177 name: konst.name.clone(),
178 type_ref: konst.type_ref.clone(),
179 visibility: item_tree[konst.visibility].clone(),
180 })
181 }
182}
183
184#[derive(Debug, Clone, PartialEq, Eq)]
185pub struct StaticData {
186 pub name: Option<Name>,
187 pub type_ref: TypeRef,
188 pub visibility: RawVisibility,
189 pub mutable: bool,
190}
191
192impl StaticData {
193 pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
194 let node = konst.lookup(db);
195 let item_tree = db.item_tree(node.id.file_id);
196 let statik = &item_tree[node.id.value];
197
198 Arc::new(StaticData {
199 name: Some(statik.name.clone()),
200 type_ref: statik.type_ref.clone(),
201 visibility: item_tree[statik.visibility].clone(),
202 mutable: statik.mutable,
203 })
204 }
205}
206
207fn collect_items(
208 db: &dyn DefDatabase,
209 module: ModuleId,
210 expander: &mut Expander,
211 assoc_items: impl Iterator<Item = AssocItem>,
212 file_id: crate::HirFileId,
213 container: AssocContainerId,
214 limit: usize,
215) -> Vec<(Name, AssocItemId)> {
216 if limit == 0 {
217 return Vec::new();
218 }
219
220 let item_tree = db.item_tree(file_id);
221 let cfg_options = db.crate_graph()[module.krate].cfg_options.clone();
222
223 let mut items = Vec::new();
224 for item in assoc_items {
225 match item {
226 AssocItem::Function(id) => {
227 let item = &item_tree[id];
228 let attrs = item_tree.attrs(ModItem::from(id).into());
229 if !attrs.is_cfg_enabled(&cfg_options) {
230 continue;
231 }
232 let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
233 items.push((item.name.clone(), def.into()));
234 }
235 // FIXME: cfg?
236 AssocItem::Const(id) => {
237 let item = &item_tree[id];
238 let name = match item.name.clone() {
239 Some(name) => name,
240 None => continue,
241 };
242 let def = ConstLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
243 items.push((name, def.into()));
244 }
245 AssocItem::TypeAlias(id) => {
246 let item = &item_tree[id];
247 let def = TypeAliasLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
248 items.push((item.name.clone(), def.into()));
249 }
250 AssocItem::MacroCall(call) => {
251 let call = &item_tree[call];
252 let ast_id_map = db.ast_id_map(file_id);
253 let root = db.parse_or_expand(file_id).unwrap();
254 let call = ast_id_map.get(call.ast_id).to_node(&root);
255
256 if let Some((mark, mac)) = expander.enter_expand(db, None, call) {
257 let src: InFile<ast::MacroItems> = expander.to_source(mac);
258 let item_tree = db.item_tree(src.file_id);
259 let iter =
260 item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
261 items.extend(collect_items(
262 db,
263 module,
264 expander,
265 iter,
266 src.file_id,
267 container,
268 limit - 1,
269 ));
270
271 expander.exit(db, mark);
272 }
273 }
274 }
275 }
276
277 items
278}
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
new file mode 100644
index 000000000..6d694de11
--- /dev/null
+++ b/crates/hir_def/src/db.rs
@@ -0,0 +1,120 @@
1//! Defines database & queries for name resolution.
2use std::sync::Arc;
3
4use base_db::{salsa, CrateId, SourceDatabase, Upcast};
5use hir_expand::{db::AstDatabase, HirFileId};
6use syntax::SmolStr;
7
8use crate::{
9 adt::{EnumData, StructData},
10 attr::Attrs,
11 body::{scope::ExprScopes, Body, BodySourceMap},
12 data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData},
13 docs::Documentation,
14 generics::GenericParams,
15 import_map::ImportMap,
16 item_tree::ItemTree,
17 lang_item::{LangItemTarget, LangItems},
18 nameres::CrateDefMap,
19 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
20 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
21 TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
22};
23
24#[salsa::query_group(InternDatabaseStorage)]
25pub trait InternDatabase: SourceDatabase {
26 #[salsa::interned]
27 fn intern_function(&self, loc: FunctionLoc) -> FunctionId;
28 #[salsa::interned]
29 fn intern_struct(&self, loc: StructLoc) -> StructId;
30 #[salsa::interned]
31 fn intern_union(&self, loc: UnionLoc) -> UnionId;
32 #[salsa::interned]
33 fn intern_enum(&self, loc: EnumLoc) -> EnumId;
34 #[salsa::interned]
35 fn intern_const(&self, loc: ConstLoc) -> ConstId;
36 #[salsa::interned]
37 fn intern_static(&self, loc: StaticLoc) -> StaticId;
38 #[salsa::interned]
39 fn intern_trait(&self, loc: TraitLoc) -> TraitId;
40 #[salsa::interned]
41 fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId;
42 #[salsa::interned]
43 fn intern_impl(&self, loc: ImplLoc) -> ImplId;
44}
45
46#[salsa::query_group(DefDatabaseStorage)]
47pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
48 #[salsa::invoke(ItemTree::item_tree_query)]
49 fn item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
50
51 #[salsa::invoke(crate_def_map_wait)]
52 #[salsa::transparent]
53 fn crate_def_map(&self, krate: CrateId) -> Arc<CrateDefMap>;
54
55 #[salsa::invoke(CrateDefMap::crate_def_map_query)]
56 fn crate_def_map_query(&self, krate: CrateId) -> Arc<CrateDefMap>;
57
58 #[salsa::invoke(StructData::struct_data_query)]
59 fn struct_data(&self, id: StructId) -> Arc<StructData>;
60 #[salsa::invoke(StructData::union_data_query)]
61 fn union_data(&self, id: UnionId) -> Arc<StructData>;
62
63 #[salsa::invoke(EnumData::enum_data_query)]
64 fn enum_data(&self, e: EnumId) -> Arc<EnumData>;
65
66 #[salsa::invoke(ImplData::impl_data_query)]
67 fn impl_data(&self, e: ImplId) -> Arc<ImplData>;
68
69 #[salsa::invoke(TraitData::trait_data_query)]
70 fn trait_data(&self, e: TraitId) -> Arc<TraitData>;
71
72 #[salsa::invoke(TypeAliasData::type_alias_data_query)]
73 fn type_alias_data(&self, e: TypeAliasId) -> Arc<TypeAliasData>;
74
75 #[salsa::invoke(FunctionData::fn_data_query)]
76 fn function_data(&self, func: FunctionId) -> Arc<FunctionData>;
77
78 #[salsa::invoke(ConstData::const_data_query)]
79 fn const_data(&self, konst: ConstId) -> Arc<ConstData>;
80
81 #[salsa::invoke(StaticData::static_data_query)]
82 fn static_data(&self, konst: StaticId) -> Arc<StaticData>;
83
84 #[salsa::invoke(Body::body_with_source_map_query)]
85 fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>);
86
87 #[salsa::invoke(Body::body_query)]
88 fn body(&self, def: DefWithBodyId) -> Arc<Body>;
89
90 #[salsa::invoke(ExprScopes::expr_scopes_query)]
91 fn expr_scopes(&self, def: DefWithBodyId) -> Arc<ExprScopes>;
92
93 #[salsa::invoke(GenericParams::generic_params_query)]
94 fn generic_params(&self, def: GenericDefId) -> Arc<GenericParams>;
95
96 #[salsa::invoke(Attrs::attrs_query)]
97 fn attrs(&self, def: AttrDefId) -> Attrs;
98
99 #[salsa::invoke(LangItems::module_lang_items_query)]
100 fn module_lang_items(&self, module: ModuleId) -> Option<Arc<LangItems>>;
101
102 #[salsa::invoke(LangItems::crate_lang_items_query)]
103 fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>;
104
105 #[salsa::invoke(LangItems::lang_item_query)]
106 fn lang_item(&self, start_crate: CrateId, item: SmolStr) -> Option<LangItemTarget>;
107
108 // FIXME(https://github.com/rust-analyzer/rust-analyzer/issues/2148#issuecomment-550519102)
109 // Remove this query completely, in favor of `Attrs::docs` method
110 #[salsa::invoke(Documentation::documentation_query)]
111 fn documentation(&self, def: AttrDefId) -> Option<Documentation>;
112
113 #[salsa::invoke(ImportMap::import_map_query)]
114 fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
115}
116
117fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
118 let _p = profile::span("crate_def_map:wait");
119 db.crate_def_map_query(krate)
120}
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
new file mode 100644
index 000000000..3e19d9117
--- /dev/null
+++ b/crates/hir_def/src/diagnostics.rs
@@ -0,0 +1,30 @@
1//! Diagnostics produced by `hir_def`.
2
3use std::any::Any;
4
5use hir_expand::diagnostics::{Diagnostic, DiagnosticCode};
6use syntax::{ast, AstPtr, SyntaxNodePtr};
7
8use hir_expand::{HirFileId, InFile};
9
10#[derive(Debug)]
11pub struct UnresolvedModule {
12 pub file: HirFileId,
13 pub decl: AstPtr<ast::Module>,
14 pub candidate: String,
15}
16
17impl Diagnostic for UnresolvedModule {
18 fn code(&self) -> DiagnosticCode {
19 DiagnosticCode("unresolved-module")
20 }
21 fn message(&self) -> String {
22 "unresolved module".to_string()
23 }
24 fn display_source(&self) -> InFile<SyntaxNodePtr> {
25 InFile::new(self.file, self.decl.clone().into())
26 }
27 fn as_any(&self) -> &(dyn Any + Send + 'static) {
28 self
29 }
30}
diff --git a/crates/hir_def/src/docs.rs b/crates/hir_def/src/docs.rs
new file mode 100644
index 000000000..e9a02b11b
--- /dev/null
+++ b/crates/hir_def/src/docs.rs
@@ -0,0 +1,121 @@
1//! Defines hir documentation.
2//!
3//! This really shouldn't exist, instead, we should deshugar doc comments into attributes, see
4//! https://github.com/rust-analyzer/rust-analyzer/issues/2148#issuecomment-550519102
5
6use std::sync::Arc;
7
8use either::Either;
9use syntax::ast;
10
11use crate::{
12 db::DefDatabase,
13 src::{HasChildSource, HasSource},
14 AdtId, AttrDefId, Lookup,
15};
16
17/// Holds documentation
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Documentation(Arc<str>);
20
21impl Into<String> for Documentation {
22 fn into(self) -> String {
23 self.as_str().to_owned()
24 }
25}
26
27impl Documentation {
28 fn new(s: &str) -> Documentation {
29 Documentation(s.into())
30 }
31
32 pub fn from_ast<N>(node: &N) -> Option<Documentation>
33 where
34 N: ast::DocCommentsOwner + ast::AttrsOwner,
35 {
36 docs_from_ast(node)
37 }
38
39 pub fn as_str(&self) -> &str {
40 &*self.0
41 }
42
43 pub(crate) fn documentation_query(
44 db: &dyn DefDatabase,
45 def: AttrDefId,
46 ) -> Option<Documentation> {
47 match def {
48 AttrDefId::ModuleId(module) => {
49 let def_map = db.crate_def_map(module.krate);
50 let src = def_map[module.local_id].declaration_source(db)?;
51 docs_from_ast(&src.value)
52 }
53 AttrDefId::FieldId(it) => {
54 let src = it.parent.child_source(db);
55 match &src.value[it.local_id] {
56 Either::Left(_tuple) => None,
57 Either::Right(record) => docs_from_ast(record),
58 }
59 }
60 AttrDefId::AdtId(it) => match it {
61 AdtId::StructId(it) => docs_from_ast(&it.lookup(db).source(db).value),
62 AdtId::EnumId(it) => docs_from_ast(&it.lookup(db).source(db).value),
63 AdtId::UnionId(it) => docs_from_ast(&it.lookup(db).source(db).value),
64 },
65 AttrDefId::EnumVariantId(it) => {
66 let src = it.parent.child_source(db);
67 docs_from_ast(&src.value[it.local_id])
68 }
69 AttrDefId::TraitId(it) => docs_from_ast(&it.lookup(db).source(db).value),
70 AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id?.to_node(db.upcast())),
71 AttrDefId::ConstId(it) => docs_from_ast(&it.lookup(db).source(db).value),
72 AttrDefId::StaticId(it) => docs_from_ast(&it.lookup(db).source(db).value),
73 AttrDefId::FunctionId(it) => docs_from_ast(&it.lookup(db).source(db).value),
74 AttrDefId::TypeAliasId(it) => docs_from_ast(&it.lookup(db).source(db).value),
75 AttrDefId::ImplId(_) => None,
76 }
77 }
78}
79
80pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation>
81where
82 N: ast::DocCommentsOwner + ast::AttrsOwner,
83{
84 let doc_comment_text = node.doc_comment_text();
85 let doc_attr_text = expand_doc_attrs(node);
86 let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
87 docs.map(|it| Documentation::new(&it))
88}
89
90fn merge_doc_comments_and_attrs(
91 doc_comment_text: Option<String>,
92 doc_attr_text: Option<String>,
93) -> Option<String> {
94 match (doc_comment_text, doc_attr_text) {
95 (Some(mut comment_text), Some(attr_text)) => {
96 comment_text.push_str("\n\n");
97 comment_text.push_str(&attr_text);
98 Some(comment_text)
99 }
100 (Some(comment_text), None) => Some(comment_text),
101 (None, Some(attr_text)) => Some(attr_text),
102 (None, None) => None,
103 }
104}
105
106fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> {
107 let mut docs = String::new();
108 for attr in owner.attrs() {
109 if let Some(("doc", value)) =
110 attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str()))
111 {
112 docs.push_str(value);
113 docs.push_str("\n\n");
114 }
115 }
116 if docs.is_empty() {
117 None
118 } else {
119 Some(docs.trim_end_matches("\n\n").to_owned())
120 }
121}
diff --git a/crates/hir_def/src/dyn_map.rs b/crates/hir_def/src/dyn_map.rs
new file mode 100644
index 000000000..6f269d7b0
--- /dev/null
+++ b/crates/hir_def/src/dyn_map.rs
@@ -0,0 +1,108 @@
1//! This module defines a `DynMap` -- a container for heterogeneous maps.
2//!
3//! This means that `DynMap` stores a bunch of hash maps inside, and those maps
4//! can be of different types.
5//!
6//! It is used like this:
7//!
8//! ```
9//! // keys define submaps of a `DynMap`
10//! const STRING_TO_U32: Key<String, u32> = Key::new();
11//! const U32_TO_VEC: Key<u32, Vec<bool>> = Key::new();
12//!
13//! // Note: concrete type, no type params!
14//! let mut map = DynMap::new();
15//!
16//! // To access a specific map, index the `DynMap` by `Key`:
17//! map[STRING_TO_U32].insert("hello".to_string(), 92);
18//! let value = map[U32_TO_VEC].get(92);
19//! assert!(value.is_none());
20//! ```
21//!
22//! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
23//! a coincidence.
24use std::{
25 hash::Hash,
26 marker::PhantomData,
27 ops::{Index, IndexMut},
28};
29
30use anymap::Map;
31use rustc_hash::FxHashMap;
32
33pub struct Key<K, V, P = (K, V)> {
34 _phantom: PhantomData<(K, V, P)>,
35}
36
37impl<K, V, P> Key<K, V, P> {
38 pub(crate) const fn new() -> Key<K, V, P> {
39 Key { _phantom: PhantomData }
40 }
41}
42
43impl<K, V, P> Copy for Key<K, V, P> {}
44
45impl<K, V, P> Clone for Key<K, V, P> {
46 fn clone(&self) -> Key<K, V, P> {
47 *self
48 }
49}
50
51pub trait Policy {
52 type K;
53 type V;
54
55 fn insert(map: &mut DynMap, key: Self::K, value: Self::V);
56 fn get<'a>(map: &'a DynMap, key: &Self::K) -> Option<&'a Self::V>;
57}
58
59impl<K: Hash + Eq + 'static, V: 'static> Policy for (K, V) {
60 type K = K;
61 type V = V;
62 fn insert(map: &mut DynMap, key: K, value: V) {
63 map.map.entry::<FxHashMap<K, V>>().or_insert_with(Default::default).insert(key, value);
64 }
65 fn get<'a>(map: &'a DynMap, key: &K) -> Option<&'a V> {
66 map.map.get::<FxHashMap<K, V>>()?.get(key)
67 }
68}
69
70pub struct DynMap {
71 pub(crate) map: Map,
72}
73
74impl Default for DynMap {
75 fn default() -> Self {
76 DynMap { map: Map::new() }
77 }
78}
79
80#[repr(transparent)]
81pub struct KeyMap<KEY> {
82 map: DynMap,
83 _phantom: PhantomData<KEY>,
84}
85
86impl<P: Policy> KeyMap<Key<P::K, P::V, P>> {
87 pub fn insert(&mut self, key: P::K, value: P::V) {
88 P::insert(&mut self.map, key, value)
89 }
90 pub fn get(&self, key: &P::K) -> Option<&P::V> {
91 P::get(&self.map, key)
92 }
93}
94
95impl<P: Policy> Index<Key<P::K, P::V, P>> for DynMap {
96 type Output = KeyMap<Key<P::K, P::V, P>>;
97 fn index(&self, _key: Key<P::K, P::V, P>) -> &Self::Output {
98 // Safe due to `#[repr(transparent)]`.
99 unsafe { std::mem::transmute::<&DynMap, &KeyMap<Key<P::K, P::V, P>>>(self) }
100 }
101}
102
103impl<P: Policy> IndexMut<Key<P::K, P::V, P>> for DynMap {
104 fn index_mut(&mut self, _key: Key<P::K, P::V, P>) -> &mut Self::Output {
105 // Safe due to `#[repr(transparent)]`.
106 unsafe { std::mem::transmute::<&mut DynMap, &mut KeyMap<Key<P::K, P::V, P>>>(self) }
107 }
108}
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
new file mode 100644
index 000000000..c94b3a36f
--- /dev/null
+++ b/crates/hir_def/src/expr.rs
@@ -0,0 +1,420 @@
1//! This module describes hir-level representation of expressions.
2//!
3//! This representaion is:
4//!
5//! 1. Identity-based. Each expression has an `id`, so we can distinguish
6//! between different `1` in `1 + 1`.
7//! 2. Independent of syntax. Though syntactic provenance information can be
8//! attached separately via id-based side map.
9//! 3. Unresolved. Paths are stored as sequences of names, and not as defs the
10//! names refer to.
11//! 4. Desugared. There's no `if let`.
12//!
13//! See also a neighboring `body` module.
14
15use arena::{Idx, RawId};
16use hir_expand::name::Name;
17use syntax::ast::RangeOp;
18
19use crate::{
20 builtin_type::{BuiltinFloat, BuiltinInt},
21 path::{GenericArgs, Path},
22 type_ref::{Mutability, Rawness, TypeRef},
23};
24
25pub type ExprId = Idx<Expr>;
26pub(crate) fn dummy_expr_id() -> ExprId {
27 ExprId::from_raw(RawId::from(!0))
28}
29
30pub type PatId = Idx<Pat>;
31
32#[derive(Debug, Clone, Eq, PartialEq)]
33pub enum Literal {
34 String(String),
35 ByteString(Vec<u8>),
36 Char(char),
37 Bool(bool),
38 Int(u64, Option<BuiltinInt>),
39 Float(u64, Option<BuiltinFloat>), // FIXME: f64 is not Eq
40}
41
42#[derive(Debug, Clone, Eq, PartialEq)]
43pub enum Expr {
44 /// This is produced if the syntax tree does not have a required expression piece.
45 Missing,
46 Path(Path),
47 If {
48 condition: ExprId,
49 then_branch: ExprId,
50 else_branch: Option<ExprId>,
51 },
52 Block {
53 statements: Vec<Statement>,
54 tail: Option<ExprId>,
55 label: Option<Name>,
56 },
57 Loop {
58 body: ExprId,
59 label: Option<Name>,
60 },
61 While {
62 condition: ExprId,
63 body: ExprId,
64 label: Option<Name>,
65 },
66 For {
67 iterable: ExprId,
68 pat: PatId,
69 body: ExprId,
70 label: Option<Name>,
71 },
72 Call {
73 callee: ExprId,
74 args: Vec<ExprId>,
75 },
76 MethodCall {
77 receiver: ExprId,
78 method_name: Name,
79 args: Vec<ExprId>,
80 generic_args: Option<GenericArgs>,
81 },
82 Match {
83 expr: ExprId,
84 arms: Vec<MatchArm>,
85 },
86 Continue {
87 label: Option<Name>,
88 },
89 Break {
90 expr: Option<ExprId>,
91 label: Option<Name>,
92 },
93 Return {
94 expr: Option<ExprId>,
95 },
96 RecordLit {
97 path: Option<Path>,
98 fields: Vec<RecordLitField>,
99 spread: Option<ExprId>,
100 },
101 Field {
102 expr: ExprId,
103 name: Name,
104 },
105 Await {
106 expr: ExprId,
107 },
108 Try {
109 expr: ExprId,
110 },
111 TryBlock {
112 body: ExprId,
113 },
114 Cast {
115 expr: ExprId,
116 type_ref: TypeRef,
117 },
118 Ref {
119 expr: ExprId,
120 rawness: Rawness,
121 mutability: Mutability,
122 },
123 Box {
124 expr: ExprId,
125 },
126 UnaryOp {
127 expr: ExprId,
128 op: UnaryOp,
129 },
130 BinaryOp {
131 lhs: ExprId,
132 rhs: ExprId,
133 op: Option<BinaryOp>,
134 },
135 Range {
136 lhs: Option<ExprId>,
137 rhs: Option<ExprId>,
138 range_type: RangeOp,
139 },
140 Index {
141 base: ExprId,
142 index: ExprId,
143 },
144 Lambda {
145 args: Vec<PatId>,
146 arg_types: Vec<Option<TypeRef>>,
147 ret_type: Option<TypeRef>,
148 body: ExprId,
149 },
150 Tuple {
151 exprs: Vec<ExprId>,
152 },
153 Unsafe {
154 body: ExprId,
155 },
156 Array(Array),
157 Literal(Literal),
158}
159
160#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
161pub enum BinaryOp {
162 LogicOp(LogicOp),
163 ArithOp(ArithOp),
164 CmpOp(CmpOp),
165 Assignment { op: Option<ArithOp> },
166}
167
168#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
169pub enum LogicOp {
170 And,
171 Or,
172}
173
174#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
175pub enum CmpOp {
176 Eq { negated: bool },
177 Ord { ordering: Ordering, strict: bool },
178}
179
180#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
181pub enum Ordering {
182 Less,
183 Greater,
184}
185
186#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
187pub enum ArithOp {
188 Add,
189 Mul,
190 Sub,
191 Div,
192 Rem,
193 Shl,
194 Shr,
195 BitXor,
196 BitOr,
197 BitAnd,
198}
199
200pub use syntax::ast::PrefixOp as UnaryOp;
201#[derive(Debug, Clone, Eq, PartialEq)]
202pub enum Array {
203 ElementList(Vec<ExprId>),
204 Repeat { initializer: ExprId, repeat: ExprId },
205}
206
207#[derive(Debug, Clone, Eq, PartialEq)]
208pub struct MatchArm {
209 pub pat: PatId,
210 pub guard: Option<ExprId>,
211 pub expr: ExprId,
212}
213
214#[derive(Debug, Clone, Eq, PartialEq)]
215pub struct RecordLitField {
216 pub name: Name,
217 pub expr: ExprId,
218}
219
220#[derive(Debug, Clone, Eq, PartialEq)]
221pub enum Statement {
222 Let { pat: PatId, type_ref: Option<TypeRef>, initializer: Option<ExprId> },
223 Expr(ExprId),
224}
225
226impl Expr {
227 pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
228 match self {
229 Expr::Missing => {}
230 Expr::Path(_) => {}
231 Expr::If { condition, then_branch, else_branch } => {
232 f(*condition);
233 f(*then_branch);
234 if let Some(else_branch) = else_branch {
235 f(*else_branch);
236 }
237 }
238 Expr::Block { statements, tail, .. } => {
239 for stmt in statements {
240 match stmt {
241 Statement::Let { initializer, .. } => {
242 if let Some(expr) = initializer {
243 f(*expr);
244 }
245 }
246 Statement::Expr(e) => f(*e),
247 }
248 }
249 if let Some(expr) = tail {
250 f(*expr);
251 }
252 }
253 Expr::TryBlock { body } | Expr::Unsafe { body } => f(*body),
254 Expr::Loop { body, .. } => f(*body),
255 Expr::While { condition, body, .. } => {
256 f(*condition);
257 f(*body);
258 }
259 Expr::For { iterable, body, .. } => {
260 f(*iterable);
261 f(*body);
262 }
263 Expr::Call { callee, args } => {
264 f(*callee);
265 for arg in args {
266 f(*arg);
267 }
268 }
269 Expr::MethodCall { receiver, args, .. } => {
270 f(*receiver);
271 for arg in args {
272 f(*arg);
273 }
274 }
275 Expr::Match { expr, arms } => {
276 f(*expr);
277 for arm in arms {
278 f(arm.expr);
279 }
280 }
281 Expr::Continue { .. } => {}
282 Expr::Break { expr, .. } | Expr::Return { expr } => {
283 if let Some(expr) = expr {
284 f(*expr);
285 }
286 }
287 Expr::RecordLit { fields, spread, .. } => {
288 for field in fields {
289 f(field.expr);
290 }
291 if let Some(expr) = spread {
292 f(*expr);
293 }
294 }
295 Expr::Lambda { body, .. } => {
296 f(*body);
297 }
298 Expr::BinaryOp { lhs, rhs, .. } => {
299 f(*lhs);
300 f(*rhs);
301 }
302 Expr::Range { lhs, rhs, .. } => {
303 if let Some(lhs) = rhs {
304 f(*lhs);
305 }
306 if let Some(rhs) = lhs {
307 f(*rhs);
308 }
309 }
310 Expr::Index { base, index } => {
311 f(*base);
312 f(*index);
313 }
314 Expr::Field { expr, .. }
315 | Expr::Await { expr }
316 | Expr::Try { expr }
317 | Expr::Cast { expr, .. }
318 | Expr::Ref { expr, .. }
319 | Expr::UnaryOp { expr, .. }
320 | Expr::Box { expr } => {
321 f(*expr);
322 }
323 Expr::Tuple { exprs } => {
324 for expr in exprs {
325 f(*expr);
326 }
327 }
328 Expr::Array(a) => match a {
329 Array::ElementList(exprs) => {
330 for expr in exprs {
331 f(*expr);
332 }
333 }
334 Array::Repeat { initializer, repeat } => {
335 f(*initializer);
336 f(*repeat)
337 }
338 },
339 Expr::Literal(_) => {}
340 }
341 }
342}
343
344/// Explicit binding annotations given in the HIR for a binding. Note
345/// that this is not the final binding *mode* that we infer after type
346/// inference.
347#[derive(Clone, PartialEq, Eq, Debug, Copy)]
348pub enum BindingAnnotation {
349 /// No binding annotation given: this means that the final binding mode
350 /// will depend on whether we have skipped through a `&` reference
351 /// when matching. For example, the `x` in `Some(x)` will have binding
352 /// mode `None`; if you do `let Some(x) = &Some(22)`, it will
353 /// ultimately be inferred to be by-reference.
354 Unannotated,
355
356 /// Annotated with `mut x` -- could be either ref or not, similar to `None`.
357 Mutable,
358
359 /// Annotated as `ref`, like `ref x`
360 Ref,
361
362 /// Annotated as `ref mut x`.
363 RefMut,
364}
365
366impl BindingAnnotation {
367 pub fn new(is_mutable: bool, is_ref: bool) -> Self {
368 match (is_mutable, is_ref) {
369 (true, true) => BindingAnnotation::RefMut,
370 (false, true) => BindingAnnotation::Ref,
371 (true, false) => BindingAnnotation::Mutable,
372 (false, false) => BindingAnnotation::Unannotated,
373 }
374 }
375}
376
377#[derive(Debug, Clone, Eq, PartialEq)]
378pub struct RecordFieldPat {
379 pub name: Name,
380 pub pat: PatId,
381}
382
383/// Close relative to rustc's hir::PatKind
384#[derive(Debug, Clone, Eq, PartialEq)]
385pub enum Pat {
386 Missing,
387 Wild,
388 Tuple { args: Vec<PatId>, ellipsis: Option<usize> },
389 Or(Vec<PatId>),
390 Record { path: Option<Path>, args: Vec<RecordFieldPat>, ellipsis: bool },
391 Range { start: ExprId, end: ExprId },
392 Slice { prefix: Vec<PatId>, slice: Option<PatId>, suffix: Vec<PatId> },
393 Path(Path),
394 Lit(ExprId),
395 Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> },
396 TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> },
397 Ref { pat: PatId, mutability: Mutability },
398}
399
400impl Pat {
401 pub fn walk_child_pats(&self, mut f: impl FnMut(PatId)) {
402 match self {
403 Pat::Range { .. } | Pat::Lit(..) | Pat::Path(..) | Pat::Wild | Pat::Missing => {}
404 Pat::Bind { subpat, .. } => {
405 subpat.iter().copied().for_each(f);
406 }
407 Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
408 args.iter().copied().for_each(f);
409 }
410 Pat::Ref { pat, .. } => f(*pat),
411 Pat::Slice { prefix, slice, suffix } => {
412 let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
413 total_iter.copied().for_each(f);
414 }
415 Pat::Record { args, .. } => {
416 args.iter().map(|f| f.pat).for_each(f);
417 }
418 }
419 }
420}
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
new file mode 100644
index 000000000..ac2c54ac5
--- /dev/null
+++ b/crates/hir_def/src/find_path.rs
@@ -0,0 +1,687 @@
1//! An algorithm to find a path to refer to a certain item.
2
3use hir_expand::name::{known, AsName, Name};
4use rustc_hash::FxHashSet;
5use test_utils::mark;
6
7use crate::{
8 db::DefDatabase,
9 item_scope::ItemInNs,
10 path::{ModPath, PathKind},
11 visibility::Visibility,
12 ModuleDefId, ModuleId,
13};
14
15// FIXME: handle local items
16
17/// Find a path that can be used to refer to a certain item. This can depend on
18/// *from where* you're referring to the item, hence the `from` parameter.
19pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
20 let _p = profile::span("find_path");
21 find_path_inner(db, item, from, MAX_PATH_LEN)
22}
23
24const MAX_PATH_LEN: usize = 15;
25
26impl ModPath {
27 fn starts_with_std(&self) -> bool {
28 self.segments.first() == Some(&known::std)
29 }
30
31 // When std library is present, paths starting with `std::`
32 // should be preferred over paths starting with `core::` and `alloc::`
33 fn can_start_with_std(&self) -> bool {
34 let first_segment = self.segments.first();
35 first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
36 }
37}
38
39fn find_path_inner(
40 db: &dyn DefDatabase,
41 item: ItemInNs,
42 from: ModuleId,
43 max_len: usize,
44) -> Option<ModPath> {
45 if max_len == 0 {
46 return None;
47 }
48
49 // Base cases:
50
51 // - if the item is already in scope, return the name under which it is
52 let def_map = db.crate_def_map(from.krate);
53 let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope;
54 if let Some((name, _)) = from_scope.name_of(item) {
55 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()]));
56 }
57
58 // - if the item is the crate root, return `crate`
59 if item
60 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
61 krate: from.krate,
62 local_id: def_map.root,
63 }))
64 {
65 return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
66 }
67
68 // - if the item is the module we're in, use `self`
69 if item == ItemInNs::Types(from.into()) {
70 return Some(ModPath::from_segments(PathKind::Super(0), Vec::new()));
71 }
72
73 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
74 if let Some(parent_id) = def_map.modules[from.local_id].parent {
75 if item
76 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
77 krate: from.krate,
78 local_id: parent_id,
79 }))
80 {
81 return Some(ModPath::from_segments(PathKind::Super(1), Vec::new()));
82 }
83 }
84
85 // - if the item is the crate root of a dependency crate, return the name from the extern prelude
86 for (name, def_id) in &def_map.extern_prelude {
87 if item == ItemInNs::Types(*def_id) {
88 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()]));
89 }
90 }
91
92 // - if the item is in the prelude, return the name from there
93 if let Some(prelude_module) = def_map.prelude {
94 let prelude_def_map = db.crate_def_map(prelude_module.krate);
95 let prelude_scope: &crate::item_scope::ItemScope =
96 &prelude_def_map.modules[prelude_module.local_id].scope;
97 if let Some((name, vis)) = prelude_scope.name_of(item) {
98 if vis.is_visible_from(db, from) {
99 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()]));
100 }
101 }
102 }
103
104 // - if the item is a builtin, it's in scope
105 if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item {
106 return Some(ModPath::from_segments(PathKind::Plain, vec![builtin.as_name()]));
107 }
108
109 // Recursive case:
110 // - if the item is an enum variant, refer to it via the enum
111 if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
112 if let Some(mut path) = find_path(db, ItemInNs::Types(variant.parent.into()), from) {
113 let data = db.enum_data(variant.parent);
114 path.segments.push(data.variants[variant.local_id].name.clone());
115 return Some(path);
116 }
117 // If this doesn't work, it seems we have no way of referring to the
118 // enum; that's very weird, but there might still be a reexport of the
119 // variant somewhere
120 }
121
122 // - otherwise, look for modules containing (reexporting) it and import it from one of those
123
124 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate };
125 let crate_attrs = db.attrs(crate_root.into());
126 let prefer_no_std = crate_attrs.by_key("no_std").exists();
127 let mut best_path = None;
128 let mut best_path_len = max_len;
129
130 if item.krate(db) == Some(from.krate) {
131 // Item was defined in the same crate that wants to import it. It cannot be found in any
132 // dependency in this case.
133
134 let local_imports = find_local_import_locations(db, item, from);
135 for (module_id, name) in local_imports {
136 if let Some(mut path) = find_path_inner(
137 db,
138 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
139 from,
140 best_path_len - 1,
141 ) {
142 path.segments.push(name);
143
144 let new_path = if let Some(best_path) = best_path {
145 select_best_path(best_path, path, prefer_no_std)
146 } else {
147 path
148 };
149 best_path_len = new_path.len();
150 best_path = Some(new_path);
151 }
152 }
153 } else {
154 // Item was defined in some upstream crate. This means that it must be exported from one,
155 // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
156 // that wants to import it here, but we always prefer to use the external path here.
157
158 let crate_graph = db.crate_graph();
159 let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
160 let import_map = db.import_map(dep.crate_id);
161 import_map.import_info_for(item).and_then(|info| {
162 // Determine best path for containing module and append last segment from `info`.
163 let mut path = find_path_inner(
164 db,
165 ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
166 from,
167 best_path_len - 1,
168 )?;
169 path.segments.push(info.path.segments.last().unwrap().clone());
170 Some(path)
171 })
172 });
173
174 for path in extern_paths {
175 let new_path = if let Some(best_path) = best_path {
176 select_best_path(best_path, path, prefer_no_std)
177 } else {
178 path
179 };
180 best_path = Some(new_path);
181 }
182 }
183
184 best_path
185}
186
187fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
188 if old_path.starts_with_std() && new_path.can_start_with_std() {
189 if prefer_no_std {
190 mark::hit!(prefer_no_std_paths);
191 new_path
192 } else {
193 mark::hit!(prefer_std_paths);
194 old_path
195 }
196 } else if new_path.starts_with_std() && old_path.can_start_with_std() {
197 if prefer_no_std {
198 mark::hit!(prefer_no_std_paths);
199 old_path
200 } else {
201 mark::hit!(prefer_std_paths);
202 new_path
203 }
204 } else if new_path.len() < old_path.len() {
205 new_path
206 } else {
207 old_path
208 }
209}
210
211/// Finds locations in `from.krate` from which `item` can be imported by `from`.
212fn find_local_import_locations(
213 db: &dyn DefDatabase,
214 item: ItemInNs,
215 from: ModuleId,
216) -> Vec<(ModuleId, Name)> {
217 let _p = profile::span("find_local_import_locations");
218
219 // `from` can import anything below `from` with visibility of at least `from`, and anything
220 // above `from` with any visibility. That means we do not need to descend into private siblings
221 // of `from` (and similar).
222
223 let def_map = db.crate_def_map(from.krate);
224
225 // Compute the initial worklist. We start with all direct child modules of `from` as well as all
226 // of its (recursive) parent modules.
227 let data = &def_map.modules[from.local_id];
228 let mut worklist = data
229 .children
230 .values()
231 .map(|child| ModuleId { krate: from.krate, local_id: *child })
232 .collect::<Vec<_>>();
233 let mut parent = data.parent;
234 while let Some(p) = parent {
235 worklist.push(ModuleId { krate: from.krate, local_id: p });
236 parent = def_map.modules[p].parent;
237 }
238
239 let mut seen: FxHashSet<_> = FxHashSet::default();
240
241 let mut locations = Vec::new();
242 while let Some(module) = worklist.pop() {
243 if !seen.insert(module) {
244 continue; // already processed this module
245 }
246
247 let ext_def_map;
248 let data = if module.krate == from.krate {
249 &def_map[module.local_id]
250 } else {
251 // The crate might reexport a module defined in another crate.
252 ext_def_map = db.crate_def_map(module.krate);
253 &ext_def_map[module.local_id]
254 };
255
256 if let Some((name, vis)) = data.scope.name_of(item) {
257 if vis.is_visible_from(db, from) {
258 let is_private = if let Visibility::Module(private_to) = vis {
259 private_to.local_id == module.local_id
260 } else {
261 false
262 };
263 let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
264 data.scope.declarations().any(|it| it == module_def_id)
265 } else {
266 false
267 };
268
269 // Ignore private imports. these could be used if we are
270 // in a submodule of this module, but that's usually not
271 // what the user wants; and if this module can import
272 // the item and we're a submodule of it, so can we.
273 // Also this keeps the cached data smaller.
274 if !is_private || is_original_def {
275 locations.push((module, name.clone()));
276 }
277 }
278 }
279
280 // Descend into all modules visible from `from`.
281 for (_, per_ns) in data.scope.entries() {
282 if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() {
283 if vis.is_visible_from(db, from) {
284 worklist.push(module);
285 }
286 }
287 }
288 }
289
290 locations
291}
292
293#[cfg(test)]
294mod tests {
295 use base_db::fixture::WithFixture;
296 use hir_expand::hygiene::Hygiene;
297 use syntax::ast::AstNode;
298 use test_utils::mark;
299
300 use crate::test_db::TestDB;
301
302 use super::*;
303
304 /// `code` needs to contain a cursor marker; checks that `find_path` for the
305 /// item the `path` refers to returns that same path when called from the
306 /// module the cursor is in.
307 fn check_found_path(ra_fixture: &str, path: &str) {
308 let (db, pos) = TestDB::with_position(ra_fixture);
309 let module = db.module_for_file(pos.file_id);
310 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
311 let ast_path =
312 parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
313 let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();
314
315 let crate_def_map = db.crate_def_map(module.krate);
316 let resolved = crate_def_map
317 .resolve_path(
318 &db,
319 module.local_id,
320 &mod_path,
321 crate::item_scope::BuiltinShadowMode::Module,
322 )
323 .0
324 .take_types()
325 .unwrap();
326
327 let found_path = find_path(&db, ItemInNs::Types(resolved), module);
328
329 assert_eq!(found_path, Some(mod_path));
330 }
331
332 #[test]
333 fn same_module() {
334 let code = r#"
335 //- /main.rs
336 struct S;
337 <|>
338 "#;
339 check_found_path(code, "S");
340 }
341
342 #[test]
343 fn enum_variant() {
344 let code = r#"
345 //- /main.rs
346 enum E { A }
347 <|>
348 "#;
349 check_found_path(code, "E::A");
350 }
351
352 #[test]
353 fn sub_module() {
354 let code = r#"
355 //- /main.rs
356 mod foo {
357 pub struct S;
358 }
359 <|>
360 "#;
361 check_found_path(code, "foo::S");
362 }
363
364 #[test]
365 fn super_module() {
366 let code = r#"
367 //- /main.rs
368 mod foo;
369 //- /foo.rs
370 mod bar;
371 struct S;
372 //- /foo/bar.rs
373 <|>
374 "#;
375 check_found_path(code, "super::S");
376 }
377
378 #[test]
379 fn self_module() {
380 let code = r#"
381 //- /main.rs
382 mod foo;
383 //- /foo.rs
384 <|>
385 "#;
386 check_found_path(code, "self");
387 }
388
389 #[test]
390 fn crate_root() {
391 let code = r#"
392 //- /main.rs
393 mod foo;
394 //- /foo.rs
395 <|>
396 "#;
397 check_found_path(code, "crate");
398 }
399
400 #[test]
401 fn same_crate() {
402 let code = r#"
403 //- /main.rs
404 mod foo;
405 struct S;
406 //- /foo.rs
407 <|>
408 "#;
409 check_found_path(code, "crate::S");
410 }
411
412 #[test]
413 fn different_crate() {
414 let code = r#"
415 //- /main.rs crate:main deps:std
416 <|>
417 //- /std.rs crate:std
418 pub struct S;
419 "#;
420 check_found_path(code, "std::S");
421 }
422
423 #[test]
424 fn different_crate_renamed() {
425 let code = r#"
426 //- /main.rs crate:main deps:std
427 extern crate std as std_renamed;
428 <|>
429 //- /std.rs crate:std
430 pub struct S;
431 "#;
432 check_found_path(code, "std_renamed::S");
433 }
434
435 #[test]
436 fn partially_imported() {
437 // Tests that short paths are used even for external items, when parts of the path are
438 // already in scope.
439 check_found_path(
440 r#"
441 //- /main.rs crate:main deps:syntax
442
443 use syntax::ast;
444 <|>
445
446 //- /lib.rs crate:syntax
447 pub mod ast {
448 pub enum ModuleItem {
449 A, B, C,
450 }
451 }
452 "#,
453 "ast::ModuleItem",
454 );
455
456 check_found_path(
457 r#"
458 //- /main.rs crate:main deps:syntax
459
460 <|>
461
462 //- /lib.rs crate:syntax
463 pub mod ast {
464 pub enum ModuleItem {
465 A, B, C,
466 }
467 }
468 "#,
469 "syntax::ast::ModuleItem",
470 );
471 }
472
473 #[test]
474 fn same_crate_reexport() {
475 let code = r#"
476 //- /main.rs
477 mod bar {
478 mod foo { pub(super) struct S; }
479 pub(crate) use foo::*;
480 }
481 <|>
482 "#;
483 check_found_path(code, "bar::S");
484 }
485
486 #[test]
487 fn same_crate_reexport_rename() {
488 let code = r#"
489 //- /main.rs
490 mod bar {
491 mod foo { pub(super) struct S; }
492 pub(crate) use foo::S as U;
493 }
494 <|>
495 "#;
496 check_found_path(code, "bar::U");
497 }
498
499 #[test]
500 fn different_crate_reexport() {
501 let code = r#"
502 //- /main.rs crate:main deps:std
503 <|>
504 //- /std.rs crate:std deps:core
505 pub use core::S;
506 //- /core.rs crate:core
507 pub struct S;
508 "#;
509 check_found_path(code, "std::S");
510 }
511
512 #[test]
513 fn prelude() {
514 let code = r#"
515 //- /main.rs crate:main deps:std
516 <|>
517 //- /std.rs crate:std
518 pub mod prelude { pub struct S; }
519 #[prelude_import]
520 pub use prelude::*;
521 "#;
522 check_found_path(code, "S");
523 }
524
525 #[test]
526 fn enum_variant_from_prelude() {
527 let code = r#"
528 //- /main.rs crate:main deps:std
529 <|>
530 //- /std.rs crate:std
531 pub mod prelude {
532 pub enum Option<T> { Some(T), None }
533 pub use Option::*;
534 }
535 #[prelude_import]
536 pub use prelude::*;
537 "#;
538 check_found_path(code, "None");
539 check_found_path(code, "Some");
540 }
541
542 #[test]
543 fn shortest_path() {
544 let code = r#"
545 //- /main.rs
546 pub mod foo;
547 pub mod baz;
548 struct S;
549 <|>
550 //- /foo.rs
551 pub mod bar { pub struct S; }
552 //- /baz.rs
553 pub use crate::foo::bar::S;
554 "#;
555 check_found_path(code, "baz::S");
556 }
557
558 #[test]
559 fn discount_private_imports() {
560 let code = r#"
561 //- /main.rs
562 mod foo;
563 pub mod bar { pub struct S; }
564 use bar::S;
565 //- /foo.rs
566 <|>
567 "#;
568 // crate::S would be shorter, but using private imports seems wrong
569 check_found_path(code, "crate::bar::S");
570 }
571
572 #[test]
573 fn import_cycle() {
574 let code = r#"
575 //- /main.rs
576 pub mod foo;
577 pub mod bar;
578 pub mod baz;
579 //- /bar.rs
580 <|>
581 //- /foo.rs
582 pub use super::baz;
583 pub struct S;
584 //- /baz.rs
585 pub use super::foo;
586 "#;
587 check_found_path(code, "crate::foo::S");
588 }
589
590 #[test]
591 fn prefer_std_paths_over_alloc() {
592 mark::check!(prefer_std_paths);
593 let code = r#"
594 //- /main.rs crate:main deps:alloc,std
595 <|>
596
597 //- /std.rs crate:std deps:alloc
598 pub mod sync {
599 pub use alloc::sync::Arc;
600 }
601
602 //- /zzz.rs crate:alloc
603 pub mod sync {
604 pub struct Arc;
605 }
606 "#;
607 check_found_path(code, "std::sync::Arc");
608 }
609
610 #[test]
611 fn prefer_core_paths_over_std() {
612 mark::check!(prefer_no_std_paths);
613 let code = r#"
614 //- /main.rs crate:main deps:core,std
615 #![no_std]
616
617 <|>
618
619 //- /std.rs crate:std deps:core
620
621 pub mod fmt {
622 pub use core::fmt::Error;
623 }
624
625 //- /zzz.rs crate:core
626
627 pub mod fmt {
628 pub struct Error;
629 }
630 "#;
631 check_found_path(code, "core::fmt::Error");
632 }
633
634 #[test]
635 fn prefer_alloc_paths_over_std() {
636 let code = r#"
637 //- /main.rs crate:main deps:alloc,std
638 #![no_std]
639
640 <|>
641
642 //- /std.rs crate:std deps:alloc
643
644 pub mod sync {
645 pub use alloc::sync::Arc;
646 }
647
648 //- /zzz.rs crate:alloc
649
650 pub mod sync {
651 pub struct Arc;
652 }
653 "#;
654 check_found_path(code, "alloc::sync::Arc");
655 }
656
657 #[test]
658 fn prefer_shorter_paths_if_not_alloc() {
659 let code = r#"
660 //- /main.rs crate:main deps:megaalloc,std
661 <|>
662
663 //- /std.rs crate:std deps:megaalloc
664 pub mod sync {
665 pub use megaalloc::sync::Arc;
666 }
667
668 //- /zzz.rs crate:megaalloc
669 pub struct Arc;
670 "#;
671 check_found_path(code, "megaalloc::Arc");
672 }
673
674 #[test]
675 fn builtins_are_in_scope() {
676 let code = r#"
677 //- /main.rs
678 <|>
679
680 pub mod primitive {
681 pub use u8;
682 }
683 "#;
684 check_found_path(code, "u8");
685 check_found_path(code, "u16");
686 }
687}
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
new file mode 100644
index 000000000..835fe3fbd
--- /dev/null
+++ b/crates/hir_def/src/generics.rs
@@ -0,0 +1,339 @@
1//! Many kinds of items or constructs can have generic parameters: functions,
2//! structs, impls, traits, etc. This module provides a common HIR for these
3//! generic parameters. See also the `Generics` type and the `generics_of` query
4//! in rustc.
5use std::sync::Arc;
6
7use arena::{map::ArenaMap, Arena};
8use base_db::FileId;
9use either::Either;
10use hir_expand::{
11 name::{name, AsName, Name},
12 InFile,
13};
14use syntax::ast::{self, GenericParamsOwner, NameOwner, TypeBoundsOwner};
15
16use crate::{
17 body::LowerCtx,
18 child_by_source::ChildBySource,
19 db::DefDatabase,
20 dyn_map::DynMap,
21 keys,
22 src::HasChildSource,
23 src::HasSource,
24 type_ref::{TypeBound, TypeRef},
25 AdtId, GenericDefId, LocalTypeParamId, Lookup, TypeParamId,
26};
27
28/// Data about a generic parameter (to a function, struct, impl, ...).
29#[derive(Clone, PartialEq, Eq, Debug)]
30pub struct TypeParamData {
31 pub name: Option<Name>,
32 pub default: Option<TypeRef>,
33 pub provenance: TypeParamProvenance,
34}
35
36#[derive(Copy, Clone, PartialEq, Eq, Debug)]
37pub enum TypeParamProvenance {
38 TypeParamList,
39 TraitSelf,
40 ArgumentImplTrait,
41}
42
43/// Data about the generic parameters of a function, struct, impl, etc.
44#[derive(Clone, PartialEq, Eq, Debug, Default)]
45pub struct GenericParams {
46 pub types: Arena<TypeParamData>,
47 // lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>,
48 pub where_predicates: Vec<WherePredicate>,
49}
50
51/// A single predicate from a where clause, i.e. `where Type: Trait`. Combined
52/// where clauses like `where T: Foo + Bar` are turned into multiple of these.
53/// It might still result in multiple actual predicates though, because of
54/// associated type bindings like `Iterator<Item = u32>`.
55#[derive(Clone, PartialEq, Eq, Debug)]
56pub struct WherePredicate {
57 pub target: WherePredicateTarget,
58 pub bound: TypeBound,
59}
60
61#[derive(Clone, PartialEq, Eq, Debug)]
62pub enum WherePredicateTarget {
63 TypeRef(TypeRef),
64 /// For desugared where predicates that can directly refer to a type param.
65 TypeParam(LocalTypeParamId),
66}
67
68type SourceMap = ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>;
69
70impl GenericParams {
71 pub(crate) fn generic_params_query(
72 db: &dyn DefDatabase,
73 def: GenericDefId,
74 ) -> Arc<GenericParams> {
75 let _p = profile::span("generic_params_query");
76
77 let generics = match def {
78 GenericDefId::FunctionId(id) => {
79 let id = id.lookup(db).id;
80 let tree = db.item_tree(id.file_id);
81 let item = &tree[id.value];
82 tree[item.generic_params].clone()
83 }
84 GenericDefId::AdtId(AdtId::StructId(id)) => {
85 let id = id.lookup(db).id;
86 let tree = db.item_tree(id.file_id);
87 let item = &tree[id.value];
88 tree[item.generic_params].clone()
89 }
90 GenericDefId::AdtId(AdtId::EnumId(id)) => {
91 let id = id.lookup(db).id;
92 let tree = db.item_tree(id.file_id);
93 let item = &tree[id.value];
94 tree[item.generic_params].clone()
95 }
96 GenericDefId::AdtId(AdtId::UnionId(id)) => {
97 let id = id.lookup(db).id;
98 let tree = db.item_tree(id.file_id);
99 let item = &tree[id.value];
100 tree[item.generic_params].clone()
101 }
102 GenericDefId::TraitId(id) => {
103 let id = id.lookup(db).id;
104 let tree = db.item_tree(id.file_id);
105 let item = &tree[id.value];
106 tree[item.generic_params].clone()
107 }
108 GenericDefId::TypeAliasId(id) => {
109 let id = id.lookup(db).id;
110 let tree = db.item_tree(id.file_id);
111 let item = &tree[id.value];
112 tree[item.generic_params].clone()
113 }
114 GenericDefId::ImplId(id) => {
115 let id = id.lookup(db).id;
116 let tree = db.item_tree(id.file_id);
117 let item = &tree[id.value];
118 tree[item.generic_params].clone()
119 }
120 GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => GenericParams::default(),
121 };
122 Arc::new(generics)
123 }
124
125 fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) {
126 let mut generics = GenericParams { types: Arena::default(), where_predicates: Vec::new() };
127 let mut sm = ArenaMap::default();
128
129 // FIXME: add `: Sized` bound for everything except for `Self` in traits
130 let file_id = match def {
131 GenericDefId::FunctionId(it) => {
132 let src = it.lookup(db).source(db);
133 let lower_ctx = LowerCtx::new(db, src.file_id);
134 generics.fill(&lower_ctx, &mut sm, &src.value);
135 // lower `impl Trait` in arguments
136 let data = db.function_data(it);
137 for param in &data.params {
138 generics.fill_implicit_impl_trait_args(param);
139 }
140 src.file_id
141 }
142 GenericDefId::AdtId(AdtId::StructId(it)) => {
143 let src = it.lookup(db).source(db);
144 let lower_ctx = LowerCtx::new(db, src.file_id);
145 generics.fill(&lower_ctx, &mut sm, &src.value);
146 src.file_id
147 }
148 GenericDefId::AdtId(AdtId::UnionId(it)) => {
149 let src = it.lookup(db).source(db);
150 let lower_ctx = LowerCtx::new(db, src.file_id);
151 generics.fill(&lower_ctx, &mut sm, &src.value);
152 src.file_id
153 }
154 GenericDefId::AdtId(AdtId::EnumId(it)) => {
155 let src = it.lookup(db).source(db);
156 let lower_ctx = LowerCtx::new(db, src.file_id);
157 generics.fill(&lower_ctx, &mut sm, &src.value);
158 src.file_id
159 }
160 GenericDefId::TraitId(it) => {
161 let src = it.lookup(db).source(db);
162 let lower_ctx = LowerCtx::new(db, src.file_id);
163
164 // traits get the Self type as an implicit first type parameter
165 let self_param_id = generics.types.alloc(TypeParamData {
166 name: Some(name![Self]),
167 default: None,
168 provenance: TypeParamProvenance::TraitSelf,
169 });
170 sm.insert(self_param_id, Either::Left(src.value.clone()));
171 // add super traits as bounds on Self
172 // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
173 let self_param = TypeRef::Path(name![Self].into());
174 generics.fill_bounds(&lower_ctx, &src.value, self_param);
175
176 generics.fill(&lower_ctx, &mut sm, &src.value);
177 src.file_id
178 }
179 GenericDefId::TypeAliasId(it) => {
180 let src = it.lookup(db).source(db);
181 let lower_ctx = LowerCtx::new(db, src.file_id);
182
183 generics.fill(&lower_ctx, &mut sm, &src.value);
184 src.file_id
185 }
186 // Note that we don't add `Self` here: in `impl`s, `Self` is not a
187 // type-parameter, but rather is a type-alias for impl's target
188 // type, so this is handled by the resolver.
189 GenericDefId::ImplId(it) => {
190 let src = it.lookup(db).source(db);
191 let lower_ctx = LowerCtx::new(db, src.file_id);
192
193 generics.fill(&lower_ctx, &mut sm, &src.value);
194 src.file_id
195 }
196 // We won't be using this ID anyway
197 GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => FileId(!0).into(),
198 };
199
200 (generics, InFile::new(file_id, sm))
201 }
202
203 pub(crate) fn fill(
204 &mut self,
205 lower_ctx: &LowerCtx,
206 sm: &mut SourceMap,
207 node: &dyn GenericParamsOwner,
208 ) {
209 if let Some(params) = node.generic_param_list() {
210 self.fill_params(lower_ctx, sm, params)
211 }
212 if let Some(where_clause) = node.where_clause() {
213 self.fill_where_predicates(lower_ctx, where_clause);
214 }
215 }
216
217 pub(crate) fn fill_bounds(
218 &mut self,
219 lower_ctx: &LowerCtx,
220 node: &dyn ast::TypeBoundsOwner,
221 type_ref: TypeRef,
222 ) {
223 for bound in
224 node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds())
225 {
226 self.add_where_predicate_from_bound(lower_ctx, bound, type_ref.clone());
227 }
228 }
229
230 fn fill_params(
231 &mut self,
232 lower_ctx: &LowerCtx,
233 sm: &mut SourceMap,
234 params: ast::GenericParamList,
235 ) {
236 for type_param in params.type_params() {
237 let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
238 // FIXME: Use `Path::from_src`
239 let default = type_param.default_type().map(|it| TypeRef::from_ast(lower_ctx, it));
240 let param = TypeParamData {
241 name: Some(name.clone()),
242 default,
243 provenance: TypeParamProvenance::TypeParamList,
244 };
245 let param_id = self.types.alloc(param);
246 sm.insert(param_id, Either::Right(type_param.clone()));
247
248 let type_ref = TypeRef::Path(name.into());
249 self.fill_bounds(&lower_ctx, &type_param, type_ref);
250 }
251 }
252
253 fn fill_where_predicates(&mut self, lower_ctx: &LowerCtx, where_clause: ast::WhereClause) {
254 for pred in where_clause.predicates() {
255 let type_ref = match pred.ty() {
256 Some(type_ref) => type_ref,
257 None => continue,
258 };
259 let type_ref = TypeRef::from_ast(lower_ctx, type_ref);
260 for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
261 self.add_where_predicate_from_bound(lower_ctx, bound, type_ref.clone());
262 }
263 }
264 }
265
266 fn add_where_predicate_from_bound(
267 &mut self,
268 lower_ctx: &LowerCtx,
269 bound: ast::TypeBound,
270 type_ref: TypeRef,
271 ) {
272 if bound.question_mark_token().is_some() {
273 // FIXME: remove this bound
274 return;
275 }
276 let bound = TypeBound::from_ast(lower_ctx, bound);
277 self.where_predicates
278 .push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound });
279 }
280
281 pub(crate) fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) {
282 type_ref.walk(&mut |type_ref| {
283 if let TypeRef::ImplTrait(bounds) = type_ref {
284 let param = TypeParamData {
285 name: None,
286 default: None,
287 provenance: TypeParamProvenance::ArgumentImplTrait,
288 };
289 let param_id = self.types.alloc(param);
290 for bound in bounds {
291 self.where_predicates.push(WherePredicate {
292 target: WherePredicateTarget::TypeParam(param_id),
293 bound: bound.clone(),
294 });
295 }
296 }
297 });
298 }
299
300 pub fn find_by_name(&self, name: &Name) -> Option<LocalTypeParamId> {
301 self.types
302 .iter()
303 .find_map(|(id, p)| if p.name.as_ref() == Some(name) { Some(id) } else { None })
304 }
305
306 pub fn find_trait_self_param(&self) -> Option<LocalTypeParamId> {
307 self.types.iter().find_map(|(id, p)| {
308 if p.provenance == TypeParamProvenance::TraitSelf {
309 Some(id)
310 } else {
311 None
312 }
313 })
314 }
315}
316
317impl HasChildSource for GenericDefId {
318 type ChildId = LocalTypeParamId;
319 type Value = Either<ast::Trait, ast::TypeParam>;
320 fn child_source(&self, db: &dyn DefDatabase) -> InFile<SourceMap> {
321 let (_, sm) = GenericParams::new(db, *self);
322 sm
323 }
324}
325
326impl ChildBySource for GenericDefId {
327 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
328 let mut res = DynMap::default();
329 let arena_map = self.child_source(db);
330 let arena_map = arena_map.as_ref();
331 for (local_id, src) in arena_map.value.iter() {
332 let id = TypeParamId { parent: *self, local_id };
333 if let Either::Right(type_param) = src {
334 res[keys::TYPE_PARAM].insert(arena_map.with_value(type_param.clone()), id)
335 }
336 }
337 res
338 }
339}
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
new file mode 100644
index 000000000..a442fb63a
--- /dev/null
+++ b/crates/hir_def/src/import_map.rs
@@ -0,0 +1,745 @@
1//! A map of all publicly exported items in a crate.
2
3use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
4
5use base_db::CrateId;
6use fst::{self, Streamer};
7use indexmap::{map::Entry, IndexMap};
8use rustc_hash::{FxHashMap, FxHasher};
9use smallvec::SmallVec;
10use syntax::SmolStr;
11
12use crate::{
13 db::DefDatabase,
14 item_scope::ItemInNs,
15 path::{ModPath, PathKind},
16 visibility::Visibility,
17 AssocItemId, ModuleDefId, ModuleId, TraitId,
18};
19
20type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
21
22/// Item import details stored in the `ImportMap`.
23#[derive(Debug, Clone, Eq, PartialEq)]
24pub struct ImportInfo {
25 /// A path that can be used to import the item, relative to the crate's root.
26 pub path: ModPath,
27 /// The module containing this item.
28 pub container: ModuleId,
29}
30
31/// A map from publicly exported items to the path needed to import/name them from a downstream
32/// crate.
33///
34/// Reexports of items are taken into account, ie. if something is exported under multiple
35/// names, the one with the shortest import path will be used.
36///
37/// Note that all paths are relative to the containing crate's root, so the crate name still needs
38/// to be prepended to the `ModPath` before the path is valid.
39#[derive(Default)]
40pub struct ImportMap {
41 map: FxIndexMap<ItemInNs, ImportInfo>,
42
43 /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
44 /// values returned by running `fst`.
45 ///
46 /// Since a path can refer to multiple items due to namespacing, we store all items with the
47 /// same path right after each other. This allows us to find all items after the FST gives us
48 /// the index of the first one.
49 importables: Vec<ItemInNs>,
50 fst: fst::Map<Vec<u8>>,
51
52 /// Maps names of associated items to the item's ID. Only includes items whose defining trait is
53 /// exported.
54 assoc_map: FxHashMap<SmolStr, SmallVec<[AssocItemId; 1]>>,
55}
56
57impl ImportMap {
58 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
59 let _p = profile::span("import_map_query");
60 let def_map = db.crate_def_map(krate);
61 let mut import_map = Self::default();
62
63 // We look only into modules that are public(ly reexported), starting with the crate root.
64 let empty = ModPath { kind: PathKind::Plain, segments: vec![] };
65 let root = ModuleId { krate, local_id: def_map.root };
66 let mut worklist = vec![(root, empty)];
67 while let Some((module, mod_path)) = worklist.pop() {
68 let ext_def_map;
69 let mod_data = if module.krate == krate {
70 &def_map[module.local_id]
71 } else {
72 // The crate might reexport a module defined in another crate.
73 ext_def_map = db.crate_def_map(module.krate);
74 &ext_def_map[module.local_id]
75 };
76
77 let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
78 let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
79 if per_ns.is_none() {
80 None
81 } else {
82 Some((name, per_ns))
83 }
84 });
85
86 for (name, per_ns) in visible_items {
87 let mk_path = || {
88 let mut path = mod_path.clone();
89 path.segments.push(name.clone());
90 path
91 };
92
93 for item in per_ns.iter_items() {
94 let path = mk_path();
95 match import_map.map.entry(item) {
96 Entry::Vacant(entry) => {
97 entry.insert(ImportInfo { path, container: module });
98 }
99 Entry::Occupied(mut entry) => {
100 // If the new path is shorter, prefer that one.
101 if path.len() < entry.get().path.len() {
102 *entry.get_mut() = ImportInfo { path, container: module };
103 } else {
104 continue;
105 }
106 }
107 }
108
109 // If we've just added a path to a module, descend into it. We might traverse
110 // modules multiple times, but only if the new path to it is shorter than the
111 // first (else we `continue` above).
112 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
113 worklist.push((mod_id, mk_path()));
114 }
115
116 // If we've added a path to a trait, add the trait's methods to the method map.
117 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
118 import_map.collect_trait_methods(db, tr);
119 }
120 }
121 }
122 }
123
124 let mut importables = import_map.map.iter().collect::<Vec<_>>();
125
126 importables.sort_by(cmp);
127
128 // Build the FST, taking care not to insert duplicate values.
129
130 let mut builder = fst::MapBuilder::memory();
131 let mut last_batch_start = 0;
132
133 for idx in 0..importables.len() {
134 if let Some(next_item) = importables.get(idx + 1) {
135 if cmp(&importables[last_batch_start], next_item) == Ordering::Equal {
136 continue;
137 }
138 }
139
140 let start = last_batch_start;
141 last_batch_start = idx + 1;
142
143 let key = fst_path(&importables[start].1.path);
144
145 builder.insert(key, start as u64).unwrap();
146 }
147
148 import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
149 import_map.importables = importables.iter().map(|(item, _)| **item).collect();
150
151 Arc::new(import_map)
152 }
153
154 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
155 pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> {
156 Some(&self.map.get(&item)?.path)
157 }
158
159 pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
160 self.map.get(&item)
161 }
162
163 fn collect_trait_methods(&mut self, db: &dyn DefDatabase, tr: TraitId) {
164 let data = db.trait_data(tr);
165 for (name, item) in data.items.iter() {
166 self.assoc_map.entry(name.to_string().into()).or_default().push(*item);
167 }
168 }
169}
170
171impl PartialEq for ImportMap {
172 fn eq(&self, other: &Self) -> bool {
173 // `fst` and `importables` are built from `map`, so we don't need to compare them.
174 self.map == other.map
175 }
176}
177
178impl Eq for ImportMap {}
179
180impl fmt::Debug for ImportMap {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 let mut importable_paths: Vec<_> = self
183 .map
184 .iter()
185 .map(|(item, info)| {
186 let ns = match item {
187 ItemInNs::Types(_) => "t",
188 ItemInNs::Values(_) => "v",
189 ItemInNs::Macros(_) => "m",
190 };
191 format!("- {} ({})", info.path, ns)
192 })
193 .collect();
194
195 importable_paths.sort();
196 f.write_str(&importable_paths.join("\n"))
197 }
198}
199
200fn fst_path(path: &ModPath) -> String {
201 let mut s = path.to_string();
202 s.make_ascii_lowercase();
203 s
204}
205
206fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering {
207 let lhs_str = fst_path(&lhs.path);
208 let rhs_str = fst_path(&rhs.path);
209 lhs_str.cmp(&rhs_str)
210}
211
212#[derive(Debug)]
213pub struct Query {
214 query: String,
215 lowercased: String,
216 anchor_end: bool,
217 case_sensitive: bool,
218 limit: usize,
219}
220
221impl Query {
222 pub fn new(query: &str) -> Self {
223 Self {
224 lowercased: query.to_lowercase(),
225 query: query.to_string(),
226 anchor_end: false,
227 case_sensitive: false,
228 limit: usize::max_value(),
229 }
230 }
231
232 /// Only returns items whose paths end with the (case-insensitive) query string as their last
233 /// segment.
234 pub fn anchor_end(self) -> Self {
235 Self { anchor_end: true, ..self }
236 }
237
238 /// Limits the returned number of items to `limit`.
239 pub fn limit(self, limit: usize) -> Self {
240 Self { limit, ..self }
241 }
242
243 /// Respect casing of the query string when matching.
244 pub fn case_sensitive(self) -> Self {
245 Self { case_sensitive: true, ..self }
246 }
247}
248
249/// Searches dependencies of `krate` for an importable path matching `query`.
250///
251/// This returns a list of items that could be imported from dependencies of `krate`.
252pub fn search_dependencies<'a>(
253 db: &'a dyn DefDatabase,
254 krate: CrateId,
255 query: Query,
256) -> Vec<ItemInNs> {
257 let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query));
258
259 let graph = db.crate_graph();
260 let import_maps: Vec<_> =
261 graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
262
263 let automaton = fst::automaton::Subsequence::new(&query.lowercased);
264
265 let mut op = fst::map::OpBuilder::new();
266 for map in &import_maps {
267 op = op.add(map.fst.search(&automaton));
268 }
269
270 let mut stream = op.union();
271 let mut res = Vec::new();
272 while let Some((_, indexed_values)) = stream.next() {
273 for indexed_value in indexed_values {
274 let import_map = &import_maps[indexed_value.index];
275 let importables = &import_map.importables[indexed_value.value as usize..];
276
277 // Path shared by the importable items in this group.
278 let path = &import_map.map[&importables[0]].path;
279
280 if query.anchor_end {
281 // Last segment must match query.
282 let last = path.segments.last().unwrap().to_string();
283 if last.to_lowercase() != query.lowercased {
284 continue;
285 }
286 }
287
288 // Add the items from this `ModPath` group. Those are all subsequent items in
289 // `importables` whose paths match `path`.
290 let iter = importables.iter().copied().take_while(|item| {
291 let item_path = &import_map.map[item].path;
292 fst_path(item_path) == fst_path(path)
293 });
294
295 if query.case_sensitive {
296 // FIXME: This does not do a subsequence match.
297 res.extend(iter.filter(|item| {
298 let item_path = &import_map.map[item].path;
299 item_path.to_string().contains(&query.query)
300 }));
301 } else {
302 res.extend(iter);
303 }
304
305 if res.len() >= query.limit {
306 res.truncate(query.limit);
307 return res;
308 }
309 }
310 }
311
312 // Add all exported associated items whose names match the query (exactly).
313 for map in &import_maps {
314 if let Some(v) = map.assoc_map.get(&*query.query) {
315 res.extend(v.iter().map(|&assoc| {
316 ItemInNs::Types(match assoc {
317 AssocItemId::FunctionId(it) => it.into(),
318 AssocItemId::ConstId(it) => it.into(),
319 AssocItemId::TypeAliasId(it) => it.into(),
320 })
321 }));
322 }
323 }
324
325 res
326}
327
328#[cfg(test)]
329mod tests {
330 use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
331 use expect_test::{expect, Expect};
332
333 use crate::{test_db::TestDB, AssocContainerId, Lookup};
334
335 use super::*;
336
337 fn check_search(ra_fixture: &str, krate_name: &str, query: Query, expect: Expect) {
338 let db = TestDB::with_files(ra_fixture);
339 let crate_graph = db.crate_graph();
340 let krate = crate_graph
341 .iter()
342 .find(|krate| {
343 crate_graph[*krate].display_name.as_ref().map(|n| n.to_string())
344 == Some(krate_name.to_string())
345 })
346 .unwrap();
347
348 let actual = search_dependencies(db.upcast(), krate, query)
349 .into_iter()
350 .filter_map(|item| {
351 let mark = match item {
352 ItemInNs::Types(_) => "t",
353 ItemInNs::Values(_) => "v",
354 ItemInNs::Macros(_) => "m",
355 };
356 let item = assoc_to_trait(&db, item);
357 item.krate(db.upcast()).map(|krate| {
358 let map = db.import_map(krate);
359 let path = map.path_of(item).unwrap();
360 format!(
361 "{}::{} ({})\n",
362 crate_graph[krate].display_name.as_ref().unwrap(),
363 path,
364 mark
365 )
366 })
367 })
368 .collect::<String>();
369 expect.assert_eq(&actual)
370 }
371
372 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> ItemInNs {
373 let assoc: AssocItemId = match item {
374 ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
375 ModuleDefId::TypeAliasId(it) => it.into(),
376 ModuleDefId::FunctionId(it) => it.into(),
377 ModuleDefId::ConstId(it) => it.into(),
378 _ => return item,
379 },
380 _ => return item,
381 };
382
383 let container = match assoc {
384 AssocItemId::FunctionId(it) => it.lookup(db).container,
385 AssocItemId::ConstId(it) => it.lookup(db).container,
386 AssocItemId::TypeAliasId(it) => it.lookup(db).container,
387 };
388
389 match container {
390 AssocContainerId::TraitId(it) => ItemInNs::Types(it.into()),
391 _ => item,
392 }
393 }
394
395 fn check(ra_fixture: &str, expect: Expect) {
396 let db = TestDB::with_files(ra_fixture);
397 let crate_graph = db.crate_graph();
398
399 let actual = crate_graph
400 .iter()
401 .filter_map(|krate| {
402 let cdata = &crate_graph[krate];
403 let name = cdata.display_name.as_ref()?;
404
405 let map = db.import_map(krate);
406
407 Some(format!("{}:\n{:?}\n", name, map))
408 })
409 .collect::<String>();
410
411 expect.assert_eq(&actual)
412 }
413
414 #[test]
415 fn smoke() {
416 check(
417 r"
418 //- /main.rs crate:main deps:lib
419
420 mod private {
421 pub use lib::Pub;
422 pub struct InPrivateModule;
423 }
424
425 pub mod publ1 {
426 use lib::Pub;
427 }
428
429 pub mod real_pub {
430 pub use lib::Pub;
431 }
432 pub mod real_pu2 { // same path length as above
433 pub use lib::Pub;
434 }
435
436 //- /lib.rs crate:lib
437 pub struct Pub {}
438 pub struct Pub2; // t + v
439 struct Priv;
440 ",
441 expect![[r#"
442 main:
443 - publ1 (t)
444 - real_pu2 (t)
445 - real_pub (t)
446 - real_pub::Pub (t)
447 lib:
448 - Pub (t)
449 - Pub2 (t)
450 - Pub2 (v)
451 "#]],
452 );
453 }
454
455 #[test]
456 fn prefers_shortest_path() {
457 check(
458 r"
459 //- /main.rs crate:main
460
461 pub mod sub {
462 pub mod subsub {
463 pub struct Def {}
464 }
465
466 pub use super::sub::subsub::Def;
467 }
468 ",
469 expect![[r#"
470 main:
471 - sub (t)
472 - sub::Def (t)
473 - sub::subsub (t)
474 "#]],
475 );
476 }
477
478 #[test]
479 fn type_reexport_cross_crate() {
480 // Reexports need to be visible from a crate, even if the original crate exports the item
481 // at a shorter path.
482 check(
483 r"
484 //- /main.rs crate:main deps:lib
485 pub mod m {
486 pub use lib::S;
487 }
488 //- /lib.rs crate:lib
489 pub struct S;
490 ",
491 expect![[r#"
492 main:
493 - m (t)
494 - m::S (t)
495 - m::S (v)
496 lib:
497 - S (t)
498 - S (v)
499 "#]],
500 );
501 }
502
503 #[test]
504 fn macro_reexport() {
505 check(
506 r"
507 //- /main.rs crate:main deps:lib
508 pub mod m {
509 pub use lib::pub_macro;
510 }
511 //- /lib.rs crate:lib
512 #[macro_export]
513 macro_rules! pub_macro {
514 () => {};
515 }
516 ",
517 expect![[r#"
518 main:
519 - m (t)
520 - m::pub_macro (m)
521 lib:
522 - pub_macro (m)
523 "#]],
524 );
525 }
526
527 #[test]
528 fn module_reexport() {
529 // Reexporting modules from a dependency adds all contents to the import map.
530 check(
531 r"
532 //- /main.rs crate:main deps:lib
533 pub use lib::module as reexported_module;
534 //- /lib.rs crate:lib
535 pub mod module {
536 pub struct S;
537 }
538 ",
539 expect![[r#"
540 main:
541 - reexported_module (t)
542 - reexported_module::S (t)
543 - reexported_module::S (v)
544 lib:
545 - module (t)
546 - module::S (t)
547 - module::S (v)
548 "#]],
549 );
550 }
551
552 #[test]
553 fn cyclic_module_reexport() {
554 // A cyclic reexport does not hang.
555 check(
556 r"
557 //- /lib.rs crate:lib
558 pub mod module {
559 pub struct S;
560 pub use super::sub::*;
561 }
562
563 pub mod sub {
564 pub use super::module;
565 }
566 ",
567 expect![[r#"
568 lib:
569 - module (t)
570 - module::S (t)
571 - module::S (v)
572 - sub (t)
573 "#]],
574 );
575 }
576
577 #[test]
578 fn private_macro() {
579 check(
580 r"
581 //- /lib.rs crate:lib
582 macro_rules! private_macro {
583 () => {};
584 }
585 ",
586 expect![[r#"
587 lib:
588
589 "#]],
590 );
591 }
592
593 #[test]
594 fn namespacing() {
595 check(
596 r"
597 //- /lib.rs crate:lib
598 pub struct Thing; // t + v
599 #[macro_export]
600 macro_rules! Thing { // m
601 () => {};
602 }
603 ",
604 expect![[r#"
605 lib:
606 - Thing (m)
607 - Thing (t)
608 - Thing (v)
609 "#]],
610 );
611
612 check(
613 r"
614 //- /lib.rs crate:lib
615 pub mod Thing {} // t
616 #[macro_export]
617 macro_rules! Thing { // m
618 () => {};
619 }
620 ",
621 expect![[r#"
622 lib:
623 - Thing (m)
624 - Thing (t)
625 "#]],
626 );
627 }
628
629 #[test]
630 fn search() {
631 let ra_fixture = r#"
632 //- /main.rs crate:main deps:dep
633 //- /dep.rs crate:dep deps:tdep
634 use tdep::fmt as fmt_dep;
635 pub mod fmt {
636 pub trait Display {
637 fn fmt();
638 }
639 }
640 #[macro_export]
641 macro_rules! Fmt {
642 () => {};
643 }
644 pub struct Fmt;
645
646 pub fn format() {}
647 pub fn no() {}
648
649 //- /tdep.rs crate:tdep
650 pub mod fmt {
651 pub struct NotImportableFromMain;
652 }
653 "#;
654
655 check_search(
656 ra_fixture,
657 "main",
658 Query::new("fmt"),
659 expect![[r#"
660 dep::fmt (t)
661 dep::Fmt (t)
662 dep::Fmt (v)
663 dep::Fmt (m)
664 dep::fmt::Display (t)
665 dep::format (v)
666 dep::fmt::Display (t)
667 "#]],
668 );
669
670 check_search(
671 ra_fixture,
672 "main",
673 Query::new("fmt").anchor_end(),
674 expect![[r#"
675 dep::fmt (t)
676 dep::Fmt (t)
677 dep::Fmt (v)
678 dep::Fmt (m)
679 dep::fmt::Display (t)
680 "#]],
681 );
682 }
683
684 #[test]
685 fn search_casing() {
686 let ra_fixture = r#"
687 //- /main.rs crate:main deps:dep
688 //- /dep.rs crate:dep
689
690 pub struct fmt;
691 pub struct FMT;
692 "#;
693
694 check_search(
695 ra_fixture,
696 "main",
697 Query::new("FMT"),
698 expect![[r#"
699 dep::fmt (t)
700 dep::fmt (v)
701 dep::FMT (t)
702 dep::FMT (v)
703 "#]],
704 );
705
706 check_search(
707 ra_fixture,
708 "main",
709 Query::new("FMT").case_sensitive(),
710 expect![[r#"
711 dep::FMT (t)
712 dep::FMT (v)
713 "#]],
714 );
715 }
716
717 #[test]
718 fn search_limit() {
719 check_search(
720 r#"
721 //- /main.rs crate:main deps:dep
722 //- /dep.rs crate:dep
723 pub mod fmt {
724 pub trait Display {
725 fn fmt();
726 }
727 }
728 #[macro_export]
729 macro_rules! Fmt {
730 () => {};
731 }
732 pub struct Fmt;
733
734 pub fn format() {}
735 pub fn no() {}
736 "#,
737 "main",
738 Query::new("").limit(2),
739 expect![[r#"
740 dep::fmt (t)
741 dep::Fmt (t)
742 "#]],
743 );
744 }
745}
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
new file mode 100644
index 000000000..f1e9dfd5b
--- /dev/null
+++ b/crates/hir_def/src/item_scope.rs
@@ -0,0 +1,341 @@
1//! Describes items defined or visible (ie, imported) in a certain scope.
2//! This is shared between modules and blocks.
3
4use std::collections::hash_map::Entry;
5
6use base_db::CrateId;
7use hir_expand::name::Name;
8use once_cell::sync::Lazy;
9use rustc_hash::{FxHashMap, FxHashSet};
10use test_utils::mark;
11
12use crate::{
13 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
14 LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId,
15};
16
17#[derive(Copy, Clone)]
18pub(crate) enum ImportType {
19 Glob,
20 Named,
21}
22
23#[derive(Debug, Default)]
24pub struct PerNsGlobImports {
25 types: FxHashSet<(LocalModuleId, Name)>,
26 values: FxHashSet<(LocalModuleId, Name)>,
27 macros: FxHashSet<(LocalModuleId, Name)>,
28}
29
30#[derive(Debug, Default, PartialEq, Eq)]
31pub struct ItemScope {
32 types: FxHashMap<Name, (ModuleDefId, Visibility)>,
33 values: FxHashMap<Name, (ModuleDefId, Visibility)>,
34 macros: FxHashMap<Name, (MacroDefId, Visibility)>,
35 unresolved: FxHashSet<Name>,
36
37 defs: Vec<ModuleDefId>,
38 impls: Vec<ImplId>,
39 /// Traits imported via `use Trait as _;`.
40 unnamed_trait_imports: FxHashMap<TraitId, Visibility>,
41 /// Macros visible in current module in legacy textual scope
42 ///
43 /// For macros invoked by an unqualified identifier like `bar!()`, `legacy_macros` will be searched in first.
44 /// If it yields no result, then it turns to module scoped `macros`.
45 /// It macros with name qualified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped,
46 /// and only normal scoped `macros` will be searched in.
47 ///
48 /// Note that this automatically inherit macros defined textually before the definition of module itself.
49 ///
50 /// Module scoped macros will be inserted into `items` instead of here.
51 // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
52 // be all resolved to the last one defined if shadowing happens.
53 legacy_macros: FxHashMap<Name, MacroDefId>,
54}
55
56pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
57 BuiltinType::ALL
58 .iter()
59 .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public)))
60 .collect()
61});
62
63/// Shadow mode for builtin type which can be shadowed by module.
64#[derive(Debug, Copy, Clone, PartialEq, Eq)]
65pub(crate) enum BuiltinShadowMode {
66 /// Prefer user-defined modules (or other types) over builtins.
67 Module,
68 /// Prefer builtins over user-defined modules (but not other types).
69 Other,
70}
71
72/// Legacy macros can only be accessed through special methods like `get_legacy_macros`.
73/// Other methods will only resolve values, types and module scoped macros only.
74impl ItemScope {
75 pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a {
76 // FIXME: shadowing
77 let keys: FxHashSet<_> = self
78 .types
79 .keys()
80 .chain(self.values.keys())
81 .chain(self.macros.keys())
82 .chain(self.unresolved.iter())
83 .collect();
84
85 keys.into_iter().map(move |name| (name, self.get(name)))
86 }
87
88 pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
89 self.defs.iter().copied()
90 }
91
92 pub fn impls(&self) -> impl Iterator<Item = ImplId> + ExactSizeIterator + '_ {
93 self.impls.iter().copied()
94 }
95
96 pub fn visibility_of(&self, def: ModuleDefId) -> Option<Visibility> {
97 self.name_of(ItemInNs::Types(def))
98 .or_else(|| self.name_of(ItemInNs::Values(def)))
99 .map(|(_, v)| v)
100 }
101
102 /// Iterate over all module scoped macros
103 pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
104 self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_)))
105 }
106
107 /// Iterate over all legacy textual scoped macros visible at the end of the module
108 pub(crate) fn legacy_macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
109 self.legacy_macros.iter().map(|(name, def)| (name, *def))
110 }
111
112 /// Get a name from current module scope, legacy macros are not included
113 pub(crate) fn get(&self, name: &Name) -> PerNs {
114 PerNs {
115 types: self.types.get(name).copied(),
116 values: self.values.get(name).copied(),
117 macros: self.macros.get(name).copied(),
118 }
119 }
120
121 pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> {
122 for (name, per_ns) in self.entries() {
123 if let Some(vis) = item.match_with(per_ns) {
124 return Some((name, vis));
125 }
126 }
127 None
128 }
129
130 pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a {
131 self.types
132 .values()
133 .filter_map(|(def, _)| match def {
134 ModuleDefId::TraitId(t) => Some(*t),
135 _ => None,
136 })
137 .chain(self.unnamed_trait_imports.keys().copied())
138 }
139
140 pub(crate) fn define_def(&mut self, def: ModuleDefId) {
141 self.defs.push(def)
142 }
143
144 pub(crate) fn get_legacy_macro(&self, name: &Name) -> Option<MacroDefId> {
145 self.legacy_macros.get(name).copied()
146 }
147
148 pub(crate) fn define_impl(&mut self, imp: ImplId) {
149 self.impls.push(imp)
150 }
151
152 pub(crate) fn define_legacy_macro(&mut self, name: Name, mac: MacroDefId) {
153 self.legacy_macros.insert(name, mac);
154 }
155
156 pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
157 self.unnamed_trait_imports.get(&tr).copied()
158 }
159
160 pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) {
161 self.unnamed_trait_imports.insert(tr, vis);
162 }
163
164 pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool {
165 let mut changed = false;
166
167 if let Some(types) = def.types {
168 self.types.entry(name.clone()).or_insert_with(|| {
169 changed = true;
170 types
171 });
172 }
173 if let Some(values) = def.values {
174 self.values.entry(name.clone()).or_insert_with(|| {
175 changed = true;
176 values
177 });
178 }
179 if let Some(macros) = def.macros {
180 self.macros.entry(name.clone()).or_insert_with(|| {
181 changed = true;
182 macros
183 });
184 }
185
186 if def.is_none() {
187 if self.unresolved.insert(name) {
188 changed = true;
189 }
190 }
191
192 changed
193 }
194
195 pub(crate) fn push_res_with_import(
196 &mut self,
197 glob_imports: &mut PerNsGlobImports,
198 lookup: (LocalModuleId, Name),
199 def: PerNs,
200 def_import_type: ImportType,
201 ) -> bool {
202 let mut changed = false;
203
204 macro_rules! check_changed {
205 (
206 $changed:ident,
207 ( $this:ident / $def:ident ) . $field:ident,
208 $glob_imports:ident [ $lookup:ident ],
209 $def_import_type:ident
210 ) => {{
211 let existing = $this.$field.entry($lookup.1.clone());
212 match (existing, $def.$field) {
213 (Entry::Vacant(entry), Some(_)) => {
214 match $def_import_type {
215 ImportType::Glob => {
216 $glob_imports.$field.insert($lookup.clone());
217 }
218 ImportType::Named => {
219 $glob_imports.$field.remove(&$lookup);
220 }
221 }
222
223 if let Some(fld) = $def.$field {
224 entry.insert(fld);
225 }
226 $changed = true;
227 }
228 (Entry::Occupied(mut entry), Some(_))
229 if $glob_imports.$field.contains(&$lookup)
230 && matches!($def_import_type, ImportType::Named) =>
231 {
232 mark::hit!(import_shadowed);
233 $glob_imports.$field.remove(&$lookup);
234 if let Some(fld) = $def.$field {
235 entry.insert(fld);
236 }
237 $changed = true;
238 }
239 _ => {}
240 }
241 }};
242 }
243
244 check_changed!(changed, (self / def).types, glob_imports[lookup], def_import_type);
245 check_changed!(changed, (self / def).values, glob_imports[lookup], def_import_type);
246 check_changed!(changed, (self / def).macros, glob_imports[lookup], def_import_type);
247
248 if def.is_none() {
249 if self.unresolved.insert(lookup.1) {
250 changed = true;
251 }
252 }
253
254 changed
255 }
256
257 pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Option<Name>, PerNs)> + 'a {
258 self.entries().map(|(name, res)| (Some(name.clone()), res)).chain(
259 self.unnamed_trait_imports
260 .iter()
261 .map(|(tr, vis)| (None, PerNs::types(ModuleDefId::TraitId(*tr), *vis))),
262 )
263 }
264
265 pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> {
266 self.legacy_macros.clone()
267 }
268}
269
270impl PerNs {
271 pub(crate) fn from_def(def: ModuleDefId, v: Visibility, has_constructor: bool) -> PerNs {
272 match def {
273 ModuleDefId::ModuleId(_) => PerNs::types(def, v),
274 ModuleDefId::FunctionId(_) => PerNs::values(def, v),
275 ModuleDefId::AdtId(adt) => match adt {
276 AdtId::UnionId(_) => PerNs::types(def, v),
277 AdtId::EnumId(_) => PerNs::types(def, v),
278 AdtId::StructId(_) => {
279 if has_constructor {
280 PerNs::both(def, def, v)
281 } else {
282 PerNs::types(def, v)
283 }
284 }
285 },
286 ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v),
287 ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v),
288 ModuleDefId::TraitId(_) => PerNs::types(def, v),
289 ModuleDefId::TypeAliasId(_) => PerNs::types(def, v),
290 ModuleDefId::BuiltinType(_) => PerNs::types(def, v),
291 }
292 }
293}
294
295#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
296pub enum ItemInNs {
297 Types(ModuleDefId),
298 Values(ModuleDefId),
299 Macros(MacroDefId),
300}
301
302impl ItemInNs {
303 fn match_with(self, per_ns: PerNs) -> Option<Visibility> {
304 match self {
305 ItemInNs::Types(def) => {
306 per_ns.types.filter(|(other_def, _)| *other_def == def).map(|(_, vis)| vis)
307 }
308 ItemInNs::Values(def) => {
309 per_ns.values.filter(|(other_def, _)| *other_def == def).map(|(_, vis)| vis)
310 }
311 ItemInNs::Macros(def) => {
312 per_ns.macros.filter(|(other_def, _)| *other_def == def).map(|(_, vis)| vis)
313 }
314 }
315 }
316
317 pub fn as_module_def_id(self) -> Option<ModuleDefId> {
318 match self {
319 ItemInNs::Types(id) | ItemInNs::Values(id) => Some(id),
320 ItemInNs::Macros(_) => None,
321 }
322 }
323
324 /// Returns the crate defining this item (or `None` if `self` is built-in).
325 pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> {
326 Some(match self {
327 ItemInNs::Types(did) | ItemInNs::Values(did) => match did {
328 ModuleDefId::ModuleId(id) => id.krate,
329 ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate,
330 ModuleDefId::AdtId(id) => id.module(db).krate,
331 ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate,
332 ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate,
333 ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate,
334 ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate,
335 ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate,
336 ModuleDefId::BuiltinType(_) => return None,
337 },
338 ItemInNs::Macros(id) => return id.krate,
339 })
340 }
341}
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
new file mode 100644
index 000000000..e14722cae
--- /dev/null
+++ b/crates/hir_def/src/item_tree.rs
@@ -0,0 +1,754 @@
1//! A simplified AST that only contains items.
2
3mod lower;
4#[cfg(test)]
5mod tests;
6
7use std::{
8 any::type_name,
9 fmt::{self, Debug},
10 hash::{Hash, Hasher},
11 marker::PhantomData,
12 ops::{Index, Range},
13 sync::Arc,
14};
15
16use arena::{Arena, Idx, RawId};
17use ast::{AstNode, AttrsOwner, NameOwner, StructKind};
18use either::Either;
19use hir_expand::{
20 ast_id_map::FileAstId,
21 hygiene::Hygiene,
22 name::{name, AsName, Name},
23 HirFileId, InFile,
24};
25use rustc_hash::FxHashMap;
26use smallvec::SmallVec;
27use syntax::{ast, match_ast};
28use test_utils::mark;
29
30use crate::{
31 attr::Attrs,
32 db::DefDatabase,
33 generics::GenericParams,
34 path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
35 type_ref::{Mutability, TypeBound, TypeRef},
36 visibility::RawVisibility,
37};
38
39#[derive(Copy, Clone, Eq, PartialEq)]
40pub struct RawVisibilityId(u32);
41
42impl RawVisibilityId {
43 pub const PUB: Self = RawVisibilityId(u32::max_value());
44 pub const PRIV: Self = RawVisibilityId(u32::max_value() - 1);
45 pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 2);
46}
47
48impl fmt::Debug for RawVisibilityId {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 let mut f = f.debug_tuple("RawVisibilityId");
51 match *self {
52 Self::PUB => f.field(&"pub"),
53 Self::PRIV => f.field(&"pub(self)"),
54 Self::PUB_CRATE => f.field(&"pub(crate)"),
55 _ => f.field(&self.0),
56 };
57 f.finish()
58 }
59}
60
61#[derive(Debug, Copy, Clone, Eq, PartialEq)]
62pub struct GenericParamsId(u32);
63
64impl GenericParamsId {
65 pub const EMPTY: Self = GenericParamsId(u32::max_value());
66}
67
68/// The item tree of a source file.
69#[derive(Debug, Eq, PartialEq)]
70pub struct ItemTree {
71 top_level: SmallVec<[ModItem; 1]>,
72 attrs: FxHashMap<AttrOwner, Attrs>,
73 inner_items: FxHashMap<FileAstId<ast::Item>, SmallVec<[ModItem; 1]>>,
74
75 data: Option<Box<ItemTreeData>>,
76}
77
78impl ItemTree {
79 pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
80 let _p = profile::span("item_tree_query").detail(|| format!("{:?}", file_id));
81 let syntax = if let Some(node) = db.parse_or_expand(file_id) {
82 node
83 } else {
84 return Arc::new(Self::empty());
85 };
86
87 let hygiene = Hygiene::new(db.upcast(), file_id);
88 let ctx = lower::Ctx::new(db, hygiene.clone(), file_id);
89 let mut top_attrs = None;
90 let mut item_tree = match_ast! {
91 match syntax {
92 ast::SourceFile(file) => {
93 top_attrs = Some(Attrs::new(&file, &hygiene));
94 ctx.lower_module_items(&file)
95 },
96 ast::MacroItems(items) => {
97 ctx.lower_module_items(&items)
98 },
99 // Macros can expand to expressions. We return an empty item tree in this case, but
100 // still need to collect inner items.
101 ast::Expr(e) => {
102 ctx.lower_inner_items(e.syntax())
103 },
104 _ => {
105 panic!("cannot create item tree from {:?}", syntax);
106 },
107 }
108 };
109
110 if let Some(attrs) = top_attrs {
111 item_tree.attrs.insert(AttrOwner::TopLevel, attrs);
112 }
113 item_tree.shrink_to_fit();
114 Arc::new(item_tree)
115 }
116
117 fn empty() -> Self {
118 Self {
119 top_level: Default::default(),
120 attrs: Default::default(),
121 inner_items: Default::default(),
122 data: Default::default(),
123 }
124 }
125
126 fn shrink_to_fit(&mut self) {
127 if let Some(data) = &mut self.data {
128 let ItemTreeData {
129 imports,
130 extern_crates,
131 functions,
132 structs,
133 fields,
134 unions,
135 enums,
136 variants,
137 consts,
138 statics,
139 traits,
140 impls,
141 type_aliases,
142 mods,
143 macro_calls,
144 exprs,
145 vis,
146 generics,
147 } = &mut **data;
148
149 imports.shrink_to_fit();
150 extern_crates.shrink_to_fit();
151 functions.shrink_to_fit();
152 structs.shrink_to_fit();
153 fields.shrink_to_fit();
154 unions.shrink_to_fit();
155 enums.shrink_to_fit();
156 variants.shrink_to_fit();
157 consts.shrink_to_fit();
158 statics.shrink_to_fit();
159 traits.shrink_to_fit();
160 impls.shrink_to_fit();
161 type_aliases.shrink_to_fit();
162 mods.shrink_to_fit();
163 macro_calls.shrink_to_fit();
164 exprs.shrink_to_fit();
165
166 vis.arena.shrink_to_fit();
167 generics.arena.shrink_to_fit();
168 }
169 }
170
171 /// Returns an iterator over all items located at the top level of the `HirFileId` this
172 /// `ItemTree` was created from.
173 pub fn top_level_items(&self) -> &[ModItem] {
174 &self.top_level
175 }
176
177 /// Returns the inner attributes of the source file.
178 pub fn top_level_attrs(&self) -> &Attrs {
179 self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&Attrs::EMPTY)
180 }
181
182 pub fn attrs(&self, of: AttrOwner) -> &Attrs {
183 self.attrs.get(&of).unwrap_or(&Attrs::EMPTY)
184 }
185
186 /// Returns the lowered inner items that `ast` corresponds to.
187 ///
188 /// Most AST items are lowered to a single `ModItem`, but some (eg. `use` items) may be lowered
189 /// to multiple items in the `ItemTree`.
190 pub fn inner_items(&self, ast: FileAstId<ast::Item>) -> &[ModItem] {
191 &self.inner_items[&ast]
192 }
193
194 pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ {
195 self.inner_items.values().flatten().copied()
196 }
197
198 pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source {
199 // This unwrap cannot fail, since it has either succeeded above, or resulted in an empty
200 // ItemTree (in which case there is no valid `FileItemTreeId` to call this method with).
201 let root =
202 db.parse_or_expand(of.file_id).expect("parse_or_expand failed on constructed ItemTree");
203
204 let id = self[of.value].ast_id();
205 let map = db.ast_id_map(of.file_id);
206 let ptr = map.get(id);
207 ptr.to_node(&root)
208 }
209
210 fn data(&self) -> &ItemTreeData {
211 self.data.as_ref().expect("attempted to access data of empty ItemTree")
212 }
213
214 fn data_mut(&mut self) -> &mut ItemTreeData {
215 self.data.get_or_insert_with(Box::default)
216 }
217}
218
219#[derive(Default, Debug, Eq, PartialEq)]
220struct ItemVisibilities {
221 arena: Arena<RawVisibility>,
222}
223
224impl ItemVisibilities {
225 fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId {
226 match &vis {
227 RawVisibility::Public => RawVisibilityId::PUB,
228 RawVisibility::Module(path) if path.segments.is_empty() => match &path.kind {
229 PathKind::Super(0) => RawVisibilityId::PRIV,
230 PathKind::Crate => RawVisibilityId::PUB_CRATE,
231 _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
232 },
233 _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
234 }
235 }
236}
237
238static VIS_PUB: RawVisibility = RawVisibility::Public;
239static VIS_PRIV: RawVisibility =
240 RawVisibility::Module(ModPath { kind: PathKind::Super(0), segments: Vec::new() });
241static VIS_PUB_CRATE: RawVisibility =
242 RawVisibility::Module(ModPath { kind: PathKind::Crate, segments: Vec::new() });
243
244#[derive(Default, Debug, Eq, PartialEq)]
245struct GenericParamsStorage {
246 arena: Arena<GenericParams>,
247}
248
249impl GenericParamsStorage {
250 fn alloc(&mut self, params: GenericParams) -> GenericParamsId {
251 if params.types.is_empty() && params.where_predicates.is_empty() {
252 return GenericParamsId::EMPTY;
253 }
254
255 GenericParamsId(self.arena.alloc(params).into_raw().into())
256 }
257}
258
259static EMPTY_GENERICS: GenericParams =
260 GenericParams { types: Arena::new(), where_predicates: Vec::new() };
261
262#[derive(Default, Debug, Eq, PartialEq)]
263struct ItemTreeData {
264 imports: Arena<Import>,
265 extern_crates: Arena<ExternCrate>,
266 functions: Arena<Function>,
267 structs: Arena<Struct>,
268 fields: Arena<Field>,
269 unions: Arena<Union>,
270 enums: Arena<Enum>,
271 variants: Arena<Variant>,
272 consts: Arena<Const>,
273 statics: Arena<Static>,
274 traits: Arena<Trait>,
275 impls: Arena<Impl>,
276 type_aliases: Arena<TypeAlias>,
277 mods: Arena<Mod>,
278 macro_calls: Arena<MacroCall>,
279 exprs: Arena<Expr>,
280
281 vis: ItemVisibilities,
282 generics: GenericParamsStorage,
283}
284
285#[derive(Debug, Eq, PartialEq, Hash)]
286pub enum AttrOwner {
287 /// Attributes on an item.
288 ModItem(ModItem),
289 /// Inner attributes of the source file.
290 TopLevel,
291
292 Variant(Idx<Variant>),
293 Field(Idx<Field>),
294 // FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`.
295}
296
297macro_rules! from_attrs {
298 ( $( $var:ident($t:ty) ),+ ) => {
299 $(
300 impl From<$t> for AttrOwner {
301 fn from(t: $t) -> AttrOwner {
302 AttrOwner::$var(t)
303 }
304 }
305 )+
306 };
307}
308
309from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>));
310
311/// Trait implemented by all item nodes in the item tree.
312pub trait ItemTreeNode: Clone {
313 type Source: AstNode + Into<ast::Item>;
314
315 fn ast_id(&self) -> FileAstId<Self::Source>;
316
317 /// Looks up an instance of `Self` in an item tree.
318 fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self;
319
320 /// Downcasts a `ModItem` to a `FileItemTreeId` specific to this type.
321 fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>>;
322
323 /// Upcasts a `FileItemTreeId` to a generic `ModItem`.
324 fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem;
325}
326
327pub struct FileItemTreeId<N: ItemTreeNode> {
328 index: Idx<N>,
329 _p: PhantomData<N>,
330}
331
332impl<N: ItemTreeNode> Clone for FileItemTreeId<N> {
333 fn clone(&self) -> Self {
334 Self { index: self.index, _p: PhantomData }
335 }
336}
337impl<N: ItemTreeNode> Copy for FileItemTreeId<N> {}
338
339impl<N: ItemTreeNode> PartialEq for FileItemTreeId<N> {
340 fn eq(&self, other: &FileItemTreeId<N>) -> bool {
341 self.index == other.index
342 }
343}
344impl<N: ItemTreeNode> Eq for FileItemTreeId<N> {}
345
346impl<N: ItemTreeNode> Hash for FileItemTreeId<N> {
347 fn hash<H: Hasher>(&self, state: &mut H) {
348 self.index.hash(state)
349 }
350}
351
352impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> {
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 self.index.fmt(f)
355 }
356}
357
358pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>;
359
360macro_rules! mod_items {
361 ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
362 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
363 pub enum ModItem {
364 $(
365 $typ(FileItemTreeId<$typ>),
366 )+
367 }
368
369 $(
370 impl From<FileItemTreeId<$typ>> for ModItem {
371 fn from(id: FileItemTreeId<$typ>) -> ModItem {
372 ModItem::$typ(id)
373 }
374 }
375 )+
376
377 $(
378 impl ItemTreeNode for $typ {
379 type Source = $ast;
380
381 fn ast_id(&self) -> FileAstId<Self::Source> {
382 self.ast_id
383 }
384
385 fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
386 &tree.data().$fld[index]
387 }
388
389 fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> {
390 if let ModItem::$typ(id) = mod_item {
391 Some(id)
392 } else {
393 None
394 }
395 }
396
397 fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem {
398 ModItem::$typ(id)
399 }
400 }
401
402 impl Index<Idx<$typ>> for ItemTree {
403 type Output = $typ;
404
405 fn index(&self, index: Idx<$typ>) -> &Self::Output {
406 &self.data().$fld[index]
407 }
408 }
409 )+
410 };
411}
412
413mod_items! {
414 Import in imports -> ast::Use,
415 ExternCrate in extern_crates -> ast::ExternCrate,
416 Function in functions -> ast::Fn,
417 Struct in structs -> ast::Struct,
418 Union in unions -> ast::Union,
419 Enum in enums -> ast::Enum,
420 Const in consts -> ast::Const,
421 Static in statics -> ast::Static,
422 Trait in traits -> ast::Trait,
423 Impl in impls -> ast::Impl,
424 TypeAlias in type_aliases -> ast::TypeAlias,
425 Mod in mods -> ast::Module,
426 MacroCall in macro_calls -> ast::MacroCall,
427}
428
429macro_rules! impl_index {
430 ( $($fld:ident: $t:ty),+ $(,)? ) => {
431 $(
432 impl Index<Idx<$t>> for ItemTree {
433 type Output = $t;
434
435 fn index(&self, index: Idx<$t>) -> &Self::Output {
436 &self.data().$fld[index]
437 }
438 }
439 )+
440 };
441}
442
443impl_index!(fields: Field, variants: Variant, exprs: Expr);
444
445impl Index<RawVisibilityId> for ItemTree {
446 type Output = RawVisibility;
447 fn index(&self, index: RawVisibilityId) -> &Self::Output {
448 match index {
449 RawVisibilityId::PRIV => &VIS_PRIV,
450 RawVisibilityId::PUB => &VIS_PUB,
451 RawVisibilityId::PUB_CRATE => &VIS_PUB_CRATE,
452 _ => &self.data().vis.arena[Idx::from_raw(index.0.into())],
453 }
454 }
455}
456
457impl Index<GenericParamsId> for ItemTree {
458 type Output = GenericParams;
459
460 fn index(&self, index: GenericParamsId) -> &Self::Output {
461 match index {
462 GenericParamsId::EMPTY => &EMPTY_GENERICS,
463 _ => &self.data().generics.arena[Idx::from_raw(index.0.into())],
464 }
465 }
466}
467
468impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
469 type Output = N;
470 fn index(&self, id: FileItemTreeId<N>) -> &N {
471 N::lookup(self, id.index)
472 }
473}
474
475/// A desugared `use` import.
476#[derive(Debug, Clone, Eq, PartialEq)]
477pub struct Import {
478 pub path: ModPath,
479 pub alias: Option<ImportAlias>,
480 pub visibility: RawVisibilityId,
481 pub is_glob: bool,
482 pub is_prelude: bool,
483 /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many
484 /// `Import`s can map to the same `use` item.
485 pub ast_id: FileAstId<ast::Use>,
486}
487
488#[derive(Debug, Clone, Eq, PartialEq)]
489pub struct ExternCrate {
490 pub path: ModPath,
491 pub alias: Option<ImportAlias>,
492 pub visibility: RawVisibilityId,
493 /// Whether this is a `#[macro_use] extern crate ...`.
494 pub is_macro_use: bool,
495 pub ast_id: FileAstId<ast::ExternCrate>,
496}
497
498#[derive(Debug, Clone, Eq, PartialEq)]
499pub struct Function {
500 pub name: Name,
501 pub visibility: RawVisibilityId,
502 pub generic_params: GenericParamsId,
503 pub has_self_param: bool,
504 pub is_unsafe: bool,
505 pub params: Box<[TypeRef]>,
506 pub is_varargs: bool,
507 pub ret_type: TypeRef,
508 pub ast_id: FileAstId<ast::Fn>,
509}
510
511#[derive(Debug, Clone, Eq, PartialEq)]
512pub struct Struct {
513 pub name: Name,
514 pub visibility: RawVisibilityId,
515 pub generic_params: GenericParamsId,
516 pub fields: Fields,
517 pub ast_id: FileAstId<ast::Struct>,
518 pub kind: StructDefKind,
519}
520
521#[derive(Debug, Clone, Eq, PartialEq)]
522pub enum StructDefKind {
523 /// `struct S { ... }` - type namespace only.
524 Record,
525 /// `struct S(...);`
526 Tuple,
527 /// `struct S;`
528 Unit,
529}
530
531#[derive(Debug, Clone, Eq, PartialEq)]
532pub struct Union {
533 pub name: Name,
534 pub visibility: RawVisibilityId,
535 pub generic_params: GenericParamsId,
536 pub fields: Fields,
537 pub ast_id: FileAstId<ast::Union>,
538}
539
540#[derive(Debug, Clone, Eq, PartialEq)]
541pub struct Enum {
542 pub name: Name,
543 pub visibility: RawVisibilityId,
544 pub generic_params: GenericParamsId,
545 pub variants: IdRange<Variant>,
546 pub ast_id: FileAstId<ast::Enum>,
547}
548
549#[derive(Debug, Clone, Eq, PartialEq)]
550pub struct Const {
551 /// const _: () = ();
552 pub name: Option<Name>,
553 pub visibility: RawVisibilityId,
554 pub type_ref: TypeRef,
555 pub ast_id: FileAstId<ast::Const>,
556}
557
558#[derive(Debug, Clone, Eq, PartialEq)]
559pub struct Static {
560 pub name: Name,
561 pub visibility: RawVisibilityId,
562 pub mutable: bool,
563 pub type_ref: TypeRef,
564 pub ast_id: FileAstId<ast::Static>,
565}
566
567#[derive(Debug, Clone, Eq, PartialEq)]
568pub struct Trait {
569 pub name: Name,
570 pub visibility: RawVisibilityId,
571 pub generic_params: GenericParamsId,
572 pub auto: bool,
573 pub items: Box<[AssocItem]>,
574 pub ast_id: FileAstId<ast::Trait>,
575}
576
577#[derive(Debug, Clone, Eq, PartialEq)]
578pub struct Impl {
579 pub generic_params: GenericParamsId,
580 pub target_trait: Option<TypeRef>,
581 pub target_type: TypeRef,
582 pub is_negative: bool,
583 pub items: Box<[AssocItem]>,
584 pub ast_id: FileAstId<ast::Impl>,
585}
586
587#[derive(Debug, Clone, PartialEq, Eq)]
588pub struct TypeAlias {
589 pub name: Name,
590 pub visibility: RawVisibilityId,
591 /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`.
592 pub bounds: Box<[TypeBound]>,
593 pub generic_params: GenericParamsId,
594 pub type_ref: Option<TypeRef>,
595 pub ast_id: FileAstId<ast::TypeAlias>,
596}
597
598#[derive(Debug, Clone, Eq, PartialEq)]
599pub struct Mod {
600 pub name: Name,
601 pub visibility: RawVisibilityId,
602 pub kind: ModKind,
603 pub ast_id: FileAstId<ast::Module>,
604}
605
606#[derive(Debug, Clone, Eq, PartialEq)]
607pub enum ModKind {
608 /// `mod m { ... }`
609 Inline { items: Box<[ModItem]> },
610
611 /// `mod m;`
612 Outline {},
613}
614
615#[derive(Debug, Clone, Eq, PartialEq)]
616pub struct MacroCall {
617 /// For `macro_rules!` declarations, this is the name of the declared macro.
618 pub name: Option<Name>,
619 /// Path to the called macro.
620 pub path: ModPath,
621 /// Has `#[macro_export]`.
622 pub is_export: bool,
623 /// Has `#[macro_export(local_inner_macros)]`.
624 pub is_local_inner: bool,
625 /// Has `#[rustc_builtin_macro]`.
626 pub is_builtin: bool,
627 pub ast_id: FileAstId<ast::MacroCall>,
628}
629
630// NB: There's no `FileAstId` for `Expr`. The only case where this would be useful is for array
631// lengths, but we don't do much with them yet.
632#[derive(Debug, Clone, Eq, PartialEq)]
633pub struct Expr;
634
635macro_rules! impl_froms {
636 ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
637 $(
638 impl From<$t> for $e {
639 fn from(it: $t) -> $e {
640 $e::$v(it)
641 }
642 }
643 )*
644 }
645}
646
647impl ModItem {
648 pub fn as_assoc_item(&self) -> Option<AssocItem> {
649 match self {
650 ModItem::Import(_)
651 | ModItem::ExternCrate(_)
652 | ModItem::Struct(_)
653 | ModItem::Union(_)
654 | ModItem::Enum(_)
655 | ModItem::Static(_)
656 | ModItem::Trait(_)
657 | ModItem::Impl(_)
658 | ModItem::Mod(_) => None,
659 ModItem::MacroCall(call) => Some(AssocItem::MacroCall(*call)),
660 ModItem::Const(konst) => Some(AssocItem::Const(*konst)),
661 ModItem::TypeAlias(alias) => Some(AssocItem::TypeAlias(*alias)),
662 ModItem::Function(func) => Some(AssocItem::Function(*func)),
663 }
664 }
665
666 pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
667 N::id_from_mod_item(self)
668 }
669}
670
671#[derive(Debug, Copy, Clone, Eq, PartialEq)]
672pub enum AssocItem {
673 Function(FileItemTreeId<Function>),
674 TypeAlias(FileItemTreeId<TypeAlias>),
675 Const(FileItemTreeId<Const>),
676 MacroCall(FileItemTreeId<MacroCall>),
677}
678
679impl_froms!(AssocItem {
680 Function(FileItemTreeId<Function>),
681 TypeAlias(FileItemTreeId<TypeAlias>),
682 Const(FileItemTreeId<Const>),
683 MacroCall(FileItemTreeId<MacroCall>),
684});
685
686impl From<AssocItem> for ModItem {
687 fn from(item: AssocItem) -> Self {
688 match item {
689 AssocItem::Function(it) => it.into(),
690 AssocItem::TypeAlias(it) => it.into(),
691 AssocItem::Const(it) => it.into(),
692 AssocItem::MacroCall(it) => it.into(),
693 }
694 }
695}
696
697#[derive(Debug, Eq, PartialEq)]
698pub struct Variant {
699 pub name: Name,
700 pub fields: Fields,
701}
702
703pub struct IdRange<T> {
704 range: Range<u32>,
705 _p: PhantomData<T>,
706}
707
708impl<T> IdRange<T> {
709 fn new(range: Range<Idx<T>>) -> Self {
710 Self { range: range.start.into_raw().into()..range.end.into_raw().into(), _p: PhantomData }
711 }
712}
713
714impl<T> Iterator for IdRange<T> {
715 type Item = Idx<T>;
716 fn next(&mut self) -> Option<Self::Item> {
717 self.range.next().map(|raw| Idx::from_raw(raw.into()))
718 }
719}
720
721impl<T> fmt::Debug for IdRange<T> {
722 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
723 f.debug_tuple(&format!("IdRange::<{}>", type_name::<T>())).field(&self.range).finish()
724 }
725}
726
727impl<T> Clone for IdRange<T> {
728 fn clone(&self) -> Self {
729 Self { range: self.range.clone(), _p: PhantomData }
730 }
731}
732
733impl<T> PartialEq for IdRange<T> {
734 fn eq(&self, other: &Self) -> bool {
735 self.range == other.range
736 }
737}
738
739impl<T> Eq for IdRange<T> {}
740
741#[derive(Debug, Clone, PartialEq, Eq)]
742pub enum Fields {
743 Record(IdRange<Field>),
744 Tuple(IdRange<Field>),
745 Unit,
746}
747
748/// A single field of an enum variant or struct
749#[derive(Debug, Clone, PartialEq, Eq)]
750pub struct Field {
751 pub name: Name,
752 pub type_ref: TypeRef,
753 pub visibility: RawVisibilityId,
754}
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
new file mode 100644
index 000000000..391ab5d39
--- /dev/null
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -0,0 +1,705 @@
1//! AST -> `ItemTree` lowering code.
2
3use std::{collections::hash_map::Entry, mem, sync::Arc};
4
5use arena::map::ArenaMap;
6use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
7use smallvec::SmallVec;
8use syntax::{
9 ast::{self, ModuleItemOwner},
10 SyntaxNode,
11};
12
13use crate::{
14 attr::Attrs,
15 generics::{GenericParams, TypeParamData, TypeParamProvenance},
16};
17
18use super::*;
19
20fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
21 FileItemTreeId { index, _p: PhantomData }
22}
23
24struct ModItems(SmallVec<[ModItem; 1]>);
25
26impl<T> From<T> for ModItems
27where
28 T: Into<ModItem>,
29{
30 fn from(t: T) -> Self {
31 ModItems(SmallVec::from_buf([t.into(); 1]))
32 }
33}
34
35pub(super) struct Ctx {
36 tree: ItemTree,
37 hygiene: Hygiene,
38 file: HirFileId,
39 source_ast_id_map: Arc<AstIdMap>,
40 body_ctx: crate::body::LowerCtx,
41 inner_items: Vec<ModItem>,
42 forced_visibility: Option<RawVisibilityId>,
43}
44
45impl Ctx {
46 pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self {
47 Self {
48 tree: ItemTree::empty(),
49 hygiene,
50 file,
51 source_ast_id_map: db.ast_id_map(file),
52 body_ctx: crate::body::LowerCtx::new(db, file),
53 inner_items: Vec::new(),
54 forced_visibility: None,
55 }
56 }
57
58 pub(super) fn lower_module_items(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree {
59 self.tree.top_level = item_owner
60 .items()
61 .flat_map(|item| self.lower_mod_item(&item, false))
62 .flat_map(|items| items.0)
63 .collect();
64 self.tree
65 }
66
67 pub(super) fn lower_inner_items(mut self, within: &SyntaxNode) -> ItemTree {
68 self.collect_inner_items(within);
69 self.tree
70 }
71
72 fn data(&mut self) -> &mut ItemTreeData {
73 self.tree.data_mut()
74 }
75
76 fn lower_mod_item(&mut self, item: &ast::Item, inner: bool) -> Option<ModItems> {
77 assert!(inner || self.inner_items.is_empty());
78
79 // Collect inner items for 1-to-1-lowered items.
80 match item {
81 ast::Item::Struct(_)
82 | ast::Item::Union(_)
83 | ast::Item::Enum(_)
84 | ast::Item::Fn(_)
85 | ast::Item::TypeAlias(_)
86 | ast::Item::Const(_)
87 | ast::Item::Static(_)
88 | ast::Item::MacroCall(_) => {
89 // Skip this if we're already collecting inner items. We'll descend into all nodes
90 // already.
91 if !inner {
92 self.collect_inner_items(item.syntax());
93 }
94 }
95
96 // These are handled in their respective `lower_X` method (since we can't just blindly
97 // walk them).
98 ast::Item::Trait(_) | ast::Item::Impl(_) | ast::Item::ExternBlock(_) => {}
99
100 // These don't have inner items.
101 ast::Item::Module(_) | ast::Item::ExternCrate(_) | ast::Item::Use(_) => {}
102 };
103
104 let attrs = Attrs::new(item, &self.hygiene);
105 let items = match item {
106 ast::Item::Struct(ast) => self.lower_struct(ast).map(Into::into),
107 ast::Item::Union(ast) => self.lower_union(ast).map(Into::into),
108 ast::Item::Enum(ast) => self.lower_enum(ast).map(Into::into),
109 ast::Item::Fn(ast) => self.lower_function(ast).map(Into::into),
110 ast::Item::TypeAlias(ast) => self.lower_type_alias(ast).map(Into::into),
111 ast::Item::Static(ast) => self.lower_static(ast).map(Into::into),
112 ast::Item::Const(ast) => Some(self.lower_const(ast).into()),
113 ast::Item::Module(ast) => self.lower_module(ast).map(Into::into),
114 ast::Item::Trait(ast) => self.lower_trait(ast).map(Into::into),
115 ast::Item::Impl(ast) => self.lower_impl(ast).map(Into::into),
116 ast::Item::Use(ast) => Some(ModItems(
117 self.lower_use(ast).into_iter().map(Into::into).collect::<SmallVec<_>>(),
118 )),
119 ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast).map(Into::into),
120 ast::Item::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
121 ast::Item::ExternBlock(ast) => {
122 Some(ModItems(self.lower_extern_block(ast).into_iter().collect::<SmallVec<_>>()))
123 }
124 };
125
126 if !attrs.is_empty() {
127 for item in items.iter().flat_map(|items| &items.0) {
128 self.add_attrs((*item).into(), attrs.clone());
129 }
130 }
131
132 items
133 }
134
135 fn add_attrs(&mut self, item: AttrOwner, attrs: Attrs) {
136 match self.tree.attrs.entry(item) {
137 Entry::Occupied(mut entry) => {
138 *entry.get_mut() = entry.get().merge(attrs);
139 }
140 Entry::Vacant(entry) => {
141 entry.insert(attrs);
142 }
143 }
144 }
145
146 fn collect_inner_items(&mut self, container: &SyntaxNode) {
147 let forced_vis = self.forced_visibility.take();
148 let mut inner_items = mem::take(&mut self.tree.inner_items);
149 inner_items.extend(container.descendants().skip(1).filter_map(ast::Item::cast).filter_map(
150 |item| {
151 let ast_id = self.source_ast_id_map.ast_id(&item);
152 Some((ast_id, self.lower_mod_item(&item, true)?.0))
153 },
154 ));
155 self.tree.inner_items = inner_items;
156 self.forced_visibility = forced_vis;
157 }
158
159 fn lower_assoc_item(&mut self, item: &ast::AssocItem) -> Option<AssocItem> {
160 match item {
161 ast::AssocItem::Fn(ast) => self.lower_function(ast).map(Into::into),
162 ast::AssocItem::TypeAlias(ast) => self.lower_type_alias(ast).map(Into::into),
163 ast::AssocItem::Const(ast) => Some(self.lower_const(ast).into()),
164 ast::AssocItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
165 }
166 }
167
168 fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<FileItemTreeId<Struct>> {
169 let visibility = self.lower_visibility(strukt);
170 let name = strukt.name()?.as_name();
171 let generic_params = self.lower_generic_params(GenericsOwner::Struct, strukt);
172 let fields = self.lower_fields(&strukt.kind());
173 let ast_id = self.source_ast_id_map.ast_id(strukt);
174 let kind = match strukt.kind() {
175 ast::StructKind::Record(_) => StructDefKind::Record,
176 ast::StructKind::Tuple(_) => StructDefKind::Tuple,
177 ast::StructKind::Unit => StructDefKind::Unit,
178 };
179 let res = Struct { name, visibility, generic_params, fields, ast_id, kind };
180 Some(id(self.data().structs.alloc(res)))
181 }
182
183 fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields {
184 match strukt_kind {
185 ast::StructKind::Record(it) => {
186 let range = self.lower_record_fields(it);
187 Fields::Record(range)
188 }
189 ast::StructKind::Tuple(it) => {
190 let range = self.lower_tuple_fields(it);
191 Fields::Tuple(range)
192 }
193 ast::StructKind::Unit => Fields::Unit,
194 }
195 }
196
197 fn lower_record_fields(&mut self, fields: &ast::RecordFieldList) -> IdRange<Field> {
198 let start = self.next_field_idx();
199 for field in fields.fields() {
200 if let Some(data) = self.lower_record_field(&field) {
201 let idx = self.data().fields.alloc(data);
202 self.add_attrs(idx.into(), Attrs::new(&field, &self.hygiene));
203 }
204 }
205 let end = self.next_field_idx();
206 IdRange::new(start..end)
207 }
208
209 fn lower_record_field(&mut self, field: &ast::RecordField) -> Option<Field> {
210 let name = field.name()?.as_name();
211 let visibility = self.lower_visibility(field);
212 let type_ref = self.lower_type_ref_opt(field.ty());
213 let res = Field { name, type_ref, visibility };
214 Some(res)
215 }
216
217 fn lower_tuple_fields(&mut self, fields: &ast::TupleFieldList) -> IdRange<Field> {
218 let start = self.next_field_idx();
219 for (i, field) in fields.fields().enumerate() {
220 let data = self.lower_tuple_field(i, &field);
221 let idx = self.data().fields.alloc(data);
222 self.add_attrs(idx.into(), Attrs::new(&field, &self.hygiene));
223 }
224 let end = self.next_field_idx();
225 IdRange::new(start..end)
226 }
227
228 fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleField) -> Field {
229 let name = Name::new_tuple_field(idx);
230 let visibility = self.lower_visibility(field);
231 let type_ref = self.lower_type_ref_opt(field.ty());
232 let res = Field { name, type_ref, visibility };
233 res
234 }
235
236 fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> {
237 let visibility = self.lower_visibility(union);
238 let name = union.name()?.as_name();
239 let generic_params = self.lower_generic_params(GenericsOwner::Union, union);
240 let fields = match union.record_field_list() {
241 Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)),
242 None => Fields::Record(IdRange::new(self.next_field_idx()..self.next_field_idx())),
243 };
244 let ast_id = self.source_ast_id_map.ast_id(union);
245 let res = Union { name, visibility, generic_params, fields, ast_id };
246 Some(id(self.data().unions.alloc(res)))
247 }
248
249 fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<FileItemTreeId<Enum>> {
250 let visibility = self.lower_visibility(enum_);
251 let name = enum_.name()?.as_name();
252 let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_);
253 let variants = match &enum_.variant_list() {
254 Some(variant_list) => self.lower_variants(variant_list),
255 None => IdRange::new(self.next_variant_idx()..self.next_variant_idx()),
256 };
257 let ast_id = self.source_ast_id_map.ast_id(enum_);
258 let res = Enum { name, visibility, generic_params, variants, ast_id };
259 Some(id(self.data().enums.alloc(res)))
260 }
261
262 fn lower_variants(&mut self, variants: &ast::VariantList) -> IdRange<Variant> {
263 let start = self.next_variant_idx();
264 for variant in variants.variants() {
265 if let Some(data) = self.lower_variant(&variant) {
266 let idx = self.data().variants.alloc(data);
267 self.add_attrs(idx.into(), Attrs::new(&variant, &self.hygiene));
268 }
269 }
270 let end = self.next_variant_idx();
271 IdRange::new(start..end)
272 }
273
274 fn lower_variant(&mut self, variant: &ast::Variant) -> Option<Variant> {
275 let name = variant.name()?.as_name();
276 let fields = self.lower_fields(&variant.kind());
277 let res = Variant { name, fields };
278 Some(res)
279 }
280
281 fn lower_function(&mut self, func: &ast::Fn) -> Option<FileItemTreeId<Function>> {
282 let visibility = self.lower_visibility(func);
283 let name = func.name()?.as_name();
284
285 let mut params = Vec::new();
286 let mut has_self_param = false;
287 if let Some(param_list) = func.param_list() {
288 if let Some(self_param) = param_list.self_param() {
289 let self_type = match self_param.ty() {
290 Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
291 None => {
292 let self_type = TypeRef::Path(name![Self].into());
293 match self_param.kind() {
294 ast::SelfParamKind::Owned => self_type,
295 ast::SelfParamKind::Ref => {
296 TypeRef::Reference(Box::new(self_type), Mutability::Shared)
297 }
298 ast::SelfParamKind::MutRef => {
299 TypeRef::Reference(Box::new(self_type), Mutability::Mut)
300 }
301 }
302 }
303 };
304 params.push(self_type);
305 has_self_param = true;
306 }
307 for param in param_list.params() {
308 let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
309 params.push(type_ref);
310 }
311 }
312
313 let mut is_varargs = false;
314 if let Some(params) = func.param_list() {
315 if let Some(last) = params.params().last() {
316 is_varargs = last.dotdotdot_token().is_some();
317 }
318 }
319
320 let ret_type = match func.ret_type().and_then(|rt| rt.ty()) {
321 Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
322 _ => TypeRef::unit(),
323 };
324
325 let ret_type = if func.async_token().is_some() {
326 let future_impl = desugar_future_path(ret_type);
327 let ty_bound = TypeBound::Path(future_impl);
328 TypeRef::ImplTrait(vec![ty_bound])
329 } else {
330 ret_type
331 };
332
333 let ast_id = self.source_ast_id_map.ast_id(func);
334 let mut res = Function {
335 name,
336 visibility,
337 generic_params: GenericParamsId::EMPTY,
338 has_self_param,
339 is_unsafe: func.unsafe_token().is_some(),
340 params: params.into_boxed_slice(),
341 is_varargs,
342 ret_type,
343 ast_id,
344 };
345 res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func);
346
347 Some(id(self.data().functions.alloc(res)))
348 }
349
350 fn lower_type_alias(
351 &mut self,
352 type_alias: &ast::TypeAlias,
353 ) -> Option<FileItemTreeId<TypeAlias>> {
354 let name = type_alias.name()?.as_name();
355 let type_ref = type_alias.ty().map(|it| self.lower_type_ref(&it));
356 let visibility = self.lower_visibility(type_alias);
357 let bounds = self.lower_type_bounds(type_alias);
358 let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias);
359 let ast_id = self.source_ast_id_map.ast_id(type_alias);
360 let res = TypeAlias {
361 name,
362 visibility,
363 bounds: bounds.into_boxed_slice(),
364 generic_params,
365 type_ref,
366 ast_id,
367 };
368 Some(id(self.data().type_aliases.alloc(res)))
369 }
370
371 fn lower_static(&mut self, static_: &ast::Static) -> Option<FileItemTreeId<Static>> {
372 let name = static_.name()?.as_name();
373 let type_ref = self.lower_type_ref_opt(static_.ty());
374 let visibility = self.lower_visibility(static_);
375 let mutable = static_.mut_token().is_some();
376 let ast_id = self.source_ast_id_map.ast_id(static_);
377 let res = Static { name, visibility, mutable, type_ref, ast_id };
378 Some(id(self.data().statics.alloc(res)))
379 }
380
381 fn lower_const(&mut self, konst: &ast::Const) -> FileItemTreeId<Const> {
382 let name = konst.name().map(|it| it.as_name());
383 let type_ref = self.lower_type_ref_opt(konst.ty());
384 let visibility = self.lower_visibility(konst);
385 let ast_id = self.source_ast_id_map.ast_id(konst);
386 let res = Const { name, visibility, type_ref, ast_id };
387 id(self.data().consts.alloc(res))
388 }
389
390 fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> {
391 let name = module.name()?.as_name();
392 let visibility = self.lower_visibility(module);
393 let kind = if module.semicolon_token().is_some() {
394 ModKind::Outline {}
395 } else {
396 ModKind::Inline {
397 items: module
398 .item_list()
399 .map(|list| {
400 list.items()
401 .flat_map(|item| self.lower_mod_item(&item, false))
402 .flat_map(|items| items.0)
403 .collect()
404 })
405 .unwrap_or_else(|| {
406 mark::hit!(name_res_works_for_broken_modules);
407 Box::new([]) as Box<[_]>
408 }),
409 }
410 };
411 let ast_id = self.source_ast_id_map.ast_id(module);
412 let res = Mod { name, visibility, kind, ast_id };
413 Some(id(self.data().mods.alloc(res)))
414 }
415
416 fn lower_trait(&mut self, trait_def: &ast::Trait) -> Option<FileItemTreeId<Trait>> {
417 let name = trait_def.name()?.as_name();
418 let visibility = self.lower_visibility(trait_def);
419 let generic_params =
420 self.lower_generic_params_and_inner_items(GenericsOwner::Trait(trait_def), trait_def);
421 let auto = trait_def.auto_token().is_some();
422 let items = trait_def.assoc_item_list().map(|list| {
423 self.with_inherited_visibility(visibility, |this| {
424 list.assoc_items()
425 .filter_map(|item| {
426 let attrs = Attrs::new(&item, &this.hygiene);
427 this.collect_inner_items(item.syntax());
428 this.lower_assoc_item(&item).map(|item| {
429 this.add_attrs(ModItem::from(item).into(), attrs);
430 item
431 })
432 })
433 .collect()
434 })
435 });
436 let ast_id = self.source_ast_id_map.ast_id(trait_def);
437 let res = Trait {
438 name,
439 visibility,
440 generic_params,
441 auto,
442 items: items.unwrap_or_default(),
443 ast_id,
444 };
445 Some(id(self.data().traits.alloc(res)))
446 }
447
448 fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option<FileItemTreeId<Impl>> {
449 let generic_params =
450 self.lower_generic_params_and_inner_items(GenericsOwner::Impl, impl_def);
451 let target_trait = impl_def.trait_().map(|tr| self.lower_type_ref(&tr));
452 let target_type = self.lower_type_ref(&impl_def.self_ty()?);
453 let is_negative = impl_def.excl_token().is_some();
454
455 // We cannot use `assoc_items()` here as that does not include macro calls.
456 let items = impl_def
457 .assoc_item_list()
458 .into_iter()
459 .flat_map(|it| it.assoc_items())
460 .filter_map(|item| {
461 self.collect_inner_items(item.syntax());
462 let assoc = self.lower_assoc_item(&item)?;
463 let attrs = Attrs::new(&item, &self.hygiene);
464 self.add_attrs(ModItem::from(assoc).into(), attrs);
465 Some(assoc)
466 })
467 .collect();
468 let ast_id = self.source_ast_id_map.ast_id(impl_def);
469 let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id };
470 Some(id(self.data().impls.alloc(res)))
471 }
472
473 fn lower_use(&mut self, use_item: &ast::Use) -> Vec<FileItemTreeId<Import>> {
474 // FIXME: cfg_attr
475 let is_prelude = use_item.has_atom_attr("prelude_import");
476 let visibility = self.lower_visibility(use_item);
477 let ast_id = self.source_ast_id_map.ast_id(use_item);
478
479 // Every use item can expand to many `Import`s.
480 let mut imports = Vec::new();
481 let tree = self.tree.data_mut();
482 ModPath::expand_use_item(
483 InFile::new(self.file, use_item.clone()),
484 &self.hygiene,
485 |path, _tree, is_glob, alias| {
486 imports.push(id(tree.imports.alloc(Import {
487 path,
488 alias,
489 visibility,
490 is_glob,
491 is_prelude,
492 ast_id,
493 })));
494 },
495 );
496
497 imports
498 }
499
500 fn lower_extern_crate(
501 &mut self,
502 extern_crate: &ast::ExternCrate,
503 ) -> Option<FileItemTreeId<ExternCrate>> {
504 let path = ModPath::from_name_ref(&extern_crate.name_ref()?);
505 let alias = extern_crate.rename().map(|a| {
506 a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
507 });
508 let visibility = self.lower_visibility(extern_crate);
509 let ast_id = self.source_ast_id_map.ast_id(extern_crate);
510 // FIXME: cfg_attr
511 let is_macro_use = extern_crate.has_atom_attr("macro_use");
512
513 let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id };
514 Some(id(self.data().extern_crates.alloc(res)))
515 }
516
517 fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
518 let name = m.name().map(|it| it.as_name());
519 let attrs = Attrs::new(m, &self.hygiene);
520 let path = ModPath::from_src(m.path()?, &self.hygiene)?;
521
522 let ast_id = self.source_ast_id_map.ast_id(m);
523
524 // FIXME: cfg_attr
525 let export_attr = attrs.by_key("macro_export");
526
527 let is_export = export_attr.exists();
528 let is_local_inner = if is_export {
529 export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
530 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
531 ident.text.contains("local_inner_macros")
532 }
533 _ => false,
534 })
535 } else {
536 false
537 };
538
539 let is_builtin = attrs.by_key("rustc_builtin_macro").exists();
540 let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id };
541 Some(id(self.data().macro_calls.alloc(res)))
542 }
543
544 fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> {
545 block.extern_item_list().map_or(Vec::new(), |list| {
546 list.extern_items()
547 .filter_map(|item| {
548 self.collect_inner_items(item.syntax());
549 let attrs = Attrs::new(&item, &self.hygiene);
550 let id: ModItem = match item {
551 ast::ExternItem::Fn(ast) => {
552 let func = self.lower_function(&ast)?;
553 self.data().functions[func.index].is_unsafe = true;
554 func.into()
555 }
556 ast::ExternItem::Static(ast) => {
557 let statik = self.lower_static(&ast)?;
558 statik.into()
559 }
560 ast::ExternItem::MacroCall(_) => return None,
561 };
562 self.add_attrs(id.into(), attrs);
563 Some(id)
564 })
565 .collect()
566 })
567 }
568
569 /// Lowers generics defined on `node` and collects inner items defined within.
570 fn lower_generic_params_and_inner_items(
571 &mut self,
572 owner: GenericsOwner<'_>,
573 node: &impl ast::GenericParamsOwner,
574 ) -> GenericParamsId {
575 // Generics are part of item headers and may contain inner items we need to collect.
576 if let Some(params) = node.generic_param_list() {
577 self.collect_inner_items(params.syntax());
578 }
579 if let Some(clause) = node.where_clause() {
580 self.collect_inner_items(clause.syntax());
581 }
582
583 self.lower_generic_params(owner, node)
584 }
585
586 fn lower_generic_params(
587 &mut self,
588 owner: GenericsOwner<'_>,
589 node: &impl ast::GenericParamsOwner,
590 ) -> GenericParamsId {
591 let mut sm = &mut ArenaMap::default();
592 let mut generics = GenericParams::default();
593 match owner {
594 GenericsOwner::Function(func) => {
595 generics.fill(&self.body_ctx, sm, node);
596 // lower `impl Trait` in arguments
597 for param in &*func.params {
598 generics.fill_implicit_impl_trait_args(param);
599 }
600 }
601 GenericsOwner::Struct
602 | GenericsOwner::Enum
603 | GenericsOwner::Union
604 | GenericsOwner::TypeAlias => {
605 generics.fill(&self.body_ctx, sm, node);
606 }
607 GenericsOwner::Trait(trait_def) => {
608 // traits get the Self type as an implicit first type parameter
609 let self_param_id = generics.types.alloc(TypeParamData {
610 name: Some(name![Self]),
611 default: None,
612 provenance: TypeParamProvenance::TraitSelf,
613 });
614 sm.insert(self_param_id, Either::Left(trait_def.clone()));
615 // add super traits as bounds on Self
616 // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
617 let self_param = TypeRef::Path(name![Self].into());
618 generics.fill_bounds(&self.body_ctx, trait_def, self_param);
619
620 generics.fill(&self.body_ctx, &mut sm, node);
621 }
622 GenericsOwner::Impl => {
623 // Note that we don't add `Self` here: in `impl`s, `Self` is not a
624 // type-parameter, but rather is a type-alias for impl's target
625 // type, so this is handled by the resolver.
626 generics.fill(&self.body_ctx, &mut sm, node);
627 }
628 }
629
630 self.data().generics.alloc(generics)
631 }
632
633 fn lower_type_bounds(&mut self, node: &impl ast::TypeBoundsOwner) -> Vec<TypeBound> {
634 match node.type_bound_list() {
635 Some(bound_list) => {
636 bound_list.bounds().map(|it| TypeBound::from_ast(&self.body_ctx, it)).collect()
637 }
638 None => Vec::new(),
639 }
640 }
641
642 fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId {
643 let vis = match self.forced_visibility {
644 Some(vis) => return vis,
645 None => RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene),
646 };
647
648 self.data().vis.alloc(vis)
649 }
650
651 fn lower_type_ref(&self, type_ref: &ast::Type) -> TypeRef {
652 TypeRef::from_ast(&self.body_ctx, type_ref.clone())
653 }
654 fn lower_type_ref_opt(&self, type_ref: Option<ast::Type>) -> TypeRef {
655 type_ref.map(|ty| self.lower_type_ref(&ty)).unwrap_or(TypeRef::Error)
656 }
657
658 /// Forces the visibility `vis` to be used for all items lowered during execution of `f`.
659 fn with_inherited_visibility<R>(
660 &mut self,
661 vis: RawVisibilityId,
662 f: impl FnOnce(&mut Self) -> R,
663 ) -> R {
664 let old = mem::replace(&mut self.forced_visibility, Some(vis));
665 let res = f(self);
666 self.forced_visibility = old;
667 res
668 }
669
670 fn next_field_idx(&self) -> Idx<Field> {
671 Idx::from_raw(RawId::from(
672 self.tree.data.as_ref().map_or(0, |data| data.fields.len() as u32),
673 ))
674 }
675 fn next_variant_idx(&self) -> Idx<Variant> {
676 Idx::from_raw(RawId::from(
677 self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32),
678 ))
679 }
680}
681
682fn desugar_future_path(orig: TypeRef) -> Path {
683 let path = path![core::future::Future];
684 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
685 let mut last = GenericArgs::empty();
686 let binding =
687 AssociatedTypeBinding { name: name![Output], type_ref: Some(orig), bounds: Vec::new() };
688 last.bindings.push(binding);
689 generic_args.push(Some(Arc::new(last)));
690
691 Path::from_known_path(path, generic_args)
692}
693
694enum GenericsOwner<'a> {
695 /// We need access to the partially-lowered `Function` for lowering `impl Trait` in argument
696 /// position.
697 Function(&'a Function),
698 Struct,
699 Enum,
700 Union,
701 /// The `TraitDef` is needed to fill the source map for the implicit `Self` parameter.
702 Trait(&'a ast::Trait),
703 TypeAlias,
704 Impl,
705}
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs
new file mode 100644
index 000000000..620e697d4
--- /dev/null
+++ b/crates/hir_def/src/item_tree/tests.rs
@@ -0,0 +1,439 @@
1use base_db::fixture::WithFixture;
2use expect_test::{expect, Expect};
3use hir_expand::{db::AstDatabase, HirFileId, InFile};
4use rustc_hash::FxHashSet;
5use std::sync::Arc;
6use stdx::format_to;
7use syntax::{ast, AstNode};
8
9use crate::{db::DefDatabase, test_db::TestDB};
10
11use super::{ItemTree, ModItem, ModKind};
12
13fn test_inner_items(ra_fixture: &str) {
14 let (db, file_id) = TestDB::with_single_file(ra_fixture);
15 let file_id = HirFileId::from(file_id);
16 let tree = db.item_tree(file_id);
17 let root = db.parse_or_expand(file_id).unwrap();
18 let ast_id_map = db.ast_id_map(file_id);
19
20 // Traverse the item tree and collect all module/impl/trait-level items as AST nodes.
21 let mut outer_items = FxHashSet::default();
22 let mut worklist = tree.top_level_items().to_vec();
23 while let Some(item) = worklist.pop() {
24 let node: ast::Item = match item {
25 ModItem::Import(it) => tree.source(&db, InFile::new(file_id, it)).into(),
26 ModItem::ExternCrate(it) => tree.source(&db, InFile::new(file_id, it)).into(),
27 ModItem::Function(it) => tree.source(&db, InFile::new(file_id, it)).into(),
28 ModItem::Struct(it) => tree.source(&db, InFile::new(file_id, it)).into(),
29 ModItem::Union(it) => tree.source(&db, InFile::new(file_id, it)).into(),
30 ModItem::Enum(it) => tree.source(&db, InFile::new(file_id, it)).into(),
31 ModItem::Const(it) => tree.source(&db, InFile::new(file_id, it)).into(),
32 ModItem::Static(it) => tree.source(&db, InFile::new(file_id, it)).into(),
33 ModItem::TypeAlias(it) => tree.source(&db, InFile::new(file_id, it)).into(),
34 ModItem::Mod(it) => {
35 if let ModKind::Inline { items } = &tree[it].kind {
36 worklist.extend(&**items);
37 }
38 tree.source(&db, InFile::new(file_id, it)).into()
39 }
40 ModItem::Trait(it) => {
41 worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item)));
42 tree.source(&db, InFile::new(file_id, it)).into()
43 }
44 ModItem::Impl(it) => {
45 worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item)));
46 tree.source(&db, InFile::new(file_id, it)).into()
47 }
48 ModItem::MacroCall(_) => continue,
49 };
50
51 outer_items.insert(node);
52 }
53
54 // Now descend the root node and check that all `ast::ModuleItem`s are either recorded above, or
55 // registered as inner items.
56 for item in root.descendants().skip(1).filter_map(ast::Item::cast) {
57 if outer_items.contains(&item) {
58 continue;
59 }
60
61 let ast_id = ast_id_map.ast_id(&item);
62 assert!(!tree.inner_items(ast_id).is_empty());
63 }
64}
65
66fn item_tree(ra_fixture: &str) -> Arc<ItemTree> {
67 let (db, file_id) = TestDB::with_single_file(ra_fixture);
68 db.item_tree(file_id.into())
69}
70
71fn print_item_tree(ra_fixture: &str) -> String {
72 let tree = item_tree(ra_fixture);
73 let mut out = String::new();
74
75 format_to!(out, "inner attrs: {:?}\n\n", tree.top_level_attrs());
76 format_to!(out, "top-level items:\n");
77 for item in tree.top_level_items() {
78 fmt_mod_item(&mut out, &tree, *item);
79 format_to!(out, "\n");
80 }
81
82 if !tree.inner_items.is_empty() {
83 format_to!(out, "\ninner items:\n\n");
84 for (ast_id, items) in &tree.inner_items {
85 format_to!(out, "for AST {:?}:\n", ast_id);
86 for inner in items {
87 fmt_mod_item(&mut out, &tree, *inner);
88 format_to!(out, "\n\n");
89 }
90 }
91 }
92
93 out
94}
95
96fn fmt_mod_item(out: &mut String, tree: &ItemTree, item: ModItem) {
97 let attrs = tree.attrs(item.into());
98 if !attrs.is_empty() {
99 format_to!(out, "#[{:?}]\n", attrs);
100 }
101
102 let mut children = String::new();
103 match item {
104 ModItem::ExternCrate(it) => {
105 format_to!(out, "{:?}", tree[it]);
106 }
107 ModItem::Import(it) => {
108 format_to!(out, "{:?}", tree[it]);
109 }
110 ModItem::Function(it) => {
111 format_to!(out, "{:?}", tree[it]);
112 }
113 ModItem::Struct(it) => {
114 format_to!(out, "{:?}", tree[it]);
115 }
116 ModItem::Union(it) => {
117 format_to!(out, "{:?}", tree[it]);
118 }
119 ModItem::Enum(it) => {
120 format_to!(out, "{:?}", tree[it]);
121 }
122 ModItem::Const(it) => {
123 format_to!(out, "{:?}", tree[it]);
124 }
125 ModItem::Static(it) => {
126 format_to!(out, "{:?}", tree[it]);
127 }
128 ModItem::Trait(it) => {
129 format_to!(out, "{:?}", tree[it]);
130 for item in &*tree[it].items {
131 fmt_mod_item(&mut children, tree, ModItem::from(*item));
132 format_to!(children, "\n");
133 }
134 }
135 ModItem::Impl(it) => {
136 format_to!(out, "{:?}", tree[it]);
137 for item in &*tree[it].items {
138 fmt_mod_item(&mut children, tree, ModItem::from(*item));
139 format_to!(children, "\n");
140 }
141 }
142 ModItem::TypeAlias(it) => {
143 format_to!(out, "{:?}", tree[it]);
144 }
145 ModItem::Mod(it) => {
146 format_to!(out, "{:?}", tree[it]);
147 match &tree[it].kind {
148 ModKind::Inline { items } => {
149 for item in &**items {
150 fmt_mod_item(&mut children, tree, *item);
151 format_to!(children, "\n");
152 }
153 }
154 ModKind::Outline {} => {}
155 }
156 }
157 ModItem::MacroCall(it) => {
158 format_to!(out, "{:?}", tree[it]);
159 }
160 }
161
162 for line in children.lines() {
163 format_to!(out, "\n> {}", line);
164 }
165}
166
167fn check(ra_fixture: &str, expect: Expect) {
168 let actual = print_item_tree(ra_fixture);
169 expect.assert_eq(&actual);
170}
171
172#[test]
173fn smoke() {
174 check(
175 r"
176 #![attr]
177
178 #[attr_on_use]
179 use {a, b::*};
180
181 #[ext_crate]
182 extern crate krate;
183
184 #[on_trait]
185 trait Tr<U> {
186 #[assoc_ty]
187 type AssocTy: Tr<()>;
188
189 #[assoc_const]
190 const CONST: u8;
191
192 #[assoc_method]
193 fn method(&self);
194
195 #[assoc_dfl_method]
196 fn dfl_method(&mut self) {}
197 }
198
199 #[struct0]
200 struct Struct0<T = ()>;
201
202 #[struct1]
203 struct Struct1<T>(#[struct1fld] u8);
204
205 #[struct2]
206 struct Struct2<T> {
207 #[struct2fld]
208 fld: (T, ),
209 }
210
211 #[en]
212 enum En {
213 #[enum_variant]
214 Variant {
215 #[enum_field]
216 field: u8,
217 },
218 }
219
220 #[un]
221 union Un {
222 #[union_fld]
223 fld: u16,
224 }
225 ",
226 expect![[r##"
227 inner attrs: Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr"))] }, input: None }]) }
228
229 top-level items:
230 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }]
231 Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0) }
232 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }]
233 Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0) }
234 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }]
235 ExternCrate { path: ModPath { kind: Plain, segments: [Name(Text("krate"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<syntax::ast::generated::nodes::ExternCrate>(1) }
236 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }]
237 Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Trait>(2) }
238 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }]
239 > TypeAlias { name: Name(Text("AssocTy")), visibility: RawVisibilityId("pub(self)"), bounds: [Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Tr"))] }, generic_args: [Some(GenericArgs { args: [Type(Tuple([]))], has_self_type: false, bindings: [] })] })], generic_params: GenericParamsId(4294967295), type_ref: None, ast_id: FileAstId::<syntax::ast::generated::nodes::TypeAlias>(8) }
240 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_const"))] }, input: None }]) }]
241 > Const { name: Some(Name(Text("CONST"))), visibility: RawVisibilityId("pub(self)"), type_ref: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("u8"))] }, generic_args: [None] }), ast_id: FileAstId::<syntax::ast::generated::nodes::Const>(9) }
242 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_method"))] }, input: None }]) }]
243 > Function { name: Name(Text("method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Shared)], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(10) }
244 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_dfl_method"))] }, input: None }]) }]
245 > Function { name: Name(Text("dfl_method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Mut)], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(11) }
246 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct0"))] }, input: None }]) }]
247 Struct { name: Name(Text("Struct0")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), fields: Unit, ast_id: FileAstId::<syntax::ast::generated::nodes::Struct>(3), kind: Unit }
248 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct1"))] }, input: None }]) }]
249 Struct { name: Name(Text("Struct1")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(2), fields: Tuple(IdRange::<hir_def::item_tree::Field>(0..1)), ast_id: FileAstId::<syntax::ast::generated::nodes::Struct>(4), kind: Tuple }
250 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct2"))] }, input: None }]) }]
251 Struct { name: Name(Text("Struct2")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(3), fields: Record(IdRange::<hir_def::item_tree::Field>(1..2)), ast_id: FileAstId::<syntax::ast::generated::nodes::Struct>(5), kind: Record }
252 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("en"))] }, input: None }]) }]
253 Enum { name: Name(Text("En")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), variants: IdRange::<hir_def::item_tree::Variant>(0..1), ast_id: FileAstId::<syntax::ast::generated::nodes::Enum>(6) }
254 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("un"))] }, input: None }]) }]
255 Union { name: Name(Text("Un")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), fields: Record(IdRange::<hir_def::item_tree::Field>(3..4)), ast_id: FileAstId::<syntax::ast::generated::nodes::Union>(7) }
256 "##]],
257 );
258}
259
260#[test]
261fn simple_inner_items() {
262 check(
263 r"
264 impl<T:A> D for Response<T> {
265 fn foo() {
266 end();
267 fn end<W: Write>() {
268 let _x: T = loop {};
269 }
270 }
271 }
272 ",
273 expect![[r#"
274 inner attrs: Attrs { entries: None }
275
276 top-level items:
277 Impl { generic_params: GenericParamsId(0), target_trait: Some(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("D"))] }, generic_args: [None] })), target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Response"))] }, generic_args: [Some(GenericArgs { args: [Type(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("T"))] }, generic_args: [None] }))], has_self_type: false, bindings: [] })] }), is_negative: false, items: [Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Impl>(0) }
278 > Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(1) }
279
280 inner items:
281
282 for AST FileAstId::<syntax::ast::generated::nodes::Item>(2):
283 Function { name: Name(Text("end")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(2) }
284
285 "#]],
286 );
287}
288
289#[test]
290fn extern_attrs() {
291 check(
292 r#"
293 #[block_attr]
294 extern "C" {
295 #[attr_a]
296 fn a() {}
297 #[attr_b]
298 fn b() {}
299 }
300 "#,
301 expect![[r##"
302 inner attrs: Attrs { entries: None }
303
304 top-level items:
305 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }]
306 Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: true, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(1) }
307 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }]
308 Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: true, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(2) }
309 "##]],
310 );
311}
312
313#[test]
314fn trait_attrs() {
315 check(
316 r#"
317 #[trait_attr]
318 trait Tr {
319 #[attr_a]
320 fn a() {}
321 #[attr_b]
322 fn b() {}
323 }
324 "#,
325 expect![[r##"
326 inner attrs: Attrs { entries: None }
327
328 top-level items:
329 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("trait_attr"))] }, input: None }]) }]
330 Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Trait>(0) }
331 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }]
332 > Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(1) }
333 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }]
334 > Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(2) }
335 "##]],
336 );
337}
338
339#[test]
340fn impl_attrs() {
341 check(
342 r#"
343 #[impl_attr]
344 impl Ty {
345 #[attr_a]
346 fn a() {}
347 #[attr_b]
348 fn b() {}
349 }
350 "#,
351 expect![[r##"
352 inner attrs: Attrs { entries: None }
353
354 top-level items:
355 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("impl_attr"))] }, input: None }]) }]
356 Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Ty"))] }, generic_args: [None] }), is_negative: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Impl>(0) }
357 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }]
358 > Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(1) }
359 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }]
360 > Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(2) }
361 "##]],
362 );
363}
364
365#[test]
366fn cursed_inner_items() {
367 test_inner_items(
368 r"
369 struct S<T: Trait = [u8; { fn f() {} 0 }]>(T);
370
371 enum En {
372 Var1 {
373 t: [(); { trait Inner {} 0 }],
374 },
375
376 Var2([u16; { enum Inner {} 0 }]),
377 }
378
379 type Ty = [En; { struct Inner; 0 }];
380
381 impl En {
382 fn assoc() {
383 trait InnerTrait<T = [u8; { fn f() {} }]> {}
384 struct InnerStruct<T = [u8; { fn f() {} }]> {}
385 impl<T = [u8; { fn f() {} }]> InnerTrait for InnerStruct {}
386 }
387 }
388
389 trait Tr<T = [u8; { fn f() {} }]> {
390 type AssocTy = [u8; { fn f() {} }];
391
392 const AssocConst: [u8; { fn f() {} }];
393 }
394 ",
395 );
396}
397
398#[test]
399fn inner_item_attrs() {
400 check(
401 r"
402 fn foo() {
403 #[on_inner]
404 fn inner() {}
405 }
406 ",
407 expect![[r##"
408 inner attrs: Attrs { entries: None }
409
410 top-level items:
411 Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(0) }
412
413 inner items:
414
415 for AST FileAstId::<syntax::ast::generated::nodes::Item>(1):
416 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_inner"))] }, input: None }]) }]
417 Function { name: Name(Text("inner")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<syntax::ast::generated::nodes::Fn>(1) }
418
419 "##]],
420 );
421}
422
423#[test]
424fn assoc_item_macros() {
425 check(
426 r"
427 impl S {
428 items!();
429 }
430 ",
431 expect![[r#"
432 inner attrs: Attrs { entries: None }
433
434 top-level items:
435 Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("S"))] }, generic_args: [None] }), is_negative: false, items: [MacroCall(Idx::<MacroCall>(0))], ast_id: FileAstId::<syntax::ast::generated::nodes::Impl>(0) }
436 > MacroCall { name: None, path: ModPath { kind: Plain, segments: [Name(Text("items"))] }, is_export: false, is_local_inner: false, is_builtin: false, ast_id: FileAstId::<syntax::ast::generated::nodes::MacroCall>(1) }
437 "#]],
438 );
439}
diff --git a/crates/hir_def/src/keys.rs b/crates/hir_def/src/keys.rs
new file mode 100644
index 000000000..40a5d92b5
--- /dev/null
+++ b/crates/hir_def/src/keys.rs
@@ -0,0 +1,58 @@
1//! keys to be used with `DynMap`
2
3use std::marker::PhantomData;
4
5use hir_expand::{InFile, MacroDefId};
6use rustc_hash::FxHashMap;
7use syntax::{ast, AstNode, AstPtr};
8
9use crate::{
10 dyn_map::{DynMap, Policy},
11 ConstId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, StaticId, StructId, TraitId,
12 TypeAliasId, TypeParamId, UnionId,
13};
14
15pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>;
16
17pub const FUNCTION: Key<ast::Fn, FunctionId> = Key::new();
18pub const CONST: Key<ast::Const, ConstId> = Key::new();
19pub const STATIC: Key<ast::Static, StaticId> = Key::new();
20pub const TYPE_ALIAS: Key<ast::TypeAlias, TypeAliasId> = Key::new();
21pub const IMPL: Key<ast::Impl, ImplId> = Key::new();
22pub const TRAIT: Key<ast::Trait, TraitId> = Key::new();
23pub const STRUCT: Key<ast::Struct, StructId> = Key::new();
24pub const UNION: Key<ast::Union, UnionId> = Key::new();
25pub const ENUM: Key<ast::Enum, EnumId> = Key::new();
26
27pub const VARIANT: Key<ast::Variant, EnumVariantId> = Key::new();
28pub const TUPLE_FIELD: Key<ast::TupleField, FieldId> = Key::new();
29pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new();
30pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new();
31
32pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
33
34/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
35/// equal if they point to exactly the same object.
36///
37/// In general, we do not guarantee that we have exactly one instance of a
38/// syntax tree for each file. We probably should add such guarantee, but, for
39/// the time being, we will use identity-less AstPtr comparison.
40pub struct AstPtrPolicy<AST, ID> {
41 _phantom: PhantomData<(AST, ID)>,
42}
43
44impl<AST: AstNode + 'static, ID: 'static> Policy for AstPtrPolicy<AST, ID> {
45 type K = InFile<AST>;
46 type V = ID;
47 fn insert(map: &mut DynMap, key: InFile<AST>, value: ID) {
48 let key = key.as_ref().map(AstPtr::new);
49 map.map
50 .entry::<FxHashMap<InFile<AstPtr<AST>>, ID>>()
51 .or_insert_with(Default::default)
52 .insert(key, value);
53 }
54 fn get<'a>(map: &'a DynMap, key: &InFile<AST>) -> Option<&'a ID> {
55 let key = key.as_ref().map(AstPtr::new);
56 map.map.get::<FxHashMap<InFile<AstPtr<AST>>, ID>>()?.get(&key)
57 }
58}
diff --git a/crates/hir_def/src/lang_item.rs b/crates/hir_def/src/lang_item.rs
new file mode 100644
index 000000000..063eadccb
--- /dev/null
+++ b/crates/hir_def/src/lang_item.rs
@@ -0,0 +1,174 @@
1//! Collects lang items: items marked with `#[lang = "..."]` attribute.
2//!
3//! This attribute to tell the compiler about semi built-in std library
4//! features, such as Fn family of traits.
5use std::sync::Arc;
6
7use rustc_hash::FxHashMap;
8use syntax::SmolStr;
9
10use crate::{
11 db::DefDatabase, AdtId, AttrDefId, CrateId, EnumId, FunctionId, ImplId, ModuleDefId, ModuleId,
12 StaticId, StructId, TraitId,
13};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub enum LangItemTarget {
17 EnumId(EnumId),
18 FunctionId(FunctionId),
19 ImplDefId(ImplId),
20 StaticId(StaticId),
21 StructId(StructId),
22 TraitId(TraitId),
23}
24
25impl LangItemTarget {
26 pub fn as_enum(self) -> Option<EnumId> {
27 match self {
28 LangItemTarget::EnumId(id) => Some(id),
29 _ => None,
30 }
31 }
32
33 pub fn as_function(self) -> Option<FunctionId> {
34 match self {
35 LangItemTarget::FunctionId(id) => Some(id),
36 _ => None,
37 }
38 }
39
40 pub fn as_impl_def(self) -> Option<ImplId> {
41 match self {
42 LangItemTarget::ImplDefId(id) => Some(id),
43 _ => None,
44 }
45 }
46
47 pub fn as_static(self) -> Option<StaticId> {
48 match self {
49 LangItemTarget::StaticId(id) => Some(id),
50 _ => None,
51 }
52 }
53
54 pub fn as_struct(self) -> Option<StructId> {
55 match self {
56 LangItemTarget::StructId(id) => Some(id),
57 _ => None,
58 }
59 }
60
61 pub fn as_trait(self) -> Option<TraitId> {
62 match self {
63 LangItemTarget::TraitId(id) => Some(id),
64 _ => None,
65 }
66 }
67}
68
69#[derive(Default, Debug, Clone, PartialEq, Eq)]
70pub struct LangItems {
71 items: FxHashMap<SmolStr, LangItemTarget>,
72}
73
74impl LangItems {
75 pub fn target(&self, item: &str) -> Option<LangItemTarget> {
76 self.items.get(item).copied()
77 }
78
79 /// Salsa query. This will look for lang items in a specific crate.
80 pub(crate) fn crate_lang_items_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<LangItems> {
81 let _p = profile::span("crate_lang_items_query");
82
83 let mut lang_items = LangItems::default();
84
85 let crate_def_map = db.crate_def_map(krate);
86
87 crate_def_map
88 .modules
89 .iter()
90 .filter_map(|(local_id, _)| db.module_lang_items(ModuleId { krate, local_id }))
91 .for_each(|it| lang_items.items.extend(it.items.iter().map(|(k, v)| (k.clone(), *v))));
92
93 Arc::new(lang_items)
94 }
95
96 pub(crate) fn module_lang_items_query(
97 db: &dyn DefDatabase,
98 module: ModuleId,
99 ) -> Option<Arc<LangItems>> {
100 let _p = profile::span("module_lang_items_query");
101 let mut lang_items = LangItems::default();
102 lang_items.collect_lang_items(db, module);
103 if lang_items.items.is_empty() {
104 None
105 } else {
106 Some(Arc::new(lang_items))
107 }
108 }
109
110 /// Salsa query. Look for a lang item, starting from the specified crate and recursively
111 /// traversing its dependencies.
112 pub(crate) fn lang_item_query(
113 db: &dyn DefDatabase,
114 start_crate: CrateId,
115 item: SmolStr,
116 ) -> Option<LangItemTarget> {
117 let _p = profile::span("lang_item_query");
118 let lang_items = db.crate_lang_items(start_crate);
119 let start_crate_target = lang_items.items.get(&item);
120 if let Some(target) = start_crate_target {
121 return Some(*target);
122 }
123 db.crate_graph()[start_crate]
124 .dependencies
125 .iter()
126 .find_map(|dep| db.lang_item(dep.crate_id, item.clone()))
127 }
128
129 fn collect_lang_items(&mut self, db: &dyn DefDatabase, module: ModuleId) {
130 // Look for impl targets
131 let def_map = db.crate_def_map(module.krate);
132 let module_data = &def_map[module.local_id];
133 for impl_def in module_data.scope.impls() {
134 self.collect_lang_item(db, impl_def, LangItemTarget::ImplDefId)
135 }
136
137 for def in module_data.scope.declarations() {
138 match def {
139 ModuleDefId::TraitId(trait_) => {
140 self.collect_lang_item(db, trait_, LangItemTarget::TraitId)
141 }
142 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
143 self.collect_lang_item(db, e, LangItemTarget::EnumId)
144 }
145 ModuleDefId::AdtId(AdtId::StructId(s)) => {
146 self.collect_lang_item(db, s, LangItemTarget::StructId)
147 }
148 ModuleDefId::FunctionId(f) => {
149 self.collect_lang_item(db, f, LangItemTarget::FunctionId)
150 }
151 ModuleDefId::StaticId(s) => self.collect_lang_item(db, s, LangItemTarget::StaticId),
152 _ => {}
153 }
154 }
155 }
156
157 fn collect_lang_item<T>(
158 &mut self,
159 db: &dyn DefDatabase,
160 item: T,
161 constructor: fn(T) -> LangItemTarget,
162 ) where
163 T: Into<AttrDefId> + Copy,
164 {
165 if let Some(lang_item_name) = lang_attr(db, item) {
166 self.items.entry(lang_item_name).or_insert_with(|| constructor(item));
167 }
168 }
169}
170
171pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<SmolStr> {
172 let attrs = db.attrs(item.into());
173 attrs.by_key("lang").string_value().cloned()
174}
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
new file mode 100644
index 000000000..f24a1dd77
--- /dev/null
+++ b/crates/hir_def/src/lib.rs
@@ -0,0 +1,541 @@
1//! `hir_def` crate contains everything between macro expansion and type
2//! inference.
3//!
4//! It defines various items (structs, enums, traits) which comprises Rust code,
5//! as well as an algorithm for resolving paths to such entities.
6//!
7//! Note that `hir_def` is a work in progress, so not all of the above is
8//! actually true.
9
10#[allow(unused)]
11macro_rules! eprintln {
12 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
13}
14
15pub mod db;
16
17pub mod attr;
18pub mod path;
19pub mod type_ref;
20pub mod builtin_type;
21pub mod diagnostics;
22pub mod per_ns;
23pub mod item_scope;
24
25pub mod dyn_map;
26pub mod keys;
27
28pub mod item_tree;
29
30pub mod adt;
31pub mod data;
32pub mod generics;
33pub mod lang_item;
34pub mod docs;
35
36pub mod expr;
37pub mod body;
38pub mod resolver;
39
40mod trace;
41pub mod nameres;
42
43pub mod src;
44pub mod child_by_source;
45
46pub mod visibility;
47pub mod find_path;
48pub mod import_map;
49
50#[cfg(test)]
51mod test_db;
52
53use std::hash::{Hash, Hasher};
54
55use arena::Idx;
56use base_db::{impl_intern_key, salsa, CrateId};
57use hir_expand::{
58 ast_id_map::FileAstId, eager::expand_eager_macro, hygiene::Hygiene, AstId, HirFileId, InFile,
59 MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
60};
61use syntax::ast;
62
63use crate::builtin_type::BuiltinType;
64use item_tree::{
65 Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
66 TypeAlias, Union,
67};
68use stdx::impl_from;
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
71pub struct ModuleId {
72 pub krate: CrateId,
73 pub local_id: LocalModuleId,
74}
75
76/// An ID of a module, **local** to a specific crate
77pub type LocalModuleId = Idx<nameres::ModuleData>;
78
79#[derive(Debug)]
80pub struct ItemLoc<N: ItemTreeNode> {
81 pub container: ContainerId,
82 pub id: ItemTreeId<N>,
83}
84
85impl<N: ItemTreeNode> Clone for ItemLoc<N> {
86 fn clone(&self) -> Self {
87 Self { container: self.container, id: self.id }
88 }
89}
90
91impl<N: ItemTreeNode> Copy for ItemLoc<N> {}
92
93impl<N: ItemTreeNode> PartialEq for ItemLoc<N> {
94 fn eq(&self, other: &Self) -> bool {
95 self.container == other.container && self.id == other.id
96 }
97}
98
99impl<N: ItemTreeNode> Eq for ItemLoc<N> {}
100
101impl<N: ItemTreeNode> Hash for ItemLoc<N> {
102 fn hash<H: Hasher>(&self, state: &mut H) {
103 self.container.hash(state);
104 self.id.hash(state);
105 }
106}
107
108#[derive(Debug)]
109pub struct AssocItemLoc<N: ItemTreeNode> {
110 pub container: AssocContainerId,
111 pub id: ItemTreeId<N>,
112}
113
114impl<N: ItemTreeNode> Clone for AssocItemLoc<N> {
115 fn clone(&self) -> Self {
116 Self { container: self.container, id: self.id }
117 }
118}
119
120impl<N: ItemTreeNode> Copy for AssocItemLoc<N> {}
121
122impl<N: ItemTreeNode> PartialEq for AssocItemLoc<N> {
123 fn eq(&self, other: &Self) -> bool {
124 self.container == other.container && self.id == other.id
125 }
126}
127
128impl<N: ItemTreeNode> Eq for AssocItemLoc<N> {}
129
130impl<N: ItemTreeNode> Hash for AssocItemLoc<N> {
131 fn hash<H: Hasher>(&self, state: &mut H) {
132 self.container.hash(state);
133 self.id.hash(state);
134 }
135}
136
137macro_rules! impl_intern {
138 ($id:ident, $loc:ident, $intern:ident, $lookup:ident) => {
139 impl_intern_key!($id);
140
141 impl Intern for $loc {
142 type ID = $id;
143 fn intern(self, db: &dyn db::DefDatabase) -> $id {
144 db.$intern(self)
145 }
146 }
147
148 impl Lookup for $id {
149 type Data = $loc;
150 fn lookup(&self, db: &dyn db::DefDatabase) -> $loc {
151 db.$lookup(*self)
152 }
153 }
154 };
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
158pub struct FunctionId(salsa::InternId);
159type FunctionLoc = AssocItemLoc<Function>;
160impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
163pub struct StructId(salsa::InternId);
164type StructLoc = ItemLoc<Struct>;
165impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
168pub struct UnionId(salsa::InternId);
169pub type UnionLoc = ItemLoc<Union>;
170impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
173pub struct EnumId(salsa::InternId);
174pub type EnumLoc = ItemLoc<Enum>;
175impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
176
177// FIXME: rename to `VariantId`, only enums can ave variants
178#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
179pub struct EnumVariantId {
180 pub parent: EnumId,
181 pub local_id: LocalEnumVariantId,
182}
183
184pub type LocalEnumVariantId = Idx<adt::EnumVariantData>;
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
187pub struct FieldId {
188 pub parent: VariantId,
189 pub local_id: LocalFieldId,
190}
191
192pub type LocalFieldId = Idx<adt::FieldData>;
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
195pub struct ConstId(salsa::InternId);
196type ConstLoc = AssocItemLoc<Const>;
197impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
200pub struct StaticId(salsa::InternId);
201pub type StaticLoc = ItemLoc<Static>;
202impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
205pub struct TraitId(salsa::InternId);
206pub type TraitLoc = ItemLoc<Trait>;
207impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
210pub struct TypeAliasId(salsa::InternId);
211type TypeAliasLoc = AssocItemLoc<TypeAlias>;
212impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
215pub struct ImplId(salsa::InternId);
216type ImplLoc = ItemLoc<Impl>;
217impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
220pub struct TypeParamId {
221 pub parent: GenericDefId,
222 pub local_id: LocalTypeParamId,
223}
224
225pub type LocalTypeParamId = Idx<generics::TypeParamData>;
226
227#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
228pub enum ContainerId {
229 ModuleId(ModuleId),
230 DefWithBodyId(DefWithBodyId),
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
234pub enum AssocContainerId {
235 ContainerId(ContainerId),
236 ImplId(ImplId),
237 TraitId(TraitId),
238}
239impl_from!(ContainerId for AssocContainerId);
240
241/// A Data Type
242#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
243pub enum AdtId {
244 StructId(StructId),
245 UnionId(UnionId),
246 EnumId(EnumId),
247}
248impl_from!(StructId, UnionId, EnumId for AdtId);
249
250/// The defs which can be visible in the module.
251#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
252pub enum ModuleDefId {
253 ModuleId(ModuleId),
254 FunctionId(FunctionId),
255 AdtId(AdtId),
256 // Can't be directly declared, but can be imported.
257 EnumVariantId(EnumVariantId),
258 ConstId(ConstId),
259 StaticId(StaticId),
260 TraitId(TraitId),
261 TypeAliasId(TypeAliasId),
262 BuiltinType(BuiltinType),
263}
264impl_from!(
265 ModuleId,
266 FunctionId,
267 AdtId(StructId, EnumId, UnionId),
268 EnumVariantId,
269 ConstId,
270 StaticId,
271 TraitId,
272 TypeAliasId,
273 BuiltinType
274 for ModuleDefId
275);
276
277/// The defs which have a body.
278#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
279pub enum DefWithBodyId {
280 FunctionId(FunctionId),
281 StaticId(StaticId),
282 ConstId(ConstId),
283}
284
285impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId);
286
287#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
288pub enum AssocItemId {
289 FunctionId(FunctionId),
290 ConstId(ConstId),
291 TypeAliasId(TypeAliasId),
292}
293// FIXME: not every function, ... is actually an assoc item. maybe we should make
294// sure that you can only turn actual assoc items into AssocItemIds. This would
295// require not implementing From, and instead having some checked way of
296// casting them, and somehow making the constructors private, which would be annoying.
297impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId);
298
299#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
300pub enum GenericDefId {
301 FunctionId(FunctionId),
302 AdtId(AdtId),
303 TraitId(TraitId),
304 TypeAliasId(TypeAliasId),
305 ImplId(ImplId),
306 // enum variants cannot have generics themselves, but their parent enums
307 // can, and this makes some code easier to write
308 EnumVariantId(EnumVariantId),
309 // consts can have type parameters from their parents (i.e. associated consts of traits)
310 ConstId(ConstId),
311}
312impl_from!(
313 FunctionId,
314 AdtId(StructId, EnumId, UnionId),
315 TraitId,
316 TypeAliasId,
317 ImplId,
318 EnumVariantId,
319 ConstId
320 for GenericDefId
321);
322
323impl From<AssocItemId> for GenericDefId {
324 fn from(item: AssocItemId) -> Self {
325 match item {
326 AssocItemId::FunctionId(f) => f.into(),
327 AssocItemId::ConstId(c) => c.into(),
328 AssocItemId::TypeAliasId(t) => t.into(),
329 }
330 }
331}
332
333#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
334pub enum AttrDefId {
335 ModuleId(ModuleId),
336 FieldId(FieldId),
337 AdtId(AdtId),
338 FunctionId(FunctionId),
339 EnumVariantId(EnumVariantId),
340 StaticId(StaticId),
341 ConstId(ConstId),
342 TraitId(TraitId),
343 TypeAliasId(TypeAliasId),
344 MacroDefId(MacroDefId),
345 ImplId(ImplId),
346}
347
348impl_from!(
349 ModuleId,
350 FieldId,
351 AdtId(StructId, EnumId, UnionId),
352 EnumVariantId,
353 StaticId,
354 ConstId,
355 FunctionId,
356 TraitId,
357 TypeAliasId,
358 MacroDefId,
359 ImplId
360 for AttrDefId
361);
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
364pub enum VariantId {
365 EnumVariantId(EnumVariantId),
366 StructId(StructId),
367 UnionId(UnionId),
368}
369impl_from!(EnumVariantId, StructId, UnionId for VariantId);
370
371trait Intern {
372 type ID;
373 fn intern(self, db: &dyn db::DefDatabase) -> Self::ID;
374}
375
376pub trait Lookup {
377 type Data;
378 fn lookup(&self, db: &dyn db::DefDatabase) -> Self::Data;
379}
380
381pub trait HasModule {
382 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId;
383}
384
385impl HasModule for ContainerId {
386 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
387 match *self {
388 ContainerId::ModuleId(it) => it,
389 ContainerId::DefWithBodyId(it) => it.module(db),
390 }
391 }
392}
393
394impl HasModule for AssocContainerId {
395 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
396 match *self {
397 AssocContainerId::ContainerId(it) => it.module(db),
398 AssocContainerId::ImplId(it) => it.lookup(db).container.module(db),
399 AssocContainerId::TraitId(it) => it.lookup(db).container.module(db),
400 }
401 }
402}
403
404impl<N: ItemTreeNode> HasModule for AssocItemLoc<N> {
405 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
406 self.container.module(db)
407 }
408}
409
410impl HasModule for AdtId {
411 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
412 match self {
413 AdtId::StructId(it) => it.lookup(db).container,
414 AdtId::UnionId(it) => it.lookup(db).container,
415 AdtId::EnumId(it) => it.lookup(db).container,
416 }
417 .module(db)
418 }
419}
420
421impl HasModule for DefWithBodyId {
422 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
423 match self {
424 DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
425 DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
426 DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
427 }
428 }
429}
430
431impl DefWithBodyId {
432 pub fn as_mod_item(self, db: &dyn db::DefDatabase) -> ModItem {
433 match self {
434 DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
435 DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
436 DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
437 }
438 }
439}
440
441impl HasModule for GenericDefId {
442 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
443 match self {
444 GenericDefId::FunctionId(it) => it.lookup(db).module(db),
445 GenericDefId::AdtId(it) => it.module(db),
446 GenericDefId::TraitId(it) => it.lookup(db).container.module(db),
447 GenericDefId::TypeAliasId(it) => it.lookup(db).module(db),
448 GenericDefId::ImplId(it) => it.lookup(db).container.module(db),
449 GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container.module(db),
450 GenericDefId::ConstId(it) => it.lookup(db).module(db),
451 }
452 }
453}
454
455impl HasModule for StaticLoc {
456 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
457 self.container.module(db)
458 }
459}
460
461/// A helper trait for converting to MacroCallId
462pub trait AsMacroCall {
463 fn as_call_id(
464 &self,
465 db: &dyn db::DefDatabase,
466 krate: CrateId,
467 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
468 ) -> Option<MacroCallId>;
469}
470
471impl AsMacroCall for InFile<&ast::MacroCall> {
472 fn as_call_id(
473 &self,
474 db: &dyn db::DefDatabase,
475 krate: CrateId,
476 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
477 ) -> Option<MacroCallId> {
478 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
479 let h = Hygiene::new(db.upcast(), self.file_id);
480 let path = path::ModPath::from_src(self.value.path()?, &h)?;
481
482 AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver)
483 }
484}
485
486/// Helper wrapper for `AstId` with `ModPath`
487#[derive(Clone, Debug, Eq, PartialEq)]
488struct AstIdWithPath<T: ast::AstNode> {
489 pub ast_id: AstId<T>,
490 pub path: path::ModPath,
491}
492
493impl<T: ast::AstNode> AstIdWithPath<T> {
494 pub fn new(file_id: HirFileId, ast_id: FileAstId<T>, path: path::ModPath) -> AstIdWithPath<T> {
495 AstIdWithPath { ast_id: AstId::new(file_id, ast_id), path }
496 }
497}
498
499impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
500 fn as_call_id(
501 &self,
502 db: &dyn db::DefDatabase,
503 krate: CrateId,
504 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
505 ) -> Option<MacroCallId> {
506 let def: MacroDefId = resolver(self.path.clone())?;
507
508 if let MacroDefKind::BuiltInEager(_) = def.kind {
509 let macro_call = InFile::new(self.ast_id.file_id, self.ast_id.to_node(db.upcast()));
510 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id);
511
512 Some(
513 expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| {
514 resolver(path::ModPath::from_src(path, &hygiene)?)
515 })?
516 .into(),
517 )
518 } else {
519 Some(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(self.ast_id)).into())
520 }
521 }
522}
523
524impl AsMacroCall for AstIdWithPath<ast::Item> {
525 fn as_call_id(
526 &self,
527 db: &dyn db::DefDatabase,
528 krate: CrateId,
529 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
530 ) -> Option<MacroCallId> {
531 let def = resolver(self.path.clone())?;
532 Some(
533 def.as_lazy_macro(
534 db.upcast(),
535 krate,
536 MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()),
537 )
538 .into(),
539 )
540 }
541}
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
new file mode 100644
index 000000000..bf302172d
--- /dev/null
+++ b/crates/hir_def/src/nameres.rs
@@ -0,0 +1,326 @@
1//! This module implements import-resolution/macro expansion algorithm.
2//!
3//! The result of this module is `CrateDefMap`: a data structure which contains:
4//!
5//! * a tree of modules for the crate
6//! * for each module, a set of items visible in the module (directly declared
7//! or imported)
8//!
9//! Note that `CrateDefMap` contains fully macro expanded code.
10//!
11//! Computing `CrateDefMap` can be partitioned into several logically
12//! independent "phases". The phases are mutually recursive though, there's no
13//! strict ordering.
14//!
15//! ## Collecting RawItems
16//!
17//! This happens in the `raw` module, which parses a single source file into a
18//! set of top-level items. Nested imports are desugared to flat imports in this
19//! phase. Macro calls are represented as a triple of (Path, Option<Name>,
20//! TokenTree).
21//!
22//! ## Collecting Modules
23//!
24//! This happens in the `collector` module. In this phase, we recursively walk
25//! tree of modules, collect raw items from submodules, populate module scopes
26//! with defined items (so, we assign item ids in this phase) and record the set
27//! of unresolved imports and macros.
28//!
29//! While we walk tree of modules, we also record macro_rules definitions and
30//! expand calls to macro_rules defined macros.
31//!
32//! ## Resolving Imports
33//!
34//! We maintain a list of currently unresolved imports. On every iteration, we
35//! try to resolve some imports from this list. If the import is resolved, we
36//! record it, by adding an item to current module scope and, if necessary, by
37//! recursively populating glob imports.
38//!
39//! ## Resolving Macros
40//!
41//! macro_rules from the same crate use a global mutable namespace. We expand
42//! them immediately, when we collect modules.
43//!
44//! Macros from other crates (including proc-macros) can be used with
45//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of
46//! unexpanded macros. On every iteration, we try to resolve each macro call
47//! path and, upon success, we run macro expansion and "collect module" phase on
48//! the result
49
50mod collector;
51mod mod_resolution;
52mod path_resolution;
53
54#[cfg(test)]
55mod tests;
56
57use std::sync::Arc;
58
59use arena::Arena;
60use base_db::{CrateId, Edition, FileId};
61use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile};
62use rustc_hash::FxHashMap;
63use stdx::format_to;
64use syntax::ast;
65
66use crate::{
67 db::DefDatabase,
68 item_scope::{BuiltinShadowMode, ItemScope},
69 nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode},
70 path::ModPath,
71 per_ns::PerNs,
72 AstId, LocalModuleId, ModuleDefId, ModuleId,
73};
74
75/// Contains all top-level defs from a macro-expanded crate
76#[derive(Debug, PartialEq, Eq)]
77pub struct CrateDefMap {
78 pub root: LocalModuleId,
79 pub modules: Arena<ModuleData>,
80 pub(crate) krate: CrateId,
81 /// The prelude module for this crate. This either comes from an import
82 /// marked with the `prelude_import` attribute, or (in the normal case) from
83 /// a dependency (`std` or `core`).
84 pub(crate) prelude: Option<ModuleId>,
85 pub(crate) extern_prelude: FxHashMap<Name, ModuleDefId>,
86
87 edition: Edition,
88 diagnostics: Vec<DefDiagnostic>,
89}
90
91impl std::ops::Index<LocalModuleId> for CrateDefMap {
92 type Output = ModuleData;
93 fn index(&self, id: LocalModuleId) -> &ModuleData {
94 &self.modules[id]
95 }
96}
97
98#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
99pub enum ModuleOrigin {
100 CrateRoot {
101 definition: FileId,
102 },
103 /// Note that non-inline modules, by definition, live inside non-macro file.
104 File {
105 is_mod_rs: bool,
106 declaration: AstId<ast::Module>,
107 definition: FileId,
108 },
109 Inline {
110 definition: AstId<ast::Module>,
111 },
112}
113
114impl Default for ModuleOrigin {
115 fn default() -> Self {
116 ModuleOrigin::CrateRoot { definition: FileId(0) }
117 }
118}
119
120impl ModuleOrigin {
121 fn declaration(&self) -> Option<AstId<ast::Module>> {
122 match self {
123 ModuleOrigin::File { declaration: module, .. }
124 | ModuleOrigin::Inline { definition: module, .. } => Some(*module),
125 ModuleOrigin::CrateRoot { .. } => None,
126 }
127 }
128
129 pub fn file_id(&self) -> Option<FileId> {
130 match self {
131 ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => {
132 Some(*definition)
133 }
134 _ => None,
135 }
136 }
137
138 pub fn is_inline(&self) -> bool {
139 match self {
140 ModuleOrigin::Inline { .. } => true,
141 ModuleOrigin::CrateRoot { .. } | ModuleOrigin::File { .. } => false,
142 }
143 }
144
145 /// Returns a node which defines this module.
146 /// That is, a file or a `mod foo {}` with items.
147 fn definition_source(&self, db: &dyn DefDatabase) -> InFile<ModuleSource> {
148 match self {
149 ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => {
150 let file_id = *definition;
151 let sf = db.parse(file_id).tree();
152 InFile::new(file_id.into(), ModuleSource::SourceFile(sf))
153 }
154 ModuleOrigin::Inline { definition } => InFile::new(
155 definition.file_id,
156 ModuleSource::Module(definition.to_node(db.upcast())),
157 ),
158 }
159 }
160}
161
162#[derive(Default, Debug, PartialEq, Eq)]
163pub struct ModuleData {
164 pub parent: Option<LocalModuleId>,
165 pub children: FxHashMap<Name, LocalModuleId>,
166 pub scope: ItemScope,
167
168 /// Where does this module come from?
169 pub origin: ModuleOrigin,
170}
171
172impl CrateDefMap {
173 pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
174 let _p = profile::span("crate_def_map_query").detail(|| {
175 db.crate_graph()[krate]
176 .display_name
177 .as_ref()
178 .map(ToString::to_string)
179 .unwrap_or_default()
180 });
181 let def_map = {
182 let edition = db.crate_graph()[krate].edition;
183 let mut modules: Arena<ModuleData> = Arena::default();
184 let root = modules.alloc(ModuleData::default());
185 CrateDefMap {
186 krate,
187 edition,
188 extern_prelude: FxHashMap::default(),
189 prelude: None,
190 root,
191 modules,
192 diagnostics: Vec::new(),
193 }
194 };
195 let def_map = collector::collect_defs(db, def_map);
196 Arc::new(def_map)
197 }
198
199 pub fn add_diagnostics(
200 &self,
201 db: &dyn DefDatabase,
202 module: LocalModuleId,
203 sink: &mut DiagnosticSink,
204 ) {
205 self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink))
206 }
207
208 pub fn modules_for_file(&self, file_id: FileId) -> impl Iterator<Item = LocalModuleId> + '_ {
209 self.modules
210 .iter()
211 .filter(move |(_id, data)| data.origin.file_id() == Some(file_id))
212 .map(|(id, _data)| id)
213 }
214
215 pub(crate) fn resolve_path(
216 &self,
217 db: &dyn DefDatabase,
218 original_module: LocalModuleId,
219 path: &ModPath,
220 shadow: BuiltinShadowMode,
221 ) -> (PerNs, Option<usize>) {
222 let res =
223 self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow);
224 (res.resolved_def, res.segment_index)
225 }
226
227 // FIXME: this can use some more human-readable format (ideally, an IR
228 // even), as this should be a great debugging aid.
229 pub fn dump(&self) -> String {
230 let mut buf = String::new();
231 go(&mut buf, self, "crate", self.root);
232 return buf;
233
234 fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: LocalModuleId) {
235 format_to!(buf, "{}\n", path);
236
237 let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect();
238 entries.sort_by_key(|(name, _)| name.clone());
239
240 for (name, def) in entries {
241 format_to!(buf, "{}:", name.map_or("_".to_string(), |name| name.to_string()));
242
243 if def.types.is_some() {
244 buf.push_str(" t");
245 }
246 if def.values.is_some() {
247 buf.push_str(" v");
248 }
249 if def.macros.is_some() {
250 buf.push_str(" m");
251 }
252 if def.is_none() {
253 buf.push_str(" _");
254 }
255
256 buf.push_str("\n");
257 }
258
259 for (name, child) in map.modules[module].children.iter() {
260 let path = format!("{}::{}", path, name);
261 buf.push('\n');
262 go(buf, map, &path, *child);
263 }
264 }
265 }
266}
267
268impl ModuleData {
269 /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
270 pub fn definition_source(&self, db: &dyn DefDatabase) -> InFile<ModuleSource> {
271 self.origin.definition_source(db)
272 }
273
274 /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
275 /// `None` for the crate root or block.
276 pub fn declaration_source(&self, db: &dyn DefDatabase) -> Option<InFile<ast::Module>> {
277 let decl = self.origin.declaration()?;
278 let value = decl.to_node(db.upcast());
279 Some(InFile { file_id: decl.file_id, value })
280 }
281}
282
283#[derive(Debug, Clone, PartialEq, Eq)]
284pub enum ModuleSource {
285 SourceFile(ast::SourceFile),
286 Module(ast::Module),
287}
288
289mod diagnostics {
290 use hir_expand::diagnostics::DiagnosticSink;
291 use syntax::{ast, AstPtr};
292
293 use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId};
294
295 #[derive(Debug, PartialEq, Eq)]
296 pub(super) enum DefDiagnostic {
297 UnresolvedModule {
298 module: LocalModuleId,
299 declaration: AstId<ast::Module>,
300 candidate: String,
301 },
302 }
303
304 impl DefDiagnostic {
305 pub(super) fn add_to(
306 &self,
307 db: &dyn DefDatabase,
308 target_module: LocalModuleId,
309 sink: &mut DiagnosticSink,
310 ) {
311 match self {
312 DefDiagnostic::UnresolvedModule { module, declaration, candidate } => {
313 if *module != target_module {
314 return;
315 }
316 let decl = declaration.to_node(db.upcast());
317 sink.push(UnresolvedModule {
318 file: declaration.file_id,
319 decl: AstPtr::new(&decl),
320 candidate: candidate.clone(),
321 })
322 }
323 }
324 }
325 }
326}
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
new file mode 100644
index 000000000..3e99c8773
--- /dev/null
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -0,0 +1,1279 @@
1//! The core of the module-level name resolution algorithm.
2//!
3//! `DefCollector::collect` contains the fixed-point iteration loop which
4//! resolves imports and expands macros.
5
6use base_db::{CrateId, FileId, ProcMacroId};
7use cfg::CfgOptions;
8use hir_expand::{
9 ast_id_map::FileAstId,
10 builtin_derive::find_builtin_derive,
11 builtin_macro::find_builtin_macro,
12 name::{name, AsName, Name},
13 proc_macro::ProcMacroExpander,
14 HirFileId, MacroCallId, MacroDefId, MacroDefKind,
15};
16use rustc_hash::FxHashMap;
17use syntax::ast;
18use test_utils::mark;
19
20use crate::{
21 attr::Attrs,
22 db::DefDatabase,
23 item_scope::{ImportType, PerNsGlobImports},
24 item_tree::{
25 self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind,
26 },
27 nameres::{
28 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
29 BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
30 },
31 path::{ImportAlias, ModPath, PathKind},
32 per_ns::PerNs,
33 visibility::{RawVisibility, Visibility},
34 AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId,
35 FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc,
36 TraitLoc, TypeAliasLoc, UnionLoc,
37};
38
39const GLOB_RECURSION_LIMIT: usize = 100;
40const EXPANSION_DEPTH_LIMIT: usize = 128;
41const FIXED_POINT_LIMIT: usize = 8192;
42
43pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap {
44 let crate_graph = db.crate_graph();
45
46 // populate external prelude
47 for dep in &crate_graph[def_map.krate].dependencies {
48 log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
49 let dep_def_map = db.crate_def_map(dep.crate_id);
50 def_map.extern_prelude.insert(
51 dep.as_name(),
52 ModuleId { krate: dep.crate_id, local_id: dep_def_map.root }.into(),
53 );
54
55 // look for the prelude
56 // If the dependency defines a prelude, we overwrite an already defined
57 // prelude. This is necessary to import the "std" prelude if a crate
58 // depends on both "core" and "std".
59 if dep_def_map.prelude.is_some() {
60 def_map.prelude = dep_def_map.prelude;
61 }
62 }
63
64 let cfg_options = &crate_graph[def_map.krate].cfg_options;
65 let proc_macros = &crate_graph[def_map.krate].proc_macro;
66 let proc_macros = proc_macros
67 .iter()
68 .enumerate()
69 .map(|(idx, it)| {
70 // FIXME: a hacky way to create a Name from string.
71 let name = tt::Ident { text: it.name.clone(), id: tt::TokenId::unspecified() };
72 (name.as_name(), ProcMacroExpander::new(def_map.krate, ProcMacroId(idx as u32)))
73 })
74 .collect();
75
76 let mut collector = DefCollector {
77 db,
78 def_map,
79 glob_imports: FxHashMap::default(),
80 unresolved_imports: Vec::new(),
81 resolved_imports: Vec::new(),
82
83 unexpanded_macros: Vec::new(),
84 unexpanded_attribute_macros: Vec::new(),
85 mod_dirs: FxHashMap::default(),
86 cfg_options,
87 proc_macros,
88 from_glob_import: Default::default(),
89 };
90 collector.collect();
91 collector.finish()
92}
93
94#[derive(Copy, Clone, Debug, Eq, PartialEq)]
95enum PartialResolvedImport {
96 /// None of any namespaces is resolved
97 Unresolved,
98 /// One of namespaces is resolved
99 Indeterminate(PerNs),
100 /// All namespaces are resolved, OR it is came from other crate
101 Resolved(PerNs),
102}
103
104impl PartialResolvedImport {
105 fn namespaces(&self) -> PerNs {
106 match self {
107 PartialResolvedImport::Unresolved => PerNs::none(),
108 PartialResolvedImport::Indeterminate(ns) => *ns,
109 PartialResolvedImport::Resolved(ns) => *ns,
110 }
111 }
112}
113
114#[derive(Clone, Debug, Eq, PartialEq)]
115struct Import {
116 pub path: ModPath,
117 pub alias: Option<ImportAlias>,
118 pub visibility: RawVisibility,
119 pub is_glob: bool,
120 pub is_prelude: bool,
121 pub is_extern_crate: bool,
122 pub is_macro_use: bool,
123}
124
125impl Import {
126 fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self {
127 let it = &tree[id];
128 let visibility = &tree[it.visibility];
129 Self {
130 path: it.path.clone(),
131 alias: it.alias.clone(),
132 visibility: visibility.clone(),
133 is_glob: it.is_glob,
134 is_prelude: it.is_prelude,
135 is_extern_crate: false,
136 is_macro_use: false,
137 }
138 }
139
140 fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self {
141 let it = &tree[id];
142 let visibility = &tree[it.visibility];
143 Self {
144 path: it.path.clone(),
145 alias: it.alias.clone(),
146 visibility: visibility.clone(),
147 is_glob: false,
148 is_prelude: false,
149 is_extern_crate: true,
150 is_macro_use: it.is_macro_use,
151 }
152 }
153}
154
155#[derive(Clone, Debug, Eq, PartialEq)]
156struct ImportDirective {
157 module_id: LocalModuleId,
158 import: Import,
159 status: PartialResolvedImport,
160}
161
162#[derive(Clone, Debug, Eq, PartialEq)]
163struct MacroDirective {
164 module_id: LocalModuleId,
165 ast_id: AstIdWithPath<ast::MacroCall>,
166 legacy: Option<MacroCallId>,
167 depth: usize,
168}
169
170#[derive(Clone, Debug, Eq, PartialEq)]
171struct DeriveDirective {
172 module_id: LocalModuleId,
173 ast_id: AstIdWithPath<ast::Item>,
174}
175
176struct DefData<'a> {
177 id: ModuleDefId,
178 name: &'a Name,
179 visibility: &'a RawVisibility,
180 has_constructor: bool,
181}
182
183/// Walks the tree of module recursively
184struct DefCollector<'a> {
185 db: &'a dyn DefDatabase,
186 def_map: CrateDefMap,
187 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
188 unresolved_imports: Vec<ImportDirective>,
189 resolved_imports: Vec<ImportDirective>,
190 unexpanded_macros: Vec<MacroDirective>,
191 unexpanded_attribute_macros: Vec<DeriveDirective>,
192 mod_dirs: FxHashMap<LocalModuleId, ModDir>,
193 cfg_options: &'a CfgOptions,
194 proc_macros: Vec<(Name, ProcMacroExpander)>,
195 from_glob_import: PerNsGlobImports,
196}
197
198impl DefCollector<'_> {
199 fn collect(&mut self) {
200 let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
201 let item_tree = self.db.item_tree(file_id.into());
202 let module_id = self.def_map.root;
203 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
204 ModCollector {
205 def_collector: &mut *self,
206 macro_depth: 0,
207 module_id,
208 file_id: file_id.into(),
209 item_tree: &item_tree,
210 mod_dir: ModDir::root(),
211 }
212 .collect(item_tree.top_level_items());
213
214 // main name resolution fixed-point loop.
215 let mut i = 0;
216 loop {
217 self.db.check_canceled();
218 self.resolve_imports();
219
220 match self.resolve_macros() {
221 ReachedFixedPoint::Yes => break,
222 ReachedFixedPoint::No => i += 1,
223 }
224 if i == FIXED_POINT_LIMIT {
225 log::error!("name resolution is stuck");
226 break;
227 }
228 }
229
230 // Resolve all indeterminate resolved imports again
231 // As some of the macros will expand newly import shadowing partial resolved imports
232 // FIXME: We maybe could skip this, if we handle the Indetermine imports in `resolve_imports`
233 // correctly
234 let partial_resolved = self.resolved_imports.iter().filter_map(|directive| {
235 if let PartialResolvedImport::Indeterminate(_) = directive.status {
236 let mut directive = directive.clone();
237 directive.status = PartialResolvedImport::Unresolved;
238 Some(directive)
239 } else {
240 None
241 }
242 });
243 self.unresolved_imports.extend(partial_resolved);
244 self.resolve_imports();
245
246 let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
247 // show unresolved imports in completion, etc
248 for directive in unresolved_imports {
249 self.record_resolved_import(&directive)
250 }
251
252 // Record proc-macros
253 self.collect_proc_macro();
254 }
255
256 fn collect_proc_macro(&mut self) {
257 let proc_macros = std::mem::take(&mut self.proc_macros);
258 for (name, expander) in proc_macros {
259 let krate = self.def_map.krate;
260
261 let macro_id = MacroDefId {
262 ast_id: None,
263 krate: Some(krate),
264 kind: MacroDefKind::CustomDerive(expander),
265 local_inner: false,
266 };
267
268 self.define_proc_macro(name.clone(), macro_id);
269 }
270 }
271
272 /// Define a macro with `macro_rules`.
273 ///
274 /// It will define the macro in legacy textual scope, and if it has `#[macro_export]`,
275 /// then it is also defined in the root module scope.
276 /// You can `use` or invoke it by `crate::macro_name` anywhere, before or after the definition.
277 ///
278 /// It is surprising that the macro will never be in the current module scope.
279 /// These code fails with "unresolved import/macro",
280 /// ```rust,compile_fail
281 /// mod m { macro_rules! foo { () => {} } }
282 /// use m::foo as bar;
283 /// ```
284 ///
285 /// ```rust,compile_fail
286 /// macro_rules! foo { () => {} }
287 /// self::foo!();
288 /// crate::foo!();
289 /// ```
290 ///
291 /// Well, this code compiles, because the plain path `foo` in `use` is searched
292 /// in the legacy textual scope only.
293 /// ```rust
294 /// macro_rules! foo { () => {} }
295 /// use foo as bar;
296 /// ```
297 fn define_macro(
298 &mut self,
299 module_id: LocalModuleId,
300 name: Name,
301 macro_: MacroDefId,
302 export: bool,
303 ) {
304 // Textual scoping
305 self.define_legacy_macro(module_id, name.clone(), macro_);
306
307 // Module scoping
308 // In Rust, `#[macro_export]` macros are unconditionally visible at the
309 // crate root, even if the parent modules is **not** visible.
310 if export {
311 self.update(
312 self.def_map.root,
313 &[(Some(name), PerNs::macros(macro_, Visibility::Public))],
314 Visibility::Public,
315 ImportType::Named,
316 );
317 }
318 }
319
320 /// Define a legacy textual scoped macro in module
321 ///
322 /// We use a map `legacy_macros` to store all legacy textual scoped macros visible per module.
323 /// It will clone all macros from parent legacy scope, whose definition is prior to
324 /// the definition of current module.
325 /// And also, `macro_use` on a module will import all legacy macros visible inside to
326 /// current legacy scope, with possible shadowing.
327 fn define_legacy_macro(&mut self, module_id: LocalModuleId, name: Name, mac: MacroDefId) {
328 // Always shadowing
329 self.def_map.modules[module_id].scope.define_legacy_macro(name, mac);
330 }
331
332 /// Define a proc macro
333 ///
334 /// A proc macro is similar to normal macro scope, but it would not visiable in legacy textual scoped.
335 /// And unconditionally exported.
336 fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) {
337 self.update(
338 self.def_map.root,
339 &[(Some(name), PerNs::macros(macro_, Visibility::Public))],
340 Visibility::Public,
341 ImportType::Named,
342 );
343 }
344
345 /// Import macros from `#[macro_use] extern crate`.
346 fn import_macros_from_extern_crate(
347 &mut self,
348 current_module_id: LocalModuleId,
349 import: &item_tree::ExternCrate,
350 ) {
351 log::debug!(
352 "importing macros from extern crate: {:?} ({:?})",
353 import,
354 self.def_map.edition,
355 );
356
357 let res = self.def_map.resolve_name_in_extern_prelude(
358 &import
359 .path
360 .as_ident()
361 .expect("extern crate should have been desugared to one-element path"),
362 );
363
364 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
365 mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
366 self.import_all_macros_exported(current_module_id, m.krate);
367 }
368 }
369
370 /// Import all exported macros from another crate
371 ///
372 /// Exported macros are just all macros in the root module scope.
373 /// Note that it contains not only all `#[macro_export]` macros, but also all aliases
374 /// created by `use` in the root module, ignoring the visibility of `use`.
375 fn import_all_macros_exported(&mut self, current_module_id: LocalModuleId, krate: CrateId) {
376 let def_map = self.db.crate_def_map(krate);
377 for (name, def) in def_map[def_map.root].scope.macros() {
378 // `macro_use` only bring things into legacy scope.
379 self.define_legacy_macro(current_module_id, name.clone(), def);
380 }
381 }
382
383 /// Import resolution
384 ///
385 /// This is a fix point algorithm. We resolve imports until no forward
386 /// progress in resolving imports is made
387 fn resolve_imports(&mut self) {
388 let mut n_previous_unresolved = self.unresolved_imports.len() + 1;
389
390 while self.unresolved_imports.len() < n_previous_unresolved {
391 n_previous_unresolved = self.unresolved_imports.len();
392 let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
393 for mut directive in imports {
394 directive.status = self.resolve_import(directive.module_id, &directive.import);
395 match directive.status {
396 PartialResolvedImport::Indeterminate(_) => {
397 self.record_resolved_import(&directive);
398 // FIXME: For avoid performance regression,
399 // we consider an imported resolved if it is indeterminate (i.e not all namespace resolved)
400 self.resolved_imports.push(directive)
401 }
402 PartialResolvedImport::Resolved(_) => {
403 self.record_resolved_import(&directive);
404 self.resolved_imports.push(directive)
405 }
406 PartialResolvedImport::Unresolved => {
407 self.unresolved_imports.push(directive);
408 }
409 }
410 }
411 }
412 }
413
414 fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
415 log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
416 if import.is_extern_crate {
417 let res = self.def_map.resolve_name_in_extern_prelude(
418 &import
419 .path
420 .as_ident()
421 .expect("extern crate should have been desugared to one-element path"),
422 );
423 PartialResolvedImport::Resolved(res)
424 } else {
425 let res = self.def_map.resolve_path_fp_with_macro(
426 self.db,
427 ResolveMode::Import,
428 module_id,
429 &import.path,
430 BuiltinShadowMode::Module,
431 );
432
433 let def = res.resolved_def;
434 if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() {
435 return PartialResolvedImport::Unresolved;
436 }
437
438 if let Some(krate) = res.krate {
439 if krate != self.def_map.krate {
440 return PartialResolvedImport::Resolved(def);
441 }
442 }
443
444 // Check whether all namespace is resolved
445 if def.take_types().is_some()
446 && def.take_values().is_some()
447 && def.take_macros().is_some()
448 {
449 PartialResolvedImport::Resolved(def)
450 } else {
451 PartialResolvedImport::Indeterminate(def)
452 }
453 }
454 }
455
456 fn record_resolved_import(&mut self, directive: &ImportDirective) {
457 let module_id = directive.module_id;
458 let import = &directive.import;
459 let def = directive.status.namespaces();
460 let vis = self
461 .def_map
462 .resolve_visibility(self.db, module_id, &directive.import.visibility)
463 .unwrap_or(Visibility::Public);
464
465 if import.is_glob {
466 log::debug!("glob import: {:?}", import);
467 match def.take_types() {
468 Some(ModuleDefId::ModuleId(m)) => {
469 if import.is_prelude {
470 mark::hit!(std_prelude);
471 self.def_map.prelude = Some(m);
472 } else if m.krate != self.def_map.krate {
473 mark::hit!(glob_across_crates);
474 // glob import from other crate => we can just import everything once
475 let item_map = self.db.crate_def_map(m.krate);
476 let scope = &item_map[m.local_id].scope;
477
478 // Module scoped macros is included
479 let items = scope
480 .resolutions()
481 // only keep visible names...
482 .map(|(n, res)| {
483 (n, res.filter_visibility(|v| v.is_visible_from_other_crate()))
484 })
485 .filter(|(_, res)| !res.is_none())
486 .collect::<Vec<_>>();
487
488 self.update(module_id, &items, vis, ImportType::Glob);
489 } else {
490 // glob import from same crate => we do an initial
491 // import, and then need to propagate any further
492 // additions
493 let scope = &self.def_map[m.local_id].scope;
494
495 // Module scoped macros is included
496 let items = scope
497 .resolutions()
498 // only keep visible names...
499 .map(|(n, res)| {
500 (
501 n,
502 res.filter_visibility(|v| {
503 v.is_visible_from_def_map(&self.def_map, module_id)
504 }),
505 )
506 })
507 .filter(|(_, res)| !res.is_none())
508 .collect::<Vec<_>>();
509
510 self.update(module_id, &items, vis, ImportType::Glob);
511 // record the glob import in case we add further items
512 let glob = self.glob_imports.entry(m.local_id).or_default();
513 if !glob.iter().any(|(mid, _)| *mid == module_id) {
514 glob.push((module_id, vis));
515 }
516 }
517 }
518 Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
519 mark::hit!(glob_enum);
520 // glob import from enum => just import all the variants
521
522 // XXX: urgh, so this works by accident! Here, we look at
523 // the enum data, and, in theory, this might require us to
524 // look back at the crate_def_map, creating a cycle. For
525 // example, `enum E { crate::some_macro!(); }`. Luckely, the
526 // only kind of macro that is allowed inside enum is a
527 // `cfg_macro`, and we don't need to run name resolution for
528 // it, but this is sheer luck!
529 let enum_data = self.db.enum_data(e);
530 let resolutions = enum_data
531 .variants
532 .iter()
533 .map(|(local_id, variant_data)| {
534 let name = variant_data.name.clone();
535 let variant = EnumVariantId { parent: e, local_id };
536 let res = PerNs::both(variant.into(), variant.into(), vis);
537 (Some(name), res)
538 })
539 .collect::<Vec<_>>();
540 self.update(module_id, &resolutions, vis, ImportType::Glob);
541 }
542 Some(d) => {
543 log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
544 }
545 None => {
546 log::debug!("glob import {:?} didn't resolve as type", import);
547 }
548 }
549 } else {
550 match import.path.segments.last() {
551 Some(last_segment) => {
552 let name = match &import.alias {
553 Some(ImportAlias::Alias(name)) => Some(name.clone()),
554 Some(ImportAlias::Underscore) => None,
555 None => Some(last_segment.clone()),
556 };
557 log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
558
559 // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
560 if import.is_extern_crate && module_id == self.def_map.root {
561 if let (Some(def), Some(name)) = (def.take_types(), name.as_ref()) {
562 self.def_map.extern_prelude.insert(name.clone(), def);
563 }
564 }
565
566 self.update(module_id, &[(name, def)], vis, ImportType::Named);
567 }
568 None => mark::hit!(bogus_paths),
569 }
570 }
571 }
572
573 fn update(
574 &mut self,
575 module_id: LocalModuleId,
576 resolutions: &[(Option<Name>, PerNs)],
577 vis: Visibility,
578 import_type: ImportType,
579 ) {
580 self.db.check_canceled();
581 self.update_recursive(module_id, resolutions, vis, import_type, 0)
582 }
583
584 fn update_recursive(
585 &mut self,
586 module_id: LocalModuleId,
587 resolutions: &[(Option<Name>, PerNs)],
588 // All resolutions are imported with this visibility; the visibilies in
589 // the `PerNs` values are ignored and overwritten
590 vis: Visibility,
591 import_type: ImportType,
592 depth: usize,
593 ) {
594 if depth > GLOB_RECURSION_LIMIT {
595 // prevent stack overflows (but this shouldn't be possible)
596 panic!("infinite recursion in glob imports!");
597 }
598 let mut changed = false;
599
600 for (name, res) in resolutions {
601 match name {
602 Some(name) => {
603 let scope = &mut self.def_map.modules[module_id].scope;
604 changed |= scope.push_res_with_import(
605 &mut self.from_glob_import,
606 (module_id, name.clone()),
607 res.with_visibility(vis),
608 import_type,
609 );
610 }
611 None => {
612 let tr = match res.take_types() {
613 Some(ModuleDefId::TraitId(tr)) => tr,
614 Some(other) => {
615 log::debug!("non-trait `_` import of {:?}", other);
616 continue;
617 }
618 None => continue,
619 };
620 let old_vis = self.def_map.modules[module_id].scope.unnamed_trait_vis(tr);
621 let should_update = match old_vis {
622 None => true,
623 Some(old_vis) => {
624 let max_vis = old_vis.max(vis, &self.def_map).unwrap_or_else(|| {
625 panic!("`Tr as _` imports with unrelated visibilities {:?} and {:?} (trait {:?})", old_vis, vis, tr);
626 });
627
628 if max_vis == old_vis {
629 false
630 } else {
631 mark::hit!(upgrade_underscore_visibility);
632 true
633 }
634 }
635 };
636
637 if should_update {
638 changed = true;
639 self.def_map.modules[module_id].scope.push_unnamed_trait(tr, vis);
640 }
641 }
642 }
643 }
644
645 if !changed {
646 return;
647 }
648 let glob_imports = self
649 .glob_imports
650 .get(&module_id)
651 .into_iter()
652 .flat_map(|v| v.iter())
653 .filter(|(glob_importing_module, _)| {
654 // we know all resolutions have the same visibility (`vis`), so we
655 // just need to check that once
656 vis.is_visible_from_def_map(&self.def_map, *glob_importing_module)
657 })
658 .cloned()
659 .collect::<Vec<_>>();
660
661 for (glob_importing_module, glob_import_vis) in glob_imports {
662 self.update_recursive(
663 glob_importing_module,
664 resolutions,
665 glob_import_vis,
666 ImportType::Glob,
667 depth + 1,
668 );
669 }
670 }
671
672 fn resolve_macros(&mut self) -> ReachedFixedPoint {
673 let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new());
674 let mut attribute_macros =
675 std::mem::replace(&mut self.unexpanded_attribute_macros, Vec::new());
676 let mut resolved = Vec::new();
677 let mut res = ReachedFixedPoint::Yes;
678 macros.retain(|directive| {
679 if let Some(call_id) = directive.legacy {
680 res = ReachedFixedPoint::No;
681 resolved.push((directive.module_id, call_id, directive.depth));
682 return false;
683 }
684
685 if let Some(call_id) =
686 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| {
687 let resolved_res = self.def_map.resolve_path_fp_with_macro(
688 self.db,
689 ResolveMode::Other,
690 directive.module_id,
691 &path,
692 BuiltinShadowMode::Module,
693 );
694 resolved_res.resolved_def.take_macros()
695 })
696 {
697 resolved.push((directive.module_id, call_id, directive.depth));
698 res = ReachedFixedPoint::No;
699 return false;
700 }
701
702 true
703 });
704 attribute_macros.retain(|directive| {
705 if let Some(call_id) =
706 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| {
707 self.resolve_attribute_macro(&directive, &path)
708 })
709 {
710 resolved.push((directive.module_id, call_id, 0));
711 res = ReachedFixedPoint::No;
712 return false;
713 }
714
715 true
716 });
717
718 self.unexpanded_macros = macros;
719 self.unexpanded_attribute_macros = attribute_macros;
720
721 for (module_id, macro_call_id, depth) in resolved {
722 self.collect_macro_expansion(module_id, macro_call_id, depth);
723 }
724
725 res
726 }
727
728 fn resolve_attribute_macro(
729 &self,
730 directive: &DeriveDirective,
731 path: &ModPath,
732 ) -> Option<MacroDefId> {
733 if let Some(name) = path.as_ident() {
734 // FIXME this should actually be handled with the normal name
735 // resolution; the std lib defines built-in stubs for the derives,
736 // but these are new-style `macro`s, which we don't support yet
737 if let Some(def_id) = find_builtin_derive(name) {
738 return Some(def_id);
739 }
740 }
741 let resolved_res = self.def_map.resolve_path_fp_with_macro(
742 self.db,
743 ResolveMode::Other,
744 directive.module_id,
745 &path,
746 BuiltinShadowMode::Module,
747 );
748
749 resolved_res.resolved_def.take_macros()
750 }
751
752 fn collect_macro_expansion(
753 &mut self,
754 module_id: LocalModuleId,
755 macro_call_id: MacroCallId,
756 depth: usize,
757 ) {
758 if depth > EXPANSION_DEPTH_LIMIT {
759 mark::hit!(macro_expansion_overflow);
760 log::warn!("macro expansion is too deep");
761 return;
762 }
763 let file_id: HirFileId = macro_call_id.as_file();
764 let item_tree = self.db.item_tree(file_id);
765 let mod_dir = self.mod_dirs[&module_id].clone();
766 ModCollector {
767 def_collector: &mut *self,
768 macro_depth: depth,
769 file_id,
770 module_id,
771 item_tree: &item_tree,
772 mod_dir,
773 }
774 .collect(item_tree.top_level_items());
775 }
776
777 fn finish(self) -> CrateDefMap {
778 self.def_map
779 }
780}
781
782/// Walks a single module, populating defs, imports and macros
783struct ModCollector<'a, 'b> {
784 def_collector: &'a mut DefCollector<'b>,
785 macro_depth: usize,
786 module_id: LocalModuleId,
787 file_id: HirFileId,
788 item_tree: &'a ItemTree,
789 mod_dir: ModDir,
790}
791
792impl ModCollector<'_, '_> {
793 fn collect(&mut self, items: &[ModItem]) {
794 // Note: don't assert that inserted value is fresh: it's simply not true
795 // for macros.
796 self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
797
798 // Prelude module is always considered to be `#[macro_use]`.
799 if let Some(prelude_module) = self.def_collector.def_map.prelude {
800 if prelude_module.krate != self.def_collector.def_map.krate {
801 mark::hit!(prelude_is_macro_use);
802 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
803 }
804 }
805
806 // This should be processed eagerly instead of deferred to resolving.
807 // `#[macro_use] extern crate` is hoisted to imports macros before collecting
808 // any other items.
809 for item in items {
810 if self.is_cfg_enabled(self.item_tree.attrs((*item).into())) {
811 if let ModItem::ExternCrate(id) = item {
812 let import = self.item_tree[*id].clone();
813 if import.is_macro_use {
814 self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
815 }
816 }
817 }
818 }
819
820 for &item in items {
821 let attrs = self.item_tree.attrs(item.into());
822 if self.is_cfg_enabled(attrs) {
823 let module =
824 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
825 let container = ContainerId::ModuleId(module);
826
827 let mut def = None;
828 match item {
829 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs),
830 ModItem::Import(import_id) => {
831 self.def_collector.unresolved_imports.push(ImportDirective {
832 module_id: self.module_id,
833 import: Import::from_use(&self.item_tree, import_id),
834 status: PartialResolvedImport::Unresolved,
835 })
836 }
837 ModItem::ExternCrate(import_id) => {
838 self.def_collector.unresolved_imports.push(ImportDirective {
839 module_id: self.module_id,
840 import: Import::from_extern_crate(&self.item_tree, import_id),
841 status: PartialResolvedImport::Unresolved,
842 })
843 }
844 ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]),
845 ModItem::Impl(imp) => {
846 let module = ModuleId {
847 krate: self.def_collector.def_map.krate,
848 local_id: self.module_id,
849 };
850 let container = ContainerId::ModuleId(module);
851 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) }
852 .intern(self.def_collector.db);
853 self.def_collector.def_map.modules[self.module_id]
854 .scope
855 .define_impl(impl_id)
856 }
857 ModItem::Function(id) => {
858 let func = &self.item_tree[id];
859 def = Some(DefData {
860 id: FunctionLoc {
861 container: container.into(),
862 id: ItemTreeId::new(self.file_id, id),
863 }
864 .intern(self.def_collector.db)
865 .into(),
866 name: &func.name,
867 visibility: &self.item_tree[func.visibility],
868 has_constructor: false,
869 });
870 }
871 ModItem::Struct(id) => {
872 let it = &self.item_tree[id];
873
874 // FIXME: check attrs to see if this is an attribute macro invocation;
875 // in which case we don't add the invocation, just a single attribute
876 // macro invocation
877 self.collect_derives(attrs, it.ast_id.upcast());
878
879 def = Some(DefData {
880 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
881 .intern(self.def_collector.db)
882 .into(),
883 name: &it.name,
884 visibility: &self.item_tree[it.visibility],
885 has_constructor: it.kind != StructDefKind::Record,
886 });
887 }
888 ModItem::Union(id) => {
889 let it = &self.item_tree[id];
890
891 // FIXME: check attrs to see if this is an attribute macro invocation;
892 // in which case we don't add the invocation, just a single attribute
893 // macro invocation
894 self.collect_derives(attrs, it.ast_id.upcast());
895
896 def = Some(DefData {
897 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) }
898 .intern(self.def_collector.db)
899 .into(),
900 name: &it.name,
901 visibility: &self.item_tree[it.visibility],
902 has_constructor: false,
903 });
904 }
905 ModItem::Enum(id) => {
906 let it = &self.item_tree[id];
907
908 // FIXME: check attrs to see if this is an attribute macro invocation;
909 // in which case we don't add the invocation, just a single attribute
910 // macro invocation
911 self.collect_derives(attrs, it.ast_id.upcast());
912
913 def = Some(DefData {
914 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) }
915 .intern(self.def_collector.db)
916 .into(),
917 name: &it.name,
918 visibility: &self.item_tree[it.visibility],
919 has_constructor: false,
920 });
921 }
922 ModItem::Const(id) => {
923 let it = &self.item_tree[id];
924
925 if let Some(name) = &it.name {
926 def = Some(DefData {
927 id: ConstLoc {
928 container: container.into(),
929 id: ItemTreeId::new(self.file_id, id),
930 }
931 .intern(self.def_collector.db)
932 .into(),
933 name,
934 visibility: &self.item_tree[it.visibility],
935 has_constructor: false,
936 });
937 }
938 }
939 ModItem::Static(id) => {
940 let it = &self.item_tree[id];
941
942 def = Some(DefData {
943 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
944 .intern(self.def_collector.db)
945 .into(),
946 name: &it.name,
947 visibility: &self.item_tree[it.visibility],
948 has_constructor: false,
949 });
950 }
951 ModItem::Trait(id) => {
952 let it = &self.item_tree[id];
953
954 def = Some(DefData {
955 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) }
956 .intern(self.def_collector.db)
957 .into(),
958 name: &it.name,
959 visibility: &self.item_tree[it.visibility],
960 has_constructor: false,
961 });
962 }
963 ModItem::TypeAlias(id) => {
964 let it = &self.item_tree[id];
965
966 def = Some(DefData {
967 id: TypeAliasLoc {
968 container: container.into(),
969 id: ItemTreeId::new(self.file_id, id),
970 }
971 .intern(self.def_collector.db)
972 .into(),
973 name: &it.name,
974 visibility: &self.item_tree[it.visibility],
975 has_constructor: false,
976 });
977 }
978 }
979
980 if let Some(DefData { id, name, visibility, has_constructor }) = def {
981 self.def_collector.def_map.modules[self.module_id].scope.define_def(id);
982 let vis = self
983 .def_collector
984 .def_map
985 .resolve_visibility(self.def_collector.db, self.module_id, visibility)
986 .unwrap_or(Visibility::Public);
987 self.def_collector.update(
988 self.module_id,
989 &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))],
990 vis,
991 ImportType::Named,
992 )
993 }
994 }
995 }
996 }
997
998 fn collect_module(&mut self, module: &Mod, attrs: &Attrs) {
999 let path_attr = attrs.by_key("path").string_value();
1000 let is_macro_use = attrs.by_key("macro_use").exists();
1001 match &module.kind {
1002 // inline module, just recurse
1003 ModKind::Inline { items } => {
1004 let module_id = self.push_child_module(
1005 module.name.clone(),
1006 AstId::new(self.file_id, module.ast_id),
1007 None,
1008 &self.item_tree[module.visibility],
1009 );
1010
1011 ModCollector {
1012 def_collector: &mut *self.def_collector,
1013 macro_depth: self.macro_depth,
1014 module_id,
1015 file_id: self.file_id,
1016 item_tree: self.item_tree,
1017 mod_dir: self.mod_dir.descend_into_definition(&module.name, path_attr),
1018 }
1019 .collect(&*items);
1020 if is_macro_use {
1021 self.import_all_legacy_macros(module_id);
1022 }
1023 }
1024 // out of line module, resolve, parse and recurse
1025 ModKind::Outline {} => {
1026 let ast_id = AstId::new(self.file_id, module.ast_id);
1027 match self.mod_dir.resolve_declaration(
1028 self.def_collector.db,
1029 self.file_id,
1030 &module.name,
1031 path_attr,
1032 ) {
1033 Ok((file_id, is_mod_rs, mod_dir)) => {
1034 let module_id = self.push_child_module(
1035 module.name.clone(),
1036 ast_id,
1037 Some((file_id, is_mod_rs)),
1038 &self.item_tree[module.visibility],
1039 );
1040 let item_tree = self.def_collector.db.item_tree(file_id.into());
1041 ModCollector {
1042 def_collector: &mut *self.def_collector,
1043 macro_depth: self.macro_depth,
1044 module_id,
1045 file_id: file_id.into(),
1046 item_tree: &item_tree,
1047 mod_dir,
1048 }
1049 .collect(item_tree.top_level_items());
1050 if is_macro_use {
1051 self.import_all_legacy_macros(module_id);
1052 }
1053 }
1054 Err(candidate) => self.def_collector.def_map.diagnostics.push(
1055 DefDiagnostic::UnresolvedModule {
1056 module: self.module_id,
1057 declaration: ast_id,
1058 candidate,
1059 },
1060 ),
1061 };
1062 }
1063 }
1064 }
1065
1066 fn push_child_module(
1067 &mut self,
1068 name: Name,
1069 declaration: AstId<ast::Module>,
1070 definition: Option<(FileId, bool)>,
1071 visibility: &crate::visibility::RawVisibility,
1072 ) -> LocalModuleId {
1073 let vis = self
1074 .def_collector
1075 .def_map
1076 .resolve_visibility(self.def_collector.db, self.module_id, visibility)
1077 .unwrap_or(Visibility::Public);
1078 let modules = &mut self.def_collector.def_map.modules;
1079 let res = modules.alloc(ModuleData::default());
1080 modules[res].parent = Some(self.module_id);
1081 modules[res].origin = match definition {
1082 None => ModuleOrigin::Inline { definition: declaration },
1083 Some((definition, is_mod_rs)) => {
1084 ModuleOrigin::File { declaration, definition, is_mod_rs }
1085 }
1086 };
1087 for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() {
1088 modules[res].scope.define_legacy_macro(name, mac)
1089 }
1090 modules[self.module_id].children.insert(name.clone(), res);
1091 let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res };
1092 let def: ModuleDefId = module.into();
1093 self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
1094 self.def_collector.update(
1095 self.module_id,
1096 &[(Some(name), PerNs::from_def(def, vis, false))],
1097 vis,
1098 ImportType::Named,
1099 );
1100 res
1101 }
1102
1103 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) {
1104 for derive_subtree in attrs.by_key("derive").tt_values() {
1105 // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
1106 for tt in &derive_subtree.token_trees {
1107 let ident = match &tt {
1108 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident,
1109 tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => continue, // , is ok
1110 _ => continue, // anything else would be an error (which we currently ignore)
1111 };
1112 let path = ModPath::from_tt_ident(ident);
1113
1114 let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
1115 self.def_collector
1116 .unexpanded_attribute_macros
1117 .push(DeriveDirective { module_id: self.module_id, ast_id });
1118 }
1119 }
1120 }
1121
1122 fn collect_macro(&mut self, mac: &MacroCall) {
1123 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
1124
1125 // Case 0: builtin macros
1126 if mac.is_builtin {
1127 if let Some(name) = &mac.name {
1128 let krate = self.def_collector.def_map.krate;
1129 if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) {
1130 self.def_collector.define_macro(
1131 self.module_id,
1132 name.clone(),
1133 macro_id,
1134 mac.is_export,
1135 );
1136 return;
1137 }
1138 }
1139 }
1140
1141 // Case 1: macro rules, define a macro in crate-global mutable scope
1142 if is_macro_rules(&mac.path) {
1143 if let Some(name) = &mac.name {
1144 let macro_id = MacroDefId {
1145 ast_id: Some(ast_id.ast_id),
1146 krate: Some(self.def_collector.def_map.krate),
1147 kind: MacroDefKind::Declarative,
1148 local_inner: mac.is_local_inner,
1149 };
1150 self.def_collector.define_macro(
1151 self.module_id,
1152 name.clone(),
1153 macro_id,
1154 mac.is_export,
1155 );
1156 }
1157 return;
1158 }
1159
1160 // Case 2: try to resolve in legacy scope and expand macro_rules
1161 if let Some(macro_call_id) =
1162 ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| {
1163 path.as_ident().and_then(|name| {
1164 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
1165 })
1166 })
1167 {
1168 self.def_collector.unexpanded_macros.push(MacroDirective {
1169 module_id: self.module_id,
1170 ast_id,
1171 legacy: Some(macro_call_id),
1172 depth: self.macro_depth + 1,
1173 });
1174
1175 return;
1176 }
1177
1178 // Case 3: resolve in module scope, expand during name resolution.
1179 // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only.
1180 if ast_id.path.is_ident() {
1181 ast_id.path.kind = PathKind::Super(0);
1182 }
1183
1184 self.def_collector.unexpanded_macros.push(MacroDirective {
1185 module_id: self.module_id,
1186 ast_id,
1187 legacy: None,
1188 depth: self.macro_depth + 1,
1189 });
1190 }
1191
1192 fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) {
1193 let macros = self.def_collector.def_map[module_id].scope.collect_legacy_macros();
1194 for (name, macro_) in macros {
1195 self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_);
1196 }
1197 }
1198
1199 fn is_cfg_enabled(&self, attrs: &Attrs) -> bool {
1200 attrs.is_cfg_enabled(self.def_collector.cfg_options)
1201 }
1202}
1203
1204fn is_macro_rules(path: &ModPath) -> bool {
1205 path.as_ident() == Some(&name![macro_rules])
1206}
1207
1208#[cfg(test)]
1209mod tests {
1210 use crate::{db::DefDatabase, test_db::TestDB};
1211 use arena::Arena;
1212 use base_db::{fixture::WithFixture, SourceDatabase};
1213
1214 use super::*;
1215
1216 fn do_collect_defs(db: &dyn DefDatabase, def_map: CrateDefMap) -> CrateDefMap {
1217 let mut collector = DefCollector {
1218 db,
1219 def_map,
1220 glob_imports: FxHashMap::default(),
1221 unresolved_imports: Vec::new(),
1222 resolved_imports: Vec::new(),
1223 unexpanded_macros: Vec::new(),
1224 unexpanded_attribute_macros: Vec::new(),
1225 mod_dirs: FxHashMap::default(),
1226 cfg_options: &CfgOptions::default(),
1227 proc_macros: Default::default(),
1228 from_glob_import: Default::default(),
1229 };
1230 collector.collect();
1231 collector.def_map
1232 }
1233
1234 fn do_resolve(code: &str) -> CrateDefMap {
1235 let (db, _file_id) = TestDB::with_single_file(&code);
1236 let krate = db.test_crate();
1237
1238 let def_map = {
1239 let edition = db.crate_graph()[krate].edition;
1240 let mut modules: Arena<ModuleData> = Arena::default();
1241 let root = modules.alloc(ModuleData::default());
1242 CrateDefMap {
1243 krate,
1244 edition,
1245 extern_prelude: FxHashMap::default(),
1246 prelude: None,
1247 root,
1248 modules,
1249 diagnostics: Vec::new(),
1250 }
1251 };
1252 do_collect_defs(&db, def_map)
1253 }
1254
1255 #[test]
1256 fn test_macro_expand_will_stop_1() {
1257 do_resolve(
1258 r#"
1259 macro_rules! foo {
1260 ($($ty:ty)*) => { foo!($($ty)*); }
1261 }
1262 foo!(KABOOM);
1263 "#,
1264 );
1265 }
1266
1267 #[ignore] // this test does succeed, but takes quite a while :/
1268 #[test]
1269 fn test_macro_expand_will_stop_2() {
1270 do_resolve(
1271 r#"
1272 macro_rules! foo {
1273 ($($ty:ty)*) => { foo!($($ty)* $($ty)*); }
1274 }
1275 foo!(KABOOM);
1276 "#,
1277 );
1278 }
1279}
diff --git a/crates/hir_def/src/nameres/mod_resolution.rs b/crates/hir_def/src/nameres/mod_resolution.rs
new file mode 100644
index 000000000..e8389b484
--- /dev/null
+++ b/crates/hir_def/src/nameres/mod_resolution.rs
@@ -0,0 +1,139 @@
1//! This module resolves `mod foo;` declaration to file.
2use base_db::FileId;
3use hir_expand::name::Name;
4use syntax::SmolStr;
5
6use crate::{db::DefDatabase, HirFileId};
7
8#[derive(Clone, Debug)]
9pub(super) struct ModDir {
10 /// `` for `mod.rs`, `lib.rs`
11 /// `foo/` for `foo.rs`
12 /// `foo/bar/` for `mod bar { mod x; }` nested in `foo.rs`
13 /// Invariant: path.is_empty() || path.ends_with('/')
14 dir_path: DirPath,
15 /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/`
16 root_non_dir_owner: bool,
17}
18
19impl ModDir {
20 pub(super) fn root() -> ModDir {
21 ModDir { dir_path: DirPath::empty(), root_non_dir_owner: false }
22 }
23
24 pub(super) fn descend_into_definition(
25 &self,
26 name: &Name,
27 attr_path: Option<&SmolStr>,
28 ) -> ModDir {
29 let path = match attr_path.map(|it| it.as_str()) {
30 None => {
31 let mut path = self.dir_path.clone();
32 path.push(&name.to_string());
33 path
34 }
35 Some(attr_path) => {
36 let mut path = self.dir_path.join_attr(attr_path, self.root_non_dir_owner);
37 if !(path.is_empty() || path.ends_with('/')) {
38 path.push('/')
39 }
40 DirPath::new(path)
41 }
42 };
43 ModDir { dir_path: path, root_non_dir_owner: false }
44 }
45
46 pub(super) fn resolve_declaration(
47 &self,
48 db: &dyn DefDatabase,
49 file_id: HirFileId,
50 name: &Name,
51 attr_path: Option<&SmolStr>,
52 ) -> Result<(FileId, bool, ModDir), String> {
53 let file_id = file_id.original_file(db.upcast());
54
55 let mut candidate_files = Vec::new();
56 match attr_path {
57 Some(attr_path) => {
58 candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
59 }
60 None => {
61 candidate_files.push(format!("{}{}.rs", self.dir_path.0, name));
62 candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name));
63 }
64 };
65
66 for candidate in candidate_files.iter() {
67 if let Some(file_id) = db.resolve_path(file_id, candidate.as_str()) {
68 let is_mod_rs = candidate.ends_with("mod.rs");
69
70 let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
71 (DirPath::empty(), false)
72 } else {
73 (DirPath::new(format!("{}/", name)), true)
74 };
75 return Ok((file_id, is_mod_rs, ModDir { dir_path, root_non_dir_owner }));
76 }
77 }
78 Err(candidate_files.remove(0))
79 }
80}
81
82#[derive(Clone, Debug)]
83struct DirPath(String);
84
85impl DirPath {
86 fn assert_invariant(&self) {
87 assert!(self.0.is_empty() || self.0.ends_with('/'));
88 }
89 fn new(repr: String) -> DirPath {
90 let res = DirPath(repr);
91 res.assert_invariant();
92 res
93 }
94 fn empty() -> DirPath {
95 DirPath::new(String::new())
96 }
97 fn push(&mut self, name: &str) {
98 self.0.push_str(name);
99 self.0.push('/');
100 self.assert_invariant();
101 }
102 fn parent(&self) -> Option<&str> {
103 if self.0.is_empty() {
104 return None;
105 };
106 let idx =
107 self.0[..self.0.len() - '/'.len_utf8()].rfind('/').map_or(0, |it| it + '/'.len_utf8());
108 Some(&self.0[..idx])
109 }
110 /// So this is the case which doesn't really work I think if we try to be
111 /// 100% platform agnostic:
112 ///
113 /// ```
114 /// mod a {
115 /// #[path="C://sad/face"]
116 /// mod b { mod c; }
117 /// }
118 /// ```
119 ///
120 /// Here, we need to join logical dir path to a string path from an
121 /// attribute. Ideally, we should somehow losslessly communicate the whole
122 /// construction to `FileLoader`.
123 fn join_attr(&self, mut attr: &str, relative_to_parent: bool) -> String {
124 let base = if relative_to_parent { self.parent().unwrap() } else { &self.0 };
125
126 if attr.starts_with("./") {
127 attr = &attr["./".len()..];
128 }
129 let tmp;
130 let attr = if attr.contains('\\') {
131 tmp = attr.replace('\\', "/");
132 &tmp
133 } else {
134 attr
135 };
136 let res = format!("{}{}", base, attr);
137 res
138 }
139}
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
new file mode 100644
index 000000000..88e10574e
--- /dev/null
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -0,0 +1,330 @@
1//! This modules implements a function to resolve a path `foo::bar::baz` to a
2//! def, which is used within the name resolution.
3//!
4//! When name resolution is finished, the result of resolving a path is either
5//! `Some(def)` or `None`. However, when we are in process of resolving imports
6//! or macros, there's a third possibility:
7//!
8//! I can't resolve this path right now, but I might be resolve this path
9//! later, when more macros are expanded.
10//!
11//! `ReachedFixedPoint` signals about this.
12
13use std::iter::successors;
14
15use base_db::Edition;
16use hir_expand::name::Name;
17use test_utils::mark;
18
19use crate::{
20 db::DefDatabase,
21 item_scope::BUILTIN_SCOPE,
22 nameres::{BuiltinShadowMode, CrateDefMap},
23 path::{ModPath, PathKind},
24 per_ns::PerNs,
25 visibility::{RawVisibility, Visibility},
26 AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
27};
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub(super) enum ResolveMode {
31 Import,
32 Other,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub(super) enum ReachedFixedPoint {
37 Yes,
38 No,
39}
40
41#[derive(Debug, Clone)]
42pub(super) struct ResolvePathResult {
43 pub(super) resolved_def: PerNs,
44 pub(super) segment_index: Option<usize>,
45 pub(super) reached_fixedpoint: ReachedFixedPoint,
46 pub(super) krate: Option<CrateId>,
47}
48
49impl ResolvePathResult {
50 fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
51 ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None)
52 }
53
54 fn with(
55 resolved_def: PerNs,
56 reached_fixedpoint: ReachedFixedPoint,
57 segment_index: Option<usize>,
58 krate: Option<CrateId>,
59 ) -> ResolvePathResult {
60 ResolvePathResult { resolved_def, reached_fixedpoint, segment_index, krate }
61 }
62}
63
64impl CrateDefMap {
65 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
66 self.extern_prelude
67 .get(name)
68 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
69 }
70
71 pub(crate) fn resolve_visibility(
72 &self,
73 db: &dyn DefDatabase,
74 original_module: LocalModuleId,
75 visibility: &RawVisibility,
76 ) -> Option<Visibility> {
77 match visibility {
78 RawVisibility::Module(path) => {
79 let (result, remaining) =
80 self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module);
81 if remaining.is_some() {
82 return None;
83 }
84 let types = result.take_types()?;
85 match types {
86 ModuleDefId::ModuleId(m) => Some(Visibility::Module(m)),
87 _ => {
88 // error: visibility needs to refer to module
89 None
90 }
91 }
92 }
93 RawVisibility::Public => Some(Visibility::Public),
94 }
95 }
96
97 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
98 // the result.
99 pub(super) fn resolve_path_fp_with_macro(
100 &self,
101 db: &dyn DefDatabase,
102 mode: ResolveMode,
103 original_module: LocalModuleId,
104 path: &ModPath,
105 shadow: BuiltinShadowMode,
106 ) -> ResolvePathResult {
107 let mut segments = path.segments.iter().enumerate();
108 let mut curr_per_ns: PerNs = match path.kind {
109 PathKind::DollarCrate(krate) => {
110 if krate == self.krate {
111 mark::hit!(macro_dollar_crate_self);
112 PerNs::types(
113 ModuleId { krate: self.krate, local_id: self.root }.into(),
114 Visibility::Public,
115 )
116 } else {
117 let def_map = db.crate_def_map(krate);
118 let module = ModuleId { krate, local_id: def_map.root };
119 mark::hit!(macro_dollar_crate_other);
120 PerNs::types(module.into(), Visibility::Public)
121 }
122 }
123 PathKind::Crate => PerNs::types(
124 ModuleId { krate: self.krate, local_id: self.root }.into(),
125 Visibility::Public,
126 ),
127 // plain import or absolute path in 2015: crate-relative with
128 // fallback to extern prelude (with the simplification in
129 // rust-lang/rust#57745)
130 // FIXME there must be a nicer way to write this condition
131 PathKind::Plain | PathKind::Abs
132 if self.edition == Edition::Edition2015
133 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
134 {
135 let (_, segment) = match segments.next() {
136 Some((idx, segment)) => (idx, segment),
137 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
138 };
139 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
140 self.resolve_name_in_crate_root_or_extern_prelude(&segment)
141 }
142 PathKind::Plain => {
143 let (_, segment) = match segments.next() {
144 Some((idx, segment)) => (idx, segment),
145 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
146 };
147 // The first segment may be a builtin type. If the path has more
148 // than one segment, we first try resolving it as a module
149 // anyway.
150 // FIXME: If the next segment doesn't resolve in the module and
151 // BuiltinShadowMode wasn't Module, then we need to try
152 // resolving it as a builtin.
153 let prefer_module =
154 if path.segments.len() == 1 { shadow } else { BuiltinShadowMode::Module };
155
156 log::debug!("resolving {:?} in module", segment);
157 self.resolve_name_in_module(db, original_module, &segment, prefer_module)
158 }
159 PathKind::Super(lvl) => {
160 let m = successors(Some(original_module), |m| self.modules[*m].parent)
161 .nth(lvl as usize);
162 if let Some(local_id) = m {
163 PerNs::types(
164 ModuleId { krate: self.krate, local_id }.into(),
165 Visibility::Public,
166 )
167 } else {
168 log::debug!("super path in root module");
169 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
170 }
171 }
172 PathKind::Abs => {
173 // 2018-style absolute path -- only extern prelude
174 let segment = match segments.next() {
175 Some((_, segment)) => segment,
176 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
177 };
178 if let Some(def) = self.extern_prelude.get(&segment) {
179 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
180 PerNs::types(*def, Visibility::Public)
181 } else {
182 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
183 }
184 }
185 };
186
187 for (i, segment) in segments {
188 let (curr, vis) = match curr_per_ns.take_types_vis() {
189 Some(r) => r,
190 None => {
191 // we still have path segments left, but the path so far
192 // didn't resolve in the types namespace => no resolution
193 // (don't break here because `curr_per_ns` might contain
194 // something in the value namespace, and it would be wrong
195 // to return that)
196 return ResolvePathResult::empty(ReachedFixedPoint::No);
197 }
198 };
199 // resolve segment in curr
200
201 curr_per_ns = match curr {
202 ModuleDefId::ModuleId(module) => {
203 if module.krate != self.krate {
204 let path = ModPath {
205 segments: path.segments[i..].to_vec(),
206 kind: PathKind::Super(0),
207 };
208 log::debug!("resolving {:?} in other crate", path);
209 let defp_map = db.crate_def_map(module.krate);
210 let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
211 return ResolvePathResult::with(
212 def,
213 ReachedFixedPoint::Yes,
214 s.map(|s| s + i),
215 Some(module.krate),
216 );
217 }
218
219 // Since it is a qualified path here, it should not contains legacy macros
220 self[module.local_id].scope.get(&segment)
221 }
222 ModuleDefId::AdtId(AdtId::EnumId(e)) => {
223 // enum variant
224 mark::hit!(can_import_enum_variant);
225 let enum_data = db.enum_data(e);
226 match enum_data.variant(&segment) {
227 Some(local_id) => {
228 let variant = EnumVariantId { parent: e, local_id };
229 match &*enum_data.variants[local_id].variant_data {
230 crate::adt::VariantData::Record(_) => {
231 PerNs::types(variant.into(), Visibility::Public)
232 }
233 crate::adt::VariantData::Tuple(_)
234 | crate::adt::VariantData::Unit => {
235 PerNs::both(variant.into(), variant.into(), Visibility::Public)
236 }
237 }
238 }
239 None => {
240 return ResolvePathResult::with(
241 PerNs::types(e.into(), vis),
242 ReachedFixedPoint::Yes,
243 Some(i),
244 Some(self.krate),
245 );
246 }
247 }
248 }
249 s => {
250 // could be an inherent method call in UFCS form
251 // (`Struct::method`), or some other kind of associated item
252 log::debug!(
253 "path segment {:?} resolved to non-module {:?}, but is not last",
254 segment,
255 curr,
256 );
257
258 return ResolvePathResult::with(
259 PerNs::types(s, vis),
260 ReachedFixedPoint::Yes,
261 Some(i),
262 Some(self.krate),
263 );
264 }
265 };
266 }
267
268 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate))
269 }
270
271 fn resolve_name_in_module(
272 &self,
273 db: &dyn DefDatabase,
274 module: LocalModuleId,
275 name: &Name,
276 shadow: BuiltinShadowMode,
277 ) -> PerNs {
278 // Resolve in:
279 // - legacy scope of macro
280 // - current module / scope
281 // - extern prelude
282 // - std prelude
283 let from_legacy_macro = self[module]
284 .scope
285 .get_legacy_macro(name)
286 .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
287 let from_scope = self[module].scope.get(name);
288 let from_builtin = BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none);
289 let from_scope_or_builtin = match shadow {
290 BuiltinShadowMode::Module => from_scope.or(from_builtin),
291 BuiltinShadowMode::Other => {
292 if let Some(ModuleDefId::ModuleId(_)) = from_scope.take_types() {
293 from_builtin.or(from_scope)
294 } else {
295 from_scope.or(from_builtin)
296 }
297 }
298 };
299 let from_extern_prelude = self
300 .extern_prelude
301 .get(name)
302 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public));
303 let from_prelude = self.resolve_in_prelude(db, name);
304
305 from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude)
306 }
307
308 fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs {
309 let from_crate_root = self[self.root].scope.get(name);
310 let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
311
312 from_crate_root.or(from_extern_prelude)
313 }
314
315 fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
316 if let Some(prelude) = self.prelude {
317 let keep;
318 let def_map = if prelude.krate == self.krate {
319 self
320 } else {
321 // Extend lifetime
322 keep = db.crate_def_map(prelude.krate);
323 &keep
324 };
325 def_map[prelude.local_id].scope.get(name)
326 } else {
327 PerNs::none()
328 }
329 }
330}
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs
new file mode 100644
index 000000000..8aaf7a158
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests.rs
@@ -0,0 +1,690 @@
1mod globs;
2mod incremental;
3mod macros;
4mod mod_resolution;
5mod primitives;
6
7use std::sync::Arc;
8
9use base_db::{fixture::WithFixture, SourceDatabase};
10use expect_test::{expect, Expect};
11use test_utils::mark;
12
13use crate::{db::DefDatabase, nameres::*, test_db::TestDB};
14
15fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> {
16 let db = TestDB::with_files(fixture);
17 let krate = db.crate_graph().iter().next().unwrap();
18 db.crate_def_map(krate)
19}
20
21fn check(ra_fixture: &str, expect: Expect) {
22 let db = TestDB::with_files(ra_fixture);
23 let krate = db.crate_graph().iter().next().unwrap();
24 let actual = db.crate_def_map(krate).dump();
25 expect.assert_eq(&actual);
26}
27
28#[test]
29fn crate_def_map_smoke_test() {
30 check(
31 r#"
32//- /lib.rs
33mod foo;
34struct S;
35use crate::foo::bar::E;
36use self::E::V;
37
38//- /foo/mod.rs
39pub mod bar;
40fn f() {}
41
42//- /foo/bar.rs
43pub struct Baz;
44
45union U { to_be: bool, not_to_be: u8 }
46enum E { V }
47
48extern {
49 static EXT: u8;
50 fn ext();
51}
52"#,
53 expect![[r#"
54 crate
55 E: t
56 S: t v
57 V: t v
58 foo: t
59
60 crate::foo
61 bar: t
62 f: v
63
64 crate::foo::bar
65 Baz: t v
66 E: t
67 EXT: v
68 U: t
69 ext: v
70 "#]],
71 );
72}
73
74#[test]
75fn crate_def_map_super_super() {
76 check(
77 r#"
78mod a {
79 const A: usize = 0;
80 mod b {
81 const B: usize = 0;
82 mod c {
83 use super::super::*;
84 }
85 }
86}
87"#,
88 expect![[r#"
89 crate
90 a: t
91
92 crate::a
93 A: v
94 b: t
95
96 crate::a::b
97 B: v
98 c: t
99
100 crate::a::b::c
101 A: v
102 b: t
103 "#]],
104 );
105}
106
107#[test]
108fn crate_def_map_fn_mod_same_name() {
109 check(
110 r#"
111mod m {
112 pub mod z {}
113 pub fn z() {}
114}
115"#,
116 expect![[r#"
117 crate
118 m: t
119
120 crate::m
121 z: t v
122
123 crate::m::z
124 "#]],
125 );
126}
127
128#[test]
129fn bogus_paths() {
130 mark::check!(bogus_paths);
131 check(
132 r#"
133//- /lib.rs
134mod foo;
135struct S;
136use self;
137
138//- /foo/mod.rs
139use super;
140use crate;
141"#,
142 expect![[r#"
143 crate
144 S: t v
145 foo: t
146
147 crate::foo
148 "#]],
149 );
150}
151
152#[test]
153fn use_as() {
154 check(
155 r#"
156//- /lib.rs
157mod foo;
158use crate::foo::Baz as Foo;
159
160//- /foo/mod.rs
161pub struct Baz;
162"#,
163 expect![[r#"
164 crate
165 Foo: t v
166 foo: t
167
168 crate::foo
169 Baz: t v
170 "#]],
171 );
172}
173
174#[test]
175fn use_trees() {
176 check(
177 r#"
178//- /lib.rs
179mod foo;
180use crate::foo::bar::{Baz, Quux};
181
182//- /foo/mod.rs
183pub mod bar;
184
185//- /foo/bar.rs
186pub struct Baz;
187pub enum Quux {};
188"#,
189 expect![[r#"
190 crate
191 Baz: t v
192 Quux: t
193 foo: t
194
195 crate::foo
196 bar: t
197
198 crate::foo::bar
199 Baz: t v
200 Quux: t
201 "#]],
202 );
203}
204
205#[test]
206fn re_exports() {
207 check(
208 r#"
209//- /lib.rs
210mod foo;
211use self::foo::Baz;
212
213//- /foo/mod.rs
214pub mod bar;
215pub use self::bar::Baz;
216
217//- /foo/bar.rs
218pub struct Baz;
219"#,
220 expect![[r#"
221 crate
222 Baz: t v
223 foo: t
224
225 crate::foo
226 Baz: t v
227 bar: t
228
229 crate::foo::bar
230 Baz: t v
231 "#]],
232 );
233}
234
235#[test]
236fn std_prelude() {
237 mark::check!(std_prelude);
238 check(
239 r#"
240//- /main.rs crate:main deps:test_crate
241use Foo::*;
242
243//- /lib.rs crate:test_crate
244mod prelude;
245#[prelude_import]
246use prelude::*;
247
248//- /prelude.rs
249pub enum Foo { Bar, Baz };
250"#,
251 expect![[r#"
252 crate
253 Bar: t v
254 Baz: t v
255 "#]],
256 );
257}
258
259#[test]
260fn can_import_enum_variant() {
261 mark::check!(can_import_enum_variant);
262 check(
263 r#"
264enum E { V }
265use self::E::V;
266"#,
267 expect![[r#"
268 crate
269 E: t
270 V: t v
271 "#]],
272 );
273}
274
275#[test]
276fn edition_2015_imports() {
277 check(
278 r#"
279//- /main.rs crate:main deps:other_crate edition:2015
280mod foo;
281mod bar;
282
283//- /bar.rs
284struct Bar;
285
286//- /foo.rs
287use bar::Bar;
288use other_crate::FromLib;
289
290//- /lib.rs crate:other_crate edition:2018
291struct FromLib;
292"#,
293 expect![[r#"
294 crate
295 bar: t
296 foo: t
297
298 crate::bar
299 Bar: t v
300
301 crate::foo
302 Bar: t v
303 FromLib: t v
304 "#]],
305 );
306}
307
308#[test]
309fn item_map_using_self() {
310 check(
311 r#"
312//- /lib.rs
313mod foo;
314use crate::foo::bar::Baz::{self};
315
316//- /foo/mod.rs
317pub mod bar;
318
319//- /foo/bar.rs
320pub struct Baz;
321"#,
322 expect![[r#"
323 crate
324 Baz: t v
325 foo: t
326
327 crate::foo
328 bar: t
329
330 crate::foo::bar
331 Baz: t v
332 "#]],
333 );
334}
335
336#[test]
337fn item_map_across_crates() {
338 check(
339 r#"
340//- /main.rs crate:main deps:test_crate
341use test_crate::Baz;
342
343//- /lib.rs crate:test_crate
344pub struct Baz;
345"#,
346 expect![[r#"
347 crate
348 Baz: t v
349 "#]],
350 );
351}
352
353#[test]
354fn extern_crate_rename() {
355 check(
356 r#"
357//- /main.rs crate:main deps:alloc
358extern crate alloc as alloc_crate;
359mod alloc;
360mod sync;
361
362//- /sync.rs
363use alloc_crate::Arc;
364
365//- /lib.rs crate:alloc
366struct Arc;
367"#,
368 expect![[r#"
369 crate
370 alloc_crate: t
371 sync: t
372
373 crate::sync
374 Arc: t v
375 "#]],
376 );
377}
378
379#[test]
380fn extern_crate_rename_2015_edition() {
381 check(
382 r#"
383//- /main.rs crate:main deps:alloc edition:2015
384extern crate alloc as alloc_crate;
385mod alloc;
386mod sync;
387
388//- /sync.rs
389use alloc_crate::Arc;
390
391//- /lib.rs crate:alloc
392struct Arc;
393"#,
394 expect![[r#"
395 crate
396 alloc_crate: t
397 sync: t
398
399 crate::sync
400 Arc: t v
401 "#]],
402 );
403}
404
405#[test]
406fn reexport_across_crates() {
407 check(
408 r#"
409//- /main.rs crate:main deps:test_crate
410use test_crate::Baz;
411
412//- /lib.rs crate:test_crate
413pub use foo::Baz;
414mod foo;
415
416//- /foo.rs
417pub struct Baz;
418"#,
419 expect![[r#"
420 crate
421 Baz: t v
422 "#]],
423 );
424}
425
426#[test]
427fn values_dont_shadow_extern_crates() {
428 check(
429 r#"
430//- /main.rs crate:main deps:foo
431fn foo() {}
432use foo::Bar;
433
434//- /foo/lib.rs crate:foo
435pub struct Bar;
436"#,
437 expect![[r#"
438 crate
439 Bar: t v
440 foo: v
441 "#]],
442 );
443}
444
445#[test]
446fn std_prelude_takes_precedence_above_core_prelude() {
447 check(
448 r#"
449//- /main.rs crate:main deps:core,std
450use {Foo, Bar};
451
452//- /std.rs crate:std deps:core
453#[prelude_import]
454pub use self::prelude::*;
455mod prelude {
456 pub struct Foo;
457 pub use core::prelude::Bar;
458}
459
460//- /core.rs crate:core
461#[prelude_import]
462pub use self::prelude::*;
463mod prelude {
464 pub struct Bar;
465}
466"#,
467 expect![[r#"
468 crate
469 Bar: t v
470 Foo: t v
471 "#]],
472 );
473}
474
475#[test]
476fn cfg_not_test() {
477 check(
478 r#"
479//- /main.rs crate:main deps:std
480use {Foo, Bar, Baz};
481
482//- /lib.rs crate:std
483#[prelude_import]
484pub use self::prelude::*;
485mod prelude {
486 #[cfg(test)]
487 pub struct Foo;
488 #[cfg(not(test))]
489 pub struct Bar;
490 #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))]
491 pub struct Baz;
492}
493"#,
494 expect![[r#"
495 crate
496 Bar: t v
497 Baz: _
498 Foo: _
499 "#]],
500 );
501}
502
503#[test]
504fn cfg_test() {
505 check(
506 r#"
507//- /main.rs crate:main deps:std
508use {Foo, Bar, Baz};
509
510//- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42
511#[prelude_import]
512pub use self::prelude::*;
513mod prelude {
514 #[cfg(test)]
515 pub struct Foo;
516 #[cfg(not(test))]
517 pub struct Bar;
518 #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))]
519 pub struct Baz;
520}
521"#,
522 expect![[r#"
523 crate
524 Bar: _
525 Baz: t v
526 Foo: t v
527 "#]],
528 );
529}
530
531#[test]
532fn infer_multiple_namespace() {
533 check(
534 r#"
535//- /main.rs
536mod a {
537 pub type T = ();
538 pub use crate::b::*;
539}
540
541use crate::a::T;
542
543mod b {
544 pub const T: () = ();
545}
546"#,
547 expect![[r#"
548 crate
549 T: t v
550 a: t
551 b: t
552
553 crate::b
554 T: v
555
556 crate::a
557 T: t v
558 "#]],
559 );
560}
561
562#[test]
563fn underscore_import() {
564 check(
565 r#"
566//- /main.rs
567use tr::Tr as _;
568use tr::Tr2 as _;
569
570mod tr {
571 pub trait Tr {}
572 pub trait Tr2 {}
573}
574 "#,
575 expect![[r#"
576 crate
577 _: t
578 _: t
579 tr: t
580
581 crate::tr
582 Tr: t
583 Tr2: t
584 "#]],
585 );
586}
587
588#[test]
589fn underscore_reexport() {
590 check(
591 r#"
592//- /main.rs
593mod tr {
594 pub trait PubTr {}
595 pub trait PrivTr {}
596}
597mod reex {
598 use crate::tr::PrivTr as _;
599 pub use crate::tr::PubTr as _;
600}
601use crate::reex::*;
602 "#,
603 expect![[r#"
604 crate
605 _: t
606 reex: t
607 tr: t
608
609 crate::tr
610 PrivTr: t
611 PubTr: t
612
613 crate::reex
614 _: t
615 _: t
616 "#]],
617 );
618}
619
620#[test]
621fn underscore_pub_crate_reexport() {
622 mark::check!(upgrade_underscore_visibility);
623 check(
624 r#"
625//- /main.rs crate:main deps:lib
626use lib::*;
627
628//- /lib.rs crate:lib
629use tr::Tr as _;
630pub use tr::Tr as _;
631
632mod tr {
633 pub trait Tr {}
634}
635 "#,
636 expect![[r#"
637 crate
638 _: t
639 "#]],
640 );
641}
642
643#[test]
644fn underscore_nontrait() {
645 check(
646 r#"
647//- /main.rs
648mod m {
649 pub struct Struct;
650 pub enum Enum {}
651 pub const CONST: () = ();
652}
653use crate::m::{Struct as _, Enum as _, CONST as _};
654 "#,
655 expect![[r#"
656 crate
657 m: t
658
659 crate::m
660 CONST: v
661 Enum: t
662 Struct: t v
663 "#]],
664 );
665}
666
667#[test]
668fn underscore_name_conflict() {
669 check(
670 r#"
671//- /main.rs
672struct Tr;
673
674use tr::Tr as _;
675
676mod tr {
677 pub trait Tr {}
678}
679 "#,
680 expect![[r#"
681 crate
682 _: t
683 Tr: t v
684 tr: t
685
686 crate::tr
687 Tr: t
688 "#]],
689 );
690}
diff --git a/crates/hir_def/src/nameres/tests/globs.rs b/crates/hir_def/src/nameres/tests/globs.rs
new file mode 100644
index 000000000..2ae836e3c
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/globs.rs
@@ -0,0 +1,338 @@
1use super::*;
2
3#[test]
4fn glob_1() {
5 check(
6 r#"
7//- /lib.rs
8mod foo;
9use foo::*;
10
11//- /foo/mod.rs
12pub mod bar;
13pub use self::bar::Baz;
14pub struct Foo;
15
16//- /foo/bar.rs
17pub struct Baz;
18"#,
19 expect![[r#"
20 crate
21 Baz: t v
22 Foo: t v
23 bar: t
24 foo: t
25
26 crate::foo
27 Baz: t v
28 Foo: t v
29 bar: t
30
31 crate::foo::bar
32 Baz: t v
33 "#]],
34 );
35}
36
37#[test]
38fn glob_2() {
39 check(
40 r#"
41//- /lib.rs
42mod foo;
43use foo::*;
44
45//- /foo/mod.rs
46pub mod bar;
47pub use self::bar::*;
48pub struct Foo;
49
50//- /foo/bar.rs
51pub struct Baz;
52pub use super::*;
53"#,
54 expect![[r#"
55 crate
56 Baz: t v
57 Foo: t v
58 bar: t
59 foo: t
60
61 crate::foo
62 Baz: t v
63 Foo: t v
64 bar: t
65
66 crate::foo::bar
67 Baz: t v
68 Foo: t v
69 bar: t
70 "#]],
71 );
72}
73
74#[test]
75fn glob_privacy_1() {
76 check(
77 r"
78//- /lib.rs
79mod foo;
80use foo::*;
81
82//- /foo/mod.rs
83pub mod bar;
84pub use self::bar::*;
85struct PrivateStructFoo;
86
87//- /foo/bar.rs
88pub struct Baz;
89struct PrivateStructBar;
90pub use super::*;
91",
92 expect![[r#"
93 crate
94 Baz: t v
95 bar: t
96 foo: t
97
98 crate::foo
99 Baz: t v
100 PrivateStructFoo: t v
101 bar: t
102
103 crate::foo::bar
104 Baz: t v
105 PrivateStructBar: t v
106 PrivateStructFoo: t v
107 bar: t
108 "#]],
109 );
110}
111
112#[test]
113fn glob_privacy_2() {
114 check(
115 r"
116//- /lib.rs
117mod foo;
118use foo::*;
119use foo::bar::*;
120
121//- /foo/mod.rs
122mod bar;
123fn Foo() {};
124pub struct Foo {};
125
126//- /foo/bar.rs
127pub(super) struct PrivateBaz;
128struct PrivateBar;
129pub(crate) struct PubCrateStruct;
130",
131 expect![[r#"
132 crate
133 Foo: t
134 PubCrateStruct: t v
135 foo: t
136
137 crate::foo
138 Foo: t v
139 bar: t
140
141 crate::foo::bar
142 PrivateBar: t v
143 PrivateBaz: t v
144 PubCrateStruct: t v
145 "#]],
146 );
147}
148
149#[test]
150fn glob_across_crates() {
151 mark::check!(glob_across_crates);
152 check(
153 r#"
154//- /main.rs crate:main deps:test_crate
155use test_crate::*;
156
157//- /lib.rs crate:test_crate
158pub struct Baz;
159"#,
160 expect![[r#"
161 crate
162 Baz: t v
163 "#]],
164 );
165}
166
167#[test]
168fn glob_privacy_across_crates() {
169 check(
170 r#"
171//- /main.rs crate:main deps:test_crate
172use test_crate::*;
173
174//- /lib.rs crate:test_crate
175pub struct Baz;
176struct Foo;
177"#,
178 expect![[r#"
179 crate
180 Baz: t v
181 "#]],
182 );
183}
184
185#[test]
186fn glob_enum() {
187 mark::check!(glob_enum);
188 check(
189 r#"
190enum Foo { Bar, Baz }
191use self::Foo::*;
192"#,
193 expect![[r#"
194 crate
195 Bar: t v
196 Baz: t v
197 Foo: t
198 "#]],
199 );
200}
201
202#[test]
203fn glob_enum_group() {
204 mark::check!(glob_enum_group);
205 check(
206 r#"
207enum Foo { Bar, Baz }
208use self::Foo::{*};
209"#,
210 expect![[r#"
211 crate
212 Bar: t v
213 Baz: t v
214 Foo: t
215 "#]],
216 );
217}
218
219#[test]
220fn glob_shadowed_def() {
221 mark::check!(import_shadowed);
222 check(
223 r#"
224//- /lib.rs
225mod foo;
226mod bar;
227use foo::*;
228use bar::baz;
229use baz::Bar;
230
231//- /foo.rs
232pub mod baz { pub struct Foo; }
233
234//- /bar.rs
235pub mod baz { pub struct Bar; }
236"#,
237 expect![[r#"
238 crate
239 Bar: t v
240 bar: t
241 baz: t
242 foo: t
243
244 crate::bar
245 baz: t
246
247 crate::bar::baz
248 Bar: t v
249
250 crate::foo
251 baz: t
252
253 crate::foo::baz
254 Foo: t v
255 "#]],
256 );
257}
258
259#[test]
260fn glob_shadowed_def_reversed() {
261 check(
262 r#"
263//- /lib.rs
264mod foo;
265mod bar;
266use bar::baz;
267use foo::*;
268use baz::Bar;
269
270//- /foo.rs
271pub mod baz { pub struct Foo; }
272
273//- /bar.rs
274pub mod baz { pub struct Bar; }
275"#,
276 expect![[r#"
277 crate
278 Bar: t v
279 bar: t
280 baz: t
281 foo: t
282
283 crate::bar
284 baz: t
285
286 crate::bar::baz
287 Bar: t v
288
289 crate::foo
290 baz: t
291
292 crate::foo::baz
293 Foo: t v
294 "#]],
295 );
296}
297
298#[test]
299fn glob_shadowed_def_dependencies() {
300 check(
301 r#"
302mod a { pub mod foo { pub struct X; } }
303mod b { pub use super::a::foo; }
304mod c { pub mod foo { pub struct Y; } }
305mod d {
306 use super::c::foo;
307 use super::b::*;
308 use foo::Y;
309}
310"#,
311 expect![[r#"
312 crate
313 a: t
314 b: t
315 c: t
316 d: t
317
318 crate::d
319 Y: t v
320 foo: t
321
322 crate::c
323 foo: t
324
325 crate::c::foo
326 Y: t v
327
328 crate::b
329 foo: t
330
331 crate::a
332 foo: t
333
334 crate::a::foo
335 X: t v
336 "#]],
337 );
338}
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs
new file mode 100644
index 000000000..cfbc62cc4
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/incremental.rs
@@ -0,0 +1,101 @@
1use std::sync::Arc;
2
3use base_db::SourceDatabaseExt;
4
5use super::*;
6
7fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change: &str) {
8 let (mut db, pos) = TestDB::with_position(ra_fixture_initial);
9 let krate = db.test_crate();
10 {
11 let events = db.log_executed(|| {
12 db.crate_def_map(krate);
13 });
14 assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
15 }
16 db.set_file_text(pos.file_id, Arc::new(ra_fixture_change.to_string()));
17
18 {
19 let events = db.log_executed(|| {
20 db.crate_def_map(krate);
21 });
22 assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
23 }
24}
25
26#[test]
27fn typing_inside_a_function_should_not_invalidate_def_map() {
28 check_def_map_is_not_recomputed(
29 r"
30 //- /lib.rs
31 mod foo;<|>
32
33 use crate::foo::bar::Baz;
34
35 enum E { A, B }
36 use E::*;
37
38 fn foo() -> i32 {
39 1 + 1
40 }
41 //- /foo/mod.rs
42 pub mod bar;
43
44 //- /foo/bar.rs
45 pub struct Baz;
46 ",
47 r"
48 mod foo;
49
50 use crate::foo::bar::Baz;
51
52 enum E { A, B }
53 use E::*;
54
55 fn foo() -> i32 { 92 }
56 ",
57 );
58}
59
60#[test]
61fn typing_inside_a_macro_should_not_invalidate_def_map() {
62 let (mut db, pos) = TestDB::with_position(
63 r"
64 //- /lib.rs
65 macro_rules! m {
66 ($ident:ident) => {
67 fn f() {
68 $ident + $ident;
69 };
70 }
71 }
72 mod foo;
73
74 //- /foo/mod.rs
75 pub mod bar;
76
77 //- /foo/bar.rs
78 <|>
79 m!(X);
80 ",
81 );
82 let krate = db.test_crate();
83 {
84 let events = db.log_executed(|| {
85 let crate_def_map = db.crate_def_map(krate);
86 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
87 assert_eq!(module_data.scope.resolutions().count(), 1);
88 });
89 assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
90 }
91 db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string()));
92
93 {
94 let events = db.log_executed(|| {
95 let crate_def_map = db.crate_def_map(krate);
96 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
97 assert_eq!(module_data.scope.resolutions().count(), 1);
98 });
99 assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
100 }
101}
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
new file mode 100644
index 000000000..e0fb8bdef
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -0,0 +1,669 @@
1use super::*;
2
3#[test]
4fn macro_rules_are_globally_visible() {
5 check(
6 r#"
7//- /lib.rs
8macro_rules! structs {
9 ($($i:ident),*) => {
10 $(struct $i { field: u32 } )*
11 }
12}
13structs!(Foo);
14mod nested;
15
16//- /nested.rs
17structs!(Bar, Baz);
18"#,
19 expect![[r#"
20 crate
21 Foo: t
22 nested: t
23
24 crate::nested
25 Bar: t
26 Baz: t
27 "#]],
28 );
29}
30
31#[test]
32fn macro_rules_can_define_modules() {
33 check(
34 r#"
35//- /lib.rs
36macro_rules! m {
37 ($name:ident) => { mod $name; }
38}
39m!(n1);
40mod m { m!(n3) }
41
42//- /n1.rs
43m!(n2)
44//- /n1/n2.rs
45struct X;
46//- /m/n3.rs
47struct Y;
48"#,
49 expect![[r#"
50 crate
51 m: t
52 n1: t
53
54 crate::m
55 n3: t
56
57 crate::m::n3
58 Y: t v
59
60 crate::n1
61 n2: t
62
63 crate::n1::n2
64 X: t v
65 "#]],
66 );
67}
68
69#[test]
70fn macro_rules_from_other_crates_are_visible() {
71 check(
72 r#"
73//- /main.rs crate:main deps:foo
74foo::structs!(Foo, Bar)
75mod bar;
76
77//- /bar.rs
78use crate::*;
79
80//- /lib.rs crate:foo
81#[macro_export]
82macro_rules! structs {
83 ($($i:ident),*) => {
84 $(struct $i { field: u32 } )*
85 }
86}
87"#,
88 expect![[r#"
89 crate
90 Bar: t
91 Foo: t
92 bar: t
93
94 crate::bar
95 Bar: t
96 Foo: t
97 bar: t
98 "#]],
99 );
100}
101
102#[test]
103fn macro_rules_export_with_local_inner_macros_are_visible() {
104 check(
105 r#"
106//- /main.rs crate:main deps:foo
107foo::structs!(Foo, Bar)
108mod bar;
109
110//- /bar.rs
111use crate::*;
112
113//- /lib.rs crate:foo
114#[macro_export(local_inner_macros)]
115macro_rules! structs {
116 ($($i:ident),*) => {
117 $(struct $i { field: u32 } )*
118 }
119}
120"#,
121 expect![[r#"
122 crate
123 Bar: t
124 Foo: t
125 bar: t
126
127 crate::bar
128 Bar: t
129 Foo: t
130 bar: t
131 "#]],
132 );
133}
134
135#[test]
136fn local_inner_macros_makes_local_macros_usable() {
137 check(
138 r#"
139//- /main.rs crate:main deps:foo
140foo::structs!(Foo, Bar);
141mod bar;
142
143//- /bar.rs
144use crate::*;
145
146//- /lib.rs crate:foo
147#[macro_export(local_inner_macros)]
148macro_rules! structs {
149 ($($i:ident),*) => {
150 inner!($($i),*);
151 }
152}
153#[macro_export]
154macro_rules! inner {
155 ($($i:ident),*) => {
156 $(struct $i { field: u32 } )*
157 }
158}
159"#,
160 expect![[r#"
161 crate
162 Bar: t
163 Foo: t
164 bar: t
165
166 crate::bar
167 Bar: t
168 Foo: t
169 bar: t
170 "#]],
171 );
172}
173
174#[test]
175fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
176 check(
177 r#"
178//- /main.rs crate:main deps:foo
179macro_rules! baz {
180 () => {
181 use foo::bar;
182 }
183}
184foo!();
185bar!();
186baz!();
187
188//- /lib.rs crate:foo
189#[macro_export]
190macro_rules! foo {
191 () => {
192 struct Foo { field: u32 }
193 }
194}
195#[macro_export]
196macro_rules! bar {
197 () => {
198 use foo::foo;
199 }
200}
201"#,
202 expect![[r#"
203 crate
204 Foo: t
205 bar: m
206 foo: m
207 "#]],
208 );
209}
210
211#[test]
212fn macro_rules_from_other_crates_are_visible_with_macro_use() {
213 mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use);
214 check(
215 r#"
216//- /main.rs crate:main deps:foo
217structs!(Foo);
218structs_priv!(Bar);
219structs_not_exported!(MacroNotResolved1);
220crate::structs!(MacroNotResolved2);
221
222mod bar;
223
224#[macro_use]
225extern crate foo;
226
227//- /bar.rs
228structs!(Baz);
229crate::structs!(MacroNotResolved3);
230
231//- /lib.rs crate:foo
232#[macro_export]
233macro_rules! structs {
234 ($i:ident) => { struct $i; }
235}
236
237macro_rules! structs_not_exported {
238 ($i:ident) => { struct $i; }
239}
240
241mod priv_mod {
242 #[macro_export]
243 macro_rules! structs_priv {
244 ($i:ident) => { struct $i; }
245 }
246}
247"#,
248 expect![[r#"
249 crate
250 Bar: t v
251 Foo: t v
252 bar: t
253 foo: t
254
255 crate::bar
256 Baz: t v
257 "#]],
258 );
259}
260
261#[test]
262fn prelude_is_macro_use() {
263 mark::check!(prelude_is_macro_use);
264 check(
265 r#"
266//- /main.rs crate:main deps:foo
267structs!(Foo);
268structs_priv!(Bar);
269structs_outside!(Out);
270crate::structs!(MacroNotResolved2);
271
272mod bar;
273
274//- /bar.rs
275structs!(Baz);
276crate::structs!(MacroNotResolved3);
277
278//- /lib.rs crate:foo
279#[prelude_import]
280use self::prelude::*;
281
282mod prelude {
283 #[macro_export]
284 macro_rules! structs {
285 ($i:ident) => { struct $i; }
286 }
287
288 mod priv_mod {
289 #[macro_export]
290 macro_rules! structs_priv {
291 ($i:ident) => { struct $i; }
292 }
293 }
294}
295
296#[macro_export]
297macro_rules! structs_outside {
298 ($i:ident) => { struct $i; }
299}
300"#,
301 expect![[r#"
302 crate
303 Bar: t v
304 Foo: t v
305 Out: t v
306 bar: t
307
308 crate::bar
309 Baz: t v
310 "#]],
311 );
312}
313
314#[test]
315fn prelude_cycle() {
316 check(
317 r#"
318#[prelude_import]
319use self::prelude::*;
320
321declare_mod!();
322
323mod prelude {
324 macro_rules! declare_mod {
325 () => (mod foo {})
326 }
327}
328"#,
329 expect![[r#"
330 crate
331 prelude: t
332
333 crate::prelude
334 "#]],
335 );
336}
337
338#[test]
339fn plain_macros_are_legacy_textual_scoped() {
340 check(
341 r#"
342//- /main.rs
343mod m1;
344bar!(NotFoundNotMacroUse);
345
346mod m2 { foo!(NotFoundBeforeInside2); }
347
348macro_rules! foo {
349 ($x:ident) => { struct $x; }
350}
351foo!(Ok);
352
353mod m3;
354foo!(OkShadowStop);
355bar!(NotFoundMacroUseStop);
356
357#[macro_use]
358mod m5 {
359 #[macro_use]
360 mod m6 {
361 macro_rules! foo {
362 ($x:ident) => { fn $x() {} }
363 }
364 }
365}
366foo!(ok_double_macro_use_shadow);
367
368baz!(NotFoundBefore);
369#[macro_use]
370mod m7 {
371 macro_rules! baz {
372 ($x:ident) => { struct $x; }
373 }
374}
375baz!(OkAfter);
376
377//- /m1.rs
378foo!(NotFoundBeforeInside1);
379macro_rules! bar {
380 ($x:ident) => { struct $x; }
381}
382
383//- /m3/mod.rs
384foo!(OkAfterInside);
385macro_rules! foo {
386 ($x:ident) => { fn $x() {} }
387}
388foo!(ok_shadow);
389
390#[macro_use]
391mod m4;
392bar!(OkMacroUse);
393
394//- /m3/m4.rs
395foo!(ok_shadow_deep);
396macro_rules! bar {
397 ($x:ident) => { struct $x; }
398}
399"#,
400 expect![[r#"
401 crate
402 Ok: t v
403 OkAfter: t v
404 OkShadowStop: t v
405 m1: t
406 m2: t
407 m3: t
408 m5: t
409 m7: t
410 ok_double_macro_use_shadow: v
411
412 crate::m7
413
414 crate::m1
415
416 crate::m5
417 m6: t
418
419 crate::m5::m6
420
421 crate::m2
422
423 crate::m3
424 OkAfterInside: t v
425 OkMacroUse: t v
426 m4: t
427 ok_shadow: v
428
429 crate::m3::m4
430 ok_shadow_deep: v
431 "#]],
432 );
433}
434
435#[test]
436fn type_value_macro_live_in_different_scopes() {
437 check(
438 r#"
439#[macro_export]
440macro_rules! foo {
441 ($x:ident) => { type $x = (); }
442}
443
444foo!(foo);
445use foo as bar;
446
447use self::foo as baz;
448fn baz() {}
449"#,
450 expect![[r#"
451 crate
452 bar: t m
453 baz: t v m
454 foo: t m
455 "#]],
456 );
457}
458
459#[test]
460fn macro_use_can_be_aliased() {
461 check(
462 r#"
463//- /main.rs crate:main deps:foo
464#[macro_use]
465extern crate foo;
466
467foo!(Direct);
468bar!(Alias);
469
470//- /lib.rs crate:foo
471use crate::foo as bar;
472
473mod m {
474 #[macro_export]
475 macro_rules! foo {
476 ($x:ident) => { struct $x; }
477 }
478}
479"#,
480 expect![[r#"
481 crate
482 Alias: t v
483 Direct: t v
484 foo: t
485 "#]],
486 );
487}
488
489#[test]
490fn path_qualified_macros() {
491 check(
492 r#"
493macro_rules! foo {
494 ($x:ident) => { struct $x; }
495}
496
497crate::foo!(NotResolved);
498
499crate::bar!(OkCrate);
500bar!(OkPlain);
501alias1!(NotHere);
502m::alias1!(OkAliasPlain);
503m::alias2!(OkAliasSuper);
504m::alias3!(OkAliasCrate);
505not_found!(NotFound);
506
507mod m {
508 #[macro_export]
509 macro_rules! bar {
510 ($x:ident) => { struct $x; }
511 }
512 pub use bar as alias1;
513 pub use super::bar as alias2;
514 pub use crate::bar as alias3;
515 pub use self::bar as not_found;
516}
517"#,
518 expect![[r#"
519 crate
520 OkAliasCrate: t v
521 OkAliasPlain: t v
522 OkAliasSuper: t v
523 OkCrate: t v
524 OkPlain: t v
525 bar: m
526 m: t
527
528 crate::m
529 alias1: m
530 alias2: m
531 alias3: m
532 not_found: _
533 "#]],
534 );
535}
536
537#[test]
538fn macro_dollar_crate_is_correct_in_item() {
539 mark::check!(macro_dollar_crate_self);
540 check(
541 r#"
542//- /main.rs crate:main deps:foo
543#[macro_use]
544extern crate foo;
545
546#[macro_use]
547mod m {
548 macro_rules! current {
549 () => {
550 use $crate::Foo as FooSelf;
551 }
552 }
553}
554
555struct Foo;
556
557current!();
558not_current1!();
559foo::not_current2!();
560
561//- /lib.rs crate:foo
562mod m {
563 #[macro_export]
564 macro_rules! not_current1 {
565 () => {
566 use $crate::Bar;
567 }
568 }
569}
570
571#[macro_export]
572macro_rules! not_current2 {
573 () => {
574 use $crate::Baz;
575 }
576}
577
578struct Bar;
579struct Baz;
580"#,
581 expect![[r#"
582 crate
583 Bar: t v
584 Baz: t v
585 Foo: t v
586 FooSelf: t v
587 foo: t
588 m: t
589
590 crate::m
591 "#]],
592 );
593}
594
595#[test]
596fn macro_dollar_crate_is_correct_in_indirect_deps() {
597 mark::check!(macro_dollar_crate_other);
598 // From std
599 check(
600 r#"
601//- /main.rs crate:main deps:std
602foo!();
603
604//- /std.rs crate:std deps:core
605#[prelude_import]
606use self::prelude::*;
607
608pub use core::foo;
609
610mod prelude {}
611
612#[macro_use]
613mod std_macros;
614
615//- /core.rs crate:core
616#[macro_export]
617macro_rules! foo {
618 () => {
619 use $crate::bar;
620 }
621}
622
623pub struct bar;
624"#,
625 expect![[r#"
626 crate
627 bar: t v
628 "#]],
629 );
630}
631
632#[test]
633fn expand_derive() {
634 let map = compute_crate_def_map(
635 "
636 //- /main.rs
637 #[derive(Copy, Clone)]
638 struct Foo;
639 ",
640 );
641 assert_eq!(map.modules[map.root].scope.impls().len(), 2);
642}
643
644#[test]
645fn macro_expansion_overflow() {
646 mark::check!(macro_expansion_overflow);
647 check(
648 r#"
649macro_rules! a {
650 ($e:expr; $($t:tt)*) => {
651 b!($($t)*);
652 };
653 () => {};
654}
655
656macro_rules! b {
657 (static = $e:expr; $($t:tt)*) => {
658 a!($e; $($t)*);
659 };
660 () => {};
661}
662
663b! { static = #[] (); }
664"#,
665 expect![[r#"
666 crate
667 "#]],
668 );
669}
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs
new file mode 100644
index 000000000..1f619787e
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs
@@ -0,0 +1,796 @@
1use super::*;
2
3#[test]
4fn name_res_works_for_broken_modules() {
5 mark::check!(name_res_works_for_broken_modules);
6 check(
7 r"
8//- /lib.rs
9mod foo // no `;`, no body
10use self::foo::Baz;
11
12//- /foo/mod.rs
13pub mod bar;
14pub use self::bar::Baz;
15
16//- /foo/bar.rs
17pub struct Baz;
18",
19 expect![[r#"
20 crate
21 Baz: _
22 foo: t
23
24 crate::foo
25 "#]],
26 );
27}
28
29#[test]
30fn nested_module_resolution() {
31 check(
32 r#"
33//- /lib.rs
34mod n1;
35
36//- /n1.rs
37mod n2;
38
39//- /n1/n2.rs
40struct X;
41"#,
42 expect![[r#"
43 crate
44 n1: t
45
46 crate::n1
47 n2: t
48
49 crate::n1::n2
50 X: t v
51 "#]],
52 );
53}
54
55#[test]
56fn nested_module_resolution_2() {
57 check(
58 r#"
59//- /lib.rs
60mod prelude;
61mod iter;
62
63//- /prelude.rs
64pub use crate::iter::Iterator;
65
66//- /iter.rs
67pub use self::traits::Iterator;
68mod traits;
69
70//- /iter/traits.rs
71pub use self::iterator::Iterator;
72mod iterator;
73
74//- /iter/traits/iterator.rs
75pub trait Iterator;
76"#,
77 expect![[r#"
78 crate
79 iter: t
80 prelude: t
81
82 crate::iter
83 Iterator: t
84 traits: t
85
86 crate::iter::traits
87 Iterator: t
88 iterator: t
89
90 crate::iter::traits::iterator
91 Iterator: t
92
93 crate::prelude
94 Iterator: t
95 "#]],
96 );
97}
98
99#[test]
100fn module_resolution_works_for_non_standard_filenames() {
101 check(
102 r#"
103//- /my_library.rs crate:my_library
104mod foo;
105use self::foo::Bar;
106
107//- /foo/mod.rs
108pub struct Bar;
109"#,
110 expect![[r#"
111 crate
112 Bar: t v
113 foo: t
114
115 crate::foo
116 Bar: t v
117 "#]],
118 );
119}
120
121#[test]
122fn module_resolution_works_for_raw_modules() {
123 check(
124 r#"
125//- /lib.rs
126mod r#async;
127use self::r#async::Bar;
128
129//- /async.rs
130pub struct Bar;
131"#,
132 expect![[r#"
133 crate
134 Bar: t v
135 async: t
136
137 crate::async
138 Bar: t v
139 "#]],
140 );
141}
142
143#[test]
144fn module_resolution_decl_path() {
145 check(
146 r#"
147//- /lib.rs
148#[path = "bar/baz/foo.rs"]
149mod foo;
150use self::foo::Bar;
151
152//- /bar/baz/foo.rs
153pub struct Bar;
154"#,
155 expect![[r#"
156 crate
157 Bar: t v
158 foo: t
159
160 crate::foo
161 Bar: t v
162 "#]],
163 );
164}
165
166#[test]
167fn module_resolution_module_with_path_in_mod_rs() {
168 check(
169 r#"
170//- /main.rs
171mod foo;
172
173//- /foo/mod.rs
174#[path = "baz.rs"]
175pub mod bar;
176use self::bar::Baz;
177
178//- /foo/baz.rs
179pub struct Baz;
180"#,
181 expect![[r#"
182 crate
183 foo: t
184
185 crate::foo
186 Baz: t v
187 bar: t
188
189 crate::foo::bar
190 Baz: t v
191 "#]],
192 );
193}
194
195#[test]
196fn module_resolution_module_with_path_non_crate_root() {
197 check(
198 r#"
199//- /main.rs
200mod foo;
201
202//- /foo.rs
203#[path = "baz.rs"]
204pub mod bar;
205use self::bar::Baz;
206
207//- /baz.rs
208pub struct Baz;
209"#,
210 expect![[r#"
211 crate
212 foo: t
213
214 crate::foo
215 Baz: t v
216 bar: t
217
218 crate::foo::bar
219 Baz: t v
220 "#]],
221 );
222}
223
224#[test]
225fn module_resolution_module_decl_path_super() {
226 check(
227 r#"
228//- /main.rs
229#[path = "bar/baz/module.rs"]
230mod foo;
231pub struct Baz;
232
233//- /bar/baz/module.rs
234use super::Baz;
235"#,
236 expect![[r#"
237 crate
238 Baz: t v
239 foo: t
240
241 crate::foo
242 Baz: t v
243 "#]],
244 );
245}
246
247#[test]
248fn module_resolution_explicit_path_mod_rs() {
249 check(
250 r#"
251//- /main.rs
252#[path = "module/mod.rs"]
253mod foo;
254
255//- /module/mod.rs
256pub struct Baz;
257"#,
258 expect![[r#"
259 crate
260 foo: t
261
262 crate::foo
263 Baz: t v
264 "#]],
265 );
266}
267
268#[test]
269fn module_resolution_relative_path() {
270 check(
271 r#"
272//- /main.rs
273mod foo;
274
275//- /foo.rs
276#[path = "./sub.rs"]
277pub mod foo_bar;
278
279//- /sub.rs
280pub struct Baz;
281"#,
282 expect![[r#"
283 crate
284 foo: t
285
286 crate::foo
287 foo_bar: t
288
289 crate::foo::foo_bar
290 Baz: t v
291 "#]],
292 );
293}
294
295#[test]
296fn module_resolution_relative_path_2() {
297 check(
298 r#"
299//- /main.rs
300mod foo;
301
302//- /foo/mod.rs
303#[path="../sub.rs"]
304pub mod foo_bar;
305
306//- /sub.rs
307pub struct Baz;
308"#,
309 expect![[r#"
310 crate
311 foo: t
312
313 crate::foo
314 foo_bar: t
315
316 crate::foo::foo_bar
317 Baz: t v
318 "#]],
319 );
320}
321
322#[test]
323fn module_resolution_relative_path_outside_root() {
324 check(
325 r#"
326//- /main.rs
327#[path="../../../../../outside.rs"]
328mod foo;
329"#,
330 expect![[r#"
331 crate
332 "#]],
333 );
334}
335
336#[test]
337fn module_resolution_explicit_path_mod_rs_2() {
338 check(
339 r#"
340//- /main.rs
341#[path = "module/bar/mod.rs"]
342mod foo;
343
344//- /module/bar/mod.rs
345pub struct Baz;
346"#,
347 expect![[r#"
348 crate
349 foo: t
350
351 crate::foo
352 Baz: t v
353 "#]],
354 );
355}
356
357#[test]
358fn module_resolution_explicit_path_mod_rs_with_win_separator() {
359 check(
360 r#"
361//- /main.rs
362#[path = "module\bar\mod.rs"]
363mod foo;
364
365//- /module/bar/mod.rs
366pub struct Baz;
367"#,
368 expect![[r#"
369 crate
370 foo: t
371
372 crate::foo
373 Baz: t v
374 "#]],
375 );
376}
377
378#[test]
379fn module_resolution_decl_inside_inline_module_with_path_attribute() {
380 check(
381 r#"
382//- /main.rs
383#[path = "models"]
384mod foo { mod bar; }
385
386//- /models/bar.rs
387pub struct Baz;
388"#,
389 expect![[r#"
390 crate
391 foo: t
392
393 crate::foo
394 bar: t
395
396 crate::foo::bar
397 Baz: t v
398 "#]],
399 );
400}
401
402#[test]
403fn module_resolution_decl_inside_inline_module() {
404 check(
405 r#"
406//- /main.rs
407mod foo { mod bar; }
408
409//- /foo/bar.rs
410pub struct Baz;
411"#,
412 expect![[r#"
413 crate
414 foo: t
415
416 crate::foo
417 bar: t
418
419 crate::foo::bar
420 Baz: t v
421 "#]],
422 );
423}
424
425#[test]
426fn module_resolution_decl_inside_inline_module_2_with_path_attribute() {
427 check(
428 r#"
429//- /main.rs
430#[path = "models/db"]
431mod foo { mod bar; }
432
433//- /models/db/bar.rs
434pub struct Baz;
435"#,
436 expect![[r#"
437 crate
438 foo: t
439
440 crate::foo
441 bar: t
442
443 crate::foo::bar
444 Baz: t v
445 "#]],
446 );
447}
448
449#[test]
450fn module_resolution_decl_inside_inline_module_3() {
451 check(
452 r#"
453//- /main.rs
454#[path = "models/db"]
455mod foo {
456 #[path = "users.rs"]
457 mod bar;
458}
459
460//- /models/db/users.rs
461pub struct Baz;
462"#,
463 expect![[r#"
464 crate
465 foo: t
466
467 crate::foo
468 bar: t
469
470 crate::foo::bar
471 Baz: t v
472 "#]],
473 );
474}
475
476#[test]
477fn module_resolution_decl_inside_inline_module_empty_path() {
478 check(
479 r#"
480//- /main.rs
481#[path = ""]
482mod foo {
483 #[path = "users.rs"]
484 mod bar;
485}
486
487//- /users.rs
488pub struct Baz;
489"#,
490 expect![[r#"
491 crate
492 foo: t
493
494 crate::foo
495 bar: t
496
497 crate::foo::bar
498 Baz: t v
499 "#]],
500 );
501}
502
503#[test]
504fn module_resolution_decl_empty_path() {
505 check(
506 r#"
507//- /main.rs
508#[path = ""] // Should try to read `/` (a directory)
509mod foo;
510
511//- /foo.rs
512pub struct Baz;
513"#,
514 expect![[r#"
515 crate
516 "#]],
517 );
518}
519
520#[test]
521fn module_resolution_decl_inside_inline_module_relative_path() {
522 check(
523 r#"
524//- /main.rs
525#[path = "./models"]
526mod foo { mod bar; }
527
528//- /models/bar.rs
529pub struct Baz;
530"#,
531 expect![[r#"
532 crate
533 foo: t
534
535 crate::foo
536 bar: t
537
538 crate::foo::bar
539 Baz: t v
540 "#]],
541 );
542}
543
544#[test]
545fn module_resolution_decl_inside_inline_module_in_crate_root() {
546 check(
547 r#"
548//- /main.rs
549mod foo {
550 #[path = "baz.rs"]
551 mod bar;
552}
553use self::foo::bar::Baz;
554
555//- /foo/baz.rs
556pub struct Baz;
557"#,
558 expect![[r#"
559 crate
560 Baz: t v
561 foo: t
562
563 crate::foo
564 bar: t
565
566 crate::foo::bar
567 Baz: t v
568 "#]],
569 );
570}
571
572#[test]
573fn module_resolution_decl_inside_inline_module_in_mod_rs() {
574 check(
575 r#"
576//- /main.rs
577mod foo;
578
579//- /foo/mod.rs
580mod bar {
581 #[path = "qwe.rs"]
582 pub mod baz;
583}
584use self::bar::baz::Baz;
585
586//- /foo/bar/qwe.rs
587pub struct Baz;
588"#,
589 expect![[r#"
590 crate
591 foo: t
592
593 crate::foo
594 Baz: t v
595 bar: t
596
597 crate::foo::bar
598 baz: t
599
600 crate::foo::bar::baz
601 Baz: t v
602 "#]],
603 );
604}
605
606#[test]
607fn module_resolution_decl_inside_inline_module_in_non_crate_root() {
608 check(
609 r#"
610//- /main.rs
611mod foo;
612
613//- /foo.rs
614mod bar {
615 #[path = "qwe.rs"]
616 pub mod baz;
617}
618use self::bar::baz::Baz;
619
620//- /foo/bar/qwe.rs
621pub struct Baz;
622"#,
623 expect![[r#"
624 crate
625 foo: t
626
627 crate::foo
628 Baz: t v
629 bar: t
630
631 crate::foo::bar
632 baz: t
633
634 crate::foo::bar::baz
635 Baz: t v
636 "#]],
637 );
638}
639
640#[test]
641fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() {
642 check(
643 r#"
644//- /main.rs
645mod foo;
646
647//- /foo.rs
648#[path = "bar"]
649mod bar {
650 pub mod baz;
651}
652use self::bar::baz::Baz;
653
654//- /bar/baz.rs
655pub struct Baz;
656"#,
657 expect![[r#"
658 crate
659 foo: t
660
661 crate::foo
662 Baz: t v
663 bar: t
664
665 crate::foo::bar
666 baz: t
667
668 crate::foo::bar::baz
669 Baz: t v
670 "#]],
671 );
672}
673
674#[test]
675fn unresolved_module_diagnostics() {
676 let db = TestDB::with_files(
677 r"
678 //- /lib.rs
679 mod foo;
680 mod bar;
681 mod baz {}
682 //- /foo.rs
683 ",
684 );
685 let krate = db.test_crate();
686
687 let crate_def_map = db.crate_def_map(krate);
688
689 expect![[r#"
690 [
691 UnresolvedModule {
692 module: Idx::<ModuleData>(0),
693 declaration: InFile {
694 file_id: HirFileId(
695 FileId(
696 FileId(
697 0,
698 ),
699 ),
700 ),
701 value: FileAstId::<syntax::ast::generated::nodes::Module>(1),
702 },
703 candidate: "bar.rs",
704 },
705 ]
706 "#]]
707 .assert_debug_eq(&crate_def_map.diagnostics);
708}
709
710#[test]
711fn module_resolution_decl_inside_module_in_non_crate_root_2() {
712 check(
713 r#"
714//- /main.rs
715#[path="module/m2.rs"]
716mod module;
717
718//- /module/m2.rs
719pub mod submod;
720
721//- /module/submod.rs
722pub struct Baz;
723"#,
724 expect![[r#"
725 crate
726 module: t
727
728 crate::module
729 submod: t
730
731 crate::module::submod
732 Baz: t v
733 "#]],
734 );
735}
736
737#[test]
738fn nested_out_of_line_module() {
739 check(
740 r#"
741//- /lib.rs
742mod a {
743 mod b {
744 mod c;
745 }
746}
747
748//- /a/b/c.rs
749struct X;
750"#,
751 expect![[r#"
752 crate
753 a: t
754
755 crate::a
756 b: t
757
758 crate::a::b
759 c: t
760
761 crate::a::b::c
762 X: t v
763 "#]],
764 );
765}
766
767#[test]
768fn nested_out_of_line_module_with_path() {
769 check(
770 r#"
771//- /lib.rs
772mod a {
773 #[path = "d/e"]
774 mod b {
775 mod c;
776 }
777}
778
779//- /a/d/e/c.rs
780struct X;
781"#,
782 expect![[r#"
783 crate
784 a: t
785
786 crate::a
787 b: t
788
789 crate::a::b
790 c: t
791
792 crate::a::b::c
793 X: t v
794 "#]],
795 );
796}
diff --git a/crates/hir_def/src/nameres/tests/primitives.rs b/crates/hir_def/src/nameres/tests/primitives.rs
new file mode 100644
index 000000000..215e8952d
--- /dev/null
+++ b/crates/hir_def/src/nameres/tests/primitives.rs
@@ -0,0 +1,23 @@
1use super::*;
2
3#[test]
4fn primitive_reexport() {
5 check(
6 r#"
7//- /lib.rs
8mod foo;
9use foo::int;
10
11//- /foo.rs
12pub use i32 as int;
13"#,
14 expect![[r#"
15 crate
16 foo: t
17 int: t
18
19 crate::foo
20 int: t
21 "#]],
22 );
23}
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
new file mode 100644
index 000000000..99395667d
--- /dev/null
+++ b/crates/hir_def/src/path.rs
@@ -0,0 +1,345 @@
1//! A desugared representation of paths like `crate::foo` or `<Type as Trait>::bar`.
2mod lower;
3
4use std::{
5 fmt::{self, Display},
6 iter,
7 sync::Arc,
8};
9
10use crate::body::LowerCtx;
11use base_db::CrateId;
12use hir_expand::{
13 hygiene::Hygiene,
14 name::{AsName, Name},
15};
16use syntax::ast;
17
18use crate::{
19 type_ref::{TypeBound, TypeRef},
20 InFile,
21};
22
23#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub struct ModPath {
25 pub kind: PathKind,
26 pub segments: Vec<Name>,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub enum PathKind {
31 Plain,
32 /// `self::` is `Super(0)`
33 Super(u8),
34 Crate,
35 /// Absolute path (::foo)
36 Abs,
37 /// `$crate` from macro expansion
38 DollarCrate(CrateId),
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum ImportAlias {
43 /// Unnamed alias, as in `use Foo as _;`
44 Underscore,
45 /// Named alias
46 Alias(Name),
47}
48
49impl ModPath {
50 pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
51 lower::lower_path(path, hygiene).map(|it| it.mod_path)
52 }
53
54 pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
55 let segments = segments.into_iter().collect::<Vec<_>>();
56 ModPath { kind, segments }
57 }
58
59 pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> ModPath {
60 name_ref.as_name().into()
61 }
62
63 /// Converts an `tt::Ident` into a single-identifier `Path`.
64 pub(crate) fn from_tt_ident(ident: &tt::Ident) -> ModPath {
65 ident.as_name().into()
66 }
67
68 /// Calls `cb` with all paths, represented by this use item.
69 pub(crate) fn expand_use_item(
70 item_src: InFile<ast::Use>,
71 hygiene: &Hygiene,
72 mut cb: impl FnMut(ModPath, &ast::UseTree, /* is_glob */ bool, Option<ImportAlias>),
73 ) {
74 if let Some(tree) = item_src.value.use_tree() {
75 lower::lower_use_tree(None, tree, hygiene, &mut cb);
76 }
77 }
78
79 /// Returns the number of segments in the path (counting special segments like `$crate` and
80 /// `super`).
81 pub fn len(&self) -> usize {
82 self.segments.len()
83 + match self.kind {
84 PathKind::Plain => 0,
85 PathKind::Super(i) => i as usize,
86 PathKind::Crate => 1,
87 PathKind::Abs => 0,
88 PathKind::DollarCrate(_) => 1,
89 }
90 }
91
92 pub fn is_ident(&self) -> bool {
93 self.kind == PathKind::Plain && self.segments.len() == 1
94 }
95
96 pub fn is_self(&self) -> bool {
97 self.kind == PathKind::Super(0) && self.segments.is_empty()
98 }
99
100 /// If this path is a single identifier, like `foo`, return its name.
101 pub fn as_ident(&self) -> Option<&Name> {
102 if self.kind != PathKind::Plain || self.segments.len() > 1 {
103 return None;
104 }
105 self.segments.first()
106 }
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Hash)]
110pub struct Path {
111 /// Type based path like `<T>::foo`.
112 /// Note that paths like `<Type as Trait>::foo` are desugard to `Trait::<Self=Type>::foo`.
113 type_anchor: Option<Box<TypeRef>>,
114 mod_path: ModPath,
115 /// Invariant: the same len as `self.mod_path.segments`
116 generic_args: Vec<Option<Arc<GenericArgs>>>,
117}
118
119/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
120/// also includes bindings of associated types, like in `Iterator<Item = Foo>`.
121#[derive(Debug, Clone, PartialEq, Eq, Hash)]
122pub struct GenericArgs {
123 pub args: Vec<GenericArg>,
124 /// This specifies whether the args contain a Self type as the first
125 /// element. This is the case for path segments like `<T as Trait>`, where
126 /// `T` is actually a type parameter for the path `Trait` specifying the
127 /// Self type. Otherwise, when we have a path `Trait<X, Y>`, the Self type
128 /// is left out.
129 pub has_self_type: bool,
130 /// Associated type bindings like in `Iterator<Item = T>`.
131 pub bindings: Vec<AssociatedTypeBinding>,
132}
133
134/// An associated type binding like in `Iterator<Item = T>`.
135#[derive(Debug, Clone, PartialEq, Eq, Hash)]
136pub struct AssociatedTypeBinding {
137 /// The name of the associated type.
138 pub name: Name,
139 /// The type bound to this associated type (in `Item = T`, this would be the
140 /// `T`). This can be `None` if there are bounds instead.
141 pub type_ref: Option<TypeRef>,
142 /// Bounds for the associated type, like in `Iterator<Item:
143 /// SomeOtherTrait>`. (This is the unstable `associated_type_bounds`
144 /// feature.)
145 pub bounds: Vec<TypeBound>,
146}
147
148/// A single generic argument.
149#[derive(Debug, Clone, PartialEq, Eq, Hash)]
150pub enum GenericArg {
151 Type(TypeRef),
152 // or lifetime...
153}
154
155impl Path {
156 /// Converts an `ast::Path` to `Path`. Works with use trees.
157 /// It correctly handles `$crate` based path from macro call.
158 pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
159 lower::lower_path(path, hygiene)
160 }
161
162 /// Converts a known mod path to `Path`.
163 pub(crate) fn from_known_path(
164 path: ModPath,
165 generic_args: Vec<Option<Arc<GenericArgs>>>,
166 ) -> Path {
167 Path { type_anchor: None, mod_path: path, generic_args }
168 }
169
170 pub fn kind(&self) -> &PathKind {
171 &self.mod_path.kind
172 }
173
174 pub fn type_anchor(&self) -> Option<&TypeRef> {
175 self.type_anchor.as_deref()
176 }
177
178 pub fn segments(&self) -> PathSegments<'_> {
179 PathSegments {
180 segments: self.mod_path.segments.as_slice(),
181 generic_args: self.generic_args.as_slice(),
182 }
183 }
184
185 pub fn mod_path(&self) -> &ModPath {
186 &self.mod_path
187 }
188
189 pub fn qualifier(&self) -> Option<Path> {
190 if self.mod_path.is_ident() {
191 return None;
192 }
193 let res = Path {
194 type_anchor: self.type_anchor.clone(),
195 mod_path: ModPath {
196 kind: self.mod_path.kind.clone(),
197 segments: self.mod_path.segments[..self.mod_path.segments.len() - 1].to_vec(),
198 },
199 generic_args: self.generic_args[..self.generic_args.len() - 1].to_vec(),
200 };
201 Some(res)
202 }
203}
204
205#[derive(Debug, Clone, PartialEq, Eq, Hash)]
206pub struct PathSegment<'a> {
207 pub name: &'a Name,
208 pub args_and_bindings: Option<&'a GenericArgs>,
209}
210
211pub struct PathSegments<'a> {
212 segments: &'a [Name],
213 generic_args: &'a [Option<Arc<GenericArgs>>],
214}
215
216impl<'a> PathSegments<'a> {
217 pub const EMPTY: PathSegments<'static> = PathSegments { segments: &[], generic_args: &[] };
218 pub fn is_empty(&self) -> bool {
219 self.len() == 0
220 }
221 pub fn len(&self) -> usize {
222 self.segments.len()
223 }
224 pub fn first(&self) -> Option<PathSegment<'a>> {
225 self.get(0)
226 }
227 pub fn last(&self) -> Option<PathSegment<'a>> {
228 self.get(self.len().checked_sub(1)?)
229 }
230 pub fn get(&self, idx: usize) -> Option<PathSegment<'a>> {
231 assert_eq!(self.segments.len(), self.generic_args.len());
232 let res = PathSegment {
233 name: self.segments.get(idx)?,
234 args_and_bindings: self.generic_args.get(idx).unwrap().as_ref().map(|it| &**it),
235 };
236 Some(res)
237 }
238 pub fn skip(&self, len: usize) -> PathSegments<'a> {
239 assert_eq!(self.segments.len(), self.generic_args.len());
240 PathSegments { segments: &self.segments[len..], generic_args: &self.generic_args[len..] }
241 }
242 pub fn take(&self, len: usize) -> PathSegments<'a> {
243 assert_eq!(self.segments.len(), self.generic_args.len());
244 PathSegments { segments: &self.segments[..len], generic_args: &self.generic_args[..len] }
245 }
246 pub fn iter(&self) -> impl Iterator<Item = PathSegment<'a>> {
247 self.segments.iter().zip(self.generic_args.iter()).map(|(name, args)| PathSegment {
248 name,
249 args_and_bindings: args.as_ref().map(|it| &**it),
250 })
251 }
252}
253
254impl GenericArgs {
255 pub(crate) fn from_ast(lower_ctx: &LowerCtx, node: ast::GenericArgList) -> Option<GenericArgs> {
256 lower::lower_generic_args(lower_ctx, node)
257 }
258
259 pub(crate) fn empty() -> GenericArgs {
260 GenericArgs { args: Vec::new(), has_self_type: false, bindings: Vec::new() }
261 }
262}
263
264impl From<Name> for Path {
265 fn from(name: Name) -> Path {
266 Path {
267 type_anchor: None,
268 mod_path: ModPath::from_segments(PathKind::Plain, iter::once(name)),
269 generic_args: vec![None],
270 }
271 }
272}
273
274impl From<Name> for ModPath {
275 fn from(name: Name) -> ModPath {
276 ModPath::from_segments(PathKind::Plain, iter::once(name))
277 }
278}
279
280impl Display for ModPath {
281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282 let mut first_segment = true;
283 let mut add_segment = |s| -> fmt::Result {
284 if !first_segment {
285 f.write_str("::")?;
286 }
287 first_segment = false;
288 f.write_str(s)?;
289 Ok(())
290 };
291 match self.kind {
292 PathKind::Plain => {}
293 PathKind::Super(n) => {
294 if n == 0 {
295 add_segment("self")?;
296 }
297 for _ in 0..n {
298 add_segment("super")?;
299 }
300 }
301 PathKind::Crate => add_segment("crate")?,
302 PathKind::Abs => add_segment("")?,
303 PathKind::DollarCrate(_) => add_segment("$crate")?,
304 }
305 for segment in &self.segments {
306 if !first_segment {
307 f.write_str("::")?;
308 }
309 first_segment = false;
310 write!(f, "{}", segment)?;
311 }
312 Ok(())
313 }
314}
315
316pub use hir_expand::name as __name;
317
318#[macro_export]
319macro_rules! __known_path {
320 (core::iter::IntoIterator) => {};
321 (core::result::Result) => {};
322 (core::ops::Range) => {};
323 (core::ops::RangeFrom) => {};
324 (core::ops::RangeFull) => {};
325 (core::ops::RangeTo) => {};
326 (core::ops::RangeToInclusive) => {};
327 (core::ops::RangeInclusive) => {};
328 (core::future::Future) => {};
329 (core::ops::Try) => {};
330 ($path:path) => {
331 compile_error!("Please register your known path in the path module")
332 };
333}
334
335#[macro_export]
336macro_rules! __path {
337 ($start:ident $(:: $seg:ident)*) => ({
338 $crate::__known_path!($start $(:: $seg)*);
339 $crate::path::ModPath::from_segments($crate::path::PathKind::Abs, vec![
340 $crate::path::__name![$start], $($crate::path::__name![$seg],)*
341 ])
342 });
343}
344
345pub use crate::__path as path;
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
new file mode 100644
index 000000000..07b9723ce
--- /dev/null
+++ b/crates/hir_def/src/path/lower.rs
@@ -0,0 +1,215 @@
1//! Transforms syntax into `Path` objects, ideally with accounting for hygiene
2
3mod lower_use;
4
5use std::sync::Arc;
6
7use either::Either;
8use hir_expand::{
9 hygiene::Hygiene,
10 name::{name, AsName},
11};
12use syntax::ast::{self, AstNode, TypeBoundsOwner};
13
14use super::AssociatedTypeBinding;
15use crate::{
16 body::LowerCtx,
17 path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
18 type_ref::{TypeBound, TypeRef},
19};
20
21pub(super) use lower_use::lower_use_tree;
22
23/// Converts an `ast::Path` to `Path`. Works with use trees.
24/// It correctly handles `$crate` based path from macro call.
25pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
26 let mut kind = PathKind::Plain;
27 let mut type_anchor = None;
28 let mut segments = Vec::new();
29 let mut generic_args = Vec::new();
30 let ctx = LowerCtx::with_hygiene(hygiene);
31 loop {
32 let segment = path.segment()?;
33
34 if segment.coloncolon_token().is_some() {
35 kind = PathKind::Abs;
36 }
37
38 match segment.kind()? {
39 ast::PathSegmentKind::Name(name_ref) => {
40 // FIXME: this should just return name
41 match hygiene.name_ref_to_name(name_ref) {
42 Either::Left(name) => {
43 let args = segment
44 .generic_arg_list()
45 .and_then(|it| lower_generic_args(&ctx, it))
46 .or_else(|| {
47 lower_generic_args_from_fn_path(
48 &ctx,
49 segment.param_list(),
50 segment.ret_type(),
51 )
52 })
53 .map(Arc::new);
54 segments.push(name);
55 generic_args.push(args)
56 }
57 Either::Right(crate_id) => {
58 kind = PathKind::DollarCrate(crate_id);
59 break;
60 }
61 }
62 }
63 ast::PathSegmentKind::Type { type_ref, trait_ref } => {
64 assert!(path.qualifier().is_none()); // this can only occur at the first segment
65
66 let self_type = TypeRef::from_ast(&ctx, type_ref?);
67
68 match trait_ref {
69 // <T>::foo
70 None => {
71 type_anchor = Some(Box::new(self_type));
72 kind = PathKind::Plain;
73 }
74 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
75 Some(trait_ref) => {
76 let path = Path::from_src(trait_ref.path()?, hygiene)?;
77 kind = path.mod_path.kind;
78
79 let mut prefix_segments = path.mod_path.segments;
80 prefix_segments.reverse();
81 segments.extend(prefix_segments);
82
83 let mut prefix_args = path.generic_args;
84 prefix_args.reverse();
85 generic_args.extend(prefix_args);
86
87 // Insert the type reference (T in the above example) as Self parameter for the trait
88 let last_segment = generic_args.last_mut()?;
89 if last_segment.is_none() {
90 *last_segment = Some(Arc::new(GenericArgs::empty()));
91 };
92 let args = last_segment.as_mut().unwrap();
93 let mut args_inner = Arc::make_mut(args);
94 args_inner.has_self_type = true;
95 args_inner.args.insert(0, GenericArg::Type(self_type));
96 }
97 }
98 }
99 ast::PathSegmentKind::CrateKw => {
100 kind = PathKind::Crate;
101 break;
102 }
103 ast::PathSegmentKind::SelfKw => {
104 kind = PathKind::Super(0);
105 break;
106 }
107 ast::PathSegmentKind::SuperKw => {
108 let nested_super_count = if let PathKind::Super(n) = kind { n } else { 0 };
109 kind = PathKind::Super(nested_super_count + 1);
110 }
111 }
112 path = match qualifier(&path) {
113 Some(it) => it,
114 None => break,
115 };
116 }
117 segments.reverse();
118 generic_args.reverse();
119
120 // handle local_inner_macros :
121 // Basically, even in rustc it is quite hacky:
122 // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456
123 // We follow what it did anyway :)
124 if segments.len() == 1 && kind == PathKind::Plain {
125 if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
126 if macro_call.is_bang() {
127 if let Some(crate_id) = hygiene.local_inner_macros() {
128 kind = PathKind::DollarCrate(crate_id);
129 }
130 }
131 }
132 }
133
134 let mod_path = ModPath { kind, segments };
135 return Some(Path { type_anchor, mod_path, generic_args });
136
137 fn qualifier(path: &ast::Path) -> Option<ast::Path> {
138 if let Some(q) = path.qualifier() {
139 return Some(q);
140 }
141 // FIXME: this bottom up traversal is not too precise.
142 // Should we handle do a top-down analysis, recording results?
143 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
144 let use_tree = use_tree_list.parent_use_tree();
145 use_tree.path()
146 }
147}
148
149pub(super) fn lower_generic_args(
150 lower_ctx: &LowerCtx,
151 node: ast::GenericArgList,
152) -> Option<GenericArgs> {
153 let mut args = Vec::new();
154 let mut bindings = Vec::new();
155 for generic_arg in node.generic_args() {
156 match generic_arg {
157 ast::GenericArg::TypeArg(type_arg) => {
158 let type_ref = TypeRef::from_ast_opt(lower_ctx, type_arg.ty());
159 args.push(GenericArg::Type(type_ref));
160 }
161 ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
162 if let Some(name_ref) = assoc_type_arg.name_ref() {
163 let name = name_ref.as_name();
164 let type_ref = assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it));
165 let bounds = if let Some(l) = assoc_type_arg.type_bound_list() {
166 l.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect()
167 } else {
168 Vec::new()
169 };
170 bindings.push(AssociatedTypeBinding { name, type_ref, bounds });
171 }
172 }
173 // Lifetimes and constants are ignored for now.
174 ast::GenericArg::LifetimeArg(_) | ast::GenericArg::ConstArg(_) => (),
175 }
176 }
177
178 if args.is_empty() && bindings.is_empty() {
179 return None;
180 }
181 Some(GenericArgs { args, has_self_type: false, bindings })
182}
183
184/// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
185/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
186fn lower_generic_args_from_fn_path(
187 ctx: &LowerCtx,
188 params: Option<ast::ParamList>,
189 ret_type: Option<ast::RetType>,
190) -> Option<GenericArgs> {
191 let mut args = Vec::new();
192 let mut bindings = Vec::new();
193 if let Some(params) = params {
194 let mut param_types = Vec::new();
195 for param in params.params() {
196 let type_ref = TypeRef::from_ast_opt(&ctx, param.ty());
197 param_types.push(type_ref);
198 }
199 let arg = GenericArg::Type(TypeRef::Tuple(param_types));
200 args.push(arg);
201 }
202 if let Some(ret_type) = ret_type {
203 let type_ref = TypeRef::from_ast_opt(&ctx, ret_type.ty());
204 bindings.push(AssociatedTypeBinding {
205 name: name![Output],
206 type_ref: Some(type_ref),
207 bounds: Vec::new(),
208 });
209 }
210 if args.is_empty() && bindings.is_empty() {
211 None
212 } else {
213 Some(GenericArgs { args, has_self_type: false, bindings })
214 }
215}
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs
new file mode 100644
index 000000000..53cecb05f
--- /dev/null
+++ b/crates/hir_def/src/path/lower/lower_use.rs
@@ -0,0 +1,120 @@
1//! Lowers a single complex use like `use foo::{bar, baz};` into a list of paths like
2//! `foo::bar`, `foo::baz`;
3
4use std::iter;
5
6use either::Either;
7use hir_expand::{hygiene::Hygiene, name::AsName};
8use syntax::ast::{self, NameOwner};
9use test_utils::mark;
10
11use crate::path::{ImportAlias, ModPath, PathKind};
12
13pub(crate) fn lower_use_tree(
14 prefix: Option<ModPath>,
15 tree: ast::UseTree,
16 hygiene: &Hygiene,
17 cb: &mut dyn FnMut(ModPath, &ast::UseTree, bool, Option<ImportAlias>),
18) {
19 if let Some(use_tree_list) = tree.use_tree_list() {
20 let prefix = match tree.path() {
21 // E.g. use something::{{{inner}}};
22 None => prefix,
23 // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
24 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
25 Some(path) => match convert_path(prefix, path, hygiene) {
26 Some(it) => Some(it),
27 None => return, // FIXME: report errors somewhere
28 },
29 };
30 for child_tree in use_tree_list.use_trees() {
31 lower_use_tree(prefix.clone(), child_tree, hygiene, cb);
32 }
33 } else {
34 let alias = tree.rename().map(|a| {
35 a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
36 });
37 let is_glob = tree.star_token().is_some();
38 if let Some(ast_path) = tree.path() {
39 // Handle self in a path.
40 // E.g. `use something::{self, <...>}`
41 if ast_path.qualifier().is_none() {
42 if let Some(segment) = ast_path.segment() {
43 if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
44 if let Some(prefix) = prefix {
45 cb(prefix, &tree, false, alias);
46 return;
47 }
48 }
49 }
50 }
51 if let Some(path) = convert_path(prefix, ast_path, hygiene) {
52 cb(path, &tree, is_glob, alias)
53 }
54 // FIXME: report errors somewhere
55 // We get here if we do
56 } else if is_glob {
57 mark::hit!(glob_enum_group);
58 if let Some(prefix) = prefix {
59 cb(prefix, &tree, is_glob, None)
60 }
61 }
62 }
63}
64
65fn convert_path(prefix: Option<ModPath>, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
66 let prefix = if let Some(qual) = path.qualifier() {
67 Some(convert_path(prefix, qual, hygiene)?)
68 } else {
69 prefix
70 };
71
72 let segment = path.segment()?;
73 let res = match segment.kind()? {
74 ast::PathSegmentKind::Name(name_ref) => {
75 match hygiene.name_ref_to_name(name_ref) {
76 Either::Left(name) => {
77 // no type args in use
78 let mut res = prefix.unwrap_or_else(|| ModPath {
79 kind: PathKind::Plain,
80 segments: Vec::with_capacity(1),
81 });
82 res.segments.push(name);
83 res
84 }
85 Either::Right(crate_id) => {
86 return Some(ModPath::from_segments(
87 PathKind::DollarCrate(crate_id),
88 iter::empty(),
89 ))
90 }
91 }
92 }
93 ast::PathSegmentKind::CrateKw => {
94 if prefix.is_some() {
95 return None;
96 }
97 ModPath::from_segments(PathKind::Crate, iter::empty())
98 }
99 ast::PathSegmentKind::SelfKw => {
100 if prefix.is_some() {
101 return None;
102 }
103 ModPath::from_segments(PathKind::Super(0), iter::empty())
104 }
105 ast::PathSegmentKind::SuperKw => {
106 let nested_super_count = match prefix.map(|p| p.kind) {
107 Some(PathKind::Super(n)) => n,
108 Some(_) => return None,
109 None => 0,
110 };
111
112 ModPath::from_segments(PathKind::Super(nested_super_count + 1), iter::empty())
113 }
114 ast::PathSegmentKind::Type { .. } => {
115 // not allowed in imports
116 return None;
117 }
118 };
119 Some(res)
120}
diff --git a/crates/hir_def/src/per_ns.rs b/crates/hir_def/src/per_ns.rs
new file mode 100644
index 000000000..74665c588
--- /dev/null
+++ b/crates/hir_def/src/per_ns.rs
@@ -0,0 +1,95 @@
1//! In rust, it is possible to have a value, a type and a macro with the same
2//! name without conflicts.
3//!
4//! `PerNs` (per namespace) captures this.
5
6use hir_expand::MacroDefId;
7
8use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId};
9
10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
11pub struct PerNs {
12 pub types: Option<(ModuleDefId, Visibility)>,
13 pub values: Option<(ModuleDefId, Visibility)>,
14 pub macros: Option<(MacroDefId, Visibility)>,
15}
16
17impl Default for PerNs {
18 fn default() -> Self {
19 PerNs { types: None, values: None, macros: None }
20 }
21}
22
23impl PerNs {
24 pub fn none() -> PerNs {
25 PerNs { types: None, values: None, macros: None }
26 }
27
28 pub fn values(t: ModuleDefId, v: Visibility) -> PerNs {
29 PerNs { types: None, values: Some((t, v)), macros: None }
30 }
31
32 pub fn types(t: ModuleDefId, v: Visibility) -> PerNs {
33 PerNs { types: Some((t, v)), values: None, macros: None }
34 }
35
36 pub fn both(types: ModuleDefId, values: ModuleDefId, v: Visibility) -> PerNs {
37 PerNs { types: Some((types, v)), values: Some((values, v)), macros: None }
38 }
39
40 pub fn macros(macro_: MacroDefId, v: Visibility) -> PerNs {
41 PerNs { types: None, values: None, macros: Some((macro_, v)) }
42 }
43
44 pub fn is_none(&self) -> bool {
45 self.types.is_none() && self.values.is_none() && self.macros.is_none()
46 }
47
48 pub fn take_types(self) -> Option<ModuleDefId> {
49 self.types.map(|it| it.0)
50 }
51
52 pub fn take_types_vis(self) -> Option<(ModuleDefId, Visibility)> {
53 self.types
54 }
55
56 pub fn take_values(self) -> Option<ModuleDefId> {
57 self.values.map(|it| it.0)
58 }
59
60 pub fn take_macros(self) -> Option<MacroDefId> {
61 self.macros.map(|it| it.0)
62 }
63
64 pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs {
65 PerNs {
66 types: self.types.filter(|(_, v)| f(*v)),
67 values: self.values.filter(|(_, v)| f(*v)),
68 macros: self.macros.filter(|(_, v)| f(*v)),
69 }
70 }
71
72 pub fn with_visibility(self, vis: Visibility) -> PerNs {
73 PerNs {
74 types: self.types.map(|(it, _)| (it, vis)),
75 values: self.values.map(|(it, _)| (it, vis)),
76 macros: self.macros.map(|(it, _)| (it, vis)),
77 }
78 }
79
80 pub fn or(self, other: PerNs) -> PerNs {
81 PerNs {
82 types: self.types.or(other.types),
83 values: self.values.or(other.values),
84 macros: self.macros.or(other.macros),
85 }
86 }
87
88 pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> {
89 self.types
90 .map(|it| ItemInNs::Types(it.0))
91 .into_iter()
92 .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter())
93 .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter())
94 }
95}
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
new file mode 100644
index 000000000..f8cc5e075
--- /dev/null
+++ b/crates/hir_def/src/resolver.rs
@@ -0,0 +1,713 @@
1//! Name resolution façade.
2use std::sync::Arc;
3
4use base_db::CrateId;
5use hir_expand::{
6 name::{name, Name},
7 MacroDefId,
8};
9use rustc_hash::FxHashSet;
10
11use crate::{
12 body::scope::{ExprScopes, ScopeId},
13 body::Body,
14 builtin_type::BuiltinType,
15 db::DefDatabase,
16 expr::{ExprId, PatId},
17 generics::GenericParams,
18 item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
19 nameres::CrateDefMap,
20 path::{ModPath, PathKind},
21 per_ns::PerNs,
22 visibility::{RawVisibility, Visibility},
23 AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId,
24 FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId,
25 StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId,
26};
27
28#[derive(Debug, Clone, Default)]
29pub struct Resolver {
30 // FIXME: all usages generally call `.rev`, so maybe reverse once in consturciton?
31 scopes: Vec<Scope>,
32}
33
34// FIXME how to store these best
35#[derive(Debug, Clone)]
36struct ModuleItemMap {
37 crate_def_map: Arc<CrateDefMap>,
38 module_id: LocalModuleId,
39}
40
41#[derive(Debug, Clone)]
42struct ExprScope {
43 owner: DefWithBodyId,
44 expr_scopes: Arc<ExprScopes>,
45 scope_id: ScopeId,
46}
47
48#[derive(Debug, Clone)]
49enum Scope {
50 /// All the items and imported names of a module
51 ModuleScope(ModuleItemMap),
52 /// Brings the generic parameters of an item into scope
53 GenericParams { def: GenericDefId, params: Arc<GenericParams> },
54 /// Brings `Self` in `impl` block into scope
55 ImplDefScope(ImplId),
56 /// Brings `Self` in enum, struct and union definitions into scope
57 AdtScope(AdtId),
58 /// Local bindings
59 ExprScope(ExprScope),
60 /// Temporary hack to support local items.
61 LocalItemsScope(Arc<Body>),
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Hash)]
65pub enum TypeNs {
66 SelfType(ImplId),
67 GenericParam(TypeParamId),
68 AdtId(AdtId),
69 AdtSelfType(AdtId),
70 // Yup, enum variants are added to the types ns, but any usage of variant as
71 // type is an error.
72 EnumVariantId(EnumVariantId),
73 TypeAliasId(TypeAliasId),
74 BuiltinType(BuiltinType),
75 TraitId(TraitId),
76 // Module belong to type ns, but the resolver is used when all module paths
77 // are fully resolved.
78 // ModuleId(ModuleId)
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, Hash)]
82pub enum ResolveValueResult {
83 ValueNs(ValueNs),
84 Partial(TypeNs, usize),
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, Hash)]
88pub enum ValueNs {
89 ImplSelf(ImplId),
90 LocalBinding(PatId),
91 FunctionId(FunctionId),
92 ConstId(ConstId),
93 StaticId(StaticId),
94 StructId(StructId),
95 EnumVariantId(EnumVariantId),
96}
97
98impl Resolver {
99 /// Resolve known trait from std, like `std::futures::Future`
100 pub fn resolve_known_trait(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<TraitId> {
101 let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?;
102 match res {
103 ModuleDefId::TraitId(it) => Some(it),
104 _ => None,
105 }
106 }
107
108 /// Resolve known struct from std, like `std::boxed::Box`
109 pub fn resolve_known_struct(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<StructId> {
110 let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?;
111 match res {
112 ModuleDefId::AdtId(AdtId::StructId(it)) => Some(it),
113 _ => None,
114 }
115 }
116
117 /// Resolve known enum from std, like `std::result::Result`
118 pub fn resolve_known_enum(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<EnumId> {
119 let res = self.resolve_module_path(db, path, BuiltinShadowMode::Other).take_types()?;
120 match res {
121 ModuleDefId::AdtId(AdtId::EnumId(it)) => Some(it),
122 _ => None,
123 }
124 }
125
126 fn resolve_module_path(
127 &self,
128 db: &dyn DefDatabase,
129 path: &ModPath,
130 shadow: BuiltinShadowMode,
131 ) -> PerNs {
132 let (item_map, module) = match self.module_scope() {
133 Some(it) => it,
134 None => return PerNs::none(),
135 };
136 let (module_res, segment_index) = item_map.resolve_path(db, module, &path, shadow);
137 if segment_index.is_some() {
138 return PerNs::none();
139 }
140 module_res
141 }
142
143 pub fn resolve_module_path_in_items(&self, db: &dyn DefDatabase, path: &ModPath) -> PerNs {
144 self.resolve_module_path(db, path, BuiltinShadowMode::Module)
145 }
146
147 pub fn resolve_path_in_type_ns(
148 &self,
149 db: &dyn DefDatabase,
150 path: &ModPath,
151 ) -> Option<(TypeNs, Option<usize>)> {
152 let first_name = path.segments.first()?;
153 let skip_to_mod = path.kind != PathKind::Plain;
154 for scope in self.scopes.iter().rev() {
155 match scope {
156 Scope::ExprScope(_) => continue,
157 Scope::GenericParams { .. }
158 | Scope::ImplDefScope(_)
159 | Scope::LocalItemsScope(_)
160 if skip_to_mod =>
161 {
162 continue
163 }
164
165 Scope::GenericParams { params, def } => {
166 if let Some(local_id) = params.find_by_name(first_name) {
167 let idx = if path.segments.len() == 1 { None } else { Some(1) };
168 return Some((
169 TypeNs::GenericParam(TypeParamId { local_id, parent: *def }),
170 idx,
171 ));
172 }
173 }
174 Scope::ImplDefScope(impl_) => {
175 if first_name == &name![Self] {
176 let idx = if path.segments.len() == 1 { None } else { Some(1) };
177 return Some((TypeNs::SelfType(*impl_), idx));
178 }
179 }
180 Scope::AdtScope(adt) => {
181 if first_name == &name![Self] {
182 let idx = if path.segments.len() == 1 { None } else { Some(1) };
183 return Some((TypeNs::AdtSelfType(*adt), idx));
184 }
185 }
186 Scope::ModuleScope(m) => {
187 let (module_def, idx) = m.crate_def_map.resolve_path(
188 db,
189 m.module_id,
190 &path,
191 BuiltinShadowMode::Other,
192 );
193 let res = to_type_ns(module_def)?;
194 return Some((res, idx));
195 }
196 Scope::LocalItemsScope(body) => {
197 let def = body.item_scope.get(first_name);
198 if let Some(res) = to_type_ns(def) {
199 return Some((res, None));
200 }
201 }
202 }
203 }
204 return None;
205 fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
206 let res = match per_ns.take_types()? {
207 ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
208 ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it),
209
210 ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it),
211 ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
212
213 ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
214
215 ModuleDefId::FunctionId(_)
216 | ModuleDefId::ConstId(_)
217 | ModuleDefId::StaticId(_)
218 | ModuleDefId::ModuleId(_) => return None,
219 };
220 Some(res)
221 }
222 }
223
224 pub fn resolve_path_in_type_ns_fully(
225 &self,
226 db: &dyn DefDatabase,
227 path: &ModPath,
228 ) -> Option<TypeNs> {
229 let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
230 if unresolved.is_some() {
231 return None;
232 }
233 Some(res)
234 }
235
236 pub fn resolve_visibility(
237 &self,
238 db: &dyn DefDatabase,
239 visibility: &RawVisibility,
240 ) -> Option<Visibility> {
241 match visibility {
242 RawVisibility::Module(_) => {
243 let (item_map, module) = match self.module_scope() {
244 Some(it) => it,
245 None => return None,
246 };
247 item_map.resolve_visibility(db, module, visibility)
248 }
249 RawVisibility::Public => Some(Visibility::Public),
250 }
251 }
252
253 pub fn resolve_path_in_value_ns(
254 &self,
255 db: &dyn DefDatabase,
256 path: &ModPath,
257 ) -> Option<ResolveValueResult> {
258 let n_segments = path.segments.len();
259 let tmp = name![self];
260 let first_name = if path.is_self() { &tmp } else { &path.segments.first()? };
261 let skip_to_mod = path.kind != PathKind::Plain && !path.is_self();
262 for scope in self.scopes.iter().rev() {
263 match scope {
264 Scope::AdtScope(_)
265 | Scope::ExprScope(_)
266 | Scope::GenericParams { .. }
267 | Scope::ImplDefScope(_)
268 | Scope::LocalItemsScope(_)
269 if skip_to_mod =>
270 {
271 continue
272 }
273
274 Scope::ExprScope(scope) if n_segments <= 1 => {
275 let entry = scope
276 .expr_scopes
277 .entries(scope.scope_id)
278 .iter()
279 .find(|entry| entry.name() == first_name);
280
281 if let Some(e) = entry {
282 return Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(e.pat())));
283 }
284 }
285 Scope::ExprScope(_) => continue,
286
287 Scope::GenericParams { params, def } if n_segments > 1 => {
288 if let Some(local_id) = params.find_by_name(first_name) {
289 let ty = TypeNs::GenericParam(TypeParamId { local_id, parent: *def });
290 return Some(ResolveValueResult::Partial(ty, 1));
291 }
292 }
293 Scope::GenericParams { .. } => continue,
294
295 Scope::ImplDefScope(impl_) => {
296 if first_name == &name![Self] {
297 if n_segments > 1 {
298 let ty = TypeNs::SelfType(*impl_);
299 return Some(ResolveValueResult::Partial(ty, 1));
300 } else {
301 return Some(ResolveValueResult::ValueNs(ValueNs::ImplSelf(*impl_)));
302 }
303 }
304 }
305 Scope::AdtScope(adt) => {
306 if n_segments == 1 {
307 // bare `Self` doesn't work in the value namespace in a struct/enum definition
308 continue;
309 }
310 if first_name == &name![Self] {
311 let ty = TypeNs::AdtSelfType(*adt);
312 return Some(ResolveValueResult::Partial(ty, 1));
313 }
314 }
315
316 Scope::ModuleScope(m) => {
317 let (module_def, idx) = m.crate_def_map.resolve_path(
318 db,
319 m.module_id,
320 &path,
321 BuiltinShadowMode::Other,
322 );
323 return match idx {
324 None => {
325 let value = to_value_ns(module_def)?;
326 Some(ResolveValueResult::ValueNs(value))
327 }
328 Some(idx) => {
329 let ty = match module_def.take_types()? {
330 ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
331 ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
332 ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it),
333 ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it),
334
335 ModuleDefId::ModuleId(_)
336 | ModuleDefId::FunctionId(_)
337 | ModuleDefId::EnumVariantId(_)
338 | ModuleDefId::ConstId(_)
339 | ModuleDefId::StaticId(_) => return None,
340 };
341 Some(ResolveValueResult::Partial(ty, idx))
342 }
343 };
344 }
345 Scope::LocalItemsScope(body) => {
346 // we don't bother looking in the builtin scope here because there are no builtin values
347 let def = to_value_ns(body.item_scope.get(first_name));
348
349 if let Some(res) = def {
350 return Some(ResolveValueResult::ValueNs(res));
351 }
352 }
353 }
354 }
355 return None;
356
357 fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> {
358 let res = match per_ns.take_values()? {
359 ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it),
360 ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it),
361 ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it),
362 ModuleDefId::ConstId(it) => ValueNs::ConstId(it),
363 ModuleDefId::StaticId(it) => ValueNs::StaticId(it),
364
365 ModuleDefId::AdtId(AdtId::EnumId(_))
366 | ModuleDefId::AdtId(AdtId::UnionId(_))
367 | ModuleDefId::TraitId(_)
368 | ModuleDefId::TypeAliasId(_)
369 | ModuleDefId::BuiltinType(_)
370 | ModuleDefId::ModuleId(_) => return None,
371 };
372 Some(res)
373 }
374 }
375
376 pub fn resolve_path_in_value_ns_fully(
377 &self,
378 db: &dyn DefDatabase,
379 path: &ModPath,
380 ) -> Option<ValueNs> {
381 match self.resolve_path_in_value_ns(db, path)? {
382 ResolveValueResult::ValueNs(it) => Some(it),
383 ResolveValueResult::Partial(..) => None,
384 }
385 }
386
387 pub fn resolve_path_as_macro(
388 &self,
389 db: &dyn DefDatabase,
390 path: &ModPath,
391 ) -> Option<MacroDefId> {
392 // Search item scope legacy macro first
393 if let Some(def) = self.resolve_local_macro_def(path) {
394 return Some(def);
395 }
396
397 let (item_map, module) = self.module_scope()?;
398 item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros()
399 }
400
401 pub fn process_all_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) {
402 for scope in self.scopes.iter().rev() {
403 scope.process_names(db, f);
404 }
405 }
406
407 pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> {
408 let mut traits = FxHashSet::default();
409 for scope in &self.scopes {
410 if let Scope::ModuleScope(m) = scope {
411 if let Some(prelude) = m.crate_def_map.prelude {
412 let prelude_def_map = db.crate_def_map(prelude.krate);
413 traits.extend(prelude_def_map[prelude.local_id].scope.traits());
414 }
415 traits.extend(m.crate_def_map[m.module_id].scope.traits());
416 }
417 }
418 traits
419 }
420
421 fn module_scope(&self) -> Option<(&CrateDefMap, LocalModuleId)> {
422 self.scopes.iter().rev().find_map(|scope| match scope {
423 Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)),
424
425 _ => None,
426 })
427 }
428
429 fn resolve_local_macro_def(&self, path: &ModPath) -> Option<MacroDefId> {
430 let name = path.as_ident()?;
431 self.scopes.iter().rev().find_map(|scope| {
432 if let Scope::LocalItemsScope(body) = scope {
433 return body.item_scope.get_legacy_macro(name);
434 }
435 None
436 })
437 }
438
439 pub fn module(&self) -> Option<ModuleId> {
440 let (def_map, local_id) = self.module_scope()?;
441 Some(ModuleId { krate: def_map.krate, local_id })
442 }
443
444 pub fn krate(&self) -> Option<CrateId> {
445 self.module_scope().map(|t| t.0.krate)
446 }
447
448 pub fn where_predicates_in_scope<'a>(
449 &'a self,
450 ) -> impl Iterator<Item = &'a crate::generics::WherePredicate> + 'a {
451 self.scopes
452 .iter()
453 .rev()
454 .filter_map(|scope| match scope {
455 Scope::GenericParams { params, .. } => Some(params),
456 _ => None,
457 })
458 .flat_map(|params| params.where_predicates.iter())
459 }
460
461 pub fn generic_def(&self) -> Option<GenericDefId> {
462 self.scopes.iter().rev().find_map(|scope| match scope {
463 Scope::GenericParams { def, .. } => Some(*def),
464 _ => None,
465 })
466 }
467
468 pub fn body_owner(&self) -> Option<DefWithBodyId> {
469 self.scopes.iter().rev().find_map(|scope| match scope {
470 Scope::ExprScope(it) => Some(it.owner),
471 _ => None,
472 })
473 }
474}
475
476pub enum ScopeDef {
477 PerNs(PerNs),
478 ImplSelfType(ImplId),
479 AdtSelfType(AdtId),
480 GenericParam(TypeParamId),
481 Local(PatId),
482}
483
484impl Scope {
485 fn process_names(&self, db: &dyn DefDatabase, f: &mut dyn FnMut(Name, ScopeDef)) {
486 match self {
487 Scope::ModuleScope(m) => {
488 // FIXME: should we provide `self` here?
489 // f(
490 // Name::self_param(),
491 // PerNs::types(Resolution::Def {
492 // def: m.module.into(),
493 // }),
494 // );
495 m.crate_def_map[m.module_id].scope.entries().for_each(|(name, def)| {
496 f(name.clone(), ScopeDef::PerNs(def));
497 });
498 m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
499 f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_, Visibility::Public)));
500 });
501 m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| {
502 f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public)));
503 });
504 BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
505 f(name.clone(), ScopeDef::PerNs(def));
506 });
507 if let Some(prelude) = m.crate_def_map.prelude {
508 let prelude_def_map = db.crate_def_map(prelude.krate);
509 prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| {
510 f(name.clone(), ScopeDef::PerNs(def));
511 });
512 }
513 }
514 Scope::LocalItemsScope(body) => body.item_scope.entries().for_each(|(name, def)| {
515 f(name.clone(), ScopeDef::PerNs(def));
516 }),
517 Scope::GenericParams { params, def } => {
518 for (local_id, param) in params.types.iter() {
519 if let Some(name) = &param.name {
520 f(
521 name.clone(),
522 ScopeDef::GenericParam(TypeParamId { local_id, parent: *def }),
523 )
524 }
525 }
526 }
527 Scope::ImplDefScope(i) => {
528 f(name![Self], ScopeDef::ImplSelfType(*i));
529 }
530 Scope::AdtScope(i) => {
531 f(name![Self], ScopeDef::AdtSelfType(*i));
532 }
533 Scope::ExprScope(scope) => {
534 scope.expr_scopes.entries(scope.scope_id).iter().for_each(|e| {
535 f(e.name().clone(), ScopeDef::Local(e.pat()));
536 });
537 }
538 }
539 }
540}
541
542// needs arbitrary_self_types to be a method... or maybe move to the def?
543pub fn resolver_for_expr(db: &dyn DefDatabase, owner: DefWithBodyId, expr_id: ExprId) -> Resolver {
544 let scopes = db.expr_scopes(owner);
545 resolver_for_scope(db, owner, scopes.scope_for(expr_id))
546}
547
548pub fn resolver_for_scope(
549 db: &dyn DefDatabase,
550 owner: DefWithBodyId,
551 scope_id: Option<ScopeId>,
552) -> Resolver {
553 let mut r = owner.resolver(db);
554 r = r.push_local_items_scope(db.body(owner));
555 let scopes = db.expr_scopes(owner);
556 let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>();
557 for scope in scope_chain.into_iter().rev() {
558 r = r.push_expr_scope(owner, Arc::clone(&scopes), scope);
559 }
560 r
561}
562
563impl Resolver {
564 fn push_scope(mut self, scope: Scope) -> Resolver {
565 self.scopes.push(scope);
566 self
567 }
568
569 fn push_generic_params_scope(self, db: &dyn DefDatabase, def: GenericDefId) -> Resolver {
570 let params = db.generic_params(def);
571 self.push_scope(Scope::GenericParams { def, params })
572 }
573
574 fn push_impl_def_scope(self, impl_def: ImplId) -> Resolver {
575 self.push_scope(Scope::ImplDefScope(impl_def))
576 }
577
578 fn push_module_scope(
579 self,
580 crate_def_map: Arc<CrateDefMap>,
581 module_id: LocalModuleId,
582 ) -> Resolver {
583 self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id }))
584 }
585
586 fn push_local_items_scope(self, body: Arc<Body>) -> Resolver {
587 self.push_scope(Scope::LocalItemsScope(body))
588 }
589
590 fn push_expr_scope(
591 self,
592 owner: DefWithBodyId,
593 expr_scopes: Arc<ExprScopes>,
594 scope_id: ScopeId,
595 ) -> Resolver {
596 self.push_scope(Scope::ExprScope(ExprScope { owner, expr_scopes, scope_id }))
597 }
598}
599
600pub trait HasResolver: Copy {
601 /// Builds a resolver for type references inside this def.
602 fn resolver(self, db: &dyn DefDatabase) -> Resolver;
603}
604
605impl HasResolver for ModuleId {
606 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
607 let def_map = db.crate_def_map(self.krate);
608 Resolver::default().push_module_scope(def_map, self.local_id)
609 }
610}
611
612impl HasResolver for TraitId {
613 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
614 self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
615 }
616}
617
618impl<T: Into<AdtId> + Copy> HasResolver for T {
619 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
620 let def = self.into();
621 def.module(db)
622 .resolver(db)
623 .push_generic_params_scope(db, def.into())
624 .push_scope(Scope::AdtScope(def))
625 }
626}
627
628impl HasResolver for FunctionId {
629 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
630 self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
631 }
632}
633
634impl HasResolver for ConstId {
635 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
636 self.lookup(db).container.resolver(db)
637 }
638}
639
640impl HasResolver for StaticId {
641 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
642 self.lookup(db).container.resolver(db)
643 }
644}
645
646impl HasResolver for TypeAliasId {
647 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
648 self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
649 }
650}
651
652impl HasResolver for ImplId {
653 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
654 self.lookup(db)
655 .container
656 .resolver(db)
657 .push_generic_params_scope(db, self.into())
658 .push_impl_def_scope(self)
659 }
660}
661
662impl HasResolver for DefWithBodyId {
663 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
664 match self {
665 DefWithBodyId::ConstId(c) => c.resolver(db),
666 DefWithBodyId::FunctionId(f) => f.resolver(db),
667 DefWithBodyId::StaticId(s) => s.resolver(db),
668 }
669 }
670}
671
672impl HasResolver for ContainerId {
673 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
674 match self {
675 ContainerId::ModuleId(it) => it.resolver(db),
676 ContainerId::DefWithBodyId(it) => it.module(db).resolver(db),
677 }
678 }
679}
680
681impl HasResolver for AssocContainerId {
682 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
683 match self {
684 AssocContainerId::ContainerId(it) => it.resolver(db),
685 AssocContainerId::TraitId(it) => it.resolver(db),
686 AssocContainerId::ImplId(it) => it.resolver(db),
687 }
688 }
689}
690
691impl HasResolver for GenericDefId {
692 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
693 match self {
694 GenericDefId::FunctionId(inner) => inner.resolver(db),
695 GenericDefId::AdtId(adt) => adt.resolver(db),
696 GenericDefId::TraitId(inner) => inner.resolver(db),
697 GenericDefId::TypeAliasId(inner) => inner.resolver(db),
698 GenericDefId::ImplId(inner) => inner.resolver(db),
699 GenericDefId::EnumVariantId(inner) => inner.parent.resolver(db),
700 GenericDefId::ConstId(inner) => inner.resolver(db),
701 }
702 }
703}
704
705impl HasResolver for VariantId {
706 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
707 match self {
708 VariantId::EnumVariantId(it) => it.parent.resolver(db),
709 VariantId::StructId(it) => it.resolver(db),
710 VariantId::UnionId(it) => it.resolver(db),
711 }
712 }
713}
diff --git a/crates/hir_def/src/src.rs b/crates/hir_def/src/src.rs
new file mode 100644
index 000000000..7a79b0314
--- /dev/null
+++ b/crates/hir_def/src/src.rs
@@ -0,0 +1,43 @@
1//! Utilities for mapping between hir IDs and the surface syntax.
2
3use arena::map::ArenaMap;
4use hir_expand::InFile;
5
6use crate::{db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc};
7
8pub trait HasSource {
9 type Value;
10 fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>;
11}
12
13impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> {
14 type Value = N::Source;
15
16 fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
17 let tree = db.item_tree(self.id.file_id);
18 let ast_id_map = db.ast_id_map(self.id.file_id);
19 let root = db.parse_or_expand(self.id.file_id).unwrap();
20 let node = &tree[self.id.value];
21
22 InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root))
23 }
24}
25
26impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
27 type Value = N::Source;
28
29 fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
30 let tree = db.item_tree(self.id.file_id);
31 let ast_id_map = db.ast_id_map(self.id.file_id);
32 let root = db.parse_or_expand(self.id.file_id).unwrap();
33 let node = &tree[self.id.value];
34
35 InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root))
36 }
37}
38
39pub trait HasChildSource {
40 type ChildId;
41 type Value;
42 fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>>;
43}
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs
new file mode 100644
index 000000000..42a762936
--- /dev/null
+++ b/crates/hir_def/src/test_db.rs
@@ -0,0 +1,101 @@
1//! Database used for testing `hir_def`.
2
3use std::{
4 fmt, panic,
5 sync::{Arc, Mutex},
6};
7
8use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
9use hir_expand::db::AstDatabase;
10use rustc_hash::FxHashSet;
11
12use crate::db::DefDatabase;
13
14#[salsa::database(
15 base_db::SourceDatabaseExtStorage,
16 base_db::SourceDatabaseStorage,
17 hir_expand::db::AstDatabaseStorage,
18 crate::db::InternDatabaseStorage,
19 crate::db::DefDatabaseStorage
20)]
21#[derive(Default)]
22pub struct TestDB {
23 storage: salsa::Storage<TestDB>,
24 events: Mutex<Option<Vec<salsa::Event>>>,
25}
26
27impl Upcast<dyn AstDatabase> for TestDB {
28 fn upcast(&self) -> &(dyn AstDatabase + 'static) {
29 &*self
30 }
31}
32
33impl Upcast<dyn DefDatabase> for TestDB {
34 fn upcast(&self) -> &(dyn DefDatabase + 'static) {
35 &*self
36 }
37}
38
39impl salsa::Database for TestDB {
40 fn salsa_event(&self, event: salsa::Event) {
41 let mut events = self.events.lock().unwrap();
42 if let Some(events) = &mut *events {
43 events.push(event);
44 }
45 }
46}
47
48impl fmt::Debug for TestDB {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 f.debug_struct("TestDB").finish()
51 }
52}
53
54impl panic::RefUnwindSafe for TestDB {}
55
56impl FileLoader for TestDB {
57 fn file_text(&self, file_id: FileId) -> Arc<String> {
58 FileLoaderDelegate(self).file_text(file_id)
59 }
60 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
61 FileLoaderDelegate(self).resolve_path(anchor, path)
62 }
63 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
64 FileLoaderDelegate(self).relevant_crates(file_id)
65 }
66}
67
68impl TestDB {
69 pub fn module_for_file(&self, file_id: FileId) -> crate::ModuleId {
70 for &krate in self.relevant_crates(file_id).iter() {
71 let crate_def_map = self.crate_def_map(krate);
72 for (local_id, data) in crate_def_map.modules.iter() {
73 if data.origin.file_id() == Some(file_id) {
74 return crate::ModuleId { krate, local_id };
75 }
76 }
77 }
78 panic!("Can't find module for file")
79 }
80
81 pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event> {
82 *self.events.lock().unwrap() = Some(Vec::new());
83 f();
84 self.events.lock().unwrap().take().unwrap()
85 }
86
87 pub fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
88 let events = self.log(f);
89 events
90 .into_iter()
91 .filter_map(|e| match e.kind {
92 // This pretty horrible, but `Debug` is the only way to inspect
93 // QueryDescriptor at the moment.
94 salsa::EventKind::WillExecute { database_key } => {
95 Some(format!("{:?}", database_key.debug(self)))
96 }
97 _ => None,
98 })
99 .collect()
100 }
101}
diff --git a/crates/hir_def/src/trace.rs b/crates/hir_def/src/trace.rs
new file mode 100644
index 000000000..fd64e7018
--- /dev/null
+++ b/crates/hir_def/src/trace.rs
@@ -0,0 +1,51 @@
1//! Trace is a pretty niche data structure which is used when lowering a CST
2//! into HIR.
3//!
4//! Lowering process calculates two bits of information:
5//! * the lowered syntax itself
6//! * a mapping between lowered syntax and original syntax
7//!
8//! Due to the way salsa works, the mapping is usually hot lava, as it contains
9//! absolute offsets. The `Trace` structure (inspired, at least in name, by
10//! Kotlin's `BindingTrace`) allows use the same code to compute both
11//! projections.
12use arena::{map::ArenaMap, Arena, Idx, RawId};
13
14pub(crate) struct Trace<T, V> {
15 arena: Option<Arena<T>>,
16 map: Option<ArenaMap<Idx<T>, V>>,
17 len: u32,
18}
19
20impl<T, V> Trace<T, V> {
21 pub(crate) fn new_for_arena() -> Trace<T, V> {
22 Trace { arena: Some(Arena::default()), map: None, len: 0 }
23 }
24
25 pub(crate) fn new_for_map() -> Trace<T, V> {
26 Trace { arena: None, map: Some(ArenaMap::default()), len: 0 }
27 }
28
29 pub(crate) fn alloc(&mut self, value: impl FnOnce() -> V, data: impl FnOnce() -> T) -> Idx<T> {
30 let id = if let Some(arena) = &mut self.arena {
31 arena.alloc(data())
32 } else {
33 let id = Idx::<T>::from_raw(RawId::from(self.len));
34 self.len += 1;
35 id
36 };
37
38 if let Some(map) = &mut self.map {
39 map.insert(id, value());
40 }
41 id
42 }
43
44 pub(crate) fn into_arena(mut self) -> Arena<T> {
45 self.arena.take().unwrap()
46 }
47
48 pub(crate) fn into_map(mut self) -> ArenaMap<Idx<T>, V> {
49 self.map.take().unwrap()
50 }
51}
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
new file mode 100644
index 000000000..1a78c1444
--- /dev/null
+++ b/crates/hir_def/src/type_ref.rs
@@ -0,0 +1,245 @@
1//! HIR for references to types. Paths in these are not yet resolved. They can
2//! be directly created from an ast::TypeRef, without further queries.
3use syntax::ast::{self};
4
5use crate::{body::LowerCtx, path::Path};
6
7#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
8pub enum Mutability {
9 Shared,
10 Mut,
11}
12
13impl Mutability {
14 pub fn from_mutable(mutable: bool) -> Mutability {
15 if mutable {
16 Mutability::Mut
17 } else {
18 Mutability::Shared
19 }
20 }
21
22 pub fn as_keyword_for_ref(self) -> &'static str {
23 match self {
24 Mutability::Shared => "",
25 Mutability::Mut => "mut ",
26 }
27 }
28
29 pub fn as_keyword_for_ptr(self) -> &'static str {
30 match self {
31 Mutability::Shared => "const ",
32 Mutability::Mut => "mut ",
33 }
34 }
35}
36
37#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
38pub enum Rawness {
39 RawPtr,
40 Ref,
41}
42
43impl Rawness {
44 pub fn from_raw(is_raw: bool) -> Rawness {
45 if is_raw {
46 Rawness::RawPtr
47 } else {
48 Rawness::Ref
49 }
50 }
51}
52
53/// Compare ty::Ty
54#[derive(Clone, PartialEq, Eq, Hash, Debug)]
55pub enum TypeRef {
56 Never,
57 Placeholder,
58 Tuple(Vec<TypeRef>),
59 Path(Path),
60 RawPtr(Box<TypeRef>, Mutability),
61 Reference(Box<TypeRef>, Mutability),
62 Array(Box<TypeRef> /*, Expr*/),
63 Slice(Box<TypeRef>),
64 /// A fn pointer. Last element of the vector is the return type.
65 Fn(Vec<TypeRef>, bool /*varargs*/),
66 // For
67 ImplTrait(Vec<TypeBound>),
68 DynTrait(Vec<TypeBound>),
69 Error,
70}
71
72#[derive(Clone, PartialEq, Eq, Hash, Debug)]
73pub enum TypeBound {
74 Path(Path),
75 // also for<> bounds
76 // also Lifetimes
77 Error,
78}
79
80impl TypeRef {
81 /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
82 pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
83 match node {
84 ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
85 ast::Type::TupleType(inner) => {
86 TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
87 }
88 ast::Type::NeverType(..) => TypeRef::Never,
89 ast::Type::PathType(inner) => {
90 // FIXME: Use `Path::from_src`
91 inner
92 .path()
93 .and_then(|it| ctx.lower_path(it))
94 .map(TypeRef::Path)
95 .unwrap_or(TypeRef::Error)
96 }
97 ast::Type::PtrType(inner) => {
98 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty());
99 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
100 TypeRef::RawPtr(Box::new(inner_ty), mutability)
101 }
102 ast::Type::ArrayType(inner) => {
103 TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())))
104 }
105 ast::Type::SliceType(inner) => {
106 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())))
107 }
108 ast::Type::RefType(inner) => {
109 let inner_ty = TypeRef::from_ast_opt(&ctx, inner.ty());
110 let mutability = Mutability::from_mutable(inner.mut_token().is_some());
111 TypeRef::Reference(Box::new(inner_ty), mutability)
112 }
113 ast::Type::InferType(_inner) => TypeRef::Placeholder,
114 ast::Type::FnPtrType(inner) => {
115 let ret_ty = inner
116 .ret_type()
117 .and_then(|rt| rt.ty())
118 .map(|it| TypeRef::from_ast(ctx, it))
119 .unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
120 let mut is_varargs = false;
121 let mut params = if let Some(pl) = inner.param_list() {
122 if let Some(param) = pl.params().last() {
123 is_varargs = param.dotdotdot_token().is_some();
124 }
125
126 pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(&ctx, it)).collect()
127 } else {
128 Vec::new()
129 };
130 params.push(ret_ty);
131 TypeRef::Fn(params, is_varargs)
132 }
133 // for types are close enough for our purposes to the inner type for now...
134 ast::Type::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
135 ast::Type::ImplTraitType(inner) => {
136 TypeRef::ImplTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
137 }
138 ast::Type::DynTraitType(inner) => {
139 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
140 }
141 }
142 }
143
144 pub(crate) fn from_ast_opt(ctx: &LowerCtx, node: Option<ast::Type>) -> Self {
145 if let Some(node) = node {
146 TypeRef::from_ast(ctx, node)
147 } else {
148 TypeRef::Error
149 }
150 }
151
152 pub(crate) fn unit() -> TypeRef {
153 TypeRef::Tuple(Vec::new())
154 }
155
156 pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) {
157 go(self, f);
158
159 fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
160 f(type_ref);
161 match type_ref {
162 TypeRef::Fn(types, _) | TypeRef::Tuple(types) => {
163 types.iter().for_each(|t| go(t, f))
164 }
165 TypeRef::RawPtr(type_ref, _)
166 | TypeRef::Reference(type_ref, _)
167 | TypeRef::Array(type_ref)
168 | TypeRef::Slice(type_ref) => go(&type_ref, f),
169 TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
170 for bound in bounds {
171 match bound {
172 TypeBound::Path(path) => go_path(path, f),
173 TypeBound::Error => (),
174 }
175 }
176 }
177 TypeRef::Path(path) => go_path(path, f),
178 TypeRef::Never | TypeRef::Placeholder | TypeRef::Error => {}
179 };
180 }
181
182 fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
183 if let Some(type_ref) = path.type_anchor() {
184 go(type_ref, f);
185 }
186 for segment in path.segments().iter() {
187 if let Some(args_and_bindings) = segment.args_and_bindings {
188 for arg in &args_and_bindings.args {
189 let crate::path::GenericArg::Type(type_ref) = arg;
190 go(type_ref, f);
191 }
192 for binding in &args_and_bindings.bindings {
193 if let Some(type_ref) = &binding.type_ref {
194 go(type_ref, f);
195 }
196 for bound in &binding.bounds {
197 match bound {
198 TypeBound::Path(path) => go_path(path, f),
199 TypeBound::Error => (),
200 }
201 }
202 }
203 }
204 }
205 }
206 }
207}
208
209pub(crate) fn type_bounds_from_ast(
210 lower_ctx: &LowerCtx,
211 type_bounds_opt: Option<ast::TypeBoundList>,
212) -> Vec<TypeBound> {
213 if let Some(type_bounds) = type_bounds_opt {
214 type_bounds.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect()
215 } else {
216 vec![]
217 }
218}
219
220impl TypeBound {
221 pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::TypeBound) -> Self {
222 match node.kind() {
223 ast::TypeBoundKind::PathType(path_type) => {
224 let path = match path_type.path() {
225 Some(p) => p,
226 None => return TypeBound::Error,
227 };
228
229 let path = match ctx.lower_path(path) {
230 Some(p) => p,
231 None => return TypeBound::Error,
232 };
233 TypeBound::Path(path)
234 }
235 ast::TypeBoundKind::ForType(_) | ast::TypeBoundKind::Lifetime(_) => TypeBound::Error,
236 }
237 }
238
239 pub fn as_path(&self) -> Option<&Path> {
240 match self {
241 TypeBound::Path(p) => Some(p),
242 _ => None,
243 }
244 }
245}
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs
new file mode 100644
index 000000000..e6e0853a3
--- /dev/null
+++ b/crates/hir_def/src/visibility.rs
@@ -0,0 +1,171 @@
1//! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
2
3use hir_expand::{hygiene::Hygiene, InFile};
4use syntax::ast;
5
6use crate::{
7 db::DefDatabase,
8 nameres::CrateDefMap,
9 path::{ModPath, PathKind},
10 ModuleId,
11};
12
13/// Visibility of an item, not yet resolved.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum RawVisibility {
16 /// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is
17 /// equivalent to `pub(self)`.
18 Module(ModPath),
19 /// `pub`.
20 Public,
21}
22
23impl RawVisibility {
24 pub(crate) const fn private() -> RawVisibility {
25 let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() };
26 RawVisibility::Module(path)
27 }
28
29 pub(crate) fn from_ast(
30 db: &dyn DefDatabase,
31 node: InFile<Option<ast::Visibility>>,
32 ) -> RawVisibility {
33 Self::from_ast_with_hygiene(node.value, &Hygiene::new(db.upcast(), node.file_id))
34 }
35
36 pub(crate) fn from_ast_with_hygiene(
37 node: Option<ast::Visibility>,
38 hygiene: &Hygiene,
39 ) -> RawVisibility {
40 Self::from_ast_with_hygiene_and_default(node, RawVisibility::private(), hygiene)
41 }
42
43 pub(crate) fn from_ast_with_hygiene_and_default(
44 node: Option<ast::Visibility>,
45 default: RawVisibility,
46 hygiene: &Hygiene,
47 ) -> RawVisibility {
48 let node = match node {
49 None => return default,
50 Some(node) => node,
51 };
52 match node.kind() {
53 ast::VisibilityKind::In(path) => {
54 let path = ModPath::from_src(path, hygiene);
55 let path = match path {
56 None => return RawVisibility::private(),
57 Some(path) => path,
58 };
59 RawVisibility::Module(path)
60 }
61 ast::VisibilityKind::PubCrate => {
62 let path = ModPath { kind: PathKind::Crate, segments: Vec::new() };
63 RawVisibility::Module(path)
64 }
65 ast::VisibilityKind::PubSuper => {
66 let path = ModPath { kind: PathKind::Super(1), segments: Vec::new() };
67 RawVisibility::Module(path)
68 }
69 ast::VisibilityKind::PubSelf => {
70 let path = ModPath { kind: PathKind::Plain, segments: Vec::new() };
71 RawVisibility::Module(path)
72 }
73 ast::VisibilityKind::Pub => RawVisibility::Public,
74 }
75 }
76
77 pub fn resolve(
78 &self,
79 db: &dyn DefDatabase,
80 resolver: &crate::resolver::Resolver,
81 ) -> Visibility {
82 // we fall back to public visibility (i.e. fail open) if the path can't be resolved
83 resolver.resolve_visibility(db, self).unwrap_or(Visibility::Public)
84 }
85}
86
87/// Visibility of an item, with the path resolved.
88#[derive(Debug, Copy, Clone, PartialEq, Eq)]
89pub enum Visibility {
90 /// Visibility is restricted to a certain module.
91 Module(ModuleId),
92 /// Visibility is unrestricted.
93 Public,
94}
95
96impl Visibility {
97 pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool {
98 let to_module = match self {
99 Visibility::Module(m) => m,
100 Visibility::Public => return true,
101 };
102 // if they're not in the same crate, it can't be visible
103 if from_module.krate != to_module.krate {
104 return false;
105 }
106 let def_map = db.crate_def_map(from_module.krate);
107 self.is_visible_from_def_map(&def_map, from_module.local_id)
108 }
109
110 pub(crate) fn is_visible_from_other_crate(self) -> bool {
111 match self {
112 Visibility::Module(_) => false,
113 Visibility::Public => true,
114 }
115 }
116
117 pub(crate) fn is_visible_from_def_map(
118 self,
119 def_map: &CrateDefMap,
120 from_module: crate::LocalModuleId,
121 ) -> bool {
122 let to_module = match self {
123 Visibility::Module(m) => m,
124 Visibility::Public => return true,
125 };
126 // from_module needs to be a descendant of to_module
127 let mut ancestors = std::iter::successors(Some(from_module), |m| {
128 let parent_id = def_map[*m].parent?;
129 Some(parent_id)
130 });
131 ancestors.any(|m| m == to_module.local_id)
132 }
133
134 /// Returns the most permissive visibility of `self` and `other`.
135 ///
136 /// If there is no subset relation between `self` and `other`, returns `None` (ie. they're only
137 /// visible in unrelated modules).
138 pub(crate) fn max(self, other: Visibility, def_map: &CrateDefMap) -> Option<Visibility> {
139 match (self, other) {
140 (Visibility::Module(_), Visibility::Public)
141 | (Visibility::Public, Visibility::Module(_))
142 | (Visibility::Public, Visibility::Public) => Some(Visibility::Public),
143 (Visibility::Module(mod_a), Visibility::Module(mod_b)) => {
144 if mod_a.krate != mod_b.krate {
145 return None;
146 }
147
148 let mut a_ancestors = std::iter::successors(Some(mod_a.local_id), |m| {
149 let parent_id = def_map[*m].parent?;
150 Some(parent_id)
151 });
152 let mut b_ancestors = std::iter::successors(Some(mod_b.local_id), |m| {
153 let parent_id = def_map[*m].parent?;
154 Some(parent_id)
155 });
156
157 if a_ancestors.any(|m| m == mod_b.local_id) {
158 // B is above A
159 return Some(Visibility::Module(mod_b));
160 }
161
162 if b_ancestors.any(|m| m == mod_a.local_id) {
163 // A is above B
164 return Some(Visibility::Module(mod_a));
165 }
166
167 None
168 }
169 }
170 }
171}