aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir')
-rw-r--r--crates/ra_hir/src/code_model_api.rs110
-rw-r--r--crates/ra_hir/src/code_model_impl.rs2
-rw-r--r--crates/ra_hir/src/code_model_impl/krate.rs47
-rw-r--r--crates/ra_hir/src/code_model_impl/module.rs154
-rw-r--r--crates/ra_hir/src/db.rs29
-rw-r--r--crates/ra_hir/src/expr.rs745
-rw-r--r--crates/ra_hir/src/function.rs79
-rw-r--r--crates/ra_hir/src/function/scope.rs368
-rw-r--r--crates/ra_hir/src/ids.rs10
-rw-r--r--crates/ra_hir/src/impl_block.rs32
-rw-r--r--crates/ra_hir/src/krate.rs48
-rw-r--r--crates/ra_hir/src/lib.rs20
-rw-r--r--crates/ra_hir/src/mock.rs3
-rw-r--r--crates/ra_hir/src/module.rs376
-rw-r--r--crates/ra_hir/src/module/imp.rs190
-rw-r--r--crates/ra_hir/src/module_tree.rs368
-rw-r--r--crates/ra_hir/src/name.rs14
-rw-r--r--crates/ra_hir/src/nameres.rs (renamed from crates/ra_hir/src/module/nameres.rs)16
-rw-r--r--crates/ra_hir/src/nameres/tests.rs (renamed from crates/ra_hir/src/module/nameres/tests.rs)4
-rw-r--r--crates/ra_hir/src/path.rs14
-rw-r--r--crates/ra_hir/src/query_definitions.rs77
-rw-r--r--crates/ra_hir/src/source_binder.rs80
-rw-r--r--crates/ra_hir/src/ty.rs19
-rw-r--r--crates/ra_hir/src/ty/tests.rs44
-rw-r--r--crates/ra_hir/src/type_ref.rs4
25 files changed, 1895 insertions, 958 deletions
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs
new file mode 100644
index 000000000..09b532f74
--- /dev/null
+++ b/crates/ra_hir/src/code_model_api.rs
@@ -0,0 +1,110 @@
1use relative_path::RelativePathBuf;
2use ra_db::{CrateId, Cancelable, FileId};
3use ra_syntax::{ast, SyntaxNode};
4
5use crate::{Name, db::HirDatabase, DefId, Path, PerNs, nameres::ModuleScope};
6
7/// hir::Crate describes a single crate. It's the main inteface with which
8/// crate's dependencies interact. Mostly, it should be just a proxy for the
9/// root module.
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct Crate {
12 pub(crate) crate_id: CrateId,
13}
14
15#[derive(Debug)]
16pub struct CrateDependency {
17 pub krate: Crate,
18 pub name: Name,
19}
20
21impl Crate {
22 pub fn crate_id(&self) -> CrateId {
23 self.crate_id
24 }
25 pub fn dependencies(&self, db: &impl HirDatabase) -> Cancelable<Vec<CrateDependency>> {
26 Ok(self.dependencies_impl(db))
27 }
28 pub fn root_module(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
29 self.root_module_impl(db)
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Hash)]
34pub struct Module {
35 pub(crate) def_id: DefId,
36}
37
38pub enum ModuleSource {
39 SourceFile(ast::SourceFileNode),
40 Module(ast::ModuleNode),
41}
42
43#[derive(Clone, Debug, Hash, PartialEq, Eq)]
44pub enum Problem {
45 UnresolvedModule {
46 candidate: RelativePathBuf,
47 },
48 NotDirOwner {
49 move_to: RelativePathBuf,
50 candidate: RelativePathBuf,
51 },
52}
53
54impl Module {
55 /// Name of this module.
56 pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
57 self.name_impl(db)
58 }
59
60 /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
61 pub fn defenition_source(&self, db: &impl HirDatabase) -> Cancelable<(FileId, ModuleSource)> {
62 self.defenition_source_impl(db)
63 }
64 /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
65 /// `None` for the crate root.
66 pub fn declaration_source(
67 &self,
68 db: &impl HirDatabase,
69 ) -> Cancelable<Option<(FileId, ast::ModuleNode)>> {
70 self.declaration_source_impl(db)
71 }
72
73 /// Returns the crate this module is part of.
74 pub fn krate(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> {
75 self.krate_impl(db)
76 }
77 /// Topmost parent of this module. Every module has a `crate_root`, but some
78 /// might miss `krate`. This can happen if a module's file is not included
79 /// into any module tree of any target from Cargo.toml.
80 pub fn crate_root(&self, db: &impl HirDatabase) -> Cancelable<Module> {
81 self.crate_root_impl(db)
82 }
83 /// Finds a child module with the specified name.
84 pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
85 self.child_impl(db, name)
86 }
87 /// Finds a parent module.
88 pub fn parent(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
89 self.parent_impl(db)
90 }
91 pub fn path_to_root(&self, db: &impl HirDatabase) -> Cancelable<Vec<Module>> {
92 let mut res = vec![self.clone()];
93 let mut curr = self.clone();
94 while let Some(next) = curr.parent(db)? {
95 res.push(next.clone());
96 curr = next
97 }
98 Ok(res)
99 }
100 /// Returns a `ModuleScope`: a set of items, visible in this module.
101 pub fn scope(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> {
102 self.scope_impl(db)
103 }
104 pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> Cancelable<PerNs<DefId>> {
105 self.resolve_path_impl(db, path)
106 }
107 pub fn problems(&self, db: &impl HirDatabase) -> Cancelable<Vec<(SyntaxNode, Problem)>> {
108 self.problems_impl(db)
109 }
110}
diff --git a/crates/ra_hir/src/code_model_impl.rs b/crates/ra_hir/src/code_model_impl.rs
new file mode 100644
index 000000000..157b0c616
--- /dev/null
+++ b/crates/ra_hir/src/code_model_impl.rs
@@ -0,0 +1,2 @@
1mod krate; // `crate` is invalid ident :(
2mod module;
diff --git a/crates/ra_hir/src/code_model_impl/krate.rs b/crates/ra_hir/src/code_model_impl/krate.rs
new file mode 100644
index 000000000..3275eafed
--- /dev/null
+++ b/crates/ra_hir/src/code_model_impl/krate.rs
@@ -0,0 +1,47 @@
1use ra_db::{CrateId, Cancelable};
2
3use crate::{
4 HirFileId, Crate, CrateDependency, AsName, DefLoc, DefKind, Module, SourceItemId,
5 db::HirDatabase,
6};
7
8impl Crate {
9 pub(crate) fn new(crate_id: CrateId) -> Crate {
10 Crate { crate_id }
11 }
12 pub(crate) fn dependencies_impl(&self, db: &impl HirDatabase) -> Vec<CrateDependency> {
13 let crate_graph = db.crate_graph();
14 crate_graph
15 .dependencies(self.crate_id)
16 .map(|dep| {
17 let krate = Crate::new(dep.crate_id());
18 let name = dep.as_name();
19 CrateDependency { krate, name }
20 })
21 .collect()
22 }
23 pub(crate) fn root_module_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
24 let crate_graph = db.crate_graph();
25 let file_id = crate_graph.crate_root(self.crate_id);
26 let source_root_id = db.file_source_root(file_id);
27 let file_id = HirFileId::from(file_id);
28 let module_tree = db.module_tree(source_root_id)?;
29 // FIXME: teach module tree about crate roots instead of guessing
30 let source = SourceItemId {
31 file_id,
32 item_id: None,
33 };
34 let module_id = ctry!(module_tree.find_module_by_source(source));
35
36 let def_loc = DefLoc {
37 kind: DefKind::Module,
38 source_root_id,
39 module_id,
40 source_item_id: module_id.source(&module_tree),
41 };
42 let def_id = def_loc.id(db);
43
44 let module = Module::new(def_id);
45 Ok(Some(module))
46 }
47}
diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs
new file mode 100644
index 000000000..eb35779f1
--- /dev/null
+++ b/crates/ra_hir/src/code_model_impl/module.rs
@@ -0,0 +1,154 @@
1use ra_db::{Cancelable, SourceRootId, FileId};
2use ra_syntax::{ast, SyntaxNode, AstNode};
3
4use crate::{
5 Module, ModuleSource, Problem,
6 Crate, DefId, DefLoc, DefKind, Name, Path, PathKind, PerNs, Def, ModuleId,
7 nameres::ModuleScope,
8 db::HirDatabase,
9};
10
11impl Module {
12 pub(crate) fn new(def_id: DefId) -> Self {
13 crate::code_model_api::Module { def_id }
14 }
15 pub(crate) fn from_module_id(
16 db: &impl HirDatabase,
17 source_root_id: SourceRootId,
18 module_id: ModuleId,
19 ) -> Cancelable<Self> {
20 let module_tree = db.module_tree(source_root_id)?;
21 let def_loc = DefLoc {
22 kind: DefKind::Module,
23 source_root_id,
24 module_id,
25 source_item_id: module_id.source(&module_tree),
26 };
27 let def_id = def_loc.id(db);
28 let module = Module::new(def_id);
29 Ok(module)
30 }
31
32 pub(crate) fn name_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
33 let loc = self.def_id.loc(db);
34 let module_tree = db.module_tree(loc.source_root_id)?;
35 let link = ctry!(loc.module_id.parent_link(&module_tree));
36 Ok(Some(link.name(&module_tree).clone()))
37 }
38
39 pub fn defenition_source_impl(
40 &self,
41 db: &impl HirDatabase,
42 ) -> Cancelable<(FileId, ModuleSource)> {
43 let loc = self.def_id.loc(db);
44 let file_id = loc.source_item_id.file_id.as_original_file();
45 let syntax_node = db.file_item(loc.source_item_id);
46 let syntax_node = syntax_node.borrowed();
47 let module_source = if let Some(source_file) = ast::SourceFile::cast(syntax_node) {
48 ModuleSource::SourceFile(source_file.owned())
49 } else {
50 let module = ast::Module::cast(syntax_node).unwrap();
51 ModuleSource::Module(module.owned())
52 };
53 Ok((file_id, module_source))
54 }
55
56 pub fn declaration_source_impl(
57 &self,
58 db: &impl HirDatabase,
59 ) -> Cancelable<Option<(FileId, ast::ModuleNode)>> {
60 let loc = self.def_id.loc(db);
61 let module_tree = db.module_tree(loc.source_root_id)?;
62 let link = ctry!(loc.module_id.parent_link(&module_tree));
63 let file_id = link
64 .owner(&module_tree)
65 .source(&module_tree)
66 .file_id
67 .as_original_file();
68 let src = link.source(&module_tree, db);
69 Ok(Some((file_id, src)))
70 }
71
72 pub(crate) fn krate_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> {
73 let root = self.crate_root(db)?;
74 let loc = root.def_id.loc(db);
75 let file_id = loc.source_item_id.file_id.as_original_file();
76
77 let crate_graph = db.crate_graph();
78 let crate_id = ctry!(crate_graph.crate_id_for_crate_root(file_id));
79 Ok(Some(Crate::new(crate_id)))
80 }
81
82 pub(crate) fn crate_root_impl(&self, db: &impl HirDatabase) -> Cancelable<Module> {
83 let loc = self.def_id.loc(db);
84 let module_tree = db.module_tree(loc.source_root_id)?;
85 let module_id = loc.module_id.crate_root(&module_tree);
86 Module::from_module_id(db, loc.source_root_id, module_id)
87 }
88 /// Finds a child module with the specified name.
89 pub fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
90 let loc = self.def_id.loc(db);
91 let module_tree = db.module_tree(loc.source_root_id)?;
92 let child_id = ctry!(loc.module_id.child(&module_tree, name));
93 Module::from_module_id(db, loc.source_root_id, child_id).map(Some)
94 }
95 pub fn parent_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
96 let loc = self.def_id.loc(db);
97 let module_tree = db.module_tree(loc.source_root_id)?;
98 let parent_id = ctry!(loc.module_id.parent(&module_tree));
99 Module::from_module_id(db, loc.source_root_id, parent_id).map(Some)
100 }
101 /// Returns a `ModuleScope`: a set of items, visible in this module.
102 pub fn scope_impl(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> {
103 let loc = self.def_id.loc(db);
104 let item_map = db.item_map(loc.source_root_id)?;
105 let res = item_map.per_module[&loc.module_id].clone();
106 Ok(res)
107 }
108 pub fn resolve_path_impl(
109 &self,
110 db: &impl HirDatabase,
111 path: &Path,
112 ) -> Cancelable<PerNs<DefId>> {
113 let mut curr_per_ns = PerNs::types(
114 match path.kind {
115 PathKind::Crate => self.crate_root(db)?,
116 PathKind::Self_ | PathKind::Plain => self.clone(),
117 PathKind::Super => {
118 if let Some(p) = self.parent(db)? {
119 p
120 } else {
121 return Ok(PerNs::none());
122 }
123 }
124 }
125 .def_id,
126 );
127
128 let segments = &path.segments;
129 for name in segments.iter() {
130 let curr = if let Some(r) = curr_per_ns.as_ref().take_types() {
131 r
132 } else {
133 return Ok(PerNs::none());
134 };
135 let module = match curr.resolve(db)? {
136 Def::Module(it) => it,
137 // TODO here would be the place to handle enum variants...
138 _ => return Ok(PerNs::none()),
139 };
140 let scope = module.scope(db)?;
141 curr_per_ns = if let Some(r) = scope.get(&name) {
142 r.def_id
143 } else {
144 return Ok(PerNs::none());
145 };
146 }
147 Ok(curr_per_ns)
148 }
149 pub fn problems_impl(&self, db: &impl HirDatabase) -> Cancelable<Vec<(SyntaxNode, Problem)>> {
150 let loc = self.def_id.loc(db);
151 let module_tree = db.module_tree(loc.source_root_id)?;
152 Ok(loc.module_id.problems(&module_tree, db))
153 }
154}
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 58296fc6f..033f9d25f 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -7,10 +7,10 @@ use crate::{
7 DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, 7 DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId,
8 SourceFileItems, SourceItemId, 8 SourceFileItems, SourceItemId,
9 query_definitions, 9 query_definitions,
10 FnScopes, 10 FnSignature, FnScopes,
11 macros::MacroExpansion, 11 macros::MacroExpansion,
12 module::{ModuleId, ModuleTree, ModuleSource, 12 module_tree::{ModuleId, ModuleTree},
13 nameres::{ItemMap, InputModuleItems}}, 13 nameres::{ItemMap, InputModuleItems},
14 ty::{InferenceResult, Ty}, 14 ty::{InferenceResult, Ty},
15 adt::{StructData, EnumData}, 15 adt::{StructData, EnumData},
16 impl_block::ModuleImplBlocks, 16 impl_block::ModuleImplBlocks,
@@ -31,7 +31,7 @@ pub trait HirDatabase: SyntaxDatabase
31 use fn crate::macros::expand_macro_invocation; 31 use fn crate::macros::expand_macro_invocation;
32 } 32 }
33 33
34 fn fn_scopes(def_id: DefId) -> Arc<FnScopes> { 34 fn fn_scopes(def_id: DefId) -> Cancelable<Arc<FnScopes>> {
35 type FnScopesQuery; 35 type FnScopesQuery;
36 use fn query_definitions::fn_scopes; 36 use fn query_definitions::fn_scopes;
37 } 37 }
@@ -71,9 +71,9 @@ pub trait HirDatabase: SyntaxDatabase
71 use fn query_definitions::file_item; 71 use fn query_definitions::file_item;
72 } 72 }
73 73
74 fn submodules(source: ModuleSource) -> Cancelable<Arc<Vec<crate::module::imp::Submodule>>> { 74 fn submodules(source: SourceItemId) -> Cancelable<Arc<Vec<crate::module_tree::Submodule>>> {
75 type SubmodulesQuery; 75 type SubmodulesQuery;
76 use fn query_definitions::submodules; 76 use fn crate::module_tree::Submodule::submodules_query;
77 } 77 }
78 78
79 fn input_module_items(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<InputModuleItems>> { 79 fn input_module_items(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<InputModuleItems>> {
@@ -86,13 +86,28 @@ pub trait HirDatabase: SyntaxDatabase
86 } 86 }
87 fn module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> { 87 fn module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
88 type ModuleTreeQuery; 88 type ModuleTreeQuery;
89 use fn crate::module::imp::module_tree; 89 use fn crate::module_tree::ModuleTree::module_tree_query;
90 } 90 }
91 91
92 fn impls_in_module(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleImplBlocks>> { 92 fn impls_in_module(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleImplBlocks>> {
93 type ImplsInModuleQuery; 93 type ImplsInModuleQuery;
94 use fn crate::impl_block::impls_in_module; 94 use fn crate::impl_block::impls_in_module;
95 } 95 }
96
97 fn body_hir(def_id: DefId) -> Cancelable<Arc<crate::expr::Body>> {
98 type BodyHirQuery;
99 use fn crate::expr::body_hir;
100 }
101
102 fn body_syntax_mapping(def_id: DefId) -> Cancelable<Arc<crate::expr::BodySyntaxMapping>> {
103 type BodySyntaxMappingQuery;
104 use fn crate::expr::body_syntax_mapping;
105 }
106
107 fn fn_signature(def_id: DefId) -> Arc<FnSignature> {
108 type FnSignatureQuery;
109 use fn crate::function::fn_signature;
110 }
96} 111}
97 112
98} 113}
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
new file mode 100644
index 000000000..6866fc2ac
--- /dev/null
+++ b/crates/ra_hir/src/expr.rs
@@ -0,0 +1,745 @@
1use std::ops::Index;
2use std::sync::Arc;
3
4use rustc_hash::FxHashMap;
5
6use ra_arena::{Arena, RawId, impl_arena_id};
7use ra_db::{LocalSyntaxPtr, Cancelable};
8use ra_syntax::ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner};
9
10use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct ExprId(RawId);
14impl_arena_id!(ExprId);
15
16/// The body of an item (function, const etc.).
17#[derive(Debug, Eq, PartialEq)]
18pub struct Body {
19 exprs: Arena<ExprId, Expr>,
20 pats: Arena<PatId, Pat>,
21 /// The patterns for the function's arguments. While the argument types are
22 /// part of the function signature, the patterns are not (they don't change
23 /// the external type of the function).
24 ///
25 /// If this `Body` is for the body of a constant, this will just be
26 /// empty.
27 args: Vec<PatId>,
28 /// The `ExprId` of the actual body expression.
29 body_expr: ExprId,
30}
31
32/// An item body together with the mapping from syntax nodes to HIR expression
33/// IDs. This is needed to go from e.g. a position in a file to the HIR
34/// expression containing it; but for type inference etc., we want to operate on
35/// a structure that is agnostic to the actual positions of expressions in the
36/// file, so that we don't recompute the type inference whenever some whitespace
37/// is typed.
38#[derive(Debug, Eq, PartialEq)]
39pub struct BodySyntaxMapping {
40 body: Arc<Body>,
41 expr_syntax_mapping: FxHashMap<LocalSyntaxPtr, ExprId>,
42 expr_syntax_mapping_back: FxHashMap<ExprId, LocalSyntaxPtr>,
43 pat_syntax_mapping: FxHashMap<LocalSyntaxPtr, PatId>,
44 pat_syntax_mapping_back: FxHashMap<PatId, LocalSyntaxPtr>,
45}
46
47impl Body {
48 pub fn args(&self) -> &[PatId] {
49 &self.args
50 }
51
52 pub fn body_expr(&self) -> ExprId {
53 self.body_expr
54 }
55}
56
57impl Index<ExprId> for Body {
58 type Output = Expr;
59
60 fn index(&self, expr: ExprId) -> &Expr {
61 &self.exprs[expr]
62 }
63}
64
65impl Index<PatId> for Body {
66 type Output = Pat;
67
68 fn index(&self, pat: PatId) -> &Pat {
69 &self.pats[pat]
70 }
71}
72
73impl BodySyntaxMapping {
74 pub fn expr_syntax(&self, expr: ExprId) -> Option<LocalSyntaxPtr> {
75 self.expr_syntax_mapping_back.get(&expr).cloned()
76 }
77 pub fn syntax_expr(&self, ptr: LocalSyntaxPtr) -> Option<ExprId> {
78 self.expr_syntax_mapping.get(&ptr).cloned()
79 }
80 pub fn pat_syntax(&self, pat: PatId) -> Option<LocalSyntaxPtr> {
81 self.pat_syntax_mapping_back.get(&pat).cloned()
82 }
83 pub fn syntax_pat(&self, ptr: LocalSyntaxPtr) -> Option<PatId> {
84 self.pat_syntax_mapping.get(&ptr).cloned()
85 }
86
87 pub fn body(&self) -> &Arc<Body> {
88 &self.body
89 }
90}
91
92#[derive(Debug, Clone, Eq, PartialEq)]
93pub enum Expr {
94 /// This is produced if syntax tree does not have a required expression piece.
95 Missing,
96 Path(Path),
97 If {
98 condition: ExprId,
99 then_branch: ExprId,
100 else_branch: Option<ExprId>,
101 },
102 Block {
103 statements: Vec<Statement>,
104 tail: Option<ExprId>,
105 },
106 Loop {
107 body: ExprId,
108 },
109 While {
110 condition: ExprId,
111 body: ExprId,
112 },
113 For {
114 iterable: ExprId,
115 pat: PatId,
116 body: ExprId,
117 },
118 Call {
119 callee: ExprId,
120 args: Vec<ExprId>,
121 },
122 MethodCall {
123 receiver: ExprId,
124 method_name: Name,
125 args: Vec<ExprId>,
126 },
127 Match {
128 expr: ExprId,
129 arms: Vec<MatchArm>,
130 },
131 Continue,
132 Break {
133 expr: Option<ExprId>,
134 },
135 Return {
136 expr: Option<ExprId>,
137 },
138 StructLit {
139 path: Option<Path>,
140 fields: Vec<StructLitField>,
141 spread: Option<ExprId>,
142 },
143 Field {
144 expr: ExprId,
145 name: Name,
146 },
147 Try {
148 expr: ExprId,
149 },
150 Cast {
151 expr: ExprId,
152 type_ref: TypeRef,
153 },
154 Ref {
155 expr: ExprId,
156 mutability: Mutability,
157 },
158 UnaryOp {
159 expr: ExprId,
160 op: Option<UnaryOp>,
161 },
162 Lambda {
163 args: Vec<PatId>,
164 arg_types: Vec<Option<TypeRef>>,
165 body: ExprId,
166 },
167}
168
169pub type UnaryOp = ast::PrefixOp;
170
171#[derive(Debug, Clone, Eq, PartialEq)]
172pub struct MatchArm {
173 pub pats: Vec<PatId>,
174 // guard: Option<ExprId>, // TODO
175 pub expr: ExprId,
176}
177
178#[derive(Debug, Clone, Eq, PartialEq)]
179pub struct StructLitField {
180 pub name: Name,
181 pub expr: ExprId,
182}
183
184#[derive(Debug, Clone, Eq, PartialEq)]
185pub enum Statement {
186 Let {
187 pat: PatId,
188 type_ref: Option<TypeRef>,
189 initializer: Option<ExprId>,
190 },
191 Expr(ExprId),
192}
193
194impl Expr {
195 pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
196 match self {
197 Expr::Missing => {}
198 Expr::Path(_) => {}
199 Expr::If {
200 condition,
201 then_branch,
202 else_branch,
203 } => {
204 f(*condition);
205 f(*then_branch);
206 if let Some(else_branch) = else_branch {
207 f(*else_branch);
208 }
209 }
210 Expr::Block { statements, tail } => {
211 for stmt in statements {
212 match stmt {
213 Statement::Let { initializer, .. } => {
214 if let Some(expr) = initializer {
215 f(*expr);
216 }
217 }
218 Statement::Expr(e) => f(*e),
219 }
220 }
221 if let Some(expr) = tail {
222 f(*expr);
223 }
224 }
225 Expr::Loop { body } => f(*body),
226 Expr::While { condition, body } => {
227 f(*condition);
228 f(*body);
229 }
230 Expr::For { iterable, body, .. } => {
231 f(*iterable);
232 f(*body);
233 }
234 Expr::Call { callee, args } => {
235 f(*callee);
236 for arg in args {
237 f(*arg);
238 }
239 }
240 Expr::MethodCall { receiver, args, .. } => {
241 f(*receiver);
242 for arg in args {
243 f(*arg);
244 }
245 }
246 Expr::Match { expr, arms } => {
247 f(*expr);
248 for arm in arms {
249 f(arm.expr);
250 }
251 }
252 Expr::Continue => {}
253 Expr::Break { expr } | Expr::Return { expr } => {
254 if let Some(expr) = expr {
255 f(*expr);
256 }
257 }
258 Expr::StructLit { fields, spread, .. } => {
259 for field in fields {
260 f(field.expr);
261 }
262 if let Some(expr) = spread {
263 f(*expr);
264 }
265 }
266 Expr::Lambda { body, .. } => {
267 f(*body);
268 }
269 Expr::Field { expr, .. }
270 | Expr::Try { expr }
271 | Expr::Cast { expr, .. }
272 | Expr::Ref { expr, .. }
273 | Expr::UnaryOp { expr, .. } => {
274 f(*expr);
275 }
276 }
277 }
278}
279
280#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
281pub struct PatId(RawId);
282impl_arena_id!(PatId);
283
284#[derive(Debug, Clone, Eq, PartialEq)]
285pub enum Pat {
286 Missing,
287 Bind {
288 name: Name,
289 },
290 TupleStruct {
291 path: Option<Path>,
292 args: Vec<PatId>,
293 },
294}
295
296impl Pat {
297 pub fn walk_child_pats(&self, f: impl FnMut(PatId)) {
298 match self {
299 Pat::Missing | Pat::Bind { .. } => {}
300 Pat::TupleStruct { args, .. } => {
301 args.iter().map(|pat| *pat).for_each(f);
302 }
303 }
304 }
305}
306
307// Queries
308
309pub(crate) fn body_hir(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<Body>> {
310 Ok(Arc::clone(&body_syntax_mapping(db, def_id)?.body))
311}
312
313struct ExprCollector {
314 exprs: Arena<ExprId, Expr>,
315 pats: Arena<PatId, Pat>,
316 expr_syntax_mapping: FxHashMap<LocalSyntaxPtr, ExprId>,
317 expr_syntax_mapping_back: FxHashMap<ExprId, LocalSyntaxPtr>,
318 pat_syntax_mapping: FxHashMap<LocalSyntaxPtr, PatId>,
319 pat_syntax_mapping_back: FxHashMap<PatId, LocalSyntaxPtr>,
320}
321
322impl ExprCollector {
323 fn new() -> Self {
324 ExprCollector {
325 exprs: Arena::default(),
326 pats: Arena::default(),
327 expr_syntax_mapping: FxHashMap::default(),
328 expr_syntax_mapping_back: FxHashMap::default(),
329 pat_syntax_mapping: FxHashMap::default(),
330 pat_syntax_mapping_back: FxHashMap::default(),
331 }
332 }
333
334 fn alloc_expr(&mut self, expr: Expr, syntax_ptr: LocalSyntaxPtr) -> ExprId {
335 let id = self.exprs.alloc(expr);
336 self.expr_syntax_mapping.insert(syntax_ptr, id);
337 self.expr_syntax_mapping_back.insert(id, syntax_ptr);
338 id
339 }
340
341 fn alloc_pat(&mut self, pat: Pat, syntax_ptr: LocalSyntaxPtr) -> PatId {
342 let id = self.pats.alloc(pat);
343 self.pat_syntax_mapping.insert(syntax_ptr, id);
344 self.pat_syntax_mapping_back.insert(id, syntax_ptr);
345 id
346 }
347
348 fn empty_block(&mut self) -> ExprId {
349 let block = Expr::Block {
350 statements: Vec::new(),
351 tail: None,
352 };
353 self.exprs.alloc(block)
354 }
355
356 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
357 let syntax_ptr = LocalSyntaxPtr::new(expr.syntax());
358 match expr {
359 ast::Expr::IfExpr(e) => {
360 if let Some(pat) = e.condition().and_then(|c| c.pat()) {
361 // if let -- desugar to match
362 let pat = self.collect_pat(pat);
363 let match_expr =
364 self.collect_expr_opt(e.condition().expect("checked above").expr());
365 let then_branch = self.collect_block_opt(e.then_branch());
366 let else_branch = e
367 .else_branch()
368 .map(|e| self.collect_block(e))
369 .unwrap_or_else(|| self.empty_block());
370 let placeholder_pat = self.pats.alloc(Pat::Missing);
371 let arms = vec![
372 MatchArm {
373 pats: vec![pat],
374 expr: then_branch,
375 },
376 MatchArm {
377 pats: vec![placeholder_pat],
378 expr: else_branch,
379 },
380 ];
381 self.alloc_expr(
382 Expr::Match {
383 expr: match_expr,
384 arms,
385 },
386 syntax_ptr,
387 )
388 } else {
389 let condition = self.collect_expr_opt(e.condition().and_then(|c| c.expr()));
390 let then_branch = self.collect_block_opt(e.then_branch());
391 let else_branch = e.else_branch().map(|e| self.collect_block(e));
392 self.alloc_expr(
393 Expr::If {
394 condition,
395 then_branch,
396 else_branch,
397 },
398 syntax_ptr,
399 )
400 }
401 }
402 ast::Expr::BlockExpr(e) => self.collect_block_opt(e.block()),
403 ast::Expr::LoopExpr(e) => {
404 let body = self.collect_block_opt(e.loop_body());
405 self.alloc_expr(Expr::Loop { body }, syntax_ptr)
406 }
407 ast::Expr::WhileExpr(e) => {
408 let condition = if let Some(condition) = e.condition() {
409 if condition.pat().is_none() {
410 self.collect_expr_opt(condition.expr())
411 } else {
412 // TODO handle while let
413 return self.alloc_expr(Expr::Missing, syntax_ptr);
414 }
415 } else {
416 self.exprs.alloc(Expr::Missing)
417 };
418 let body = self.collect_block_opt(e.loop_body());
419 self.alloc_expr(Expr::While { condition, body }, syntax_ptr)
420 }
421 ast::Expr::ForExpr(e) => {
422 let iterable = self.collect_expr_opt(e.iterable());
423 let pat = self.collect_pat_opt(e.pat());
424 let body = self.collect_block_opt(e.loop_body());
425 self.alloc_expr(
426 Expr::For {
427 iterable,
428 pat,
429 body,
430 },
431 syntax_ptr,
432 )
433 }
434 ast::Expr::CallExpr(e) => {
435 let callee = self.collect_expr_opt(e.expr());
436 let args = if let Some(arg_list) = e.arg_list() {
437 arg_list.args().map(|e| self.collect_expr(e)).collect()
438 } else {
439 Vec::new()
440 };
441 self.alloc_expr(Expr::Call { callee, args }, syntax_ptr)
442 }
443 ast::Expr::MethodCallExpr(e) => {
444 let receiver = self.collect_expr_opt(e.expr());
445 let args = if let Some(arg_list) = e.arg_list() {
446 arg_list.args().map(|e| self.collect_expr(e)).collect()
447 } else {
448 Vec::new()
449 };
450 let method_name = e
451 .name_ref()
452 .map(|nr| nr.as_name())
453 .unwrap_or_else(Name::missing);
454 self.alloc_expr(
455 Expr::MethodCall {
456 receiver,
457 method_name,
458 args,
459 },
460 syntax_ptr,
461 )
462 }
463 ast::Expr::MatchExpr(e) => {
464 let expr = self.collect_expr_opt(e.expr());
465 let arms = if let Some(match_arm_list) = e.match_arm_list() {
466 match_arm_list
467 .arms()
468 .map(|arm| MatchArm {
469 pats: arm.pats().map(|p| self.collect_pat(p)).collect(),
470 expr: self.collect_expr_opt(arm.expr()),
471 })
472 .collect()
473 } else {
474 Vec::new()
475 };
476 self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
477 }
478 ast::Expr::PathExpr(e) => {
479 let path = e
480 .path()
481 .and_then(Path::from_ast)
482 .map(Expr::Path)
483 .unwrap_or(Expr::Missing);
484 self.alloc_expr(path, syntax_ptr)
485 }
486 ast::Expr::ContinueExpr(_e) => {
487 // TODO: labels
488 self.alloc_expr(Expr::Continue, syntax_ptr)
489 }
490 ast::Expr::BreakExpr(e) => {
491 let expr = e.expr().map(|e| self.collect_expr(e));
492 self.alloc_expr(Expr::Break { expr }, syntax_ptr)
493 }
494 ast::Expr::ParenExpr(e) => {
495 let inner = self.collect_expr_opt(e.expr());
496 // make the paren expr point to the inner expression as well
497 self.expr_syntax_mapping.insert(syntax_ptr, inner);
498 inner
499 }
500 ast::Expr::ReturnExpr(e) => {
501 let expr = e.expr().map(|e| self.collect_expr(e));
502 self.alloc_expr(Expr::Return { expr }, syntax_ptr)
503 }
504 ast::Expr::StructLit(e) => {
505 let path = e.path().and_then(Path::from_ast);
506 let fields = if let Some(nfl) = e.named_field_list() {
507 nfl.fields()
508 .map(|field| StructLitField {
509 name: field
510 .name_ref()
511 .map(|nr| nr.as_name())
512 .unwrap_or_else(Name::missing),
513 expr: if let Some(e) = field.expr() {
514 self.collect_expr(e)
515 } else if let Some(nr) = field.name_ref() {
516 // field shorthand
517 let id = self.exprs.alloc(Expr::Path(Path::from_name_ref(nr)));
518 self.expr_syntax_mapping
519 .insert(LocalSyntaxPtr::new(nr.syntax()), id);
520 self.expr_syntax_mapping_back
521 .insert(id, LocalSyntaxPtr::new(nr.syntax()));
522 id
523 } else {
524 self.exprs.alloc(Expr::Missing)
525 },
526 })
527 .collect()
528 } else {
529 Vec::new()
530 };
531 let spread = e.spread().map(|s| self.collect_expr(s));
532 self.alloc_expr(
533 Expr::StructLit {
534 path,
535 fields,
536 spread,
537 },
538 syntax_ptr,
539 )
540 }
541 ast::Expr::FieldExpr(e) => {
542 let expr = self.collect_expr_opt(e.expr());
543 let name = e
544 .name_ref()
545 .map(|nr| nr.as_name())
546 .unwrap_or_else(Name::missing);
547 self.alloc_expr(Expr::Field { expr, name }, syntax_ptr)
548 }
549 ast::Expr::TryExpr(e) => {
550 let expr = self.collect_expr_opt(e.expr());
551 self.alloc_expr(Expr::Try { expr }, syntax_ptr)
552 }
553 ast::Expr::CastExpr(e) => {
554 let expr = self.collect_expr_opt(e.expr());
555 let type_ref = TypeRef::from_ast_opt(e.type_ref());
556 self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
557 }
558 ast::Expr::RefExpr(e) => {
559 let expr = self.collect_expr_opt(e.expr());
560 let mutability = Mutability::from_mutable(e.is_mut());
561 self.alloc_expr(Expr::Ref { expr, mutability }, syntax_ptr)
562 }
563 ast::Expr::PrefixExpr(e) => {
564 let expr = self.collect_expr_opt(e.expr());
565 let op = e.op();
566 self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr)
567 }
568 ast::Expr::LambdaExpr(e) => {
569 let mut args = Vec::new();
570 let mut arg_types = Vec::new();
571 if let Some(pl) = e.param_list() {
572 for param in pl.params() {
573 let pat = self.collect_pat_opt(param.pat());
574 let type_ref = param.type_ref().map(TypeRef::from_ast);
575 args.push(pat);
576 arg_types.push(type_ref);
577 }
578 }
579 let body = self.collect_expr_opt(e.body());
580 self.alloc_expr(
581 Expr::Lambda {
582 args,
583 arg_types,
584 body,
585 },
586 syntax_ptr,
587 )
588 }
589
590 // TODO implement HIR for these:
591 ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
592 ast::Expr::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
593 ast::Expr::TupleExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
594 ast::Expr::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
595 ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
596 ast::Expr::BinExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
597 ast::Expr::Literal(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
598 }
599 }
600
601 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
602 if let Some(expr) = expr {
603 self.collect_expr(expr)
604 } else {
605 self.exprs.alloc(Expr::Missing)
606 }
607 }
608
609 fn collect_block(&mut self, block: ast::Block) -> ExprId {
610 let statements = block
611 .statements()
612 .map(|s| match s {
613 ast::Stmt::LetStmt(stmt) => {
614 let pat = self.collect_pat_opt(stmt.pat());
615 let type_ref = stmt.type_ref().map(TypeRef::from_ast);
616 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
617 Statement::Let {
618 pat,
619 type_ref,
620 initializer,
621 }
622 }
623 ast::Stmt::ExprStmt(stmt) => Statement::Expr(self.collect_expr_opt(stmt.expr())),
624 })
625 .collect();
626 let tail = block.expr().map(|e| self.collect_expr(e));
627 self.alloc_expr(
628 Expr::Block { statements, tail },
629 LocalSyntaxPtr::new(block.syntax()),
630 )
631 }
632
633 fn collect_block_opt(&mut self, block: Option<ast::Block>) -> ExprId {
634 if let Some(block) = block {
635 self.collect_block(block)
636 } else {
637 self.exprs.alloc(Expr::Missing)
638 }
639 }
640
641 fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
642 let syntax_ptr = LocalSyntaxPtr::new(pat.syntax());
643 match pat {
644 ast::Pat::BindPat(bp) => {
645 let name = bp
646 .name()
647 .map(|nr| nr.as_name())
648 .unwrap_or_else(Name::missing);
649 self.alloc_pat(Pat::Bind { name }, syntax_ptr)
650 }
651 ast::Pat::TupleStructPat(p) => {
652 let path = p.path().and_then(Path::from_ast);
653 let args = p.args().map(|p| self.collect_pat(p)).collect();
654 self.alloc_pat(Pat::TupleStruct { path, args }, syntax_ptr)
655 }
656 _ => {
657 // TODO
658 self.alloc_pat(Pat::Missing, syntax_ptr)
659 }
660 }
661 }
662
663 fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
664 if let Some(pat) = pat {
665 self.collect_pat(pat)
666 } else {
667 self.pats.alloc(Pat::Missing)
668 }
669 }
670
671 fn into_body_syntax_mapping(self, args: Vec<PatId>, body_expr: ExprId) -> BodySyntaxMapping {
672 let body = Body {
673 exprs: self.exprs,
674 pats: self.pats,
675 args,
676 body_expr,
677 };
678 BodySyntaxMapping {
679 body: Arc::new(body),
680 expr_syntax_mapping: self.expr_syntax_mapping,
681 expr_syntax_mapping_back: self.expr_syntax_mapping_back,
682 pat_syntax_mapping: self.pat_syntax_mapping,
683 pat_syntax_mapping_back: self.pat_syntax_mapping_back,
684 }
685 }
686}
687
688pub(crate) fn collect_fn_body_syntax(node: ast::FnDef) -> BodySyntaxMapping {
689 let mut collector = ExprCollector::new();
690
691 let args = if let Some(param_list) = node.param_list() {
692 let mut args = Vec::new();
693
694 if let Some(self_param) = param_list.self_param() {
695 let self_param = LocalSyntaxPtr::new(
696 self_param
697 .self_kw()
698 .expect("self param without self keyword")
699 .syntax(),
700 );
701 let arg = collector.alloc_pat(
702 Pat::Bind {
703 name: Name::self_param(),
704 },
705 self_param,
706 );
707 args.push(arg);
708 }
709
710 for param in param_list.params() {
711 let pat = if let Some(pat) = param.pat() {
712 pat
713 } else {
714 continue;
715 };
716 args.push(collector.collect_pat(pat));
717 }
718 args
719 } else {
720 Vec::new()
721 };
722
723 let body = collector.collect_block_opt(node.body());
724 collector.into_body_syntax_mapping(args, body)
725}
726
727pub(crate) fn body_syntax_mapping(
728 db: &impl HirDatabase,
729 def_id: DefId,
730) -> Cancelable<Arc<BodySyntaxMapping>> {
731 let def = def_id.resolve(db)?;
732
733 let body_syntax_mapping = match def {
734 Def::Function(f) => {
735 let node = f.syntax(db);
736 let node = node.borrowed();
737
738 collect_fn_body_syntax(node)
739 }
740 // TODO: consts, etc.
741 _ => panic!("Trying to get body for item type without body"),
742 };
743
744 Ok(Arc::new(body_syntax_mapping))
745}
diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs
index 75ef308ae..4627be071 100644
--- a/crates/ra_hir/src/function.rs
+++ b/crates/ra_hir/src/function.rs
@@ -11,9 +11,9 @@ use ra_syntax::{
11 ast::{self, AstNode, DocCommentsOwner, NameOwner}, 11 ast::{self, AstNode, DocCommentsOwner, NameOwner},
12}; 12};
13 13
14use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock}; 14use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock, expr::{Body, BodySyntaxMapping}, type_ref::{TypeRef, Mutability}, Name};
15 15
16pub use self::scope::FnScopes; 16pub use self::scope::{FnScopes, ScopesWithSyntaxMapping};
17 17
18#[derive(Debug, Clone, PartialEq, Eq)] 18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Function { 19pub struct Function {
@@ -36,8 +36,25 @@ impl Function {
36 ast::FnDef::cast(syntax.borrowed()).unwrap().owned() 36 ast::FnDef::cast(syntax.borrowed()).unwrap().owned()
37 } 37 }
38 38
39 pub fn scopes(&self, db: &impl HirDatabase) -> Arc<FnScopes> { 39 pub fn body(&self, db: &impl HirDatabase) -> Cancelable<Arc<Body>> {
40 db.fn_scopes(self.def_id) 40 db.body_hir(self.def_id)
41 }
42
43 pub fn body_syntax_mapping(&self, db: &impl HirDatabase) -> Cancelable<Arc<BodySyntaxMapping>> {
44 db.body_syntax_mapping(self.def_id)
45 }
46
47 pub fn scopes(&self, db: &impl HirDatabase) -> Cancelable<ScopesWithSyntaxMapping> {
48 let scopes = db.fn_scopes(self.def_id)?;
49 let syntax_mapping = db.body_syntax_mapping(self.def_id)?;
50 Ok(ScopesWithSyntaxMapping {
51 scopes,
52 syntax_mapping,
53 })
54 }
55
56 pub fn signature(&self, db: &impl HirDatabase) -> Arc<FnSignature> {
57 db.fn_signature(self.def_id)
41 } 58 }
42 59
43 pub fn signature_info(&self, db: &impl HirDatabase) -> Option<FnSignatureInfo> { 60 pub fn signature_info(&self, db: &impl HirDatabase) -> Option<FnSignatureInfo> {
@@ -63,6 +80,60 @@ impl Function {
63 } 80 }
64} 81}
65 82
83/// The declared signature of a function.
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub struct FnSignature {
86 args: Vec<TypeRef>,
87 ret_type: TypeRef,
88}
89
90impl FnSignature {
91 pub fn args(&self) -> &[TypeRef] {
92 &self.args
93 }
94
95 pub fn ret_type(&self) -> &TypeRef {
96 &self.ret_type
97 }
98}
99
100pub(crate) fn fn_signature(db: &impl HirDatabase, def_id: DefId) -> Arc<FnSignature> {
101 let func = Function::new(def_id);
102 let syntax = func.syntax(db);
103 let node = syntax.borrowed();
104 let mut args = Vec::new();
105 if let Some(param_list) = node.param_list() {
106 if let Some(self_param) = param_list.self_param() {
107 let self_type = if let Some(type_ref) = self_param.type_ref() {
108 TypeRef::from_ast(type_ref)
109 } else {
110 let self_type = TypeRef::Path(Name::self_type().into());
111 match self_param.flavor() {
112 ast::SelfParamFlavor::Owned => self_type,
113 ast::SelfParamFlavor::Ref => {
114 TypeRef::Reference(Box::new(self_type), Mutability::Shared)
115 }
116 ast::SelfParamFlavor::MutRef => {
117 TypeRef::Reference(Box::new(self_type), Mutability::Mut)
118 }
119 }
120 };
121 args.push(self_type);
122 }
123 for param in param_list.params() {
124 let type_ref = TypeRef::from_ast_opt(param.type_ref());
125 args.push(type_ref);
126 }
127 }
128 let ret_type = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) {
129 TypeRef::from_ast(type_ref)
130 } else {
131 TypeRef::unit()
132 };
133 let sig = FnSignature { args, ret_type };
134 Arc::new(sig)
135}
136
66#[derive(Debug, Clone)] 137#[derive(Debug, Clone)]
67pub struct FnSignatureInfo { 138pub struct FnSignatureInfo {
68 pub name: String, 139 pub name: String,
diff --git a/crates/ra_hir/src/function/scope.rs b/crates/ra_hir/src/function/scope.rs
index 42bfe4f32..0a12f0b35 100644
--- a/crates/ra_hir/src/function/scope.rs
+++ b/crates/ra_hir/src/function/scope.rs
@@ -1,14 +1,16 @@
1use std::sync::Arc;
2
1use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::{FxHashMap, FxHashSet};
2 4
3use ra_syntax::{ 5use ra_syntax::{
4 AstNode, SyntaxNodeRef, TextUnit, TextRange, 6 AstNode, SyntaxNodeRef, TextUnit, TextRange,
5 algo::generate, 7 algo::generate,
6 ast::{self, ArgListOwner, LoopBodyOwner, NameOwner}, 8 ast,
7}; 9};
8use ra_arena::{Arena, RawId, impl_arena_id}; 10use ra_arena::{Arena, RawId, impl_arena_id};
9use ra_db::LocalSyntaxPtr; 11use ra_db::LocalSyntaxPtr;
10 12
11use crate::{Name, AsName}; 13use crate::{Name, AsName, expr::{PatId, ExprId, Pat, Expr, Body, Statement, BodySyntaxMapping}};
12 14
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct ScopeId(RawId); 16pub struct ScopeId(RawId);
@@ -16,15 +18,15 @@ impl_arena_id!(ScopeId);
16 18
17#[derive(Debug, PartialEq, Eq)] 19#[derive(Debug, PartialEq, Eq)]
18pub struct FnScopes { 20pub struct FnScopes {
19 pub self_param: Option<LocalSyntaxPtr>, 21 body: Arc<Body>,
20 scopes: Arena<ScopeId, ScopeData>, 22 scopes: Arena<ScopeId, ScopeData>,
21 scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>, 23 scope_for: FxHashMap<ExprId, ScopeId>,
22} 24}
23 25
24#[derive(Debug, PartialEq, Eq)] 26#[derive(Debug, PartialEq, Eq)]
25pub struct ScopeEntry { 27pub struct ScopeEntry {
26 name: Name, 28 name: Name,
27 ptr: LocalSyntaxPtr, 29 pat: PatId,
28} 30}
29 31
30#[derive(Debug, PartialEq, Eq)] 32#[derive(Debug, PartialEq, Eq)]
@@ -34,28 +36,100 @@ pub struct ScopeData {
34} 36}
35 37
36impl FnScopes { 38impl FnScopes {
37 pub(crate) fn new(fn_def: ast::FnDef) -> FnScopes { 39 pub(crate) fn new(body: Arc<Body>) -> FnScopes {
38 let mut scopes = FnScopes { 40 let mut scopes = FnScopes {
39 self_param: fn_def 41 body: body.clone(),
40 .param_list()
41 .and_then(|it| it.self_param())
42 .map(|it| LocalSyntaxPtr::new(it.syntax())),
43 scopes: Arena::default(), 42 scopes: Arena::default(),
44 scope_for: FxHashMap::default(), 43 scope_for: FxHashMap::default(),
45 }; 44 };
46 let root = scopes.root_scope(); 45 let root = scopes.root_scope();
47 scopes.add_params_bindings(root, fn_def.param_list()); 46 scopes.add_params_bindings(root, body.args());
48 if let Some(body) = fn_def.body() { 47 compute_expr_scopes(body.body_expr(), &body, &mut scopes, root);
49 compute_block_scopes(body, &mut scopes, root)
50 }
51 scopes 48 scopes
52 } 49 }
53 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { 50 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
54 &self.scopes[scope].entries 51 &self.scopes[scope].entries
55 } 52 }
53 pub fn scope_chain_for<'a>(&'a self, expr: ExprId) -> impl Iterator<Item = ScopeId> + 'a {
54 generate(self.scope_for(expr), move |&scope| {
55 self.scopes[scope].parent
56 })
57 }
58
59 pub fn resolve_local_name<'a>(
60 &'a self,
61 context_expr: ExprId,
62 name: Name,
63 ) -> Option<&'a ScopeEntry> {
64 let mut shadowed = FxHashSet::default();
65 let ret = self
66 .scope_chain_for(context_expr)
67 .flat_map(|scope| self.entries(scope).iter())
68 .filter(|entry| shadowed.insert(entry.name()))
69 .find(|entry| entry.name() == &name);
70 ret
71 }
72
73 fn root_scope(&mut self) -> ScopeId {
74 self.scopes.alloc(ScopeData {
75 parent: None,
76 entries: vec![],
77 })
78 }
79 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
80 self.scopes.alloc(ScopeData {
81 parent: Some(parent),
82 entries: vec![],
83 })
84 }
85 fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
86 match &body[pat] {
87 Pat::Bind { name } => self.scopes[scope].entries.push(ScopeEntry {
88 name: name.clone(),
89 pat,
90 }),
91 p => p.walk_child_pats(|pat| self.add_bindings(body, scope, pat)),
92 }
93 }
94 fn add_params_bindings(&mut self, scope: ScopeId, params: &[PatId]) {
95 let body = Arc::clone(&self.body);
96 params
97 .into_iter()
98 .for_each(|pat| self.add_bindings(&body, scope, *pat));
99 }
100 fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
101 self.scope_for.insert(node, scope);
102 }
103 fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
104 self.scope_for.get(&expr).map(|&scope| scope)
105 }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct ScopesWithSyntaxMapping {
110 pub syntax_mapping: Arc<BodySyntaxMapping>,
111 pub scopes: Arc<FnScopes>,
112}
113
114#[derive(Debug, Clone, PartialEq, Eq)]
115pub struct ScopeEntryWithSyntax {
116 name: Name,
117 ptr: LocalSyntaxPtr,
118}
119
120impl ScopeEntryWithSyntax {
121 pub fn name(&self) -> &Name {
122 &self.name
123 }
124 pub fn ptr(&self) -> LocalSyntaxPtr {
125 self.ptr
126 }
127}
128
129impl ScopesWithSyntaxMapping {
56 pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a { 130 pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a {
57 generate(self.scope_for(node), move |&scope| { 131 generate(self.scope_for(node), move |&scope| {
58 self.scopes[scope].parent 132 self.scopes.scopes[scope].parent
59 }) 133 })
60 } 134 }
61 pub fn scope_chain_for_offset<'a>( 135 pub fn scope_chain_for_offset<'a>(
@@ -63,26 +137,30 @@ impl FnScopes {
63 offset: TextUnit, 137 offset: TextUnit,
64 ) -> impl Iterator<Item = ScopeId> + 'a { 138 ) -> impl Iterator<Item = ScopeId> + 'a {
65 let scope = self 139 let scope = self
140 .scopes
66 .scope_for 141 .scope_for
67 .iter() 142 .iter()
68 // find containin scope 143 .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope)))
144 // find containing scope
69 .min_by_key(|(ptr, _scope)| { 145 .min_by_key(|(ptr, _scope)| {
70 ( 146 (
71 !(ptr.range().start() <= offset && offset <= ptr.range().end()), 147 !(ptr.range().start() <= offset && offset <= ptr.range().end()),
72 ptr.range().len(), 148 ptr.range().len(),
73 ) 149 )
74 }) 150 })
75 .map(|(ptr, scope)| self.adjust(*ptr, *scope, offset)); 151 .map(|(ptr, scope)| self.adjust(ptr, *scope, offset));
76 152
77 generate(scope, move |&scope| self.scopes[scope].parent) 153 generate(scope, move |&scope| self.scopes.scopes[scope].parent)
78 } 154 }
79 // XXX: during completion, cursor might be outside of any particular 155 // XXX: during completion, cursor might be outside of any particular
80 // expression. Try to figure out the correct scope... 156 // expression. Try to figure out the correct scope...
81 fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId { 157 fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
82 let r = ptr.range(); 158 let r = ptr.range();
83 let child_scopes = self 159 let child_scopes = self
160 .scopes
84 .scope_for 161 .scope_for
85 .iter() 162 .iter()
163 .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope)))
86 .map(|(ptr, scope)| (ptr.range(), scope)) 164 .map(|(ptr, scope)| (ptr.range(), scope))
87 .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); 165 .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
88 166
@@ -100,22 +178,27 @@ impl FnScopes {
100 .unwrap_or(original_scope) 178 .unwrap_or(original_scope)
101 } 179 }
102 180
103 pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> { 181 pub fn resolve_local_name(&self, name_ref: ast::NameRef) -> Option<ScopeEntryWithSyntax> {
104 let mut shadowed = FxHashSet::default(); 182 let mut shadowed = FxHashSet::default();
105 let name = name_ref.as_name(); 183 let name = name_ref.as_name();
106 let ret = self 184 let ret = self
107 .scope_chain(name_ref.syntax()) 185 .scope_chain(name_ref.syntax())
108 .flat_map(|scope| self.entries(scope).iter()) 186 .flat_map(|scope| self.scopes.entries(scope).iter())
109 .filter(|entry| shadowed.insert(entry.name())) 187 .filter(|entry| shadowed.insert(entry.name()))
110 .filter(|entry| entry.name() == &name) 188 .filter(|entry| entry.name() == &name)
111 .nth(0); 189 .nth(0);
112 ret 190 ret.and_then(|entry| {
191 Some(ScopeEntryWithSyntax {
192 name: entry.name().clone(),
193 ptr: self.syntax_mapping.pat_syntax(entry.pat())?,
194 })
195 })
113 } 196 }
114 197
115 pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> { 198 pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> {
116 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); 199 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
117 let name_ptr = LocalSyntaxPtr::new(pat.syntax()); 200 let name_ptr = LocalSyntaxPtr::new(pat.syntax());
118 let refs: Vec<_> = fn_def 201 fn_def
119 .syntax() 202 .syntax()
120 .descendants() 203 .descendants()
121 .filter_map(ast::NameRef::cast) 204 .filter_map(ast::NameRef::cast)
@@ -127,203 +210,94 @@ impl FnScopes {
127 name: name_ref.syntax().text().to_string(), 210 name: name_ref.syntax().text().to_string(),
128 range: name_ref.syntax().range(), 211 range: name_ref.syntax().range(),
129 }) 212 })
130 .collect(); 213 .collect()
131
132 refs
133 } 214 }
134 215
135 fn root_scope(&mut self) -> ScopeId {
136 self.scopes.alloc(ScopeData {
137 parent: None,
138 entries: vec![],
139 })
140 }
141 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
142 self.scopes.alloc(ScopeData {
143 parent: Some(parent),
144 entries: vec![],
145 })
146 }
147 fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
148 let entries = pat
149 .syntax()
150 .descendants()
151 .filter_map(ast::BindPat::cast)
152 .filter_map(ScopeEntry::new);
153 self.scopes[scope].entries.extend(entries);
154 }
155 fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
156 params
157 .into_iter()
158 .flat_map(|it| it.params())
159 .filter_map(|it| it.pat())
160 .for_each(|it| self.add_bindings(scope, it));
161 }
162 fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
163 self.scope_for.insert(LocalSyntaxPtr::new(node), scope);
164 }
165 fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> { 216 fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
166 node.ancestors() 217 node.ancestors()
167 .map(LocalSyntaxPtr::new) 218 .map(LocalSyntaxPtr::new)
168 .filter_map(|it| self.scope_for.get(&it).map(|&scope| scope)) 219 .filter_map(|ptr| self.syntax_mapping.syntax_expr(ptr))
169 .next() 220 .find_map(|it| self.scopes.scope_for(it))
170 } 221 }
171} 222}
172 223
173impl ScopeEntry { 224impl ScopeEntry {
174 fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
175 let name = pat.name()?.as_name();
176 let res = ScopeEntry {
177 name,
178 ptr: LocalSyntaxPtr::new(pat.syntax()),
179 };
180 Some(res)
181 }
182 pub fn name(&self) -> &Name { 225 pub fn name(&self) -> &Name {
183 &self.name 226 &self.name
184 } 227 }
185 pub fn ptr(&self) -> LocalSyntaxPtr { 228 pub fn pat(&self) -> PatId {
186 self.ptr 229 self.pat
187 } 230 }
188} 231}
189 232
190fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) { 233fn compute_block_scopes(
191 // A hack for completion :( 234 statements: &[Statement],
192 scopes.set_scope(block.syntax(), scope); 235 tail: Option<ExprId>,
193 for stmt in block.statements() { 236 body: &Body,
237 scopes: &mut FnScopes,
238 mut scope: ScopeId,
239) {
240 for stmt in statements {
194 match stmt { 241 match stmt {
195 ast::Stmt::LetStmt(stmt) => { 242 Statement::Let {
196 if let Some(expr) = stmt.initializer() { 243 pat, initializer, ..
197 scopes.set_scope(expr.syntax(), scope); 244 } => {
198 compute_expr_scopes(expr, scopes, scope); 245 if let Some(expr) = initializer {
246 scopes.set_scope(*expr, scope);
247 compute_expr_scopes(*expr, body, scopes, scope);
199 } 248 }
200 scope = scopes.new_scope(scope); 249 scope = scopes.new_scope(scope);
201 if let Some(pat) = stmt.pat() { 250 scopes.add_bindings(body, scope, *pat);
202 scopes.add_bindings(scope, pat);
203 }
204 } 251 }
205 ast::Stmt::ExprStmt(expr_stmt) => { 252 Statement::Expr(expr) => {
206 if let Some(expr) = expr_stmt.expr() { 253 scopes.set_scope(*expr, scope);
207 scopes.set_scope(expr.syntax(), scope); 254 compute_expr_scopes(*expr, body, scopes, scope);
208 compute_expr_scopes(expr, scopes, scope);
209 }
210 } 255 }
211 } 256 }
212 } 257 }
213 if let Some(expr) = block.expr() { 258 if let Some(expr) = tail {
214 scopes.set_scope(expr.syntax(), scope); 259 compute_expr_scopes(expr, body, scopes, scope);
215 compute_expr_scopes(expr, scopes, scope);
216 } 260 }
217} 261}
218 262
219fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { 263fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut FnScopes, scope: ScopeId) {
220 match expr { 264 scopes.set_scope(expr, scope);
221 ast::Expr::IfExpr(e) => { 265 match &body[expr] {
222 let cond_scope = e 266 Expr::Block { statements, tail } => {
223 .condition() 267 compute_block_scopes(&statements, *tail, body, scopes, scope);
224 .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
225 if let Some(block) = e.then_branch() {
226 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
227 }
228 if let Some(block) = e.else_branch() {
229 compute_block_scopes(block, scopes, scope);
230 }
231 }
232 ast::Expr::BlockExpr(e) => {
233 if let Some(block) = e.block() {
234 compute_block_scopes(block, scopes, scope);
235 }
236 }
237 ast::Expr::LoopExpr(e) => {
238 if let Some(block) = e.loop_body() {
239 compute_block_scopes(block, scopes, scope);
240 }
241 }
242 ast::Expr::WhileExpr(e) => {
243 let cond_scope = e
244 .condition()
245 .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
246 if let Some(block) = e.loop_body() {
247 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
248 }
249 } 268 }
250 ast::Expr::ForExpr(e) => { 269 Expr::For {
251 if let Some(expr) = e.iterable() { 270 iterable,
252 compute_expr_scopes(expr, scopes, scope); 271 pat,
253 } 272 body: body_expr,
254 let mut scope = scope; 273 } => {
255 if let Some(pat) = e.pat() { 274 compute_expr_scopes(*iterable, body, scopes, scope);
256 scope = scopes.new_scope(scope);
257 scopes.add_bindings(scope, pat);
258 }
259 if let Some(block) = e.loop_body() {
260 compute_block_scopes(block, scopes, scope);
261 }
262 }
263 ast::Expr::LambdaExpr(e) => {
264 let scope = scopes.new_scope(scope); 275 let scope = scopes.new_scope(scope);
265 scopes.add_params_bindings(scope, e.param_list()); 276 scopes.add_bindings(body, scope, *pat);
266 if let Some(body) = e.body() { 277 compute_expr_scopes(*body_expr, body, scopes, scope);
267 scopes.set_scope(body.syntax(), scope);
268 compute_expr_scopes(body, scopes, scope);
269 }
270 } 278 }
271 ast::Expr::CallExpr(e) => { 279 Expr::Lambda {
272 compute_call_scopes(e.expr(), e.arg_list(), scopes, scope); 280 args,
273 } 281 body: body_expr,
274 ast::Expr::MethodCallExpr(e) => { 282 ..
275 compute_call_scopes(e.expr(), e.arg_list(), scopes, scope); 283 } => {
284 let scope = scopes.new_scope(scope);
285 scopes.add_params_bindings(scope, &args);
286 compute_expr_scopes(*body_expr, body, scopes, scope);
276 } 287 }
277 ast::Expr::MatchExpr(e) => { 288 Expr::Match { expr, arms } => {
278 if let Some(expr) = e.expr() { 289 compute_expr_scopes(*expr, body, scopes, scope);
279 compute_expr_scopes(expr, scopes, scope); 290 for arm in arms {
280 }
281 for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) {
282 let scope = scopes.new_scope(scope); 291 let scope = scopes.new_scope(scope);
283 for pat in arm.pats() { 292 for pat in &arm.pats {
284 scopes.add_bindings(scope, pat); 293 scopes.add_bindings(body, scope, *pat);
285 }
286 if let Some(expr) = arm.expr() {
287 compute_expr_scopes(expr, scopes, scope);
288 } 294 }
295 scopes.set_scope(arm.expr, scope);
296 compute_expr_scopes(arm.expr, body, scopes, scope);
289 } 297 }
290 } 298 }
291 _ => expr 299 e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
292 .syntax()
293 .children()
294 .filter_map(ast::Expr::cast)
295 .for_each(|expr| compute_expr_scopes(expr, scopes, scope)),
296 }; 300 };
297
298 fn compute_call_scopes(
299 receiver: Option<ast::Expr>,
300 arg_list: Option<ast::ArgList>,
301 scopes: &mut FnScopes,
302 scope: ScopeId,
303 ) {
304 arg_list
305 .into_iter()
306 .flat_map(|it| it.args())
307 .chain(receiver)
308 .for_each(|expr| compute_expr_scopes(expr, scopes, scope));
309 }
310
311 fn compute_cond_scopes(
312 cond: ast::Condition,
313 scopes: &mut FnScopes,
314 scope: ScopeId,
315 ) -> Option<ScopeId> {
316 if let Some(expr) = cond.expr() {
317 compute_expr_scopes(expr, scopes, scope);
318 }
319 if let Some(pat) = cond.pat() {
320 let s = scopes.new_scope(scope);
321 scopes.add_bindings(s, pat);
322 Some(s)
323 } else {
324 None
325 }
326 }
327} 301}
328 302
329#[derive(Debug)] 303#[derive(Debug)]
@@ -338,6 +312,8 @@ mod tests {
338 use ra_syntax::SourceFileNode; 312 use ra_syntax::SourceFileNode;
339 use test_utils::{extract_offset, assert_eq_text}; 313 use test_utils::{extract_offset, assert_eq_text};
340 314
315 use crate::expr;
316
341 use super::*; 317 use super::*;
342 318
343 fn do_check(code: &str, expected: &[&str]) { 319 fn do_check(code: &str, expected: &[&str]) {
@@ -353,15 +329,20 @@ mod tests {
353 let file = SourceFileNode::parse(&code); 329 let file = SourceFileNode::parse(&code);
354 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); 330 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
355 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); 331 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
356 let scopes = FnScopes::new(fn_def); 332 let body_hir = expr::collect_fn_body_syntax(fn_def);
333 let scopes = FnScopes::new(Arc::clone(body_hir.body()));
334 let scopes = ScopesWithSyntaxMapping {
335 scopes: Arc::new(scopes),
336 syntax_mapping: Arc::new(body_hir),
337 };
357 let actual = scopes 338 let actual = scopes
358 .scope_chain(marker.syntax()) 339 .scope_chain(marker.syntax())
359 .flat_map(|scope| scopes.entries(scope)) 340 .flat_map(|scope| scopes.scopes.entries(scope))
360 .map(|it| it.name().to_string()) 341 .map(|it| it.name().to_string())
361 .collect::<Vec<_>>() 342 .collect::<Vec<_>>()
362 .join("\n"); 343 .join("\n");
363 let expected = expected.join("\n"); 344 let expected = expected.join("\n");
364 assert_eq_text!(&actual, &expected); 345 assert_eq_text!(&expected, &actual);
365 } 346 }
366 347
367 #[test] 348 #[test]
@@ -389,7 +370,7 @@ mod tests {
389 } 370 }
390 371
391 #[test] 372 #[test]
392 fn test_metod_call_scope() { 373 fn test_method_call_scope() {
393 do_check( 374 do_check(
394 r" 375 r"
395 fn quux() { 376 fn quux() {
@@ -445,10 +426,15 @@ mod tests {
445 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); 426 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
446 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); 427 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
447 428
448 let scopes = FnScopes::new(fn_def); 429 let body_hir = expr::collect_fn_body_syntax(fn_def);
430 let scopes = FnScopes::new(Arc::clone(body_hir.body()));
431 let scopes = ScopesWithSyntaxMapping {
432 scopes: Arc::new(scopes),
433 syntax_mapping: Arc::new(body_hir),
434 };
449 435
450 let local_name_entry = scopes.resolve_local_name(name_ref).unwrap(); 436 let local_name_entry = scopes.resolve_local_name(name_ref).unwrap();
451 let local_name = local_name_entry.ptr().resolve(&file); 437 let local_name = local_name_entry.ptr();
452 let expected_name = 438 let expected_name =
453 find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); 439 find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap();
454 assert_eq!(local_name.range(), expected_name.syntax().range()); 440 assert_eq!(local_name.range(), expected_name.syntax().range());
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs
index 4d6378e02..c7391ee05 100644
--- a/crates/ra_hir/src/ids.rs
+++ b/crates/ra_hir/src/ids.rs
@@ -2,7 +2,9 @@ use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId};
2use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast}; 2use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast};
3use ra_arena::{Arena, RawId, impl_arena_id}; 3use ra_arena::{Arena, RawId, impl_arena_id};
4 4
5use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum, ImplBlock, Crate}; 5use crate::{HirDatabase, PerNs, ModuleId, Def, Function, Struct, Enum, ImplBlock, Crate};
6
7use crate::code_model_api::Module;
6 8
7/// hir makes a heavy use of ids: integer (u32) handlers to various things. You 9/// hir makes a heavy use of ids: integer (u32) handlers to various things. You
8/// can think of id as a pointer (but without a lifetime) or a file descriptor 10/// can think of id as a pointer (but without a lifetime) or a file descriptor
@@ -151,7 +153,7 @@ impl DefId {
151 let loc = self.loc(db); 153 let loc = self.loc(db);
152 let res = match loc.kind { 154 let res = match loc.kind {
153 DefKind::Module => { 155 DefKind::Module => {
154 let module = Module::new(db, loc.source_root_id, loc.module_id)?; 156 let module = Module::from_module_id(db, loc.source_root_id, loc.module_id)?;
155 Def::Module(module) 157 Def::Module(module)
156 } 158 }
157 DefKind::Function => { 159 DefKind::Function => {
@@ -175,12 +177,12 @@ impl DefId {
175 /// For a module, returns that module; for any other def, returns the containing module. 177 /// For a module, returns that module; for any other def, returns the containing module.
176 pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> { 178 pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> {
177 let loc = self.loc(db); 179 let loc = self.loc(db);
178 Module::new(db, loc.source_root_id, loc.module_id) 180 Module::from_module_id(db, loc.source_root_id, loc.module_id)
179 } 181 }
180 182
181 /// Returns the containing crate. 183 /// Returns the containing crate.
182 pub fn krate(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> { 184 pub fn krate(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> {
183 Ok(self.module(db)?.krate(db)) 185 Ok(self.module(db)?.krate(db)?)
184 } 186 }
185 187
186 /// Returns the containing impl block, if this is an impl item. 188 /// Returns the containing impl block, if this is an impl item.
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs
index 01afa84c4..7ce8d17e6 100644
--- a/crates/ra_hir/src/impl_block.rs
+++ b/crates/ra_hir/src/impl_block.rs
@@ -7,12 +7,14 @@ use ra_db::{LocationIntener, Cancelable, SourceRootId};
7 7
8use crate::{ 8use crate::{
9 DefId, DefLoc, DefKind, SourceItemId, SourceFileItems, 9 DefId, DefLoc, DefKind, SourceItemId, SourceFileItems,
10 Module, Function, 10 Function,
11 db::HirDatabase, 11 db::HirDatabase,
12 type_ref::TypeRef, 12 type_ref::TypeRef,
13 module::{ModuleSourceNode, ModuleId}, 13 module_tree::ModuleId,
14}; 14};
15 15
16use crate::code_model_api::{Module, ModuleSource};
17
16#[derive(Debug, Clone, PartialEq, Eq)] 18#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct ImplBlock { 19pub struct ImplBlock {
18 module_impl_blocks: Arc<ModuleImplBlocks>, 20 module_impl_blocks: Arc<ModuleImplBlocks>,
@@ -64,7 +66,7 @@ impl ImplData {
64 ) -> Self { 66 ) -> Self {
65 let target_trait = node.target_type().map(TypeRef::from_ast); 67 let target_trait = node.target_type().map(TypeRef::from_ast);
66 let target_type = TypeRef::from_ast_opt(node.target_type()); 68 let target_type = TypeRef::from_ast_opt(node.target_type());
67 let file_id = module.source().file_id(); 69 let module_loc = module.def_id.loc(db);
68 let items = if let Some(item_list) = node.item_list() { 70 let items = if let Some(item_list) = node.item_list() {
69 item_list 71 item_list
70 .impl_items() 72 .impl_items()
@@ -75,14 +77,14 @@ impl ImplData {
75 ast::ImplItem::TypeDef(..) => DefKind::Item, 77 ast::ImplItem::TypeDef(..) => DefKind::Item,
76 }; 78 };
77 let item_id = file_items.id_of_unchecked(item_node.syntax()); 79 let item_id = file_items.id_of_unchecked(item_node.syntax());
80 let source_item_id = SourceItemId {
81 file_id: module_loc.source_item_id.file_id,
82 item_id: Some(item_id),
83 };
78 let def_loc = DefLoc { 84 let def_loc = DefLoc {
79 kind, 85 kind,
80 source_root_id: module.source_root_id, 86 source_item_id,
81 module_id: module.module_id, 87 ..module_loc
82 source_item_id: SourceItemId {
83 file_id,
84 item_id: Some(item_id),
85 },
86 }; 88 };
87 let def_id = def_loc.id(db); 89 let def_id = def_loc.id(db);
88 match item_node { 90 match item_node {
@@ -148,13 +150,13 @@ impl ModuleImplBlocks {
148 } 150 }
149 151
150 fn collect(&mut self, db: &impl HirDatabase, module: Module) -> Cancelable<()> { 152 fn collect(&mut self, db: &impl HirDatabase, module: Module) -> Cancelable<()> {
151 let module_source_node = module.source().resolve(db); 153 let (file_id, module_source) = module.defenition_source(db)?;
152 let node = match &module_source_node { 154 let node = match &module_source {
153 ModuleSourceNode::SourceFile(node) => node.borrowed().syntax(), 155 ModuleSource::SourceFile(node) => node.borrowed().syntax(),
154 ModuleSourceNode::Module(node) => node.borrowed().syntax(), 156 ModuleSource::Module(node) => node.borrowed().syntax(),
155 }; 157 };
156 158
157 let source_file_items = db.file_items(module.source().file_id()); 159 let source_file_items = db.file_items(file_id.into());
158 160
159 for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) { 161 for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) {
160 let impl_block = ImplData::from_ast(db, &source_file_items, &module, impl_block_ast); 162 let impl_block = ImplData::from_ast(db, &source_file_items, &module, impl_block_ast);
@@ -174,7 +176,7 @@ pub(crate) fn impls_in_module(
174 module_id: ModuleId, 176 module_id: ModuleId,
175) -> Cancelable<Arc<ModuleImplBlocks>> { 177) -> Cancelable<Arc<ModuleImplBlocks>> {
176 let mut result = ModuleImplBlocks::new(); 178 let mut result = ModuleImplBlocks::new();
177 let module = Module::new(db, source_root_id, module_id)?; 179 let module = Module::from_module_id(db, source_root_id, module_id)?;
178 result.collect(db, module)?; 180 result.collect(db, module)?;
179 Ok(Arc::new(result)) 181 Ok(Arc::new(result))
180} 182}
diff --git a/crates/ra_hir/src/krate.rs b/crates/ra_hir/src/krate.rs
deleted file mode 100644
index 5194e280b..000000000
--- a/crates/ra_hir/src/krate.rs
+++ /dev/null
@@ -1,48 +0,0 @@
1pub use ra_db::{CrateId, Cancelable};
2
3use crate::{HirDatabase, Module, Name, AsName, HirFileId};
4
5/// hir::Crate describes a single crate. It's the main inteface with which
6/// crate's dependencies interact. Mostly, it should be just a proxy for the
7/// root module.
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub struct Crate {
10 crate_id: CrateId,
11}
12
13#[derive(Debug)]
14pub struct CrateDependency {
15 pub krate: Crate,
16 pub name: Name,
17}
18
19impl Crate {
20 pub(crate) fn new(crate_id: CrateId) -> Crate {
21 Crate { crate_id }
22 }
23 pub fn dependencies(&self, db: &impl HirDatabase) -> Vec<CrateDependency> {
24 let crate_graph = db.crate_graph();
25 crate_graph
26 .dependencies(self.crate_id)
27 .map(|dep| {
28 let krate = Crate::new(dep.crate_id());
29 let name = dep.as_name();
30 CrateDependency { krate, name }
31 })
32 .collect()
33 }
34 pub fn root_module(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
35 let crate_graph = db.crate_graph();
36 let file_id = crate_graph.crate_root(self.crate_id);
37 let source_root_id = db.file_source_root(file_id);
38 let file_id = HirFileId::from(file_id);
39 let module_tree = db.module_tree(source_root_id)?;
40 // FIXME: teach module tree about crate roots instead of guessing
41 let (module_id, _) = ctry!(module_tree
42 .modules_with_sources()
43 .find(|(_, src)| src.file_id() == file_id));
44
45 let module = Module::new(db, source_root_id, module_id)?;
46 Ok(Some(module))
47 }
48}
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 2abcec441..9f133f174 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -24,14 +24,17 @@ pub mod source_binder;
24mod ids; 24mod ids;
25mod macros; 25mod macros;
26mod name; 26mod name;
27// can't use `crate` or `r#crate` here :( 27mod module_tree;
28mod krate; 28mod nameres;
29mod module;
30mod function; 29mod function;
31mod adt; 30mod adt;
32mod type_ref; 31mod type_ref;
33mod ty; 32mod ty;
34mod impl_block; 33mod impl_block;
34mod expr;
35
36mod code_model_api;
37mod code_model_impl;
35 38
36use crate::{ 39use crate::{
37 db::HirDatabase, 40 db::HirDatabase,
@@ -42,11 +45,11 @@ use crate::{
42pub use self::{ 45pub use self::{
43 path::{Path, PathKind}, 46 path::{Path, PathKind},
44 name::Name, 47 name::Name,
45 krate::Crate,
46 ids::{HirFileId, DefId, DefLoc, MacroCallId, MacroCallLoc}, 48 ids::{HirFileId, DefId, DefLoc, MacroCallId, MacroCallLoc},
47 macros::{MacroDef, MacroInput, MacroExpansion}, 49 macros::{MacroDef, MacroInput, MacroExpansion},
48 module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, 50 module_tree::ModuleId,
49 function::{Function, FnScopes}, 51 nameres::{ItemMap, PerNs, Namespace, Resolution},
52 function::{Function, FnSignature, FnScopes, ScopesWithSyntaxMapping},
50 adt::{Struct, Enum}, 53 adt::{Struct, Enum},
51 ty::Ty, 54 ty::Ty,
52 impl_block::{ImplBlock, ImplItem}, 55 impl_block::{ImplBlock, ImplItem},
@@ -54,6 +57,11 @@ pub use self::{
54 57
55pub use self::function::FnSignatureInfo; 58pub use self::function::FnSignatureInfo;
56 59
60pub use self::code_model_api::{
61 Crate, CrateDependency,
62 Module, ModuleSource, Problem,
63};
64
57pub enum Def { 65pub enum Def {
58 Module(Module), 66 Module(Module),
59 Function(Function), 67 Function(Function),
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index a9db932ff..8d176662c 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -208,6 +208,9 @@ salsa::database_storage! {
208 fn struct_data() for db::StructDataQuery; 208 fn struct_data() for db::StructDataQuery;
209 fn enum_data() for db::EnumDataQuery; 209 fn enum_data() for db::EnumDataQuery;
210 fn impls_in_module() for db::ImplsInModuleQuery; 210 fn impls_in_module() for db::ImplsInModuleQuery;
211 fn body_hir() for db::BodyHirQuery;
212 fn body_syntax_mapping() for db::BodySyntaxMappingQuery;
213 fn fn_signature() for db::FnSignatureQuery;
211 } 214 }
212 } 215 }
213} 216}
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs
deleted file mode 100644
index b9821115c..000000000
--- a/crates/ra_hir/src/module.rs
+++ /dev/null
@@ -1,376 +0,0 @@
1pub(super) mod imp;
2pub(super) mod nameres;
3
4use std::sync::Arc;
5use log;
6
7use ra_syntax::{
8 algo::generate,
9 ast::{self, AstNode, NameOwner},
10 SyntaxNode,
11};
12use ra_arena::{Arena, RawId, impl_arena_id};
13use ra_db::{SourceRootId, FileId, Cancelable};
14use relative_path::RelativePathBuf;
15
16use crate::{
17 Def, DefKind, DefLoc, DefId,
18 Name, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate,
19 HirFileId,
20};
21
22pub use self::nameres::{ModuleScope, Resolution, Namespace, PerNs};
23
24/// `Module` is API entry point to get all the information
25/// about a particular module.
26#[derive(Debug, Clone)]
27pub struct Module {
28 tree: Arc<ModuleTree>,
29 pub(crate) source_root_id: SourceRootId,
30 pub(crate) module_id: ModuleId,
31}
32
33impl Module {
34 pub(super) fn new(
35 db: &impl HirDatabase,
36 source_root_id: SourceRootId,
37 module_id: ModuleId,
38 ) -> Cancelable<Module> {
39 let module_tree = db.module_tree(source_root_id)?;
40 let res = Module {
41 tree: module_tree,
42 source_root_id,
43 module_id,
44 };
45 Ok(res)
46 }
47
48 /// Returns `mod foo;` or `mod foo {}` node whihc declared this module.
49 /// Returns `None` for the root module
50 pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> {
51 let link = self.module_id.parent_link(&self.tree)?;
52 let file_id = link
53 .owner(&self.tree)
54 .source(&self.tree)
55 .file_id()
56 .as_original_file();
57 let src = link.bind_source(&self.tree, db);
58 Some((file_id, src))
59 }
60
61 pub fn file_id(&self) -> FileId {
62 self.source().file_id().as_original_file()
63 }
64
65 /// Parent module. Returns `None` if this is a root module.
66 pub fn parent(&self) -> Option<Module> {
67 let parent_id = self.module_id.parent(&self.tree)?;
68 Some(Module {
69 module_id: parent_id,
70 ..self.clone()
71 })
72 }
73
74 /// Returns an iterator of all children of this module.
75 pub fn children<'a>(&'a self) -> impl Iterator<Item = (Name, Module)> + 'a {
76 self.module_id
77 .children(&self.tree)
78 .map(move |(name, module_id)| {
79 (
80 name,
81 Module {
82 module_id,
83 ..self.clone()
84 },
85 )
86 })
87 }
88
89 /// Returns the crate this module is part of.
90 pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> {
91 let root_id = self.module_id.crate_root(&self.tree);
92 let file_id = root_id.source(&self.tree).file_id().as_original_file();
93 let crate_graph = db.crate_graph();
94 let crate_id = crate_graph.crate_id_for_crate_root(file_id)?;
95 Some(Crate::new(crate_id))
96 }
97
98 /// Returns the all modules on the way to the root.
99 pub fn path_to_root(&self) -> Vec<Module> {
100 generate(Some(self.clone()), move |it| it.parent()).collect::<Vec<Module>>()
101 }
102
103 /// The root of the tree this module is part of
104 pub fn crate_root(&self) -> Module {
105 let root_id = self.module_id.crate_root(&self.tree);
106 Module {
107 module_id: root_id,
108 ..self.clone()
109 }
110 }
111
112 /// `name` is `None` for the crate's root module
113 pub fn name(&self) -> Option<&Name> {
114 let link = self.module_id.parent_link(&self.tree)?;
115 Some(link.name(&self.tree))
116 }
117
118 pub fn def_id(&self, db: &impl HirDatabase) -> DefId {
119 let def_loc = DefLoc {
120 kind: DefKind::Module,
121 source_root_id: self.source_root_id,
122 module_id: self.module_id,
123 source_item_id: self.module_id.source(&self.tree).0,
124 };
125 def_loc.id(db)
126 }
127
128 /// Finds a child module with the specified name.
129 pub fn child(&self, name: &Name) -> Option<Module> {
130 let child_id = self.module_id.child(&self.tree, name)?;
131 Some(Module {
132 module_id: child_id,
133 ..self.clone()
134 })
135 }
136
137 /// Returns a `ModuleScope`: a set of items, visible in this module.
138 pub fn scope(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> {
139 let item_map = db.item_map(self.source_root_id)?;
140 let res = item_map.per_module[&self.module_id].clone();
141 Ok(res)
142 }
143
144 pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> Cancelable<PerNs<DefId>> {
145 let mut curr_per_ns = PerNs::types(
146 match path.kind {
147 PathKind::Crate => self.crate_root(),
148 PathKind::Self_ | PathKind::Plain => self.clone(),
149 PathKind::Super => {
150 if let Some(p) = self.parent() {
151 p
152 } else {
153 return Ok(PerNs::none());
154 }
155 }
156 }
157 .def_id(db),
158 );
159
160 let segments = &path.segments;
161 for name in segments.iter() {
162 let curr = if let Some(r) = curr_per_ns.as_ref().take(Namespace::Types) {
163 r
164 } else {
165 return Ok(PerNs::none());
166 };
167 let module = match curr.resolve(db)? {
168 Def::Module(it) => it,
169 // TODO here would be the place to handle enum variants...
170 _ => return Ok(PerNs::none()),
171 };
172 let scope = module.scope(db)?;
173 curr_per_ns = if let Some(r) = scope.get(&name) {
174 r.def_id
175 } else {
176 return Ok(PerNs::none());
177 };
178 }
179 Ok(curr_per_ns)
180 }
181
182 pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
183 self.module_id.problems(&self.tree, db)
184 }
185
186 pub(crate) fn source(&self) -> ModuleSource {
187 self.module_id.source(&self.tree)
188 }
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
192pub struct ModuleId(RawId);
193impl_arena_id!(ModuleId);
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
196pub struct LinkId(RawId);
197impl_arena_id!(LinkId);
198
199/// Physically, rust source is organized as a set of files, but logically it is
200/// organized as a tree of modules. Usually, a single file corresponds to a
201/// single module, but it is not nessary the case.
202///
203/// Module encapsulate the logic of transitioning from the fuzzy world of files
204/// (which can have multiple parents) to the precise world of modules (which
205/// always have one parent).
206#[derive(Default, Debug, PartialEq, Eq)]
207pub struct ModuleTree {
208 mods: Arena<ModuleId, ModuleData>,
209 links: Arena<LinkId, LinkData>,
210}
211
212impl ModuleTree {
213 pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
214 self.mods.iter().map(|(id, _)| id)
215 }
216
217 pub(crate) fn modules_with_sources<'a>(
218 &'a self,
219 ) -> impl Iterator<Item = (ModuleId, ModuleSource)> + 'a {
220 self.mods.iter().map(|(id, m)| (id, m.source))
221 }
222}
223
224/// `ModuleSource` is the syntax tree element that produced this module:
225/// either a file, or an inlinde module.
226#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
227pub struct ModuleSource(SourceItemId);
228
229/// An owned syntax node for a module. Unlike `ModuleSource`,
230/// this holds onto the AST for the whole file.
231pub(crate) enum ModuleSourceNode {
232 SourceFile(ast::SourceFileNode),
233 Module(ast::ModuleNode),
234}
235
236#[derive(Clone, Debug, Hash, PartialEq, Eq)]
237pub enum Problem {
238 UnresolvedModule {
239 candidate: RelativePathBuf,
240 },
241 NotDirOwner {
242 move_to: RelativePathBuf,
243 candidate: RelativePathBuf,
244 },
245}
246
247impl ModuleId {
248 pub(crate) fn source(self, tree: &ModuleTree) -> ModuleSource {
249 tree.mods[self].source
250 }
251 fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
252 tree.mods[self].parent
253 }
254 fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
255 let link = self.parent_link(tree)?;
256 Some(tree.links[link].owner)
257 }
258 fn crate_root(self, tree: &ModuleTree) -> ModuleId {
259 generate(Some(self), move |it| it.parent(tree))
260 .last()
261 .unwrap()
262 }
263 fn child(self, tree: &ModuleTree, name: &Name) -> Option<ModuleId> {
264 let link = tree.mods[self]
265 .children
266 .iter()
267 .map(|&it| &tree.links[it])
268 .find(|it| it.name == *name)?;
269 Some(*link.points_to.first()?)
270 }
271 fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator<Item = (Name, ModuleId)> + 'a {
272 tree.mods[self].children.iter().filter_map(move |&it| {
273 let link = &tree.links[it];
274 let module = *link.points_to.first()?;
275 Some((link.name.clone(), module))
276 })
277 }
278 fn problems(self, tree: &ModuleTree, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> {
279 tree.mods[self]
280 .children
281 .iter()
282 .filter_map(|&it| {
283 let p = tree.links[it].problem.clone()?;
284 let s = it.bind_source(tree, db);
285 let s = s.borrowed().name().unwrap().syntax().owned();
286 Some((s, p))
287 })
288 .collect()
289 }
290}
291
292impl LinkId {
293 fn owner(self, tree: &ModuleTree) -> ModuleId {
294 tree.links[self].owner
295 }
296 fn name(self, tree: &ModuleTree) -> &Name {
297 &tree.links[self].name
298 }
299 fn bind_source<'a>(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode {
300 let owner = self.owner(tree);
301 match owner.source(tree).resolve(db) {
302 ModuleSourceNode::SourceFile(root) => {
303 let ast = imp::modules(root.borrowed())
304 .find(|(name, _)| name == &tree.links[self].name)
305 .unwrap()
306 .1;
307 ast.owned()
308 }
309 ModuleSourceNode::Module(it) => it,
310 }
311 }
312}
313
314#[derive(Debug, PartialEq, Eq, Hash)]
315pub struct ModuleData {
316 source: ModuleSource,
317 parent: Option<LinkId>,
318 children: Vec<LinkId>,
319}
320
321impl ModuleSource {
322 // precondition: item_id **must** point to module
323 fn new(file_id: HirFileId, item_id: Option<SourceFileItemId>) -> ModuleSource {
324 let source_item_id = SourceItemId { file_id, item_id };
325 ModuleSource(source_item_id)
326 }
327
328 pub(crate) fn new_file(file_id: HirFileId) -> ModuleSource {
329 ModuleSource::new(file_id, None)
330 }
331
332 pub(crate) fn new_inline(
333 db: &impl HirDatabase,
334 file_id: HirFileId,
335 m: ast::Module,
336 ) -> ModuleSource {
337 assert!(!m.has_semi());
338 let file_items = db.file_items(file_id);
339 let item_id = file_items.id_of(file_id, m.syntax());
340 ModuleSource::new(file_id, Some(item_id))
341 }
342
343 pub(crate) fn file_id(self) -> HirFileId {
344 self.0.file_id
345 }
346
347 pub(crate) fn resolve(self, db: &impl HirDatabase) -> ModuleSourceNode {
348 let syntax_node = db.file_item(self.0);
349 let syntax_node = syntax_node.borrowed();
350 if let Some(file) = ast::SourceFile::cast(syntax_node) {
351 return ModuleSourceNode::SourceFile(file.owned());
352 }
353 let module = ast::Module::cast(syntax_node).unwrap();
354 ModuleSourceNode::Module(module.owned())
355 }
356}
357
358#[derive(Hash, Debug, PartialEq, Eq)]
359struct LinkData {
360 owner: ModuleId,
361 name: Name,
362 points_to: Vec<ModuleId>,
363 problem: Option<Problem>,
364}
365
366impl ModuleTree {
367 fn push_mod(&mut self, data: ModuleData) -> ModuleId {
368 self.mods.alloc(data)
369 }
370 fn push_link(&mut self, data: LinkData) -> LinkId {
371 let owner = data.owner;
372 let id = self.links.alloc(data);
373 self.mods[owner].children.push(id);
374 id
375 }
376}
diff --git a/crates/ra_hir/src/module/imp.rs b/crates/ra_hir/src/module/imp.rs
deleted file mode 100644
index 3849026db..000000000
--- a/crates/ra_hir/src/module/imp.rs
+++ /dev/null
@@ -1,190 +0,0 @@
1use std::sync::Arc;
2
3use ra_syntax::ast::{self, NameOwner};
4use relative_path::RelativePathBuf;
5use rustc_hash::{FxHashMap, FxHashSet};
6use arrayvec::ArrayVec;
7use ra_db::{SourceRoot, SourceRootId, Cancelable, FileId};
8
9use crate::{
10 HirDatabase, Name, AsName,
11};
12
13use super::{
14 LinkData, LinkId, ModuleData, ModuleId, ModuleSource,
15 ModuleTree, Problem,
16};
17
18#[derive(Clone, Hash, PartialEq, Eq, Debug)]
19pub enum Submodule {
20 Declaration(Name),
21 Definition(Name, ModuleSource),
22}
23
24impl Submodule {
25 fn name(&self) -> &Name {
26 match self {
27 Submodule::Declaration(name) => name,
28 Submodule::Definition(name, _) => name,
29 }
30 }
31}
32
33pub(crate) fn modules<'a>(
34 root: impl ast::ModuleItemOwner<'a>,
35) -> impl Iterator<Item = (Name, ast::Module<'a>)> {
36 root.items()
37 .filter_map(|item| match item {
38 ast::ModuleItem::Module(m) => Some(m),
39 _ => None,
40 })
41 .filter_map(|module| {
42 let name = module.name()?.as_name();
43 Some((name, module))
44 })
45}
46
47pub(crate) fn module_tree(
48 db: &impl HirDatabase,
49 source_root: SourceRootId,
50) -> Cancelable<Arc<ModuleTree>> {
51 db.check_canceled()?;
52 let res = create_module_tree(db, source_root)?;
53 Ok(Arc::new(res))
54}
55
56fn create_module_tree<'a>(
57 db: &impl HirDatabase,
58 source_root: SourceRootId,
59) -> Cancelable<ModuleTree> {
60 let mut tree = ModuleTree::default();
61
62 let mut roots = FxHashMap::default();
63 let mut visited = FxHashSet::default();
64
65 let source_root = db.source_root(source_root);
66 for &file_id in source_root.files.values() {
67 let source = ModuleSource::new_file(file_id.into());
68 if visited.contains(&source) {
69 continue; // TODO: use explicit crate_roots here
70 }
71 assert!(!roots.contains_key(&file_id));
72 let module_id = build_subtree(
73 db,
74 &source_root,
75 &mut tree,
76 &mut visited,
77 &mut roots,
78 None,
79 source,
80 )?;
81 roots.insert(file_id, module_id);
82 }
83 Ok(tree)
84}
85
86fn build_subtree(
87 db: &impl HirDatabase,
88 source_root: &SourceRoot,
89 tree: &mut ModuleTree,
90 visited: &mut FxHashSet<ModuleSource>,
91 roots: &mut FxHashMap<FileId, ModuleId>,
92 parent: Option<LinkId>,
93 source: ModuleSource,
94) -> Cancelable<ModuleId> {
95 visited.insert(source);
96 let id = tree.push_mod(ModuleData {
97 source,
98 parent,
99 children: Vec::new(),
100 });
101 for sub in db.submodules(source)?.iter() {
102 let link = tree.push_link(LinkData {
103 name: sub.name().clone(),
104 owner: id,
105 points_to: Vec::new(),
106 problem: None,
107 });
108
109 let (points_to, problem) = match sub {
110 Submodule::Declaration(name) => {
111 let (points_to, problem) = resolve_submodule(db, source, &name);
112 let points_to = points_to
113 .into_iter()
114 .map(|file_id| match roots.remove(&file_id) {
115 Some(module_id) => {
116 tree.mods[module_id].parent = Some(link);
117 Ok(module_id)
118 }
119 None => build_subtree(
120 db,
121 source_root,
122 tree,
123 visited,
124 roots,
125 Some(link),
126 ModuleSource::new_file(file_id.into()),
127 ),
128 })
129 .collect::<Cancelable<Vec<_>>>()?;
130 (points_to, problem)
131 }
132 Submodule::Definition(_name, submodule_source) => {
133 let points_to = build_subtree(
134 db,
135 source_root,
136 tree,
137 visited,
138 roots,
139 Some(link),
140 *submodule_source,
141 )?;
142 (vec![points_to], None)
143 }
144 };
145
146 tree.links[link].points_to = points_to;
147 tree.links[link].problem = problem;
148 }
149 Ok(id)
150}
151
152fn resolve_submodule(
153 db: &impl HirDatabase,
154 source: ModuleSource,
155 name: &Name,
156) -> (Vec<FileId>, Option<Problem>) {
157 // FIXME: handle submodules of inline modules properly
158 let file_id = source.file_id().original_file(db);
159 let source_root_id = db.file_source_root(file_id);
160 let path = db.file_relative_path(file_id);
161 let root = RelativePathBuf::default();
162 let dir_path = path.parent().unwrap_or(&root);
163 let mod_name = path.file_stem().unwrap_or("unknown");
164 let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
165
166 let file_mod = dir_path.join(format!("{}.rs", name));
167 let dir_mod = dir_path.join(format!("{}/mod.rs", name));
168 let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name));
169 let mut candidates = ArrayVec::<[_; 2]>::new();
170 if is_dir_owner {
171 candidates.push(file_mod.clone());
172 candidates.push(dir_mod);
173 } else {
174 candidates.push(file_dir_mod.clone());
175 };
176 let sr = db.source_root(source_root_id);
177 let points_to = candidates
178 .into_iter()
179 .filter_map(|path| sr.files.get(&path))
180 .map(|&it| it)
181 .collect::<Vec<_>>();
182 let problem = if points_to.is_empty() {
183 Some(Problem::UnresolvedModule {
184 candidate: if is_dir_owner { file_mod } else { file_dir_mod },
185 })
186 } else {
187 None
188 };
189 (points_to, problem)
190}
diff --git a/crates/ra_hir/src/module_tree.rs b/crates/ra_hir/src/module_tree.rs
new file mode 100644
index 000000000..c7a442319
--- /dev/null
+++ b/crates/ra_hir/src/module_tree.rs
@@ -0,0 +1,368 @@
1use std::sync::Arc;
2
3use rustc_hash::{FxHashMap, FxHashSet};
4use arrayvec::ArrayVec;
5use relative_path::RelativePathBuf;
6use ra_db::{FileId, SourceRootId, Cancelable, SourceRoot};
7use ra_syntax::{
8 algo::generate,
9 ast::{self, AstNode, NameOwner},
10 SyntaxNode,
11};
12use ra_arena::{Arena, RawId, impl_arena_id};
13
14use crate::{Name, AsName, HirDatabase, SourceItemId, HirFileId, Problem, SourceFileItems, ModuleSource};
15
16impl ModuleSource {
17 pub fn from_source_item_id(
18 db: &impl HirDatabase,
19 source_item_id: SourceItemId,
20 ) -> ModuleSource {
21 let module_syntax = db.file_item(source_item_id);
22 let module_syntax = module_syntax.borrowed();
23 if let Some(source_file) = ast::SourceFile::cast(module_syntax) {
24 ModuleSource::SourceFile(source_file.owned())
25 } else if let Some(module) = ast::Module::cast(module_syntax) {
26 assert!(module.item_list().is_some(), "expected inline module");
27 ModuleSource::Module(module.owned())
28 } else {
29 panic!("expected file or inline module")
30 }
31 }
32}
33
34#[derive(Clone, Hash, PartialEq, Eq, Debug)]
35pub struct Submodule {
36 name: Name,
37 is_declaration: bool,
38 source: SourceItemId,
39}
40
41impl Submodule {
42 pub(crate) fn submodules_query(
43 db: &impl HirDatabase,
44 source: SourceItemId,
45 ) -> Cancelable<Arc<Vec<Submodule>>> {
46 db.check_canceled()?;
47 let file_id = source.file_id;
48 let file_items = db.file_items(file_id);
49 let module_source = ModuleSource::from_source_item_id(db, source);
50 let submodules = match module_source {
51 ModuleSource::SourceFile(source_file) => {
52 collect_submodules(file_id, &file_items, source_file.borrowed())
53 }
54 ModuleSource::Module(module) => {
55 let module = module.borrowed();
56 collect_submodules(file_id, &file_items, module.item_list().unwrap())
57 }
58 };
59 return Ok(Arc::new(submodules));
60
61 fn collect_submodules<'a>(
62 file_id: HirFileId,
63 file_items: &SourceFileItems,
64 root: impl ast::ModuleItemOwner<'a>,
65 ) -> Vec<Submodule> {
66 modules(root)
67 .map(|(name, m)| Submodule {
68 name,
69 is_declaration: m.has_semi(),
70 source: SourceItemId {
71 file_id,
72 item_id: Some(file_items.id_of(file_id, m.syntax())),
73 },
74 })
75 .collect()
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
81pub struct ModuleId(RawId);
82impl_arena_id!(ModuleId);
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
85pub struct LinkId(RawId);
86impl_arena_id!(LinkId);
87
88/// Physically, rust source is organized as a set of files, but logically it is
89/// organized as a tree of modules. Usually, a single file corresponds to a
90/// single module, but it is not nessary the case.
91///
92/// Module encapsulate the logic of transitioning from the fuzzy world of files
93/// (which can have multiple parents) to the precise world of modules (which
94/// always have one parent).
95#[derive(Default, Debug, PartialEq, Eq)]
96pub struct ModuleTree {
97 mods: Arena<ModuleId, ModuleData>,
98 links: Arena<LinkId, LinkData>,
99}
100
101#[derive(Debug, PartialEq, Eq, Hash)]
102pub struct ModuleData {
103 source: SourceItemId,
104 parent: Option<LinkId>,
105 children: Vec<LinkId>,
106}
107
108#[derive(Hash, Debug, PartialEq, Eq)]
109struct LinkData {
110 source: SourceItemId,
111 owner: ModuleId,
112 name: Name,
113 points_to: Vec<ModuleId>,
114 problem: Option<Problem>,
115}
116
117impl ModuleTree {
118 pub(crate) fn module_tree_query(
119 db: &impl HirDatabase,
120 source_root: SourceRootId,
121 ) -> Cancelable<Arc<ModuleTree>> {
122 db.check_canceled()?;
123 let res = create_module_tree(db, source_root)?;
124 Ok(Arc::new(res))
125 }
126
127 pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
128 self.mods.iter().map(|(id, _)| id)
129 }
130
131 pub(crate) fn find_module_by_source(&self, source: SourceItemId) -> Option<ModuleId> {
132 let (res, _) = self.mods.iter().find(|(_, m)| m.source == source)?;
133 Some(res)
134 }
135}
136
137impl ModuleId {
138 pub(crate) fn source(self, tree: &ModuleTree) -> SourceItemId {
139 tree.mods[self].source
140 }
141 pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
142 tree.mods[self].parent
143 }
144 pub(crate) fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
145 let link = self.parent_link(tree)?;
146 Some(tree.links[link].owner)
147 }
148 pub(crate) fn crate_root(self, tree: &ModuleTree) -> ModuleId {
149 generate(Some(self), move |it| it.parent(tree))
150 .last()
151 .unwrap()
152 }
153 pub(crate) fn child(self, tree: &ModuleTree, name: &Name) -> Option<ModuleId> {
154 let link = tree.mods[self]
155 .children
156 .iter()
157 .map(|&it| &tree.links[it])
158 .find(|it| it.name == *name)?;
159 Some(*link.points_to.first()?)
160 }
161 pub(crate) fn children<'a>(
162 self,
163 tree: &'a ModuleTree,
164 ) -> impl Iterator<Item = (Name, ModuleId)> + 'a {
165 tree.mods[self].children.iter().filter_map(move |&it| {
166 let link = &tree.links[it];
167 let module = *link.points_to.first()?;
168 Some((link.name.clone(), module))
169 })
170 }
171 pub(crate) fn problems(
172 self,
173 tree: &ModuleTree,
174 db: &impl HirDatabase,
175 ) -> Vec<(SyntaxNode, Problem)> {
176 tree.mods[self]
177 .children
178 .iter()
179 .filter_map(|&link| {
180 let p = tree.links[link].problem.clone()?;
181 let s = link.source(tree, db);
182 let s = s.borrowed().name().unwrap().syntax().owned();
183 Some((s, p))
184 })
185 .collect()
186 }
187}
188
189impl LinkId {
190 pub(crate) fn owner(self, tree: &ModuleTree) -> ModuleId {
191 tree.links[self].owner
192 }
193 pub(crate) fn name(self, tree: &ModuleTree) -> &Name {
194 &tree.links[self].name
195 }
196 pub(crate) fn source(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode {
197 let syntax_node = db.file_item(tree.links[self].source);
198 ast::ModuleNode::cast(syntax_node.borrowed())
199 .unwrap()
200 .owned()
201 }
202}
203
204impl ModuleTree {
205 fn push_mod(&mut self, data: ModuleData) -> ModuleId {
206 self.mods.alloc(data)
207 }
208 fn push_link(&mut self, data: LinkData) -> LinkId {
209 let owner = data.owner;
210 let id = self.links.alloc(data);
211 self.mods[owner].children.push(id);
212 id
213 }
214}
215
216fn modules<'a>(
217 root: impl ast::ModuleItemOwner<'a>,
218) -> impl Iterator<Item = (Name, ast::Module<'a>)> {
219 root.items()
220 .filter_map(|item| match item {
221 ast::ModuleItem::Module(m) => Some(m),
222 _ => None,
223 })
224 .filter_map(|module| {
225 let name = module.name()?.as_name();
226 Some((name, module))
227 })
228}
229
230fn create_module_tree<'a>(
231 db: &impl HirDatabase,
232 source_root: SourceRootId,
233) -> Cancelable<ModuleTree> {
234 let mut tree = ModuleTree::default();
235
236 let mut roots = FxHashMap::default();
237 let mut visited = FxHashSet::default();
238
239 let source_root = db.source_root(source_root);
240 for &file_id in source_root.files.values() {
241 let source = SourceItemId {
242 file_id: file_id.into(),
243 item_id: None,
244 };
245 if visited.contains(&source) {
246 continue; // TODO: use explicit crate_roots here
247 }
248 assert!(!roots.contains_key(&file_id));
249 let module_id = build_subtree(
250 db,
251 &source_root,
252 &mut tree,
253 &mut visited,
254 &mut roots,
255 None,
256 source,
257 )?;
258 roots.insert(file_id, module_id);
259 }
260 Ok(tree)
261}
262
263fn build_subtree(
264 db: &impl HirDatabase,
265 source_root: &SourceRoot,
266 tree: &mut ModuleTree,
267 visited: &mut FxHashSet<SourceItemId>,
268 roots: &mut FxHashMap<FileId, ModuleId>,
269 parent: Option<LinkId>,
270 source: SourceItemId,
271) -> Cancelable<ModuleId> {
272 visited.insert(source);
273 let id = tree.push_mod(ModuleData {
274 source,
275 parent,
276 children: Vec::new(),
277 });
278 for sub in db.submodules(source)?.iter() {
279 let link = tree.push_link(LinkData {
280 source: sub.source,
281 name: sub.name.clone(),
282 owner: id,
283 points_to: Vec::new(),
284 problem: None,
285 });
286
287 let (points_to, problem) = if sub.is_declaration {
288 let (points_to, problem) = resolve_submodule(db, source.file_id, &sub.name);
289 let points_to = points_to
290 .into_iter()
291 .map(|file_id| match roots.remove(&file_id) {
292 Some(module_id) => {
293 tree.mods[module_id].parent = Some(link);
294 Ok(module_id)
295 }
296 None => build_subtree(
297 db,
298 source_root,
299 tree,
300 visited,
301 roots,
302 Some(link),
303 SourceItemId {
304 file_id: file_id.into(),
305 item_id: None,
306 },
307 ),
308 })
309 .collect::<Cancelable<Vec<_>>>()?;
310 (points_to, problem)
311 } else {
312 let points_to = build_subtree(
313 db,
314 source_root,
315 tree,
316 visited,
317 roots,
318 Some(link),
319 sub.source,
320 )?;
321 (vec![points_to], None)
322 };
323
324 tree.links[link].points_to = points_to;
325 tree.links[link].problem = problem;
326 }
327 Ok(id)
328}
329
330fn resolve_submodule(
331 db: &impl HirDatabase,
332 file_id: HirFileId,
333 name: &Name,
334) -> (Vec<FileId>, Option<Problem>) {
335 // FIXME: handle submodules of inline modules properly
336 let file_id = file_id.original_file(db);
337 let source_root_id = db.file_source_root(file_id);
338 let path = db.file_relative_path(file_id);
339 let root = RelativePathBuf::default();
340 let dir_path = path.parent().unwrap_or(&root);
341 let mod_name = path.file_stem().unwrap_or("unknown");
342 let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
343
344 let file_mod = dir_path.join(format!("{}.rs", name));
345 let dir_mod = dir_path.join(format!("{}/mod.rs", name));
346 let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name));
347 let mut candidates = ArrayVec::<[_; 2]>::new();
348 if is_dir_owner {
349 candidates.push(file_mod.clone());
350 candidates.push(dir_mod);
351 } else {
352 candidates.push(file_dir_mod.clone());
353 };
354 let sr = db.source_root(source_root_id);
355 let points_to = candidates
356 .into_iter()
357 .filter_map(|path| sr.files.get(&path))
358 .map(|&it| it)
359 .collect::<Vec<_>>();
360 let problem = if points_to.is_empty() {
361 Some(Problem::UnresolvedModule {
362 candidate: if is_dir_owner { file_mod } else { file_dir_mod },
363 })
364 } else {
365 None
366 };
367 (points_to, problem)
368}
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs
index 017caf442..90229bc54 100644
--- a/crates/ra_hir/src/name.rs
+++ b/crates/ra_hir/src/name.rs
@@ -31,6 +31,14 @@ impl Name {
31 Name::new("[missing name]".into()) 31 Name::new("[missing name]".into())
32 } 32 }
33 33
34 pub(crate) fn self_param() -> Name {
35 Name::new("self".into())
36 }
37
38 pub(crate) fn self_type() -> Name {
39 Name::new("Self".into())
40 }
41
34 pub(crate) fn tuple_field_name(idx: usize) -> Name { 42 pub(crate) fn tuple_field_name(idx: usize) -> Name {
35 Name::new(idx.to_string().into()) 43 Name::new(idx.to_string().into())
36 } 44 }
@@ -51,7 +59,8 @@ impl Name {
51 "u128" => KnownName::U128, 59 "u128" => KnownName::U128,
52 "f32" => KnownName::F32, 60 "f32" => KnownName::F32,
53 "f64" => KnownName::F64, 61 "f64" => KnownName::F64,
54 "Self" => KnownName::Self_, 62 "Self" => KnownName::SelfType,
63 "self" => KnownName::SelfParam,
55 _ => return None, 64 _ => return None,
56 }; 65 };
57 Some(name) 66 Some(name)
@@ -104,5 +113,6 @@ pub(crate) enum KnownName {
104 F32, 113 F32,
105 F64, 114 F64,
106 115
107 Self_, 116 SelfType,
117 SelfParam,
108} 118}
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/nameres.rs
index 3c6851a0a..9a412bc82 100644
--- a/crates/ra_hir/src/module/nameres.rs
+++ b/crates/ra_hir/src/nameres.rs
@@ -31,7 +31,7 @@ use crate::{
31 Path, PathKind, 31 Path, PathKind,
32 HirDatabase, Crate, 32 HirDatabase, Crate,
33 Name, AsName, 33 Name, AsName,
34 module::{Module, ModuleId, ModuleTree}, 34 module_tree::{ModuleId, ModuleTree},
35}; 35};
36 36
37/// Item map is the result of the name resolution. Item map contains, for each 37/// Item map is the result of the name resolution. Item map contains, for each
@@ -177,11 +177,11 @@ impl<T> PerNs<T> {
177 } 177 }
178 178
179 pub fn take_types(self) -> Option<T> { 179 pub fn take_types(self) -> Option<T> {
180 self.types 180 self.take(Namespace::Types)
181 } 181 }
182 182
183 pub fn take_values(self) -> Option<T> { 183 pub fn take_values(self) -> Option<T> {
184 self.values 184 self.take(Namespace::Values)
185 } 185 }
186 186
187 pub fn get(&self, namespace: Namespace) -> Option<&T> { 187 pub fn get(&self, namespace: Namespace) -> Option<&T> {
@@ -339,14 +339,14 @@ where
339 // Populate extern crates prelude 339 // Populate extern crates prelude
340 { 340 {
341 let root_id = module_id.crate_root(&self.module_tree); 341 let root_id = module_id.crate_root(&self.module_tree);
342 let file_id = root_id.source(&self.module_tree).file_id(); 342 let file_id = root_id.source(&self.module_tree).file_id;
343 let crate_graph = self.db.crate_graph(); 343 let crate_graph = self.db.crate_graph();
344 if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file()) 344 if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file())
345 { 345 {
346 let krate = Crate::new(crate_id); 346 let krate = Crate::new(crate_id);
347 for dep in krate.dependencies(self.db) { 347 for dep in krate.dependencies(self.db)? {
348 if let Some(module) = dep.krate.root_module(self.db)? { 348 if let Some(module) = dep.krate.root_module(self.db)? {
349 let def_id = module.def_id(self.db); 349 let def_id = module.def_id;
350 self.add_module_item( 350 self.add_module_item(
351 &mut module_items, 351 &mut module_items,
352 dep.name.clone(), 352 dep.name.clone(),
@@ -399,7 +399,7 @@ where
399 kind: DefKind::Module, 399 kind: DefKind::Module,
400 source_root_id: self.source_root, 400 source_root_id: self.source_root,
401 module_id, 401 module_id,
402 source_item_id: module_id.source(&self.module_tree).0, 402 source_item_id: module_id.source(&self.module_tree),
403 }; 403 };
404 let def_id = def_loc.id(self.db); 404 let def_id = def_loc.id(self.db);
405 self.add_module_item(&mut module_items, name, PerNs::types(def_id)); 405 self.add_module_item(&mut module_items, name, PerNs::types(def_id));
@@ -466,7 +466,7 @@ where
466 if source_root_id == self.source_root { 466 if source_root_id == self.source_root {
467 target_module_id 467 target_module_id
468 } else { 468 } else {
469 let module = Module::new(self.db, source_root_id, target_module_id)?; 469 let module = crate::code_model_api::Module::new(type_def_id);
470 let path = Path { 470 let path = Path {
471 segments: import.path.segments[i + 1..].iter().cloned().collect(), 471 segments: import.path.segments[i + 1..].iter().cloned().collect(),
472 kind: PathKind::Crate, 472 kind: PathKind::Crate,
diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs
index a6a0bea31..dcbe65aec 100644
--- a/crates/ra_hir/src/module/nameres/tests.rs
+++ b/crates/ra_hir/src/nameres/tests.rs
@@ -17,7 +17,7 @@ fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) {
17 let module = hir::source_binder::module_from_position(&db, pos) 17 let module = hir::source_binder::module_from_position(&db, pos)
18 .unwrap() 18 .unwrap()
19 .unwrap(); 19 .unwrap();
20 let module_id = module.module_id; 20 let module_id = module.def_id.loc(&db).module_id;
21 (db.item_map(source_root).unwrap(), module_id) 21 (db.item_map(source_root).unwrap(), module_id)
22} 22}
23 23
@@ -155,7 +155,7 @@ fn item_map_across_crates() {
155 let module = hir::source_binder::module_from_file_id(&db, main_id) 155 let module = hir::source_binder::module_from_file_id(&db, main_id)
156 .unwrap() 156 .unwrap()
157 .unwrap(); 157 .unwrap();
158 let module_id = module.module_id; 158 let module_id = module.def_id.loc(&db).module_id;
159 let item_map = db.item_map(source_root).unwrap(); 159 let item_map = db.item_map(source_root).unwrap();
160 160
161 check_module_item_map( 161 check_module_item_map(
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs
index 9fdfa0d13..dcf4cf8b6 100644
--- a/crates/ra_hir/src/path.rs
+++ b/crates/ra_hir/src/path.rs
@@ -65,6 +65,11 @@ impl Path {
65 } 65 }
66 } 66 }
67 67
68 /// Converts an `ast::NameRef` into a single-identifier `Path`.
69 pub fn from_name_ref(name_ref: ast::NameRef) -> Path {
70 name_ref.as_name().into()
71 }
72
68 /// `true` is this path is a single identifier, like `foo` 73 /// `true` is this path is a single identifier, like `foo`
69 pub fn is_ident(&self) -> bool { 74 pub fn is_ident(&self) -> bool {
70 self.kind == PathKind::Plain && self.segments.len() == 1 75 self.kind == PathKind::Plain && self.segments.len() == 1
@@ -84,6 +89,15 @@ impl Path {
84 } 89 }
85} 90}
86 91
92impl From<Name> for Path {
93 fn from(name: Name) -> Path {
94 Path {
95 kind: PathKind::Plain,
96 segments: vec![name],
97 }
98 }
99}
100
87fn expand_use_tree( 101fn expand_use_tree(
88 prefix: Option<Path>, 102 prefix: Option<Path>,
89 tree: ast::UseTree, 103 tree: ast::UseTree,
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs
index a5d99beda..8f2c40669 100644
--- a/crates/ra_hir/src/query_definitions.rs
+++ b/crates/ra_hir/src/query_definitions.rs
@@ -6,28 +6,24 @@ use std::{
6use rustc_hash::FxHashMap; 6use rustc_hash::FxHashMap;
7use ra_syntax::{ 7use ra_syntax::{
8 AstNode, SyntaxNode, 8 AstNode, SyntaxNode,
9 ast::{self, NameOwner, ModuleItemOwner} 9 ast::{self, ModuleItemOwner}
10}; 10};
11use ra_db::{SourceRootId, Cancelable,}; 11use ra_db::{SourceRootId, Cancelable,};
12 12
13use crate::{ 13use crate::{
14 SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName, HirFileId, 14 SourceFileItems, SourceItemId, DefKind, DefId, HirFileId, ModuleSource,
15 MacroCallLoc, 15 MacroCallLoc,
16 db::HirDatabase, 16 db::HirDatabase,
17 function::FnScopes, 17 function::FnScopes,
18 module::{ 18 module_tree::ModuleId,
19 ModuleSource, ModuleSourceNode, ModuleId, 19 nameres::{InputModuleItems, ItemMap, Resolver},
20 imp::Submodule,
21 nameres::{InputModuleItems, ItemMap, Resolver},
22 },
23 adt::{StructData, EnumData}, 20 adt::{StructData, EnumData},
24}; 21};
25 22
26pub(super) fn fn_scopes(db: &impl HirDatabase, def_id: DefId) -> Arc<FnScopes> { 23pub(super) fn fn_scopes(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<FnScopes>> {
27 let function = Function::new(def_id); 24 let body = db.body_hir(def_id)?;
28 let syntax = function.syntax(db); 25 let res = FnScopes::new(body);
29 let res = FnScopes::new(syntax.borrowed()); 26 Ok(Arc::new(res))
30 Arc::new(res)
31} 27}
32 28
33pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> { 29pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> {
@@ -62,54 +58,6 @@ pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) ->
62 } 58 }
63} 59}
64 60
65pub(crate) fn submodules(
66 db: &impl HirDatabase,
67 source: ModuleSource,
68) -> Cancelable<Arc<Vec<Submodule>>> {
69 db.check_canceled()?;
70 let file_id = source.file_id();
71 let submodules = match source.resolve(db) {
72 ModuleSourceNode::SourceFile(it) => collect_submodules(db, file_id, it.borrowed()),
73 ModuleSourceNode::Module(it) => it
74 .borrowed()
75 .item_list()
76 .map(|it| collect_submodules(db, file_id, it))
77 .unwrap_or_else(Vec::new),
78 };
79 return Ok(Arc::new(submodules));
80
81 fn collect_submodules<'a>(
82 db: &impl HirDatabase,
83 file_id: HirFileId,
84 root: impl ast::ModuleItemOwner<'a>,
85 ) -> Vec<Submodule> {
86 modules(root)
87 .map(|(name, m)| {
88 if m.has_semi() {
89 Submodule::Declaration(name)
90 } else {
91 let src = ModuleSource::new_inline(db, file_id, m);
92 Submodule::Definition(name, src)
93 }
94 })
95 .collect()
96 }
97}
98
99pub(crate) fn modules<'a>(
100 root: impl ast::ModuleItemOwner<'a>,
101) -> impl Iterator<Item = (Name, ast::Module<'a>)> {
102 root.items()
103 .filter_map(|item| match item {
104 ast::ModuleItem::Module(m) => Some(m),
105 _ => None,
106 })
107 .filter_map(|module| {
108 let name = module.name()?.as_name();
109 Some((name, module))
110 })
111}
112
113pub(super) fn input_module_items( 61pub(super) fn input_module_items(
114 db: &impl HirDatabase, 62 db: &impl HirDatabase,
115 source_root_id: SourceRootId, 63 source_root_id: SourceRootId,
@@ -117,7 +65,8 @@ pub(super) fn input_module_items(
117) -> Cancelable<Arc<InputModuleItems>> { 65) -> Cancelable<Arc<InputModuleItems>> {
118 let module_tree = db.module_tree(source_root_id)?; 66 let module_tree = db.module_tree(source_root_id)?;
119 let source = module_id.source(&module_tree); 67 let source = module_id.source(&module_tree);
120 let file_id = source.file_id(); 68 let file_id = source.file_id;
69 let source = ModuleSource::from_source_item_id(db, source);
121 let file_items = db.file_items(file_id); 70 let file_items = db.file_items(file_id);
122 let fill = |acc: &mut InputModuleItems, items: &mut Iterator<Item = ast::ItemOrMacro>| { 71 let fill = |acc: &mut InputModuleItems, items: &mut Iterator<Item = ast::ItemOrMacro>| {
123 for item in items { 72 for item in items {
@@ -148,9 +97,9 @@ pub(super) fn input_module_items(
148 }; 97 };
149 98
150 let mut res = InputModuleItems::default(); 99 let mut res = InputModuleItems::default();
151 match source.resolve(db) { 100 match source {
152 ModuleSourceNode::SourceFile(it) => fill(&mut res, &mut it.borrowed().items_with_macros()), 101 ModuleSource::SourceFile(it) => fill(&mut res, &mut it.borrowed().items_with_macros()),
153 ModuleSourceNode::Module(it) => { 102 ModuleSource::Module(it) => {
154 if let Some(item_list) = it.borrowed().item_list() { 103 if let Some(item_list) = it.borrowed().item_list() {
155 fill(&mut res, &mut item_list.items_with_macros()) 104 fill(&mut res, &mut item_list.items_with_macros())
156 } 105 }
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 85bd84469..4c14650c0 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -13,14 +13,16 @@ use ra_syntax::{
13}; 13};
14 14
15use crate::{ 15use crate::{
16 HirDatabase, Module, Function, SourceItemId, 16 HirDatabase, Function, SourceItemId,
17 module::ModuleSource, 17 DefKind, DefLoc, AsName, Module,
18 DefKind, DefLoc, AsName,
19}; 18};
20 19
21/// Locates the module by `FileId`. Picks topmost module in the file. 20/// Locates the module by `FileId`. Picks topmost module in the file.
22pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { 21pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> {
23 let module_source = ModuleSource::new_file(file_id.into()); 22 let module_source = SourceItemId {
23 file_id: file_id.into(),
24 item_id: None,
25 };
24 module_from_source(db, module_source) 26 module_from_source(db, module_source)
25} 27}
26 28
@@ -34,7 +36,7 @@ pub fn module_from_declaration(
34 let child_name = decl.name(); 36 let child_name = decl.name();
35 match (parent_module, child_name) { 37 match (parent_module, child_name) {
36 (Some(parent_module), Some(child_name)) => { 38 (Some(parent_module), Some(child_name)) => {
37 if let Some(child) = parent_module.child(&child_name.as_name()) { 39 if let Some(child) = parent_module.child(db, &child_name.as_name())? {
38 return Ok(Some(child)); 40 return Ok(Some(child));
39 } 41 }
40 } 42 }
@@ -49,11 +51,26 @@ pub fn module_from_position(
49 position: FilePosition, 51 position: FilePosition,
50) -> Cancelable<Option<Module>> { 52) -> Cancelable<Option<Module>> {
51 let file = db.source_file(position.file_id); 53 let file = db.source_file(position.file_id);
52 let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) { 54 match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) {
53 Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id.into(), m), 55 Some(m) if !m.has_semi() => module_from_inline(db, position.file_id.into(), m),
54 _ => ModuleSource::new_file(position.file_id.into()), 56 _ => module_from_file_id(db, position.file_id.into()),
57 }
58}
59
60fn module_from_inline(
61 db: &impl HirDatabase,
62 file_id: FileId,
63 module: ast::Module,
64) -> Cancelable<Option<Module>> {
65 assert!(!module.has_semi());
66 let file_id = file_id.into();
67 let file_items = db.file_items(file_id);
68 let item_id = file_items.id_of(file_id, module.syntax());
69 let source = SourceItemId {
70 file_id,
71 item_id: Some(item_id),
55 }; 72 };
56 module_from_source(db, module_source) 73 module_from_source(db, source)
57} 74}
58 75
59/// Locates the module by child syntax element within the module 76/// Locates the module by child syntax element within the module
@@ -62,29 +79,34 @@ pub fn module_from_child_node(
62 file_id: FileId, 79 file_id: FileId,
63 child: SyntaxNodeRef, 80 child: SyntaxNodeRef,
64) -> Cancelable<Option<Module>> { 81) -> Cancelable<Option<Module>> {
65 let module_source = if let Some(m) = child 82 if let Some(m) = child
66 .ancestors() 83 .ancestors()
67 .filter_map(ast::Module::cast) 84 .filter_map(ast::Module::cast)
68 .find(|it| !it.has_semi()) 85 .find(|it| !it.has_semi())
69 { 86 {
70 ModuleSource::new_inline(db, file_id.into(), m) 87 module_from_inline(db, file_id.into(), m)
71 } else { 88 } else {
72 ModuleSource::new_file(file_id.into()) 89 module_from_file_id(db, file_id.into())
73 }; 90 }
74 module_from_source(db, module_source)
75} 91}
76 92
77fn module_from_source( 93fn module_from_source(db: &impl HirDatabase, source: SourceItemId) -> Cancelable<Option<Module>> {
78 db: &impl HirDatabase, 94 let source_root_id = db.file_source_root(source.file_id.as_original_file());
79 module_source: ModuleSource,
80) -> Cancelable<Option<Module>> {
81 let source_root_id = db.file_source_root(module_source.file_id().as_original_file());
82 let module_tree = db.module_tree(source_root_id)?; 95 let module_tree = db.module_tree(source_root_id)?;
83 let m = module_tree 96 let module_id = ctry!(module_tree.find_module_by_source(source));
84 .modules_with_sources() 97 Ok(Some(Module::from_module_id(db, source_root_id, module_id)?))
85 .find(|(_id, src)| src == &module_source); 98}
86 let module_id = ctry!(m).0; 99
87 Ok(Some(Module::new(db, source_root_id, module_id)?)) 100pub fn function_from_position(
101 db: &impl HirDatabase,
102 position: FilePosition,
103) -> Cancelable<Option<Function>> {
104 let file = db.source_file(position.file_id);
105 let fn_def = ctry!(find_node_at_offset::<ast::FnDef>(
106 file.syntax(),
107 position.offset
108 ));
109 function_from_source(db, position.file_id, fn_def)
88} 110}
89 111
90pub fn function_from_source( 112pub fn function_from_source(
@@ -102,7 +124,8 @@ pub fn function_from_module(
102 module: &Module, 124 module: &Module,
103 fn_def: ast::FnDef, 125 fn_def: ast::FnDef,
104) -> Function { 126) -> Function {
105 let file_id = module.source().file_id(); 127 let loc = module.def_id.loc(db);
128 let file_id = loc.source_item_id.file_id;
106 let file_items = db.file_items(file_id); 129 let file_items = db.file_items(file_id);
107 let item_id = file_items.id_of(file_id, fn_def.syntax()); 130 let item_id = file_items.id_of(file_id, fn_def.syntax());
108 let source_item_id = SourceItemId { 131 let source_item_id = SourceItemId {
@@ -111,8 +134,8 @@ pub fn function_from_module(
111 }; 134 };
112 let def_loc = DefLoc { 135 let def_loc = DefLoc {
113 kind: DefKind::Function, 136 kind: DefKind::Function,
114 source_root_id: module.source_root_id, 137 source_root_id: loc.source_root_id,
115 module_id: module.module_id, 138 module_id: loc.module_id,
116 source_item_id, 139 source_item_id,
117 }; 140 };
118 Function::new(def_loc.id(db)) 141 Function::new(def_loc.id(db))
@@ -135,7 +158,8 @@ pub fn macro_symbols(
135 Some(it) => it, 158 Some(it) => it,
136 None => return Ok(Vec::new()), 159 None => return Ok(Vec::new()),
137 }; 160 };
138 let items = db.input_module_items(module.source_root_id, module.module_id)?; 161 let loc = module.def_id.loc(db);
162 let items = db.input_module_items(loc.source_root_id, loc.module_id)?;
139 let mut res = Vec::new(); 163 let mut res = Vec::new();
140 164
141 for macro_call_id in items 165 for macro_call_id in items
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index 8f56cdb15..b685259d7 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -31,10 +31,11 @@ use ra_syntax::{
31}; 31};
32 32
33use crate::{ 33use crate::{
34 Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock, 34 Def, DefId, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock,
35 db::HirDatabase, 35 db::HirDatabase,
36 type_ref::{TypeRef, Mutability}, 36 type_ref::{TypeRef, Mutability},
37 name::KnownName, 37 name::KnownName,
38 ScopesWithSyntaxMapping,
38}; 39};
39 40
40/// The ID of a type variable. 41/// The ID of a type variable.
@@ -305,7 +306,7 @@ impl Ty {
305 return Ok(Ty::Uint(uint_ty)); 306 return Ok(Ty::Uint(uint_ty));
306 } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { 307 } else if let Some(float_ty) = primitive::FloatTy::from_name(name) {
307 return Ok(Ty::Float(float_ty)); 308 return Ok(Ty::Float(float_ty));
308 } else if name.as_known_name() == Some(KnownName::Self_) { 309 } else if name.as_known_name() == Some(KnownName::SelfType) {
309 return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type())); 310 return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type()));
310 } 311 }
311 } 312 }
@@ -515,7 +516,7 @@ impl InferenceResult {
515#[derive(Clone, Debug)] 516#[derive(Clone, Debug)]
516struct InferenceContext<'a, D: HirDatabase> { 517struct InferenceContext<'a, D: HirDatabase> {
517 db: &'a D, 518 db: &'a D,
518 scopes: Arc<FnScopes>, 519 scopes: ScopesWithSyntaxMapping,
519 /// The self param for the current method, if it exists. 520 /// The self param for the current method, if it exists.
520 self_param: Option<LocalSyntaxPtr>, 521 self_param: Option<LocalSyntaxPtr>,
521 module: Module, 522 module: Module,
@@ -543,7 +544,7 @@ fn is_boolean_operator(op: BinOp) -> bool {
543impl<'a, D: HirDatabase> InferenceContext<'a, D> { 544impl<'a, D: HirDatabase> InferenceContext<'a, D> {
544 fn new( 545 fn new(
545 db: &'a D, 546 db: &'a D,
546 scopes: Arc<FnScopes>, 547 scopes: ScopesWithSyntaxMapping,
547 module: Module, 548 module: Module,
548 impl_block: Option<ImplBlock>, 549 impl_block: Option<ImplBlock>,
549 ) -> Self { 550 ) -> Self {
@@ -840,10 +841,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
840 self.infer_expr_opt(e.expr(), &Expectation::none())?; 841 self.infer_expr_opt(e.expr(), &Expectation::none())?;
841 Ty::Never 842 Ty::Never
842 } 843 }
843 ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => {
844 // Can this even occur outside of a match expression?
845 Ty::Unknown
846 }
847 ast::Expr::StructLit(e) => { 844 ast::Expr::StructLit(e) => {
848 let (ty, def_id) = self.resolve_variant(e.path())?; 845 let (ty, def_id) = self.resolve_variant(e.path())?;
849 if let Some(nfl) = e.named_field_list() { 846 if let Some(nfl) = e.named_field_list() {
@@ -859,10 +856,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
859 } 856 }
860 ty 857 ty
861 } 858 }
862 ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => {
863 // Can this even occur outside of a struct literal?
864 Ty::Unknown
865 }
866 ast::Expr::IndexExpr(_e) => Ty::Unknown, 859 ast::Expr::IndexExpr(_e) => Ty::Unknown,
867 ast::Expr::FieldExpr(e) => { 860 ast::Expr::FieldExpr(e) => {
868 let receiver_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; 861 let receiver_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
@@ -1047,7 +1040,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
1047 1040
1048pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> { 1041pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> {
1049 let function = Function::new(def_id); // TODO: consts also need inference 1042 let function = Function::new(def_id); // TODO: consts also need inference
1050 let scopes = function.scopes(db); 1043 let scopes = function.scopes(db)?;
1051 let module = function.module(db)?; 1044 let module = function.module(db)?;
1052 let impl_block = function.impl_block(db)?; 1045 let impl_block = function.impl_block(db)?;
1053 let mut ctx = InferenceContext::new(db, scopes, module, impl_block); 1046 let mut ctx = InferenceContext::new(db, scopes, module, impl_block);
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 1650606b7..25a354947 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -1,7 +1,10 @@
1use std::sync::Arc;
1use std::fmt::Write; 2use std::fmt::Write;
2use std::path::{PathBuf, Path}; 3use std::path::{PathBuf, Path};
3use std::fs; 4use std::fs;
4 5
6use salsa::Database;
7
5use ra_db::{SyntaxDatabase}; 8use ra_db::{SyntaxDatabase};
6use ra_syntax::ast::{self, AstNode}; 9use ra_syntax::ast::{self, AstNode};
7use test_utils::{project_dir, assert_eq_text, read_text}; 10use test_utils::{project_dir, assert_eq_text, read_text};
@@ -241,3 +244,44 @@ fn ellipsize(mut text: String, max_len: usize) -> String {
241fn test_data_dir() -> PathBuf { 244fn test_data_dir() -> PathBuf {
242 project_dir().join("crates/ra_hir/src/ty/tests/data") 245 project_dir().join("crates/ra_hir/src/ty/tests/data")
243} 246}
247
248#[test]
249#[should_panic] // TODO this should work once hir::Expr is used
250fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
251 let (mut db, pos) = MockDatabase::with_position(
252 "
253 //- /lib.rs
254 fn foo() -> i32 {
255 <|>1 + 1
256 }
257 ",
258 );
259 let func = source_binder::function_from_position(&db, pos)
260 .unwrap()
261 .unwrap();
262 {
263 let events = db.log_executed(|| {
264 func.infer(&db).unwrap();
265 });
266 assert!(format!("{:?}", events).contains("infer"))
267 }
268
269 let new_text = "
270 fn foo() -> i32 {
271 1
272 +
273 1
274 }
275 "
276 .to_string();
277
278 db.query_mut(ra_db::FileTextQuery)
279 .set(pos.file_id, Arc::new(new_text));
280
281 {
282 let events = db.log_executed(|| {
283 func.infer(&db).unwrap();
284 });
285 assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
286 }
287}
diff --git a/crates/ra_hir/src/type_ref.rs b/crates/ra_hir/src/type_ref.rs
index b36bb35d8..859f330c2 100644
--- a/crates/ra_hir/src/type_ref.rs
+++ b/crates/ra_hir/src/type_ref.rs
@@ -107,4 +107,8 @@ impl TypeRef {
107 TypeRef::Error 107 TypeRef::Error
108 } 108 }
109 } 109 }
110
111 pub fn unit() -> TypeRef {
112 TypeRef::Tuple(Vec::new())
113 }
110} 114}