From fa2ea8f494d8434da705dc0e0f047f3bd7503af9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 18 Apr 2020 22:05:06 +0200 Subject: Fix goto definition for record patterns --- crates/ra_hir/src/semantics.rs | 4 ++++ crates/ra_hir/src/source_analyzer.rs | 11 +++++++++++ crates/ra_hir_ty/src/infer.rs | 4 ++++ crates/ra_hir_ty/src/infer/pat.rs | 6 ++++++ crates/ra_ide/src/goto_definition.rs | 28 ++++++++++++++++++++++------ crates/ra_ide_db/src/defs.rs | 9 +++++++++ crates/ra_ide_db/src/marks.rs | 1 + 7 files changed, 57 insertions(+), 6 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 0b477f0e9..5d6edc45c 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs @@ -195,6 +195,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.analyze(field.syntax()).resolve_record_field(self.db, field) } + pub fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option { + self.analyze(field.syntax()).resolve_record_field_pat(self.db, field) + } + pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option { let sa = self.analyze(macro_call.syntax()); let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index 23af400b8..0ed6d0958 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs @@ -95,6 +95,7 @@ impl SourceAnalyzer { } fn pat_id(&self, pat: &ast::Pat) -> Option { + // FIXME: macros, see `expr_id` let src = InFile { file_id: self.file_id, value: pat }; self.body_source_map.as_ref()?.node_pat(src) } @@ -167,6 +168,16 @@ impl SourceAnalyzer { Some((struct_field.into(), local)) } + pub(crate) fn resolve_record_field_pat( + &self, + _db: &dyn HirDatabase, + field: &ast::RecordFieldPat, + ) -> Option { + let pat_id = self.pat_id(&field.pat()?)?; + let struct_field = self.infer.as_ref()?.record_field_pat_resolution(pat_id)?; + Some(struct_field.into()) + } + pub(crate) fn resolve_macro_call( &self, db: &dyn HirDatabase, diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index b6d9b3438..dfb6a435f 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -127,6 +127,7 @@ pub struct InferenceResult { field_resolutions: FxHashMap, /// For each field in record literal, records the field it resolves to. record_field_resolutions: FxHashMap, + record_field_pat_resolutions: FxHashMap, /// For each struct literal, records the variant it resolves to. variant_resolutions: FxHashMap, /// For each associated item record what it resolves to @@ -147,6 +148,9 @@ impl InferenceResult { pub fn record_field_resolution(&self, expr: ExprId) -> Option { self.record_field_resolutions.get(&expr).copied() } + pub fn record_field_pat_resolution(&self, pat: PatId) -> Option { + self.record_field_pat_resolutions.get(&pat).copied() + } pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option { self.variant_resolutions.get(&id.into()).copied() } diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs index 8ec4d4ace..7c2ad4384 100644 --- a/crates/ra_hir_ty/src/infer/pat.rs +++ b/crates/ra_hir_ty/src/infer/pat.rs @@ -7,6 +7,7 @@ use hir_def::{ expr::{BindingAnnotation, Pat, PatId, RecordFieldPat}, path::Path, type_ref::Mutability, + StructFieldId, }; use hir_expand::name::Name; use test_utils::tested_by; @@ -67,6 +68,11 @@ impl<'a> InferenceContext<'a> { let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); for subpat in subpats { let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name)); + if let Some(local_id) = matching_field { + let field_def = StructFieldId { parent: def.unwrap(), local_id }; + self.result.record_field_pat_resolutions.insert(subpat.pat, field_def); + } + let expected_ty = matching_field.map_or(Ty::Unknown, |field| field_tys[field].clone().subst(&substs)); let expected_ty = self.normalize_associated_types_in(expected_ty); diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 8aed94d16..9998ca5a3 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -62,10 +62,9 @@ pub(crate) enum ReferenceResult { impl ReferenceResult { fn to_vec(self) -> Vec { - use self::ReferenceResult::*; match self { - Exact(target) => vec![target], - Approximate(vec) => vec, + ReferenceResult::Exact(target) => vec![target], + ReferenceResult::Approximate(vec) => vec, } } } @@ -74,8 +73,6 @@ pub(crate) fn reference_definition( sema: &Semantics, name_ref: &ast::NameRef, ) -> ReferenceResult { - use self::ReferenceResult::*; - let name_kind = classify_name_ref(sema, name_ref); if let Some(def) = name_kind { let def = def.definition(); @@ -91,7 +88,7 @@ pub(crate) fn reference_definition( .into_iter() .map(|s| s.to_nav(sema.db)) .collect(); - Approximate(navs) + ReferenceResult::Approximate(navs) } #[cfg(test)] @@ -398,6 +395,25 @@ mod tests { ); } + #[test] + fn goto_def_for_record_pat_fields() { + covers!(ra_ide_db::goto_def_for_record_field_pats); + check_goto( + r" + //- /lib.rs + struct Foo { + spam: u32, + } + + fn bar(foo: Foo) -> Foo { + let Foo { spam<|>: _, } = foo + } + ", + "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", + "spam: u32|spam", + ); + } + #[test] fn goto_def_for_record_fields_macros() { check_goto( diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 49a8c74fb..785613b82 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs @@ -180,6 +180,7 @@ fn classify_name_inner(sema: &Semantics, name: &ast::Name) -> Opti } } +#[derive(Debug)] pub enum NameRefClass { Definition(Definition), FieldShorthand { local: Local, field: Definition }, @@ -229,6 +230,14 @@ pub fn classify_name_ref( } } + if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) { + tested_by!(goto_def_for_record_field_pats; force); + if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { + let field = Definition::StructField(field); + return Some(NameRefClass::Definition(field)); + } + } + if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { tested_by!(goto_def_for_macros; force); if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { diff --git a/crates/ra_ide_db/src/marks.rs b/crates/ra_ide_db/src/marks.rs index 4f0a22af0..03b4be21c 100644 --- a/crates/ra_ide_db/src/marks.rs +++ b/crates/ra_ide_db/src/marks.rs @@ -6,5 +6,6 @@ test_utils::marks![ goto_def_for_fields goto_def_for_record_fields goto_def_for_field_init_shorthand + goto_def_for_record_field_pats search_filters_by_range ]; -- cgit v1.2.3