aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-04-10 09:15:55 +0100
committerAleksey Kladov <[email protected]>2019-04-11 14:29:33 +0100
commit10d66d63d716a10ba7a5a8d1b69c9066249caf69 (patch)
tree3897deea123f2bec49c28cdeb453ae0cfa0f49f1 /crates/ra_ide_api
parente6e2571bdf780d304c792d4317bbaf1d6f5d7a0a (diff)
introduce SourceAnalyzer
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r--crates/ra_ide_api/src/completion/complete_dot.rs11
-rw-r--r--crates/ra_ide_api/src/completion/complete_struct_literal.rs11
-rw-r--r--crates/ra_ide_api/src/completion/completion_context.rs3
-rw-r--r--crates/ra_ide_api/src/display/navigation_target.rs25
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs152
-rw-r--r--crates/ra_ide_api/src/hover.rs18
6 files changed, 81 insertions, 139 deletions
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs
index c093d5cfb..358057364 100644
--- a/crates/ra_ide_api/src/completion/complete_dot.rs
+++ b/crates/ra_ide_api/src/completion/complete_dot.rs
@@ -4,17 +4,10 @@ use crate::completion::{CompletionContext, Completions};
4 4
5/// Complete dot accesses, i.e. fields or methods (currently only fields). 5/// Complete dot accesses, i.e. fields or methods (currently only fields).
6pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 6pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
7 let (function, receiver) = match (&ctx.function, ctx.dot_receiver) { 7 let receiver_ty = match ctx.dot_receiver.and_then(|it| ctx.analyzer.type_of(ctx.db, it)) {
8 (Some(function), Some(receiver)) => (function, receiver), 8 Some(it) => it,
9 _ => return,
10 };
11 let infer_result = function.infer(ctx.db);
12 let source_map = function.body_source_map(ctx.db);
13 let expr = match source_map.node_expr(receiver) {
14 Some(expr) => expr,
15 None => return, 9 None => return,
16 }; 10 };
17 let receiver_ty = infer_result[expr].clone();
18 if !ctx.is_call { 11 if !ctx.is_call {
19 complete_fields(acc, ctx, receiver_ty.clone()); 12 complete_fields(acc, ctx, receiver_ty.clone());
20 } 13 }
diff --git a/crates/ra_ide_api/src/completion/complete_struct_literal.rs b/crates/ra_ide_api/src/completion/complete_struct_literal.rs
index f58bcd03e..48fbf67f7 100644
--- a/crates/ra_ide_api/src/completion/complete_struct_literal.rs
+++ b/crates/ra_ide_api/src/completion/complete_struct_literal.rs
@@ -4,17 +4,10 @@ use crate::completion::{CompletionContext, Completions};
4 4
5/// Complete fields in fields literals. 5/// Complete fields in fields literals.
6pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) { 6pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) {
7 let (function, struct_lit) = match (&ctx.function, ctx.struct_lit_syntax) { 7 let ty = match ctx.struct_lit_syntax.and_then(|it| ctx.analyzer.type_of(ctx.db, it.into())) {
8 (Some(function), Some(struct_lit)) => (function, struct_lit), 8 Some(it) => it,
9 _ => return,
10 };
11 let infer_result = function.infer(ctx.db);
12 let source_map = function.body_source_map(ctx.db);
13 let expr = match source_map.node_expr(struct_lit.into()) {
14 Some(expr) => expr,
15 None => return, 9 None => return,
16 }; 10 };
17 let ty = infer_result[expr].clone();
18 let (adt, substs) = match ty.as_adt() { 11 let (adt, substs) = match ty.as_adt() {
19 Some(res) => res, 12 Some(res) => res,
20 _ => return, 13 _ => return,
diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs
index 65dffa470..ce21fca9b 100644
--- a/crates/ra_ide_api/src/completion/completion_context.rs
+++ b/crates/ra_ide_api/src/completion/completion_context.rs
@@ -14,6 +14,7 @@ use crate::{db, FilePosition};
14#[derive(Debug)] 14#[derive(Debug)]
15pub(crate) struct CompletionContext<'a> { 15pub(crate) struct CompletionContext<'a> {
16 pub(super) db: &'a db::RootDatabase, 16 pub(super) db: &'a db::RootDatabase,
17 pub(super) analyzer: hir::SourceAnalyser,
17 pub(super) offset: TextUnit, 18 pub(super) offset: TextUnit,
18 pub(super) token: SyntaxToken<'a>, 19 pub(super) token: SyntaxToken<'a>,
19 pub(super) resolver: Resolver, 20 pub(super) resolver: Resolver,
@@ -50,8 +51,10 @@ impl<'a> CompletionContext<'a> {
50 let resolver = source_binder::resolver_for_position(db, position); 51 let resolver = source_binder::resolver_for_position(db, position);
51 let module = source_binder::module_from_position(db, position); 52 let module = source_binder::module_from_position(db, position);
52 let token = find_token_at_offset(original_file.syntax(), position.offset).left_biased()?; 53 let token = find_token_at_offset(original_file.syntax(), position.offset).left_biased()?;
54 let analyzer = hir::SourceAnalyser::new(db, position.file_id, token.parent());
53 let mut ctx = CompletionContext { 55 let mut ctx = CompletionContext {
54 db, 56 db,
57 analyzer,
55 token, 58 token,
56 offset: position.offset, 59 offset: position.offset,
57 resolver, 60 resolver,
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs
index 3c518faf5..84645287d 100644
--- a/crates/ra_ide_api/src/display/navigation_target.rs
+++ b/crates/ra_ide_api/src/display/navigation_target.rs
@@ -1,11 +1,11 @@
1use ra_db::{FileId, SourceDatabase}; 1use ra_db::{FileId, SourceDatabase};
2use ra_syntax::{ 2use ra_syntax::{
3 SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, TreeArc, 3 SyntaxNode, AstNode, SmolStr, TextRange, TreeArc, AstPtr,
4 SyntaxKind::{self, NAME}, 4 SyntaxKind::{self, NAME},
5 ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, 5 ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner},
6 algo::visit::{visitor, Visitor}, 6 algo::visit::{visitor, Visitor},
7}; 7};
8use hir::{ModuleSource, FieldSource, Name, ImplItem}; 8use hir::{ModuleSource, FieldSource, ImplItem, Either};
9 9
10use crate::{FileSymbol, db::RootDatabase}; 10use crate::{FileSymbol, db::RootDatabase};
11 11
@@ -74,15 +74,25 @@ impl NavigationTarget {
74 } 74 }
75 } 75 }
76 76
77 pub(crate) fn from_scope_entry( 77 pub(crate) fn from_pat(
78 db: &RootDatabase,
78 file_id: FileId, 79 file_id: FileId,
79 name: Name, 80 pat: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>,
80 ptr: SyntaxNodePtr,
81 ) -> NavigationTarget { 81 ) -> NavigationTarget {
82 let file = db.parse(file_id);
83 let (name, full_range) = match pat {
84 Either::A(pat) => match pat.to_node(&file).kind() {
85 ast::PatKind::BindPat(pat) => {
86 return NavigationTarget::from_bind_pat(file_id, &pat)
87 }
88 _ => ("_".into(), pat.syntax_node_ptr().range()),
89 },
90 Either::B(slf) => ("self".into(), slf.syntax_node_ptr().range()),
91 };
82 NavigationTarget { 92 NavigationTarget {
83 file_id, 93 file_id,
84 name: name.to_string().into(), 94 name,
85 full_range: ptr.range(), 95 full_range,
86 focus_range: None, 96 focus_range: None,
87 kind: NAME, 97 kind: NAME,
88 container_name: None, 98 container_name: None,
@@ -229,6 +239,7 @@ impl NavigationTarget {
229 239
230 /// Allows `NavigationTarget` to be created from a `NameOwner` 240 /// Allows `NavigationTarget` to be created from a `NameOwner`
231 pub(crate) fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget { 241 pub(crate) fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget {
242 //FIXME: use `_` instead of empty string
232 let name = node.name().map(|it| it.text().clone()).unwrap_or_default(); 243 let name = node.name().map(|it| it.text().clone()).unwrap_or_default();
233 let focus_range = node.name().map(|it| it.syntax().range()); 244 let focus_range = node.name().map(|it| it.syntax().range());
234 NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax()) 245 NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax())
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index 60c1f5085..7f93f50c4 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -5,7 +5,6 @@ use ra_syntax::{
5 SyntaxNode, 5 SyntaxNode,
6}; 6};
7use test_utils::tested_by; 7use test_utils::tested_by;
8use hir::Resolution;
9 8
10use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; 9use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};
11 10
@@ -48,127 +47,72 @@ pub(crate) fn reference_definition(
48) -> ReferenceResult { 47) -> ReferenceResult {
49 use self::ReferenceResult::*; 48 use self::ReferenceResult::*;
50 49
51 let function = hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax()); 50 let analyzer = hir::SourceAnalyser::new(db, file_id, name_ref.syntax());
52
53 if let Some(function) = function {
54 // Check if it is a method
55 if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
56 tested_by!(goto_definition_works_for_methods);
57 let infer_result = function.infer(db);
58 let source_map = function.body_source_map(db);
59 let expr = ast::Expr::cast(method_call.syntax()).unwrap();
60 if let Some(func) =
61 source_map.node_expr(expr).and_then(|it| infer_result.method_resolution(it))
62 {
63 return Exact(NavigationTarget::from_function(db, func));
64 };
65 }
66 // It could also be a field access
67 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) {
68 tested_by!(goto_definition_works_for_fields);
69 let infer_result = function.infer(db);
70 let source_map = function.body_source_map(db);
71 let expr = ast::Expr::cast(field_expr.syntax()).unwrap();
72 if let Some(field) =
73 source_map.node_expr(expr).and_then(|it| infer_result.field_resolution(it))
74 {
75 return Exact(NavigationTarget::from_field(db, field));
76 };
77 }
78 51
79 // It could also be a named field 52 // Special cases:
80 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) {
81 tested_by!(goto_definition_works_for_named_fields);
82 53
83 let infer_result = function.infer(db); 54 // Check if it is a method
84 let source_map = function.body_source_map(db); 55 if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
56 tested_by!(goto_definition_works_for_methods);
57 if let Some(func) = analyzer.resolve_method_call(method_call) {
58 return Exact(NavigationTarget::from_function(db, func));
59 }
60 }
61 // It could also be a field access
62 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) {
63 tested_by!(goto_definition_works_for_fields);
64 if let Some(field) = analyzer.resolve_field(field_expr) {
65 return Exact(NavigationTarget::from_field(db, field));
66 };
67 }
85 68
86 let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); 69 // It could also be a named field
70 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) {
71 tested_by!(goto_definition_works_for_named_fields);
87 72
88 if let Some(expr) = struct_lit.and_then(|lit| source_map.node_expr(lit.into())) { 73 let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast);
89 let ty = infer_result[expr].clone();
90 if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() {
91 let hir_path = hir::Path::from_name_ref(name_ref);
92 let hir_name = hir_path.as_ident().unwrap();
93 74
94 if let Some(field) = s.field(db, hir_name) { 75 if let Some(ty) = struct_lit.and_then(|lit| analyzer.type_of(db, lit.into())) {
95 return Exact(NavigationTarget::from_field(db, field)); 76 if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() {
96 } 77 let hir_path = hir::Path::from_name_ref(name_ref);
78 let hir_name = hir_path.as_ident().unwrap();
79
80 if let Some(field) = s.field(db, hir_name) {
81 return Exact(NavigationTarget::from_field(db, field));
97 } 82 }
98 } 83 }
99 } 84 }
100 } 85 }
101 86
102 // Try name resolution 87 // General case, a path or a local:
103 let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax()); 88 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
104 if let Some(path) = 89 if let Some(resolved) = analyzer.resolve_path(db, path) {
105 name_ref.syntax().ancestors().find_map(ast::Path::cast).and_then(hir::Path::from_ast) 90 match resolved {
106 { 91 hir::PathResolution::Def(def) => return Exact(NavigationTarget::from_def(db, def)),
107 let resolved = resolver.resolve_path(db, &path); 92 hir::PathResolution::LocalBinding(pat) => {
108 match resolved.clone().take_types().or_else(|| resolved.take_values()) { 93 if let Some(pat) = analyzer.pat_syntax(db, pat) {
109 Some(Resolution::Def(def)) => return Exact(NavigationTarget::from_def(db, def)), 94 let nav = NavigationTarget::from_pat(db, file_id, pat);
110 Some(Resolution::LocalBinding(pat)) => { 95 return Exact(nav);
111 let body = resolver.body().expect("no body for local binding");
112 let source_map = body.owner().body_source_map(db);
113 let ptr = source_map.pat_syntax(pat).expect("pattern not found in syntax mapping");
114 let name =
115 path.as_ident().cloned().expect("local binding from a multi-segment path");
116 let ptr = ptr.either(|it| it.into(), |it| it.into());
117 let nav = NavigationTarget::from_scope_entry(file_id, name, ptr);
118 return Exact(nav);
119 }
120 Some(Resolution::GenericParam(..)) => {
121 // FIXME: go to the generic param def
122 }
123 Some(Resolution::SelfType(impl_block)) => {
124 let ty = impl_block.target_ty(db);
125
126 if let Some((def_id, _)) = ty.as_adt() {
127 return Exact(NavigationTarget::from_adt_def(db, def_id));
128 }
129 }
130 None => {
131 // If we failed to resolve then check associated items
132 if let Some(function) = function {
133 // Resolve associated item for path expressions
134 if let Some(path_expr) =
135 name_ref.syntax().ancestors().find_map(ast::PathExpr::cast)
136 {
137 let infer_result = function.infer(db);
138 let source_map = function.body_source_map(db);
139
140 if let Some(expr) = ast::Expr::cast(path_expr.syntax()) {
141 if let Some(res) = source_map
142 .node_expr(expr)
143 .and_then(|it| infer_result.assoc_resolutions_for_expr(it.into()))
144 {
145 return Exact(NavigationTarget::from_impl_item(db, res));
146 }
147 }
148 } 96 }
97 }
98 hir::PathResolution::GenericParam(..) => {
99 // FIXME: go to the generic param def
100 }
101 hir::PathResolution::SelfType(impl_block) => {
102 let ty = impl_block.target_ty(db);
149 103
150 // Resolve associated item for path patterns 104 if let Some((def_id, _)) = ty.as_adt() {
151 if let Some(path_pat) = 105 return Exact(NavigationTarget::from_adt_def(db, def_id));
152 name_ref.syntax().ancestors().find_map(ast::PathPat::cast)
153 {
154 let infer_result = function.infer(db);
155 let source_map = function.body_source_map(db);
156
157 let pat: &ast::Pat = path_pat.into();
158
159 if let Some(res) = source_map
160 .node_pat(pat)
161 .and_then(|it| infer_result.assoc_resolutions_for_pat(it.into()))
162 {
163 return Exact(NavigationTarget::from_impl_item(db, res));
164 }
165 } 106 }
166 } 107 }
108 hir::PathResolution::AssocItem(assoc) => {
109 return Exact(NavigationTarget::from_impl_item(db, assoc))
110 }
167 } 111 }
168 } 112 }
169 } 113 }
170 114
171 // If that fails try the index based approach. 115 // Fallback index based approach:
172 let navs = crate::symbol_index::index_resolve(db, name_ref) 116 let navs = crate::symbol_index::index_resolve(db, name_ref)
173 .into_iter() 117 .into_iter()
174 .map(NavigationTarget::from_symbol) 118 .map(NavigationTarget::from_symbol)
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index 3a8c93b99..ec167a196 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -132,17 +132,15 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
132 .ancestors() 132 .ancestors()
133 .take_while(|it| it.range() == leaf_node.range()) 133 .take_while(|it| it.range() == leaf_node.range())
134 .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some())?; 134 .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some())?;
135 let parent_fn = node.ancestors().find_map(ast::FnDef::cast)?; 135 let analyzer = hir::SourceAnalyser::new(db, frange.file_id, node);
136 let function = hir::source_binder::function_from_source(db, frange.file_id, parent_fn)?; 136 let ty = if let Some(ty) = ast::Expr::cast(node).and_then(|e| analyzer.type_of(db, e)) {
137 let infer = function.infer(db); 137 ty
138 let source_map = function.body_source_map(db); 138 } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, p)) {
139 if let Some(expr) = ast::Expr::cast(node).and_then(|e| source_map.node_expr(e)) { 139 ty
140 Some(infer[expr].display(db).to_string())
141 } else if let Some(pat) = ast::Pat::cast(node).and_then(|p| source_map.node_pat(p)) {
142 Some(infer[pat].display(db).to_string())
143 } else { 140 } else {
144 None 141 return None;
145 } 142 };
143 Some(ty.display(db).to_string())
146} 144}
147 145
148#[cfg(test)] 146#[cfg(test)]