diff options
Diffstat (limited to 'crates/ra_hir_def/src')
34 files changed, 4385 insertions, 1438 deletions
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 2bc34d449..4994a2125 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs | |||
@@ -8,12 +8,12 @@ use hir_expand::{ | |||
8 | InFile, | 8 | InFile, |
9 | }; | 9 | }; |
10 | use ra_arena::{map::ArenaMap, Arena}; | 10 | use ra_arena::{map::ArenaMap, Arena}; |
11 | use ra_prof::profile; | ||
12 | use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner}; | 11 | use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner}; |
13 | 12 | ||
14 | use crate::{ | 13 | use crate::{ |
15 | body::{CfgExpander, LowerCtx}, | 14 | body::{CfgExpander, LowerCtx}, |
16 | db::DefDatabase, | 15 | db::DefDatabase, |
16 | item_tree::{Field, Fields, ItemTree}, | ||
17 | src::HasChildSource, | 17 | src::HasChildSource, |
18 | src::HasSource, | 18 | src::HasSource, |
19 | trace::Trace, | 19 | trace::Trace, |
@@ -22,6 +22,7 @@ use crate::{ | |||
22 | EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, | 22 | EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, |
23 | VariantId, | 23 | VariantId, |
24 | }; | 24 | }; |
25 | use ra_cfg::CfgOptions; | ||
25 | 26 | ||
26 | /// Note that we use `StructData` for unions as well! | 27 | /// Note that we use `StructData` for unions as well! |
27 | #[derive(Debug, Clone, PartialEq, Eq)] | 28 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -59,39 +60,48 @@ pub struct FieldData { | |||
59 | 60 | ||
60 | impl StructData { | 61 | impl StructData { |
61 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { | 62 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { |
62 | let src = id.lookup(db).source(db); | 63 | let loc = id.lookup(db); |
64 | let item_tree = db.item_tree(loc.id.file_id); | ||
65 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); | ||
63 | 66 | ||
64 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); | 67 | let strukt = &item_tree[loc.id.value]; |
65 | let variant_data = | 68 | let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields); |
66 | VariantData::new(db, src.map(|s| s.kind()), id.lookup(db).container.module(db)); | 69 | |
67 | let variant_data = Arc::new(variant_data); | 70 | Arc::new(StructData { name: strukt.name.clone(), variant_data: Arc::new(variant_data) }) |
68 | Arc::new(StructData { name, variant_data }) | ||
69 | } | 71 | } |
70 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { | 72 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { |
71 | let src = id.lookup(db).source(db); | 73 | let loc = id.lookup(db); |
72 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); | 74 | let item_tree = db.item_tree(loc.id.file_id); |
73 | let variant_data = VariantData::new( | 75 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); |
74 | db, | 76 | |
75 | src.map(|s| { | 77 | let union = &item_tree[loc.id.value]; |
76 | s.record_field_def_list() | 78 | let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields); |
77 | .map(ast::StructKind::Record) | 79 | |
78 | .unwrap_or(ast::StructKind::Unit) | 80 | Arc::new(StructData { name: union.name.clone(), variant_data: Arc::new(variant_data) }) |
79 | }), | ||
80 | id.lookup(db).container.module(db), | ||
81 | ); | ||
82 | let variant_data = Arc::new(variant_data); | ||
83 | Arc::new(StructData { name, variant_data }) | ||
84 | } | 81 | } |
85 | } | 82 | } |
86 | 83 | ||
87 | impl EnumData { | 84 | impl EnumData { |
88 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { | 85 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { |
89 | let _p = profile("enum_data_query"); | 86 | let loc = e.lookup(db); |
90 | let src = e.lookup(db).source(db); | 87 | let item_tree = db.item_tree(loc.id.file_id); |
91 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); | 88 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); |
92 | let mut trace = Trace::new_for_arena(); | 89 | |
93 | lower_enum(db, &mut trace, &src, e.lookup(db).container.module(db)); | 90 | let enum_ = &item_tree[loc.id.value]; |
94 | Arc::new(EnumData { name, variants: trace.into_arena() }) | 91 | let mut variants = Arena::new(); |
92 | for var_id in enum_.variants.clone() { | ||
93 | if item_tree.attrs(var_id.into()).is_cfg_enabled(&cfg_options) { | ||
94 | let var = &item_tree[var_id]; | ||
95 | let var_data = lower_fields(&item_tree, &cfg_options, &var.fields); | ||
96 | |||
97 | variants.alloc(EnumVariantData { | ||
98 | name: var.name.clone(), | ||
99 | variant_data: Arc::new(var_data), | ||
100 | }); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | Arc::new(EnumData { name: enum_.name.clone(), variants }) | ||
95 | } | 105 | } |
96 | 106 | ||
97 | pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> { | 107 | pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> { |
@@ -251,3 +261,35 @@ fn lower_struct( | |||
251 | ast::StructKind::Unit => StructKind::Unit, | 261 | ast::StructKind::Unit => StructKind::Unit, |
252 | } | 262 | } |
253 | } | 263 | } |
264 | |||
265 | fn lower_fields(item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields) -> VariantData { | ||
266 | match fields { | ||
267 | Fields::Record(flds) => { | ||
268 | let mut arena = Arena::new(); | ||
269 | for field_id in flds.clone() { | ||
270 | if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) { | ||
271 | arena.alloc(lower_field(item_tree, &item_tree[field_id])); | ||
272 | } | ||
273 | } | ||
274 | VariantData::Record(arena) | ||
275 | } | ||
276 | Fields::Tuple(flds) => { | ||
277 | let mut arena = Arena::new(); | ||
278 | for field_id in flds.clone() { | ||
279 | if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) { | ||
280 | arena.alloc(lower_field(item_tree, &item_tree[field_id])); | ||
281 | } | ||
282 | } | ||
283 | VariantData::Tuple(arena) | ||
284 | } | ||
285 | Fields::Unit => VariantData::Unit, | ||
286 | } | ||
287 | } | ||
288 | |||
289 | fn lower_field(item_tree: &ItemTree, field: &Field) -> FieldData { | ||
290 | FieldData { | ||
291 | name: field.name.clone(), | ||
292 | type_ref: field.type_ref.clone(), | ||
293 | visibility: item_tree[field.visibility].clone(), | ||
294 | } | ||
295 | } | ||
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs index 8b6c0bede..e228e2145 100644 --- a/crates/ra_hir_def/src/attr.rs +++ b/crates/ra_hir_def/src/attr.rs | |||
@@ -13,7 +13,11 @@ use ra_syntax::{ | |||
13 | use tt::Subtree; | 13 | use tt::Subtree; |
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | db::DefDatabase, nameres::ModuleSource, path::ModPath, src::HasChildSource, src::HasSource, | 16 | db::DefDatabase, |
17 | item_tree::{ItemTreeId, ItemTreeNode}, | ||
18 | nameres::ModuleSource, | ||
19 | path::ModPath, | ||
20 | src::HasChildSource, | ||
17 | AdtId, AttrDefId, Lookup, | 21 | AdtId, AttrDefId, Lookup, |
18 | }; | 22 | }; |
19 | 23 | ||
@@ -34,6 +38,8 @@ impl ops::Deref for Attrs { | |||
34 | } | 38 | } |
35 | 39 | ||
36 | impl Attrs { | 40 | impl Attrs { |
41 | pub const EMPTY: Attrs = Attrs { entries: None }; | ||
42 | |||
37 | pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { | 43 | pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { |
38 | match def { | 44 | match def { |
39 | AttrDefId::ModuleId(module) => { | 45 | AttrDefId::ModuleId(module) => { |
@@ -65,19 +71,19 @@ impl Attrs { | |||
65 | Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner)) | 71 | Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner)) |
66 | } | 72 | } |
67 | AttrDefId::AdtId(it) => match it { | 73 | AttrDefId::AdtId(it) => match it { |
68 | AdtId::StructId(it) => attrs_from_loc(it.lookup(db), db), | 74 | AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
69 | AdtId::EnumId(it) => attrs_from_loc(it.lookup(db), db), | 75 | AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
70 | AdtId::UnionId(it) => attrs_from_loc(it.lookup(db), db), | 76 | AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
71 | }, | 77 | }, |
72 | AttrDefId::TraitId(it) => attrs_from_loc(it.lookup(db), db), | 78 | AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
73 | AttrDefId::MacroDefId(it) => { | 79 | AttrDefId::MacroDefId(it) => { |
74 | it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db)) | 80 | it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db)) |
75 | } | 81 | } |
76 | AttrDefId::ImplId(it) => attrs_from_loc(it.lookup(db), db), | 82 | AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
77 | AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db), | 83 | AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
78 | AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db), | 84 | AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
79 | AttrDefId::FunctionId(it) => attrs_from_loc(it.lookup(db), db), | 85 | AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
80 | AttrDefId::TypeAliasId(it) => attrs_from_loc(it.lookup(db), db), | 86 | AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
81 | } | 87 | } |
82 | } | 88 | } |
83 | 89 | ||
@@ -87,16 +93,34 @@ impl Attrs { | |||
87 | } | 93 | } |
88 | 94 | ||
89 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { | 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 | ); | ||
90 | let mut attrs = owner.attrs().peekable(); | 102 | let mut attrs = owner.attrs().peekable(); |
91 | let entries = if attrs.peek().is_none() { | 103 | let entries = if attrs.peek().is_none() { |
92 | // Avoid heap allocation | 104 | // Avoid heap allocation |
93 | None | 105 | None |
94 | } else { | 106 | } else { |
95 | Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect()) | 107 | Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect()) |
96 | }; | 108 | }; |
97 | Attrs { entries } | 109 | Attrs { entries } |
98 | } | 110 | } |
99 | 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 | |||
100 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { | 124 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { |
101 | AttrQuery { attrs: self, key } | 125 | AttrQuery { attrs: self, key } |
102 | } | 126 | } |
@@ -181,11 +205,8 @@ where | |||
181 | Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) | 205 | Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) |
182 | } | 206 | } |
183 | 207 | ||
184 | fn attrs_from_loc<T>(node: T, db: &dyn DefDatabase) -> Attrs | 208 | fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> Attrs { |
185 | where | 209 | let tree = db.item_tree(id.file_id); |
186 | T: HasSource, | 210 | let mod_item = N::id_to_mod_item(id.value); |
187 | T::Value: ast::AttrsOwner, | 211 | tree.attrs(mod_item.into()).clone() |
188 | { | ||
189 | let src = node.source(db); | ||
190 | Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) | ||
191 | } | 212 | } |
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 273036cee..2fe04db2b 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs | |||
@@ -14,6 +14,7 @@ use ra_db::CrateId; | |||
14 | use ra_prof::profile; | 14 | use ra_prof::profile; |
15 | use ra_syntax::{ast, AstNode, AstPtr}; | 15 | use ra_syntax::{ast, AstNode, AstPtr}; |
16 | use rustc_hash::FxHashMap; | 16 | use rustc_hash::FxHashMap; |
17 | use test_utils::mark; | ||
17 | 18 | ||
18 | pub(crate) use lower::LowerCtx; | 19 | pub(crate) use lower::LowerCtx; |
19 | 20 | ||
@@ -42,9 +43,15 @@ pub(crate) struct Expander { | |||
42 | current_file_id: HirFileId, | 43 | current_file_id: HirFileId, |
43 | ast_id_map: Arc<AstIdMap>, | 44 | ast_id_map: Arc<AstIdMap>, |
44 | module: ModuleId, | 45 | module: ModuleId, |
45 | recursive_limit: usize, | 46 | recursion_limit: usize, |
46 | } | 47 | } |
47 | 48 | ||
49 | #[cfg(test)] | ||
50 | const EXPANSION_RECURSION_LIMIT: usize = 32; | ||
51 | |||
52 | #[cfg(not(test))] | ||
53 | const EXPANSION_RECURSION_LIMIT: usize = 128; | ||
54 | |||
48 | impl CfgExpander { | 55 | impl CfgExpander { |
49 | pub(crate) fn new( | 56 | pub(crate) fn new( |
50 | db: &dyn DefDatabase, | 57 | db: &dyn DefDatabase, |
@@ -81,7 +88,7 @@ impl Expander { | |||
81 | current_file_id, | 88 | current_file_id, |
82 | ast_id_map, | 89 | ast_id_map, |
83 | module, | 90 | module, |
84 | recursive_limit: 0, | 91 | recursion_limit: 0, |
85 | } | 92 | } |
86 | } | 93 | } |
87 | 94 | ||
@@ -91,13 +98,15 @@ impl Expander { | |||
91 | local_scope: Option<&ItemScope>, | 98 | local_scope: Option<&ItemScope>, |
92 | macro_call: ast::MacroCall, | 99 | macro_call: ast::MacroCall, |
93 | ) -> Option<(Mark, T)> { | 100 | ) -> Option<(Mark, T)> { |
94 | if self.recursive_limit > 1024 { | 101 | self.recursion_limit += 1; |
102 | if self.recursion_limit > EXPANSION_RECURSION_LIMIT { | ||
103 | mark::hit!(your_stack_belongs_to_me); | ||
95 | return None; | 104 | return None; |
96 | } | 105 | } |
97 | 106 | ||
98 | let macro_call = InFile::new(self.current_file_id, ¯o_call); | 107 | let macro_call = InFile::new(self.current_file_id, ¯o_call); |
99 | 108 | ||
100 | if let Some(call_id) = macro_call.as_call_id(db, |path| { | 109 | if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| { |
101 | if let Some(local_scope) = local_scope { | 110 | if let Some(local_scope) = local_scope { |
102 | if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { | 111 | if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { |
103 | return Some(def); | 112 | return Some(def); |
@@ -118,8 +127,6 @@ impl Expander { | |||
118 | self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); | 127 | self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); |
119 | self.current_file_id = file_id; | 128 | self.current_file_id = file_id; |
120 | self.ast_id_map = db.ast_id_map(file_id); | 129 | self.ast_id_map = db.ast_id_map(file_id); |
121 | self.recursive_limit += 1; | ||
122 | |||
123 | return Some((mark, expr)); | 130 | return Some((mark, expr)); |
124 | } | 131 | } |
125 | } | 132 | } |
@@ -134,7 +141,7 @@ impl Expander { | |||
134 | self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); | 141 | self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); |
135 | self.current_file_id = mark.file_id; | 142 | self.current_file_id = mark.file_id; |
136 | self.ast_id_map = mem::take(&mut mark.ast_id_map); | 143 | self.ast_id_map = mem::take(&mut mark.ast_id_map); |
137 | self.recursive_limit -= 1; | 144 | self.recursion_limit -= 1; |
138 | mark.bomb.defuse(); | 145 | mark.bomb.defuse(); |
139 | } | 146 | } |
140 | 147 | ||
@@ -302,7 +309,53 @@ impl BodySourceMap { | |||
302 | self.pat_map.get(&src).cloned() | 309 | self.pat_map.get(&src).cloned() |
303 | } | 310 | } |
304 | 311 | ||
312 | pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId> { | ||
313 | let src = node.map(|it| Either::Right(AstPtr::new(it))); | ||
314 | self.pat_map.get(&src).cloned() | ||
315 | } | ||
316 | |||
305 | pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordField>> { | 317 | pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordField>> { |
306 | self.field_map[&(expr, field)].clone() | 318 | self.field_map[&(expr, field)].clone() |
307 | } | 319 | } |
308 | } | 320 | } |
321 | |||
322 | #[cfg(test)] | ||
323 | mod tests { | ||
324 | use ra_db::{fixture::WithFixture, SourceDatabase}; | ||
325 | use test_utils::mark; | ||
326 | |||
327 | use crate::ModuleDefId; | ||
328 | |||
329 | use super::*; | ||
330 | |||
331 | fn lower(ra_fixture: &str) -> Arc<Body> { | ||
332 | let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture); | ||
333 | |||
334 | let krate = db.crate_graph().iter().next().unwrap(); | ||
335 | let def_map = db.crate_def_map(krate); | ||
336 | let module = def_map.modules_for_file(file_id).next().unwrap(); | ||
337 | let module = &def_map[module]; | ||
338 | let fn_def = match module.scope.declarations().next().unwrap() { | ||
339 | ModuleDefId::FunctionId(it) => it, | ||
340 | _ => panic!(), | ||
341 | }; | ||
342 | |||
343 | db.body(fn_def.into()) | ||
344 | } | ||
345 | |||
346 | #[test] | ||
347 | fn your_stack_belongs_to_me() { | ||
348 | mark::check!(your_stack_belongs_to_me); | ||
349 | lower( | ||
350 | " | ||
351 | macro_rules! n_nuple { | ||
352 | ($e:tt) => (); | ||
353 | ($($rest:tt)*) => {{ | ||
354 | (n_nuple!($($rest)*)None,) | ||
355 | }}; | ||
356 | } | ||
357 | fn main() { n_nuple!(1,2,3); } | ||
358 | ", | ||
359 | ); | ||
360 | } | ||
361 | } | ||
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index f159f80af..c6bc85e2f 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs | |||
@@ -27,6 +27,7 @@ use crate::{ | |||
27 | LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, | 27 | LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, |
28 | }, | 28 | }, |
29 | item_scope::BuiltinShadowMode, | 29 | item_scope::BuiltinShadowMode, |
30 | item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, | ||
30 | path::{GenericArgs, Path}, | 31 | path::{GenericArgs, Path}, |
31 | type_ref::{Mutability, Rawness, TypeRef}, | 32 | type_ref::{Mutability, Rawness, TypeRef}, |
32 | AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, | 33 | AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, |
@@ -35,6 +36,8 @@ use crate::{ | |||
35 | 36 | ||
36 | use super::{ExprSource, PatSource}; | 37 | use super::{ExprSource, PatSource}; |
37 | use ast::AstChildren; | 38 | use ast::AstChildren; |
39 | use rustc_hash::FxHashMap; | ||
40 | use std::{any::type_name, sync::Arc}; | ||
38 | 41 | ||
39 | pub(crate) struct LowerCtx { | 42 | pub(crate) struct LowerCtx { |
40 | hygiene: Hygiene, | 43 | hygiene: Hygiene, |
@@ -60,10 +63,10 @@ pub(super) fn lower( | |||
60 | params: Option<ast::ParamList>, | 63 | params: Option<ast::ParamList>, |
61 | body: Option<ast::Expr>, | 64 | body: Option<ast::Expr>, |
62 | ) -> (Body, BodySourceMap) { | 65 | ) -> (Body, BodySourceMap) { |
66 | let item_tree = db.item_tree(expander.current_file_id); | ||
63 | ExprCollector { | 67 | ExprCollector { |
64 | db, | 68 | db, |
65 | def, | 69 | def, |
66 | expander, | ||
67 | source_map: BodySourceMap::default(), | 70 | source_map: BodySourceMap::default(), |
68 | body: Body { | 71 | body: Body { |
69 | exprs: Arena::default(), | 72 | exprs: Arena::default(), |
@@ -72,6 +75,12 @@ pub(super) fn lower( | |||
72 | body_expr: dummy_expr_id(), | 75 | body_expr: dummy_expr_id(), |
73 | item_scope: Default::default(), | 76 | item_scope: Default::default(), |
74 | }, | 77 | }, |
78 | item_trees: { | ||
79 | let mut map = FxHashMap::default(); | ||
80 | map.insert(expander.current_file_id, item_tree); | ||
81 | map | ||
82 | }, | ||
83 | expander, | ||
75 | } | 84 | } |
76 | .collect(params, body) | 85 | .collect(params, body) |
77 | } | 86 | } |
@@ -82,6 +91,8 @@ struct ExprCollector<'a> { | |||
82 | expander: Expander, | 91 | expander: Expander, |
83 | body: Body, | 92 | body: Body, |
84 | source_map: BodySourceMap, | 93 | source_map: BodySourceMap, |
94 | |||
95 | item_trees: FxHashMap<HirFileId, Arc<ItemTree>>, | ||
85 | } | 96 | } |
86 | 97 | ||
87 | impl ExprCollector<'_> { | 98 | impl ExprCollector<'_> { |
@@ -165,6 +176,7 @@ impl ExprCollector<'_> { | |||
165 | if !self.expander.is_cfg_enabled(&expr) { | 176 | if !self.expander.is_cfg_enabled(&expr) { |
166 | return self.missing_expr(); | 177 | return self.missing_expr(); |
167 | } | 178 | } |
179 | |||
168 | match expr { | 180 | match expr { |
169 | ast::Expr::IfExpr(e) => { | 181 | ast::Expr::IfExpr(e) => { |
170 | let then_branch = self.collect_block_opt(e.then_branch()); | 182 | let then_branch = self.collect_block_opt(e.then_branch()); |
@@ -207,8 +219,12 @@ impl ExprCollector<'_> { | |||
207 | let body = self.collect_block_opt(e.block_expr()); | 219 | let body = self.collect_block_opt(e.block_expr()); |
208 | self.alloc_expr(Expr::TryBlock { body }, syntax_ptr) | 220 | self.alloc_expr(Expr::TryBlock { body }, syntax_ptr) |
209 | } | 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 | } | ||
210 | // FIXME: we need to record these effects somewhere... | 226 | // FIXME: we need to record these effects somewhere... |
211 | ast::Effect::Async(_) | ast::Effect::Label(_) | ast::Effect::Unsafe(_) => { | 227 | ast::Effect::Async(_) | ast::Effect::Label(_) => { |
212 | self.collect_block_opt(e.block_expr()) | 228 | self.collect_block_opt(e.block_expr()) |
213 | } | 229 | } |
214 | }, | 230 | }, |
@@ -434,7 +450,6 @@ impl ExprCollector<'_> { | |||
434 | Mutability::from_mutable(e.mut_token().is_some()) | 450 | Mutability::from_mutable(e.mut_token().is_some()) |
435 | }; | 451 | }; |
436 | let rawness = Rawness::from_raw(raw_tok); | 452 | let rawness = Rawness::from_raw(raw_tok); |
437 | |||
438 | self.alloc_expr(Expr::Ref { expr, rawness, mutability }, syntax_ptr) | 453 | self.alloc_expr(Expr::Ref { expr, rawness, mutability }, syntax_ptr) |
439 | } | 454 | } |
440 | ast::Expr::PrefixExpr(e) => { | 455 | ast::Expr::PrefixExpr(e) => { |
@@ -533,6 +548,9 @@ impl ExprCollector<'_> { | |||
533 | self.source_map | 548 | self.source_map |
534 | .expansions | 549 | .expansions |
535 | .insert(macro_call, self.expander.current_file_id); | 550 | .insert(macro_call, self.expander.current_file_id); |
551 | |||
552 | let item_tree = self.db.item_tree(self.expander.current_file_id); | ||
553 | self.item_trees.insert(self.expander.current_file_id, item_tree); | ||
536 | let id = self.collect_expr(expansion); | 554 | let id = self.collect_expr(expansion); |
537 | self.expander.exit(self.db, mark); | 555 | self.expander.exit(self.db, mark); |
538 | id | 556 | id |
@@ -547,6 +565,32 @@ impl ExprCollector<'_> { | |||
547 | } | 565 | } |
548 | } | 566 | } |
549 | 567 | ||
568 | fn find_inner_item<N: ItemTreeNode>(&self, ast: &N::Source) -> Option<ItemTreeId<N>> { | ||
569 | let id = self.expander.ast_id(ast); | ||
570 | let tree = &self.item_trees[&id.file_id]; | ||
571 | |||
572 | // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes | ||
573 | |||
574 | // Root file (non-macro). | ||
575 | let item_tree_id = tree | ||
576 | .all_inner_items() | ||
577 | .chain(tree.top_level_items().iter().copied()) | ||
578 | .filter_map(|mod_item| mod_item.downcast::<N>()) | ||
579 | .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value.upcast()) | ||
580 | .or_else(|| { | ||
581 | log::debug!( | ||
582 | "couldn't find inner {} item for {:?} (AST: `{}` - {:?})", | ||
583 | type_name::<N>(), | ||
584 | id, | ||
585 | ast.syntax(), | ||
586 | ast.syntax(), | ||
587 | ); | ||
588 | None | ||
589 | })?; | ||
590 | |||
591 | Some(ItemTreeId::new(id.file_id, item_tree_id)) | ||
592 | } | ||
593 | |||
550 | fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { | 594 | fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { |
551 | if let Some(expr) = expr { | 595 | if let Some(expr) = expr { |
552 | self.collect_expr(expr) | 596 | self.collect_expr(expr) |
@@ -578,56 +622,65 @@ impl ExprCollector<'_> { | |||
578 | 622 | ||
579 | fn collect_block_items(&mut self, block: &ast::BlockExpr) { | 623 | fn collect_block_items(&mut self, block: &ast::BlockExpr) { |
580 | let container = ContainerId::DefWithBodyId(self.def); | 624 | let container = ContainerId::DefWithBodyId(self.def); |
581 | for item in block.items() { | 625 | |
582 | let (def, name): (ModuleDefId, Option<ast::Name>) = match item { | 626 | let items = block |
583 | ast::ModuleItem::FnDef(def) => { | 627 | .items() |
584 | let ast_id = self.expander.ast_id(&def); | 628 | .filter_map(|item| { |
585 | ( | 629 | let (def, name): (ModuleDefId, Option<ast::Name>) = match item { |
586 | FunctionLoc { container: container.into(), ast_id }.intern(self.db).into(), | 630 | ast::ModuleItem::FnDef(def) => { |
587 | def.name(), | 631 | let id = self.find_inner_item(&def)?; |
588 | ) | 632 | ( |
589 | } | 633 | FunctionLoc { container: container.into(), id }.intern(self.db).into(), |
590 | ast::ModuleItem::TypeAliasDef(def) => { | 634 | def.name(), |
591 | let ast_id = self.expander.ast_id(&def); | 635 | ) |
592 | ( | 636 | } |
593 | TypeAliasLoc { container: container.into(), ast_id }.intern(self.db).into(), | 637 | ast::ModuleItem::TypeAliasDef(def) => { |
594 | def.name(), | 638 | let id = self.find_inner_item(&def)?; |
595 | ) | 639 | ( |
596 | } | 640 | TypeAliasLoc { container: container.into(), id }.intern(self.db).into(), |
597 | ast::ModuleItem::ConstDef(def) => { | 641 | def.name(), |
598 | let ast_id = self.expander.ast_id(&def); | 642 | ) |
599 | ( | 643 | } |
600 | ConstLoc { container: container.into(), ast_id }.intern(self.db).into(), | 644 | ast::ModuleItem::ConstDef(def) => { |
601 | def.name(), | 645 | let id = self.find_inner_item(&def)?; |
602 | ) | 646 | ( |
603 | } | 647 | ConstLoc { container: container.into(), id }.intern(self.db).into(), |
604 | ast::ModuleItem::StaticDef(def) => { | 648 | def.name(), |
605 | let ast_id = self.expander.ast_id(&def); | 649 | ) |
606 | (StaticLoc { container, ast_id }.intern(self.db).into(), def.name()) | 650 | } |
607 | } | 651 | ast::ModuleItem::StaticDef(def) => { |
608 | ast::ModuleItem::StructDef(def) => { | 652 | let id = self.find_inner_item(&def)?; |
609 | let ast_id = self.expander.ast_id(&def); | 653 | (StaticLoc { container, id }.intern(self.db).into(), def.name()) |
610 | (StructLoc { container, ast_id }.intern(self.db).into(), def.name()) | 654 | } |
611 | } | 655 | ast::ModuleItem::StructDef(def) => { |
612 | ast::ModuleItem::EnumDef(def) => { | 656 | let id = self.find_inner_item(&def)?; |
613 | let ast_id = self.expander.ast_id(&def); | 657 | (StructLoc { container, id }.intern(self.db).into(), def.name()) |
614 | (EnumLoc { container, ast_id }.intern(self.db).into(), def.name()) | 658 | } |
615 | } | 659 | ast::ModuleItem::EnumDef(def) => { |
616 | ast::ModuleItem::UnionDef(def) => { | 660 | let id = self.find_inner_item(&def)?; |
617 | let ast_id = self.expander.ast_id(&def); | 661 | (EnumLoc { container, id }.intern(self.db).into(), def.name()) |
618 | (UnionLoc { container, ast_id }.intern(self.db).into(), def.name()) | 662 | } |
619 | } | 663 | ast::ModuleItem::UnionDef(def) => { |
620 | ast::ModuleItem::TraitDef(def) => { | 664 | let id = self.find_inner_item(&def)?; |
621 | let ast_id = self.expander.ast_id(&def); | 665 | (UnionLoc { container, id }.intern(self.db).into(), def.name()) |
622 | (TraitLoc { container, ast_id }.intern(self.db).into(), def.name()) | 666 | } |
623 | } | 667 | ast::ModuleItem::TraitDef(def) => { |
624 | ast::ModuleItem::ExternBlock(_) => continue, // FIXME: collect from extern blocks | 668 | let id = self.find_inner_item(&def)?; |
625 | ast::ModuleItem::ImplDef(_) | 669 | (TraitLoc { container, id }.intern(self.db).into(), def.name()) |
626 | | ast::ModuleItem::UseItem(_) | 670 | } |
627 | | ast::ModuleItem::ExternCrateItem(_) | 671 | ast::ModuleItem::ExternBlock(_) => return None, // FIXME: collect from extern blocks |
628 | | ast::ModuleItem::Module(_) | 672 | ast::ModuleItem::ImplDef(_) |
629 | | ast::ModuleItem::MacroCall(_) => continue, | 673 | | ast::ModuleItem::UseItem(_) |
630 | }; | 674 | | ast::ModuleItem::ExternCrateItem(_) |
675 | | ast::ModuleItem::Module(_) | ||
676 | | ast::ModuleItem::MacroCall(_) => return None, | ||
677 | }; | ||
678 | |||
679 | Some((def, name)) | ||
680 | }) | ||
681 | .collect::<Vec<_>>(); | ||
682 | |||
683 | for (def, name) in items { | ||
631 | self.body.item_scope.define_def(def); | 684 | self.body.item_scope.define_def(def); |
632 | if let Some(name) = name { | 685 | if let Some(name) = name { |
633 | let vis = crate::visibility::Visibility::Public; // FIXME determine correctly | 686 | let vis = crate::visibility::Visibility::Public; // FIXME determine correctly |
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs index e48ff38f9..99e876683 100644 --- a/crates/ra_hir_def/src/body/scope.rs +++ b/crates/ra_hir_def/src/body/scope.rs | |||
@@ -87,15 +87,13 @@ impl ExprScopes { | |||
87 | } | 87 | } |
88 | 88 | ||
89 | fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { | 89 | fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { |
90 | match &body[pat] { | 90 | let pattern = &body[pat]; |
91 | Pat::Bind { name, .. } => { | 91 | if let Pat::Bind { name, .. } = pattern { |
92 | // bind can have a sub pattern, but it's actually not allowed | 92 | let entry = ScopeEntry { name: name.clone(), pat }; |
93 | // to bind to things in there | 93 | self.scopes[scope].entries.push(entry); |
94 | let entry = ScopeEntry { name: name.clone(), pat }; | ||
95 | self.scopes[scope].entries.push(entry) | ||
96 | } | ||
97 | p => p.walk_child_pats(|pat| self.add_bindings(body, scope, pat)), | ||
98 | } | 94 | } |
95 | |||
96 | pattern.walk_child_pats(|pat| self.add_bindings(body, scope, pat)); | ||
99 | } | 97 | } |
100 | 98 | ||
101 | fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) { | 99 | fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) { |
@@ -190,21 +188,23 @@ mod tests { | |||
190 | } | 188 | } |
191 | } | 189 | } |
192 | 190 | ||
193 | fn do_check(code: &str, expected: &[&str]) { | 191 | fn do_check(ra_fixture: &str, expected: &[&str]) { |
194 | let (off, code) = extract_offset(code); | 192 | let (offset, code) = extract_offset(ra_fixture); |
195 | let code = { | 193 | let code = { |
196 | let mut buf = String::new(); | 194 | let mut buf = String::new(); |
197 | let off: usize = off.into(); | 195 | let off: usize = offset.into(); |
198 | buf.push_str(&code[..off]); | 196 | buf.push_str(&code[..off]); |
199 | buf.push_str("marker"); | 197 | buf.push_str("<|>marker"); |
200 | buf.push_str(&code[off..]); | 198 | buf.push_str(&code[off..]); |
201 | buf | 199 | buf |
202 | }; | 200 | }; |
203 | 201 | ||
204 | let (db, file_id) = TestDB::with_single_file(&code); | 202 | let (db, position) = TestDB::with_position(&code); |
203 | let file_id = position.file_id; | ||
204 | let offset = position.offset; | ||
205 | 205 | ||
206 | let file_syntax = db.parse(file_id).syntax_node(); | 206 | let file_syntax = db.parse(file_id).syntax_node(); |
207 | let marker: ast::PathExpr = find_node_at_offset(&file_syntax, off).unwrap(); | 207 | let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap(); |
208 | let function = find_function(&db, file_id); | 208 | let function = find_function(&db, file_id); |
209 | 209 | ||
210 | let scopes = db.expr_scopes(function.into()); | 210 | let scopes = db.expr_scopes(function.into()); |
@@ -300,15 +300,65 @@ mod tests { | |||
300 | ); | 300 | ); |
301 | } | 301 | } |
302 | 302 | ||
303 | fn do_check_local_name(code: &str, expected_offset: u32) { | 303 | #[test] |
304 | let (off, code) = extract_offset(code); | 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 | } | ||
305 | 352 | ||
306 | let (db, file_id) = TestDB::with_single_file(&code); | 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; | ||
307 | 357 | ||
308 | let file = db.parse(file_id).ok().unwrap(); | 358 | let file = db.parse(file_id).ok().unwrap(); |
309 | let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()) | 359 | let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()) |
310 | .expect("failed to find a name at the target offset"); | 360 | .expect("failed to find a name at the target offset"); |
311 | let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); | 361 | let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset).unwrap(); |
312 | 362 | ||
313 | let function = find_function(&db, file_id); | 363 | let function = find_function(&db, file_id); |
314 | 364 | ||
@@ -336,15 +386,16 @@ mod tests { | |||
336 | fn test_resolve_local_name() { | 386 | fn test_resolve_local_name() { |
337 | do_check_local_name( | 387 | do_check_local_name( |
338 | r#" | 388 | r#" |
339 | fn foo(x: i32, y: u32) { | 389 | fn foo(x: i32, y: u32) { |
340 | { | 390 | { |
341 | let z = x * 2; | 391 | let z = x * 2; |
342 | } | 392 | } |
343 | { | 393 | { |
344 | let t = x<|> * 3; | 394 | let t = x<|> * 3; |
345 | } | 395 | } |
346 | }"#, | 396 | } |
347 | 21, | 397 | "#, |
398 | 7, | ||
348 | ); | 399 | ); |
349 | } | 400 | } |
350 | 401 | ||
@@ -352,10 +403,11 @@ mod tests { | |||
352 | fn test_resolve_local_name_declaration() { | 403 | fn test_resolve_local_name_declaration() { |
353 | do_check_local_name( | 404 | do_check_local_name( |
354 | r#" | 405 | r#" |
355 | fn foo(x: String) { | 406 | fn foo(x: String) { |
356 | let x : &str = &x<|>; | 407 | let x : &str = &x<|>; |
357 | }"#, | 408 | } |
358 | 21, | 409 | "#, |
410 | 7, | ||
359 | ); | 411 | ); |
360 | } | 412 | } |
361 | 413 | ||
@@ -363,12 +415,12 @@ mod tests { | |||
363 | fn test_resolve_local_name_shadow() { | 415 | fn test_resolve_local_name_shadow() { |
364 | do_check_local_name( | 416 | do_check_local_name( |
365 | r" | 417 | r" |
366 | fn foo(x: String) { | 418 | fn foo(x: String) { |
367 | let x : &str = &x; | 419 | let x : &str = &x; |
368 | x<|> | 420 | x<|> |
369 | } | 421 | } |
370 | ", | 422 | ", |
371 | 53, | 423 | 28, |
372 | ); | 424 | ); |
373 | } | 425 | } |
374 | 426 | ||
@@ -376,13 +428,13 @@ mod tests { | |||
376 | fn ref_patterns_contribute_bindings() { | 428 | fn ref_patterns_contribute_bindings() { |
377 | do_check_local_name( | 429 | do_check_local_name( |
378 | r" | 430 | r" |
379 | fn foo() { | 431 | fn foo() { |
380 | if let Some(&from) = bar() { | 432 | if let Some(&from) = bar() { |
381 | from<|>; | 433 | from<|>; |
382 | } | 434 | } |
383 | } | 435 | } |
384 | ", | 436 | ", |
385 | 53, | 437 | 28, |
386 | ); | 438 | ); |
387 | } | 439 | } |
388 | 440 | ||
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index e2130d931..88a8ef9bf 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs | |||
@@ -2,27 +2,19 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_expand::{ | 5 | use hir_expand::{name::Name, InFile}; |
6 | hygiene::Hygiene, | ||
7 | name::{name, AsName, Name}, | ||
8 | AstId, InFile, | ||
9 | }; | ||
10 | use ra_prof::profile; | 6 | use ra_prof::profile; |
11 | use ra_syntax::ast::{ | 7 | use ra_syntax::ast; |
12 | self, AssocItem, AstNode, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner, | ||
13 | VisibilityOwner, | ||
14 | }; | ||
15 | 8 | ||
16 | use crate::{ | 9 | use crate::{ |
17 | attr::Attrs, | 10 | attr::Attrs, |
18 | body::LowerCtx, | 11 | body::Expander, |
19 | db::DefDatabase, | 12 | db::DefDatabase, |
20 | path::{path, AssociatedTypeBinding, GenericArgs, Path}, | 13 | item_tree::{AssocItem, ItemTreeId, ModItem}, |
21 | src::HasSource, | 14 | type_ref::{TypeBound, TypeRef}, |
22 | type_ref::{Mutability, TypeBound, TypeRef}, | ||
23 | visibility::RawVisibility, | 15 | visibility::RawVisibility, |
24 | AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule, | 16 | AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, |
25 | ImplId, Intern, Lookup, StaticId, TraitId, TypeAliasId, TypeAliasLoc, | 17 | Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc, |
26 | }; | 18 | }; |
27 | 19 | ||
28 | #[derive(Debug, Clone, PartialEq, Eq)] | 20 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -34,85 +26,36 @@ pub struct FunctionData { | |||
34 | /// True if the first param is `self`. This is relevant to decide whether this | 26 | /// True if the first param is `self`. This is relevant to decide whether this |
35 | /// can be called as a method. | 27 | /// can be called as a method. |
36 | pub has_self_param: bool, | 28 | pub has_self_param: bool, |
29 | pub is_unsafe: bool, | ||
30 | pub is_varargs: bool, | ||
37 | pub visibility: RawVisibility, | 31 | pub visibility: RawVisibility, |
38 | } | 32 | } |
39 | 33 | ||
40 | impl FunctionData { | 34 | impl FunctionData { |
41 | pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> { | 35 | pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> { |
42 | let loc = func.lookup(db); | 36 | let loc = func.lookup(db); |
43 | let src = loc.source(db); | 37 | let item_tree = db.item_tree(loc.id.file_id); |
44 | let ctx = LowerCtx::new(db, src.file_id); | 38 | let func = &item_tree[loc.id.value]; |
45 | let name = src.value.name().map(|n| n.as_name()).unwrap_or_else(Name::missing); | 39 | |
46 | let mut params = Vec::new(); | 40 | Arc::new(FunctionData { |
47 | let mut has_self_param = false; | 41 | name: func.name.clone(), |
48 | if let Some(param_list) = src.value.param_list() { | 42 | params: func.params.to_vec(), |
49 | if let Some(self_param) = param_list.self_param() { | 43 | ret_type: func.ret_type.clone(), |
50 | let self_type = if let Some(type_ref) = self_param.ascribed_type() { | 44 | attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(), |
51 | TypeRef::from_ast(&ctx, type_ref) | 45 | has_self_param: func.has_self_param, |
52 | } else { | 46 | is_unsafe: func.is_unsafe, |
53 | let self_type = TypeRef::Path(name![Self].into()); | 47 | is_varargs: func.is_varargs, |
54 | match self_param.kind() { | 48 | visibility: item_tree[func.visibility].clone(), |
55 | ast::SelfParamKind::Owned => self_type, | 49 | }) |
56 | ast::SelfParamKind::Ref => { | ||
57 | TypeRef::Reference(Box::new(self_type), Mutability::Shared) | ||
58 | } | ||
59 | ast::SelfParamKind::MutRef => { | ||
60 | TypeRef::Reference(Box::new(self_type), Mutability::Mut) | ||
61 | } | ||
62 | } | ||
63 | }; | ||
64 | params.push(self_type); | ||
65 | has_self_param = true; | ||
66 | } | ||
67 | for param in param_list.params() { | ||
68 | let type_ref = TypeRef::from_ast_opt(&ctx, param.ascribed_type()); | ||
69 | params.push(type_ref); | ||
70 | } | ||
71 | } | ||
72 | let attrs = Attrs::new(&src.value, &Hygiene::new(db.upcast(), src.file_id)); | ||
73 | |||
74 | let ret_type = if let Some(type_ref) = src.value.ret_type().and_then(|rt| rt.type_ref()) { | ||
75 | TypeRef::from_ast(&ctx, type_ref) | ||
76 | } else { | ||
77 | TypeRef::unit() | ||
78 | }; | ||
79 | |||
80 | let ret_type = if src.value.async_token().is_some() { | ||
81 | let future_impl = desugar_future_path(ret_type); | ||
82 | let ty_bound = TypeBound::Path(future_impl); | ||
83 | TypeRef::ImplTrait(vec![ty_bound]) | ||
84 | } else { | ||
85 | ret_type | ||
86 | }; | ||
87 | |||
88 | let vis_default = RawVisibility::default_for_container(loc.container); | ||
89 | let visibility = | ||
90 | RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility())); | ||
91 | |||
92 | let sig = FunctionData { name, params, ret_type, has_self_param, visibility, attrs }; | ||
93 | Arc::new(sig) | ||
94 | } | 50 | } |
95 | } | 51 | } |
96 | 52 | ||
97 | fn desugar_future_path(orig: TypeRef) -> Path { | ||
98 | let path = path![std::future::Future]; | ||
99 | let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); | ||
100 | let mut last = GenericArgs::empty(); | ||
101 | last.bindings.push(AssociatedTypeBinding { | ||
102 | name: name![Output], | ||
103 | type_ref: Some(orig), | ||
104 | bounds: Vec::new(), | ||
105 | }); | ||
106 | generic_args.push(Some(Arc::new(last))); | ||
107 | |||
108 | Path::from_known_path(path, generic_args) | ||
109 | } | ||
110 | |||
111 | #[derive(Debug, Clone, PartialEq, Eq)] | 53 | #[derive(Debug, Clone, PartialEq, Eq)] |
112 | pub struct TypeAliasData { | 54 | pub struct TypeAliasData { |
113 | pub name: Name, | 55 | pub name: Name, |
114 | pub type_ref: Option<TypeRef>, | 56 | pub type_ref: Option<TypeRef>, |
115 | pub visibility: RawVisibility, | 57 | pub visibility: RawVisibility, |
58 | /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl). | ||
116 | pub bounds: Vec<TypeBound>, | 59 | pub bounds: Vec<TypeBound>, |
117 | } | 60 | } |
118 | 61 | ||
@@ -122,22 +65,15 @@ impl TypeAliasData { | |||
122 | typ: TypeAliasId, | 65 | typ: TypeAliasId, |
123 | ) -> Arc<TypeAliasData> { | 66 | ) -> Arc<TypeAliasData> { |
124 | let loc = typ.lookup(db); | 67 | let loc = typ.lookup(db); |
125 | let node = loc.source(db); | 68 | let item_tree = db.item_tree(loc.id.file_id); |
126 | let name = node.value.name().map_or_else(Name::missing, |n| n.as_name()); | 69 | let typ = &item_tree[loc.id.value]; |
127 | let lower_ctx = LowerCtx::new(db, node.file_id); | 70 | |
128 | let type_ref = node.value.type_ref().map(|it| TypeRef::from_ast(&lower_ctx, it)); | 71 | Arc::new(TypeAliasData { |
129 | let vis_default = RawVisibility::default_for_container(loc.container); | 72 | name: typ.name.clone(), |
130 | let visibility = RawVisibility::from_ast_with_default( | 73 | type_ref: typ.type_ref.clone(), |
131 | db, | 74 | visibility: item_tree[typ.visibility].clone(), |
132 | vis_default, | 75 | bounds: typ.bounds.to_vec(), |
133 | node.as_ref().map(|n| n.visibility()), | 76 | }) |
134 | ); | ||
135 | let bounds = if let Some(bound_list) = node.value.type_bound_list() { | ||
136 | bound_list.bounds().map(|it| TypeBound::from_ast(&lower_ctx, it)).collect() | ||
137 | } else { | ||
138 | Vec::new() | ||
139 | }; | ||
140 | Arc::new(TypeAliasData { name, type_ref, visibility, bounds }) | ||
141 | } | 77 | } |
142 | } | 78 | } |
143 | 79 | ||
@@ -151,30 +87,24 @@ pub struct TraitData { | |||
151 | impl TraitData { | 87 | impl TraitData { |
152 | pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> { | 88 | pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> { |
153 | let tr_loc = tr.lookup(db); | 89 | let tr_loc = tr.lookup(db); |
154 | let src = tr_loc.source(db); | 90 | let item_tree = db.item_tree(tr_loc.id.file_id); |
155 | let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); | 91 | let tr_def = &item_tree[tr_loc.id.value]; |
156 | let auto = src.value.auto_token().is_some(); | 92 | let name = tr_def.name.clone(); |
93 | let auto = tr_def.auto; | ||
157 | let module_id = tr_loc.container.module(db); | 94 | let module_id = tr_loc.container.module(db); |
158 | |||
159 | let container = AssocContainerId::TraitId(tr); | 95 | let container = AssocContainerId::TraitId(tr); |
160 | let mut items = Vec::new(); | 96 | let mut expander = Expander::new(db, tr_loc.id.file_id, module_id); |
161 | 97 | ||
162 | if let Some(item_list) = src.value.item_list() { | 98 | let items = collect_items( |
163 | let mut expander = Expander::new(db, tr_loc.ast_id.file_id, module_id); | 99 | db, |
164 | items.extend(collect_items( | 100 | module_id, |
165 | db, | 101 | &mut expander, |
166 | &mut expander, | 102 | tr_def.items.iter().copied(), |
167 | item_list.assoc_items(), | 103 | tr_loc.id.file_id, |
168 | src.file_id, | 104 | container, |
169 | container, | 105 | 100, |
170 | )); | 106 | ); |
171 | items.extend(collect_items_in_macros( | 107 | |
172 | db, | ||
173 | &mut expander, | ||
174 | &src.with_value(item_list), | ||
175 | container, | ||
176 | )); | ||
177 | } | ||
178 | Arc::new(TraitData { name, items, auto }) | 108 | Arc::new(TraitData { name, items, auto }) |
179 | } | 109 | } |
180 | 110 | ||
@@ -205,33 +135,28 @@ impl ImplData { | |||
205 | pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> { | 135 | pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> { |
206 | let _p = profile("impl_data_query"); | 136 | let _p = profile("impl_data_query"); |
207 | let impl_loc = id.lookup(db); | 137 | let impl_loc = id.lookup(db); |
208 | let src = impl_loc.source(db); | ||
209 | let lower_ctx = LowerCtx::new(db, src.file_id); | ||
210 | 138 | ||
211 | let target_trait = src.value.target_trait().map(|it| TypeRef::from_ast(&lower_ctx, it)); | 139 | let item_tree = db.item_tree(impl_loc.id.file_id); |
212 | let target_type = TypeRef::from_ast_opt(&lower_ctx, src.value.target_type()); | 140 | let impl_def = &item_tree[impl_loc.id.value]; |
213 | let is_negative = src.value.excl_token().is_some(); | 141 | let target_trait = impl_def.target_trait.clone(); |
142 | let target_type = impl_def.target_type.clone(); | ||
143 | let is_negative = impl_def.is_negative; | ||
214 | let module_id = impl_loc.container.module(db); | 144 | let module_id = impl_loc.container.module(db); |
215 | let container = AssocContainerId::ImplId(id); | 145 | let container = AssocContainerId::ImplId(id); |
146 | let mut expander = Expander::new(db, impl_loc.id.file_id, module_id); | ||
216 | 147 | ||
217 | let mut items: Vec<AssocItemId> = Vec::new(); | 148 | let items = collect_items( |
218 | 149 | db, | |
219 | if let Some(item_list) = src.value.item_list() { | 150 | module_id, |
220 | let mut expander = Expander::new(db, impl_loc.ast_id.file_id, module_id); | 151 | &mut expander, |
221 | items.extend( | 152 | impl_def.items.iter().copied(), |
222 | collect_items(db, &mut expander, item_list.assoc_items(), src.file_id, container) | 153 | impl_loc.id.file_id, |
223 | .into_iter() | 154 | container, |
224 | .map(|(_, item)| item), | 155 | 100, |
225 | ); | 156 | ); |
226 | items.extend( | 157 | let items = items.into_iter().map(|(_, item)| item).collect(); |
227 | collect_items_in_macros(db, &mut expander, &src.with_value(item_list), container) | ||
228 | .into_iter() | ||
229 | .map(|(_, item)| item), | ||
230 | ); | ||
231 | } | ||
232 | 158 | ||
233 | let res = ImplData { target_trait, target_type, items, is_negative }; | 159 | Arc::new(ImplData { target_trait, target_type, items, is_negative }) |
234 | Arc::new(res) | ||
235 | } | 160 | } |
236 | } | 161 | } |
237 | 162 | ||
@@ -246,22 +171,14 @@ pub struct ConstData { | |||
246 | impl ConstData { | 171 | impl ConstData { |
247 | pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> { | 172 | pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> { |
248 | let loc = konst.lookup(db); | 173 | let loc = konst.lookup(db); |
249 | let node = loc.source(db); | 174 | let item_tree = db.item_tree(loc.id.file_id); |
250 | let vis_default = RawVisibility::default_for_container(loc.container); | 175 | let konst = &item_tree[loc.id.value]; |
251 | Arc::new(ConstData::new(db, vis_default, node)) | ||
252 | } | ||
253 | 176 | ||
254 | fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>( | 177 | Arc::new(ConstData { |
255 | db: &dyn DefDatabase, | 178 | name: konst.name.clone(), |
256 | vis_default: RawVisibility, | 179 | type_ref: konst.type_ref.clone(), |
257 | node: InFile<N>, | 180 | visibility: item_tree[konst.visibility].clone(), |
258 | ) -> ConstData { | 181 | }) |
259 | let ctx = LowerCtx::new(db, node.file_id); | ||
260 | let name = node.value.name().map(|n| n.as_name()); | ||
261 | let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type()); | ||
262 | let visibility = | ||
263 | RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility())); | ||
264 | ConstData { name, type_ref, visibility } | ||
265 | } | 182 | } |
266 | } | 183 | } |
267 | 184 | ||
@@ -275,44 +192,25 @@ pub struct StaticData { | |||
275 | 192 | ||
276 | impl StaticData { | 193 | impl StaticData { |
277 | pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> { | 194 | pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> { |
278 | let node = konst.lookup(db).source(db); | 195 | let node = konst.lookup(db); |
279 | let ctx = LowerCtx::new(db, node.file_id); | 196 | let item_tree = db.item_tree(node.id.file_id); |
280 | 197 | let statik = &item_tree[node.id.value]; | |
281 | let name = node.value.name().map(|n| n.as_name()); | 198 | |
282 | let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type()); | 199 | Arc::new(StaticData { |
283 | let mutable = node.value.mut_token().is_some(); | 200 | name: Some(statik.name.clone()), |
284 | let visibility = RawVisibility::from_ast_with_default( | 201 | type_ref: statik.type_ref.clone(), |
285 | db, | 202 | visibility: item_tree[statik.visibility].clone(), |
286 | RawVisibility::private(), | 203 | mutable: statik.mutable, |
287 | node.map(|n| n.visibility()), | 204 | }) |
288 | ); | ||
289 | |||
290 | Arc::new(StaticData { name, type_ref, visibility, mutable }) | ||
291 | } | ||
292 | } | ||
293 | |||
294 | fn collect_items_in_macros( | ||
295 | db: &dyn DefDatabase, | ||
296 | expander: &mut Expander, | ||
297 | impl_def: &InFile<ast::ItemList>, | ||
298 | container: AssocContainerId, | ||
299 | ) -> Vec<(Name, AssocItemId)> { | ||
300 | let mut res = Vec::new(); | ||
301 | |||
302 | // We set a limit to protect against infinite recursion | ||
303 | let limit = 100; | ||
304 | |||
305 | for m in impl_def.value.syntax().children().filter_map(ast::MacroCall::cast) { | ||
306 | res.extend(collect_items_in_macro(db, expander, m, container, limit)) | ||
307 | } | 205 | } |
308 | |||
309 | res | ||
310 | } | 206 | } |
311 | 207 | ||
312 | fn collect_items_in_macro( | 208 | fn collect_items( |
313 | db: &dyn DefDatabase, | 209 | db: &dyn DefDatabase, |
210 | module: ModuleId, | ||
314 | expander: &mut Expander, | 211 | expander: &mut Expander, |
315 | m: ast::MacroCall, | 212 | assoc_items: impl Iterator<Item = AssocItem>, |
213 | file_id: crate::HirFileId, | ||
316 | container: AssocContainerId, | 214 | container: AssocContainerId, |
317 | limit: usize, | 215 | limit: usize, |
318 | ) -> Vec<(Name, AssocItemId)> { | 216 | ) -> Vec<(Name, AssocItemId)> { |
@@ -320,62 +218,62 @@ fn collect_items_in_macro( | |||
320 | return Vec::new(); | 218 | return Vec::new(); |
321 | } | 219 | } |
322 | 220 | ||
323 | if let Some((mark, items)) = expander.enter_expand(db, None, m) { | 221 | let item_tree = db.item_tree(file_id); |
324 | let items: InFile<ast::MacroItems> = expander.to_source(items); | 222 | let cfg_options = db.crate_graph()[module.krate].cfg_options.clone(); |
325 | let mut res = collect_items( | 223 | |
326 | db, | 224 | let mut items = Vec::new(); |
327 | expander, | 225 | for item in assoc_items { |
328 | items.value.items().filter_map(|it| AssocItem::cast(it.syntax().clone())), | 226 | match item { |
329 | items.file_id, | 227 | AssocItem::Function(id) => { |
330 | container, | 228 | let item = &item_tree[id]; |
331 | ); | 229 | let attrs = item_tree.attrs(ModItem::from(id).into()); |
332 | 230 | if !attrs.is_cfg_enabled(&cfg_options) { | |
333 | // Recursive collect macros | 231 | continue; |
334 | // Note that ast::ModuleItem do not include ast::MacroCall | ||
335 | // We cannot use ModuleItemOwner::items here | ||
336 | for it in items.value.syntax().children().filter_map(ast::MacroCall::cast) { | ||
337 | res.extend(collect_items_in_macro(db, expander, it, container, limit - 1)) | ||
338 | } | ||
339 | expander.exit(db, mark); | ||
340 | res | ||
341 | } else { | ||
342 | Vec::new() | ||
343 | } | ||
344 | } | ||
345 | |||
346 | fn collect_items( | ||
347 | db: &dyn DefDatabase, | ||
348 | expander: &mut Expander, | ||
349 | assoc_items: impl Iterator<Item = AssocItem>, | ||
350 | file_id: crate::HirFileId, | ||
351 | container: AssocContainerId, | ||
352 | ) -> Vec<(Name, AssocItemId)> { | ||
353 | let items = db.ast_id_map(file_id); | ||
354 | |||
355 | assoc_items | ||
356 | .filter_map(|item_node| match item_node { | ||
357 | ast::AssocItem::FnDef(it) => { | ||
358 | let name = it.name().map_or_else(Name::missing, |it| it.as_name()); | ||
359 | if !expander.is_cfg_enabled(&it) { | ||
360 | return None; | ||
361 | } | 232 | } |
362 | let def = FunctionLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } | 233 | let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db); |
363 | .intern(db); | 234 | items.push((item.name.clone(), def.into())); |
364 | Some((name, def.into())) | ||
365 | } | 235 | } |
366 | ast::AssocItem::ConstDef(it) => { | 236 | // FIXME: cfg? |
367 | let name = it.name().map_or_else(Name::missing, |it| it.as_name()); | 237 | AssocItem::Const(id) => { |
368 | let def = ConstLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } | 238 | let item = &item_tree[id]; |
369 | .intern(db); | 239 | let name = match item.name.clone() { |
370 | Some((name, def.into())) | 240 | Some(name) => name, |
241 | None => continue, | ||
242 | }; | ||
243 | let def = ConstLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db); | ||
244 | items.push((name, def.into())); | ||
371 | } | 245 | } |
372 | ast::AssocItem::TypeAliasDef(it) => { | 246 | AssocItem::TypeAlias(id) => { |
373 | let name = it.name().map_or_else(Name::missing, |it| it.as_name()); | 247 | let item = &item_tree[id]; |
374 | let def = | 248 | let def = TypeAliasLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db); |
375 | TypeAliasLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } | 249 | items.push((item.name.clone(), def.into())); |
376 | .intern(db); | ||
377 | Some((name, def.into())) | ||
378 | } | 250 | } |
379 | }) | 251 | AssocItem::MacroCall(call) => { |
380 | .collect() | 252 | let call = &item_tree[call]; |
253 | let ast_id_map = db.ast_id_map(file_id); | ||
254 | let root = db.parse_or_expand(file_id).unwrap(); | ||
255 | let call = ast_id_map.get(call.ast_id).to_node(&root); | ||
256 | |||
257 | if let Some((mark, mac)) = expander.enter_expand(db, None, call) { | ||
258 | let src: InFile<ast::MacroItems> = expander.to_source(mac); | ||
259 | let item_tree = db.item_tree(src.file_id); | ||
260 | let iter = | ||
261 | item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item); | ||
262 | items.extend(collect_items( | ||
263 | db, | ||
264 | module, | ||
265 | expander, | ||
266 | iter, | ||
267 | src.file_id, | ||
268 | container, | ||
269 | limit - 1, | ||
270 | )); | ||
271 | |||
272 | expander.exit(db, mark); | ||
273 | } | ||
274 | } | ||
275 | } | ||
276 | } | ||
277 | |||
278 | items | ||
381 | } | 279 | } |
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index 945a0025e..9c3ede2d7 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Defines database & queries for name resolution. | 1 | //! Defines database & queries for name resolution. |
2 | use std::sync::Arc; | 2 | use std::sync::Arc; |
3 | 3 | ||
4 | use hir_expand::{db::AstDatabase, name::Name, HirFileId}; | 4 | use hir_expand::{db::AstDatabase, HirFileId}; |
5 | use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; | 5 | use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; |
6 | use ra_prof::profile; | 6 | use ra_prof::profile; |
7 | use ra_syntax::SmolStr; | 7 | use ra_syntax::SmolStr; |
@@ -12,13 +12,11 @@ use crate::{ | |||
12 | body::{scope::ExprScopes, Body, BodySourceMap}, | 12 | body::{scope::ExprScopes, Body, BodySourceMap}, |
13 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, | 13 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, |
14 | docs::Documentation, | 14 | docs::Documentation, |
15 | find_path, | ||
16 | generics::GenericParams, | 15 | generics::GenericParams, |
17 | item_scope::ItemInNs, | 16 | import_map::ImportMap, |
17 | item_tree::ItemTree, | ||
18 | lang_item::{LangItemTarget, LangItems}, | 18 | lang_item::{LangItemTarget, LangItems}, |
19 | nameres::{raw::RawItems, CrateDefMap}, | 19 | nameres::CrateDefMap, |
20 | path::ModPath, | ||
21 | visibility::Visibility, | ||
22 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, | 20 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, |
23 | GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, | 21 | GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, |
24 | TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, | 22 | TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, |
@@ -48,8 +46,8 @@ pub trait InternDatabase: SourceDatabase { | |||
48 | 46 | ||
49 | #[salsa::query_group(DefDatabaseStorage)] | 47 | #[salsa::query_group(DefDatabaseStorage)] |
50 | pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | 48 | pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { |
51 | #[salsa::invoke(RawItems::raw_items_query)] | 49 | #[salsa::invoke(ItemTree::item_tree_query)] |
52 | fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>; | 50 | fn item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; |
53 | 51 | ||
54 | #[salsa::invoke(crate_def_map_wait)] | 52 | #[salsa::invoke(crate_def_map_wait)] |
55 | #[salsa::transparent] | 53 | #[salsa::transparent] |
@@ -113,15 +111,8 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
113 | #[salsa::invoke(Documentation::documentation_query)] | 111 | #[salsa::invoke(Documentation::documentation_query)] |
114 | fn documentation(&self, def: AttrDefId) -> Option<Documentation>; | 112 | fn documentation(&self, def: AttrDefId) -> Option<Documentation>; |
115 | 113 | ||
116 | #[salsa::invoke(find_path::importable_locations_of_query)] | 114 | #[salsa::invoke(ImportMap::import_map_query)] |
117 | fn importable_locations_of( | 115 | fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; |
118 | &self, | ||
119 | item: ItemInNs, | ||
120 | krate: CrateId, | ||
121 | ) -> Arc<[(ModuleId, Name, Visibility)]>; | ||
122 | |||
123 | #[salsa::invoke(find_path::find_path_inner_query)] | ||
124 | fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>; | ||
125 | } | 116 | } |
126 | 117 | ||
127 | fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { | 118 | fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { |
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs index 510c5e064..30db48f86 100644 --- a/crates/ra_hir_def/src/diagnostics.rs +++ b/crates/ra_hir_def/src/diagnostics.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | use std::any::Any; | 3 | use std::any::Any; |
4 | 4 | ||
5 | use hir_expand::diagnostics::Diagnostic; | 5 | use hir_expand::diagnostics::Diagnostic; |
6 | use ra_db::RelativePathBuf; | ||
7 | use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; | 6 | use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; |
8 | 7 | ||
9 | use hir_expand::{HirFileId, InFile}; | 8 | use hir_expand::{HirFileId, InFile}; |
@@ -12,7 +11,7 @@ use hir_expand::{HirFileId, InFile}; | |||
12 | pub struct UnresolvedModule { | 11 | pub struct UnresolvedModule { |
13 | pub file: HirFileId, | 12 | pub file: HirFileId, |
14 | pub decl: AstPtr<ast::Module>, | 13 | pub decl: AstPtr<ast::Module>, |
15 | pub candidate: RelativePathBuf, | 14 | pub candidate: String, |
16 | } | 15 | } |
17 | 16 | ||
18 | impl Diagnostic for UnresolvedModule { | 17 | impl Diagnostic for UnresolvedModule { |
diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs index b221ae1ce..2630b3d89 100644 --- a/crates/ra_hir_def/src/docs.rs +++ b/crates/ra_hir_def/src/docs.rs | |||
@@ -29,6 +29,13 @@ impl Documentation { | |||
29 | Documentation(s.into()) | 29 | Documentation(s.into()) |
30 | } | 30 | } |
31 | 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 | |||
32 | pub fn as_str(&self) -> &str { | 39 | pub fn as_str(&self) -> &str { |
33 | &*self.0 | 40 | &*self.0 |
34 | } | 41 | } |
@@ -70,6 +77,45 @@ impl Documentation { | |||
70 | } | 77 | } |
71 | } | 78 | } |
72 | 79 | ||
73 | pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> { | 80 | pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation> |
74 | node.doc_comment_text().map(|it| Documentation::new(&it)) | 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 | } | ||
75 | } | 121 | } |
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs index ca49b26d1..e41cfc16b 100644 --- a/crates/ra_hir_def/src/expr.rs +++ b/crates/ra_hir_def/src/expr.rs | |||
@@ -150,6 +150,9 @@ pub enum Expr { | |||
150 | Tuple { | 150 | Tuple { |
151 | exprs: Vec<ExprId>, | 151 | exprs: Vec<ExprId>, |
152 | }, | 152 | }, |
153 | Unsafe { | ||
154 | body: ExprId, | ||
155 | }, | ||
153 | Array(Array), | 156 | Array(Array), |
154 | Literal(Literal), | 157 | Literal(Literal), |
155 | } | 158 | } |
@@ -247,7 +250,7 @@ impl Expr { | |||
247 | f(*expr); | 250 | f(*expr); |
248 | } | 251 | } |
249 | } | 252 | } |
250 | Expr::TryBlock { body } => f(*body), | 253 | Expr::TryBlock { body } | Expr::Unsafe { body } => f(*body), |
251 | Expr::Loop { body, .. } => f(*body), | 254 | Expr::Loop { body, .. } => f(*body), |
252 | Expr::While { condition, body, .. } => { | 255 | Expr::While { condition, body, .. } => { |
253 | f(*condition); | 256 | f(*condition); |
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 4db798473..06701a830 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs | |||
@@ -1,9 +1,8 @@ | |||
1 | //! An algorithm to find a path to refer to a certain item. | 1 | //! An algorithm to find a path to refer to a certain item. |
2 | 2 | ||
3 | use std::sync::Arc; | ||
4 | |||
5 | use hir_expand::name::{known, AsName, Name}; | 3 | use hir_expand::name::{known, AsName, Name}; |
6 | use ra_prof::profile; | 4 | use ra_prof::profile; |
5 | use rustc_hash::FxHashSet; | ||
7 | use test_utils::mark; | 6 | use test_utils::mark; |
8 | 7 | ||
9 | use crate::{ | 8 | use crate::{ |
@@ -11,7 +10,7 @@ use crate::{ | |||
11 | item_scope::ItemInNs, | 10 | item_scope::ItemInNs, |
12 | path::{ModPath, PathKind}, | 11 | path::{ModPath, PathKind}, |
13 | visibility::Visibility, | 12 | visibility::Visibility, |
14 | CrateId, ModuleDefId, ModuleId, | 13 | ModuleDefId, ModuleId, |
15 | }; | 14 | }; |
16 | 15 | ||
17 | // FIXME: handle local items | 16 | // FIXME: handle local items |
@@ -20,7 +19,7 @@ use crate::{ | |||
20 | /// *from where* you're referring to the item, hence the `from` parameter. | 19 | /// *from where* you're referring to the item, hence the `from` parameter. |
21 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | 20 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { |
22 | let _p = profile("find_path"); | 21 | let _p = profile("find_path"); |
23 | db.find_path_inner(item, from, MAX_PATH_LEN) | 22 | find_path_inner(db, item, from, MAX_PATH_LEN) |
24 | } | 23 | } |
25 | 24 | ||
26 | const MAX_PATH_LEN: usize = 15; | 25 | const MAX_PATH_LEN: usize = 15; |
@@ -36,20 +35,9 @@ impl ModPath { | |||
36 | let first_segment = self.segments.first(); | 35 | let first_segment = self.segments.first(); |
37 | first_segment == Some(&known::alloc) || first_segment == Some(&known::core) | 36 | first_segment == Some(&known::alloc) || first_segment == Some(&known::core) |
38 | } | 37 | } |
39 | |||
40 | fn len(&self) -> usize { | ||
41 | self.segments.len() | ||
42 | + match self.kind { | ||
43 | PathKind::Plain => 0, | ||
44 | PathKind::Super(i) => i as usize, | ||
45 | PathKind::Crate => 1, | ||
46 | PathKind::Abs => 0, | ||
47 | PathKind::DollarCrate(_) => 1, | ||
48 | } | ||
49 | } | ||
50 | } | 38 | } |
51 | 39 | ||
52 | pub(crate) fn find_path_inner_query( | 40 | fn find_path_inner( |
53 | db: &dyn DefDatabase, | 41 | db: &dyn DefDatabase, |
54 | item: ItemInNs, | 42 | item: ItemInNs, |
55 | from: ModuleId, | 43 | from: ModuleId, |
@@ -133,31 +121,67 @@ pub(crate) fn find_path_inner_query( | |||
133 | } | 121 | } |
134 | 122 | ||
135 | // - otherwise, look for modules containing (reexporting) it and import it from one of those | 123 | // - otherwise, look for modules containing (reexporting) it and import it from one of those |
124 | |||
136 | let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; | 125 | let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; |
137 | let crate_attrs = db.attrs(crate_root.into()); | 126 | let crate_attrs = db.attrs(crate_root.into()); |
138 | let prefer_no_std = crate_attrs.by_key("no_std").exists(); | 127 | let prefer_no_std = crate_attrs.by_key("no_std").exists(); |
139 | let importable_locations = find_importable_locations(db, item, from); | ||
140 | let mut best_path = None; | 128 | let mut best_path = None; |
141 | let mut best_path_len = max_len; | 129 | let mut best_path_len = max_len; |
142 | for (module_id, name) in importable_locations { | ||
143 | let mut path = match db.find_path_inner( | ||
144 | ItemInNs::Types(ModuleDefId::ModuleId(module_id)), | ||
145 | from, | ||
146 | best_path_len - 1, | ||
147 | ) { | ||
148 | None => continue, | ||
149 | Some(path) => path, | ||
150 | }; | ||
151 | path.segments.push(name); | ||
152 | 130 | ||
153 | let new_path = if let Some(best_path) = best_path { | 131 | if item.krate(db) == Some(from.krate) { |
154 | select_best_path(best_path, path, prefer_no_std) | 132 | // Item was defined in the same crate that wants to import it. It cannot be found in any |
155 | } else { | 133 | // dependency in this case. |
156 | path | 134 | |
157 | }; | 135 | let local_imports = find_local_import_locations(db, item, from); |
158 | best_path_len = new_path.len(); | 136 | for (module_id, name) in local_imports { |
159 | best_path = Some(new_path); | 137 | if let Some(mut path) = find_path_inner( |
138 | db, | ||
139 | ItemInNs::Types(ModuleDefId::ModuleId(module_id)), | ||
140 | from, | ||
141 | best_path_len - 1, | ||
142 | ) { | ||
143 | path.segments.push(name); | ||
144 | |||
145 | let new_path = if let Some(best_path) = best_path { | ||
146 | select_best_path(best_path, path, prefer_no_std) | ||
147 | } else { | ||
148 | path | ||
149 | }; | ||
150 | best_path_len = new_path.len(); | ||
151 | best_path = Some(new_path); | ||
152 | } | ||
153 | } | ||
154 | } else { | ||
155 | // Item was defined in some upstream crate. This means that it must be exported from one, | ||
156 | // too (unless we can't name it at all). It could *also* be (re)exported by the same crate | ||
157 | // that wants to import it here, but we always prefer to use the external path here. | ||
158 | |||
159 | let crate_graph = db.crate_graph(); | ||
160 | let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { | ||
161 | let import_map = db.import_map(dep.crate_id); | ||
162 | import_map.import_info_for(item).and_then(|info| { | ||
163 | // Determine best path for containing module and append last segment from `info`. | ||
164 | let mut path = find_path_inner( | ||
165 | db, | ||
166 | ItemInNs::Types(ModuleDefId::ModuleId(info.container)), | ||
167 | from, | ||
168 | best_path_len - 1, | ||
169 | )?; | ||
170 | path.segments.push(info.path.segments.last().unwrap().clone()); | ||
171 | Some(path) | ||
172 | }) | ||
173 | }); | ||
174 | |||
175 | for path in extern_paths { | ||
176 | let new_path = if let Some(best_path) = best_path { | ||
177 | select_best_path(best_path, path, prefer_no_std) | ||
178 | } else { | ||
179 | path | ||
180 | }; | ||
181 | best_path = Some(new_path); | ||
182 | } | ||
160 | } | 183 | } |
184 | |||
161 | best_path | 185 | best_path |
162 | } | 186 | } |
163 | 187 | ||
@@ -185,69 +209,86 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) - | |||
185 | } | 209 | } |
186 | } | 210 | } |
187 | 211 | ||
188 | fn find_importable_locations( | 212 | /// Finds locations in `from.krate` from which `item` can be imported by `from`. |
213 | fn find_local_import_locations( | ||
189 | db: &dyn DefDatabase, | 214 | db: &dyn DefDatabase, |
190 | item: ItemInNs, | 215 | item: ItemInNs, |
191 | from: ModuleId, | 216 | from: ModuleId, |
192 | ) -> Vec<(ModuleId, Name)> { | 217 | ) -> Vec<(ModuleId, Name)> { |
193 | let crate_graph = db.crate_graph(); | 218 | let _p = profile("find_local_import_locations"); |
194 | let mut result = Vec::new(); | 219 | |
195 | // We only look in the crate from which we are importing, and the direct | 220 | // `from` can import anything below `from` with visibility of at least `from`, and anything |
196 | // dependencies. We cannot refer to names from transitive dependencies | 221 | // above `from` with any visibility. That means we do not need to descend into private siblings |
197 | // directly (only through reexports in direct dependencies). | 222 | // of `from` (and similar). |
198 | for krate in Some(from.krate) | 223 | |
199 | .into_iter() | 224 | let def_map = db.crate_def_map(from.krate); |
200 | .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) | 225 | |
201 | { | 226 | // Compute the initial worklist. We start with all direct child modules of `from` as well as all |
202 | result.extend( | 227 | // of its (recursive) parent modules. |
203 | db.importable_locations_of(item, krate) | 228 | let data = &def_map.modules[from.local_id]; |
204 | .iter() | 229 | let mut worklist = data |
205 | .filter(|(_, _, vis)| vis.is_visible_from(db, from)) | 230 | .children |
206 | .map(|(m, n, _)| (*m, n.clone())), | 231 | .values() |
207 | ); | 232 | .map(|child| ModuleId { krate: from.krate, local_id: *child }) |
208 | } | 233 | .collect::<Vec<_>>(); |
209 | result | 234 | let mut parent = data.parent; |
210 | } | 235 | while let Some(p) = parent { |
236 | worklist.push(ModuleId { krate: from.krate, local_id: p }); | ||
237 | parent = def_map.modules[p].parent; | ||
238 | } | ||
239 | |||
240 | let mut seen: FxHashSet<_> = FxHashSet::default(); | ||
241 | |||
242 | let mut locations = Vec::new(); | ||
243 | while let Some(module) = worklist.pop() { | ||
244 | if !seen.insert(module) { | ||
245 | continue; // already processed this module | ||
246 | } | ||
247 | |||
248 | let ext_def_map; | ||
249 | let data = if module.krate == from.krate { | ||
250 | &def_map[module.local_id] | ||
251 | } else { | ||
252 | // The crate might reexport a module defined in another crate. | ||
253 | ext_def_map = db.crate_def_map(module.krate); | ||
254 | &ext_def_map[module.local_id] | ||
255 | }; | ||
211 | 256 | ||
212 | /// Collects all locations from which we might import the item in a particular | ||
213 | /// crate. These include the original definition of the item, and any | ||
214 | /// non-private `use`s. | ||
215 | /// | ||
216 | /// Note that the crate doesn't need to be the one in which the item is defined; | ||
217 | /// it might be re-exported in other crates. | ||
218 | pub(crate) fn importable_locations_of_query( | ||
219 | db: &dyn DefDatabase, | ||
220 | item: ItemInNs, | ||
221 | krate: CrateId, | ||
222 | ) -> Arc<[(ModuleId, Name, Visibility)]> { | ||
223 | let _p = profile("importable_locations_of_query"); | ||
224 | let def_map = db.crate_def_map(krate); | ||
225 | let mut result = Vec::new(); | ||
226 | for (local_id, data) in def_map.modules.iter() { | ||
227 | if let Some((name, vis)) = data.scope.name_of(item) { | 257 | if let Some((name, vis)) = data.scope.name_of(item) { |
228 | let is_private = if let Visibility::Module(private_to) = vis { | 258 | if vis.is_visible_from(db, from) { |
229 | private_to.local_id == local_id | 259 | let is_private = if let Visibility::Module(private_to) = vis { |
230 | } else { | 260 | private_to.local_id == module.local_id |
231 | false | 261 | } else { |
232 | }; | 262 | false |
233 | let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { | 263 | }; |
234 | data.scope.declarations().any(|it| it == module_def_id) | 264 | let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { |
235 | } else { | 265 | data.scope.declarations().any(|it| it == module_def_id) |
236 | false | 266 | } else { |
237 | }; | 267 | false |
238 | if is_private && !is_original_def { | 268 | }; |
269 | |||
239 | // Ignore private imports. these could be used if we are | 270 | // Ignore private imports. these could be used if we are |
240 | // in a submodule of this module, but that's usually not | 271 | // in a submodule of this module, but that's usually not |
241 | // what the user wants; and if this module can import | 272 | // what the user wants; and if this module can import |
242 | // the item and we're a submodule of it, so can we. | 273 | // the item and we're a submodule of it, so can we. |
243 | // Also this keeps the cached data smaller. | 274 | // Also this keeps the cached data smaller. |
244 | continue; | 275 | if !is_private || is_original_def { |
276 | locations.push((module, name.clone())); | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | |||
281 | // Descend into all modules visible from `from`. | ||
282 | for (_, per_ns) in data.scope.entries() { | ||
283 | if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() { | ||
284 | if vis.is_visible_from(db, from) { | ||
285 | worklist.push(module); | ||
286 | } | ||
245 | } | 287 | } |
246 | result.push((ModuleId { krate, local_id }, name.clone(), vis)); | ||
247 | } | 288 | } |
248 | } | 289 | } |
249 | 290 | ||
250 | Arc::from(result) | 291 | locations |
251 | } | 292 | } |
252 | 293 | ||
253 | #[cfg(test)] | 294 | #[cfg(test)] |
@@ -264,8 +305,8 @@ mod tests { | |||
264 | /// `code` needs to contain a cursor marker; checks that `find_path` for the | 305 | /// `code` needs to contain a cursor marker; checks that `find_path` for the |
265 | /// item the `path` refers to returns that same path when called from the | 306 | /// item the `path` refers to returns that same path when called from the |
266 | /// module the cursor is in. | 307 | /// module the cursor is in. |
267 | fn check_found_path(code: &str, path: &str) { | 308 | fn check_found_path(ra_fixture: &str, path: &str) { |
268 | let (db, pos) = TestDB::with_position(code); | 309 | let (db, pos) = TestDB::with_position(ra_fixture); |
269 | let module = db.module_for_file(pos.file_id); | 310 | let module = db.module_for_file(pos.file_id); |
270 | let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); | 311 | let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); |
271 | let ast_path = parsed_path_file | 312 | let ast_path = parsed_path_file |
@@ -396,6 +437,44 @@ mod tests { | |||
396 | } | 437 | } |
397 | 438 | ||
398 | #[test] | 439 | #[test] |
440 | fn partially_imported() { | ||
441 | // Tests that short paths are used even for external items, when parts of the path are | ||
442 | // already in scope. | ||
443 | check_found_path( | ||
444 | r#" | ||
445 | //- /main.rs crate:main deps:ra_syntax | ||
446 | |||
447 | use ra_syntax::ast; | ||
448 | <|> | ||
449 | |||
450 | //- /lib.rs crate:ra_syntax | ||
451 | pub mod ast { | ||
452 | pub enum ModuleItem { | ||
453 | A, B, C, | ||
454 | } | ||
455 | } | ||
456 | "#, | ||
457 | "ast::ModuleItem", | ||
458 | ); | ||
459 | |||
460 | check_found_path( | ||
461 | r#" | ||
462 | //- /main.rs crate:main deps:ra_syntax | ||
463 | |||
464 | <|> | ||
465 | |||
466 | //- /lib.rs crate:ra_syntax | ||
467 | pub mod ast { | ||
468 | pub enum ModuleItem { | ||
469 | A, B, C, | ||
470 | } | ||
471 | } | ||
472 | "#, | ||
473 | "ra_syntax::ast::ModuleItem", | ||
474 | ); | ||
475 | } | ||
476 | |||
477 | #[test] | ||
399 | fn same_crate_reexport() { | 478 | fn same_crate_reexport() { |
400 | let code = r#" | 479 | let code = r#" |
401 | //- /main.rs | 480 | //- /main.rs |
diff --git a/crates/ra_hir_def/src/generics.rs b/crates/ra_hir_def/src/generics.rs index 09a5241f7..6a0f493a7 100644 --- a/crates/ra_hir_def/src/generics.rs +++ b/crates/ra_hir_def/src/generics.rs | |||
@@ -42,7 +42,7 @@ pub enum TypeParamProvenance { | |||
42 | } | 42 | } |
43 | 43 | ||
44 | /// Data about the generic parameters of a function, struct, impl, etc. | 44 | /// Data about the generic parameters of a function, struct, impl, etc. |
45 | #[derive(Clone, PartialEq, Eq, Debug)] | 45 | #[derive(Clone, PartialEq, Eq, Debug, Default)] |
46 | pub struct GenericParams { | 46 | pub struct GenericParams { |
47 | pub types: Arena<TypeParamData>, | 47 | pub types: Arena<TypeParamData>, |
48 | // lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>, | 48 | // lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>, |
@@ -74,8 +74,53 @@ impl GenericParams { | |||
74 | def: GenericDefId, | 74 | def: GenericDefId, |
75 | ) -> Arc<GenericParams> { | 75 | ) -> Arc<GenericParams> { |
76 | let _p = profile("generic_params_query"); | 76 | let _p = profile("generic_params_query"); |
77 | let (params, _source_map) = GenericParams::new(db, def); | 77 | |
78 | Arc::new(params) | 78 | let generics = match def { |
79 | GenericDefId::FunctionId(id) => { | ||
80 | let id = id.lookup(db).id; | ||
81 | let tree = db.item_tree(id.file_id); | ||
82 | let item = &tree[id.value]; | ||
83 | tree[item.generic_params].clone() | ||
84 | } | ||
85 | GenericDefId::AdtId(AdtId::StructId(id)) => { | ||
86 | let id = id.lookup(db).id; | ||
87 | let tree = db.item_tree(id.file_id); | ||
88 | let item = &tree[id.value]; | ||
89 | tree[item.generic_params].clone() | ||
90 | } | ||
91 | GenericDefId::AdtId(AdtId::EnumId(id)) => { | ||
92 | let id = id.lookup(db).id; | ||
93 | let tree = db.item_tree(id.file_id); | ||
94 | let item = &tree[id.value]; | ||
95 | tree[item.generic_params].clone() | ||
96 | } | ||
97 | GenericDefId::AdtId(AdtId::UnionId(id)) => { | ||
98 | let id = id.lookup(db).id; | ||
99 | let tree = db.item_tree(id.file_id); | ||
100 | let item = &tree[id.value]; | ||
101 | tree[item.generic_params].clone() | ||
102 | } | ||
103 | GenericDefId::TraitId(id) => { | ||
104 | let id = id.lookup(db).id; | ||
105 | let tree = db.item_tree(id.file_id); | ||
106 | let item = &tree[id.value]; | ||
107 | tree[item.generic_params].clone() | ||
108 | } | ||
109 | GenericDefId::TypeAliasId(id) => { | ||
110 | let id = id.lookup(db).id; | ||
111 | let tree = db.item_tree(id.file_id); | ||
112 | let item = &tree[id.value]; | ||
113 | tree[item.generic_params].clone() | ||
114 | } | ||
115 | GenericDefId::ImplId(id) => { | ||
116 | let id = id.lookup(db).id; | ||
117 | let tree = db.item_tree(id.file_id); | ||
118 | let item = &tree[id.value]; | ||
119 | tree[item.generic_params].clone() | ||
120 | } | ||
121 | GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => GenericParams::default(), | ||
122 | }; | ||
123 | Arc::new(generics) | ||
79 | } | 124 | } |
80 | 125 | ||
81 | fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) { | 126 | fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) { |
@@ -156,7 +201,12 @@ impl GenericParams { | |||
156 | (generics, InFile::new(file_id, sm)) | 201 | (generics, InFile::new(file_id, sm)) |
157 | } | 202 | } |
158 | 203 | ||
159 | fn fill(&mut self, lower_ctx: &LowerCtx, sm: &mut SourceMap, node: &dyn TypeParamsOwner) { | 204 | pub(crate) fn fill( |
205 | &mut self, | ||
206 | lower_ctx: &LowerCtx, | ||
207 | sm: &mut SourceMap, | ||
208 | node: &dyn TypeParamsOwner, | ||
209 | ) { | ||
160 | if let Some(params) = node.type_param_list() { | 210 | if let Some(params) = node.type_param_list() { |
161 | self.fill_params(lower_ctx, sm, params) | 211 | self.fill_params(lower_ctx, sm, params) |
162 | } | 212 | } |
@@ -165,7 +215,7 @@ impl GenericParams { | |||
165 | } | 215 | } |
166 | } | 216 | } |
167 | 217 | ||
168 | fn fill_bounds( | 218 | pub(crate) fn fill_bounds( |
169 | &mut self, | 219 | &mut self, |
170 | lower_ctx: &LowerCtx, | 220 | lower_ctx: &LowerCtx, |
171 | node: &dyn ast::TypeBoundsOwner, | 221 | node: &dyn ast::TypeBoundsOwner, |
@@ -229,7 +279,7 @@ impl GenericParams { | |||
229 | .push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound }); | 279 | .push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound }); |
230 | } | 280 | } |
231 | 281 | ||
232 | fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) { | 282 | pub(crate) fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) { |
233 | type_ref.walk(&mut |type_ref| { | 283 | type_ref.walk(&mut |type_ref| { |
234 | if let TypeRef::ImplTrait(bounds) = type_ref { | 284 | if let TypeRef::ImplTrait(bounds) = type_ref { |
235 | let param = TypeParamData { | 285 | let param = TypeParamData { |
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs new file mode 100644 index 000000000..869f3ca5a --- /dev/null +++ b/crates/ra_hir_def/src/import_map.rs | |||
@@ -0,0 +1,737 @@ | |||
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 fst::{self, Streamer}; | ||
6 | use indexmap::{map::Entry, IndexMap}; | ||
7 | use ra_db::CrateId; | ||
8 | use ra_syntax::SmolStr; | ||
9 | use rustc_hash::{FxHashMap, FxHasher}; | ||
10 | use smallvec::SmallVec; | ||
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 = ra_prof::profile("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 = ra_prof::profile("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 super::*; | ||
331 | use crate::{test_db::TestDB, AssocContainerId, Lookup}; | ||
332 | use insta::assert_snapshot; | ||
333 | use itertools::Itertools; | ||
334 | use ra_db::fixture::WithFixture; | ||
335 | use ra_db::{SourceDatabase, Upcast}; | ||
336 | |||
337 | fn import_map(ra_fixture: &str) -> String { | ||
338 | let db = TestDB::with_files(ra_fixture); | ||
339 | let crate_graph = db.crate_graph(); | ||
340 | |||
341 | let s = crate_graph | ||
342 | .iter() | ||
343 | .filter_map(|krate| { | ||
344 | let cdata = &crate_graph[krate]; | ||
345 | let name = cdata.display_name.as_ref()?; | ||
346 | |||
347 | let map = db.import_map(krate); | ||
348 | |||
349 | Some(format!("{}:\n{:?}", name, map)) | ||
350 | }) | ||
351 | .join("\n"); | ||
352 | s | ||
353 | } | ||
354 | |||
355 | fn search_dependencies_of(ra_fixture: &str, krate_name: &str, query: Query) -> String { | ||
356 | let db = TestDB::with_files(ra_fixture); | ||
357 | let crate_graph = db.crate_graph(); | ||
358 | let krate = crate_graph | ||
359 | .iter() | ||
360 | .find(|krate| { | ||
361 | crate_graph[*krate].display_name.as_ref().map(|n| n.to_string()) | ||
362 | == Some(krate_name.to_string()) | ||
363 | }) | ||
364 | .unwrap(); | ||
365 | |||
366 | search_dependencies(db.upcast(), krate, query) | ||
367 | .into_iter() | ||
368 | .filter_map(|item| { | ||
369 | let mark = match item { | ||
370 | ItemInNs::Types(_) => "t", | ||
371 | ItemInNs::Values(_) => "v", | ||
372 | ItemInNs::Macros(_) => "m", | ||
373 | }; | ||
374 | let item = assoc_to_trait(&db, item); | ||
375 | item.krate(db.upcast()).map(|krate| { | ||
376 | let map = db.import_map(krate); | ||
377 | let path = map.path_of(item).unwrap(); | ||
378 | format!( | ||
379 | "{}::{} ({})", | ||
380 | crate_graph[krate].display_name.as_ref().unwrap(), | ||
381 | path, | ||
382 | mark | ||
383 | ) | ||
384 | }) | ||
385 | }) | ||
386 | .join("\n") | ||
387 | } | ||
388 | |||
389 | fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> ItemInNs { | ||
390 | let assoc: AssocItemId = match item { | ||
391 | ItemInNs::Types(it) | ItemInNs::Values(it) => match it { | ||
392 | ModuleDefId::TypeAliasId(it) => it.into(), | ||
393 | ModuleDefId::FunctionId(it) => it.into(), | ||
394 | ModuleDefId::ConstId(it) => it.into(), | ||
395 | _ => return item, | ||
396 | }, | ||
397 | _ => return item, | ||
398 | }; | ||
399 | |||
400 | let container = match assoc { | ||
401 | AssocItemId::FunctionId(it) => it.lookup(db).container, | ||
402 | AssocItemId::ConstId(it) => it.lookup(db).container, | ||
403 | AssocItemId::TypeAliasId(it) => it.lookup(db).container, | ||
404 | }; | ||
405 | |||
406 | match container { | ||
407 | AssocContainerId::TraitId(it) => ItemInNs::Types(it.into()), | ||
408 | _ => item, | ||
409 | } | ||
410 | } | ||
411 | |||
412 | #[test] | ||
413 | fn smoke() { | ||
414 | let map = import_map( | ||
415 | r" | ||
416 | //- /main.rs crate:main deps:lib | ||
417 | |||
418 | mod private { | ||
419 | pub use lib::Pub; | ||
420 | pub struct InPrivateModule; | ||
421 | } | ||
422 | |||
423 | pub mod publ1 { | ||
424 | use lib::Pub; | ||
425 | } | ||
426 | |||
427 | pub mod real_pub { | ||
428 | pub use lib::Pub; | ||
429 | } | ||
430 | pub mod real_pu2 { // same path length as above | ||
431 | pub use lib::Pub; | ||
432 | } | ||
433 | |||
434 | //- /lib.rs crate:lib | ||
435 | pub struct Pub {} | ||
436 | pub struct Pub2; // t + v | ||
437 | struct Priv; | ||
438 | ", | ||
439 | ); | ||
440 | |||
441 | assert_snapshot!(map, @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 | #[test] | ||
455 | fn prefers_shortest_path() { | ||
456 | let map = import_map( | ||
457 | r" | ||
458 | //- /main.rs crate:main | ||
459 | |||
460 | pub mod sub { | ||
461 | pub mod subsub { | ||
462 | pub struct Def {} | ||
463 | } | ||
464 | |||
465 | pub use super::sub::subsub::Def; | ||
466 | } | ||
467 | ", | ||
468 | ); | ||
469 | |||
470 | assert_snapshot!(map, @r###" | ||
471 | main: | ||
472 | - sub (t) | ||
473 | - sub::Def (t) | ||
474 | - sub::subsub (t) | ||
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 | let map = import_map( | ||
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 | ); | ||
492 | |||
493 | assert_snapshot!(map, @r###" | ||
494 | main: | ||
495 | - m (t) | ||
496 | - m::S (t) | ||
497 | - m::S (v) | ||
498 | lib: | ||
499 | - S (t) | ||
500 | - S (v) | ||
501 | "###); | ||
502 | } | ||
503 | |||
504 | #[test] | ||
505 | fn macro_reexport() { | ||
506 | let map = import_map( | ||
507 | r" | ||
508 | //- /main.rs crate:main deps:lib | ||
509 | pub mod m { | ||
510 | pub use lib::pub_macro; | ||
511 | } | ||
512 | //- /lib.rs crate:lib | ||
513 | #[macro_export] | ||
514 | macro_rules! pub_macro { | ||
515 | () => {}; | ||
516 | } | ||
517 | ", | ||
518 | ); | ||
519 | |||
520 | assert_snapshot!(map, @r###" | ||
521 | main: | ||
522 | - m (t) | ||
523 | - m::pub_macro (m) | ||
524 | lib: | ||
525 | - pub_macro (m) | ||
526 | "###); | ||
527 | } | ||
528 | |||
529 | #[test] | ||
530 | fn module_reexport() { | ||
531 | // Reexporting modules from a dependency adds all contents to the import map. | ||
532 | let map = import_map( | ||
533 | r" | ||
534 | //- /main.rs crate:main deps:lib | ||
535 | pub use lib::module as reexported_module; | ||
536 | //- /lib.rs crate:lib | ||
537 | pub mod module { | ||
538 | pub struct S; | ||
539 | } | ||
540 | ", | ||
541 | ); | ||
542 | |||
543 | assert_snapshot!(map, @r###" | ||
544 | main: | ||
545 | - reexported_module (t) | ||
546 | - reexported_module::S (t) | ||
547 | - reexported_module::S (v) | ||
548 | lib: | ||
549 | - module (t) | ||
550 | - module::S (t) | ||
551 | - module::S (v) | ||
552 | "###); | ||
553 | } | ||
554 | |||
555 | #[test] | ||
556 | fn cyclic_module_reexport() { | ||
557 | // A cyclic reexport does not hang. | ||
558 | let map = import_map( | ||
559 | r" | ||
560 | //- /lib.rs crate:lib | ||
561 | pub mod module { | ||
562 | pub struct S; | ||
563 | pub use super::sub::*; | ||
564 | } | ||
565 | |||
566 | pub mod sub { | ||
567 | pub use super::module; | ||
568 | } | ||
569 | ", | ||
570 | ); | ||
571 | |||
572 | assert_snapshot!(map, @r###" | ||
573 | lib: | ||
574 | - module (t) | ||
575 | - module::S (t) | ||
576 | - module::S (v) | ||
577 | - sub (t) | ||
578 | "###); | ||
579 | } | ||
580 | |||
581 | #[test] | ||
582 | fn private_macro() { | ||
583 | let map = import_map( | ||
584 | r" | ||
585 | //- /lib.rs crate:lib | ||
586 | macro_rules! private_macro { | ||
587 | () => {}; | ||
588 | } | ||
589 | ", | ||
590 | ); | ||
591 | |||
592 | assert_snapshot!(map, @r###" | ||
593 | lib: | ||
594 | "###); | ||
595 | } | ||
596 | |||
597 | #[test] | ||
598 | fn namespacing() { | ||
599 | let map = import_map( | ||
600 | r" | ||
601 | //- /lib.rs crate:lib | ||
602 | pub struct Thing; // t + v | ||
603 | #[macro_export] | ||
604 | macro_rules! Thing { // m | ||
605 | () => {}; | ||
606 | } | ||
607 | ", | ||
608 | ); | ||
609 | |||
610 | assert_snapshot!(map, @r###" | ||
611 | lib: | ||
612 | - Thing (m) | ||
613 | - Thing (t) | ||
614 | - Thing (v) | ||
615 | "###); | ||
616 | |||
617 | let map = import_map( | ||
618 | r" | ||
619 | //- /lib.rs crate:lib | ||
620 | pub mod Thing {} // t | ||
621 | #[macro_export] | ||
622 | macro_rules! Thing { // m | ||
623 | () => {}; | ||
624 | } | ||
625 | ", | ||
626 | ); | ||
627 | |||
628 | assert_snapshot!(map, @r###" | ||
629 | lib: | ||
630 | - Thing (m) | ||
631 | - Thing (t) | ||
632 | "###); | ||
633 | } | ||
634 | |||
635 | #[test] | ||
636 | fn search() { | ||
637 | let ra_fixture = r#" | ||
638 | //- /main.rs crate:main deps:dep | ||
639 | //- /dep.rs crate:dep deps:tdep | ||
640 | use tdep::fmt as fmt_dep; | ||
641 | pub mod fmt { | ||
642 | pub trait Display { | ||
643 | fn fmt(); | ||
644 | } | ||
645 | } | ||
646 | #[macro_export] | ||
647 | macro_rules! Fmt { | ||
648 | () => {}; | ||
649 | } | ||
650 | pub struct Fmt; | ||
651 | |||
652 | pub fn format() {} | ||
653 | pub fn no() {} | ||
654 | |||
655 | //- /tdep.rs crate:tdep | ||
656 | pub mod fmt { | ||
657 | pub struct NotImportableFromMain; | ||
658 | } | ||
659 | "#; | ||
660 | |||
661 | let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt")); | ||
662 | assert_snapshot!(res, @r###" | ||
663 | dep::fmt (t) | ||
664 | dep::Fmt (t) | ||
665 | dep::Fmt (v) | ||
666 | dep::Fmt (m) | ||
667 | dep::fmt::Display (t) | ||
668 | dep::format (v) | ||
669 | dep::fmt::Display (t) | ||
670 | "###); | ||
671 | |||
672 | let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end()); | ||
673 | assert_snapshot!(res, @r###" | ||
674 | dep::fmt (t) | ||
675 | dep::Fmt (t) | ||
676 | dep::Fmt (v) | ||
677 | dep::Fmt (m) | ||
678 | dep::fmt::Display (t) | ||
679 | "###); | ||
680 | } | ||
681 | |||
682 | #[test] | ||
683 | fn search_casing() { | ||
684 | let ra_fixture = r#" | ||
685 | //- /main.rs crate:main deps:dep | ||
686 | //- /dep.rs crate:dep | ||
687 | |||
688 | pub struct fmt; | ||
689 | pub struct FMT; | ||
690 | "#; | ||
691 | |||
692 | let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT")); | ||
693 | |||
694 | assert_snapshot!(res, @r###" | ||
695 | dep::fmt (t) | ||
696 | dep::fmt (v) | ||
697 | dep::FMT (t) | ||
698 | dep::FMT (v) | ||
699 | "###); | ||
700 | |||
701 | let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT").case_sensitive()); | ||
702 | |||
703 | assert_snapshot!(res, @r###" | ||
704 | dep::FMT (t) | ||
705 | dep::FMT (v) | ||
706 | "###); | ||
707 | } | ||
708 | |||
709 | #[test] | ||
710 | fn search_limit() { | ||
711 | let res = search_dependencies_of( | ||
712 | r#" | ||
713 | //- /main.rs crate:main deps:dep | ||
714 | //- /dep.rs crate:dep | ||
715 | pub mod fmt { | ||
716 | pub trait Display { | ||
717 | fn fmt(); | ||
718 | } | ||
719 | } | ||
720 | #[macro_export] | ||
721 | macro_rules! Fmt { | ||
722 | () => {}; | ||
723 | } | ||
724 | pub struct Fmt; | ||
725 | |||
726 | pub fn format() {} | ||
727 | pub fn no() {} | ||
728 | "#, | ||
729 | "main", | ||
730 | Query::new("").limit(2), | ||
731 | ); | ||
732 | assert_snapshot!(res, @r###" | ||
733 | dep::fmt (t) | ||
734 | dep::Fmt (t) | ||
735 | "###); | ||
736 | } | ||
737 | } | ||
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index fc15948ad..beeb98559 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -1,18 +1,39 @@ | |||
1 | //! Describes items defined or visible (ie, imported) in a certain scope. | 1 | //! Describes items defined or visible (ie, imported) in a certain scope. |
2 | //! This is shared between modules and blocks. | 2 | //! This is shared between modules and blocks. |
3 | 3 | ||
4 | use std::collections::hash_map::Entry; | ||
5 | |||
4 | use hir_expand::name::Name; | 6 | use hir_expand::name::Name; |
5 | use once_cell::sync::Lazy; | 7 | use once_cell::sync::Lazy; |
6 | use rustc_hash::FxHashMap; | 8 | use ra_db::CrateId; |
9 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
10 | use test_utils::mark; | ||
7 | 11 | ||
8 | use crate::{ | 12 | use crate::{ |
9 | per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, | 13 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, |
10 | TraitId, | 14 | LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, |
11 | }; | 15 | }; |
12 | 16 | ||
17 | #[derive(Copy, Clone)] | ||
18 | pub(crate) enum ImportType { | ||
19 | Glob, | ||
20 | Named, | ||
21 | } | ||
22 | |||
23 | #[derive(Debug, Default)] | ||
24 | pub struct PerNsGlobImports { | ||
25 | types: FxHashSet<(LocalModuleId, Name)>, | ||
26 | values: FxHashSet<(LocalModuleId, Name)>, | ||
27 | macros: FxHashSet<(LocalModuleId, Name)>, | ||
28 | } | ||
29 | |||
13 | #[derive(Debug, Default, PartialEq, Eq)] | 30 | #[derive(Debug, Default, PartialEq, Eq)] |
14 | pub struct ItemScope { | 31 | pub struct ItemScope { |
15 | visible: FxHashMap<Name, PerNs>, | 32 | types: FxHashMap<Name, (ModuleDefId, Visibility)>, |
33 | values: FxHashMap<Name, (ModuleDefId, Visibility)>, | ||
34 | macros: FxHashMap<Name, (MacroDefId, Visibility)>, | ||
35 | unresolved: FxHashSet<Name>, | ||
36 | |||
16 | defs: Vec<ModuleDefId>, | 37 | defs: Vec<ModuleDefId>, |
17 | impls: Vec<ImplId>, | 38 | impls: Vec<ImplId>, |
18 | /// Macros visible in current module in legacy textual scope | 39 | /// Macros visible in current module in legacy textual scope |
@@ -50,14 +71,16 @@ pub(crate) enum BuiltinShadowMode { | |||
50 | /// Other methods will only resolve values, types and module scoped macros only. | 71 | /// Other methods will only resolve values, types and module scoped macros only. |
51 | impl ItemScope { | 72 | impl ItemScope { |
52 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { | 73 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { |
53 | //FIXME: shadowing | 74 | // FIXME: shadowing |
54 | self.visible.iter().map(|(n, def)| (n, *def)) | 75 | let keys: FxHashSet<_> = self |
55 | } | 76 | .types |
77 | .keys() | ||
78 | .chain(self.values.keys()) | ||
79 | .chain(self.macros.keys()) | ||
80 | .chain(self.unresolved.iter()) | ||
81 | .collect(); | ||
56 | 82 | ||
57 | pub fn entries_without_primitives<'a>( | 83 | keys.into_iter().map(move |name| (name, self.get(name))) |
58 | &'a self, | ||
59 | ) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { | ||
60 | self.visible.iter().map(|(n, def)| (n, *def)) | ||
61 | } | 84 | } |
62 | 85 | ||
63 | pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ { | 86 | pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ { |
@@ -76,7 +99,7 @@ impl ItemScope { | |||
76 | 99 | ||
77 | /// Iterate over all module scoped macros | 100 | /// Iterate over all module scoped macros |
78 | pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { | 101 | pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { |
79 | self.visible.iter().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) | 102 | self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) |
80 | } | 103 | } |
81 | 104 | ||
82 | /// Iterate over all legacy textual scoped macros visible at the end of the module | 105 | /// Iterate over all legacy textual scoped macros visible at the end of the module |
@@ -86,12 +109,16 @@ impl ItemScope { | |||
86 | 109 | ||
87 | /// Get a name from current module scope, legacy macros are not included | 110 | /// Get a name from current module scope, legacy macros are not included |
88 | pub(crate) fn get(&self, name: &Name) -> PerNs { | 111 | pub(crate) fn get(&self, name: &Name) -> PerNs { |
89 | self.visible.get(name).copied().unwrap_or_else(PerNs::none) | 112 | PerNs { |
113 | types: self.types.get(name).copied(), | ||
114 | values: self.values.get(name).copied(), | ||
115 | macros: self.macros.get(name).copied(), | ||
116 | } | ||
90 | } | 117 | } |
91 | 118 | ||
92 | pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { | 119 | pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { |
93 | for (name, per_ns) in &self.visible { | 120 | for (name, per_ns) in self.entries() { |
94 | if let Some(vis) = item.match_with(*per_ns) { | 121 | if let Some(vis) = item.match_with(per_ns) { |
95 | return Some((name, vis)); | 122 | return Some((name, vis)); |
96 | } | 123 | } |
97 | } | 124 | } |
@@ -99,8 +126,8 @@ impl ItemScope { | |||
99 | } | 126 | } |
100 | 127 | ||
101 | pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { | 128 | pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { |
102 | self.visible.values().filter_map(|def| match def.take_types() { | 129 | self.types.values().filter_map(|(def, _)| match def { |
103 | Some(ModuleDefId::TraitId(t)) => Some(t), | 130 | ModuleDefId::TraitId(t) => Some(*t), |
104 | _ => None, | 131 | _ => None, |
105 | }) | 132 | }) |
106 | } | 133 | } |
@@ -123,26 +150,99 @@ impl ItemScope { | |||
123 | 150 | ||
124 | pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool { | 151 | pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool { |
125 | let mut changed = false; | 152 | let mut changed = false; |
126 | let existing = self.visible.entry(name).or_default(); | ||
127 | 153 | ||
128 | if existing.types.is_none() && def.types.is_some() { | 154 | if let Some(types) = def.types { |
129 | existing.types = def.types; | 155 | self.types.entry(name.clone()).or_insert_with(|| { |
130 | changed = true; | 156 | changed = true; |
157 | types | ||
158 | }); | ||
131 | } | 159 | } |
132 | if existing.values.is_none() && def.values.is_some() { | 160 | if let Some(values) = def.values { |
133 | existing.values = def.values; | 161 | self.values.entry(name.clone()).or_insert_with(|| { |
134 | changed = true; | 162 | changed = true; |
163 | values | ||
164 | }); | ||
165 | } | ||
166 | if let Some(macros) = def.macros { | ||
167 | self.macros.entry(name.clone()).or_insert_with(|| { | ||
168 | changed = true; | ||
169 | macros | ||
170 | }); | ||
171 | } | ||
172 | |||
173 | if def.is_none() { | ||
174 | if self.unresolved.insert(name) { | ||
175 | changed = true; | ||
176 | } | ||
177 | } | ||
178 | |||
179 | changed | ||
180 | } | ||
181 | |||
182 | pub(crate) fn push_res_with_import( | ||
183 | &mut self, | ||
184 | glob_imports: &mut PerNsGlobImports, | ||
185 | lookup: (LocalModuleId, Name), | ||
186 | def: PerNs, | ||
187 | def_import_type: ImportType, | ||
188 | ) -> bool { | ||
189 | let mut changed = false; | ||
190 | |||
191 | macro_rules! check_changed { | ||
192 | ( | ||
193 | $changed:ident, | ||
194 | ( $this:ident / $def:ident ) . $field:ident, | ||
195 | $glob_imports:ident [ $lookup:ident ], | ||
196 | $def_import_type:ident | ||
197 | ) => {{ | ||
198 | let existing = $this.$field.entry($lookup.1.clone()); | ||
199 | match (existing, $def.$field) { | ||
200 | (Entry::Vacant(entry), Some(_)) => { | ||
201 | match $def_import_type { | ||
202 | ImportType::Glob => { | ||
203 | $glob_imports.$field.insert($lookup.clone()); | ||
204 | } | ||
205 | ImportType::Named => { | ||
206 | $glob_imports.$field.remove(&$lookup); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | if let Some(fld) = $def.$field { | ||
211 | entry.insert(fld); | ||
212 | } | ||
213 | $changed = true; | ||
214 | } | ||
215 | (Entry::Occupied(mut entry), Some(_)) | ||
216 | if $glob_imports.$field.contains(&$lookup) | ||
217 | && matches!($def_import_type, ImportType::Named) => | ||
218 | { | ||
219 | mark::hit!(import_shadowed); | ||
220 | $glob_imports.$field.remove(&$lookup); | ||
221 | if let Some(fld) = $def.$field { | ||
222 | entry.insert(fld); | ||
223 | } | ||
224 | $changed = true; | ||
225 | } | ||
226 | _ => {} | ||
227 | } | ||
228 | }}; | ||
135 | } | 229 | } |
136 | if existing.macros.is_none() && def.macros.is_some() { | 230 | |
137 | existing.macros = def.macros; | 231 | check_changed!(changed, (self / def).types, glob_imports[lookup], def_import_type); |
138 | changed = true; | 232 | check_changed!(changed, (self / def).values, glob_imports[lookup], def_import_type); |
233 | check_changed!(changed, (self / def).macros, glob_imports[lookup], def_import_type); | ||
234 | |||
235 | if def.is_none() { | ||
236 | if self.unresolved.insert(lookup.1) { | ||
237 | changed = true; | ||
238 | } | ||
139 | } | 239 | } |
140 | 240 | ||
141 | changed | 241 | changed |
142 | } | 242 | } |
143 | 243 | ||
144 | pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a { | 244 | pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a { |
145 | self.visible.iter().map(|(name, res)| (name.clone(), *res)) | 245 | self.entries().map(|(name, res)| (name.clone(), res)) |
146 | } | 246 | } |
147 | 247 | ||
148 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { | 248 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { |
@@ -203,4 +303,22 @@ impl ItemInNs { | |||
203 | ItemInNs::Macros(_) => None, | 303 | ItemInNs::Macros(_) => None, |
204 | } | 304 | } |
205 | } | 305 | } |
306 | |||
307 | /// Returns the crate defining this item (or `None` if `self` is built-in). | ||
308 | pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> { | ||
309 | Some(match self { | ||
310 | ItemInNs::Types(did) | ItemInNs::Values(did) => match did { | ||
311 | ModuleDefId::ModuleId(id) => id.krate, | ||
312 | ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate, | ||
313 | ModuleDefId::AdtId(id) => id.module(db).krate, | ||
314 | ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate, | ||
315 | ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate, | ||
316 | ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate, | ||
317 | ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate, | ||
318 | ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate, | ||
319 | ModuleDefId::BuiltinType(_) => return None, | ||
320 | }, | ||
321 | ItemInNs::Macros(id) => return id.krate, | ||
322 | }) | ||
323 | } | ||
206 | } | 324 | } |
diff --git a/crates/ra_hir_def/src/item_tree.rs b/crates/ra_hir_def/src/item_tree.rs new file mode 100644 index 000000000..da79d8ffd --- /dev/null +++ b/crates/ra_hir_def/src/item_tree.rs | |||
@@ -0,0 +1,754 @@ | |||
1 | //! A simplified AST that only contains items. | ||
2 | |||
3 | mod lower; | ||
4 | #[cfg(test)] | ||
5 | mod tests; | ||
6 | |||
7 | use std::{ | ||
8 | any::type_name, | ||
9 | fmt::{self, Debug}, | ||
10 | hash::{Hash, Hasher}, | ||
11 | marker::PhantomData, | ||
12 | ops::{Index, Range}, | ||
13 | sync::Arc, | ||
14 | }; | ||
15 | |||
16 | use ast::{AstNode, AttrsOwner, NameOwner, StructKind, TypeAscriptionOwner}; | ||
17 | use either::Either; | ||
18 | use hir_expand::{ | ||
19 | ast_id_map::FileAstId, | ||
20 | hygiene::Hygiene, | ||
21 | name::{name, AsName, Name}, | ||
22 | HirFileId, InFile, | ||
23 | }; | ||
24 | use ra_arena::{Arena, Idx, RawId}; | ||
25 | use ra_syntax::{ast, match_ast}; | ||
26 | use rustc_hash::FxHashMap; | ||
27 | use smallvec::SmallVec; | ||
28 | use test_utils::mark; | ||
29 | |||
30 | use crate::{ | ||
31 | attr::Attrs, | ||
32 | db::DefDatabase, | ||
33 | generics::GenericParams, | ||
34 | path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind}, | ||
35 | type_ref::{Mutability, TypeBound, TypeRef}, | ||
36 | visibility::RawVisibility, | ||
37 | }; | ||
38 | |||
39 | #[derive(Copy, Clone, Eq, PartialEq)] | ||
40 | pub struct RawVisibilityId(u32); | ||
41 | |||
42 | impl RawVisibilityId { | ||
43 | pub const PUB: Self = RawVisibilityId(u32::max_value()); | ||
44 | pub const PRIV: Self = RawVisibilityId(u32::max_value() - 1); | ||
45 | pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 2); | ||
46 | } | ||
47 | |||
48 | impl fmt::Debug for RawVisibilityId { | ||
49 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
50 | let mut f = f.debug_tuple("RawVisibilityId"); | ||
51 | match *self { | ||
52 | Self::PUB => f.field(&"pub"), | ||
53 | Self::PRIV => f.field(&"pub(self)"), | ||
54 | Self::PUB_CRATE => f.field(&"pub(crate)"), | ||
55 | _ => f.field(&self.0), | ||
56 | }; | ||
57 | f.finish() | ||
58 | } | ||
59 | } | ||
60 | |||
61 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
62 | pub struct GenericParamsId(u32); | ||
63 | |||
64 | impl GenericParamsId { | ||
65 | pub const EMPTY: Self = GenericParamsId(u32::max_value()); | ||
66 | } | ||
67 | |||
68 | /// The item tree of a source file. | ||
69 | #[derive(Debug, Eq, PartialEq)] | ||
70 | pub struct ItemTree { | ||
71 | top_level: SmallVec<[ModItem; 1]>, | ||
72 | attrs: FxHashMap<AttrOwner, Attrs>, | ||
73 | inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>, | ||
74 | |||
75 | data: Option<Box<ItemTreeData>>, | ||
76 | } | ||
77 | |||
78 | impl ItemTree { | ||
79 | pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { | ||
80 | let _p = ra_prof::profile("item_tree_query").detail(|| format!("{:?}", file_id)); | ||
81 | let syntax = if let Some(node) = db.parse_or_expand(file_id) { | ||
82 | node | ||
83 | } else { | ||
84 | return Arc::new(Self::empty()); | ||
85 | }; | ||
86 | |||
87 | let hygiene = Hygiene::new(db.upcast(), file_id); | ||
88 | let ctx = lower::Ctx::new(db, hygiene.clone(), file_id); | ||
89 | let mut top_attrs = None; | ||
90 | let mut item_tree = match_ast! { | ||
91 | match syntax { | ||
92 | ast::SourceFile(file) => { | ||
93 | top_attrs = Some(Attrs::new(&file, &hygiene)); | ||
94 | ctx.lower_module_items(&file) | ||
95 | }, | ||
96 | ast::MacroItems(items) => { | ||
97 | ctx.lower_module_items(&items) | ||
98 | }, | ||
99 | // Macros can expand to expressions. We return an empty item tree in this case, but | ||
100 | // still need to collect inner items. | ||
101 | ast::Expr(e) => { | ||
102 | ctx.lower_inner_items(e.syntax()) | ||
103 | }, | ||
104 | _ => { | ||
105 | panic!("cannot create item tree from {:?}", syntax); | ||
106 | }, | ||
107 | } | ||
108 | }; | ||
109 | |||
110 | if let Some(attrs) = top_attrs { | ||
111 | item_tree.attrs.insert(AttrOwner::TopLevel, attrs); | ||
112 | } | ||
113 | item_tree.shrink_to_fit(); | ||
114 | Arc::new(item_tree) | ||
115 | } | ||
116 | |||
117 | fn empty() -> Self { | ||
118 | Self { | ||
119 | top_level: Default::default(), | ||
120 | attrs: Default::default(), | ||
121 | inner_items: Default::default(), | ||
122 | data: Default::default(), | ||
123 | } | ||
124 | } | ||
125 | |||
126 | fn shrink_to_fit(&mut self) { | ||
127 | if let Some(data) = &mut self.data { | ||
128 | let ItemTreeData { | ||
129 | imports, | ||
130 | extern_crates, | ||
131 | functions, | ||
132 | structs, | ||
133 | fields, | ||
134 | unions, | ||
135 | enums, | ||
136 | variants, | ||
137 | consts, | ||
138 | statics, | ||
139 | traits, | ||
140 | impls, | ||
141 | type_aliases, | ||
142 | mods, | ||
143 | macro_calls, | ||
144 | exprs, | ||
145 | vis, | ||
146 | generics, | ||
147 | } = &mut **data; | ||
148 | |||
149 | imports.shrink_to_fit(); | ||
150 | extern_crates.shrink_to_fit(); | ||
151 | functions.shrink_to_fit(); | ||
152 | structs.shrink_to_fit(); | ||
153 | fields.shrink_to_fit(); | ||
154 | unions.shrink_to_fit(); | ||
155 | enums.shrink_to_fit(); | ||
156 | variants.shrink_to_fit(); | ||
157 | consts.shrink_to_fit(); | ||
158 | statics.shrink_to_fit(); | ||
159 | traits.shrink_to_fit(); | ||
160 | impls.shrink_to_fit(); | ||
161 | type_aliases.shrink_to_fit(); | ||
162 | mods.shrink_to_fit(); | ||
163 | macro_calls.shrink_to_fit(); | ||
164 | exprs.shrink_to_fit(); | ||
165 | |||
166 | vis.arena.shrink_to_fit(); | ||
167 | generics.arena.shrink_to_fit(); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | /// Returns an iterator over all items located at the top level of the `HirFileId` this | ||
172 | /// `ItemTree` was created from. | ||
173 | pub fn top_level_items(&self) -> &[ModItem] { | ||
174 | &self.top_level | ||
175 | } | ||
176 | |||
177 | /// Returns the inner attributes of the source file. | ||
178 | pub fn top_level_attrs(&self) -> &Attrs { | ||
179 | self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&Attrs::EMPTY) | ||
180 | } | ||
181 | |||
182 | pub fn attrs(&self, of: AttrOwner) -> &Attrs { | ||
183 | self.attrs.get(&of).unwrap_or(&Attrs::EMPTY) | ||
184 | } | ||
185 | |||
186 | /// Returns the lowered inner items that `ast` corresponds to. | ||
187 | /// | ||
188 | /// Most AST items are lowered to a single `ModItem`, but some (eg. `use` items) may be lowered | ||
189 | /// to multiple items in the `ItemTree`. | ||
190 | pub fn inner_items(&self, ast: FileAstId<ast::ModuleItem>) -> &[ModItem] { | ||
191 | &self.inner_items[&ast] | ||
192 | } | ||
193 | |||
194 | pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ { | ||
195 | self.inner_items.values().flatten().copied() | ||
196 | } | ||
197 | |||
198 | pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source { | ||
199 | // This unwrap cannot fail, since it has either succeeded above, or resulted in an empty | ||
200 | // ItemTree (in which case there is no valid `FileItemTreeId` to call this method with). | ||
201 | let root = | ||
202 | db.parse_or_expand(of.file_id).expect("parse_or_expand failed on constructed ItemTree"); | ||
203 | |||
204 | let id = self[of.value].ast_id(); | ||
205 | let map = db.ast_id_map(of.file_id); | ||
206 | let ptr = map.get(id); | ||
207 | ptr.to_node(&root) | ||
208 | } | ||
209 | |||
210 | fn data(&self) -> &ItemTreeData { | ||
211 | self.data.as_ref().expect("attempted to access data of empty ItemTree") | ||
212 | } | ||
213 | |||
214 | fn data_mut(&mut self) -> &mut ItemTreeData { | ||
215 | self.data.get_or_insert_with(Box::default) | ||
216 | } | ||
217 | } | ||
218 | |||
219 | #[derive(Default, Debug, Eq, PartialEq)] | ||
220 | struct ItemVisibilities { | ||
221 | arena: Arena<RawVisibility>, | ||
222 | } | ||
223 | |||
224 | impl ItemVisibilities { | ||
225 | fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId { | ||
226 | match &vis { | ||
227 | RawVisibility::Public => RawVisibilityId::PUB, | ||
228 | RawVisibility::Module(path) if path.segments.is_empty() => match &path.kind { | ||
229 | PathKind::Super(0) => RawVisibilityId::PRIV, | ||
230 | PathKind::Crate => RawVisibilityId::PUB_CRATE, | ||
231 | _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()), | ||
232 | }, | ||
233 | _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()), | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | |||
238 | static VIS_PUB: RawVisibility = RawVisibility::Public; | ||
239 | static VIS_PRIV: RawVisibility = | ||
240 | RawVisibility::Module(ModPath { kind: PathKind::Super(0), segments: Vec::new() }); | ||
241 | static VIS_PUB_CRATE: RawVisibility = | ||
242 | RawVisibility::Module(ModPath { kind: PathKind::Crate, segments: Vec::new() }); | ||
243 | |||
244 | #[derive(Default, Debug, Eq, PartialEq)] | ||
245 | struct GenericParamsStorage { | ||
246 | arena: Arena<GenericParams>, | ||
247 | } | ||
248 | |||
249 | impl GenericParamsStorage { | ||
250 | fn alloc(&mut self, params: GenericParams) -> GenericParamsId { | ||
251 | if params.types.is_empty() && params.where_predicates.is_empty() { | ||
252 | return GenericParamsId::EMPTY; | ||
253 | } | ||
254 | |||
255 | GenericParamsId(self.arena.alloc(params).into_raw().into()) | ||
256 | } | ||
257 | } | ||
258 | |||
259 | static EMPTY_GENERICS: GenericParams = | ||
260 | GenericParams { types: Arena::new(), where_predicates: Vec::new() }; | ||
261 | |||
262 | #[derive(Default, Debug, Eq, PartialEq)] | ||
263 | struct ItemTreeData { | ||
264 | imports: Arena<Import>, | ||
265 | extern_crates: Arena<ExternCrate>, | ||
266 | functions: Arena<Function>, | ||
267 | structs: Arena<Struct>, | ||
268 | fields: Arena<Field>, | ||
269 | unions: Arena<Union>, | ||
270 | enums: Arena<Enum>, | ||
271 | variants: Arena<Variant>, | ||
272 | consts: Arena<Const>, | ||
273 | statics: Arena<Static>, | ||
274 | traits: Arena<Trait>, | ||
275 | impls: Arena<Impl>, | ||
276 | type_aliases: Arena<TypeAlias>, | ||
277 | mods: Arena<Mod>, | ||
278 | macro_calls: Arena<MacroCall>, | ||
279 | exprs: Arena<Expr>, | ||
280 | |||
281 | vis: ItemVisibilities, | ||
282 | generics: GenericParamsStorage, | ||
283 | } | ||
284 | |||
285 | #[derive(Debug, Eq, PartialEq, Hash)] | ||
286 | pub enum AttrOwner { | ||
287 | /// Attributes on an item. | ||
288 | ModItem(ModItem), | ||
289 | /// Inner attributes of the source file. | ||
290 | TopLevel, | ||
291 | |||
292 | Variant(Idx<Variant>), | ||
293 | Field(Idx<Field>), | ||
294 | // FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`. | ||
295 | } | ||
296 | |||
297 | macro_rules! from_attrs { | ||
298 | ( $( $var:ident($t:ty) ),+ ) => { | ||
299 | $( | ||
300 | impl From<$t> for AttrOwner { | ||
301 | fn from(t: $t) -> AttrOwner { | ||
302 | AttrOwner::$var(t) | ||
303 | } | ||
304 | } | ||
305 | )+ | ||
306 | }; | ||
307 | } | ||
308 | |||
309 | from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>)); | ||
310 | |||
311 | /// Trait implemented by all item nodes in the item tree. | ||
312 | pub trait ItemTreeNode: Clone { | ||
313 | type Source: AstNode + Into<ast::ModuleItem>; | ||
314 | |||
315 | fn ast_id(&self) -> FileAstId<Self::Source>; | ||
316 | |||
317 | /// Looks up an instance of `Self` in an item tree. | ||
318 | fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self; | ||
319 | |||
320 | /// Downcasts a `ModItem` to a `FileItemTreeId` specific to this type. | ||
321 | fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>>; | ||
322 | |||
323 | /// Upcasts a `FileItemTreeId` to a generic `ModItem`. | ||
324 | fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem; | ||
325 | } | ||
326 | |||
327 | pub struct FileItemTreeId<N: ItemTreeNode> { | ||
328 | index: Idx<N>, | ||
329 | _p: PhantomData<N>, | ||
330 | } | ||
331 | |||
332 | impl<N: ItemTreeNode> Clone for FileItemTreeId<N> { | ||
333 | fn clone(&self) -> Self { | ||
334 | Self { index: self.index, _p: PhantomData } | ||
335 | } | ||
336 | } | ||
337 | impl<N: ItemTreeNode> Copy for FileItemTreeId<N> {} | ||
338 | |||
339 | impl<N: ItemTreeNode> PartialEq for FileItemTreeId<N> { | ||
340 | fn eq(&self, other: &FileItemTreeId<N>) -> bool { | ||
341 | self.index == other.index | ||
342 | } | ||
343 | } | ||
344 | impl<N: ItemTreeNode> Eq for FileItemTreeId<N> {} | ||
345 | |||
346 | impl<N: ItemTreeNode> Hash for FileItemTreeId<N> { | ||
347 | fn hash<H: Hasher>(&self, state: &mut H) { | ||
348 | self.index.hash(state) | ||
349 | } | ||
350 | } | ||
351 | |||
352 | impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> { | ||
353 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
354 | self.index.fmt(f) | ||
355 | } | ||
356 | } | ||
357 | |||
358 | pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>; | ||
359 | |||
360 | macro_rules! mod_items { | ||
361 | ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => { | ||
362 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] | ||
363 | pub enum ModItem { | ||
364 | $( | ||
365 | $typ(FileItemTreeId<$typ>), | ||
366 | )+ | ||
367 | } | ||
368 | |||
369 | $( | ||
370 | impl From<FileItemTreeId<$typ>> for ModItem { | ||
371 | fn from(id: FileItemTreeId<$typ>) -> ModItem { | ||
372 | ModItem::$typ(id) | ||
373 | } | ||
374 | } | ||
375 | )+ | ||
376 | |||
377 | $( | ||
378 | impl ItemTreeNode for $typ { | ||
379 | type Source = $ast; | ||
380 | |||
381 | fn ast_id(&self) -> FileAstId<Self::Source> { | ||
382 | self.ast_id | ||
383 | } | ||
384 | |||
385 | fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self { | ||
386 | &tree.data().$fld[index] | ||
387 | } | ||
388 | |||
389 | fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> { | ||
390 | if let ModItem::$typ(id) = mod_item { | ||
391 | Some(id) | ||
392 | } else { | ||
393 | None | ||
394 | } | ||
395 | } | ||
396 | |||
397 | fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem { | ||
398 | ModItem::$typ(id) | ||
399 | } | ||
400 | } | ||
401 | |||
402 | impl Index<Idx<$typ>> for ItemTree { | ||
403 | type Output = $typ; | ||
404 | |||
405 | fn index(&self, index: Idx<$typ>) -> &Self::Output { | ||
406 | &self.data().$fld[index] | ||
407 | } | ||
408 | } | ||
409 | )+ | ||
410 | }; | ||
411 | } | ||
412 | |||
413 | mod_items! { | ||