]> git.lizzy.rs Git - rust.git/commitdiff
More principled approach for gotodef for field shorhand
authorAleksey Kladov <aleksey.kladov@gmail.com>
Mon, 2 Mar 2020 18:00:38 +0000 (19:00 +0100)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Mon, 2 Mar 2020 18:00:38 +0000 (19:00 +0100)
Callers can now decide for themselves if they should prefer field or
local definition. By default, it's the local.

crates/ra_hir/src/semantics.rs
crates/ra_hir/src/source_analyzer.rs
crates/ra_ide/src/goto_definition.rs
crates/ra_ide/src/hover.rs
crates/ra_ide/src/references.rs
crates/ra_ide/src/references/classify.rs
crates/ra_ide/src/syntax_highlighting.rs

index a0853957c1beb81573153ae538112a2c0649dced..afc7f7ee77a0d136dd43d9992f63f191089e9333 100644 (file)
@@ -107,8 +107,11 @@ pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<StructField> {
         self.analyze(field.syntax()).resolve_field(field)
     }
 
-    pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<StructField> {
-        self.analyze(field.syntax()).resolve_record_field(field)
+    pub fn resolve_record_field(
+        &self,
+        field: &ast::RecordField,
+    ) -> Option<(StructField, Option<Local>)> {
+        self.analyze(field.syntax()).resolve_record_field(self.db, field)
     }
 
     pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<VariantDef> {
index 4c121eb73923f979f07c2251eabbe5a8f2edf9ab..015389fb06ac1b6f8f8d2c7354798562e73e07e1 100644 (file)
@@ -5,7 +5,7 @@
 //!
 //! So, this modules should not be used during hir construction, it exists
 //! purely for "IDE needs".
-use std::sync::Arc;
+use std::{iter::once, sync::Arc};
 
 use either::Either;
 use hir_def::{
@@ -25,8 +25,8 @@
 };
 
 use crate::{
-    db::HirDatabase, Adt, Const, EnumVariant, Function, Local, MacroDef, ModuleDef, Path, Static,
-    Struct, Trait, Type, TypeAlias, TypeParam,
+    db::HirDatabase, Adt, Const, EnumVariant, Function, Local, MacroDef, ModPath, ModuleDef, Path,
+    PathKind, Static, Struct, Trait, Type, TypeAlias, TypeParam,
 };
 
 /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
@@ -162,16 +162,27 @@ pub(crate) fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::Stru
 
     pub(crate) fn resolve_record_field(
         &self,
+        db: &impl HirDatabase,
         field: &ast::RecordField,
-    ) -> Option<crate::StructField> {
-        let expr_id = match field.expr() {
-            Some(it) => self.expr_id(&it)?,
+    ) -> Option<(crate::StructField, Option<Local>)> {
+        let (expr_id, local) = match field.expr() {
+            Some(it) => (self.expr_id(&it)?, None),
             None => {
                 let src = InFile { file_id: self.file_id, value: field };
-                self.body_source_map.as_ref()?.field_init_shorthand_expr(src)?
+                let expr_id = self.body_source_map.as_ref()?.field_init_shorthand_expr(src)?;
+                let local_name = field.name_ref()?.as_name();
+                let path = ModPath::from_segments(PathKind::Plain, once(local_name));
+                let local = match self.resolver.resolve_path_in_value_ns_fully(db, &path) {
+                    Some(ValueNs::LocalBinding(pat_id)) => {
+                        Some(Local { pat_id, parent: self.resolver.body_owner()? })
+                    }
+                    _ => None,
+                };
+                (expr_id, local)
             }
         };
-        self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into())
+        let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?;
+        Some((struct_field.into(), local))
     }
 
     pub(crate) fn resolve_record_literal(
index e675852035994a4d2ac4f3a32cbbcf952327e039..76ee232a3a1774e9c9f2d51a31e6ab810ac20c25 100644 (file)
@@ -76,6 +76,8 @@ pub(crate) fn reference_definition(
 
     let name_kind = classify_name_ref(sema, name_ref);
     if let Some(def) = name_kind {
+        let def = def.definition();
+
         return match def.try_to_nav(sema.db) {
             Some(nav) => ReferenceResult::Exact(nav),
             None => ReferenceResult::Approximate(Vec::new()),
@@ -795,8 +797,8 @@ fn main() {
                 Foo { x<|> };
             }
             ",
-            "x RECORD_FIELD_DEF FileId(1) [13; 19) [13; 14)",
-            "x: i32|x",
+            "x BIND_PAT FileId(1) [42; 43)",
+            "x",
         )
     }
 }
index 5073bb1cf8964b410748893db5440065b0cbee3b..b319566267759a8c70e1a0b724c900fba00e4cfa 100644 (file)
@@ -153,7 +153,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
     if let Some((node, name_kind)) = match_ast! {
         match (token.parent()) {
             ast::NameRef(name_ref) => {
-                classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d))
+                classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition()))
             },
             ast::Name(name) => {
                 classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
index f763013aecc8c11d1d9a6c384766daf1a5aaf835..2eb047c9684715d9b327764cb481f9590f76104a 100644 (file)
 
 use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
 
-pub(crate) use self::{classify::classify_name_ref, rename::rename};
+pub(crate) use self::{
+    classify::{classify_name_ref, NameRefClass},
+    rename::rename,
+};
 pub(crate) use ra_ide_db::defs::{classify_name, NameDefinition};
 
 pub use self::search_scope::SearchScope;
@@ -160,7 +163,7 @@ fn find_name(
         return Some(RangeInfo::new(range, (name.text().to_string(), def)));
     }
     let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?;
-    let def = classify_name_ref(sema, &name_ref)?;
+    let def = classify_name_ref(sema, &name_ref)?.definition();
     let range = name_ref.syntax().text_range();
     Some(RangeInfo::new(range, (name_ref.text().to_string(), def)))
 }
@@ -212,6 +215,7 @@ fn process_definition(
             // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098
 
             if let Some(d) = classify_name_ref(&sema, &name_ref) {
+                let d = d.definition();
                 if d == def {
                     let kind =
                         if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {
index fdd07d8d186f26b0392b162941e9f77f97fd7884..e837ca8a3cecd36c9bdff7cc86ca91fe54ab79b9 100644 (file)
@@ -1,6 +1,6 @@
 //! Functions that are used to classify an element from its definition or reference.
 
-use hir::{PathResolution, Semantics};
+use hir::{Local, PathResolution, Semantics};
 use ra_ide_db::defs::NameDefinition;
 use ra_ide_db::RootDatabase;
 use ra_prof::profile;
@@ -9,10 +9,24 @@
 
 pub use ra_ide_db::defs::{from_module_def, from_struct_field};
 
+pub enum NameRefClass {
+    NameDefinition(NameDefinition),
+    FieldShorthand { local: Local, field: NameDefinition },
+}
+
+impl NameRefClass {
+    pub fn definition(self) -> NameDefinition {
+        match self {
+            NameRefClass::NameDefinition(def) => def,
+            NameRefClass::FieldShorthand { local, field: _ } => NameDefinition::Local(local),
+        }
+    }
+}
+
 pub(crate) fn classify_name_ref(
     sema: &Semantics<RootDatabase>,
     name_ref: &ast::NameRef,
-) -> Option<NameDefinition> {
+) -> Option<NameRefClass> {
     let _p = profile("classify_name_ref");
 
     let parent = name_ref.syntax().parent()?;
@@ -20,29 +34,34 @@ pub(crate) fn classify_name_ref(
     if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
         tested_by!(goto_def_for_methods);
         if let Some(func) = sema.resolve_method_call(&method_call) {
-            return Some(from_module_def(func.into()));
+            return Some(NameRefClass::NameDefinition(NameDefinition::ModuleDef(func.into())));
         }
     }
 
     if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
         tested_by!(goto_def_for_fields);
         if let Some(field) = sema.resolve_field(&field_expr) {
-            return Some(from_struct_field(field));
+            return Some(NameRefClass::NameDefinition(NameDefinition::StructField(field)));
         }
     }
 
     if let Some(record_field) = ast::RecordField::cast(parent.clone()) {
         tested_by!(goto_def_for_record_fields);
         tested_by!(goto_def_for_field_init_shorthand);
-        if let Some(field_def) = sema.resolve_record_field(&record_field) {
-            return Some(from_struct_field(field_def));
+        if let Some((field, local)) = sema.resolve_record_field(&record_field) {
+            let field = NameDefinition::StructField(field);
+            let res = match local {
+                None => NameRefClass::NameDefinition(field),
+                Some(local) => NameRefClass::FieldShorthand { field, local },
+            };
+            return Some(res);
         }
     }
 
     if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
         tested_by!(goto_def_for_macros);
         if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
-            return Some(NameDefinition::Macro(macro_def));
+            return Some(NameRefClass::NameDefinition(NameDefinition::Macro(macro_def)));
         }
     }
 
@@ -63,5 +82,5 @@ pub(crate) fn classify_name_ref(
         PathResolution::Macro(def) => NameDefinition::Macro(def),
         PathResolution::SelfType(impl_def) => NameDefinition::SelfType(impl_def),
     };
-    Some(res)
+    Some(NameRefClass::NameDefinition(res))
 }
index 28117b4d8dd0f9e25f295e99745f6d26b6237285..b89501aff6fd3d93b9eee4cd719f655041949a7b 100644 (file)
 };
 use rustc_hash::FxHashMap;
 
-use crate::{call_info::call_info_for_token, references::classify_name_ref, Analysis, FileId};
+use crate::{
+    call_info::call_info_for_token,
+    references::{classify_name_ref, NameRefClass},
+    Analysis, FileId,
+};
 
 pub(crate) use html::highlight_as_html;
 pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag};
@@ -186,24 +190,24 @@ fn highlight_element(
         }
 
         // Highlight references like the definitions they resolve to
-
-        // Special-case field init shorthand
-        NAME_REF if element.parent().and_then(ast::RecordField::cast).is_some() => {
-            HighlightTag::Field.into()
-        }
         NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => return None,
         NAME_REF => {
             let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
             let name_kind = classify_name_ref(sema, &name_ref)?;
 
-            if let NameDefinition::Local(local) = &name_kind {
-                if let Some(name) = local.name(db) {
-                    let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
-                    binding_hash = Some(calc_binding_hash(&name, *shadow_count))
+            match name_kind {
+                NameRefClass::NameDefinition(def) => {
+                    if let NameDefinition::Local(local) = &def {
+                        if let Some(name) = local.name(db) {
+                            let shadow_count =
+                                bindings_shadow_count.entry(name.clone()).or_default();
+                            binding_hash = Some(calc_binding_hash(&name, *shadow_count))
+                        }
+                    };
+                    highlight_name(db, def)
                 }
-            };
-
-            highlight_name(db, name_kind)
+                NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
+            }
         }
 
         // Simple token-based highlighting