aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src')
-rw-r--r--crates/ra_hir_def/src/adt.rs94
-rw-r--r--crates/ra_hir_def/src/attr.rs57
-rw-r--r--crates/ra_hir_def/src/body.rs67
-rw-r--r--crates/ra_hir_def/src/body/lower.rs159
-rw-r--r--crates/ra_hir_def/src/body/scope.rs140
-rw-r--r--crates/ra_hir_def/src/data.rs384
-rw-r--r--crates/ra_hir_def/src/db.rs25
-rw-r--r--crates/ra_hir_def/src/diagnostics.rs3
-rw-r--r--crates/ra_hir_def/src/docs.rs50
-rw-r--r--crates/ra_hir_def/src/expr.rs5
-rw-r--r--crates/ra_hir_def/src/find_path.rs247
-rw-r--r--crates/ra_hir_def/src/generics.rs62
-rw-r--r--crates/ra_hir_def/src/import_map.rs737
-rw-r--r--crates/ra_hir_def/src/item_scope.rs174
-rw-r--r--crates/ra_hir_def/src/item_tree.rs754
-rw-r--r--crates/ra_hir_def/src/item_tree/lower.rs708
-rw-r--r--crates/ra_hir_def/src/item_tree/tests.rs439
-rw-r--r--crates/ra_hir_def/src/lib.rs166
-rw-r--r--crates/ra_hir_def/src/nameres.rs12
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs514
-rw-r--r--crates/ra_hir_def/src/nameres/mod_resolution.rs121
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs482
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs25
-rw-r--r--crates/ra_hir_def/src/nameres/tests/globs.rs137
-rw-r--r--crates/ra_hir_def/src/nameres/tests/incremental.rs38
-rw-r--r--crates/ra_hir_def/src/nameres/tests/macros.rs24
-rw-r--r--crates/ra_hir_def/src/nameres/tests/mod_resolution.rs28
-rw-r--r--crates/ra_hir_def/src/path.rs37
-rw-r--r--crates/ra_hir_def/src/per_ns.rs10
-rw-r--r--crates/ra_hir_def/src/resolver.rs8
-rw-r--r--crates/ra_hir_def/src/src.rs31
-rw-r--r--crates/ra_hir_def/src/test_db.rs51
-rw-r--r--crates/ra_hir_def/src/type_ref.rs13
-rw-r--r--crates/ra_hir_def/src/visibility.rs21
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};
10use ra_arena::{map::ArenaMap, Arena}; 10use ra_arena::{map::ArenaMap, Arena};
11use ra_prof::profile;
12use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner}; 11use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner};
13 12
14use crate::{ 13use 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};
25use 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
60impl StructData { 61impl 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
87impl EnumData { 84impl 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
265fn 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
289fn 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::{
13use tt::Subtree; 13use tt::Subtree;
14 14
15use crate::{ 15use 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
36impl Attrs { 40impl 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
184fn attrs_from_loc<T>(node: T, db: &dyn DefDatabase) -> Attrs 208fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> Attrs {
185where 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;
14use ra_prof::profile; 14use ra_prof::profile;
15use ra_syntax::{ast, AstNode, AstPtr}; 15use ra_syntax::{ast, AstNode, AstPtr};
16use rustc_hash::FxHashMap; 16use rustc_hash::FxHashMap;
17use test_utils::mark;
17 18
18pub(crate) use lower::LowerCtx; 19pub(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)]
50const EXPANSION_RECURSION_LIMIT: usize = 32;
51
52#[cfg(not(test))]
53const EXPANSION_RECURSION_LIMIT: usize = 128;
54
48impl CfgExpander { 55impl 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, &macro_call); 107 let macro_call = InFile::new(self.current_file_id, &macro_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)]
323mod 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 "
351macro_rules! n_nuple {
352 ($e:tt) => ();
353 ($($rest:tt)*) => {{
354 (n_nuple!($($rest)*)None,)
355 }};
356}
357fn 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
36use super::{ExprSource, PatSource}; 37use super::{ExprSource, PatSource};
37use ast::AstChildren; 38use ast::AstChildren;
39use rustc_hash::FxHashMap;
40use std::{any::type_name, sync::Arc};
38 41
39pub(crate) struct LowerCtx { 42pub(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
87impl ExprCollector<'_> { 98impl 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"
307fn foo() {
308 match Some(()) {
309 opt @ Some(unit) => {
310 <|>
311 }
312 _ => {}
313 }
314}
315",
316 &["opt", "unit"],
317 );
318 }
319
320 #[test]
321 fn macro_inner_item() {
322 do_check(
323 r"
324 macro_rules! mac {
325 () => {{
326 fn inner() {}
327 inner();
328 }};
329 }
330
331 fn foo() {
332 mac!();
333 <|>
334 }
335 ",
336 &[],
337 );
338 }
339
340 #[test]
341 fn broken_inner_item() {
342 do_check(
343 r"
344 fn foo() {
345 trait {}
346 <|>
347 }
348 ",
349 &[],
350 );
351 }
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) { 389fn 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) { 406fn 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) { 418fn 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() { 431fn 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
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_expand::{ 5use hir_expand::{name::Name, InFile};
6 hygiene::Hygiene,
7 name::{name, AsName, Name},
8 AstId, InFile,
9};
10use ra_prof::profile; 6use ra_prof::profile;
11use ra_syntax::ast::{ 7use ra_syntax::ast;
12 self, AssocItem, AstNode, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
13 VisibilityOwner,
14};
15 8
16use crate::{ 9use 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
40impl FunctionData { 34impl 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
97fn 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)]
112pub struct TypeAliasData { 54pub 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 {
151impl TraitData { 87impl 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 {
246impl ConstData { 171impl 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
276impl StaticData { 193impl 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
294fn 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
312fn collect_items_in_macro( 208fn 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
346fn 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.
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use hir_expand::{db::AstDatabase, name::Name, HirFileId}; 4use hir_expand::{db::AstDatabase, HirFileId};
5use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; 5use ra_db::{salsa, CrateId, SourceDatabase, Upcast};
6use ra_prof::profile; 6use ra_prof::profile;
7use ra_syntax::SmolStr; 7use 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)]
50pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { 48pub 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
127fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 118fn 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 @@
3use std::any::Any; 3use std::any::Any;
4 4
5use hir_expand::diagnostics::Diagnostic; 5use hir_expand::diagnostics::Diagnostic;
6use ra_db::RelativePathBuf;
7use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; 6use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
8 7
9use hir_expand::{HirFileId, InFile}; 8use hir_expand::{HirFileId, InFile};
@@ -12,7 +11,7 @@ use hir_expand::{HirFileId, InFile};
12pub struct UnresolvedModule { 11pub 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
18impl Diagnostic for UnresolvedModule { 17impl 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
73pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> { 80pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation>
74 node.doc_comment_text().map(|it| Documentation::new(&it)) 81where
82 N: ast::DocCommentsOwner + ast::AttrsOwner,
83{
84 let doc_comment_text = node.doc_comment_text();
85 let doc_attr_text = expand_doc_attrs(node);
86 let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
87 docs.map(|it| Documentation::new(&it))
88}
89
90fn merge_doc_comments_and_attrs(
91 doc_comment_text: Option<String>,
92 doc_attr_text: Option<String>,
93) -> Option<String> {
94 match (doc_comment_text, doc_attr_text) {
95 (Some(mut comment_text), Some(attr_text)) => {
96 comment_text.push_str("\n\n");
97 comment_text.push_str(&attr_text);
98 Some(comment_text)
99 }
100 (Some(comment_text), None) => Some(comment_text),
101 (None, Some(attr_text)) => Some(attr_text),
102 (None, None) => None,
103 }
104}
105
106fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> {
107 let mut docs = String::new();
108 for attr in owner.attrs() {
109 if let Some(("doc", value)) =
110 attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str()))
111 {
112 docs.push_str(value);
113 docs.push_str("\n\n");
114 }
115 }
116 if docs.is_empty() {
117 None
118 } else {
119 Some(docs.trim_end_matches("\n\n").to_owned())
120 }
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
3use std::sync::Arc;
4
5use hir_expand::name::{known, AsName, Name}; 3use hir_expand::name::{known, AsName, Name};
6use ra_prof::profile; 4use ra_prof::profile;
5use rustc_hash::FxHashSet;
7use test_utils::mark; 6use test_utils::mark;
8 7
9use crate::{ 8use 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.
21pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 20pub 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
26const MAX_PATH_LEN: usize = 15; 25const 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
52pub(crate) fn find_path_inner_query( 40fn 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
188fn find_importable_locations( 212/// Finds locations in `from.krate` from which `item` can be imported by `from`.
213fn 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.
218pub(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)]
46pub struct GenericParams { 46pub 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
3use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
4
5use fst::{self, Streamer};
6use indexmap::{map::Entry, IndexMap};
7use ra_db::CrateId;
8use ra_syntax::SmolStr;
9use rustc_hash::{FxHashMap, FxHasher};
10use smallvec::SmallVec;
11
12use crate::{
13 db::DefDatabase,
14 item_scope::ItemInNs,
15 path::{ModPath, PathKind},
16 visibility::Visibility,
17 AssocItemId, ModuleDefId, ModuleId, TraitId,
18};
19
20type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
21
22/// Item import details stored in the `ImportMap`.
23#[derive(Debug, Clone, Eq, PartialEq)]
24pub struct ImportInfo {
25 /// A path that can be used to import the item, relative to the crate's root.
26 pub path: ModPath,
27 /// The module containing this item.
28 pub container: ModuleId,
29}
30
31/// A map from publicly exported items to the path needed to import/name them from a downstream
32/// crate.
33///
34/// Reexports of items are taken into account, ie. if something is exported under multiple
35/// names, the one with the shortest import path will be used.
36///
37/// Note that all paths are relative to the containing crate's root, so the crate name still needs
38/// to be prepended to the `ModPath` before the path is valid.
39#[derive(Default)]
40pub struct ImportMap {
41 map: FxIndexMap<ItemInNs, ImportInfo>,
42
43 /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
44 /// values returned by running `fst`.
45 ///
46 /// Since a path can refer to multiple items due to namespacing, we store all items with the
47 /// same path right after each other. This allows us to find all items after the FST gives us
48 /// the index of the first one.
49 importables: Vec<ItemInNs>,
50 fst: fst::Map<Vec<u8>>,
51
52 /// Maps names of associated items to the item's ID. Only includes items whose defining trait is
53 /// exported.
54 assoc_map: FxHashMap<SmolStr, SmallVec<[AssocItemId; 1]>>,
55}
56
57impl ImportMap {
58 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
59 let _p = 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
171impl PartialEq for ImportMap {
172 fn eq(&self, other: &Self) -> bool {
173 // `fst` and `importables` are built from `map`, so we don't need to compare them.
174 self.map == other.map
175 }
176}
177
178impl Eq for ImportMap {}
179
180impl fmt::Debug for ImportMap {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 let mut importable_paths: Vec<_> = self
183 .map
184 .iter()
185 .map(|(item, info)| {
186 let ns = match item {
187 ItemInNs::Types(_) => "t",
188 ItemInNs::Values(_) => "v",
189 ItemInNs::Macros(_) => "m",
190 };
191 format!("- {} ({})", info.path, ns)
192 })
193 .collect();
194
195 importable_paths.sort();
196 f.write_str(&importable_paths.join("\n"))
197 }
198}
199
200fn fst_path(path: &ModPath) -> String {
201 let mut s = path.to_string();
202 s.make_ascii_lowercase();
203 s
204}
205
206fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering {
207 let lhs_str = fst_path(&lhs.path);
208 let rhs_str = fst_path(&rhs.path);
209 lhs_str.cmp(&rhs_str)
210}
211
212#[derive(Debug)]
213pub struct Query {
214 query: String,
215 lowercased: String,
216 anchor_end: bool,
217 case_sensitive: bool,
218 limit: usize,
219}
220
221impl Query {
222 pub fn new(query: &str) -> Self {
223 Self {
224 lowercased: query.to_lowercase(),
225 query: query.to_string(),
226 anchor_end: false,
227 case_sensitive: false,
228 limit: usize::max_value(),
229 }
230 }
231
232 /// Only returns items whose paths end with the (case-insensitive) query string as their last
233 /// segment.
234 pub fn anchor_end(self) -> Self {
235 Self { anchor_end: true, ..self }
236 }
237
238 /// Limits the returned number of items to `limit`.
239 pub fn limit(self, limit: usize) -> Self {
240 Self { limit, ..self }
241 }
242
243 /// Respect casing of the query string when matching.
244 pub fn case_sensitive(self) -> Self {
245 Self { case_sensitive: true, ..self }
246 }
247}
248
249/// Searches dependencies of `krate` for an importable path matching `query`.
250///
251/// This returns a list of items that could be imported from dependencies of `krate`.
252pub fn search_dependencies<'a>(
253 db: &'a dyn DefDatabase,
254 krate: CrateId,
255 query: Query,
256) -> Vec<ItemInNs> {
257 let _p = 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)]
329mod 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
4use std::collections::hash_map::Entry;
5
4use hir_expand::name::Name; 6use hir_expand::name::Name;
5use once_cell::sync::Lazy; 7use once_cell::sync::Lazy;
6use rustc_hash::FxHashMap; 8use ra_db::CrateId;
9use rustc_hash::{FxHashMap, FxHashSet};
10use test_utils::mark;
7 11
8use crate::{ 12use 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)]
18pub(crate) enum ImportType {
19 Glob,
20 Named,
21}
22
23#[derive(Debug, Default)]
24pub struct PerNsGlobImports {
25 types: FxHashSet<(LocalModuleId, Name)>,
26 values: FxHashSet<(LocalModuleId, Name)>,
27 macros: FxHashSet<(LocalModuleId, Name)>,
28}
29
13#[derive(Debug, Default, PartialEq, Eq)] 30#[derive(Debug, Default, PartialEq, Eq)]
14pub struct ItemScope { 31pub 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.
51impl ItemScope { 72impl 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
3mod lower;
4#[cfg(test)]
5mod tests;
6
7use std::{
8 any::type_name,
9 fmt::{self, Debug},
10 hash::{Hash, Hasher},
11 marker::PhantomData,
12 ops::{Index, Range},
13 sync::Arc,
14};
15
16use ast::{AstNode, AttrsOwner, NameOwner, StructKind, TypeAscriptionOwner};
17use either::Either;
18use hir_expand::{
19 ast_id_map::FileAstId,
20 hygiene::Hygiene,
21 name::{name, AsName, Name},
22 HirFileId, InFile,
23};
24use ra_arena::{Arena, Idx, RawId};
25use ra_syntax::{ast, match_ast};
26use rustc_hash::FxHashMap;
27use smallvec::SmallVec;
28use test_utils::mark;
29
30use crate::{
31 attr::Attrs,
32 db::DefDatabase,
33 generics::GenericParams,
34 path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
35 type_ref::{Mutability, TypeBound, TypeRef},
36 visibility::RawVisibility,
37};
38
39#[derive(Copy, Clone, Eq, PartialEq)]
40pub struct RawVisibilityId(u32);
41
42impl RawVisibilityId {
43 pub const PUB: Self = RawVisibilityId(u32::max_value());
44 pub const PRIV: Self = RawVisibilityId(u32::max_value() - 1);
45 pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 2);
46}
47
48impl fmt::Debug for RawVisibilityId {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 let mut f = f.debug_tuple("RawVisibilityId");
51 match *self {
52 Self::PUB => f.field(&"pub"),
53 Self::PRIV => f.field(&"pub(self)"),
54 Self::PUB_CRATE => f.field(&"pub(crate)"),
55 _ => f.field(&self.0),
56 };
57 f.finish()
58 }
59}
60
61#[derive(Debug, Copy, Clone, Eq, PartialEq)]
62pub struct GenericParamsId(u32);
63
64impl GenericParamsId {
65 pub const EMPTY: Self = GenericParamsId(u32::max_value());
66}
67
68/// The item tree of a source file.
69#[derive(Debug, Eq, PartialEq)]
70pub struct ItemTree {
71 top_level: SmallVec<[ModItem; 1]>,
72 attrs: FxHashMap<AttrOwner, Attrs>,
73 inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>,
74
75 data: Option<Box<ItemTreeData>>,
76}
77
78impl ItemTree {
79 pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
80 let _p = 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)]
220struct ItemVisibilities {
221 arena: Arena<RawVisibility>,
222}
223
224impl ItemVisibilities {
225 fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId {
226 match &vis {
227 RawVisibility::Public => RawVisibilityId::PUB,
228 RawVisibility::Module(path) if path.segments.is_empty() => match &path.kind {
229 PathKind::Super(0) => RawVisibilityId::PRIV,
230 PathKind::Crate => RawVisibilityId::PUB_CRATE,
231 _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
232 },
233 _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
234 }
235 }
236}
237
238static VIS_PUB: RawVisibility = RawVisibility::Public;
239static VIS_PRIV: RawVisibility =
240 RawVisibility::Module(ModPath { kind: PathKind::Super(0), segments: Vec::new() });
241static VIS_PUB_CRATE: RawVisibility =
242 RawVisibility::Module(ModPath { kind: PathKind::Crate, segments: Vec::new() });
243
244#[derive(Default, Debug, Eq, PartialEq)]
245struct GenericParamsStorage {
246 arena: Arena<GenericParams>,
247}
248
249impl GenericParamsStorage {
250 fn alloc(&mut self, params: GenericParams) -> GenericParamsId {
251 if params.types.is_empty() && params.where_predicates.is_empty() {
252 return GenericParamsId::EMPTY;
253 }
254
255 GenericParamsId(self.arena.alloc(params).into_raw().into())
256 }
257}
258
259static EMPTY_GENERICS: GenericParams =
260 GenericParams { types: Arena::new(), where_predicates: Vec::new() };
261
262#[derive(Default, Debug, Eq, PartialEq)]
263struct ItemTreeData {
264 imports: Arena<Import>,
265 extern_crates: Arena<ExternCrate>,
266 functions: Arena<Function>,
267 structs: Arena<Struct>,
268 fields: Arena<Field>,
269 unions: Arena<Union>,
270 enums: Arena<Enum>,
271 variants: Arena<Variant>,
272 consts: Arena<Const>,
273 statics: Arena<Static>,
274 traits: Arena<Trait>,
275 impls: Arena<Impl>,
276 type_aliases: Arena<TypeAlias>,
277 mods: Arena<Mod>,
278 macro_calls: Arena<MacroCall>,
279 exprs: Arena<Expr>,
280
281 vis: ItemVisibilities,
282 generics: GenericParamsStorage,
283}
284
285#[derive(Debug, Eq, PartialEq, Hash)]
286pub enum AttrOwner {
287 /// Attributes on an item.
288 ModItem(ModItem),
289 /// Inner attributes of the source file.
290 TopLevel,
291
292 Variant(Idx<Variant>),
293 Field(Idx<Field>),
294 // FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`.
295}
296
297macro_rules! from_attrs {
298 ( $( $var:ident($t:ty) ),+ ) => {
299 $(
300 impl From<$t> for AttrOwner {
301 fn from(t: $t) -> AttrOwner {
302 AttrOwner::$var(t)
303 }
304 }
305 )+
306 };
307}
308
309from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>));
310
311/// Trait implemented by all item nodes in the item tree.
312pub trait ItemTreeNode: Clone {
313 type Source: AstNode + Into<ast::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
327pub struct FileItemTreeId<N: ItemTreeNode> {
328 index: Idx<N>,
329 _p: PhantomData<N>,
330}
331
332impl<N: ItemTreeNode> Clone for FileItemTreeId<N> {
333 fn clone(&self) -> Self {
334 Self { index: self.index, _p: PhantomData }
335 }
336}
337impl<N: ItemTreeNode> Copy for FileItemTreeId<N> {}
338
339impl<N: ItemTreeNode> PartialEq for FileItemTreeId<N> {
340 fn eq(&self, other: &FileItemTreeId<N>) -> bool {
341 self.index == other.index
342 }
343}
344impl<N: ItemTreeNode> Eq for FileItemTreeId<N> {}
345
346impl<N: ItemTreeNode> Hash for FileItemTreeId<N> {
347 fn hash<H: Hasher>(&self, state: &mut H) {
348 self.index.hash(state)
349 }
350}
351
352impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> {
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 self.index.fmt(f)
355 }
356}
357
358pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>;
359
360macro_rules! mod_items {
361 ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
362 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
363 pub enum ModItem {
364 $(
365 $typ(FileItemTreeId<$typ>),
366 )+
367 }
368
369 $(
370 impl From<FileItemTreeId<$typ>> for ModItem {
371 fn from(id: FileItemTreeId<$typ>) -> ModItem {
372 ModItem::$typ(id)
373 }
374 }
375 )+
376
377 $(
378 impl ItemTreeNode for $typ {
379 type Source = $ast;
380
381 fn ast_id(&self) -> FileAstId<Self::Source> {
382 self.ast_id
383 }
384
385 fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
386 &tree.data().$fld[index]
387 }
388
389 fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> {
390 if let ModItem::$typ(id) = mod_item {
391 Some(id)
392 } else {
393 None
394 }
395 }
396
397 fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem {
398 ModItem::$typ(id)
399 }
400 }
401
402 impl Index<Idx<$typ>> for ItemTree {
403 type Output = $typ;
404
405 fn index(&self, index: Idx<$typ>) -> &Self::Output {
406 &self.data().$fld[index]
407 }
408 }
409 )+
410 };
411}
412
413mod_items! {