3489: More robust expression lowering  r=matklad a=matklad

Closes #2236

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2020-03-06 14:19:58 +00:00 committed by GitHub
commit 1cc6879576
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 51 additions and 38 deletions

View file

@ -21,9 +21,9 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
(db, file_id) (db, file_id)
} }
fn with_files(fixture: &str) -> Self { fn with_files(ra_fixture: &str) -> Self {
let mut db = Self::default(); let mut db = Self::default();
let pos = with_files(&mut db, fixture); let pos = with_files(&mut db, ra_fixture);
assert!(pos.is_none()); assert!(pos.is_none());
db db
} }

View file

@ -261,7 +261,7 @@ fn scope_for_offset(
.scope_by_expr() .scope_by_expr()
.iter() .iter()
.filter_map(|(id, scope)| { .filter_map(|(id, scope)| {
let source = source_map.expr_syntax(*id)?; let source = source_map.expr_syntax(*id).ok()?;
// FIXME: correctly handle macro expansion // FIXME: correctly handle macro expansion
if source.file_id != offset.file_id { if source.file_id != offset.file_id {
return None; return None;
@ -337,7 +337,7 @@ fn adjust(
.scope_by_expr() .scope_by_expr()
.iter() .iter()
.filter_map(|(id, scope)| { .filter_map(|(id, scope)| {
let source = source_map.expr_syntax(*id)?; let source = source_map.expr_syntax(*id).ok()?;
// FIXME: correctly handle macro expansion // FIXME: correctly handle macro expansion
if source.file_id != file_id { if source.file_id != file_id {
return None; return None;

View file

@ -149,13 +149,16 @@ pub type PatSource = InFile<PatPtr>;
#[derive(Default, Debug, Eq, PartialEq)] #[derive(Default, Debug, Eq, PartialEq)]
pub struct BodySourceMap { pub struct BodySourceMap {
expr_map: FxHashMap<ExprSource, ExprId>, expr_map: FxHashMap<ExprSource, ExprId>,
expr_map_back: ArenaMap<ExprId, ExprSource>, expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
pat_map: FxHashMap<PatSource, PatId>, pat_map: FxHashMap<PatSource, PatId>,
pat_map_back: ArenaMap<PatId, PatSource>, pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>, field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>,
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
} }
#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
pub struct SyntheticSyntax;
impl Body { impl Body {
pub(crate) fn body_with_source_map_query( pub(crate) fn body_with_source_map_query(
db: &impl DefDatabase, db: &impl DefDatabase,
@ -219,8 +222,8 @@ impl Index<PatId> for Body {
} }
impl BodySourceMap { impl BodySourceMap {
pub fn expr_syntax(&self, expr: ExprId) -> Option<ExprSource> { pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
self.expr_map_back.get(expr).copied() self.expr_map_back[expr]
} }
pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> { pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
@ -238,8 +241,8 @@ impl BodySourceMap {
self.expr_map.get(&src).cloned() self.expr_map.get(&src).cloned()
} }
pub fn pat_syntax(&self, pat: PatId) -> Option<PatSource> { pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
self.pat_map_back.get(pat).copied() self.pat_map_back[pat]
} }
pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> { pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {

View file

@ -14,9 +14,10 @@ use ra_syntax::{
}; };
use test_utils::tested_by; use test_utils::tested_by;
use super::{ExprSource, PatSource};
use crate::{ use crate::{
adt::StructKind, adt::StructKind,
body::{Body, BodySourceMap, Expander, PatPtr}, body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax},
builtin_type::{BuiltinFloat, BuiltinInt}, builtin_type::{BuiltinFloat, BuiltinInt},
db::DefDatabase, db::DefDatabase,
expr::{ expr::{
@ -102,44 +103,48 @@ where
fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId { fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId {
let ptr = Either::Left(ptr); let ptr = Either::Left(ptr);
let id = self.body.exprs.alloc(expr);
let src = self.expander.to_source(ptr); let src = self.expander.to_source(ptr);
let id = self.make_expr(expr, Ok(src));
self.source_map.expr_map.insert(src, id); self.source_map.expr_map.insert(src, id);
self.source_map.expr_map_back.insert(id, src);
id id
} }
// desugared exprs don't have ptr, that's wrong and should be fixed // desugared exprs don't have ptr, that's wrong and should be fixed
// somehow. // somehow.
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
self.body.exprs.alloc(expr) self.make_expr(expr, Err(SyntheticSyntax))
} }
fn alloc_expr_field_shorthand(&mut self, expr: Expr, ptr: AstPtr<ast::RecordField>) -> ExprId { fn alloc_expr_field_shorthand(&mut self, expr: Expr, ptr: AstPtr<ast::RecordField>) -> ExprId {
let ptr = Either::Right(ptr); let ptr = Either::Right(ptr);
let id = self.body.exprs.alloc(expr);
let src = self.expander.to_source(ptr); let src = self.expander.to_source(ptr);
let id = self.make_expr(expr, Ok(src));
self.source_map.expr_map.insert(src, id); self.source_map.expr_map.insert(src, id);
id
}
fn empty_block(&mut self) -> ExprId {
self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None })
}
fn missing_expr(&mut self) -> ExprId {
self.alloc_expr_desugared(Expr::Missing)
}
fn make_expr(&mut self, expr: Expr, src: Result<ExprSource, SyntheticSyntax>) -> ExprId {
let id = self.body.exprs.alloc(expr);
self.source_map.expr_map_back.insert(id, src); self.source_map.expr_map_back.insert(id, src);
id id
} }
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId { fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
let id = self.body.pats.alloc(pat);
let src = self.expander.to_source(ptr); let src = self.expander.to_source(ptr);
let id = self.make_pat(pat, Ok(src));
self.source_map.pat_map.insert(src, id); self.source_map.pat_map.insert(src, id);
self.source_map.pat_map_back.insert(id, src);
id id
} }
fn empty_block(&mut self) -> ExprId {
let block = Expr::Block { statements: Vec::new(), tail: None };
self.body.exprs.alloc(block)
}
fn missing_expr(&mut self) -> ExprId {
self.body.exprs.alloc(Expr::Missing)
}
fn missing_pat(&mut self) -> PatId { fn missing_pat(&mut self) -> PatId {
self.body.pats.alloc(Pat::Missing) self.make_pat(Pat::Missing, Err(SyntheticSyntax))
}
fn make_pat(&mut self, pat: Pat, src: Result<PatSource, SyntheticSyntax>) -> PatId {
let id = self.body.pats.alloc(pat);
self.source_map.pat_map_back.insert(id, src);
id
} }
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {

View file

@ -100,7 +100,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
} }
let (_, source_map) = db.body_with_source_map(self.func.into()); let (_, source_map) = db.body_with_source_map(self.func.into());
if let Some(source_ptr) = source_map.expr_syntax(id) { if let Ok(source_ptr) = source_map.expr_syntax(id) {
if let Some(expr) = source_ptr.value.left() { if let Some(expr) = source_ptr.value.left() {
let root = source_ptr.file_syntax(db); let root = source_ptr.file_syntax(db);
if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) { if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
@ -145,7 +145,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
if params.len() == 2 && params[0] == mismatch.actual { if params.len() == 2 && params[0] == mismatch.actual {
let (_, source_map) = db.body_with_source_map(self.func.into()); let (_, source_map) = db.body_with_source_map(self.func.into());
if let Some(source_ptr) = source_map.expr_syntax(id) { if let Ok(source_ptr) = source_map.expr_syntax(id) {
if let Some(expr) = source_ptr.value.left() { if let Some(expr) = source_ptr.value.left() {
self.sink.push(MissingOkInTailExpr { file: source_ptr.file_id, expr }); self.sink.push(MissingOkInTailExpr { file: source_ptr.file_id, expr });
} }

View file

@ -11,8 +11,13 @@ use std::fmt::Write;
use std::sync::Arc; use std::sync::Arc;
use hir_def::{ use hir_def::{
body::BodySourceMap, child_by_source::ChildBySource, db::DefDatabase, item_scope::ItemScope, body::{BodySourceMap, SyntheticSyntax},
keys, nameres::CrateDefMap, AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, child_by_source::ChildBySource,
db::DefDatabase,
item_scope::ItemScope,
keys,
nameres::CrateDefMap,
AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
}; };
use hir_expand::InFile; use hir_expand::InFile;
use insta::assert_snapshot; use insta::assert_snapshot;
@ -67,20 +72,20 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
for (pat, ty) in inference_result.type_of_pat.iter() { for (pat, ty) in inference_result.type_of_pat.iter() {
let syntax_ptr = match body_source_map.pat_syntax(pat) { let syntax_ptr = match body_source_map.pat_syntax(pat) {
Some(sp) => { Ok(sp) => {
sp.map(|ast| ast.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr())) sp.map(|ast| ast.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()))
} }
None => continue, Err(SyntheticSyntax) => continue,
}; };
types.push((syntax_ptr, ty)); types.push((syntax_ptr, ty));
} }
for (expr, ty) in inference_result.type_of_expr.iter() { for (expr, ty) in inference_result.type_of_expr.iter() {
let syntax_ptr = match body_source_map.expr_syntax(expr) { let syntax_ptr = match body_source_map.expr_syntax(expr) {
Some(sp) => { Ok(sp) => {
sp.map(|ast| ast.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr())) sp.map(|ast| ast.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()))
} }
None => continue, Err(SyntheticSyntax) => continue,
}; };
types.push((syntax_ptr, ty)); types.push((syntax_ptr, ty));
if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) {

View file

@ -158,7 +158,7 @@ pub fn analysis_stats(
// in super-verbose mode for just one function, we print every single expression // in super-verbose mode for just one function, we print every single expression
let (_, sm) = db.body_with_source_map(f_id.into()); let (_, sm) = db.body_with_source_map(f_id.into());
let src = sm.expr_syntax(expr_id); let src = sm.expr_syntax(expr_id);
if let Some(src) = src { if let Ok(src) = src {
let original_file = src.file_id.original_file(db); let original_file = src.file_id.original_file(db);
let line_index = host.analysis().file_line_index(original_file).unwrap(); let line_index = host.analysis().file_line_index(original_file).unwrap();
let text_range = src.value.either( let text_range = src.value.either(
@ -186,7 +186,7 @@ pub fn analysis_stats(
if verbosity.is_verbose() { if verbosity.is_verbose() {
let (_, sm) = db.body_with_source_map(f_id.into()); let (_, sm) = db.body_with_source_map(f_id.into());
let src = sm.expr_syntax(expr_id); let src = sm.expr_syntax(expr_id);
if let Some(src) = src { if let Ok(src) = src {
// FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly // FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly
// But also, we should just turn the type mismatches into diagnostics and provide these // But also, we should just turn the type mismatches into diagnostics and provide these
let root = db.parse_or_expand(src.file_id).unwrap(); let root = db.parse_or_expand(src.file_id).unwrap();