Simplify source maps for fields

This commit is contained in:
Aleksey Kladov 2021-03-15 15:38:50 +03:00
parent af2366acdf
commit f7156cb0ae
5 changed files with 33 additions and 45 deletions

View File

@ -24,7 +24,7 @@ use hir_ty::{
}; };
use syntax::{ use syntax::{
ast::{self, AstNode}, ast::{self, AstNode},
AstPtr, SyntaxNode, TextRange, TextSize, SyntaxNode, TextRange, TextSize,
}; };
use crate::{ use crate::{
@ -161,26 +161,8 @@ impl SourceAnalyzer {
db: &dyn HirDatabase, db: &dyn HirDatabase,
field: &ast::RecordExprField, field: &ast::RecordExprField,
) -> Option<(Field, Option<Local>)> { ) -> Option<(Field, Option<Local>)> {
let expr_id = { let expr_id =
let record_lit = field.parent_record_lit(); self.body_source_map.as_ref()?.node_field(InFile::new(self.file_id, field))?;
let record_lit_expr = self.expr_id(db, &ast::Expr::from(record_lit))?;
let body = self.body.as_ref()?;
let body_source_map = self.body_source_map.as_ref()?;
match &body[record_lit_expr] {
hir_def::expr::Expr::RecordLit { fields, .. } => {
let field_ptr = InFile::new(self.file_id, AstPtr::new(field));
fields.iter().enumerate().find_map(|(i, f)| {
let ptr = body_source_map.field_syntax(record_lit_expr, i);
if ptr == field_ptr {
Some(f.expr)
} else {
None
}
})?
}
_ => return None,
}
};
let local = if field.name_ref().is_some() { let local = if field.name_ref().is_some() {
None None

View File

@ -253,11 +253,18 @@ pub type LabelSource = InFile<LabelPtr>;
pub struct BodySourceMap { pub struct BodySourceMap {
expr_map: FxHashMap<ExprSource, ExprId>, expr_map: FxHashMap<ExprSource, ExprId>,
expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>, expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
pat_map: FxHashMap<PatSource, PatId>, pat_map: FxHashMap<PatSource, PatId>,
pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>, pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
label_map: FxHashMap<LabelSource, LabelId>, label_map: FxHashMap<LabelSource, LabelId>,
label_map_back: ArenaMap<LabelId, LabelSource>, label_map_back: ArenaMap<LabelId, LabelSource>,
field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordExprField>>>,
/// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
/// Instead, we use id of expression (`92`) to identify the field.
field_map: FxHashMap<InFile<AstPtr<ast::RecordExprField>>, ExprId>,
field_map_back: FxHashMap<ExprId, InFile<AstPtr<ast::RecordExprField>>>,
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
/// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in
@ -337,6 +344,8 @@ impl Index<LabelId> for Body {
} }
} }
// FIXME: Change `node_` prefix to something more reasonable.
// Perhaps `expr_syntax` and `expr_id`?
impl BodySourceMap { impl BodySourceMap {
pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> { pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
self.expr_map_back[expr].clone() self.expr_map_back[expr].clone()
@ -375,8 +384,12 @@ impl BodySourceMap {
self.label_map.get(&src).cloned() self.label_map.get(&src).cloned()
} }
pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordExprField>> { pub fn field_syntax(&self, expr: ExprId) -> InFile<AstPtr<ast::RecordExprField>> {
self.field_map[&(expr, field)].clone() self.field_map_back[&expr].clone()
}
pub fn node_field(&self, node: InFile<&ast::RecordExprField>) -> Option<ExprId> {
let src = node.map(|it| AstPtr::new(it));
self.field_map.get(&src).cloned()
} }
pub(crate) fn add_diagnostics(&self, _db: &dyn DefDatabase, sink: &mut DiagnosticSink<'_>) { pub(crate) fn add_diagnostics(&self, _db: &dyn DefDatabase, sink: &mut DiagnosticSink<'_>) {

View File

@ -379,23 +379,22 @@ impl ExprCollector<'_> {
} }
ast::Expr::RecordExpr(e) => { ast::Expr::RecordExpr(e) => {
let path = e.path().and_then(|path| self.expander.parse_path(path)); let path = e.path().and_then(|path| self.expander.parse_path(path));
let mut field_ptrs = Vec::new();
let record_lit = if let Some(nfl) = e.record_expr_field_list() { let record_lit = if let Some(nfl) = e.record_expr_field_list() {
let fields = nfl let fields = nfl
.fields() .fields()
.inspect(|field| field_ptrs.push(AstPtr::new(field)))
.filter_map(|field| { .filter_map(|field| {
self.check_cfg(&field)?; self.check_cfg(&field)?;
let name = field.field_name()?.as_name(); let name = field.field_name()?.as_name();
Some(RecordLitField { let expr = match field.expr() {
name,
expr: match field.expr() {
Some(e) => self.collect_expr(e), Some(e) => self.collect_expr(e),
None => self.missing_expr(), None => self.missing_expr(),
}, };
}) let src = self.expander.to_source(AstPtr::new(&field));
self.source_map.field_map.insert(src.clone(), expr);
self.source_map.field_map_back.insert(expr, src);
Some(RecordLitField { name, expr })
}) })
.collect(); .collect();
let spread = nfl.spread().map(|s| self.collect_expr(s)); let spread = nfl.spread().map(|s| self.collect_expr(s));
@ -404,12 +403,7 @@ impl ExprCollector<'_> {
Expr::RecordLit { path, fields: Vec::new(), spread: None } Expr::RecordLit { path, fields: Vec::new(), spread: None }
}; };
let res = self.alloc_expr(record_lit, syntax_ptr); self.alloc_expr(record_lit, syntax_ptr)
for (i, ptr) in field_ptrs.into_iter().enumerate() {
let src = self.expander.to_source(ptr);
self.source_map.field_map.insert((res, i), src);
}
res
} }
ast::Expr::FieldExpr(e) => { ast::Expr::FieldExpr(e) => {
let expr = self.collect_expr_opt(e.expr()); let expr = self.collect_expr_opt(e.expr());

View File

@ -784,7 +784,7 @@ mod diagnostics {
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub(super) enum InferenceDiagnostic { pub(super) enum InferenceDiagnostic {
NoSuchField { expr: ExprId, field: usize }, NoSuchField { expr: ExprId },
BreakOutsideOfLoop { expr: ExprId }, BreakOutsideOfLoop { expr: ExprId },
} }
@ -796,9 +796,9 @@ mod diagnostics {
sink: &mut DiagnosticSink, sink: &mut DiagnosticSink,
) { ) {
match self { match self {
InferenceDiagnostic::NoSuchField { expr, field } => { InferenceDiagnostic::NoSuchField { expr } => {
let (_, source_map) = db.body_with_source_map(owner); let (_, source_map) = db.body_with_source_map(owner);
let field = source_map.field_syntax(*expr, *field); let field = source_map.field_syntax(*expr);
sink.push(NoSuchField { file: field.file_id, field: field.value }) sink.push(NoSuchField { file: field.file_id, field: field.value })
} }
InferenceDiagnostic::BreakOutsideOfLoop { expr } => { InferenceDiagnostic::BreakOutsideOfLoop { expr } => {

View File

@ -405,14 +405,13 @@ impl<'a> InferenceContext<'a> {
let substs = ty.substs().cloned().unwrap_or_else(Substs::empty); let substs = ty.substs().cloned().unwrap_or_else(Substs::empty);
let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default(); let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default();
let variant_data = def_id.map(|it| variant_data(self.db.upcast(), it)); let variant_data = def_id.map(|it| variant_data(self.db.upcast(), it));
for (field_idx, field) in fields.iter().enumerate() { for field in fields.iter() {
let field_def = let field_def =
variant_data.as_ref().and_then(|it| match it.field(&field.name) { variant_data.as_ref().and_then(|it| match it.field(&field.name) {
Some(local_id) => Some(FieldId { parent: def_id.unwrap(), local_id }), Some(local_id) => Some(FieldId { parent: def_id.unwrap(), local_id }),
None => { None => {
self.push_diagnostic(InferenceDiagnostic::NoSuchField { self.push_diagnostic(InferenceDiagnostic::NoSuchField {
expr: tgt_expr, expr: field.expr,
field: field_idx,
}); });
None None
} }