diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 9f06b147cf6..a98f2f247d2 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -510,7 +510,7 @@ pub fn fields(self, db: &impl HirDatabase) -> Vec { } } - pub fn field(self, db: &impl HirDatabase, name: &Name) -> Option { + pub(crate) fn field(self, db: &impl HirDatabase, name: &Name) -> Option { match self { VariantDef::Struct(it) => it.field(db, name), VariantDef::EnumVariant(it) => it.field(db, name), diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index cfc4bd3260e..9e5ce550836 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -216,6 +216,11 @@ pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option Option { + let expr_id = self.expr_id(&field.expr()?)?; + self.infer.as_ref()?.record_field_resolution(expr_id) + } + pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option { let expr_id = self.expr_id(&record_lit.clone().into())?; self.infer.as_ref()?.variant_resolution_for_expr(expr_id) diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 2e744e5ece0..0a9a83800ee 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -126,6 +126,8 @@ pub struct InferenceResult { method_resolutions: FxHashMap, /// For each field access expr, records the field it resolves to. field_resolutions: FxHashMap, + /// For each field in record literal, records the field it resolves to. + record_field_resolutions: FxHashMap, /// For each struct literal, records the variant it resolves to. variant_resolutions: FxHashMap, /// For each associated item record what it resolves to @@ -143,6 +145,9 @@ pub fn method_resolution(&self, expr: ExprId) -> Option { pub fn field_resolution(&self, expr: ExprId) -> Option { self.field_resolutions.get(&expr).copied() } + pub fn record_field_resolution(&self, expr: ExprId) -> Option { + self.record_field_resolutions.get(&expr).copied() + } pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option { self.variant_resolutions.get(&id.into()).copied() } diff --git a/crates/ra_hir/src/ty/infer/expr.rs b/crates/ra_hir/src/ty/infer/expr.rs index 20a7e93525e..2996920c626 100644 --- a/crates/ra_hir/src/ty/infer/expr.rs +++ b/crates/ra_hir/src/ty/infer/expr.rs @@ -215,19 +215,21 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { let substs = ty.substs().unwrap_or_else(Substs::empty); for (field_idx, field) in fields.iter().enumerate() { - let field_ty = def_id - .and_then(|it| match it.field(self.db, &field.name) { - Some(field) => Some(field), - None => { - self.push_diagnostic(InferenceDiagnostic::NoSuchField { - expr: tgt_expr, - field: field_idx, - }); - None - } - }) - .map_or(Ty::Unknown, |field| field.ty(self.db)) - .subst(&substs); + let field_def = def_id.and_then(|it| match it.field(self.db, &field.name) { + Some(field) => Some(field), + None => { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + expr: tgt_expr, + field: field_idx, + }); + None + } + }); + if let Some(field_def) = field_def { + self.result.record_field_resolutions.insert(field.expr, field_def); + } + let field_ty = + field_def.map_or(Ty::Unknown, |field| field.ty(self.db)).subst(&substs); self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty)); } if let Some(expr) = spread { diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index 7b2723d5716..0e606fd0e81 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs @@ -195,7 +195,7 @@ fn qualifier(path: &ast::Path) -> Option { } /// Converts an `ast::NameRef` into a single-identifier `Path`. - pub fn from_name_ref(name_ref: &ast::NameRef) -> Path { + pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> Path { name_ref.as_name().into() } diff --git a/crates/ra_ide_api/src/references/classify.rs b/crates/ra_ide_api/src/references/classify.rs index 4a4b030f89d..cab06dea941 100644 --- a/crates/ra_ide_api/src/references/classify.rs +++ b/crates/ra_ide_api/src/references/classify.rs @@ -1,6 +1,6 @@ //! Functions that are used to classify an element from its definition or reference. -use hir::{FromSource, Module, ModuleSource, Path, PathResolution, Source, SourceAnalyzer}; +use hir::{FromSource, Module, ModuleSource, PathResolution, Source, SourceAnalyzer}; use ra_prof::profile; use ra_syntax::{ast, match_ast, AstNode}; use test_utils::tested_by; @@ -140,12 +140,8 @@ pub(crate) fn classify_name_ref( if let Some(record_field) = ast::RecordField::cast(parent.clone()) { tested_by!(goto_definition_works_for_record_fields); - if let Some(record_lit) = record_field.syntax().ancestors().find_map(ast::RecordLit::cast) { - let variant_def = analyzer.resolve_record_literal(&record_lit)?; - let hir_path = Path::from_name_ref(name_ref.value); - let hir_name = hir_path.as_ident()?; - let field = variant_def.field(db, hir_name)?; - return Some(from_struct_field(db, field)); + if let Some(field_def) = analyzer.resolve_record_field(&record_field) { + return Some(from_struct_field(db, field_def)); } }