diff options
Diffstat (limited to 'crates/ra_ide/src')
23 files changed, 496 insertions, 256 deletions
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index e00589733..a936900da 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs | |||
@@ -2,27 +2,53 @@ | |||
2 | 2 | ||
3 | use ra_db::{FilePosition, FileRange}; | 3 | use ra_db::{FilePosition, FileRange}; |
4 | 4 | ||
5 | use crate::{db::RootDatabase, SourceChange, SourceFileEdit}; | 5 | use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit}; |
6 | 6 | ||
7 | use either::Either; | ||
7 | pub use ra_assists::AssistId; | 8 | pub use ra_assists::AssistId; |
9 | use ra_assists::{AssistAction, AssistLabel}; | ||
8 | 10 | ||
9 | #[derive(Debug)] | 11 | #[derive(Debug)] |
10 | pub struct Assist { | 12 | pub struct Assist { |
11 | pub id: AssistId, | 13 | pub id: AssistId, |
12 | pub change: SourceChange, | 14 | pub label: String, |
15 | pub change_data: Either<SourceChange, Vec<SourceChange>>, | ||
13 | } | 16 | } |
14 | 17 | ||
15 | pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { | 18 | pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { |
16 | ra_assists::assists(db, frange) | 19 | ra_assists::assists(db, frange) |
17 | .into_iter() | 20 | .into_iter() |
18 | .map(|(label, action)| { | 21 | .map(|assist| { |
19 | let file_id = frange.file_id; | 22 | let file_id = frange.file_id; |
20 | let file_edit = SourceFileEdit { file_id, edit: action.edit }; | 23 | let assist_label = &assist.label; |
21 | let id = label.id; | 24 | Assist { |
22 | let change = SourceChange::source_file_edit(label.label, file_edit).with_cursor_opt( | 25 | id: assist_label.id, |
23 | action.cursor_position.map(|offset| FilePosition { offset, file_id }), | 26 | label: assist_label.label.clone(), |
24 | ); | 27 | change_data: match assist.action_data { |
25 | Assist { id, change } | 28 | Either::Left(action) => { |
29 | Either::Left(action_to_edit(action, file_id, assist_label)) | ||
30 | } | ||
31 | Either::Right(actions) => Either::Right( | ||
32 | actions | ||
33 | .into_iter() | ||
34 | .map(|action| action_to_edit(action, file_id, assist_label)) | ||
35 | .collect(), | ||
36 | ), | ||
37 | }, | ||
38 | } | ||
26 | }) | 39 | }) |
27 | .collect() | 40 | .collect() |
28 | } | 41 | } |
42 | |||
43 | fn action_to_edit( | ||
44 | action: AssistAction, | ||
45 | file_id: FileId, | ||
46 | assist_label: &AssistLabel, | ||
47 | ) -> SourceChange { | ||
48 | let file_edit = SourceFileEdit { file_id, edit: action.edit }; | ||
49 | SourceChange::source_file_edit( | ||
50 | action.label.unwrap_or_else(|| assist_label.label.clone()), | ||
51 | file_edit, | ||
52 | ) | ||
53 | .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id })) | ||
54 | } | ||
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs index 1cb712e32..aa5d60c7b 100644 --- a/crates/ra_ide/src/call_hierarchy.rs +++ b/crates/ra_ide/src/call_hierarchy.rs | |||
@@ -121,7 +121,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
121 | Some(macro_def.to_nav(db)) | 121 | Some(macro_def.to_nav(db)) |
122 | } | 122 | } |
123 | } { | 123 | } { |
124 | Some((func_target.clone(), name_ref.value.text_range())) | 124 | Some((func_target, name_ref.value.text_range())) |
125 | } else { | 125 | } else { |
126 | None | 126 | None |
127 | } | 127 | } |
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index a7023529b..72a68522e 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs | |||
@@ -1,10 +1,10 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | |||
3 | use hir::db::AstDatabase; | 2 | use hir::db::AstDatabase; |
4 | use ra_syntax::{ | 3 | use ra_syntax::{ |
5 | ast::{self, ArgListOwner}, | 4 | ast::{self, ArgListOwner}, |
6 | match_ast, AstNode, SyntaxNode, | 5 | match_ast, AstNode, SyntaxNode, |
7 | }; | 6 | }; |
7 | |||
8 | use test_utils::tested_by; | 8 | use test_utils::tested_by; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
@@ -51,36 +51,39 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal | |||
51 | // If we have a calling expression let's find which argument we are on | 51 | // If we have a calling expression let's find which argument we are on |
52 | let num_params = call_info.parameters().len(); | 52 | let num_params = call_info.parameters().len(); |
53 | 53 | ||
54 | if num_params == 1 { | 54 | match num_params { |
55 | if !has_self { | 55 | 0 => (), |
56 | call_info.active_parameter = Some(0); | 56 | 1 => { |
57 | } | 57 | if !has_self { |
58 | } else if num_params > 1 { | 58 | call_info.active_parameter = Some(0); |
59 | // Count how many parameters into the call we are. | ||
60 | if let Some(arg_list) = calling_node.arg_list() { | ||
61 | // Number of arguments specified at the call site | ||
62 | let num_args_at_callsite = arg_list.args().count(); | ||
63 | |||
64 | let arg_list_range = arg_list.syntax().text_range(); | ||
65 | if !arg_list_range.contains_inclusive(position.offset) { | ||
66 | tested_by!(call_info_bad_offset); | ||
67 | return None; | ||
68 | } | 59 | } |
60 | } | ||
61 | _ => { | ||
62 | if let Some(arg_list) = calling_node.arg_list() { | ||
63 | // Number of arguments specified at the call site | ||
64 | let num_args_at_callsite = arg_list.args().count(); | ||
65 | |||
66 | let arg_list_range = arg_list.syntax().text_range(); | ||
67 | if !arg_list_range.contains_inclusive(position.offset) { | ||
68 | tested_by!(call_info_bad_offset); | ||
69 | return None; | ||
70 | } | ||
69 | 71 | ||
70 | let mut param = std::cmp::min( | 72 | let mut param = std::cmp::min( |
71 | num_args_at_callsite, | 73 | num_args_at_callsite, |
72 | arg_list | 74 | arg_list |
73 | .args() | 75 | .args() |
74 | .take_while(|arg| arg.syntax().text_range().end() < position.offset) | 76 | .take_while(|arg| arg.syntax().text_range().end() < position.offset) |
75 | .count(), | 77 | .count(), |
76 | ); | 78 | ); |
77 | 79 | ||
78 | // If we are in a method account for `self` | 80 | // If we are in a method account for `self` |
79 | if has_self { | 81 | if has_self { |
80 | param += 1; | 82 | param += 1; |
81 | } | 83 | } |
82 | 84 | ||
83 | call_info.active_parameter = Some(param); | 85 | call_info.active_parameter = Some(param); |
86 | } | ||
84 | } | 87 | } |
85 | } | 88 | } |
86 | 89 | ||
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 210a685e4..2ca78c927 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -27,7 +27,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
27 | complete_methods(acc, ctx, &receiver_ty); | 27 | complete_methods(acc, ctx, &receiver_ty); |
28 | 28 | ||
29 | // Suggest .await syntax for types that implement Future trait | 29 | // Suggest .await syntax for types that implement Future trait |
30 | if ctx.analyzer.impls_future(ctx.db, receiver_ty) { | 30 | if receiver_ty.impls_future(ctx.db) { |
31 | CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") | 31 | CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") |
32 | .detail("expr.await") | 32 | .detail("expr.await") |
33 | .insert_text("await") | 33 | .insert_text("await") |
@@ -53,13 +53,16 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty | |||
53 | } | 53 | } |
54 | 54 | ||
55 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 55 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { |
56 | let mut seen_methods = FxHashSet::default(); | 56 | if let Some(krate) = ctx.module.map(|it| it.krate()) { |
57 | ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, func| { | 57 | let mut seen_methods = FxHashSet::default(); |
58 | if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { | 58 | let traits_in_scope = ctx.analyzer.traits_in_scope(ctx.db); |
59 | acc.add_function(ctx, func); | 59 | receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { |
60 | } | 60 | if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { |
61 | None::<()> | 61 | acc.add_function(ctx, func); |
62 | }); | 62 | } |
63 | None::<()> | ||
64 | }); | ||
65 | } | ||
63 | } | 66 | } |
64 | 67 | ||
65 | #[cfg(test)] | 68 | #[cfg(test)] |
@@ -525,6 +528,7 @@ mod tests { | |||
525 | 528 | ||
526 | //- /std/lib.rs | 529 | //- /std/lib.rs |
527 | pub mod future { | 530 | pub mod future { |
531 | #[lang = "future_trait"] | ||
528 | pub trait Future {} | 532 | pub trait Future {} |
529 | } | 533 | } |
530 | "###, CompletionKind::Keyword), | 534 | "###, CompletionKind::Keyword), |
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index cc1f7c830..af24e9f48 100644 --- a/crates/ra_ide/src/completion/complete_path.rs +++ b/crates/ra_ide/src/completion/complete_path.rs | |||
@@ -26,7 +26,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
26 | } | 26 | } |
27 | if let ScopeDef::Unknown = def { | 27 | if let ScopeDef::Unknown = def { |
28 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { | 28 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { |
29 | if &name_ref.syntax().text() == name.to_string().as_str() { | 29 | if name_ref.syntax().text() == name.to_string().as_str() { |
30 | // for `use self::foo<|>`, don't suggest `foo` as a completion | 30 | // for `use self::foo<|>`, don't suggest `foo` as a completion |
31 | tested_by!(dont_complete_current_use); | 31 | tested_by!(dont_complete_current_use); |
32 | continue; | 32 | continue; |
@@ -49,22 +49,24 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
49 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), | 49 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), |
50 | _ => unreachable!(), | 50 | _ => unreachable!(), |
51 | }; | 51 | }; |
52 | ctx.analyzer.iterate_path_candidates(ctx.db, &ty, None, |_ty, item| { | ||
53 | match item { | ||
54 | hir::AssocItem::Function(func) => { | ||
55 | if !func.has_self_param(ctx.db) { | ||
56 | acc.add_function(ctx, func); | ||
57 | } | ||
58 | } | ||
59 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
60 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
61 | } | ||
62 | None::<()> | ||
63 | }); | ||
64 | // Iterate assoc types separately | 52 | // Iterate assoc types separately |
65 | // FIXME: complete T::AssocType | 53 | // FIXME: complete T::AssocType |
66 | let krate = ctx.module.map(|m| m.krate()); | 54 | let krate = ctx.module.map(|m| m.krate()); |
67 | if let Some(krate) = krate { | 55 | if let Some(krate) = krate { |
56 | let traits_in_scope = ctx.analyzer.traits_in_scope(ctx.db); | ||
57 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | ||
58 | match item { | ||
59 | hir::AssocItem::Function(func) => { | ||
60 | if !func.has_self_param(ctx.db) { | ||
61 | acc.add_function(ctx, func); | ||
62 | } | ||
63 | } | ||
64 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
65 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
66 | } | ||
67 | None::<()> | ||
68 | }); | ||
69 | |||
68 | ty.iterate_impl_items(ctx.db, krate, |item| { | 70 | ty.iterate_impl_items(ctx.db, krate, |item| { |
69 | match item { | 71 | match item { |
70 | hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} | 72 | hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} |
diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs index 1f2988b36..731b4fd82 100644 --- a/crates/ra_ide/src/completion/complete_snippet.rs +++ b/crates/ra_ide/src/completion/complete_snippet.rs | |||
@@ -36,6 +36,7 @@ fn ${1:feature}() { | |||
36 | .lookup_by("tfn") | 36 | .lookup_by("tfn") |
37 | .add_to(acc); | 37 | .add_to(acc); |
38 | 38 | ||
39 | snippet(ctx, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc); | ||
39 | snippet(ctx, "pub(crate)", "pub(crate) $0").add_to(acc); | 40 | snippet(ctx, "pub(crate)", "pub(crate) $0").add_to(acc); |
40 | } | 41 | } |
41 | 42 | ||
@@ -107,6 +108,13 @@ mod tests { | |||
107 | lookup: "tfn", | 108 | lookup: "tfn", |
108 | }, | 109 | }, |
109 | CompletionItem { | 110 | CompletionItem { |
111 | label: "macro_rules", | ||
112 | source_range: [78; 78), | ||
113 | delete: [78; 78), | ||
114 | insert: "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}", | ||
115 | kind: Snippet, | ||
116 | }, | ||
117 | CompletionItem { | ||
110 | label: "pub(crate)", | 118 | label: "pub(crate)", |
111 | source_range: [78; 78), | 119 | source_range: [78; 78), |
112 | delete: [78; 78), | 120 | delete: [78; 78), |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 48d69f7e5..deaacda6c 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -52,15 +52,11 @@ impl<'a> CompletionContext<'a> { | |||
52 | original_parse: &'a Parse<ast::SourceFile>, | 52 | original_parse: &'a Parse<ast::SourceFile>, |
53 | position: FilePosition, | 53 | position: FilePosition, |
54 | ) -> Option<CompletionContext<'a>> { | 54 | ) -> Option<CompletionContext<'a>> { |
55 | let src = hir::ModuleSource::from_position(db, position); | 55 | let mut sb = hir::SourceBinder::new(db); |
56 | let module = hir::Module::from_definition( | 56 | let module = sb.to_module_def(position.file_id); |
57 | db, | ||
58 | hir::InFile { file_id: position.file_id.into(), value: src }, | ||
59 | ); | ||
60 | let token = | 57 | let token = |
61 | original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?; | 58 | original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?; |
62 | let analyzer = hir::SourceAnalyzer::new( | 59 | let analyzer = sb.analyze( |
63 | db, | ||
64 | hir::InFile::new(position.file_id.into(), &token.parent()), | 60 | hir::InFile::new(position.file_id.into(), &token.parent()), |
65 | Some(position.offset), | 61 | Some(position.offset), |
66 | ); | 62 | ); |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 478368529..f403b3bcf 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -23,6 +23,7 @@ pub enum Severity { | |||
23 | 23 | ||
24 | pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> { | 24 | pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> { |
25 | let _p = profile("diagnostics"); | 25 | let _p = profile("diagnostics"); |
26 | let mut sb = hir::SourceBinder::new(db); | ||
26 | let parse = db.parse(file_id); | 27 | let parse = db.parse(file_id); |
27 | let mut res = Vec::new(); | 28 | let mut res = Vec::new(); |
28 | 29 | ||
@@ -108,10 +109,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
108 | fix: Some(fix), | 109 | fix: Some(fix), |
109 | }) | 110 | }) |
110 | }); | 111 | }); |
111 | let source_file = db.parse(file_id).tree(); | 112 | if let Some(m) = sb.to_module_def(file_id) { |
112 | let src = | ||
113 | hir::InFile { file_id: file_id.into(), value: hir::ModuleSource::SourceFile(source_file) }; | ||
114 | if let Some(m) = hir::Module::from_definition(db, src) { | ||
115 | m.diagnostics(db, &mut sink); | 113 | m.diagnostics(db, &mut sink); |
116 | }; | 114 | }; |
117 | drop(sink); | 115 | drop(sink); |
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index 324ad9552..ddc53a52b 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs | |||
@@ -34,6 +34,8 @@ pub struct FunctionSignature { | |||
34 | pub generic_parameters: Vec<String>, | 34 | pub generic_parameters: Vec<String>, |
35 | /// Parameters of the function | 35 | /// Parameters of the function |
36 | pub parameters: Vec<String>, | 36 | pub parameters: Vec<String>, |
37 | /// Parameter names of the function | ||
38 | pub parameter_names: Vec<String>, | ||
37 | /// Optional return type | 39 | /// Optional return type |
38 | pub ret_type: Option<String>, | 40 | pub ret_type: Option<String>, |
39 | /// Where predicates | 41 | /// Where predicates |
@@ -75,6 +77,7 @@ impl FunctionSignature { | |||
75 | name: node.name().map(|n| n.text().to_string()), | 77 | name: node.name().map(|n| n.text().to_string()), |
76 | ret_type: node.name().map(|n| n.text().to_string()), | 78 | ret_type: node.name().map(|n| n.text().to_string()), |
77 | parameters: params, | 79 | parameters: params, |
80 | parameter_names: vec![], | ||
78 | generic_parameters: generic_parameters(&node), | 81 | generic_parameters: generic_parameters(&node), |
79 | where_predicates: where_predicates(&node), | 82 | where_predicates: where_predicates(&node), |
80 | doc: None, | 83 | doc: None, |
@@ -114,6 +117,7 @@ impl FunctionSignature { | |||
114 | name: Some(name), | 117 | name: Some(name), |
115 | ret_type: None, | 118 | ret_type: None, |
116 | parameters: params, | 119 | parameters: params, |
120 | parameter_names: vec![], | ||
117 | generic_parameters: vec![], | 121 | generic_parameters: vec![], |
118 | where_predicates: vec![], | 122 | where_predicates: vec![], |
119 | doc: None, | 123 | doc: None, |
@@ -134,6 +138,7 @@ impl FunctionSignature { | |||
134 | name: node.name().map(|n| n.text().to_string()), | 138 | name: node.name().map(|n| n.text().to_string()), |
135 | ret_type: None, | 139 | ret_type: None, |
136 | parameters: params, | 140 | parameters: params, |
141 | parameter_names: vec![], | ||
137 | generic_parameters: vec![], | 142 | generic_parameters: vec![], |
138 | where_predicates: vec![], | 143 | where_predicates: vec![], |
139 | doc: None, | 144 | doc: None, |
@@ -157,6 +162,20 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
157 | res | 162 | res |
158 | } | 163 | } |
159 | 164 | ||
165 | fn param_name_list(node: &ast::FnDef) -> Vec<String> { | ||
166 | let mut res = vec![]; | ||
167 | if let Some(param_list) = node.param_list() { | ||
168 | if let Some(self_param) = param_list.self_param() { | ||
169 | res.push(self_param.syntax().text().to_string()) | ||
170 | } | ||
171 | |||
172 | res.extend(param_list.params().map(|param| { | ||
173 | param.pat().map(|pat| pat.syntax().text().to_string()).unwrap_or_default() | ||
174 | })); | ||
175 | } | ||
176 | res | ||
177 | } | ||
178 | |||
160 | FunctionSignature { | 179 | FunctionSignature { |
161 | kind: CallableKind::Function, | 180 | kind: CallableKind::Function, |
162 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | 181 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), |
@@ -166,6 +185,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
166 | .and_then(|r| r.type_ref()) | 185 | .and_then(|r| r.type_ref()) |
167 | .map(|n| n.syntax().text().to_string()), | 186 | .map(|n| n.syntax().text().to_string()), |
168 | parameters: param_list(node), | 187 | parameters: param_list(node), |
188 | parameter_names: param_name_list(node), | ||
169 | generic_parameters: generic_parameters(node), | 189 | generic_parameters: generic_parameters(node), |
170 | where_predicates: where_predicates(node), | 190 | where_predicates: where_predicates(node), |
171 | // docs are processed separately | 191 | // docs are processed separately |
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs index a80d65ac7..944cc79df 100644 --- a/crates/ra_ide/src/display/structure.rs +++ b/crates/ra_ide/src/display/structure.rs | |||
@@ -151,11 +151,11 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> { | |||
151 | Some(node) | 151 | Some(node) |
152 | }, | 152 | }, |
153 | ast::MacroCall(it) => { | 153 | ast::MacroCall(it) => { |
154 | let first_token = it.syntax().first_token().unwrap(); | 154 | match it.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) { |
155 | if first_token.text().as_str() != "macro_rules" { | 155 | Some(path_segment) if path_segment.text() == "macro_rules" |
156 | return None; | 156 | => decl(it), |
157 | _ => None, | ||
157 | } | 158 | } |
158 | decl(it) | ||
159 | }, | 159 | }, |
160 | _ => None, | 160 | _ => None, |
161 | } | 161 | } |
@@ -198,6 +198,16 @@ macro_rules! mc { | |||
198 | () => {} | 198 | () => {} |
199 | } | 199 | } |
200 | 200 | ||
201 | #[macro_export] | ||
202 | macro_rules! mcexp { | ||
203 | () => {} | ||
204 | } | ||
205 | |||
206 | /// Doc comment | ||
207 | macro_rules! mcexp { | ||
208 | () => {} | ||
209 | } | ||
210 | |||
201 | #[deprecated] | 211 | #[deprecated] |
202 | fn obsolete() {} | 212 | fn obsolete() {} |
203 | 213 | ||
@@ -374,9 +384,27 @@ fn very_obsolete() {} | |||
374 | }, | 384 | }, |
375 | StructureNode { | 385 | StructureNode { |
376 | parent: None, | 386 | parent: None, |
387 | label: "mcexp", | ||
388 | navigation_range: [334; 339), | ||
389 | node_range: [305; 356), | ||
390 | kind: MACRO_CALL, | ||
391 | detail: None, | ||
392 | deprecated: false, | ||
393 | }, | ||
394 | StructureNode { | ||
395 | parent: None, | ||
396 | label: "mcexp", | ||
397 | navigation_range: [387; 392), | ||
398 | node_range: [358; 409), | ||
399 | kind: MACRO_CALL, | ||
400 | detail: None, | ||
401 | deprecated: false, | ||
402 | }, | ||
403 | StructureNode { | ||
404 | parent: None, | ||
377 | label: "obsolete", | 405 | label: "obsolete", |
378 | navigation_range: [322; 330), | 406 | navigation_range: [428; 436), |
379 | node_range: [305; 335), | 407 | node_range: [411; 441), |
380 | kind: FN_DEF, | 408 | kind: FN_DEF, |
381 | detail: Some( | 409 | detail: Some( |
382 | "fn()", | 410 | "fn()", |
@@ -386,8 +414,8 @@ fn very_obsolete() {} | |||
386 | StructureNode { | 414 | StructureNode { |
387 | parent: None, | 415 | parent: None, |
388 | label: "very_obsolete", | 416 | label: "very_obsolete", |
389 | navigation_range: [375; 388), | 417 | navigation_range: [481; 494), |
390 | node_range: [337; 393), | 418 | node_range: [443; 499), |
391 | kind: FN_DEF, | 419 | kind: FN_DEF, |
392 | detail: Some( | 420 | detail: Some( |
393 | "fn()", | 421 | "fn()", |
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs index 70b6fde82..930e0c4c2 100644 --- a/crates/ra_ide/src/extend_selection.rs +++ b/crates/ra_ide/src/extend_selection.rs | |||
@@ -339,7 +339,7 @@ mod tests { | |||
339 | let (cursor, before) = extract_offset(before); | 339 | let (cursor, before) = extract_offset(before); |
340 | let (analysis, file_id) = single_file(&before); | 340 | let (analysis, file_id) = single_file(&before); |
341 | let range = TextRange::offset_len(cursor, 0.into()); | 341 | let range = TextRange::offset_len(cursor, 0.into()); |
342 | let mut frange = FileRange { file_id: file_id, range }; | 342 | let mut frange = FileRange { file_id, range }; |
343 | 343 | ||
344 | for &after in afters { | 344 | for &after in afters { |
345 | frange.range = analysis.extend_selection(frange).unwrap(); | 345 | frange.range = analysis.extend_selection(frange).unwrap(); |
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 79d332e8c..5a12a619c 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{db::AstDatabase, InFile}; | 3 | use hir::{db::AstDatabase, InFile, SourceBinder}; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | ast::{self, DocCommentsOwner}, | 5 | ast::{self, DocCommentsOwner}, |
6 | match_ast, AstNode, | 6 | match_ast, AstNode, |
@@ -24,13 +24,14 @@ pub(crate) fn goto_definition( | |||
24 | let original_token = pick_best(file.token_at_offset(position.offset))?; | 24 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
25 | let token = descend_into_macros(db, position.file_id, original_token.clone()); | 25 | let token = descend_into_macros(db, position.file_id, original_token.clone()); |
26 | 26 | ||
27 | let mut sb = SourceBinder::new(db); | ||
27 | let nav_targets = match_ast! { | 28 | let nav_targets = match_ast! { |
28 | match (token.value.parent()) { | 29 | match (token.value.parent()) { |
29 | ast::NameRef(name_ref) => { | 30 | ast::NameRef(name_ref) => { |
30 | reference_definition(db, token.with_value(&name_ref)).to_vec() | 31 | reference_definition(&mut sb, token.with_value(&name_ref)).to_vec() |
31 | }, | 32 | }, |
32 | ast::Name(name) => { | 33 | ast::Name(name) => { |
33 | name_definition(db, token.with_value(&name))? | 34 | name_definition(&mut sb, token.with_value(&name))? |
34 | }, | 35 | }, |
35 | _ => return None, | 36 | _ => return None, |
36 | } | 37 | } |
@@ -67,19 +68,19 @@ impl ReferenceResult { | |||
67 | } | 68 | } |
68 | 69 | ||
69 | pub(crate) fn reference_definition( | 70 | pub(crate) fn reference_definition( |
70 | db: &RootDatabase, | 71 | sb: &mut SourceBinder<RootDatabase>, |
71 | name_ref: InFile<&ast::NameRef>, | 72 | name_ref: InFile<&ast::NameRef>, |
72 | ) -> ReferenceResult { | 73 | ) -> ReferenceResult { |
73 | use self::ReferenceResult::*; | 74 | use self::ReferenceResult::*; |
74 | 75 | ||
75 | let name_kind = classify_name_ref(db, name_ref).map(|d| d.kind); | 76 | let name_kind = classify_name_ref(sb, name_ref).map(|d| d.kind); |
76 | match name_kind { | 77 | match name_kind { |
77 | Some(Macro(it)) => return Exact(it.to_nav(db)), | 78 | Some(Macro(it)) => return Exact(it.to_nav(sb.db)), |
78 | Some(Field(it)) => return Exact(it.to_nav(db)), | 79 | Some(Field(it)) => return Exact(it.to_nav(sb.db)), |
79 | Some(TypeParam(it)) => return Exact(it.to_nav(db)), | 80 | Some(TypeParam(it)) => return Exact(it.to_nav(sb.db)), |
80 | Some(AssocItem(it)) => return Exact(it.to_nav(db)), | 81 | Some(AssocItem(it)) => return Exact(it.to_nav(sb.db)), |
81 | Some(Local(it)) => return Exact(it.to_nav(db)), | 82 | Some(Local(it)) => return Exact(it.to_nav(sb.db)), |
82 | Some(Def(def)) => match NavigationTarget::from_def(db, def) { | 83 | Some(Def(def)) => match NavigationTarget::from_def(sb.db, def) { |
83 | Some(nav) => return Exact(nav), | 84 | Some(nav) => return Exact(nav), |
84 | None => return Approximate(vec![]), | 85 | None => return Approximate(vec![]), |
85 | }, | 86 | }, |
@@ -87,21 +88,21 @@ pub(crate) fn reference_definition( | |||
87 | // FIXME: ideally, this should point to the type in the impl, and | 88 | // FIXME: ideally, this should point to the type in the impl, and |
88 | // not at the whole impl. And goto **type** definition should bring | 89 | // not at the whole impl. And goto **type** definition should bring |
89 | // us to the actual type | 90 | // us to the actual type |
90 | return Exact(imp.to_nav(db)); | 91 | return Exact(imp.to_nav(sb.db)); |
91 | } | 92 | } |
92 | None => {} | 93 | None => {} |
93 | }; | 94 | }; |
94 | 95 | ||
95 | // Fallback index based approach: | 96 | // Fallback index based approach: |
96 | let navs = crate::symbol_index::index_resolve(db, name_ref.value) | 97 | let navs = crate::symbol_index::index_resolve(sb.db, name_ref.value) |
97 | .into_iter() | 98 | .into_iter() |
98 | .map(|s| s.to_nav(db)) | 99 | .map(|s| s.to_nav(sb.db)) |
99 | .collect(); | 100 | .collect(); |
100 | Approximate(navs) | 101 | Approximate(navs) |
101 | } | 102 | } |
102 | 103 | ||
103 | pub(crate) fn name_definition( | 104 | fn name_definition( |
104 | db: &RootDatabase, | 105 | sb: &mut SourceBinder<RootDatabase>, |
105 | name: InFile<&ast::Name>, | 106 | name: InFile<&ast::Name>, |
106 | ) -> Option<Vec<NavigationTarget>> { | 107 | ) -> Option<Vec<NavigationTarget>> { |
107 | let parent = name.value.syntax().parent()?; | 108 | let parent = name.value.syntax().parent()?; |
@@ -109,14 +110,14 @@ pub(crate) fn name_definition( | |||
109 | if let Some(module) = ast::Module::cast(parent.clone()) { | 110 | if let Some(module) = ast::Module::cast(parent.clone()) { |
110 | if module.has_semi() { | 111 | if module.has_semi() { |
111 | let src = name.with_value(module); | 112 | let src = name.with_value(module); |
112 | if let Some(child_module) = hir::Module::from_declaration(db, src) { | 113 | if let Some(child_module) = sb.to_def(src) { |
113 | let nav = child_module.to_nav(db); | 114 | let nav = child_module.to_nav(sb.db); |
114 | return Some(vec![nav]); | 115 | return Some(vec![nav]); |
115 | } | 116 | } |
116 | } | 117 | } |
117 | } | 118 | } |
118 | 119 | ||
119 | if let Some(nav) = named_target(db, name.with_value(&parent)) { | 120 | if let Some(nav) = named_target(sb.db, name.with_value(&parent)) { |
120 | return Some(vec![nav]); | 121 | return Some(vec![nav]); |
121 | } | 122 | } |
122 | 123 | ||
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 5548681f1..6661e5cb2 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{db::AstDatabase, Adt, HasSource, HirDisplay}; | 3 | use hir::{db::AstDatabase, Adt, HasSource, HirDisplay, SourceBinder}; |
4 | use ra_db::SourceDatabase; | 4 | use ra_db::SourceDatabase; |
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | algo::find_covering_element, | 6 | algo::find_covering_element, |
@@ -152,13 +152,14 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
152 | 152 | ||
153 | let mut res = HoverResult::new(); | 153 | let mut res = HoverResult::new(); |
154 | 154 | ||
155 | let mut sb = SourceBinder::new(db); | ||
155 | if let Some((range, name_kind)) = match_ast! { | 156 | if let Some((range, name_kind)) = match_ast! { |
156 | match (token.value.parent()) { | 157 | match (token.value.parent()) { |
157 | ast::NameRef(name_ref) => { | 158 | ast::NameRef(name_ref) => { |
158 | classify_name_ref(db, token.with_value(&name_ref)).map(|d| (name_ref.syntax().text_range(), d.kind)) | 159 | classify_name_ref(&mut sb, token.with_value(&name_ref)).map(|d| (name_ref.syntax().text_range(), d.kind)) |
159 | }, | 160 | }, |
160 | ast::Name(name) => { | 161 | ast::Name(name) => { |
161 | classify_name(db, token.with_value(&name)).map(|d| (name.syntax().text_range(), d.kind)) | 162 | classify_name(&mut sb, token.with_value(&name)).map(|d| (name.syntax().text_range(), d.kind)) |
162 | }, | 163 | }, |
163 | _ => None, | 164 | _ => None, |
164 | } | 165 | } |
@@ -742,7 +743,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
742 | } | 743 | } |
743 | fn foo(bar:u32) { | 744 | fn foo(bar:u32) { |
744 | let a = id!(ba<|>r); | 745 | let a = id!(ba<|>r); |
745 | } | 746 | } |
746 | ", | 747 | ", |
747 | &["u32"], | 748 | &["u32"], |
748 | ); | 749 | ); |
diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/impls.rs index 31195036e..9834025d3 100644 --- a/crates/ra_ide/src/impls.rs +++ b/crates/ra_ide/src/impls.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{FromSource, ImplBlock}; | 3 | use hir::{Crate, ImplBlock, SourceBinder}; |
4 | use ra_db::SourceDatabase; | 4 | use ra_db::SourceDatabase; |
5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; | 5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; |
6 | 6 | ||
@@ -12,22 +12,19 @@ pub(crate) fn goto_implementation( | |||
12 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { | 12 | ) -> Option<RangeInfo<Vec<NavigationTarget>>> { |
13 | let parse = db.parse(position.file_id); | 13 | let parse = db.parse(position.file_id); |
14 | let syntax = parse.tree().syntax().clone(); | 14 | let syntax = parse.tree().syntax().clone(); |
15 | let mut sb = SourceBinder::new(db); | ||
15 | 16 | ||
16 | let src = hir::ModuleSource::from_position(db, position); | 17 | let krate = sb.to_module_def(position.file_id)?.krate(); |
17 | let module = hir::Module::from_definition( | ||
18 | db, | ||
19 | hir::InFile { file_id: position.file_id.into(), value: src }, | ||
20 | )?; | ||
21 | 18 | ||
22 | if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) { | 19 | if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) { |
23 | return Some(RangeInfo::new( | 20 | return Some(RangeInfo::new( |
24 | nominal_def.syntax().text_range(), | 21 | nominal_def.syntax().text_range(), |
25 | impls_for_def(db, position, &nominal_def, module)?, | 22 | impls_for_def(&mut sb, position, &nominal_def, krate)?, |
26 | )); | 23 | )); |
27 | } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(&syntax, position.offset) { | 24 | } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(&syntax, position.offset) { |
28 | return Some(RangeInfo::new( | 25 | return Some(RangeInfo::new( |
29 | trait_def.syntax().text_range(), | 26 | trait_def.syntax().text_range(), |
30 | impls_for_trait(db, position, &trait_def, module)?, | 27 | impls_for_trait(&mut sb, position, &trait_def, krate)?, |
31 | )); | 28 | )); |
32 | } | 29 | } |
33 | 30 | ||
@@ -35,51 +32,49 @@ pub(crate) fn goto_implementation( | |||
35 | } | 32 | } |
36 | 33 | ||
37 | fn impls_for_def( | 34 | fn impls_for_def( |
38 | db: &RootDatabase, | 35 | sb: &mut SourceBinder<RootDatabase>, |
39 | position: FilePosition, | 36 | position: FilePosition, |
40 | node: &ast::NominalDef, | 37 | node: &ast::NominalDef, |
41 | module: hir::Module, | 38 | krate: Crate, |
42 | ) -> Option<Vec<NavigationTarget>> { | 39 | ) -> Option<Vec<NavigationTarget>> { |
43 | let ty = match node { | 40 | let ty = match node { |
44 | ast::NominalDef::StructDef(def) => { | 41 | ast::NominalDef::StructDef(def) => { |
45 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; | 42 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; |
46 | hir::Struct::from_source(db, src)?.ty(db) | 43 | sb.to_def(src)?.ty(sb.db) |
47 | } | 44 | } |
48 | ast::NominalDef::EnumDef(def) => { | 45 | ast::NominalDef::EnumDef(def) => { |
49 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; | 46 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; |
50 | hir::Enum::from_source(db, src)?.ty(db) | 47 | sb.to_def(src)?.ty(sb.db) |
51 | } | 48 | } |
52 | ast::NominalDef::UnionDef(def) => { | 49 | ast::NominalDef::UnionDef(def) => { |
53 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; | 50 | let src = hir::InFile { file_id: position.file_id.into(), value: def.clone() }; |
54 | hir::Union::from_source(db, src)?.ty(db) | 51 | sb.to_def(src)?.ty(sb.db) |
55 | } | 52 | } |
56 | }; | 53 | }; |
57 | 54 | ||
58 | let krate = module.krate(); | 55 | let impls = ImplBlock::all_in_crate(sb.db, krate); |
59 | let impls = ImplBlock::all_in_crate(db, krate); | ||
60 | 56 | ||
61 | Some( | 57 | Some( |
62 | impls | 58 | impls |
63 | .into_iter() | 59 | .into_iter() |
64 | .filter(|impl_block| ty.is_equal_for_find_impls(&impl_block.target_ty(db))) | 60 | .filter(|impl_block| ty.is_equal_for_find_impls(&impl_block.target_ty(sb.db))) |
65 | .map(|imp| imp.to_nav(db)) | 61 | .map(|imp| imp.to_nav(sb.db)) |
66 | .collect(), | 62 | .collect(), |
67 | ) | 63 | ) |
68 | } | 64 | } |
69 | 65 | ||
70 | fn impls_for_trait( | 66 | fn impls_for_trait( |
71 | db: &RootDatabase, | 67 | sb: &mut SourceBinder<RootDatabase>, |
72 | position: FilePosition, | 68 | position: FilePosition, |
73 | node: &ast::TraitDef, | 69 | node: &ast::TraitDef, |
74 | module: hir::Module, | 70 | krate: Crate, |
75 | ) -> Option<Vec<NavigationTarget>> { | 71 | ) -> Option<Vec<NavigationTarget>> { |
76 | let src = hir::InFile { file_id: position.file_id.into(), value: node.clone() }; | 72 | let src = hir::InFile { file_id: position.file_id.into(), value: node.clone() }; |
77 | let tr = hir::Trait::from_source(db, src)?; | 73 | let tr = sb.to_def(src)?; |
78 | 74 | ||
79 | let krate = module.krate(); | 75 | let impls = ImplBlock::for_trait(sb.db, krate, tr); |
80 | let impls = ImplBlock::for_trait(db, krate, tr); | ||
81 | 76 | ||
82 | Some(impls.into_iter().map(|imp| imp.to_nav(db)).collect()) | 77 | Some(impls.into_iter().map(|imp| imp.to_nav(sb.db)).collect()) |
83 | } | 78 | } |
84 | 79 | ||
85 | #[cfg(test)] | 80 | #[cfg(test)] |
@@ -210,7 +205,7 @@ mod tests { | |||
210 | " | 205 | " |
211 | //- /lib.rs | 206 | //- /lib.rs |
212 | #[derive(Copy)] | 207 | #[derive(Copy)] |
213 | struct Foo<|>; | 208 | struct Foo<|>; |
214 | ", | 209 | ", |
215 | &["impl IMPL_BLOCK FileId(1) [0; 15)"], | 210 | &["impl IMPL_BLOCK FileId(1) [0; 15)"], |
216 | ); | 211 | ); |
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 977aafc51..1b631c7cd 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -1,18 +1,19 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{HirDisplay, SourceAnalyzer}; | 3 | use hir::{HirDisplay, SourceAnalyzer, SourceBinder}; |
4 | use once_cell::unsync::Lazy; | 4 | use once_cell::unsync::Lazy; |
5 | use ra_prof::profile; | 5 | use ra_prof::profile; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | ast::{self, AstNode, TypeAscriptionOwner}, | 7 | ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, |
8 | match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, | 8 | match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::{db::RootDatabase, FileId}; | 11 | use crate::{db::RootDatabase, FileId, FunctionSignature}; |
12 | 12 | ||
13 | #[derive(Debug, PartialEq, Eq)] | 13 | #[derive(Debug, PartialEq, Eq)] |
14 | pub enum InlayKind { | 14 | pub enum InlayKind { |
15 | TypeHint, | 15 | TypeHint, |
16 | ParameterHint, | ||
16 | } | 17 | } |
17 | 18 | ||
18 | #[derive(Debug)] | 19 | #[derive(Debug)] |
@@ -28,22 +29,24 @@ pub(crate) fn inlay_hints( | |||
28 | file: &SourceFile, | 29 | file: &SourceFile, |
29 | max_inlay_hint_length: Option<usize>, | 30 | max_inlay_hint_length: Option<usize>, |
30 | ) -> Vec<InlayHint> { | 31 | ) -> Vec<InlayHint> { |
31 | file.syntax() | 32 | let mut sb = SourceBinder::new(db); |
32 | .descendants() | 33 | let mut res = Vec::new(); |
33 | .flat_map(|node| get_inlay_hints(db, file_id, &node, max_inlay_hint_length)) | 34 | for node in file.syntax().descendants() { |
34 | .flatten() | 35 | get_inlay_hints(&mut res, &mut sb, file_id, &node, max_inlay_hint_length); |
35 | .collect() | 36 | } |
37 | res | ||
36 | } | 38 | } |
37 | 39 | ||
38 | fn get_inlay_hints( | 40 | fn get_inlay_hints( |
39 | db: &RootDatabase, | 41 | acc: &mut Vec<InlayHint>, |
42 | sb: &mut SourceBinder<RootDatabase>, | ||
40 | file_id: FileId, | 43 | file_id: FileId, |
41 | node: &SyntaxNode, | 44 | node: &SyntaxNode, |
42 | max_inlay_hint_length: Option<usize>, | 45 | max_inlay_hint_length: Option<usize>, |
43 | ) -> Option<Vec<InlayHint>> { | 46 | ) -> Option<()> { |
44 | let _p = profile("get_inlay_hints"); | 47 | let _p = profile("get_inlay_hints"); |
45 | let analyzer = | 48 | let db = sb.db; |
46 | Lazy::new(|| SourceAnalyzer::new(db, hir::InFile::new(file_id.into(), node), None)); | 49 | let analyzer = Lazy::new(move || sb.analyze(hir::InFile::new(file_id.into(), node), None)); |
47 | match_ast! { | 50 | match_ast! { |
48 | match node { | 51 | match node { |
49 | ast::LetStmt(it) => { | 52 | ast::LetStmt(it) => { |
@@ -51,7 +54,7 @@ fn get_inlay_hints( | |||
51 | return None; | 54 | return None; |
52 | } | 55 | } |
53 | let pat = it.pat()?; | 56 | let pat = it.pat()?; |
54 | Some(get_pat_type_hints(db, &analyzer, pat, false, max_inlay_hint_length)) | 57 | get_pat_type_hints(acc, db, &analyzer, pat, false, max_inlay_hint_length); |
55 | }, | 58 | }, |
56 | ast::LambdaExpr(it) => { | 59 | ast::LambdaExpr(it) => { |
57 | it.param_list().map(|param_list| { | 60 | it.param_list().map(|param_list| { |
@@ -59,49 +62,115 @@ fn get_inlay_hints( | |||
59 | .params() | 62 | .params() |
60 | .filter(|closure_param| closure_param.ascribed_type().is_none()) | 63 | .filter(|closure_param| closure_param.ascribed_type().is_none()) |
61 | .filter_map(|closure_param| closure_param.pat()) | 64 | .filter_map(|closure_param| closure_param.pat()) |
62 | .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, false, max_inlay_hint_length)) | 65 | .for_each(|root_pat| get_pat_type_hints(acc, db, &analyzer, root_pat, false, max_inlay_hint_length)) |
63 | .flatten() | 66 | }); |
64 | .collect() | ||
65 | }) | ||
66 | }, | 67 | }, |
67 | ast::ForExpr(it) => { | 68 | ast::ForExpr(it) => { |
68 | let pat = it.pat()?; | 69 | let pat = it.pat()?; |
69 | Some(get_pat_type_hints(db, &analyzer, pat, false, max_inlay_hint_length)) | 70 | get_pat_type_hints(acc, db, &analyzer, pat, false, max_inlay_hint_length); |
70 | }, | 71 | }, |
71 | ast::IfExpr(it) => { | 72 | ast::IfExpr(it) => { |
72 | let pat = it.condition()?.pat()?; | 73 | let pat = it.condition()?.pat()?; |
73 | Some(get_pat_type_hints(db, &analyzer, pat, true, max_inlay_hint_length)) | 74 | get_pat_type_hints(acc, db, &analyzer, pat, true, max_inlay_hint_length); |
74 | }, | 75 | }, |
75 | ast::WhileExpr(it) => { | 76 | ast::WhileExpr(it) => { |
76 | let pat = it.condition()?.pat()?; | 77 | let pat = it.condition()?.pat()?; |
77 | Some(get_pat_type_hints(db, &analyzer, pat, true, max_inlay_hint_length)) | 78 | get_pat_type_hints(acc, db, &analyzer, pat, true, max_inlay_hint_length); |
78 | }, | 79 | }, |
79 | ast::MatchArmList(it) => { | 80 | ast::MatchArmList(it) => { |
80 | Some( | 81 | it.arms() |
81 | it | 82 | .map(|match_arm| match_arm.pats()) |
82 | .arms() | 83 | .flatten() |
83 | .map(|match_arm| match_arm.pats()) | 84 | .for_each(|root_pat| get_pat_type_hints(acc, db, &analyzer, root_pat, true, max_inlay_hint_length)); |
84 | .flatten() | 85 | }, |
85 | .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, true, max_inlay_hint_length)) | 86 | ast::CallExpr(it) => { |
86 | .flatten() | 87 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); |
87 | .collect(), | 88 | }, |
88 | ) | 89 | ast::MethodCallExpr(it) => { |
89 | }, | 90 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); |
90 | _ => None, | 91 | }, |
92 | _ => (), | ||
91 | } | 93 | } |
94 | }; | ||
95 | Some(()) | ||
96 | } | ||
97 | |||
98 | fn get_param_name_hints( | ||
99 | acc: &mut Vec<InlayHint>, | ||
100 | db: &RootDatabase, | ||
101 | analyzer: &SourceAnalyzer, | ||
102 | expr: ast::Expr, | ||
103 | ) -> Option<()> { | ||
104 | let args = match &expr { | ||
105 | ast::Expr::CallExpr(expr) => expr.arg_list()?.args(), | ||
106 | ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(), | ||
107 | _ => return None, | ||
108 | }; | ||
109 | |||
110 | let mut parameters = get_fn_signature(db, analyzer, &expr)?.parameter_names.into_iter(); | ||
111 | |||
112 | if let ast::Expr::MethodCallExpr(_) = &expr { | ||
113 | parameters.next(); | ||
114 | }; | ||
115 | |||
116 | let hints = parameters | ||
117 | .zip(args) | ||
118 | .filter_map(|(param, arg)| { | ||
119 | if arg.syntax().kind() == SyntaxKind::LITERAL { | ||
120 | Some((arg.syntax().text_range(), param)) | ||
121 | } else { | ||
122 | None | ||
123 | } | ||
124 | }) | ||
125 | .map(|(range, param_name)| InlayHint { | ||
126 | range, | ||
127 | kind: InlayKind::ParameterHint, | ||
128 | label: param_name.into(), | ||
129 | }); | ||
130 | |||
131 | acc.extend(hints); | ||
132 | Some(()) | ||
133 | } | ||
134 | |||
135 | fn get_fn_signature( | ||
136 | db: &RootDatabase, | ||
137 | analyzer: &SourceAnalyzer, | ||
138 | expr: &ast::Expr, | ||
139 | ) -> Option<FunctionSignature> { | ||
140 | match expr { | ||
141 | ast::Expr::CallExpr(expr) => { | ||
142 | // FIXME: Type::as_callable is broken for closures | ||
143 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; | ||
144 | match callable_def { | ||
145 | hir::CallableDef::FunctionId(it) => { | ||
146 | let fn_def = it.into(); | ||
147 | Some(FunctionSignature::from_hir(db, fn_def)) | ||
148 | } | ||
149 | hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into()), | ||
150 | hir::CallableDef::EnumVariantId(it) => { | ||
151 | FunctionSignature::from_enum_variant(db, it.into()) | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | ast::Expr::MethodCallExpr(expr) => { | ||
156 | let fn_def = analyzer.resolve_method_call(&expr)?; | ||
157 | Some(FunctionSignature::from_hir(db, fn_def)) | ||
158 | } | ||
159 | _ => None, | ||
92 | } | 160 | } |
93 | } | 161 | } |
94 | 162 | ||
95 | fn get_pat_type_hints( | 163 | fn get_pat_type_hints( |
164 | acc: &mut Vec<InlayHint>, | ||
96 | db: &RootDatabase, | 165 | db: &RootDatabase, |
97 | analyzer: &SourceAnalyzer, | 166 | analyzer: &SourceAnalyzer, |
98 | root_pat: ast::Pat, | 167 | root_pat: ast::Pat, |
99 | skip_root_pat_hint: bool, | 168 | skip_root_pat_hint: bool, |
100 | max_inlay_hint_length: Option<usize>, | 169 | max_inlay_hint_length: Option<usize>, |
101 | ) -> Vec<InlayHint> { | 170 | ) { |
102 | let original_pat = &root_pat.clone(); | 171 | let original_pat = &root_pat.clone(); |
103 | 172 | ||
104 | get_leaf_pats(root_pat) | 173 | let hints = get_leaf_pats(root_pat) |
105 | .into_iter() | 174 | .into_iter() |
106 | .filter(|pat| !skip_root_pat_hint || pat != original_pat) | 175 | .filter(|pat| !skip_root_pat_hint || pat != original_pat) |
107 | .filter_map(|pat| { | 176 | .filter_map(|pat| { |
@@ -115,8 +184,9 @@ fn get_pat_type_hints( | |||
115 | range, | 184 | range, |
116 | kind: InlayKind::TypeHint, | 185 | kind: InlayKind::TypeHint, |
117 | label: pat_type.display_truncated(db, max_inlay_hint_length).to_string().into(), | 186 | label: pat_type.display_truncated(db, max_inlay_hint_length).to_string().into(), |
118 | }) | 187 | }); |
119 | .collect() | 188 | |
189 | acc.extend(hints); | ||
120 | } | 190 | } |
121 | 191 | ||
122 | fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> { | 192 | fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> { |
@@ -605,4 +675,71 @@ fn main() { | |||
605 | "### | 675 | "### |
606 | ); | 676 | ); |
607 | } | 677 | } |
678 | |||
679 | #[test] | ||
680 | fn function_call_parameter_hint() { | ||
681 | let (analysis, file_id) = single_file( | ||
682 | r#" | ||
683 | struct Test {} | ||
684 | |||
685 | impl Test { | ||
686 | fn method(&self, param: i32) -> i32 { | ||
687 | param * 2 | ||
688 | } | ||
689 | } | ||
690 | |||
691 | fn test_func(foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { | ||
692 | foo + bar | ||
693 | } | ||
694 | |||
695 | fn main() { | ||
696 | let not_literal = 1; | ||
697 | let _: i32 = test_func(1, 2, "hello", 3, not_literal); | ||
698 | let t: Test = Test {}; | ||
699 | t.method(123); | ||
700 | Test::method(&t, 3456); | ||
701 | }"#, | ||
702 | ); | ||
703 | |||
704 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" | ||
705 | [ | ||
706 | InlayHint { | ||
707 | range: [207; 218), | ||
708 | kind: TypeHint, | ||
709 | label: "i32", | ||
710 | }, | ||
711 | InlayHint { | ||
712 | range: [251; 252), | ||
713 | kind: ParameterHint, | ||
714 | label: "foo", | ||
715 | }, | ||
716 | InlayHint { | ||
717 | range: [254; 255), | ||
718 | kind: ParameterHint, | ||
719 | label: "bar", | ||
720 | }, | ||
721 | InlayHint { | ||
722 | range: [257; 264), | ||
723 | kind: ParameterHint, | ||
724 | label: "msg", | ||
725 | }, | ||
726 | InlayHint { | ||
727 | range: [266; 267), | ||
728 | kind: ParameterHint, | ||
729 | label: "_", | ||
730 | }, | ||
731 | InlayHint { | ||
732 | range: [323; 326), | ||
733 | kind: ParameterHint, | ||
734 | label: "param", | ||
735 | }, | ||
736 | InlayHint { | ||
737 | range: [350; 354), | ||
738 | kind: ParameterHint, | ||
739 | label: "param", | ||
740 | }, | ||
741 | ] | ||
742 | "### | ||
743 | ); | ||
744 | } | ||
608 | } | 745 | } |
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index f5a788c07..2dbccfc3b 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs | |||
@@ -1,17 +1,23 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_db::{CrateId, FileId, FilePosition, SourceDatabase}; | 3 | use ra_db::{CrateId, FileId, FilePosition, SourceDatabase}; |
4 | use ra_syntax::{ | ||
5 | algo::find_node_at_offset, | ||
6 | ast::{self, AstNode}, | ||
7 | }; | ||
4 | 8 | ||
5 | use crate::{db::RootDatabase, NavigationTarget}; | 9 | use crate::{db::RootDatabase, NavigationTarget}; |
6 | 10 | ||
7 | /// This returns `Vec` because a module may be included from several places. We | 11 | /// This returns `Vec` because a module may be included from several places. We |
8 | /// don't handle this case yet though, so the Vec has length at most one. | 12 | /// don't handle this case yet though, so the Vec has length at most one. |
9 | pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { | 13 | pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { |
10 | let src = hir::ModuleSource::from_position(db, position); | 14 | let mut sb = hir::SourceBinder::new(db); |
11 | let module = match hir::Module::from_definition( | 15 | let parse = db.parse(position.file_id); |
12 | db, | 16 | let module = match find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset) { |
13 | hir::InFile { file_id: position.file_id.into(), value: src }, | 17 | Some(module) => sb.to_def(hir::InFile::new(position.file_id.into(), module)), |
14 | ) { | 18 | None => sb.to_module_def(position.file_id), |
19 | }; | ||
20 | let module = match module { | ||
15 | None => return Vec::new(), | 21 | None => return Vec::new(), |
16 | Some(it) => it, | 22 | Some(it) => it, |
17 | }; | 23 | }; |
@@ -21,14 +27,11 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na | |||
21 | 27 | ||
22 | /// Returns `Vec` for the same reason as `parent_module` | 28 | /// Returns `Vec` for the same reason as `parent_module` |
23 | pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { | 29 | pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { |
24 | let source_file = db.parse(file_id).tree(); | 30 | let mut sb = hir::SourceBinder::new(db); |
25 | let src = hir::ModuleSource::SourceFile(source_file); | 31 | let module = match sb.to_module_def(file_id) { |
26 | let module = | 32 | Some(it) => it, |
27 | match hir::Module::from_definition(db, hir::InFile { file_id: file_id.into(), value: src }) | 33 | None => return Vec::new(), |
28 | { | 34 | }; |
29 | Some(it) => it, | ||
30 | None => return Vec::new(), | ||
31 | }; | ||
32 | let krate = module.krate(); | 35 | let krate = module.krate(); |
33 | vec![krate.into()] | 36 | vec![krate.into()] |
34 | } | 37 | } |
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 4e52e0e7b..5e2fe1905 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -14,7 +14,7 @@ mod name_definition; | |||
14 | mod rename; | 14 | mod rename; |
15 | mod search_scope; | 15 | mod search_scope; |
16 | 16 | ||
17 | use hir::InFile; | 17 | use hir::{InFile, SourceBinder}; |
18 | use once_cell::unsync::Lazy; | 18 | use once_cell::unsync::Lazy; |
19 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | 19 | use ra_db::{SourceDatabase, SourceDatabaseExt}; |
20 | use ra_prof::profile; | 20 | use ra_prof::profile; |
@@ -166,18 +166,19 @@ pub(crate) fn find_all_refs( | |||
166 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) | 166 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) |
167 | } | 167 | } |
168 | 168 | ||
169 | fn find_name<'a>( | 169 | fn find_name( |
170 | db: &RootDatabase, | 170 | db: &RootDatabase, |
171 | syntax: &SyntaxNode, | 171 | syntax: &SyntaxNode, |
172 | position: FilePosition, | 172 | position: FilePosition, |
173 | ) -> Option<RangeInfo<(String, NameDefinition)>> { | 173 | ) -> Option<RangeInfo<(String, NameDefinition)>> { |
174 | let mut sb = SourceBinder::new(db); | ||
174 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { | 175 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { |
175 | let def = classify_name(db, InFile::new(position.file_id.into(), &name))?; | 176 | let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; |
176 | let range = name.syntax().text_range(); | 177 | let range = name.syntax().text_range(); |
177 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); | 178 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); |
178 | } | 179 | } |
179 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; | 180 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; |
180 | let def = classify_name_ref(db, InFile::new(position.file_id.into(), &name_ref))?; | 181 | let def = classify_name_ref(&mut sb, InFile::new(position.file_id.into(), &name_ref))?; |
181 | let range = name_ref.syntax().text_range(); | 182 | let range = name_ref.syntax().text_range(); |
182 | Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) | 183 | Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) |
183 | } | 184 | } |
@@ -195,7 +196,9 @@ fn process_definition( | |||
195 | 196 | ||
196 | for (file_id, search_range) in scope { | 197 | for (file_id, search_range) in scope { |
197 | let text = db.file_text(file_id); | 198 | let text = db.file_text(file_id); |
199 | |||
198 | let parse = Lazy::new(|| SourceFile::parse(&text)); | 200 | let parse = Lazy::new(|| SourceFile::parse(&text)); |
201 | let mut sb = Lazy::new(|| SourceBinder::new(db)); | ||
199 | 202 | ||
200 | for (idx, _) in text.match_indices(pat) { | 203 | for (idx, _) in text.match_indices(pat) { |
201 | let offset = TextUnit::from_usize(idx); | 204 | let offset = TextUnit::from_usize(idx); |
@@ -209,7 +212,11 @@ fn process_definition( | |||
209 | continue; | 212 | continue; |
210 | } | 213 | } |
211 | } | 214 | } |
212 | if let Some(d) = classify_name_ref(db, InFile::new(file_id.into(), &name_ref)) { | 215 | // FIXME: reuse sb |
216 | // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 | ||
217 | |||
218 | if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) | ||
219 | { | ||
213 | if d == def { | 220 | if d == def { |
214 | let kind = if name_ref | 221 | let kind = if name_ref |
215 | .syntax() | 222 | .syntax() |
@@ -253,13 +260,10 @@ fn decl_access( | |||
253 | let stmt = find_node_at_offset::<ast::LetStmt>(syntax, range.start())?; | 260 | let stmt = find_node_at_offset::<ast::LetStmt>(syntax, range.start())?; |
254 | if let Some(_) = stmt.initializer() { | 261 | if let Some(_) = stmt.initializer() { |
255 | let pat = stmt.pat()?; | 262 | let pat = stmt.pat()?; |
256 | match pat { | 263 | if let ast::Pat::BindPat(it) = pat { |
257 | ast::Pat::BindPat(it) => { | 264 | if it.name()?.text().as_str() == name { |
258 | if it.name()?.text().as_str() == name { | 265 | return Some(ReferenceAccess::Write); |
259 | return Some(ReferenceAccess::Write); | ||
260 | } | ||
261 | } | 266 | } |
262 | _ => {} | ||
263 | } | 267 | } |
264 | } | 268 | } |
265 | 269 | ||
@@ -286,7 +290,7 @@ fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<Referenc | |||
286 | } | 290 | } |
287 | } | 291 | } |
288 | } | 292 | } |
289 | return Some(ReferenceAccess::Read); | 293 | Some(ReferenceAccess::Read) |
290 | }, | 294 | }, |
291 | _ => {None} | 295 | _ => {None} |
292 | } | 296 | } |
@@ -312,7 +316,7 @@ mod tests { | |||
312 | } | 316 | } |
313 | impl Foo { | 317 | impl Foo { |
314 | fn f() -> i32 { 42 } | 318 | fn f() -> i32 { 42 } |
315 | } | 319 | } |
316 | fn main() { | 320 | fn main() { |
317 | let f: Foo; | 321 | let f: Foo; |
318 | f = Foo {a: Foo::f()}; | 322 | f = Foo {a: Foo::f()}; |
@@ -322,7 +326,7 @@ mod tests { | |||
322 | check_result( | 326 | check_result( |
323 | refs, | 327 | refs, |
324 | "Foo STRUCT_DEF FileId(1) [5; 39) [12; 15) Other", | 328 | "Foo STRUCT_DEF FileId(1) [5; 39) [12; 15) Other", |
325 | &["FileId(1) [142; 145) StructLiteral"], | 329 | &["FileId(1) [138; 141) StructLiteral"], |
326 | ); | 330 | ); |
327 | } | 331 | } |
328 | 332 | ||
diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs index 3483a7176..46cba30a3 100644 --- a/crates/ra_ide/src/references/classify.rs +++ b/crates/ra_ide/src/references/classify.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! Functions that are used to classify an element from its definition or reference. | 1 | //! Functions that are used to classify an element from its definition or reference. |
2 | 2 | ||
3 | use hir::{FromSource, InFile, Module, ModuleSource, PathResolution, SourceAnalyzer}; | 3 | use hir::{InFile, PathResolution, SourceBinder}; |
4 | use ra_prof::profile; | 4 | use ra_prof::profile; |
5 | use ra_syntax::{ast, match_ast, AstNode}; | 5 | use ra_syntax::{ast, match_ast, AstNode}; |
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
@@ -11,7 +11,10 @@ use super::{ | |||
11 | }; | 11 | }; |
12 | use crate::db::RootDatabase; | 12 | use crate::db::RootDatabase; |
13 | 13 | ||
14 | pub(crate) fn classify_name(db: &RootDatabase, name: InFile<&ast::Name>) -> Option<NameDefinition> { | 14 | pub(crate) fn classify_name( |
15 | sb: &mut SourceBinder<RootDatabase>, | ||
16 | name: InFile<&ast::Name>, | ||
17 | ) -> Option<NameDefinition> { | ||
15 | let _p = profile("classify_name"); | 18 | let _p = profile("classify_name"); |
16 | let parent = name.value.syntax().parent()?; | 19 | let parent = name.value.syntax().parent()?; |
17 | 20 | ||
@@ -19,90 +22,79 @@ pub(crate) fn classify_name(db: &RootDatabase, name: InFile<&ast::Name>) -> Opti | |||
19 | match parent { | 22 | match parent { |
20 | ast::BindPat(it) => { | 23 | ast::BindPat(it) => { |
21 | let src = name.with_value(it); | 24 | let src = name.with_value(it); |
22 | let local = hir::Local::from_source(db, src)?; | 25 | let local = sb.to_def(src)?; |
23 | Some(NameDefinition { | 26 | Some(NameDefinition { |
24 | visibility: None, | 27 | visibility: None, |
25 | container: local.module(db), | 28 | container: local.module(sb.db), |
26 | kind: NameKind::Local(local), | 29 | kind: NameKind::Local(local), |
27 | }) | 30 | }) |
28 | }, | 31 | }, |
29 | ast::RecordFieldDef(it) => { | 32 | ast::RecordFieldDef(it) => { |
30 | let ast = hir::FieldSource::Named(it); | 33 | let src = name.with_value(it); |
31 | let src = name.with_value(ast); | 34 | let field: hir::StructField = sb.to_def(src)?; |
32 | let field = hir::StructField::from_source(db, src)?; | 35 | Some(from_struct_field(sb.db, field)) |
33 | Some(from_struct_field(db, field)) | ||
34 | }, | 36 | }, |
35 | ast::Module(it) => { | 37 | ast::Module(it) => { |
36 | let def = { | 38 | let def = sb.to_def(name.with_value(it))?; |
37 | if !it.has_semi() { | 39 | Some(from_module_def(sb.db, def.into(), None)) |
38 | let ast = hir::ModuleSource::Module(it); | ||
39 | let src = name.with_value(ast); | ||
40 | hir::Module::from_definition(db, src) | ||
41 | } else { | ||
42 | let src = name.with_value(it); | ||
43 | hir::Module::from_declaration(db, src) | ||
44 | } | ||
45 | }?; | ||
46 | Some(from_module_def(db, def.into(), None)) | ||
47 | }, | 40 | }, |
48 | ast::StructDef(it) => { | 41 | ast::StructDef(it) => { |
49 | let src = name.with_value(it); | 42 | let src = name.with_value(it); |
50 | let def = hir::Struct::from_source(db, src)?; | 43 | let def: hir::Struct = sb.to_def(src)?; |
51 | Some(from_module_def(db, def.into(), None)) | 44 | Some(from_module_def(sb.db, def.into(), None)) |
52 | }, | 45 | }, |
53 | ast::EnumDef(it) => { | 46 | ast::EnumDef(it) => { |
54 | let src = name.with_value(it); | 47 | let src = name.with_value(it); |
55 | let def = hir::Enum::from_source(db, src)?; | 48 | let def: hir::Enum = sb.to_def(src)?; |
56 | Some(from_module_def(db, def.into(), None)) | 49 | Some(from_module_def(sb.db, def.into(), None)) |
57 | }, | 50 | }, |
58 | ast::TraitDef(it) => { | 51 | ast::TraitDef(it) => { |
59 | let src = name.with_value(it); | 52 | let src = name.with_value(it); |
60 | let def = hir::Trait::from_source(db, src)?; | 53 | let def: hir::Trait = sb.to_def(src)?; |
61 | Some(from_module_def(db, def.into(), None)) | 54 | Some(from_module_def(sb.db, def.into(), None)) |
62 | }, | 55 | }, |
63 | ast::StaticDef(it) => { | 56 | ast::StaticDef(it) => { |
64 | let src = name.with_value(it); | 57 | let src = name.with_value(it); |
65 | let def = hir::Static::from_source(db, src)?; | 58 | let def: hir::Static = sb.to_def(src)?; |
66 | Some(from_module_def(db, def.into(), None)) | 59 | Some(from_module_def(sb.db, def.into(), None)) |
67 | }, | 60 | }, |
68 | ast::EnumVariant(it) => { | 61 | ast::EnumVariant(it) => { |
69 | let src = name.with_value(it); | 62 | let src = name.with_value(it); |
70 | let def = hir::EnumVariant::from_source(db, src)?; | 63 | let def: hir::EnumVariant = sb.to_def(src)?; |
71 | Some(from_module_def(db, def.into(), None)) | 64 | Some(from_module_def(sb.db, def.into(), None)) |
72 | }, | 65 | }, |
73 | ast::FnDef(it) => { | 66 | ast::FnDef(it) => { |
74 | let src = name.with_value(it); | 67 | let src = name.with_value(it); |
75 | let def = hir::Function::from_source(db, src)?; | 68 | let def: hir::Function = sb.to_def(src)?; |
76 | if parent.parent().and_then(ast::ItemList::cast).is_some() { | 69 | if parent.parent().and_then(ast::ItemList::cast).is_some() { |
77 | Some(from_assoc_item(db, def.into())) | 70 | Some(from_assoc_item(sb.db, def.into())) |
78 | } else { | 71 | } else { |
79 | Some(from_module_def(db, def.into(), None)) | 72 | Some(from_module_def(sb.db, def.into(), None)) |
80 | } | 73 | } |
81 | }, | 74 | }, |
82 | ast::ConstDef(it) => { | 75 | ast::ConstDef(it) => { |
83 | let src = name.with_value(it); | 76 | let src = name.with_value(it); |
84 | let def = hir::Const::from_source(db, src)?; | 77 | let def: hir::Const = sb.to_def(src)?; |
85 | if parent.parent().and_then(ast::ItemList::cast).is_some() { | 78 | if parent.parent().and_then(ast::ItemList::cast).is_some() { |
86 | Some(from_assoc_item(db, def.into())) | 79 | Some(from_assoc_item(sb.db, def.into())) |
87 | } else { | 80 | } else { |
88 | Some(from_module_def(db, def.into(), None)) | 81 | Some(from_module_def(sb.db, def.into(), None)) |
89 | } | 82 | } |
90 | }, | 83 | }, |
91 | ast::TypeAliasDef(it) => { | 84 | ast::TypeAliasDef(it) => { |
92 | let src = name.with_value(it); | 85 | let src = name.with_value(it); |
93 | let def = hir::TypeAlias::from_source(db, src)?; | 86 | let def: hir::TypeAlias = sb.to_def(src)?; |
94 | if parent.parent().and_then(ast::ItemList::cast).is_some() { | 87 | if parent.parent().and_then(ast::ItemList::cast).is_some() { |
95 | Some(from_assoc_item(db, def.into())) | 88 | Some(from_assoc_item(sb.db, def.into())) |
96 | } else { | 89 | } else { |
97 | Some(from_module_def(db, def.into(), None)) | 90 | Some(from_module_def(sb.db, def.into(), None)) |
98 | } | 91 | } |
99 | }, | 92 | }, |
100 | ast::MacroCall(it) => { | 93 | ast::MacroCall(it) => { |
101 | let src = name.with_value(it); | 94 | let src = name.with_value(it); |
102 | let def = hir::MacroDef::from_source(db, src.clone())?; | 95 | let def = sb.to_def(src.clone())?; |
103 | 96 | ||
104 | let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax())); | 97 | let module = sb.to_module_def(src.file_id.original_file(sb.db))?; |
105 | let module = Module::from_definition(db, src.with_value(module_src))?; | ||
106 | 98 | ||
107 | Some(NameDefinition { | 99 | Some(NameDefinition { |
108 | visibility: None, | 100 | visibility: None, |
@@ -112,10 +104,10 @@ pub(crate) fn classify_name(db: &RootDatabase, name: InFile<&ast::Name>) -> Opti | |||
112 | }, | 104 | }, |
113 | ast::TypeParam(it) => { | 105 | ast::TypeParam(it) => { |
114 | let src = name.with_value(it); | 106 | let src = name.with_value(it); |
115 | let def = hir::TypeParam::from_source(db, src)?; | 107 | let def = sb.to_def(src)?; |
116 | Some(NameDefinition { | 108 | Some(NameDefinition { |
117 | visibility: None, | 109 | visibility: None, |
118 | container: def.module(db), | 110 | container: def.module(sb.db), |
119 | kind: NameKind::TypeParam(def), | 111 | kind: NameKind::TypeParam(def), |
120 | }) | 112 | }) |
121 | }, | 113 | }, |
@@ -125,25 +117,25 @@ pub(crate) fn classify_name(db: &RootDatabase, name: InFile<&ast::Name>) -> Opti | |||
125 | } | 117 | } |
126 | 118 | ||
127 | pub(crate) fn classify_name_ref( | 119 | pub(crate) fn classify_name_ref( |
128 | db: &RootDatabase, | 120 | sb: &mut SourceBinder<RootDatabase>, |
129 | name_ref: InFile<&ast::NameRef>, | 121 | name_ref: InFile<&ast::NameRef>, |
130 | ) -> Option<NameDefinition> { | 122 | ) -> Option<NameDefinition> { |
131 | let _p = profile("classify_name_ref"); | 123 | let _p = profile("classify_name_ref"); |
132 | 124 | ||
133 | let parent = name_ref.value.syntax().parent()?; | 125 | let parent = name_ref.value.syntax().parent()?; |
134 | let analyzer = SourceAnalyzer::new(db, name_ref.map(|it| it.syntax()), None); | 126 | let analyzer = sb.analyze(name_ref.map(|it| it.syntax()), None); |
135 | 127 | ||
136 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { | 128 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { |
137 | tested_by!(goto_def_for_methods); | 129 | tested_by!(goto_def_for_methods); |
138 | if let Some(func) = analyzer.resolve_method_call(&method_call) { | 130 | if let Some(func) = analyzer.resolve_method_call(&method_call) { |
139 | return Some(from_assoc_item(db, func.into())); | 131 | return Some(from_assoc_item(sb.db, func.into())); |
140 | } | 132 | } |
141 | } | 133 | } |
142 | 134 | ||
143 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | 135 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { |
144 | tested_by!(goto_def_for_fields); | 136 | tested_by!(goto_def_for_fields); |
145 | if let Some(field) = analyzer.resolve_field(&field_expr) { | 137 | if let Some(field) = analyzer.resolve_field(&field_expr) { |
146 | return Some(from_struct_field(db, field)); | 138 | return Some(from_struct_field(sb.db, field)); |
147 | } | 139 | } |
148 | } | 140 | } |
149 | 141 | ||
@@ -151,35 +143,37 @@ pub(crate) fn classify_name_ref( | |||
151 | tested_by!(goto_def_for_record_fields); | 143 | tested_by!(goto_def_for_record_fields); |
152 | tested_by!(goto_def_for_field_init_shorthand); | 144 | tested_by!(goto_def_for_field_init_shorthand); |
153 | if let Some(field_def) = analyzer.resolve_record_field(&record_field) { | 145 | if let Some(field_def) = analyzer.resolve_record_field(&record_field) { |
154 | return Some(from_struct_field(db, field_def)); | 146 | return Some(from_struct_field(sb.db, field_def)); |
155 | } | 147 | } |
156 | } | 148 | } |
157 | 149 | ||
158 | let ast = ModuleSource::from_child_node(db, name_ref.with_value(&parent)); | ||
159 | // FIXME: find correct container and visibility for each case | 150 | // FIXME: find correct container and visibility for each case |
160 | let container = Module::from_definition(db, name_ref.with_value(ast))?; | ||
161 | let visibility = None; | 151 | let visibility = None; |
152 | let container = sb.to_module_def(name_ref.file_id.original_file(sb.db))?; | ||
162 | 153 | ||
163 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { | 154 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { |
164 | tested_by!(goto_def_for_macros); | 155 | tested_by!(goto_def_for_macros); |
165 | if let Some(macro_def) = analyzer.resolve_macro_call(db, name_ref.with_value(¯o_call)) { | 156 | if let Some(macro_def) = |
157 | analyzer.resolve_macro_call(sb.db, name_ref.with_value(¯o_call)) | ||
158 | { | ||
166 | let kind = NameKind::Macro(macro_def); | 159 | let kind = NameKind::Macro(macro_def); |
167 | return Some(NameDefinition { kind, container, visibility }); | 160 | return Some(NameDefinition { kind, container, visibility }); |
168 | } | 161 | } |
169 | } | 162 | } |
170 | 163 | ||
171 | let path = name_ref.value.syntax().ancestors().find_map(ast::Path::cast)?; | 164 | let path = name_ref.value.syntax().ancestors().find_map(ast::Path::cast)?; |
172 | let resolved = analyzer.resolve_path(db, &path)?; | 165 | let resolved = analyzer.resolve_path(sb.db, &path)?; |
173 | match resolved { | 166 | match resolved { |
174 | PathResolution::Def(def) => Some(from_module_def(db, def, Some(container))), | 167 | PathResolution::Def(def) => Some(from_module_def(sb.db, def, Some(container))), |
175 | PathResolution::AssocItem(item) => Some(from_assoc_item(db, item)), | 168 | PathResolution::AssocItem(item) => Some(from_assoc_item(sb.db, item)), |
176 | PathResolution::Local(local) => { | 169 | PathResolution::Local(local) => { |
177 | let container = local.module(db); | ||
178 | let kind = NameKind::Local(local); | 170 | let kind = NameKind::Local(local); |
171 | let container = local.module(sb.db); | ||
179 | Some(NameDefinition { kind, container, visibility: None }) | 172 | Some(NameDefinition { kind, container, visibility: None }) |
180 | } | 173 | } |
181 | PathResolution::TypeParam(par) => { | 174 | PathResolution::TypeParam(par) => { |
182 | let kind = NameKind::TypeParam(par); | 175 | let kind = NameKind::TypeParam(par); |
176 | let container = par.module(sb.db); | ||
183 | Some(NameDefinition { kind, container, visibility }) | 177 | Some(NameDefinition { kind, container, visibility }) |
184 | } | 178 | } |
185 | PathResolution::Macro(def) => { | 179 | PathResolution::Macro(def) => { |
@@ -188,7 +182,7 @@ pub(crate) fn classify_name_ref( | |||
188 | } | 182 | } |
189 | PathResolution::SelfType(impl_block) => { | 183 | PathResolution::SelfType(impl_block) => { |
190 | let kind = NameKind::SelfType(impl_block); | 184 | let kind = NameKind::SelfType(impl_block); |
191 | let container = impl_block.module(db); | 185 | let container = impl_block.module(sb.db); |
192 | Some(NameDefinition { kind, container, visibility }) | 186 | Some(NameDefinition { kind, container, visibility }) |
193 | } | 187 | } |
194 | } | 188 | } |
diff --git a/crates/ra_ide/src/references/name_definition.rs b/crates/ra_ide/src/references/name_definition.rs index 8c67c8863..1e4226ab9 100644 --- a/crates/ra_ide/src/references/name_definition.rs +++ b/crates/ra_ide/src/references/name_definition.rs | |||
@@ -25,6 +25,8 @@ pub enum NameKind { | |||
25 | #[derive(PartialEq, Eq)] | 25 | #[derive(PartialEq, Eq)] |
26 | pub(crate) struct NameDefinition { | 26 | pub(crate) struct NameDefinition { |
27 | pub visibility: Option<ast::Visibility>, | 27 | pub visibility: Option<ast::Visibility>, |
28 | /// FIXME: this doesn't really make sense. For example, builtin types don't | ||
29 | /// really have a module. | ||
28 | pub container: Module, | 30 | pub container: Module, |
29 | pub kind: NameKind, | 31 | pub kind: NameKind, |
30 | } | 32 | } |
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index e02985dcd..626efb603 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -63,7 +63,7 @@ fn rename_mod( | |||
63 | let mut source_file_edits = Vec::new(); | 63 | let mut source_file_edits = Vec::new(); |
64 | let mut file_system_edits = Vec::new(); | 64 | let mut file_system_edits = Vec::new(); |
65 | let module_src = hir::InFile { file_id: position.file_id.into(), value: ast_module.clone() }; | 65 | let module_src = hir::InFile { file_id: position.file_id.into(), value: ast_module.clone() }; |
66 | if let Some(module) = hir::Module::from_declaration(db, module_src) { | 66 | if let Some(module) = hir::SourceBinder::new(db).to_def(module_src) { |
67 | let src = module.definition_source(db); | 67 | let src = module.definition_source(db); |
68 | let file_id = src.file_id.original_file(db); | 68 | let file_id = src.file_id.original_file(db); |
69 | match src.value { | 69 | match src.value { |
diff --git a/crates/ra_ide/src/references/search_scope.rs b/crates/ra_ide/src/references/search_scope.rs index 241dd358f..f8211a746 100644 --- a/crates/ra_ide/src/references/search_scope.rs +++ b/crates/ra_ide/src/references/search_scope.rs | |||
@@ -82,8 +82,7 @@ impl NameDefinition { | |||
82 | return SearchScope::new(res); | 82 | return SearchScope::new(res); |
83 | } | 83 | } |
84 | 84 | ||
85 | let vis = | 85 | let vis = self.visibility.as_ref().map(|v| v.syntax().to_string()).unwrap_or_default(); |
86 | self.visibility.as_ref().map(|v| v.syntax().to_string()).unwrap_or("".to_string()); | ||
87 | 86 | ||
88 | if vis.as_str() == "pub(super)" { | 87 | if vis.as_str() == "pub(super)" { |
89 | if let Some(parent_module) = self.container.parent(db) { | 88 | if let Some(parent_module) = self.container.parent(db) { |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index e213e1a06..7533692f6 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -66,8 +66,8 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Opti | |||
66 | return None; | 66 | return None; |
67 | } | 67 | } |
68 | let range = module.syntax().text_range(); | 68 | let range = module.syntax().text_range(); |
69 | let src = hir::ModuleSource::from_child_node(db, InFile::new(file_id.into(), &module.syntax())); | 69 | let mut sb = hir::SourceBinder::new(db); |
70 | let module = hir::Module::from_definition(db, InFile::new(file_id.into(), src))?; | 70 | let module = sb.to_def(InFile::new(file_id.into(), module))?; |
71 | 71 | ||
72 | let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); | 72 | let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); |
73 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) | 73 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 56a36f587..0411977b9 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use rustc_hash::{FxHashMap, FxHashSet}; | 3 | use rustc_hash::{FxHashMap, FxHashSet}; |
4 | 4 | ||
5 | use hir::{InFile, Name}; | 5 | use hir::{InFile, Name, SourceBinder}; |
6 | use ra_db::SourceDatabase; | 6 | use ra_db::SourceDatabase; |
7 | use ra_prof::profile; | 7 | use ra_prof::profile; |
8 | use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T}; | 8 | use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T}; |
@@ -84,6 +84,8 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
84 | hash((file_id, name, shadow_count)) | 84 | hash((file_id, name, shadow_count)) |
85 | } | 85 | } |
86 | 86 | ||
87 | let mut sb = SourceBinder::new(db); | ||
88 | |||
87 | // Visited nodes to handle highlighting priorities | 89 | // Visited nodes to handle highlighting priorities |
88 | // FIXME: retain only ranges here | 90 | // FIXME: retain only ranges here |
89 | let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default(); | 91 | let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default(); |
@@ -108,8 +110,8 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
108 | NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => continue, | 110 | NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => continue, |
109 | NAME_REF => { | 111 | NAME_REF => { |
110 | let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); | 112 | let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); |
111 | let name_kind = | 113 | let name_kind = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) |
112 | classify_name_ref(db, InFile::new(file_id.into(), &name_ref)).map(|d| d.kind); | 114 | .map(|d| d.kind); |
113 | match name_kind { | 115 | match name_kind { |
114 | Some(name_kind) => { | 116 | Some(name_kind) => { |
115 | if let Local(local) = &name_kind { | 117 | if let Local(local) = &name_kind { |
@@ -129,7 +131,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
129 | NAME => { | 131 | NAME => { |
130 | let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); | 132 | let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); |
131 | let name_kind = | 133 | let name_kind = |
132 | classify_name(db, InFile::new(file_id.into(), &name)).map(|d| d.kind); | 134 | classify_name(&mut sb, InFile::new(file_id.into(), &name)).map(|d| d.kind); |
133 | 135 | ||
134 | if let Some(Local(local)) = &name_kind { | 136 | if let Some(Local(local)) = &name_kind { |
135 | if let Some(name) = local.name(db) { | 137 | if let Some(name) = local.name(db) { |
@@ -308,9 +310,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
308 | 310 | ||
309 | #[cfg(test)] | 311 | #[cfg(test)] |
310 | mod tests { | 312 | mod tests { |
311 | use crate::mock_analysis::single_file; | 313 | use std::fs; |
314 | |||
312 | use test_utils::{assert_eq_text, project_dir, read_text}; | 315 | use test_utils::{assert_eq_text, project_dir, read_text}; |
313 | 316 | ||
317 | use crate::mock_analysis::{single_file, MockAnalysis}; | ||
318 | |||
314 | #[test] | 319 | #[test] |
315 | fn test_highlighting() { | 320 | fn test_highlighting() { |
316 | let (analysis, file_id) = single_file( | 321 | let (analysis, file_id) = single_file( |
@@ -357,7 +362,7 @@ impl<X> E<X> { | |||
357 | let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlighting.html"); | 362 | let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlighting.html"); |
358 | let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); | 363 | let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); |
359 | let expected_html = &read_text(&dst_file); | 364 | let expected_html = &read_text(&dst_file); |
360 | std::fs::write(dst_file, &actual_html).unwrap(); | 365 | fs::write(dst_file, &actual_html).unwrap(); |
361 | assert_eq_text!(expected_html, actual_html); | 366 | assert_eq_text!(expected_html, actual_html); |
362 | } | 367 | } |
363 | 368 | ||
@@ -383,7 +388,21 @@ fn bar() { | |||
383 | let dst_file = project_dir().join("crates/ra_ide/src/snapshots/rainbow_highlighting.html"); | 388 | let dst_file = project_dir().join("crates/ra_ide/src/snapshots/rainbow_highlighting.html"); |
384 | let actual_html = &analysis.highlight_as_html(file_id, true).unwrap(); | 389 | let actual_html = &analysis.highlight_as_html(file_id, true).unwrap(); |
385 | let expected_html = &read_text(&dst_file); | 390 | let expected_html = &read_text(&dst_file); |
386 | std::fs::write(dst_file, &actual_html).unwrap(); | 391 | fs::write(dst_file, &actual_html).unwrap(); |
387 | assert_eq_text!(expected_html, actual_html); | 392 | assert_eq_text!(expected_html, actual_html); |
388 | } | 393 | } |
394 | |||
395 | #[test] | ||
396 | fn accidentally_quadratic() { | ||
397 | let file = project_dir().join("crates/ra_syntax/test_data/accidentally_quadratic"); | ||
398 | let src = fs::read_to_string(file).unwrap(); | ||
399 | |||
400 | let mut mock = MockAnalysis::new(); | ||
401 | let file_id = mock.add_file("/main.rs", &src); | ||
402 | let host = mock.analysis_host(); | ||
403 | |||
404 | // let t = std::time::Instant::now(); | ||
405 | let _ = host.analysis().highlight(file_id).unwrap(); | ||
406 | // eprintln!("elapsed: {:?}", t.elapsed()); | ||
407 | } | ||
389 | } | 408 | } |