diff options
Diffstat (limited to 'crates/hir_def/src')
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 | |||
3 | use std::sync::Arc; | ||
4 | |||
5 | use arena::{map::ArenaMap, Arena}; | ||
6 | use either::Either; | ||
7 | use hir_expand::{ | ||
8 | name::{AsName, Name}, | ||
9 | InFile, | ||
10 | }; | ||
11 | use syntax::ast::{self, NameOwner, VisibilityOwner}; | ||
12 | use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}; | ||
13 | |||
14 | use 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 | }; | ||
26 | use cfg::CfgOptions; | ||
27 | |||
28 | /// Note that we use `StructData` for unions as well! | ||
29 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
30 | pub 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)] | ||
37 | pub struct EnumData { | ||
38 | pub name: Name, | ||
39 | pub variants: Arena<EnumVariantData>, | ||
40 | } | ||
41 | |||
42 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
43 | pub struct EnumVariantData { | ||
44 | pub name: Name, | ||
45 | pub variant_data: Arc<VariantData>, | ||
46 | } | ||
47 | |||
48 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
49 | pub 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)] | ||
57 | pub struct FieldData { | ||
58 | pub name: Name, | ||
59 | pub type_ref: TypeRef, | ||
60 | pub visibility: RawVisibility, | ||
61 | } | ||
62 | |||
63 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
64 | pub enum ReprKind { | ||
65 | Packed, | ||
66 | Other, | ||
67 | } | ||
68 | |||
69 | fn 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 | |||
73 | fn 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 | |||
86 | impl 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 | |||
118 | impl 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 | |||
147 | impl 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 | |||
158 | fn 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 | |||
182 | impl 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 | |||
214 | impl 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)] | ||
246 | pub enum StructKind { | ||
247 | Tuple, | ||
248 | Record, | ||
249 | Unit, | ||
250 | } | ||
251 | |||
252 | fn 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 | |||
299 | fn 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 | |||
323 | fn 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 | |||
3 | use std::{ops, sync::Arc}; | ||
4 | |||
5 | use cfg::{CfgExpr, CfgOptions}; | ||
6 | use either::Either; | ||
7 | use hir_expand::{hygiene::Hygiene, AstId, InFile}; | ||
8 | use mbe::ast_to_token_tree; | ||
9 | use syntax::{ | ||
10 | ast::{self, AstNode, AttrsOwner}, | ||
11 | SmolStr, | ||
12 | }; | ||
13 | use tt::Subtree; | ||
14 | |||
15 | use 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)] | ||
25 | pub struct Attrs { | ||
26 | entries: Option<Arc<[Attr]>>, | ||
27 | } | ||
28 | |||
29 | impl 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 | |||
40 | impl 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)] | ||
138 | pub struct Attr { | ||
139 | pub(crate) path: ModPath, | ||
140 | pub(crate) input: Option<AttrInput>, | ||
141 | } | ||
142 | |||
143 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
144 | pub enum AttrInput { | ||
145 | /// `#[attr = "string"]` | ||
146 | Literal(SmolStr), | ||
147 | /// `#[attr(subtree)]` | ||
148 | TokenTree(Subtree), | ||
149 | } | ||
150 | |||
151 | impl 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)] | ||
168 | pub struct AttrQuery<'a> { | ||
169 | attrs: &'a Attrs, | ||
170 | key: &'static str, | ||
171 | } | ||
172 | |||
173 | impl<'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 | |||
200 | fn attrs_from_ast<N>(src: AstId<N>, db: &dyn DefDatabase) -> Attrs | ||
201 | where | ||
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 | |||
208 | fn 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. | ||
3 | mod lower; | ||
4 | pub mod scope; | ||
5 | |||
6 | use std::{mem, ops::Index, sync::Arc}; | ||
7 | |||
8 | use arena::{map::ArenaMap, Arena}; | ||
9 | use base_db::CrateId; | ||
10 | use cfg::CfgOptions; | ||
11 | use drop_bomb::DropBomb; | ||
12 | use either::Either; | ||
13 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId}; | ||
14 | use rustc_hash::FxHashMap; | ||
15 | use syntax::{ast, AstNode, AstPtr}; | ||
16 | use test_utils::mark; | ||
17 | |||
18 | pub(crate) use lower::LowerCtx; | ||
19 | |||
20 | use 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. | ||
34 | pub(crate) struct CfgExpander { | ||
35 | cfg_options: CfgOptions, | ||
36 | hygiene: Hygiene, | ||
37 | } | ||
38 | |||
39 | pub(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)] | ||
49 | const EXPANSION_RECURSION_LIMIT: usize = 32; | ||
50 | |||
51 | #[cfg(not(test))] | ||
52 | const EXPANSION_RECURSION_LIMIT: usize = 128; | ||
53 | |||
54 | impl 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 | |||
75 | impl 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, ¯o_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 | |||
172 | pub(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)] | ||
180 | pub 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 | |||
195 | pub type ExprPtr = AstPtr<ast::Expr>; | ||
196 | pub type ExprSource = InFile<ExprPtr>; | ||
197 | |||
198 | pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>; | ||
199 | pub 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)] | ||
213 | pub 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)] | ||
223 | pub struct SyntheticSyntax; | ||
224 | |||
225 | impl 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 | |||
271 | impl Index<ExprId> for Body { | ||
272 | type Output = Expr; | ||
273 | |||
274 | fn index(&self, expr: ExprId) -> &Expr { | ||
275 | &self.exprs[expr] | ||
276 | } | ||
277 | } | ||
278 | |||
279 | impl Index<PatId> for Body { | ||
280 | type Output = Pat; | ||
281 | |||
282 | fn index(&self, pat: PatId) -> &Pat { | ||
283 | &self.pats[pat] | ||
284 | } | ||
285 | } | ||
286 | |||
287 | impl 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)] | ||
322 | mod 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 | " | ||
350 | macro_rules! n_nuple { | ||
351 | ($e:tt) => (); | ||
352 | ($($rest:tt)*) => {{ | ||
353 | (n_nuple!($($rest)*)None,) | ||
354 | }}; | ||
355 | } | ||
356 | fn 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 | |||
4 | use std::{any::type_name, sync::Arc}; | ||
5 | |||
6 | use arena::Arena; | ||
7 | use either::Either; | ||
8 | use hir_expand::{ | ||
9 | hygiene::Hygiene, | ||
10 | name::{name, AsName, Name}, | ||
11 | HirFileId, MacroDefId, MacroDefKind, | ||
12 | }; | ||
13 | use rustc_hash::FxHashMap; | ||
14 | use syntax::{ | ||
15 | ast::{ | ||
16 | self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner, | ||
17 | SlicePatComponents, | ||
18 | }, | ||
19 | AstNode, AstPtr, | ||
20 | }; | ||
21 | use test_utils::mark; | ||
22 | |||
23 | use 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 | |||
40 | use super::{ExprSource, PatSource}; | ||
41 | |||
42 | pub(crate) struct LowerCtx { | ||
43 | hygiene: Hygiene, | ||
44 | } | ||
45 | |||
46 | impl 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 | |||
59 | pub(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 | |||
88 | struct 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 | |||
98 | impl 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 | |||
867 | impl 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 | |||
911 | impl 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. | ||
2 | use std::sync::Arc; | ||
3 | |||
4 | use arena::{Arena, Idx}; | ||
5 | use hir_expand::name::Name; | ||
6 | use rustc_hash::FxHashMap; | ||
7 | |||
8 | use crate::{ | ||
9 | body::Body, | ||
10 | db::DefDatabase, | ||
11 | expr::{Expr, ExprId, Pat, PatId, Statement}, | ||
12 | DefWithBodyId, | ||
13 | }; | ||
14 | |||
15 | pub type ScopeId = Idx<ScopeData>; | ||
16 | |||
17 | #[derive(Debug, PartialEq, Eq)] | ||
18 | pub struct ExprScopes { | ||
19 | scopes: Arena<ScopeData>, | ||
20 | scope_by_expr: FxHashMap<ExprId, ScopeId>, | ||
21 | } | ||
22 | |||
23 | #[derive(Debug, PartialEq, Eq)] | ||
24 | pub struct ScopeEntry { | ||
25 | name: Name, | ||
26 | pat: PatId, | ||
27 | } | ||
28 | |||
29 | impl 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)] | ||
40 | pub struct ScopeData { | ||
41 | parent: Option<ScopeId>, | ||
42 | entries: Vec<ScopeEntry>, | ||
43 | } | ||
44 | |||
45 | impl 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 | |||
108 | fn 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 | |||
136 | fn 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)] | ||
171 | mod 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" | ||
307 | fn 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#" | ||
389 | fn 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#" | ||
406 | fn 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" | ||
418 | fn 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" | ||
431 | fn 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#" | ||
446 | fn 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 | |||
6 | use std::fmt; | ||
7 | |||
8 | use hir_expand::name::{name, AsName, Name}; | ||
9 | |||
10 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] | ||
11 | pub enum Signedness { | ||
12 | Signed, | ||
13 | Unsigned, | ||
14 | } | ||
15 | |||
16 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] | ||
17 | pub enum IntBitness { | ||
18 | Xsize, | ||
19 | X8, | ||
20 | X16, | ||
21 | X32, | ||
22 | X64, | ||
23 | X128, | ||
24 | } | ||
25 | |||
26 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] | ||
27 | pub enum FloatBitness { | ||
28 | X32, | ||
29 | X64, | ||
30 | } | ||
31 | |||
32 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
33 | pub struct BuiltinInt { | ||
34 | pub signedness: Signedness, | ||
35 | pub bitness: IntBitness, | ||
36 | } | ||
37 | |||
38 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
39 | pub struct BuiltinFloat { | ||
40 | pub bitness: FloatBitness, | ||
41 | } | ||
42 | |||
43 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
44 | pub enum BuiltinType { | ||
45 | Char, | ||
46 | Bool, | ||
47 | Str, | ||
48 | Int(BuiltinInt), | ||
49 | Float(BuiltinFloat), | ||
50 | } | ||
51 | |||
52 | impl 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 | |||
78 | impl 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 | |||
107 | impl 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] | ||
115 | impl 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] | ||
154 | impl 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 | |||
7 | use either::Either; | ||
8 | |||
9 | use 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 | |||
19 | pub trait ChildBySource { | ||
20 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap; | ||
21 | } | ||
22 | |||
23 | impl 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 | |||
49 | impl 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 | |||
75 | impl 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 | |||
83 | impl 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 | |||
136 | impl 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 | |||
157 | impl 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 | |||
172 | impl 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 | |||
3 | use std::sync::Arc; | ||
4 | |||
5 | use hir_expand::{name::Name, InFile}; | ||
6 | use syntax::ast; | ||
7 | |||
8 | use 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)] | ||
20 | pub 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 | |||
33 | impl 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)] | ||
53 | pub 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 | |||
61 | impl 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)] | ||
80 | pub struct TraitData { | ||
81 | pub name: Name, | ||
82 | pub items: Vec<(Name, AssocItemId)>, | ||
83 | pub auto: bool, | ||
84 | } | ||
85 | |||
86 | impl 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)] | ||
126 | pub 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 | |||
133 | impl 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)] | ||
163 | pub struct ConstData { | ||
164 | /// const _: () = (); | ||
165 | pub name: Option<Name>, | ||
166 | pub type_ref: TypeRef, | ||
167 | pub visibility: RawVisibility, | ||
168 | } | ||
169 | |||
170 | impl 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)] | ||
185 | pub struct StaticData { | ||
186 | pub name: Option<Name>, | ||
187 | pub type_ref: TypeRef, | ||
188 | pub visibility: RawVisibility, | ||
189 | pub mutable: bool, | ||
190 | } | ||
191 | |||
192 | impl 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 | |||
207 | fn 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. | ||
2 | use std::sync::Arc; | ||
3 | |||
4 | use base_db::{salsa, CrateId, SourceDatabase, Upcast}; | ||
5 | use hir_expand::{db::AstDatabase, HirFileId}; | ||
6 | use syntax::SmolStr; | ||
7 | |||
8 | use 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)] | ||
25 | pub 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)] | ||
47 | pub 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 | |||
117 | fn 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 | |||
3 | use std::any::Any; | ||
4 | |||
5 | use hir_expand::diagnostics::{Diagnostic, DiagnosticCode}; | ||
6 | use syntax::{ast, AstPtr, SyntaxNodePtr}; | ||
7 | |||
8 | use hir_expand::{HirFileId, InFile}; | ||
9 | |||
10 | #[derive(Debug)] | ||
11 | pub struct UnresolvedModule { | ||
12 | pub file: HirFileId, | ||
13 | pub decl: AstPtr<ast::Module>, | ||
14 | pub candidate: String, | ||
15 | } | ||
16 | |||
17 | impl 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 | |||
6 | use std::sync::Arc; | ||
7 | |||
8 | use either::Either; | ||
9 | use syntax::ast; | ||
10 | |||
11 | use crate::{ | ||
12 | db::DefDatabase, | ||
13 | src::{HasChildSource, HasSource}, | ||
14 | AdtId, AttrDefId, Lookup, | ||
15 | }; | ||
16 | |||
17 | /// Holds documentation | ||
18 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
19 | pub struct Documentation(Arc<str>); | ||
20 | |||
21 | impl Into<String> for Documentation { | ||
22 | fn into(self) -> String { | ||
23 | self.as_str().to_owned() | ||
24 | } | ||
25 | } | ||
26 | |||
27 | impl 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 | |||
80 | pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation> | ||
81 | where | ||
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 | |||
90 | fn 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 | |||
106 | fn 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. | ||
24 | use std::{ | ||
25 | hash::Hash, | ||
26 | marker::PhantomData, | ||
27 | ops::{Index, IndexMut}, | ||
28 | }; | ||
29 | |||
30 | use anymap::Map; | ||
31 | use rustc_hash::FxHashMap; | ||
32 | |||
33 | pub struct Key<K, V, P = (K, V)> { | ||
34 | _phantom: PhantomData<(K, V, P)>, | ||
35 | } | ||
36 | |||
37 | impl<K, V, P> Key<K, V, P> { | ||
38 | pub(crate) const fn new() -> Key<K, V, P> { | ||
39 | Key { _phantom: PhantomData } | ||
40 | } | ||
41 | } | ||
42 | |||
43 | impl<K, V, P> Copy for Key<K, V, P> {} | ||
44 | |||
45 | impl<K, V, P> Clone for Key<K, V, P> { | ||
46 | fn clone(&self) -> Key<K, V, P> { | ||
47 | *self | ||
48 | } | ||
49 | } | ||
50 | |||
51 | pub 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 | |||
59 | impl<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 | |||
70 | pub struct DynMap { | ||
71 | pub(crate) map: Map, | ||
72 | } | ||
73 | |||
74 | impl Default for DynMap { | ||
75 | fn default() -> Self { | ||
76 | DynMap { map: Map::new() } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | #[repr(transparent)] | ||
81 | pub struct KeyMap<KEY> { | ||
82 | map: DynMap, | ||
83 | _phantom: PhantomData<KEY>, | ||
84 | } | ||
85 | |||
86 | impl<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 | |||
95 | impl<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 | |||
103 | impl<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 | |||
15 | use arena::{Idx, RawId}; | ||
16 | use hir_expand::name::Name; | ||
17 | use syntax::ast::RangeOp; | ||
18 | |||
19 | use crate::{ | ||
20 | builtin_type::{BuiltinFloat, BuiltinInt}, | ||
21 | path::{GenericArgs, Path}, | ||
22 | type_ref::{Mutability, Rawness, TypeRef}, | ||
23 | }; | ||
24 | |||
25 | pub type ExprId = Idx<Expr>; | ||
26 | pub(crate) fn dummy_expr_id() -> ExprId { | ||
27 | ExprId::from_raw(RawId::from(!0)) | ||
28 | } | ||
29 | |||
30 | pub type PatId = Idx<Pat>; | ||
31 | |||
32 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
33 | pub 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)] | ||
43 | pub 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)] | ||
161 | pub 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)] | ||
169 | pub enum LogicOp { | ||
170 | And, | ||
171 | Or, | ||
172 | } | ||
173 | |||
174 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
175 | pub enum CmpOp { | ||
176 | Eq { negated: bool }, | ||
177 | Ord { ordering: Ordering, strict: bool }, | ||
178 | } | ||
179 | |||
180 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
181 | pub enum Ordering { | ||
182 | Less, | ||
183 | Greater, | ||
184 | } | ||
185 | |||
186 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
187 | pub enum ArithOp { | ||
188 | Add, | ||
189 | Mul, | ||
190 | Sub, | ||
191 | Div, | ||
192 | Rem, | ||
193 | Shl, | ||
194 | Shr, | ||
195 | BitXor, | ||
196 | BitOr, | ||
197 | BitAnd, | ||
198 | } | ||
199 | |||
200 | pub use syntax::ast::PrefixOp as UnaryOp; | ||
201 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
202 | pub enum Array { | ||
203 | ElementList(Vec<ExprId>), | ||
204 | Repeat { initializer: ExprId, repeat: ExprId }, | ||
205 | } | ||
206 | |||
207 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
208 | pub struct MatchArm { | ||
209 | pub pat: PatId, | ||
210 | pub guard: Option<ExprId>, | ||
211 | pub expr: ExprId, | ||
212 | } | ||
213 | |||
214 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
215 | pub struct RecordLitField { | ||
216 | pub name: Name, | ||
217 | pub expr: ExprId, | ||
218 | } | ||
219 | |||
220 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
221 | pub enum Statement { | ||
222 | Let { pat: PatId, type_ref: Option<TypeRef>, initializer: Option<ExprId> }, | ||
223 | Expr(ExprId), | ||
224 | } | ||
225 | |||
226 | impl 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)] | ||
348 | pub 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 | |||
366 | impl 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)] | ||
378 | pub 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)] | ||
385 | pub 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 | |||
400 | impl 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 | |||
3 | use hir_expand::name::{known, AsName, Name}; | ||
4 | use rustc_hash::FxHashSet; | ||
5 | use test_utils::mark; | ||
6 | |||
7 | use 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. | ||
19 | pub 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 | |||
24 | const MAX_PATH_LEN: usize = 15; | ||
25 | |||
26 | impl 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 | |||
39 | fn 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 | |||
187 | fn 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`. | ||
212 | fn 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)] | ||
294 | mod 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. | ||
5 | use std::sync::Arc; | ||
6 | |||
7 | use arena::{map::ArenaMap, Arena}; | ||
8 | use base_db::FileId; | ||
9 | use either::Either; | ||
10 | use hir_expand::{ | ||
11 | name::{name, AsName, Name}, | ||
12 | InFile, | ||
13 | }; | ||
14 | use syntax::ast::{self, GenericParamsOwner, NameOwner, TypeBoundsOwner}; | ||
15 | |||
16 | use 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)] | ||
30 | pub 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)] | ||
37 | pub 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)] | ||
45 | pub 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)] | ||
56 | pub struct WherePredicate { | ||
57 | pub target: WherePredicateTarget, | ||
58 | pub bound: TypeBound, | ||
59 | } | ||
60 | |||
61 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
62 | pub enum WherePredicateTarget { | ||
63 | TypeRef(TypeRef), | ||
64 | /// For desugared where predicates that can directly refer to a type param. | ||
65 | TypeParam(LocalTypeParamId), | ||
66 | } | ||
67 | |||
68 | type SourceMap = ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>; | ||
69 | |||
70 | impl 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 | |||
317 | impl 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 | |||
326 | impl 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 | |||
3 | use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc}; | ||
4 | |||
5 | use base_db::CrateId; | ||
6 | use fst::{self, Streamer}; | ||
7 | use indexmap::{map::Entry, IndexMap}; | ||
8 | use rustc_hash::{FxHashMap, FxHasher}; | ||
9 | use smallvec::SmallVec; | ||
10 | use syntax::SmolStr; | ||
11 | |||
12 | use crate::{ | ||
13 | db::DefDatabase, | ||
14 | item_scope::ItemInNs, | ||
15 | path::{ModPath, PathKind}, | ||
16 | visibility::Visibility, | ||
17 | AssocItemId, ModuleDefId, ModuleId, TraitId, | ||
18 | }; | ||
19 | |||
20 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; | ||
21 | |||
22 | /// Item import details stored in the `ImportMap`. | ||
23 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
24 | pub 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)] | ||
40 | pub 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 | |||
57 | impl 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 | |||
171 | impl 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 | |||
178 | impl Eq for ImportMap {} | ||
179 | |||
180 | impl 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 | |||
200 | fn fst_path(path: &ModPath) -> String { | ||
201 | let mut s = path.to_string(); | ||
202 | s.make_ascii_lowercase(); | ||
203 | s | ||
204 | } | ||
205 | |||
206 | fn 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)] | ||
213 | pub struct Query { | ||
214 | query: String, | ||
215 | lowercased: String, | ||
216 | anchor_end: bool, | ||
217 | case_sensitive: bool, | ||
218 | limit: usize, | ||
219 | } | ||
220 | |||
221 | impl 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`. | ||
252 | pub 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)] | ||
329 | mod 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 | |||
4 | use std::collections::hash_map::Entry; | ||
5 | |||
6 | use base_db::CrateId; | ||
7 | use hir_expand::name::Name; | ||
8 | use once_cell::sync::Lazy; | ||
9 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
10 | use test_utils::mark; | ||
11 | |||
12 | use 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)] | ||
18 | pub(crate) enum ImportType { | ||
19 | Glob, | ||
20 | Named, | ||
21 | } | ||
22 | |||
23 | #[derive(Debug, Default)] | ||
24 | pub 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)] | ||
31 | pub 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 | |||
56 | pub(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)] | ||
65 | pub(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. | ||
74 | impl 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 | |||
270 | impl 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)] | ||
296 | pub enum ItemInNs { | ||
297 | Types(ModuleDefId), | ||
298 | Values(ModuleDefId), | ||
299 | Macros(MacroDefId), | ||
300 | } | ||
301 | |||
302 | impl 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 | |||
3 | mod lower; | ||
4 | #[cfg(test)] | ||
5 | mod tests; | ||
6 | |||
7 | use 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 | |||
16 | use arena::{Arena, Idx, RawId}; | ||
17 | use ast::{AstNode, AttrsOwner, NameOwner, StructKind}; | ||
18 | use either::Either; | ||
19 | use hir_expand::{ | ||
20 | ast_id_map::FileAstId, | ||
21 | hygiene::Hygiene, | ||
22 | name::{name, AsName, Name}, | ||
23 | HirFileId, InFile, | ||
24 | }; | ||
25 | use rustc_hash::FxHashMap; | ||
26 | use smallvec::SmallVec; | ||
27 | use syntax::{ast, match_ast}; | ||
28 | use test_utils::mark; | ||
29 | |||
30 | use 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)] | ||
40 | pub struct RawVisibilityId(u32); | ||
41 | |||
42 | impl 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 | |||
48 | impl 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)] | ||
62 | pub struct GenericParamsId(u32); | ||
63 | |||
64 | impl 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)] | ||
70 | pub 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 | |||
78 | impl 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)] | ||
220 | struct ItemVisibilities { | ||
221 | arena: Arena<RawVisibility>, | ||
222 | } | ||
223 | |||
224 | impl 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 | |||
238 | static VIS_PUB: RawVisibility = RawVisibility::Public; | ||
239 | static VIS_PRIV: RawVisibility = | ||
240 | RawVisibility::Module(ModPath { kind: PathKind::Super(0), segments: Vec::new() }); | ||
241 | static VIS_PUB_CRATE: RawVisibility = | ||
242 | RawVisibility::Module(ModPath { kind: PathKind::Crate, segments: Vec::new() }); | ||
243 | |||
244 | #[derive(Default, Debug, Eq, PartialEq)] | ||
245 | struct GenericParamsStorage { | ||
246 | arena: Arena<GenericParams>, | ||
247 | } | ||
248 | |||
249 | impl 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 | |||
259 | static EMPTY_GENERICS: GenericParams = | ||
260 | GenericParams { types: Arena::new(), where_predicates: Vec::new() }; | ||
261 | |||
262 | #[derive(Default, Debug, Eq, PartialEq)] | ||
263 | struct 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)] | ||
286 | pub 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 | |||
297 | macro_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 | |||
309 | from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>)); | ||
310 | |||
311 | /// Trait implemented by all item nodes in the item tree. | ||
312 | pub 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 | |||
327 | pub struct FileItemTreeId<N: ItemTreeNode> { | ||
328 | index: Idx<N>, | ||
329 | _p: PhantomData<N>, | ||
330 | } | ||
331 | |||
332 | impl<N: ItemTreeNode> Clone for FileItemTreeId<N> { | ||
333 | fn clone(&self) -> Self { | ||
334 | Self { index: self.index, _p: PhantomData } | ||
335 | } | ||
336 | } | ||
337 | impl<N: ItemTreeNode> Copy for FileItemTreeId<N> {} | ||
338 | |||
339 | impl<N: ItemTreeNode> PartialEq for FileItemTreeId<N> { | ||
340 | fn eq(&self, other: &FileItemTreeId<N>) -> bool { | ||
341 | self.index == other.index | ||
342 | } | ||
343 | } | ||
344 | impl<N: ItemTreeNode> Eq for FileItemTreeId<N> {} | ||
345 | |||
346 | impl<N: ItemTreeNode> Hash for FileItemTreeId<N> { | ||
347 | fn hash<H: Hasher>(&self, state: &mut H) { | ||
348 | self.index.hash(state) | ||
349 | } | ||
350 | } | ||
351 | |||
352 | impl<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 | |||
358 | pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>; | ||
359 | |||
360 | macro_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 | |||
413 | mod_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 | |||
429 | macro_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 | |||
443 | impl_index!(fields: Field, variants: Variant, exprs: Expr); | ||
444 | |||
445 | impl 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 | |||
457 | impl 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 | |||
468 | impl<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)] | ||
477 | pub 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)] | ||
489 | pub 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)] | ||
499 | pub 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)] | ||
512 | pub 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)] | ||
522 | pub 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)] | ||
532 | pub 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)] | ||
541 | pub 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)] | ||
550 | pub 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)] | ||
559 | pub 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)] | ||
568 | pub 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)] | ||
578 | pub 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)] | ||
588 | pub 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)] | ||
599 | pub 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)] | ||
607 | pub enum ModKind { | ||
608 | /// `mod m { ... }` | ||
609 | Inline { items: Box<[ModItem]> }, | ||
610 | |||
611 | /// `mod m;` | ||
612 | Outline {}, | ||
613 | } | ||
614 | |||
615 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
616 | pub 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)] | ||
633 | pub struct Expr; | ||
634 | |||
635 | macro_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 | |||
647 | impl 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)] | ||
672 | pub enum AssocItem { | ||
673 | Function(FileItemTreeId<Function>), | ||
674 | TypeAlias(FileItemTreeId<TypeAlias>), | ||
675 | Const(FileItemTreeId<Const>), | ||
676 | MacroCall(FileItemTreeId<MacroCall>), | ||
677 | } | ||
678 | |||
679 | impl_froms!(AssocItem { | ||
680 | Function(FileItemTreeId<Function>), | ||
681 | TypeAlias(FileItemTreeId<TypeAlias>), | ||
682 | Const(FileItemTreeId<Const>), | ||
683 | MacroCall(FileItemTreeId<MacroCall>), | ||
684 | }); | ||
685 | |||
686 | impl 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)] | ||
698 | pub struct Variant { | ||
699 | pub name: Name, | ||
700 | pub fields: Fields, | ||
701 | } | ||
702 | |||
703 | pub struct IdRange<T> { | ||
704 | range: Range<u32>, | ||
705 | _p: PhantomData<T>, | ||
706 | } | ||
707 | |||
708 | impl<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 | |||
714 | impl<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 | |||
721 | impl<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 | |||
727 | impl<T> Clone for IdRange<T> { | ||
728 | fn clone(&self) -> Self { | ||
729 | Self { range: self.range.clone(), _p: PhantomData } | ||
730 | } | ||
731 | } | ||
732 | |||
733 | impl<T> PartialEq for IdRange<T> { | ||
734 | fn eq(&self, other: &Self) -> bool { | ||
735 | self.range == other.range | ||
736 | } | ||
737 | } | ||
738 | |||
739 | impl<T> Eq for IdRange<T> {} | ||
740 | |||
741 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
742 | pub 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)] | ||
750 | pub 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 | |||
3 | use std::{collections::hash_map::Entry, mem, sync::Arc}; | ||
4 | |||
5 | use arena::map::ArenaMap; | ||
6 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId}; | ||
7 | use smallvec::SmallVec; | ||
8 | use syntax::{ | ||
9 | ast::{self, ModuleItemOwner}, | ||
10 | SyntaxNode, | ||
11 | }; | ||
12 | |||
13 | use crate::{ | ||
14 | attr::Attrs, | ||
15 | generics::{GenericParams, TypeParamData, TypeParamProvenance}, | ||
16 | }; | ||
17 | |||
18 | use super::*; | ||
19 | |||
20 | fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> { | ||
21 | FileItemTreeId { index, _p: PhantomData } | ||
22 | } | ||
23 | |||
24 | struct ModItems(SmallVec<[ModItem; 1]>); | ||
25 | |||
26 | impl<T> From<T> for ModItems | ||
27 | where | ||
28 | T: Into<ModItem>, | ||
29 | { | ||
30 | fn from(t: T) -> Self { | ||
31 | ModItems(SmallVec::from_buf([t.into(); 1])) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | pub(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 | |||
45 | impl 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 | |||
682 | fn 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 | |||
694 | enum 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 @@ | |||
1 | use base_db::fixture::WithFixture; | ||
2 | use expect_test::{expect, Expect}; | ||
3 | use hir_expand::{db::AstDatabase, HirFileId, InFile}; | ||
4 | use rustc_hash::FxHashSet; | ||
5 | use std::sync::Arc; | ||
6 | use stdx::format_to; | ||
7 | use syntax::{ast, AstNode}; | ||
8 | |||
9 | use crate::{db::DefDatabase, test_db::TestDB}; | ||
10 | |||
11 | use super::{ItemTree, ModItem, ModKind}; | ||
12 | |||
13 | fn 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 | |||
66 | fn 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 | |||
71 | fn 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 | |||
96 | fn 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 | |||
167 | fn check(ra_fixture: &str, expect: Expect) { | ||
168 | let actual = print_item_tree(ra_fixture); | ||
169 | expect.assert_eq(&actual); | ||
170 | } | ||
171 | |||
172 | #[test] | ||
173 | fn 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] | ||
261 | fn 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] | ||
290 | fn 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] | ||
314 | fn 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] | ||
340 | fn 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] | ||
366 | fn 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] | ||
399 | fn 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] | ||
424 | fn 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 | |||
3 | use std::marker::PhantomData; | ||
4 | |||
5 | use hir_expand::{InFile, MacroDefId}; | ||
6 | use rustc_hash::FxHashMap; | ||
7 | use syntax::{ast, AstNode, AstPtr}; | ||
8 | |||
9 | use crate::{ | ||
10 | dyn_map::{DynMap, Policy}, | ||
11 | ConstId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, StaticId, StructId, TraitId, | ||
12 | TypeAliasId, TypeParamId, UnionId, | ||
13 | }; | ||
14 | |||
15 | pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>; | ||
16 | |||
17 | pub const FUNCTION: Key<ast::Fn, FunctionId> = Key::new(); | ||
18 | pub const CONST: Key<ast::Const, ConstId> = Key::new(); | ||
19 | pub const STATIC: Key<ast::Static, StaticId> = Key::new(); | ||
20 | pub const TYPE_ALIAS: Key<ast::TypeAlias, TypeAliasId> = Key::new(); | ||
21 | pub const IMPL: Key<ast::Impl, ImplId> = Key::new(); | ||
22 | pub const TRAIT: Key<ast::Trait, TraitId> = Key::new(); | ||
23 | pub const STRUCT: Key<ast::Struct, StructId> = Key::new(); | ||
24 | pub const UNION: Key<ast::Union, UnionId> = Key::new(); | ||
25 | pub const ENUM: Key<ast::Enum, EnumId> = Key::new(); | ||
26 | |||
27 | pub const VARIANT: Key<ast::Variant, EnumVariantId> = Key::new(); | ||
28 | pub const TUPLE_FIELD: Key<ast::TupleField, FieldId> = Key::new(); | ||
29 | pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new(); | ||
30 | pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new(); | ||
31 | |||
32 | pub 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. | ||
40 | pub struct AstPtrPolicy<AST, ID> { | ||
41 | _phantom: PhantomData<(AST, ID)>, | ||
42 | } | ||
43 | |||
44 | impl<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. | ||
5 | use std::sync::Arc; | ||
6 | |||
7 | use rustc_hash::FxHashMap; | ||
8 | use syntax::SmolStr; | ||
9 | |||
10 | use 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)] | ||
16 | pub enum LangItemTarget { | ||
17 | EnumId(EnumId), | ||
18 | FunctionId(FunctionId), | ||
19 | ImplDefId(ImplId), | ||
20 | StaticId(StaticId), | ||
21 | StructId(StructId), | ||
22 | TraitId(TraitId), | ||
23 | } | ||
24 | |||
25 | impl 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)] | ||
70 | pub struct LangItems { | ||
71 | items: FxHashMap<SmolStr, LangItemTarget>, | ||
72 | } | ||
73 | |||
74 | impl 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 | |||
171 | pub 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)] | ||
11 | macro_rules! eprintln { | ||
12 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; | ||
13 | } | ||
14 | |||
15 | pub mod db; | ||
16 | |||
17 | pub mod attr; | ||
18 | pub mod path; | ||
19 | pub mod type_ref; | ||
20 | pub mod builtin_type; | ||
21 | pub mod diagnostics; | ||
22 | pub mod per_ns; | ||
23 | pub mod item_scope; | ||
24 | |||
25 | pub mod dyn_map; | ||
26 | pub mod keys; | ||
27 | |||
28 | pub mod item_tree; | ||
29 | |||
30 | pub mod adt; | ||
31 | pub mod data; | ||
32 | pub mod generics; | ||
33 | pub mod lang_item; | ||
34 | pub mod docs; | ||
35 | |||
36 | pub mod expr; | ||
37 | pub mod body; | ||
38 | pub mod resolver; | ||
39 | |||
40 | mod trace; | ||
41 | pub mod nameres; | ||
42 | |||
43 | pub mod src; | ||
44 | pub mod child_by_source; | ||
45 | |||
46 | pub mod visibility; | ||
47 | pub mod find_path; | ||
48 | pub mod import_map; | ||
49 | |||
50 | #[cfg(test)] | ||
51 | mod test_db; | ||
52 | |||
53 | use std::hash::{Hash, Hasher}; | ||
54 | |||
55 | use arena::Idx; | ||
56 | use base_db::{impl_intern_key, salsa, CrateId}; | ||
57 | use hir_expand::{ | ||
58 | ast_id_map::FileAstId, eager::expand_eager_macro, hygiene::Hygiene, AstId, HirFileId, InFile, | ||
59 | MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, | ||
60 | }; | ||
61 | use syntax::ast; | ||
62 | |||
63 | use crate::builtin_type::BuiltinType; | ||
64 | use item_tree::{ | ||
65 | Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait, | ||
66 | TypeAlias, Union, | ||
67 | }; | ||
68 | use stdx::impl_from; | ||
69 | |||
70 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
71 | pub struct ModuleId { | ||
72 | pub krate: CrateId, | ||
73 | pub local_id: LocalModuleId, | ||
74 | } | ||
75 | |||
76 | /// An ID of a module, **local** to a specific crate | ||
77 | pub type LocalModuleId = Idx<nameres::ModuleData>; | ||
78 | |||
79 | #[derive(Debug)] | ||
80 | pub struct ItemLoc<N: ItemTreeNode> { | ||
81 | pub container: ContainerId, | ||
82 | pub id: ItemTreeId<N>, | ||
83 | } | ||
84 | |||
85 | impl<N: ItemTreeNode> Clone for ItemLoc<N> { | ||
86 | fn clone(&self) -> Self { | ||
87 | Self { container: self.container, id: self.id } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | impl<N: ItemTreeNode> Copy for ItemLoc<N> {} | ||
92 | |||
93 | impl<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 | |||
99 | impl<N: ItemTreeNode> Eq for ItemLoc<N> {} | ||
100 | |||
101 | impl<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)] | ||
109 | pub struct AssocItemLoc<N: ItemTreeNode> { | ||
110 | pub container: AssocContainerId, | ||
111 | pub id: ItemTreeId<N>, | ||
112 | } | ||
113 | |||
114 | impl<N: ItemTreeNode> Clone for AssocItemLoc<N> { | ||
115 | fn clone(&self) -> Self { | ||
116 | Self { container: self.container, id: self.id } | ||
117 | } | ||
118 | } | ||
119 | |||
120 | impl<N: ItemTreeNode> Copy for AssocItemLoc<N> {} | ||
121 | |||
122 | impl<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 | |||
128 | impl<N: ItemTreeNode> Eq for AssocItemLoc<N> {} | ||
129 | |||
130 | impl<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 | |||
137 | macro_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)] | ||
158 | pub struct FunctionId(salsa::InternId); | ||
159 | type FunctionLoc = AssocItemLoc<Function>; | ||
160 | impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function); | ||
161 | |||
162 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
163 | pub struct StructId(salsa::InternId); | ||
164 | type StructLoc = ItemLoc<Struct>; | ||
165 | impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct); | ||
166 | |||
167 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
168 | pub struct UnionId(salsa::InternId); | ||
169 | pub type UnionLoc = ItemLoc<Union>; | ||
170 | impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union); | ||
171 | |||
172 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
173 | pub struct EnumId(salsa::InternId); | ||
174 | pub type EnumLoc = ItemLoc<Enum>; | ||
175 | impl_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)] | ||
179 | pub struct EnumVariantId { | ||
180 | pub parent: EnumId, | ||
181 | pub local_id: LocalEnumVariantId, | ||
182 | } | ||
183 | |||
184 | pub type LocalEnumVariantId = Idx<adt::EnumVariantData>; | ||
185 | |||
186 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
187 | pub struct FieldId { | ||
188 | pub parent: VariantId, | ||
189 | pub local_id: LocalFieldId, | ||
190 | } | ||
191 | |||
192 | pub type LocalFieldId = Idx<adt::FieldData>; | ||
193 | |||
194 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
195 | pub struct ConstId(salsa::InternId); | ||
196 | type ConstLoc = AssocItemLoc<Const>; | ||
197 | impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const); | ||
198 | |||
199 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
200 | pub struct StaticId(salsa::InternId); | ||
201 | pub type StaticLoc = ItemLoc<Static>; | ||
202 | impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static); | ||
203 | |||
204 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
205 | pub struct TraitId(salsa::InternId); | ||
206 | pub type TraitLoc = ItemLoc<Trait>; | ||
207 | impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait); | ||
208 | |||
209 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
210 | pub struct TypeAliasId(salsa::InternId); | ||
211 | type TypeAliasLoc = AssocItemLoc<TypeAlias>; | ||
212 | impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias); | ||
213 | |||
214 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] | ||
215 | pub struct ImplId(salsa::InternId); | ||
216 | type ImplLoc = ItemLoc<Impl>; | ||
217 | impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); | ||
218 | |||
219 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
220 | pub struct TypeParamId { | ||
221 | pub parent: GenericDefId, | ||
222 | pub local_id: LocalTypeParamId, | ||
223 | } | ||
224 | |||
225 | pub type LocalTypeParamId = Idx<generics::TypeParamData>; | ||
226 | |||
227 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
228 | pub enum ContainerId { | ||
229 | ModuleId(ModuleId), | ||
230 | DefWithBodyId(DefWithBodyId), | ||
231 | } | ||
232 | |||
233 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
234 | pub enum AssocContainerId { | ||
235 | ContainerId(ContainerId), | ||
236 | ImplId(ImplId), | ||
237 | TraitId(TraitId), | ||
238 | } | ||
239 | impl_from!(ContainerId for AssocContainerId); | ||
240 | |||
241 | /// A Data Type | ||
242 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
243 | pub enum AdtId { | ||
244 | StructId(StructId), | ||
245 | UnionId(UnionId), | ||
246 | EnumId(EnumId), | ||
247 | } | ||
248 | impl_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)] | ||
252 | pub 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 | } | ||
264 | impl_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)] | ||
279 | pub enum DefWithBodyId { | ||
280 | FunctionId(FunctionId), | ||
281 | StaticId(StaticId), | ||
282 | ConstId(ConstId), | ||
283 | } | ||
284 | |||
285 | impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId); | ||
286 | |||
287 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
288 | pub 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. | ||
297 | impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId); | ||
298 | |||
299 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | ||
300 | pub 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 | } | ||
312 | impl_from!( | ||
313 | FunctionId, | ||
314 | AdtId(StructId, EnumId, UnionId), | ||
315 | TraitId, | ||
316 | TypeAliasId, | ||
317 | ImplId, | ||
318 | EnumVariantId, | ||
319 | ConstId | ||
320 | for GenericDefId | ||
321 | ); | ||
322 | |||
323 | impl 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)] | ||
334 | pub 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 | |||
348 | impl_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)] | ||
364 | pub enum VariantId { | ||
365 | EnumVariantId(EnumVariantId), | ||
366 | StructId(StructId), | ||
367 | UnionId(UnionId), | ||
368 | } | ||
369 | impl_from!(EnumVariantId, StructId, UnionId for VariantId); | ||
370 | |||
371 | trait Intern { | ||
372 | type ID; | ||
373 | fn intern(self, db: &dyn db::DefDatabase) -> Self::ID; | ||
374 | } | ||
375 | |||
376 | pub trait Lookup { | ||
377 | type Data; | ||
378 | fn lookup(&self, db: &dyn db::DefDatabase) -> Self::Data; | ||
379 | } | ||
380 | |||
381 | pub trait HasModule { | ||
382 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId; | ||
383 | } | ||
384 | |||
385 | impl 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 | |||
394 | impl 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 | |||
404 | impl<N: ItemTreeNode> HasModule for AssocItemLoc<N> { | ||
405 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { | ||
406 | self.container.module(db) | ||
407 | } | ||
408 | } | ||
409 | |||
410 | impl 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 | |||
421 | impl 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 | |||
431 | impl 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 | |||
441 | impl 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 | |||
455 | impl 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 | ||
462 | pub 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 | |||
471 | impl 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)] | ||
488 | struct AstIdWithPath<T: ast::AstNode> { | ||
489 | pub ast_id: AstId<T>, | ||
490 | pub path: path::ModPath, | ||
491 | } | ||
492 | |||
493 | impl<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 | |||
499 | impl 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 | |||
524 | impl 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 | |||
50 | mod collector; | ||
51 | mod mod_resolution; | ||
52 | mod path_resolution; | ||
53 | |||
54 | #[cfg(test)] | ||
55 | mod tests; | ||
56 | |||
57 | use std::sync::Arc; | ||
58 | |||
59 | use arena::Arena; | ||
60 | use base_db::{CrateId, Edition, FileId}; | ||
61 | use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile}; | ||
62 | use rustc_hash::FxHashMap; | ||
63 | use stdx::format_to; | ||
64 | use syntax::ast; | ||
65 | |||
66 | use 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)] | ||
77 | pub 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 | |||
91 | impl 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)] | ||
99 | pub 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 | |||
114 | impl Default for ModuleOrigin { | ||
115 | fn default() -> Self { | ||
116 | ModuleOrigin::CrateRoot { definition: FileId(0) } | ||
117 | } | ||
118 | } | ||
119 | |||
120 | impl 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)] | ||
163 | pub 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 | |||
172 | impl 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 | |||
268 | impl 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)] | ||
284 | pub enum ModuleSource { | ||
285 | SourceFile(ast::SourceFile), | ||
286 | Module(ast::Module), | ||
287 | } | ||
288 | |||
289 | mod 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 | |||
6 | use base_db::{CrateId, FileId, ProcMacroId}; | ||
7 | use cfg::CfgOptions; | ||
8 | use 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 | }; | ||
16 | use rustc_hash::FxHashMap; | ||
17 | use syntax::ast; | ||
18 | use test_utils::mark; | ||
19 | |||
20 | use 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 | |||
39 | const GLOB_RECURSION_LIMIT: usize = 100; | ||
40 | const EXPANSION_DEPTH_LIMIT: usize = 128; | ||
41 | const FIXED_POINT_LIMIT: usize = 8192; | ||
42 | |||
43 | pub(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)] | ||
95 | enum 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 | |||
104 | impl 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)] | ||
115 | struct 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 | |||
125 | impl 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)] | ||
156 | struct ImportDirective { | ||
157 | module_id: LocalModuleId, | ||
158 | import: Import, | ||
159 | status: PartialResolvedImport, | ||
160 | } | ||
161 | |||
162 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
163 | struct 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)] | ||
171 | struct DeriveDirective { | ||
172 | module_id: LocalModuleId, | ||
173 | ast_id: AstIdWithPath<ast::Item>, | ||
174 | } | ||
175 | |||
176 | struct 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 | ||
184 | struct 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 | |||
198 | impl 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 | ||
783 | struct 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 | |||
792 | impl 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 | |||
1204 | fn is_macro_rules(path: &ModPath) -> bool { | ||
1205 | path.as_ident() == Some(&name![macro_rules]) | ||
1206 | } | ||
1207 | |||
1208 | #[cfg(test)] | ||
1209 | mod 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. | ||
2 | use base_db::FileId; | ||
3 | use hir_expand::name::Name; | ||
4 | use syntax::SmolStr; | ||
5 | |||
6 | use crate::{db::DefDatabase, HirFileId}; | ||
7 | |||
8 | #[derive(Clone, Debug)] | ||
9 | pub(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 | |||
19 | impl 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)] | ||
83 | struct DirPath(String); | ||
84 | |||
85 | impl 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 | |||
13 | use std::iter::successors; | ||
14 | |||
15 | use base_db::Edition; | ||
16 | use hir_expand::name::Name; | ||
17 | use test_utils::mark; | ||
18 | |||
19 | use 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)] | ||
30 | pub(super) enum ResolveMode { | ||
31 | Import, | ||
32 | Other, | ||
33 | } | ||
34 | |||
35 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
36 | pub(super) enum ReachedFixedPoint { | ||
37 | Yes, | ||
38 | No, | ||
39 | } | ||
40 | |||
41 | #[derive(Debug, Clone)] | ||
42 | pub(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 | |||
49 | impl 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 | |||
64 | impl 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 @@ | |||
1 | mod globs; | ||
2 | mod incremental; | ||
3 | mod macros; | ||
4 | mod mod_resolution; | ||
5 | mod primitives; | ||
6 | |||
7 | use std::sync::Arc; | ||
8 | |||
9 | use base_db::{fixture::WithFixture, SourceDatabase}; | ||
10 | use expect_test::{expect, Expect}; | ||
11 | use test_utils::mark; | ||
12 | |||
13 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; | ||
14 | |||
15 | fn 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 | |||
21 | fn 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] | ||
29 | fn crate_def_map_smoke_test() { | ||
30 | check( | ||
31 | r#" | ||
32 | //- /lib.rs | ||
33 | mod foo; | ||
34 | struct S; | ||
35 | use crate::foo::bar::E; | ||
36 | use self::E::V; | ||
37 | |||
38 | //- /foo/mod.rs | ||
39 | pub mod bar; | ||
40 | fn f() {} | ||
41 | |||
42 | //- /foo/bar.rs | ||
43 | pub struct Baz; | ||
44 | |||
45 | union U { to_be: bool, not_to_be: u8 } | ||
46 | enum E { V } | ||
47 | |||
48 | extern { | ||
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] | ||
75 | fn crate_def_map_super_super() { | ||
76 | check( | ||
77 | r#" | ||
78 | mod 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] | ||
108 | fn crate_def_map_fn_mod_same_name() { | ||
109 | check( | ||
110 | r#" | ||
111 | mod 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] | ||
129 | fn bogus_paths() { | ||
130 | mark::check!(bogus_paths); | ||
131 | check( | ||
132 | r#" | ||
133 | //- /lib.rs | ||
134 | mod foo; | ||
135 | struct S; | ||
136 | use self; | ||
137 | |||
138 | //- /foo/mod.rs | ||
139 | use super; | ||
140 | use crate; | ||
141 | "#, | ||
142 | expect![[r#" | ||
143 | crate | ||
144 | S: t v | ||
145 | foo: t | ||
146 | |||
147 | crate::foo | ||
148 | "#]], | ||
149 | ); | ||
150 | } | ||
151 | |||
152 | #[test] | ||
153 | fn use_as() { | ||
154 | check( | ||
155 | r#" | ||
156 | //- /lib.rs | ||
157 | mod foo; | ||
158 | use crate::foo::Baz as Foo; | ||
159 | |||
160 | //- /foo/mod.rs | ||
161 | pub 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] | ||
175 | fn use_trees() { | ||
176 | check( | ||
177 | r#" | ||
178 | //- /lib.rs | ||
179 | mod foo; | ||
180 | use crate::foo::bar::{Baz, Quux}; | ||
181 | |||
182 | //- /foo/mod.rs | ||
183 | pub mod bar; | ||
184 | |||
185 | //- /foo/bar.rs | ||
186 | pub struct Baz; | ||
187 | pub 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] | ||
206 | fn re_exports() { | ||
207 | check( | ||
208 | r#" | ||
209 | //- /lib.rs | ||
210 | mod foo; | ||
211 | use self::foo::Baz; | ||
212 | |||
213 | //- /foo/mod.rs | ||
214 | pub mod bar; | ||
215 | pub use self::bar::Baz; | ||
216 | |||
217 | //- /foo/bar.rs | ||
218 | pub 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] | ||
236 | fn std_prelude() { | ||
237 | mark::check!(std_prelude); | ||
238 | check( | ||
239 | r#" | ||
240 | //- /main.rs crate:main deps:test_crate | ||
241 | use Foo::*; | ||
242 | |||
243 | //- /lib.rs crate:test_crate | ||
244 | mod prelude; | ||
245 | #[prelude_import] | ||
246 | use prelude::*; | ||
247 | |||
248 | //- /prelude.rs | ||
249 | pub enum Foo { Bar, Baz }; | ||
250 | "#, | ||
251 | expect![[r#" | ||
252 | crate | ||
253 | Bar: t v | ||
254 | Baz: t v | ||
255 | "#]], | ||
256 | ); | ||
257 | } | ||
258 | |||
259 | #[test] | ||
260 | fn can_import_enum_variant() { | ||
261 | mark::check!(can_import_enum_variant); | ||
262 | check( | ||
263 | r#" | ||
264 | enum E { V } | ||
265 | use self::E::V; | ||
266 | "#, | ||
267 | expect![[r#" | ||
268 | crate | ||
269 | E: t | ||
270 | V: t v | ||
271 | "#]], | ||
272 | ); | ||
273 | } | ||
274 | |||
275 | #[test] | ||
276 | fn edition_2015_imports() { | ||
277 | check( | ||
278 | r#" | ||
279 | //- /main.rs crate:main deps:other_crate edition:2015 | ||
280 | mod foo; | ||
281 | mod bar; | ||
282 | |||
283 | //- /bar.rs | ||
284 | struct Bar; | ||
285 | |||
286 | //- /foo.rs | ||
287 | use bar::Bar; | ||
288 | use other_crate::FromLib; | ||
289 | |||
290 | //- /lib.rs crate:other_crate edition:2018 | ||
291 | struct 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] | ||
309 | fn item_map_using_self() { | ||
310 | check( | ||
311 | r#" | ||
312 | //- /lib.rs | ||
313 | mod foo; | ||
314 | use crate::foo::bar::Baz::{self}; | ||
315 | |||
316 | //- /foo/mod.rs | ||
317 | pub mod bar; | ||
318 | |||
319 | //- /foo/bar.rs | ||
320 | pub 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] | ||
337 | fn item_map_across_crates() { | ||
338 | check( | ||
339 | r#" | ||
340 | //- /main.rs crate:main deps:test_crate | ||
341 | use test_crate::Baz; | ||
342 | |||
343 | //- /lib.rs crate:test_crate | ||
344 | pub struct Baz; | ||
345 | "#, | ||
346 | expect![[r#" | ||
347 | crate | ||
348 | Baz: t v | ||
349 | "#]], | ||
350 | ); | ||
351 | } | ||
352 | |||
353 | #[test] | ||
354 | fn extern_crate_rename() { | ||
355 | check( | ||
356 | r#" | ||
357 | //- /main.rs crate:main deps:alloc | ||
358 | extern crate alloc as alloc_crate; | ||
359 | mod alloc; | ||
360 | mod sync; | ||
361 | |||
362 | //- /sync.rs | ||
363 | use alloc_crate::Arc; | ||
364 | |||
365 | //- /lib.rs crate:alloc | ||
366 | struct 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] | ||
380 | fn extern_crate_rename_2015_edition() { | ||
381 | check( | ||
382 | r#" | ||
383 | //- /main.rs crate:main deps:alloc edition:2015 | ||
384 | extern crate alloc as alloc_crate; | ||
385 | mod alloc; | ||
386 | mod sync; | ||
387 | |||
388 | //- /sync.rs | ||
389 | use alloc_crate::Arc; | ||
390 | |||
391 | //- /lib.rs crate:alloc | ||
392 | struct 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] | ||
406 | fn reexport_across_crates() { | ||
407 | check( | ||
408 | r#" | ||
409 | //- /main.rs crate:main deps:test_crate | ||
410 | use test_crate::Baz; | ||
411 | |||
412 | //- /lib.rs crate:test_crate | ||
413 | pub use foo::Baz; | ||
414 | mod foo; | ||
415 | |||
416 | //- /foo.rs | ||
417 | pub struct Baz; | ||
418 | "#, | ||
419 | expect![[r#" | ||
420 | crate | ||
421 | Baz: t v | ||
422 | "#]], | ||
423 | ); | ||
424 | } | ||
425 | |||
426 | #[test] | ||
427 | fn values_dont_shadow_extern_crates() { | ||
428 | check( | ||
429 | r#" | ||
430 | //- /main.rs crate:main deps:foo | ||
431 | fn foo() {} | ||
432 | use foo::Bar; | ||
433 | |||
434 | //- /foo/lib.rs crate:foo | ||
435 | pub struct Bar; | ||
436 | "#, | ||
437 | expect![[r#" | ||
438 | crate | ||
439 | Bar: t v | ||
440 | foo: v | ||
441 | "#]], | ||
442 | ); | ||
443 | } | ||
444 | |||
445 | #[test] | ||
446 | fn std_prelude_takes_precedence_above_core_prelude() { | ||
447 | check( | ||
448 | r#" | ||
449 | //- /main.rs crate:main deps:core,std | ||
450 | use {Foo, Bar}; | ||
451 | |||
452 | //- /std.rs crate:std deps:core | ||
453 | #[prelude_import] | ||
454 | pub use self::prelude::*; | ||
455 | mod prelude { | ||
456 | pub struct Foo; | ||
457 | pub use core::prelude::Bar; | ||
458 | } | ||
459 | |||
460 | //- /core.rs crate:core | ||
461 | #[prelude_import] | ||
462 | pub use self::prelude::*; | ||
463 | mod 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] | ||
476 | fn cfg_not_test() { | ||
477 | check( | ||
478 | r#" | ||
479 | //- /main.rs crate:main deps:std | ||
480 | use {Foo, Bar, Baz}; | ||
481 | |||
482 | //- /lib.rs crate:std | ||
483 | #[prelude_import] | ||
484 | pub use self::prelude::*; | ||
485 | mod 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] | ||
504 | fn cfg_test() { | ||
505 | check( | ||
506 | r#" | ||
507 | //- /main.rs crate:main deps:std | ||
508 | use {Foo, Bar, Baz}; | ||
509 | |||
510 | //- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42 | ||
511 | #[prelude_import] | ||
512 | pub use self::prelude::*; | ||
513 | mod 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] | ||
532 | fn infer_multiple_namespace() { | ||
533 | check( | ||
534 | r#" | ||
535 | //- /main.rs | ||
536 | mod a { | ||
537 | pub type T = (); | ||
538 | pub use crate::b::*; | ||
539 | } | ||
540 | |||
541 | use crate::a::T; | ||
542 | |||
543 | mod 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] | ||
563 | fn underscore_import() { | ||
564 | check( | ||
565 | r#" | ||
566 | //- /main.rs | ||
567 | use tr::Tr as _; | ||
568 | use tr::Tr2 as _; | ||
569 | |||
570 | mod 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] | ||
589 | fn underscore_reexport() { | ||
590 | check( | ||
591 | r#" | ||
592 | //- /main.rs | ||
593 | mod tr { | ||
594 | pub trait PubTr {} | ||
595 | pub trait PrivTr {} | ||
596 | } | ||
597 | mod reex { | ||
598 | use crate::tr::PrivTr as _; | ||
599 | pub use crate::tr::PubTr as _; | ||
600 | } | ||
601 | use 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] | ||
621 | fn underscore_pub_crate_reexport() { | ||
622 | mark::check!(upgrade_underscore_visibility); | ||
623 | check( | ||
624 | r#" | ||
625 | //- /main.rs crate:main deps:lib | ||
626 | use lib::*; | ||
627 | |||
628 | //- /lib.rs crate:lib | ||
629 | use tr::Tr as _; | ||
630 | pub use tr::Tr as _; | ||
631 | |||
632 | mod tr { | ||
633 | pub trait Tr {} | ||
634 | } | ||
635 | "#, | ||
636 | expect![[r#" | ||
637 | crate | ||
638 | _: t | ||
639 | "#]], | ||
640 | ); | ||
641 | } | ||
642 | |||
643 | #[test] | ||
644 | fn underscore_nontrait() { | ||
645 | check( | ||
646 | r#" | ||
647 | //- /main.rs | ||
648 | mod m { | ||
649 | pub struct Struct; | ||
650 | pub enum Enum {} | ||
651 | pub const CONST: () = (); | ||
652 | } | ||
653 | use 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] | ||
668 | fn underscore_name_conflict() { | ||
669 | check( | ||
670 | r#" | ||
671 | //- /main.rs | ||
672 | struct Tr; | ||
673 | |||
674 | use tr::Tr as _; | ||
675 | |||
676 | mod 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 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn glob_1() { | ||
5 | check( | ||
6 | r#" | ||
7 | //- /lib.rs | ||
8 | mod foo; | ||
9 | use foo::*; | ||
10 | |||
11 | //- /foo/mod.rs | ||
12 | pub mod bar; | ||
13 | pub use self::bar::Baz; | ||
14 | pub struct Foo; | ||
15 | |||
16 | //- /foo/bar.rs | ||
17 | pub 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] | ||
38 | fn glob_2() { | ||
39 | check( | ||
40 | r#" | ||
41 | //- /lib.rs | ||
42 | mod foo; | ||
43 | use foo::*; | ||
44 | |||
45 | //- /foo/mod.rs | ||
46 | pub mod bar; | ||
47 | pub use self::bar::*; | ||
48 | pub struct Foo; | ||
49 | |||
50 | //- /foo/bar.rs | ||
51 | pub struct Baz; | ||
52 | pub 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] | ||
75 | fn glob_privacy_1() { | ||
76 | check( | ||
77 | r" | ||
78 | //- /lib.rs | ||
79 | mod foo; | ||
80 | use foo::*; | ||
81 | |||
82 | //- /foo/mod.rs | ||
83 | pub mod bar; | ||
84 | pub use self::bar::*; | ||
85 | struct PrivateStructFoo; | ||
86 | |||
87 | //- /foo/bar.rs | ||
88 | pub struct Baz; | ||
89 | struct PrivateStructBar; | ||
90 | pub 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] | ||
113 | fn glob_privacy_2() { | ||
114 | check( | ||
115 | r" | ||
116 | //- /lib.rs | ||
117 | mod foo; | ||
118 | use foo::*; | ||
119 | use foo::bar::*; | ||
120 | |||
121 | //- /foo/mod.rs | ||
122 | mod bar; | ||
123 | fn Foo() {}; | ||
124 | pub struct Foo {}; | ||
125 | |||
126 | //- /foo/bar.rs | ||
127 | pub(super) struct PrivateBaz; | ||
128 | struct PrivateBar; | ||
129 | pub(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] | ||
150 | fn glob_across_crates() { | ||
151 | mark::check!(glob_across_crates); | ||
152 | check( | ||
153 | r#" | ||
154 | //- /main.rs crate:main deps:test_crate | ||
155 | use test_crate::*; | ||
156 | |||
157 | //- /lib.rs crate:test_crate | ||
158 | pub struct Baz; | ||
159 | "#, | ||
160 | expect![[r#" | ||
161 | crate | ||
162 | Baz: t v | ||
163 | "#]], | ||
164 | ); | ||
165 | } | ||
166 | |||
167 | #[test] | ||
168 | fn glob_privacy_across_crates() { | ||
169 | check( | ||
170 | r#" | ||
171 | //- /main.rs crate:main deps:test_crate | ||
172 | use test_crate::*; | ||
173 | |||
174 | //- /lib.rs crate:test_crate | ||
175 | pub struct Baz; | ||
176 | struct Foo; | ||
177 | "#, | ||
178 | expect![[r#" | ||
179 | crate | ||
180 | Baz: t v | ||
181 | "#]], | ||
182 | ); | ||
183 | } | ||
184 | |||
185 | #[test] | ||
186 | fn glob_enum() { | ||
187 | mark::check!(glob_enum); | ||
188 | check( | ||
189 | r#" | ||
190 | enum Foo { Bar, Baz } | ||
191 | use 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] | ||
203 | fn glob_enum_group() { | ||
204 | mark::check!(glob_enum_group); | ||
205 | check( | ||
206 | r#" | ||
207 | enum Foo { Bar, Baz } | ||
208 | use 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] | ||
220 | fn glob_shadowed_def() { | ||
221 | mark::check!(import_shadowed); | ||
222 | check( | ||
223 | r#" | ||
224 | //- /lib.rs | ||
225 | mod foo; | ||
226 | mod bar; | ||
227 | use foo::*; | ||
228 | use bar::baz; | ||
229 | use baz::Bar; | ||
230 | |||
231 | //- /foo.rs | ||
232 | pub mod baz { pub struct Foo; } | ||
233 | |||
234 | //- /bar.rs | ||
235 | pub 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] | ||
260 | fn glob_shadowed_def_reversed() { | ||
261 | check( | ||
262 | r#" | ||
263 | //- /lib.rs | ||
264 | mod foo; | ||
265 | mod bar; | ||
266 | use bar::baz; | ||
267 | use foo::*; | ||
268 | use baz::Bar; | ||
269 | |||
270 | //- /foo.rs | ||
271 | pub mod baz { pub struct Foo; } | ||
272 | |||
273 | //- /bar.rs | ||
274 | pub 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] | ||
299 | fn glob_shadowed_def_dependencies() { | ||
300 | check( | ||
301 | r#" | ||
302 | mod a { pub mod foo { pub struct X; } } | ||
303 | mod b { pub use super::a::foo; } | ||
304 | mod c { pub mod foo { pub struct Y; } } | ||
305 | mod 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 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use base_db::SourceDatabaseExt; | ||
4 | |||
5 | use super::*; | ||
6 | |||
7 | fn 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] | ||
27 | fn 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] | ||
61 | fn 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 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn macro_rules_are_globally_visible() { | ||
5 | check( | ||
6 | r#" | ||
7 | //- /lib.rs | ||
8 | macro_rules! structs { | ||
9 | ($($i:ident),*) => { | ||
10 | $(struct $i { field: u32 } )* | ||
11 | } | ||
12 | } | ||
13 | structs!(Foo); | ||
14 | mod nested; | ||
15 | |||
16 | //- /nested.rs | ||
17 | structs!(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] | ||
32 | fn macro_rules_can_define_modules() { | ||
33 | check( | ||
34 | r#" | ||
35 | //- /lib.rs | ||
36 | macro_rules! m { | ||
37 | ($name:ident) => { mod $name; } | ||
38 | } | ||
39 | m!(n1); | ||
40 | mod m { m!(n3) } | ||
41 | |||
42 | //- /n1.rs | ||
43 | m!(n2) | ||
44 | //- /n1/n2.rs | ||
45 | struct X; | ||
46 | //- /m/n3.rs | ||
47 | struct 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] | ||
70 | fn macro_rules_from_other_crates_are_visible() { | ||
71 | check( | ||
72 | r#" | ||
73 | //- /main.rs crate:main deps:foo | ||
74 | foo::structs!(Foo, Bar) | ||
75 | mod bar; | ||
76 | |||
77 | //- /bar.rs | ||
78 | use crate::*; | ||
79 | |||
80 | //- /lib.rs crate:foo | ||
81 | #[macro_export] | ||
82 | macro_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] | ||
103 | fn macro_rules_export_with_local_inner_macros_are_visible() { | ||
104 | check( | ||
105 | r#" | ||
106 | //- /main.rs crate:main deps:foo | ||
107 | foo::structs!(Foo, Bar) | ||
108 | mod bar; | ||
109 | |||
110 | //- /bar.rs | ||
111 | use crate::*; | ||
112 | |||
113 | //- /lib.rs crate:foo | ||
114 | #[macro_export(local_inner_macros)] | ||
115 | macro_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] | ||
136 | fn local_inner_macros_makes_local_macros_usable() { | ||
137 | check( | ||
138 | r#" | ||
139 | //- /main.rs crate:main deps:foo | ||
140 | foo::structs!(Foo, Bar); | ||
141 | mod bar; | ||
142 | |||
143 | //- /bar.rs | ||
144 | use crate::*; | ||
145 | |||
146 | //- /lib.rs crate:foo | ||
147 | #[macro_export(local_inner_macros)] | ||
148 | macro_rules! structs { | ||
149 | ($($i:ident),*) => { | ||
150 | inner!($($i),*); | ||
151 | } | ||
152 | } | ||
153 | #[macro_export] | ||
154 | macro_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] | ||
175 | fn unexpanded_macro_should_expand_by_fixedpoint_loop() { | ||
176 | check( | ||
177 | r#" | ||
178 | //- /main.rs crate:main deps:foo | ||
179 | macro_rules! baz { | ||
180 | () => { | ||
181 | use foo::bar; | ||
182 | } | ||
183 | } | ||
184 | foo!(); | ||
185 | bar!(); | ||
186 | baz!(); | ||
187 | |||
188 | //- /lib.rs crate:foo | ||
189 | #[macro_export] | ||
190 | macro_rules! foo { | ||
191 | () => { | ||
192 | struct Foo { field: u32 } | ||
193 | } | ||
194 | } | ||
195 | #[macro_export] | ||
196 | macro_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] | ||
212 | fn 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 | ||
217 | structs!(Foo); | ||
218 | structs_priv!(Bar); | ||
219 | structs_not_exported!(MacroNotResolved1); | ||
220 | crate::structs!(MacroNotResolved2); | ||
221 | |||
222 | mod bar; | ||
223 | |||
224 | #[macro_use] | ||
225 | extern crate foo; | ||
226 | |||
227 | //- /bar.rs | ||
228 | structs!(Baz); | ||
229 | crate::structs!(MacroNotResolved3); | ||
230 | |||
231 | //- /lib.rs crate:foo | ||
232 | #[macro_export] | ||
233 | macro_rules! structs { | ||
234 | ($i:ident) => { struct $i; } | ||
235 | } | ||
236 | |||
237 | macro_rules! structs_not_exported { | ||
238 | ($i:ident) => { struct $i; } | ||
239 | } | ||
240 | |||
241 | mod 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] | ||
262 | fn prelude_is_macro_use() { | ||
263 | mark::check!(prelude_is_macro_use); | ||
264 | check( | ||
265 | r#" | ||
266 | //- /main.rs crate:main deps:foo | ||
267 | structs!(Foo); | ||
268 | structs_priv!(Bar); | ||
269 | structs_outside!(Out); | ||
270 | crate::structs!(MacroNotResolved2); | ||
271 | |||
272 | mod bar; | ||
273 | |||
274 | //- /bar.rs | ||
275 | structs!(Baz); | ||
276 | crate::structs!(MacroNotResolved3); | ||
277 | |||
278 | //- /lib.rs crate:foo | ||
279 | #[prelude_import] | ||
280 | use self::prelude::*; | ||
281 | |||
282 | mod 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] | ||
297 | macro_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] | ||
315 | fn prelude_cycle() { | ||
316 | check( | ||
317 | r#" | ||
318 | #[prelude_import] | ||
319 | use self::prelude::*; | ||
320 | |||
321 | declare_mod!(); | ||
322 | |||
323 | mod 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] | ||
339 | fn plain_macros_are_legacy_textual_scoped() { | ||
340 | check( | ||
341 | r#" | ||
342 | //- /main.rs | ||
343 | mod m1; | ||
344 | bar!(NotFoundNotMacroUse); | ||
345 | |||
346 | mod m2 { foo!(NotFoundBeforeInside2); } | ||
347 | |||
348 | macro_rules! foo { | ||
349 | ($x:ident) => { struct $x; } | ||
350 | } | ||
351 | foo!(Ok); | ||
352 | |||
353 | mod m3; | ||
354 | foo!(OkShadowStop); | ||
355 | bar!(NotFoundMacroUseStop); | ||
356 | |||
357 | #[macro_use] | ||
358 | mod m5 { | ||
359 | #[macro_use] | ||
360 | mod m6 { | ||
361 | macro_rules! foo { | ||
362 | ($x:ident) => { fn $x() {} } | ||
363 | } | ||
364 | } | ||
365 | } | ||
366 | foo!(ok_double_macro_use_shadow); | ||
367 | |||
368 | baz!(NotFoundBefore); | ||
369 | #[macro_use] | ||
370 | mod m7 { | ||
371 | macro_rules! baz { | ||
372 | ($x:ident) => { struct $x; } | ||
373 | } | ||
374 | } | ||
375 | baz!(OkAfter); | ||
376 | |||
377 | //- /m1.rs | ||
378 | foo!(NotFoundBeforeInside1); | ||
379 | macro_rules! bar { | ||
380 | ($x:ident) => { struct $x; } | ||
381 | } | ||
382 | |||
383 | //- /m3/mod.rs | ||
384 | foo!(OkAfterInside); | ||
385 | macro_rules! foo { | ||
386 | ($x:ident) => { fn $x() {} } | ||
387 | } | ||
388 | foo!(ok_shadow); | ||
389 | |||
390 | #[macro_use] | ||
391 | mod m4; | ||
392 | bar!(OkMacroUse); | ||
393 | |||
394 | //- /m3/m4.rs | ||
395 | foo!(ok_shadow_deep); | ||
396 | macro_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] | ||
436 | fn type_value_macro_live_in_different_scopes() { | ||
437 | check( | ||
438 | r#" | ||
439 | #[macro_export] | ||
440 | macro_rules! foo { | ||
441 | ($x:ident) => { type $x = (); } | ||
442 | } | ||
443 | |||
444 | foo!(foo); | ||
445 | use foo as bar; | ||
446 | |||
447 | use self::foo as baz; | ||
448 | fn 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] | ||
460 | fn macro_use_can_be_aliased() { | ||
461 | check( | ||
462 | r#" | ||
463 | //- /main.rs crate:main deps:foo | ||
464 | #[macro_use] | ||
465 | extern crate foo; | ||
466 | |||
467 | foo!(Direct); | ||
468 | bar!(Alias); | ||
469 | |||
470 | //- /lib.rs crate:foo | ||
471 | use crate::foo as bar; | ||
472 | |||
473 | mod 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] | ||
490 | fn path_qualified_macros() { | ||
491 | check( | ||
492 | r#" | ||
493 | macro_rules! foo { | ||
494 | ($x:ident) => { struct $x; } | ||
495 | } | ||
496 | |||
497 | crate::foo!(NotResolved); | ||
498 | |||
499 | crate::bar!(OkCrate); | ||
500 | bar!(OkPlain); | ||
501 | alias1!(NotHere); | ||
502 | m::alias1!(OkAliasPlain); | ||
503 | m::alias2!(OkAliasSuper); | ||
504 | m::alias3!(OkAliasCrate); | ||
505 | not_found!(NotFound); | ||
506 | |||
507 | mod 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] | ||
538 | fn 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] | ||
544 | extern crate foo; | ||
545 | |||
546 | #[macro_use] | ||
547 | mod m { | ||
548 | macro_rules! current { | ||
549 | () => { | ||
550 | use $crate::Foo as FooSelf; | ||
551 | } | ||
552 | } | ||
553 | } | ||
554 | |||
555 | struct Foo; | ||
556 | |||
557 | current!(); | ||
558 | not_current1!(); | ||
559 | foo::not_current2!(); | ||
560 | |||
561 | //- /lib.rs crate:foo | ||
562 | mod m { | ||
563 | #[macro_export] | ||
564 | macro_rules! not_current1 { | ||
565 | () => { | ||
566 | use $crate::Bar; | ||
567 | } | ||
568 | } | ||
569 | } | ||
570 | |||
571 | #[macro_export] | ||
572 | macro_rules! not_current2 { | ||
573 | () => { | ||
574 | use $crate::Baz; | ||
575 | } | ||
576 | } | ||
577 | |||
578 | struct Bar; | ||
579 | struct 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] | ||
596 | fn 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 | ||
602 | foo!(); | ||
603 | |||
604 | //- /std.rs crate:std deps:core | ||
605 | #[prelude_import] | ||
606 | use self::prelude::*; | ||
607 | |||
608 | pub use core::foo; | ||
609 | |||
610 | mod prelude {} | ||
611 | |||
612 | #[macro_use] | ||
613 | mod std_macros; | ||
614 | |||
615 | //- /core.rs crate:core | ||
616 | #[macro_export] | ||
617 | macro_rules! foo { | ||
618 | () => { | ||
619 | use $crate::bar; | ||
620 | } | ||
621 | } | ||
622 | |||
623 | pub struct bar; | ||
624 | "#, | ||
625 | expect![[r#" | ||
626 | crate | ||
627 | bar: t v | ||
628 | "#]], | ||
629 | ); | ||
630 | } | ||
631 | |||
632 | #[test] | ||
633 | fn 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] | ||
645 | fn macro_expansion_overflow() { | ||
646 | mark::check!(macro_expansion_overflow); | ||
647 | check( | ||
648 | r#" | ||
649 | macro_rules! a { | ||
650 | ($e:expr; $($t:tt)*) => { | ||
651 | b!($($t)*); | ||
652 | }; | ||
653 | () => {}; | ||
654 | } | ||
655 | |||
656 | macro_rules! b { | ||
657 | (static = $e:expr; $($t:tt)*) => { | ||
658 | a!($e; $($t)*); | ||
659 | }; | ||
660 | () => {}; | ||
661 | } | ||
662 | |||
663 | b! { 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 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn name_res_works_for_broken_modules() { | ||
5 | mark::check!(name_res_works_for_broken_modules); | ||
6 | check( | ||
7 | r" | ||
8 | //- /lib.rs | ||
9 | mod foo // no `;`, no body | ||
10 | use self::foo::Baz; | ||
11 | |||
12 | //- /foo/mod.rs | ||
13 | pub mod bar; | ||
14 | pub use self::bar::Baz; | ||
15 | |||
16 | //- /foo/bar.rs | ||
17 | pub struct Baz; | ||
18 | ", | ||
19 | expect![[r#" | ||
20 | crate | ||
21 | Baz: _ | ||
22 | foo: t | ||
23 | |||
24 | crate::foo | ||
25 | "#]], | ||
26 | ); | ||
27 | } | ||
28 | |||
29 | #[test] | ||
30 | fn nested_module_resolution() { | ||
31 | check( | ||
32 | r#" | ||
33 | //- /lib.rs | ||
34 | mod n1; | ||
35 | |||
36 | //- /n1.rs | ||
37 | mod n2; | ||
38 | |||
39 | //- /n1/n2.rs | ||
40 | struct 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] | ||
56 | fn nested_module_resolution_2() { | ||
57 | check( | ||
58 | r#" | ||
59 | //- /lib.rs | ||
60 | mod prelude; | ||
61 | mod iter; | ||
62 | |||
63 | //- /prelude.rs | ||
64 | pub use crate::iter::Iterator; | ||
65 | |||
66 | //- /iter.rs | ||
67 | pub use self::traits::Iterator; | ||
68 | mod traits; | ||
69 | |||
70 | //- /iter/traits.rs | ||
71 | pub use self::iterator::Iterator; | ||
72 | mod iterator; | ||
73 | |||
74 | //- /iter/traits/iterator.rs | ||
75 | pub 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] | ||
100 | fn module_resolution_works_for_non_standard_filenames() { | ||
101 | check( | ||
102 | r#" | ||
103 | //- /my_library.rs crate:my_library | ||
104 | mod foo; | ||
105 | use self::foo::Bar; | ||
106 | |||
107 | //- /foo/mod.rs | ||
108 | pub 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] | ||
122 | fn module_resolution_works_for_raw_modules() { | ||
123 | check( | ||
124 | r#" | ||
125 | //- /lib.rs | ||
126 | mod r#async; | ||
127 | use self::r#async::Bar; | ||
128 | |||
129 | //- /async.rs | ||
130 | pub 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] | ||
144 | fn module_resolution_decl_path() { | ||
145 | check( | ||
146 | r#" | ||
147 | //- /lib.rs | ||
148 | #[path = "bar/baz/foo.rs"] | ||
149 | mod foo; | ||
150 | use self::foo::Bar; | ||
151 | |||
152 | //- /bar/baz/foo.rs | ||
153 | pub 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] | ||
167 | fn module_resolution_module_with_path_in_mod_rs() { | ||
168 | check( | ||
169 | r#" | ||
170 | //- /main.rs | ||
171 | mod foo; | ||
172 | |||
173 | //- /foo/mod.rs | ||
174 | #[path = "baz.rs"] | ||
175 | pub mod bar; | ||
176 | use self::bar::Baz; | ||
177 | |||
178 | //- /foo/baz.rs | ||
179 | pub 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] | ||
196 | fn module_resolution_module_with_path_non_crate_root() { | ||
197 | check( | ||
198 | r#" | ||
199 | //- /main.rs | ||
200 | mod foo; | ||
201 | |||
202 | //- /foo.rs | ||
203 | #[path = "baz.rs"] | ||
204 | pub mod bar; | ||
205 | use self::bar::Baz; | ||
206 | |||
207 | //- /baz.rs | ||
208 | pub 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] | ||
225 | fn module_resolution_module_decl_path_super() { | ||
226 | check( | ||
227 | r#" | ||
228 | //- /main.rs | ||
229 | #[path = "bar/baz/module.rs"] | ||
230 | mod foo; | ||
231 | pub struct Baz; | ||
232 | |||
233 | //- /bar/baz/module.rs | ||
234 | use 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] | ||
248 | fn module_resolution_explicit_path_mod_rs() { | ||
249 | check( | ||
250 | r#" | ||
251 | //- /main.rs | ||
252 | #[path = "module/mod.rs"] | ||
253 | mod foo; | ||
254 | |||
255 | //- /module/mod.rs | ||
256 | pub 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] | ||
269 | fn module_resolution_relative_path() { | ||
270 | check( | ||
271 | r#" | ||
272 | //- /main.rs | ||
273 | mod foo; | ||
274 | |||
275 | //- /foo.rs | ||
276 | #[path = "./sub.rs"] | ||
277 | pub mod foo_bar; | ||
278 | |||
279 | //- /sub.rs | ||
280 | pub 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] | ||
296 | fn module_resolution_relative_path_2() { | ||
297 | check( | ||
298 | r#" | ||
299 | //- /main.rs | ||
300 | mod foo; | ||
301 | |||
302 | //- /foo/mod.rs | ||
303 | #[path="../sub.rs"] | ||
304 | pub mod foo_bar; | ||
305 | |||
306 | //- /sub.rs | ||
307 | pub 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] | ||
323 | fn module_resolution_relative_path_outside_root() { | ||
324 | check( | ||
325 | r#" | ||
326 | //- /main.rs | ||
327 | #[path="../../../../../outside.rs"] | ||
328 | mod foo; | ||
329 | "#, | ||
330 | expect![[r#" | ||
331 | crate | ||
332 | "#]], | ||
333 | ); | ||
334 | } | ||
335 | |||
336 | #[test] | ||
337 | fn module_resolution_explicit_path_mod_rs_2() { | ||
338 | check( | ||
339 | r#" | ||
340 | //- /main.rs | ||
341 | #[path = "module/bar/mod.rs"] | ||
342 | mod foo; | ||
343 | |||
344 | //- /module/bar/mod.rs | ||
345 | pub 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] | ||
358 | fn module_resolution_explicit_path_mod_rs_with_win_separator() { | ||
359 | check( | ||
360 | r#" | ||
361 | //- /main.rs | ||
362 | #[path = "module\bar\mod.rs"] | ||
363 | mod foo; | ||
364 | |||
365 | //- /module/bar/mod.rs | ||
366 | pub 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] | ||
379 | fn module_resolution_decl_inside_inline_module_with_path_attribute() { | ||
380 | check( | ||
381 | r#" | ||
382 | //- /main.rs | ||
383 | #[path = "models"] | ||
384 | mod foo { mod bar; } | ||
385 | |||
386 | //- /models/bar.rs | ||
387 | pub 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] | ||
403 | fn module_resolution_decl_inside_inline_module() { | ||
404 | check( | ||
405 | r#" | ||
406 | //- /main.rs | ||
407 | mod foo { mod bar; } | ||
408 | |||
409 | //- /foo/bar.rs | ||
410 | pub 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] | ||
426 | fn module_resolution_decl_inside_inline_module_2_with_path_attribute() { | ||
427 | check( | ||
428 | r#" | ||
429 | //- /main.rs | ||
430 | #[path = "models/db"] | ||
431 | mod foo { mod bar; } | ||
432 | |||
433 | //- /models/db/bar.rs | ||
434 | pub 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] | ||
450 | fn module_resolution_decl_inside_inline_module_3() { | ||
451 | check( | ||
452 | r#" | ||
453 | //- /main.rs | ||
454 | #[path = "models/db"] | ||
455 | mod foo { | ||
456 | #[path = "users.rs"] | ||
457 | mod bar; | ||
458 | } | ||
459 | |||
460 | //- /models/db/users.rs | ||
461 | pub 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] | ||
477 | fn module_resolution_decl_inside_inline_module_empty_path() { | ||
478 | check( | ||
479 | r#" | ||
480 | //- /main.rs | ||
481 | #[path = ""] | ||
482 | mod foo { | ||
483 | #[path = "users.rs"] | ||
484 | mod bar; | ||
485 | } | ||
486 | |||
487 | //- /users.rs | ||
488 | pub 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] | ||
504 | fn module_resolution_decl_empty_path() { | ||
505 | check( | ||
506 | r#" | ||
507 | //- /main.rs | ||
508 | #[path = ""] // Should try to read `/` (a directory) | ||
509 | mod foo; | ||
510 | |||
511 | //- /foo.rs | ||
512 | pub struct Baz; | ||
513 | "#, | ||
514 | expect![[r#" | ||
515 | crate | ||
516 | "#]], | ||
517 | ); | ||
518 | } | ||
519 | |||
520 | #[test] | ||
521 | fn module_resolution_decl_inside_inline_module_relative_path() { | ||
522 | check( | ||
523 | r#" | ||
524 | //- /main.rs | ||
525 | #[path = "./models"] | ||
526 | mod foo { mod bar; } | ||
527 | |||
528 | //- /models/bar.rs | ||
529 | pub 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] | ||
545 | fn module_resolution_decl_inside_inline_module_in_crate_root() { | ||
546 | check( | ||
547 | r#" | ||
548 | //- /main.rs | ||
549 | mod foo { | ||
550 | #[path = "baz.rs"] | ||
551 | mod bar; | ||
552 | } | ||
553 | use self::foo::bar::Baz; | ||
554 | |||
555 | //- /foo/baz.rs | ||
556 | pub 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] | ||
573 | fn module_resolution_decl_inside_inline_module_in_mod_rs() { | ||
574 | check( | ||
575 | r#" | ||
576 | //- /main.rs | ||
577 | mod foo; | ||
578 | |||
579 | //- /foo/mod.rs | ||
580 | mod bar { | ||
581 | #[path = "qwe.rs"] | ||
582 | pub mod baz; | ||
583 | } | ||
584 | use self::bar::baz::Baz; | ||
585 | |||
586 | //- /foo/bar/qwe.rs | ||
587 | pub 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] | ||
607 | fn module_resolution_decl_inside_inline_module_in_non_crate_root() { | ||
608 | check( | ||
609 | r#" | ||
610 | //- /main.rs | ||
611 | mod foo; | ||
612 | |||
613 | //- /foo.rs | ||
614 | mod bar { | ||
615 | #[path = "qwe.rs"] | ||
616 | pub mod baz; | ||
617 | } | ||
618 | use self::bar::baz::Baz; | ||
619 | |||
620 | //- /foo/bar/qwe.rs | ||
621 | pub 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] | ||
641 | fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() { | ||
642 | check( | ||
643 | r#" | ||
644 | //- /main.rs | ||
645 | mod foo; | ||
646 | |||
647 | //- /foo.rs | ||
648 | #[path = "bar"] | ||
649 | mod bar { | ||
650 | pub mod baz; | ||
651 | } | ||
652 | use self::bar::baz::Baz; | ||
653 | |||
654 | //- /bar/baz.rs | ||
655 | pub 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] | ||
675 | fn 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] | ||
711 | fn module_resolution_decl_inside_module_in_non_crate_root_2() { | ||
712 | check( | ||
713 | r#" | ||
714 | //- /main.rs | ||
715 | #[path="module/m2.rs"] | ||
716 | mod module; | ||
717 | |||
718 | //- /module/m2.rs | ||
719 | pub mod submod; | ||
720 | |||
721 | //- /module/submod.rs | ||
722 | pub 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] | ||
738 | fn nested_out_of_line_module() { | ||
739 | check( | ||
740 | r#" | ||
741 | //- /lib.rs | ||
742 | mod a { | ||
743 | mod b { | ||
744 | mod c; | ||
745 | } | ||
746 | } | ||
747 | |||
748 | //- /a/b/c.rs | ||
749 | struct 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] | ||
768 | fn nested_out_of_line_module_with_path() { | ||
769 | check( | ||
770 | r#" | ||
771 | //- /lib.rs | ||
772 | mod a { | ||
773 | #[path = "d/e"] | ||
774 | mod b { | ||
775 | mod c; | ||
776 | } | ||
777 | } | ||
778 | |||
779 | //- /a/d/e/c.rs | ||
780 | struct 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 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn primitive_reexport() { | ||
5 | check( | ||
6 | r#" | ||
7 | //- /lib.rs | ||
8 | mod foo; | ||
9 | use foo::int; | ||
10 | |||
11 | //- /foo.rs | ||
12 | pub 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`. | ||
2 | mod lower; | ||
3 | |||
4 | use std::{ | ||
5 | fmt::{self, Display}, | ||
6 | iter, | ||
7 | sync::Arc, | ||
8 | }; | ||
9 | |||
10 | use crate::body::LowerCtx; | ||
11 | use base_db::CrateId; | ||
12 | use hir_expand::{ | ||
13 | hygiene::Hygiene, | ||
14 | name::{AsName, Name}, | ||
15 | }; | ||
16 | use syntax::ast; | ||
17 | |||
18 | use crate::{ | ||
19 | type_ref::{TypeBound, TypeRef}, | ||
20 | InFile, | ||
21 | }; | ||
22 | |||
23 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
24 | pub struct ModPath { | ||
25 | pub kind: PathKind, | ||
26 | pub segments: Vec<Name>, | ||
27 | } | ||
28 | |||
29 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
30 | pub 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)] | ||
42 | pub enum ImportAlias { | ||
43 | /// Unnamed alias, as in `use Foo as _;` | ||
44 | Underscore, | ||
45 | /// Named alias | ||
46 | Alias(Name), | ||
47 | } | ||
48 | |||
49 | impl 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)] | ||
110 | pub 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)] | ||
122 | pub 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)] | ||
136 | pub 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)] | ||
150 | pub enum GenericArg { | ||
151 | Type(TypeRef), | ||
152 | // or lifetime... | ||
153 | } | ||
154 | |||
155 | impl 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)] | ||
206 | pub struct PathSegment<'a> { | ||
207 | pub name: &'a Name, | ||
208 | pub args_and_bindings: Option<&'a GenericArgs>, | ||
209 | } | ||
210 | |||
211 | pub struct PathSegments<'a> { | ||
212 | segments: &'a [Name], | ||
213 | generic_args: &'a [Option<Arc<GenericArgs>>], | ||
214 | } | ||
215 | |||
216 | impl<'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 | |||
254 | impl 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 | |||
264 | impl 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 | |||
274 | impl From<Name> for ModPath { | ||
275 | fn from(name: Name) -> ModPath { | ||
276 | ModPath::from_segments(PathKind::Plain, iter::once(name)) | ||
277 | } | ||
278 | } | ||
279 | |||
280 | impl 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 | |||
316 | pub use hir_expand::name as __name; | ||
317 | |||
318 | #[macro_export] | ||
319 | macro_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] | ||
336 | macro_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 | |||
345 | pub 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 | |||
3 | mod lower_use; | ||
4 | |||
5 | use std::sync::Arc; | ||
6 | |||
7 | use either::Either; | ||
8 | use hir_expand::{ | ||
9 | hygiene::Hygiene, | ||
10 | name::{name, AsName}, | ||
11 | }; | ||
12 | use syntax::ast::{self, AstNode, TypeBoundsOwner}; | ||
13 | |||
14 | use super::AssociatedTypeBinding; | ||
15 | use crate::{ | ||
16 | body::LowerCtx, | ||
17 | path::{GenericArg, GenericArgs, ModPath, Path, PathKind}, | ||
18 | type_ref::{TypeBound, TypeRef}, | ||
19 | }; | ||
20 | |||
21 | pub(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. | ||
25 | pub(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 | |||
149 | pub(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>`). | ||
186 | fn 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 | |||
4 | use std::iter; | ||
5 | |||
6 | use either::Either; | ||
7 | use hir_expand::{hygiene::Hygiene, name::AsName}; | ||
8 | use syntax::ast::{self, NameOwner}; | ||
9 | use test_utils::mark; | ||
10 | |||
11 | use crate::path::{ImportAlias, ModPath, PathKind}; | ||
12 | |||
13 | pub(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 | |||
65 | fn 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 | |||
6 | use hir_expand::MacroDefId; | ||
7 | |||
8 | use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId}; | ||
9 | |||
10 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
11 | pub struct PerNs { | ||
12 | pub types: Option<(ModuleDefId, Visibility)>, | ||
13 | pub values: Option<(ModuleDefId, Visibility)>, | ||
14 | pub macros: Option<(MacroDefId, Visibility)>, | ||
15 | } | ||
16 | |||
17 | impl Default for PerNs { | ||
18 | fn default() -> Self { | ||
19 | PerNs { types: None, values: None, macros: None } | ||
20 | } | ||
21 | } | ||
22 | |||
23 | impl 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. | ||
2 | use std::sync::Arc; | ||
3 | |||
4 | use base_db::CrateId; | ||
5 | use hir_expand::{ | ||
6 | name::{name, Name}, | ||
7 | MacroDefId, | ||
8 | }; | ||
9 | use rustc_hash::FxHashSet; | ||
10 | |||
11 | use 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)] | ||
29 | pub 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)] | ||
36 | struct ModuleItemMap { | ||
37 | crate_def_map: Arc<CrateDefMap>, | ||
38 | module_id: LocalModuleId, | ||
39 | } | ||
40 | |||
41 | #[derive(Debug, Clone)] | ||
42 | struct ExprScope { | ||
43 | owner: DefWithBodyId, | ||
44 | expr_scopes: Arc<ExprScopes>, | ||
45 | scope_id: ScopeId, | ||
46 | } | ||
47 | |||
48 | #[derive(Debug, Clone)] | ||
49 | enum 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)] | ||
65 | pub 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)] | ||
82 | pub enum ResolveValueResult { | ||
83 | ValueNs(ValueNs), | ||
84 | Partial(TypeNs, usize), | ||
85 | } | ||
86 | |||
87 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
88 | pub 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 | |||
98 | impl 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 | |||
476 | pub enum ScopeDef { | ||
477 | PerNs(PerNs), | ||
478 | ImplSelfType(ImplId), | ||
479 | AdtSelfType(AdtId), | ||
480 | GenericParam(TypeParamId), | ||
481 | Local(PatId), | ||
482 | } | ||
483 | |||
484 | impl 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) = ¶m.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? | ||
543 | pub 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 | |||
548 | pub 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 | |||
563 | impl 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 | |||
600 | pub trait HasResolver: Copy { | ||
601 | /// Builds a resolver for type references inside this def. | ||
602 | fn resolver(self, db: &dyn DefDatabase) -> Resolver; | ||
603 | } | ||
604 | |||
605 | impl 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 | |||
612 | impl 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 | |||
618 | impl<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 | |||
628 | impl 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 | |||
634 | impl HasResolver for ConstId { | ||
635 | fn resolver(self, db: &dyn DefDatabase) -> Resolver { | ||
636 | self.lookup(db).container.resolver(db) | ||
637 | } | ||
638 | } | ||
639 | |||
640 | impl HasResolver for StaticId { | ||
641 | fn resolver(self, db: &dyn DefDatabase) -> Resolver { | ||
642 | self.lookup(db).container.resolver(db) | ||
643 | } | ||
644 | } | ||
645 | |||
646 | impl 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 | |||
652 | impl 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 | |||
662 | impl 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 | |||
672 | impl 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 | |||
681 | impl 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 | |||
691 | impl 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 | |||
705 | impl 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 | |||
3 | use arena::map::ArenaMap; | ||
4 | use hir_expand::InFile; | ||
5 | |||
6 | use crate::{db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc}; | ||
7 | |||
8 | pub trait HasSource { | ||
9 | type Value; | ||
10 | fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>; | ||
11 | } | ||
12 | |||
13 | impl<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 | |||
26 | impl<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 | |||
39 | pub 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 | |||
3 | use std::{ | ||
4 | fmt, panic, | ||
5 | sync::{Arc, Mutex}, | ||
6 | }; | ||
7 | |||
8 | use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; | ||
9 | use hir_expand::db::AstDatabase; | ||
10 | use rustc_hash::FxHashSet; | ||
11 | |||
12 | use 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)] | ||
22 | pub struct TestDB { | ||
23 | storage: salsa::Storage<TestDB>, | ||
24 | events: Mutex<Option<Vec<salsa::Event>>>, | ||
25 | } | ||
26 | |||
27 | impl Upcast<dyn AstDatabase> for TestDB { | ||
28 | fn upcast(&self) -> &(dyn AstDatabase + 'static) { | ||
29 | &*self | ||
30 | } | ||
31 | } | ||
32 | |||
33 | impl Upcast<dyn DefDatabase> for TestDB { | ||
34 | fn upcast(&self) -> &(dyn DefDatabase + 'static) { | ||
35 | &*self | ||
36 | } | ||
37 | } | ||
38 | |||
39 | impl 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 | |||
48 | impl fmt::Debug for TestDB { | ||
49 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
50 | f.debug_struct("TestDB").finish() | ||
51 | } | ||
52 | } | ||
53 | |||
54 | impl panic::RefUnwindSafe for TestDB {} | ||
55 | |||
56 | impl 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 | |||
68 | impl 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. | ||
12 | use arena::{map::ArenaMap, Arena, Idx, RawId}; | ||
13 | |||
14 | pub(crate) struct Trace<T, V> { | ||
15 | arena: Option<Arena<T>>, | ||
16 | map: Option<ArenaMap<Idx<T>, V>>, | ||
17 | len: u32, | ||
18 | } | ||
19 | |||
20 | impl<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. | ||
3 | use syntax::ast::{self}; | ||
4 | |||
5 | use crate::{body::LowerCtx, path::Path}; | ||
6 | |||
7 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||
8 | pub enum Mutability { | ||
9 | Shared, | ||
10 | Mut, | ||
11 | } | ||
12 | |||
13 | impl 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)] | ||
38 | pub enum Rawness { | ||
39 | RawPtr, | ||
40 | Ref, | ||
41 | } | ||
42 | |||
43 | impl 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)] | ||
55 | pub 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)] | ||
73 | pub enum TypeBound { | ||
74 | Path(Path), | ||
75 | // also for<> bounds | ||
76 | // also Lifetimes | ||
77 | Error, | ||
78 | } | ||
79 | |||
80 | impl 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 | |||
209 | pub(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 | |||
220 | impl 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 | |||
3 | use hir_expand::{hygiene::Hygiene, InFile}; | ||
4 | use syntax::ast; | ||
5 | |||
6 | use 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)] | ||
15 | pub 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 | |||
23 | impl 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)] | ||
89 | pub enum Visibility { | ||
90 | /// Visibility is restricted to a certain module. | ||
91 | Module(ModuleId), | ||
92 | /// Visibility is unrestricted. | ||
93 | Public, | ||
94 | } | ||
95 | |||
96 | impl 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 | } | ||