aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-03-02 18:00:38 +0000
committerAleksey Kladov <[email protected]>2020-03-02 18:00:38 +0000
commit2716a1fa3f8a7410248724a6ce5d4c28837db682 (patch)
treead1faf323e4cb1478e1e34e2ed9fe858c5be80e5
parent96eca8a1abd79840b9040250db9d1e65ec663b0e (diff)
More principled approach for gotodef for field shorhand
Callers can now decide for themselves if they should prefer field or local definition. By default, it's the local.
-rw-r--r--crates/ra_hir/src/semantics.rs7
-rw-r--r--crates/ra_hir/src/source_analyzer.rs27
-rw-r--r--crates/ra_ide/src/goto_definition.rs6
-rw-r--r--crates/ra_ide/src/hover.rs2
-rw-r--r--crates/ra_ide/src/references.rs8
-rw-r--r--crates/ra_ide/src/references/classify.rs35
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs30
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".
8use std::sync::Arc; 8use std::{iter::once, sync::Arc};
9 9
10use either::Either; 10use either::Either;
11use hir_def::{ 11use hir_def::{
@@ -25,8 +25,8 @@ use ra_syntax::{
25}; 25};
26 26
27use crate::{ 27use 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
28use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; 28use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
29 29
30pub(crate) use self::{classify::classify_name_ref, rename::rename}; 30pub(crate) use self::{
31 classify::{classify_name_ref, NameRefClass},
32 rename::rename,
33};
31pub(crate) use ra_ide_db::defs::{classify_name, NameDefinition}; 34pub(crate) use ra_ide_db::defs::{classify_name, NameDefinition};
32 35
33pub use self::search_scope::SearchScope; 36pub 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
3use hir::{PathResolution, Semantics}; 3use hir::{Local, PathResolution, Semantics};
4use ra_ide_db::defs::NameDefinition; 4use ra_ide_db::defs::NameDefinition;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_prof::profile; 6use ra_prof::profile;
@@ -9,10 +9,24 @@ use test_utils::tested_by;
9 9
10pub use ra_ide_db::defs::{from_module_def, from_struct_field}; 10pub use ra_ide_db::defs::{from_module_def, from_struct_field};
11 11
12pub enum NameRefClass {
13 NameDefinition(NameDefinition),
14 FieldShorthand { local: Local, field: NameDefinition },
15}
16
17impl 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
12pub(crate) fn classify_name_ref( 26pub(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(&macro_call) { 63 if let Some(macro_def) = sema.resolve_macro_call(&macro_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};
20use rustc_hash::FxHashMap; 20use rustc_hash::FxHashMap;
21 21
22use crate::{call_info::call_info_for_token, references::classify_name_ref, Analysis, FileId}; 22use crate::{
23 call_info::call_info_for_token,
24 references::{classify_name_ref, NameRefClass},
25 Analysis, FileId,
26};
23 27
24pub(crate) use html::highlight_as_html; 28pub(crate) use html::highlight_as_html;
25pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; 29pub 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