Don't redo field resolution in the IDE

This commit is contained in:
Aleksey Kladov 2019-11-24 20:06:55 +03:00
parent ac9ba5eb32
commit 63e3ea38d3
6 changed files with 30 additions and 22 deletions

View file

@ -510,7 +510,7 @@ impl VariantDef {
} }
} }
pub fn field(self, db: &impl HirDatabase, name: &Name) -> Option<StructField> { pub(crate) fn field(self, db: &impl HirDatabase, name: &Name) -> Option<StructField> {
match self { match self {
VariantDef::Struct(it) => it.field(db, name), VariantDef::Struct(it) => it.field(db, name),
VariantDef::EnumVariant(it) => it.field(db, name), VariantDef::EnumVariant(it) => it.field(db, name),

View file

@ -216,6 +216,11 @@ impl SourceAnalyzer {
self.infer.as_ref()?.field_resolution(expr_id) self.infer.as_ref()?.field_resolution(expr_id)
} }
pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<crate::StructField> {
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<crate::VariantDef> { pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<crate::VariantDef> {
let expr_id = self.expr_id(&record_lit.clone().into())?; let expr_id = self.expr_id(&record_lit.clone().into())?;
self.infer.as_ref()?.variant_resolution_for_expr(expr_id) self.infer.as_ref()?.variant_resolution_for_expr(expr_id)

View file

@ -126,6 +126,8 @@ pub struct InferenceResult {
method_resolutions: FxHashMap<ExprId, Function>, method_resolutions: FxHashMap<ExprId, Function>,
/// For each field access expr, records the field it resolves to. /// For each field access expr, records the field it resolves to.
field_resolutions: FxHashMap<ExprId, StructField>, field_resolutions: FxHashMap<ExprId, StructField>,
/// For each field in record literal, records the field it resolves to.
record_field_resolutions: FxHashMap<ExprId, StructField>,
/// For each struct literal, records the variant it resolves to. /// For each struct literal, records the variant it resolves to.
variant_resolutions: FxHashMap<ExprOrPatId, VariantDef>, variant_resolutions: FxHashMap<ExprOrPatId, VariantDef>,
/// For each associated item record what it resolves to /// For each associated item record what it resolves to
@ -143,6 +145,9 @@ impl InferenceResult {
pub fn field_resolution(&self, expr: ExprId) -> Option<StructField> { pub fn field_resolution(&self, expr: ExprId) -> Option<StructField> {
self.field_resolutions.get(&expr).copied() self.field_resolutions.get(&expr).copied()
} }
pub fn record_field_resolution(&self, expr: ExprId) -> Option<StructField> {
self.record_field_resolutions.get(&expr).copied()
}
pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantDef> { pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantDef> {
self.variant_resolutions.get(&id.into()).copied() self.variant_resolutions.get(&id.into()).copied()
} }

View file

@ -215,8 +215,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
let substs = ty.substs().unwrap_or_else(Substs::empty); let substs = ty.substs().unwrap_or_else(Substs::empty);
for (field_idx, field) in fields.iter().enumerate() { for (field_idx, field) in fields.iter().enumerate() {
let field_ty = def_id let field_def = def_id.and_then(|it| match it.field(self.db, &field.name) {
.and_then(|it| match it.field(self.db, &field.name) {
Some(field) => Some(field), Some(field) => Some(field),
None => { None => {
self.push_diagnostic(InferenceDiagnostic::NoSuchField { self.push_diagnostic(InferenceDiagnostic::NoSuchField {
@ -225,9 +224,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}); });
None None
} }
}) });
.map_or(Ty::Unknown, |field| field.ty(self.db)) if let Some(field_def) = field_def {
.subst(&substs); 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)); self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty));
} }
if let Some(expr) = spread { if let Some(expr) = spread {

View file

@ -195,7 +195,7 @@ impl Path {
} }
/// Converts an `ast::NameRef` into a single-identifier `Path`. /// 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() name_ref.as_name().into()
} }

View file

@ -1,6 +1,6 @@
//! Functions that are used to classify an element from its definition or reference. //! 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_prof::profile;
use ra_syntax::{ast, match_ast, AstNode}; use ra_syntax::{ast, match_ast, AstNode};
use test_utils::tested_by; 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()) { if let Some(record_field) = ast::RecordField::cast(parent.clone()) {
tested_by!(goto_definition_works_for_record_fields); tested_by!(goto_definition_works_for_record_fields);
if let Some(record_lit) = record_field.syntax().ancestors().find_map(ast::RecordLit::cast) { if let Some(field_def) = analyzer.resolve_record_field(&record_field) {
let variant_def = analyzer.resolve_record_literal(&record_lit)?; return Some(from_struct_field(db, field_def));
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));
} }
} }