mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Fix goto definition for record patterns
This commit is contained in:
parent
ca61356b01
commit
fa2ea8f494
7 changed files with 57 additions and 6 deletions
|
@ -195,6 +195,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
||||||
self.analyze(field.syntax()).resolve_record_field(self.db, field)
|
self.analyze(field.syntax()).resolve_record_field(self.db, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option<StructField> {
|
||||||
|
self.analyze(field.syntax()).resolve_record_field_pat(self.db, field)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> {
|
pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> {
|
||||||
let sa = self.analyze(macro_call.syntax());
|
let sa = self.analyze(macro_call.syntax());
|
||||||
let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call);
|
let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call);
|
||||||
|
|
|
@ -95,6 +95,7 @@ impl SourceAnalyzer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> {
|
fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> {
|
||||||
|
// FIXME: macros, see `expr_id`
|
||||||
let src = InFile { file_id: self.file_id, value: pat };
|
let src = InFile { file_id: self.file_id, value: pat };
|
||||||
self.body_source_map.as_ref()?.node_pat(src)
|
self.body_source_map.as_ref()?.node_pat(src)
|
||||||
}
|
}
|
||||||
|
@ -167,6 +168,16 @@ impl SourceAnalyzer {
|
||||||
Some((struct_field.into(), local))
|
Some((struct_field.into(), local))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve_record_field_pat(
|
||||||
|
&self,
|
||||||
|
_db: &dyn HirDatabase,
|
||||||
|
field: &ast::RecordFieldPat,
|
||||||
|
) -> Option<StructField> {
|
||||||
|
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(
|
pub(crate) fn resolve_macro_call(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
|
|
|
@ -127,6 +127,7 @@ pub struct InferenceResult {
|
||||||
field_resolutions: FxHashMap<ExprId, StructFieldId>,
|
field_resolutions: FxHashMap<ExprId, StructFieldId>,
|
||||||
/// For each field in record literal, records the field it resolves to.
|
/// For each field in record literal, records the field it resolves to.
|
||||||
record_field_resolutions: FxHashMap<ExprId, StructFieldId>,
|
record_field_resolutions: FxHashMap<ExprId, StructFieldId>,
|
||||||
|
record_field_pat_resolutions: FxHashMap<PatId, StructFieldId>,
|
||||||
/// For each struct literal, records the variant it resolves to.
|
/// For each struct literal, records the variant it resolves to.
|
||||||
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
|
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
|
||||||
/// For each associated item record what it resolves to
|
/// For each associated item record what it resolves to
|
||||||
|
@ -147,6 +148,9 @@ impl InferenceResult {
|
||||||
pub fn record_field_resolution(&self, expr: ExprId) -> Option<StructFieldId> {
|
pub fn record_field_resolution(&self, expr: ExprId) -> Option<StructFieldId> {
|
||||||
self.record_field_resolutions.get(&expr).copied()
|
self.record_field_resolutions.get(&expr).copied()
|
||||||
}
|
}
|
||||||
|
pub fn record_field_pat_resolution(&self, pat: PatId) -> Option<StructFieldId> {
|
||||||
|
self.record_field_pat_resolutions.get(&pat).copied()
|
||||||
|
}
|
||||||
pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> {
|
pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> {
|
||||||
self.variant_resolutions.get(&id.into()).copied()
|
self.variant_resolutions.get(&id.into()).copied()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use hir_def::{
|
||||||
expr::{BindingAnnotation, Pat, PatId, RecordFieldPat},
|
expr::{BindingAnnotation, Pat, PatId, RecordFieldPat},
|
||||||
path::Path,
|
path::Path,
|
||||||
type_ref::Mutability,
|
type_ref::Mutability,
|
||||||
|
StructFieldId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use test_utils::tested_by;
|
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();
|
let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
|
||||||
for subpat in subpats {
|
for subpat in subpats {
|
||||||
let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name));
|
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 =
|
let expected_ty =
|
||||||
matching_field.map_or(Ty::Unknown, |field| field_tys[field].clone().subst(&substs));
|
matching_field.map_or(Ty::Unknown, |field| field_tys[field].clone().subst(&substs));
|
||||||
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
||||||
|
|
|
@ -62,10 +62,9 @@ pub(crate) enum ReferenceResult {
|
||||||
|
|
||||||
impl ReferenceResult {
|
impl ReferenceResult {
|
||||||
fn to_vec(self) -> Vec<NavigationTarget> {
|
fn to_vec(self) -> Vec<NavigationTarget> {
|
||||||
use self::ReferenceResult::*;
|
|
||||||
match self {
|
match self {
|
||||||
Exact(target) => vec![target],
|
ReferenceResult::Exact(target) => vec![target],
|
||||||
Approximate(vec) => vec,
|
ReferenceResult::Approximate(vec) => vec,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,8 +73,6 @@ pub(crate) fn reference_definition(
|
||||||
sema: &Semantics<RootDatabase>,
|
sema: &Semantics<RootDatabase>,
|
||||||
name_ref: &ast::NameRef,
|
name_ref: &ast::NameRef,
|
||||||
) -> ReferenceResult {
|
) -> ReferenceResult {
|
||||||
use self::ReferenceResult::*;
|
|
||||||
|
|
||||||
let name_kind = classify_name_ref(sema, name_ref);
|
let name_kind = classify_name_ref(sema, name_ref);
|
||||||
if let Some(def) = name_kind {
|
if let Some(def) = name_kind {
|
||||||
let def = def.definition();
|
let def = def.definition();
|
||||||
|
@ -91,7 +88,7 @@ pub(crate) fn reference_definition(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|s| s.to_nav(sema.db))
|
.map(|s| s.to_nav(sema.db))
|
||||||
.collect();
|
.collect();
|
||||||
Approximate(navs)
|
ReferenceResult::Approximate(navs)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[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]
|
#[test]
|
||||||
fn goto_def_for_record_fields_macros() {
|
fn goto_def_for_record_fields_macros() {
|
||||||
check_goto(
|
check_goto(
|
||||||
|
|
|
@ -180,6 +180,7 @@ fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Opti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum NameRefClass {
|
pub enum NameRefClass {
|
||||||
Definition(Definition),
|
Definition(Definition),
|
||||||
FieldShorthand { local: Local, field: 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) {
|
if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
|
||||||
tested_by!(goto_def_for_macros; force);
|
tested_by!(goto_def_for_macros; force);
|
||||||
if let Some(macro_def) = sema.resolve_macro_call(¯o_call) {
|
if let Some(macro_def) = sema.resolve_macro_call(¯o_call) {
|
||||||
|
|
|
@ -6,5 +6,6 @@ test_utils::marks![
|
||||||
goto_def_for_fields
|
goto_def_for_fields
|
||||||
goto_def_for_record_fields
|
goto_def_for_record_fields
|
||||||
goto_def_for_field_init_shorthand
|
goto_def_for_field_init_shorthand
|
||||||
|
goto_def_for_record_field_pats
|
||||||
search_filters_by_range
|
search_filters_by_range
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in a new issue