diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-03-02 18:01:50 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-02 18:01:50 +0000 |
commit | 7928ac4bbd8c75ac779d6da7e9cbe6aa314fb5bd (patch) | |
tree | 4a0f3b337f334d5355a79c60279bebefa0c68616 | |
parent | 71b53b2cae74d6f84c79d21b24754792a8435149 (diff) | |
parent | 2716a1fa3f8a7410248724a6ce5d4c28837db682 (diff) |
Merge #3405
3405: More principled approach for gotodef for field shorhand r=matklad a=matklad
Callers can now decide for themselves if they should prefer field or
local definition. By default, it's the local.
bors r+
🤖
Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r-- | crates/ra_hir/src/semantics.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir/src/source_analyzer.rs | 27 | ||||
-rw-r--r-- | crates/ra_ide/src/goto_definition.rs | 6 | ||||
-rw-r--r-- | crates/ra_ide/src/hover.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/references.rs | 8 | ||||
-rw-r--r-- | crates/ra_ide/src/references/classify.rs | 35 | ||||
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting.rs | 30 |
7 files changed, 79 insertions, 36 deletions
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index a0853957c..afc7f7ee7 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -107,8 +107,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
107 | self.analyze(field.syntax()).resolve_field(field) | 107 | self.analyze(field.syntax()).resolve_field(field) |
108 | } | 108 | } |
109 | 109 | ||
110 | pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<StructField> { | 110 | pub fn resolve_record_field( |
111 | self.analyze(field.syntax()).resolve_record_field(field) | 111 | &self, |
112 | field: &ast::RecordField, | ||
113 | ) -> Option<(StructField, Option<Local>)> { | ||
114 | self.analyze(field.syntax()).resolve_record_field(self.db, field) | ||
112 | } | 115 | } |
113 | 116 | ||
114 | pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<VariantDef> { | 117 | pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<VariantDef> { |
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index 4c121eb73..015389fb0 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | //! | 5 | //! |
6 | //! So, this modules should not be used during hir construction, it exists | 6 | //! So, this modules should not be used during hir construction, it exists |
7 | //! purely for "IDE needs". | 7 | //! purely for "IDE needs". |
8 | use std::sync::Arc; | 8 | use std::{iter::once, sync::Arc}; |
9 | 9 | ||
10 | use either::Either; | 10 | use either::Either; |
11 | use hir_def::{ | 11 | use hir_def::{ |
@@ -25,8 +25,8 @@ use ra_syntax::{ | |||
25 | }; | 25 | }; |
26 | 26 | ||
27 | use crate::{ | 27 | use crate::{ |
28 | db::HirDatabase, Adt, Const, EnumVariant, Function, Local, MacroDef, ModuleDef, Path, Static, | 28 | db::HirDatabase, Adt, Const, EnumVariant, Function, Local, MacroDef, ModPath, ModuleDef, Path, |
29 | Struct, Trait, Type, TypeAlias, TypeParam, | 29 | PathKind, Static, Struct, Trait, Type, TypeAlias, TypeParam, |
30 | }; | 30 | }; |
31 | 31 | ||
32 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of | 32 | /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of |
@@ -162,16 +162,27 @@ impl SourceAnalyzer { | |||
162 | 162 | ||
163 | pub(crate) fn resolve_record_field( | 163 | pub(crate) fn resolve_record_field( |
164 | &self, | 164 | &self, |
165 | db: &impl HirDatabase, | ||
165 | field: &ast::RecordField, | 166 | field: &ast::RecordField, |
166 | ) -> Option<crate::StructField> { | 167 | ) -> Option<(crate::StructField, Option<Local>)> { |
167 | let expr_id = match field.expr() { | 168 | let (expr_id, local) = match field.expr() { |
168 | Some(it) => self.expr_id(&it)?, | 169 | Some(it) => (self.expr_id(&it)?, None), |
169 | None => { | 170 | None => { |
170 | let src = InFile { file_id: self.file_id, value: field }; | 171 | let src = InFile { file_id: self.file_id, value: field }; |
171 | self.body_source_map.as_ref()?.field_init_shorthand_expr(src)? | 172 | let expr_id = self.body_source_map.as_ref()?.field_init_shorthand_expr(src)?; |
173 | let local_name = field.name_ref()?.as_name(); | ||
174 | let path = ModPath::from_segments(PathKind::Plain, once(local_name)); | ||
175 | let local = match self.resolver.resolve_path_in_value_ns_fully(db, &path) { | ||
176 | Some(ValueNs::LocalBinding(pat_id)) => { | ||
177 | Some(Local { pat_id, parent: self.resolver.body_owner()? }) | ||
178 | } | ||
179 | _ => None, | ||
180 | }; | ||
181 | (expr_id, local) | ||
172 | } | 182 | } |
173 | }; | 183 | }; |
174 | self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into()) | 184 | let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?; |
185 | Some((struct_field.into(), local)) | ||
175 | } | 186 | } |
176 | 187 | ||
177 | pub(crate) fn resolve_record_literal( | 188 | pub(crate) fn resolve_record_literal( |
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index e67585203..76ee232a3 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -76,6 +76,8 @@ pub(crate) fn reference_definition( | |||
76 | 76 | ||
77 | let name_kind = classify_name_ref(sema, name_ref); | 77 | let name_kind = classify_name_ref(sema, name_ref); |
78 | if let Some(def) = name_kind { | 78 | if let Some(def) = name_kind { |
79 | let def = def.definition(); | ||
80 | |||
79 | return match def.try_to_nav(sema.db) { | 81 | return match def.try_to_nav(sema.db) { |
80 | Some(nav) => ReferenceResult::Exact(nav), | 82 | Some(nav) => ReferenceResult::Exact(nav), |
81 | None => ReferenceResult::Approximate(Vec::new()), | 83 | None => ReferenceResult::Approximate(Vec::new()), |
@@ -795,8 +797,8 @@ mod tests { | |||
795 | Foo { x<|> }; | 797 | Foo { x<|> }; |
796 | } | 798 | } |
797 | ", | 799 | ", |
798 | "x RECORD_FIELD_DEF FileId(1) [13; 19) [13; 14)", | 800 | "x BIND_PAT FileId(1) [42; 43)", |
799 | "x: i32|x", | 801 | "x", |
800 | ) | 802 | ) |
801 | } | 803 | } |
802 | } | 804 | } |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 5073bb1cf..b31956626 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -153,7 +153,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
153 | if let Some((node, name_kind)) = match_ast! { | 153 | if let Some((node, name_kind)) = match_ast! { |
154 | match (token.parent()) { | 154 | match (token.parent()) { |
155 | ast::NameRef(name_ref) => { | 155 | ast::NameRef(name_ref) => { |
156 | classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d)) | 156 | classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition())) |
157 | }, | 157 | }, |
158 | ast::Name(name) => { | 158 | ast::Name(name) => { |
159 | classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition())) | 159 | classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition())) |
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index f763013ae..2eb047c96 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -27,7 +27,10 @@ use test_utils::tested_by; | |||
27 | 27 | ||
28 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; | 28 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; |
29 | 29 | ||
30 | pub(crate) use self::{classify::classify_name_ref, rename::rename}; | 30 | pub(crate) use self::{ |
31 | classify::{classify_name_ref, NameRefClass}, | ||
32 | rename::rename, | ||
33 | }; | ||
31 | pub(crate) use ra_ide_db::defs::{classify_name, NameDefinition}; | 34 | pub(crate) use ra_ide_db::defs::{classify_name, NameDefinition}; |
32 | 35 | ||
33 | pub use self::search_scope::SearchScope; | 36 | pub use self::search_scope::SearchScope; |
@@ -160,7 +163,7 @@ fn find_name( | |||
160 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); | 163 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); |
161 | } | 164 | } |
162 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; | 165 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; |
163 | let def = classify_name_ref(sema, &name_ref)?; | 166 | let def = classify_name_ref(sema, &name_ref)?.definition(); |
164 | let range = name_ref.syntax().text_range(); | 167 | let range = name_ref.syntax().text_range(); |
165 | Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) | 168 | Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) |
166 | } | 169 | } |
@@ -212,6 +215,7 @@ fn process_definition( | |||
212 | // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 | 215 | // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 |
213 | 216 | ||
214 | if let Some(d) = classify_name_ref(&sema, &name_ref) { | 217 | if let Some(d) = classify_name_ref(&sema, &name_ref) { |
218 | let d = d.definition(); | ||
215 | if d == def { | 219 | if d == def { |
216 | let kind = | 220 | let kind = |
217 | if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) { | 221 | if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) { |
diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs index fdd07d8d1..e837ca8a3 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::{PathResolution, Semantics}; | 3 | use hir::{Local, PathResolution, Semantics}; |
4 | use ra_ide_db::defs::NameDefinition; | 4 | use ra_ide_db::defs::NameDefinition; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_prof::profile; | 6 | use ra_prof::profile; |
@@ -9,10 +9,24 @@ use test_utils::tested_by; | |||
9 | 9 | ||
10 | pub use ra_ide_db::defs::{from_module_def, from_struct_field}; | 10 | pub use ra_ide_db::defs::{from_module_def, from_struct_field}; |
11 | 11 | ||
12 | pub enum NameRefClass { | ||
13 | NameDefinition(NameDefinition), | ||
14 | FieldShorthand { local: Local, field: NameDefinition }, | ||
15 | } | ||
16 | |||
17 | impl NameRefClass { | ||
18 | pub fn definition(self) -> NameDefinition { | ||
19 | match self { | ||
20 | NameRefClass::NameDefinition(def) => def, | ||
21 | NameRefClass::FieldShorthand { local, field: _ } => NameDefinition::Local(local), | ||
22 | } | ||
23 | } | ||
24 | } | ||
25 | |||
12 | pub(crate) fn classify_name_ref( | 26 | pub(crate) fn classify_name_ref( |
13 | sema: &Semantics<RootDatabase>, | 27 | sema: &Semantics<RootDatabase>, |
14 | name_ref: &ast::NameRef, | 28 | name_ref: &ast::NameRef, |
15 | ) -> Option<NameDefinition> { | 29 | ) -> Option<NameRefClass> { |
16 | let _p = profile("classify_name_ref"); | 30 | let _p = profile("classify_name_ref"); |
17 | 31 | ||
18 | let parent = name_ref.syntax().parent()?; | 32 | let parent = name_ref.syntax().parent()?; |
@@ -20,29 +34,34 @@ pub(crate) fn classify_name_ref( | |||
20 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { | 34 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { |
21 | tested_by!(goto_def_for_methods); | 35 | tested_by!(goto_def_for_methods); |
22 | if let Some(func) = sema.resolve_method_call(&method_call) { | 36 | if let Some(func) = sema.resolve_method_call(&method_call) { |
23 | return Some(from_module_def(func.into())); | 37 | return Some(NameRefClass::NameDefinition(NameDefinition::ModuleDef(func.into()))); |
24 | } | 38 | } |
25 | } | 39 | } |
26 | 40 | ||
27 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | 41 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { |
28 | tested_by!(goto_def_for_fields); | 42 | tested_by!(goto_def_for_fields); |
29 | if let Some(field) = sema.resolve_field(&field_expr) { | 43 | if let Some(field) = sema.resolve_field(&field_expr) { |
30 | return Some(from_struct_field(field)); | 44 | return Some(NameRefClass::NameDefinition(NameDefinition::StructField(field))); |
31 | } | 45 | } |
32 | } | 46 | } |
33 | 47 | ||
34 | if let Some(record_field) = ast::RecordField::cast(parent.clone()) { | 48 | if let Some(record_field) = ast::RecordField::cast(parent.clone()) { |
35 | tested_by!(goto_def_for_record_fields); | 49 | tested_by!(goto_def_for_record_fields); |
36 | tested_by!(goto_def_for_field_init_shorthand); | 50 | tested_by!(goto_def_for_field_init_shorthand); |
37 | if let Some(field_def) = sema.resolve_record_field(&record_field) { | 51 | if let Some((field, local)) = sema.resolve_record_field(&record_field) { |
38 | return Some(from_struct_field(field_def)); | 52 | let field = NameDefinition::StructField(field); |
53 | let res = match local { | ||
54 | None => NameRefClass::NameDefinition(field), | ||
55 | Some(local) => NameRefClass::FieldShorthand { field, local }, | ||
56 | }; | ||
57 | return Some(res); | ||
39 | } | 58 | } |
40 | } | 59 | } |
41 | 60 | ||
42 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { | 61 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { |
43 | tested_by!(goto_def_for_macros); | 62 | tested_by!(goto_def_for_macros); |
44 | if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { | 63 | if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { |
45 | return Some(NameDefinition::Macro(macro_def)); | 64 | return Some(NameRefClass::NameDefinition(NameDefinition::Macro(macro_def))); |
46 | } | 65 | } |
47 | } | 66 | } |
48 | 67 | ||
@@ -63,5 +82,5 @@ pub(crate) fn classify_name_ref( | |||
63 | PathResolution::Macro(def) => NameDefinition::Macro(def), | 82 | PathResolution::Macro(def) => NameDefinition::Macro(def), |
64 | PathResolution::SelfType(impl_def) => NameDefinition::SelfType(impl_def), | 83 | PathResolution::SelfType(impl_def) => NameDefinition::SelfType(impl_def), |
65 | }; | 84 | }; |
66 | Some(res) | 85 | Some(NameRefClass::NameDefinition(res)) |
67 | } | 86 | } |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 28117b4d8..b89501aff 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -19,7 +19,11 @@ use ra_syntax::{ | |||
19 | }; | 19 | }; |
20 | use rustc_hash::FxHashMap; | 20 | use rustc_hash::FxHashMap; |
21 | 21 | ||
22 | use crate::{call_info::call_info_for_token, references::classify_name_ref, Analysis, FileId}; | 22 | use crate::{ |
23 | call_info::call_info_for_token, | ||
24 | references::{classify_name_ref, NameRefClass}, | ||
25 | Analysis, FileId, | ||
26 | }; | ||
23 | 27 | ||
24 | pub(crate) use html::highlight_as_html; | 28 | pub(crate) use html::highlight_as_html; |
25 | pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; | 29 | pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; |
@@ -186,24 +190,24 @@ fn highlight_element( | |||
186 | } | 190 | } |
187 | 191 | ||
188 | // Highlight references like the definitions they resolve to | 192 | // Highlight references like the definitions they resolve to |
189 | |||
190 | // Special-case field init shorthand | ||
191 | NAME_REF if element.parent().and_then(ast::RecordField::cast).is_some() => { | ||
192 | HighlightTag::Field.into() | ||
193 | } | ||
194 | NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => return None, | 193 | NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => return None, |
195 | NAME_REF => { | 194 | NAME_REF => { |
196 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); | 195 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); |
197 | let name_kind = classify_name_ref(sema, &name_ref)?; | 196 | let name_kind = classify_name_ref(sema, &name_ref)?; |
198 | 197 | ||
199 | if let NameDefinition::Local(local) = &name_kind { | 198 | match name_kind { |
200 | if let Some(name) = local.name(db) { | 199 | NameRefClass::NameDefinition(def) => { |
201 | let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); | 200 | if let NameDefinition::Local(local) = &def { |
202 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) | 201 | if let Some(name) = local.name(db) { |
202 | let shadow_count = | ||
203 | bindings_shadow_count.entry(name.clone()).or_default(); | ||
204 | binding_hash = Some(calc_binding_hash(&name, *shadow_count)) | ||
205 | } | ||
206 | }; | ||
207 | highlight_name(db, def) | ||
203 | } | 208 | } |
204 | }; | 209 | NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), |
205 | 210 | } | |
206 | highlight_name(db, name_kind) | ||
207 | } | 211 | } |
208 | 212 | ||
209 | // Simple token-based highlighting | 213 | // Simple token-based highlighting |