Skip to content

Commit fa2ea8f

Browse files
committed
Fix goto definition for record patterns
1 parent ca61356 commit fa2ea8f

File tree

7 files changed

+57
-6
lines changed

7 files changed

+57
-6
lines changed

crates/ra_hir/src/semantics.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
195195
self.analyze(field.syntax()).resolve_record_field(self.db, field)
196196
}
197197

198+
pub fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option<StructField> {
199+
self.analyze(field.syntax()).resolve_record_field_pat(self.db, field)
200+
}
201+
198202
pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> {
199203
let sa = self.analyze(macro_call.syntax());
200204
let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call);

crates/ra_hir/src/source_analyzer.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ impl SourceAnalyzer {
9595
}
9696

9797
fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> {
98+
// FIXME: macros, see `expr_id`
9899
let src = InFile { file_id: self.file_id, value: pat };
99100
self.body_source_map.as_ref()?.node_pat(src)
100101
}
@@ -167,6 +168,16 @@ impl SourceAnalyzer {
167168
Some((struct_field.into(), local))
168169
}
169170

171+
pub(crate) fn resolve_record_field_pat(
172+
&self,
173+
_db: &dyn HirDatabase,
174+
field: &ast::RecordFieldPat,
175+
) -> Option<StructField> {
176+
let pat_id = self.pat_id(&field.pat()?)?;
177+
let struct_field = self.infer.as_ref()?.record_field_pat_resolution(pat_id)?;
178+
Some(struct_field.into())
179+
}
180+
170181
pub(crate) fn resolve_macro_call(
171182
&self,
172183
db: &dyn HirDatabase,

crates/ra_hir_ty/src/infer.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ pub struct InferenceResult {
127127
field_resolutions: FxHashMap<ExprId, StructFieldId>,
128128
/// For each field in record literal, records the field it resolves to.
129129
record_field_resolutions: FxHashMap<ExprId, StructFieldId>,
130+
record_field_pat_resolutions: FxHashMap<PatId, StructFieldId>,
130131
/// For each struct literal, records the variant it resolves to.
131132
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
132133
/// For each associated item record what it resolves to
@@ -147,6 +148,9 @@ impl InferenceResult {
147148
pub fn record_field_resolution(&self, expr: ExprId) -> Option<StructFieldId> {
148149
self.record_field_resolutions.get(&expr).copied()
149150
}
151+
pub fn record_field_pat_resolution(&self, pat: PatId) -> Option<StructFieldId> {
152+
self.record_field_pat_resolutions.get(&pat).copied()
153+
}
150154
pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> {
151155
self.variant_resolutions.get(&id.into()).copied()
152156
}

crates/ra_hir_ty/src/infer/pat.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use hir_def::{
77
expr::{BindingAnnotation, Pat, PatId, RecordFieldPat},
88
path::Path,
99
type_ref::Mutability,
10+
StructFieldId,
1011
};
1112
use hir_expand::name::Name;
1213
use test_utils::tested_by;
@@ -67,6 +68,11 @@ impl<'a> InferenceContext<'a> {
6768
let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
6869
for subpat in subpats {
6970
let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name));
71+
if let Some(local_id) = matching_field {
72+
let field_def = StructFieldId { parent: def.unwrap(), local_id };
73+
self.result.record_field_pat_resolutions.insert(subpat.pat, field_def);
74+
}
75+
7076
let expected_ty =
7177
matching_field.map_or(Ty::Unknown, |field| field_tys[field].clone().subst(&substs));
7278
let expected_ty = self.normalize_associated_types_in(expected_ty);

crates/ra_ide/src/goto_definition.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,9 @@ pub(crate) enum ReferenceResult {
6262

6363
impl ReferenceResult {
6464
fn to_vec(self) -> Vec<NavigationTarget> {
65-
use self::ReferenceResult::*;
6665
match self {
67-
Exact(target) => vec![target],
68-
Approximate(vec) => vec,
66+
ReferenceResult::Exact(target) => vec![target],
67+
ReferenceResult::Approximate(vec) => vec,
6968
}
7069
}
7170
}
@@ -74,8 +73,6 @@ pub(crate) fn reference_definition(
7473
sema: &Semantics<RootDatabase>,
7574
name_ref: &ast::NameRef,
7675
) -> ReferenceResult {
77-
use self::ReferenceResult::*;
78-
7976
let name_kind = classify_name_ref(sema, name_ref);
8077
if let Some(def) = name_kind {
8178
let def = def.definition();
@@ -91,7 +88,7 @@ pub(crate) fn reference_definition(
9188
.into_iter()
9289
.map(|s| s.to_nav(sema.db))
9390
.collect();
94-
Approximate(navs)
91+
ReferenceResult::Approximate(navs)
9592
}
9693

9794
#[cfg(test)]
@@ -398,6 +395,25 @@ mod tests {
398395
);
399396
}
400397

398+
#[test]
399+
fn goto_def_for_record_pat_fields() {
400+
covers!(ra_ide_db::goto_def_for_record_field_pats);
401+
check_goto(
402+
r"
403+
//- /lib.rs
404+
struct Foo {
405+
spam: u32,
406+
}
407+
408+
fn bar(foo: Foo) -> Foo {
409+
let Foo { spam<|>: _, } = foo
410+
}
411+
",
412+
"spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)",
413+
"spam: u32|spam",
414+
);
415+
}
416+
401417
#[test]
402418
fn goto_def_for_record_fields_macros() {
403419
check_goto(

crates/ra_ide_db/src/defs.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Opti
180180
}
181181
}
182182

183+
#[derive(Debug)]
183184
pub enum NameRefClass {
184185
Definition(Definition),
185186
FieldShorthand { local: Local, field: Definition },
@@ -229,6 +230,14 @@ pub fn classify_name_ref(
229230
}
230231
}
231232

233+
if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) {
234+
tested_by!(goto_def_for_record_field_pats; force);
235+
if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
236+
let field = Definition::StructField(field);
237+
return Some(NameRefClass::Definition(field));
238+
}
239+
}
240+
232241
if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
233242
tested_by!(goto_def_for_macros; force);
234243
if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {

crates/ra_ide_db/src/marks.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ test_utils::marks![
66
goto_def_for_fields
77
goto_def_for_record_fields
88
goto_def_for_field_init_shorthand
9+
goto_def_for_record_field_pats
910
search_filters_by_range
1011
];

0 commit comments

Comments
 (0)