diff options
author | Aleksey Kladov <[email protected]> | 2020-08-13 15:28:27 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-08-13 15:29:33 +0100 |
commit | b28c54a2c239acd73f2eea80fda9ee3960d2c046 (patch) | |
tree | 1bf0ea193bdb3b16ff42c2c01118b13a4276b2bb /crates/hir_def/src | |
parent | b7aa4898e0841ab8199643f89a0caa967b698ca8 (diff) |
Rename ra_hir_def -> hir_def
Diffstat (limited to 'crates/hir_def/src')
43 files changed, 15284 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..a26251cde --- /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.expr()); | ||
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..2e38a978f --- /dev/null +++ b/crates/hir_def/src/diagnostics.rs | |||
@@ -0,0 +1,27 @@ | |||
1 | //! Diagnostics produced by `hir_def`. | ||
2 | |||
3 | use std::any::Any; | ||
4 | |||
5 | use hir_expand::diagnostics::Diagnostic; | ||
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 message(&self) -> String { | ||
19 | "unresolved module".to_string() | ||
20 | } | ||
21 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
22 | InFile::new(self.file, self.decl.clone().into()) | ||
23 | } | ||
24 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
25 | self | ||
26 | } | ||
27 | } | ||
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..d32a0bdaf --- /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::{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) | ||