//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
//! representation.
-use std::mem;
+use std::{mem, sync::Arc};
use either::Either;
use hir_expand::{
+ ast_id_map::{AstIdMap, FileAstId},
hygiene::Hygiene,
name::{name, AsName, Name},
- ExpandError, HirFileId,
+ ExpandError, HirFileId, InFile,
};
use la_arena::Arena;
use profile::Count;
use syntax::{
ast::{
- self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner,
+ self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind,
SlicePatComponents,
},
AstNode, AstPtr, SyntaxNodePtr,
use crate::{
adt::StructKind,
body::{Body, BodySourceMap, Expander, LabelSource, PatPtr, SyntheticSyntax},
+ body::{BodyDiagnostic, ExprSource, PatSource},
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
db::DefDatabase,
- diagnostics::{InactiveCode, MacroError, UnresolvedMacroCall, UnresolvedProcMacro},
expr::{
- dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label,
- LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField,
- Statement,
+ dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, Label, LabelId, Literal, MatchArm,
+ MatchGuard, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
},
intern::Interned,
item_scope::BuiltinShadowMode,
AdtId, BlockLoc, ModuleDefId, UnresolvedMacro,
};
-use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
-
-pub(crate) struct LowerCtx {
+pub struct LowerCtx<'a> {
+ pub db: &'a dyn DefDatabase,
hygiene: Hygiene,
+ file_id: Option<HirFileId>,
+ source_ast_id_map: Option<Arc<AstIdMap>>,
}
-impl LowerCtx {
- pub(crate) fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self {
- LowerCtx { hygiene: Hygiene::new(db.upcast(), file_id) }
+impl<'a> LowerCtx<'a> {
+ pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
+ LowerCtx {
+ db,
+ hygiene: Hygiene::new(db.upcast(), file_id),
+ file_id: Some(file_id),
+ source_ast_id_map: Some(db.ast_id_map(file_id)),
+ }
+ }
+
+ pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
+ LowerCtx { db, hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None }
}
- pub(crate) fn with_hygiene(hygiene: &Hygiene) -> Self {
- LowerCtx { hygiene: hygiene.clone() }
+
+ pub(crate) fn hygiene(&self) -> &Hygiene {
+ &self.hygiene
+ }
+
+ pub(crate) fn file_id(&self) -> HirFileId {
+ self.file_id.unwrap()
}
pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
- Path::from_src(ast, &self.hygiene)
+ Path::from_src(ast, self)
+ }
+
+ pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<FileAstId<N>> {
+ self.source_ast_id_map.as_ref().map(|ast_id_map| ast_id_map.ast_id(item))
}
}
(self.body, self.source_map)
}
- fn ctx(&self) -> LowerCtx {
+ fn ctx(&self) -> LowerCtx<'_> {
LowerCtx::new(self.db, self.expander.current_file_id)
}
self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr())
}
- /// Returns `None` if the expression is `#[cfg]`d out.
+ /// Returns `None` if and only if the expression is `#[cfg]`d out.
fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
let syntax_ptr = AstPtr::new(&expr);
self.check_cfg(&expr)?;
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
}
- ast::Expr::EffectExpr(e) => match e.effect() {
- ast::Effect::Try(_) => {
- let body = self.collect_block_opt(e.block_expr());
+ ast::Expr::BlockExpr(e) => match e.modifier() {
+ Some(ast::BlockModifier::Try(_)) => {
+ let body = self.collect_block(e);
self.alloc_expr(Expr::TryBlock { body }, syntax_ptr)
}
- ast::Effect::Unsafe(_) => {
- let body = self.collect_block_opt(e.block_expr());
+ Some(ast::BlockModifier::Unsafe(_)) => {
+ let body = self.collect_block(e);
self.alloc_expr(Expr::Unsafe { body }, syntax_ptr)
}
// FIXME: we need to record these effects somewhere...
- ast::Effect::Label(label) => {
+ Some(ast::BlockModifier::Label(label)) => {
let label = self.collect_label(label);
- match e.block_expr() {
- Some(block) => {
- let res = self.collect_block(block);
- match &mut self.body.exprs[res] {
- Expr::Block { label: block_label, .. } => {
- *block_label = Some(label);
- }
- _ => unreachable!(),
- }
- res
+ let res = self.collect_block(e);
+ match &mut self.body.exprs[res] {
+ Expr::Block { label: block_label, .. } => {
+ *block_label = Some(label);
}
- None => self.missing_expr(),
+ _ => unreachable!(),
}
+ res
}
- // FIXME: we need to record these effects somewhere...
- ast::Effect::Async(_) => {
- let body = self.collect_block_opt(e.block_expr());
+ Some(ast::BlockModifier::Async(_)) => {
+ let body = self.collect_block(e);
self.alloc_expr(Expr::Async { body }, syntax_ptr)
}
- ast::Effect::Const(_) => {
- let body = self.collect_block_opt(e.block_expr());
+ Some(ast::BlockModifier::Const(_)) => {
+ let body = self.collect_block(e);
self.alloc_expr(Expr::Const { body }, syntax_ptr)
}
+ None => self.collect_block(e),
},
- ast::Expr::BlockExpr(e) => self.collect_block(e),
ast::Expr::LoopExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
let body = self.collect_block_opt(e.loop_body());
self.check_cfg(&arm).map(|()| MatchArm {
pat: self.collect_pat_opt(arm.pat()),
expr: self.collect_expr_opt(arm.expr()),
- guard: arm
- .guard()
- .and_then(|guard| guard.expr())
- .map(|e| self.collect_expr(e)),
+ guard: arm.guard().map(|guard| match guard.pat() {
+ Some(pat) => MatchGuard::IfLet {
+ pat: self.collect_pat(pat),
+ expr: self.collect_expr_opt(guard.expr()),
+ },
+ None => {
+ MatchGuard::If { expr: self.collect_expr_opt(guard.expr()) }
+ }
+ }),
})
})
.collect()
ast::Expr::PathExpr(e) => {
let path = e
.path()
- .and_then(|path| self.expander.parse_path(path))
+ .and_then(|path| self.expander.parse_path(self.db, path))
.map(Expr::Path)
.unwrap_or(Expr::Missing);
self.alloc_expr(path, syntax_ptr)
self.alloc_expr(Expr::Yield { expr }, syntax_ptr)
}
ast::Expr::RecordExpr(e) => {
- let path = e.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
+ let path =
+ e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
let record_lit = if let Some(nfl) = e.record_expr_field_list() {
let fields = nfl
.fields()
}
ast::Expr::PrefixExpr(e) => {
let expr = self.collect_expr_opt(e.expr());
- if let Some(op) = e.op_kind() {
- self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr)
- } else {
- self.alloc_expr(Expr::Missing, syntax_ptr)
+ match e.op_kind() {
+ Some(op) => self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr),
+ None => self.alloc_expr(Expr::Missing, syntax_ptr),
}
}
ast::Expr::ClosureExpr(e) => {
ast::Expr::BinExpr(e) => {
let lhs = self.collect_expr_opt(e.lhs());
let rhs = self.collect_expr_opt(e.rhs());
- let op = e.op_kind().map(BinaryOp::from);
+ let op = e.op_kind();
self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
}
ast::Expr::TupleExpr(e) => {
ast::Expr::MacroCall(e) => {
let macro_ptr = AstPtr::new(&e);
let mut ids = vec![];
- self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
+ self.collect_macro_call(e, macro_ptr, |this, expansion| {
ids.push(match expansion {
Some(it) => this.collect_expr(it),
None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()),
&mut self,
e: ast::MacroCall,
syntax_ptr: AstPtr<ast::MacroCall>,
- is_error_recoverable: bool,
mut collector: F,
) {
// File containing the macro call. Expansion errors will be attached here.
let res = match res {
Ok(res) => res,
Err(UnresolvedMacro { path }) => {
- self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall(
- UnresolvedMacroCall {
- file: outer_file,
- node: syntax_ptr.cast().unwrap(),
- path,
- },
- ));
+ self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall {
+ node: InFile::new(outer_file, syntax_ptr),
+ path,
+ });
collector(self, None);
return;
}
match &res.err {
Some(ExpandError::UnresolvedProcMacro) => {
- self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro(
- UnresolvedProcMacro {
- file: outer_file,
- node: syntax_ptr.into(),
- precise_location: None,
- macro_name: None,
- },
- ));
+ self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro {
+ node: InFile::new(outer_file, syntax_ptr),
+ });
}
Some(err) => {
- self.source_map.diagnostics.push(BodyDiagnostic::MacroError(MacroError {
- file: outer_file,
- node: syntax_ptr.into(),
+ self.source_map.diagnostics.push(BodyDiagnostic::MacroError {
+ node: InFile::new(outer_file, syntax_ptr),
message: err.to_string(),
- }));
+ });
}
None => {}
}
match res.value {
Some((mark, expansion)) => {
- // FIXME: Statements are too complicated to recover from error for now.
- // It is because we don't have any hygiene for local variable expansion right now.
- if !is_error_recoverable && res.err.is_some() {
- self.expander.exit(self.db, mark);
- collector(self, None);
- } else {
- self.source_map.expansions.insert(macro_call, self.expander.current_file_id);
+ self.source_map.expansions.insert(macro_call, self.expander.current_file_id);
- let id = collector(self, Some(expansion));
- self.expander.exit(self.db, mark);
- id
- }
+ let id = collector(self, Some(expansion));
+ self.expander.exit(self.db, mark);
+ id
}
None => collector(self, None),
}
}
fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
- if let Some(expr) = expr {
- self.collect_expr(expr)
- } else {
- self.missing_expr()
+ match expr {
+ Some(expr) => self.collect_expr(expr),
+ None => self.missing_expr(),
}
}
self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer });
}
ast::Stmt::ExprStmt(stmt) => {
- if self.check_cfg(&stmt).is_none() {
- return;
+ if let Some(expr) = stmt.expr() {
+ if self.check_cfg(&expr).is_none() {
+ return;
+ }
}
-
+ let has_semi = stmt.semicolon_token().is_some();
// Note that macro could be expended to multiple statements
if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {
let macro_ptr = AstPtr::new(&m);
let syntax_ptr = AstPtr::new(&stmt.expr().unwrap());
- self.collect_macro_call(
- m,
- macro_ptr,
- false,
- |this, expansion| match expansion {
- Some(expansion) => {
- let statements: ast::MacroStmts = expansion;
-
- statements.statements().for_each(|stmt| this.collect_stmt(stmt));
- if let Some(expr) = statements.expr() {
- let expr = this.collect_expr(expr);
- this.statements_in_scope.push(Statement::Expr(expr));
- }
- }
- None => {
- let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone());
- this.statements_in_scope.push(Statement::Expr(expr));
+ self.collect_macro_call(m, macro_ptr, |this, expansion| match expansion {
+ Some(expansion) => {
+ let statements: ast::MacroStmts = expansion;
+
+ statements.statements().for_each(|stmt| this.collect_stmt(stmt));
+ if let Some(expr) = statements.expr() {
+ let expr = this.collect_expr(expr);
+ this.statements_in_scope.push(Statement::Expr { expr, has_semi });
}
- },
- );
+ }
+ None => {
+ let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone());
+ this.statements_in_scope.push(Statement::Expr { expr, has_semi });
+ }
+ });
} else {
let expr = self.collect_expr_opt(stmt.expr());
- self.statements_in_scope.push(Statement::Expr(expr));
+ self.statements_in_scope.push(Statement::Expr { expr, has_semi });
}
}
ast::Stmt::Item(item) => {
- if self.check_cfg(&item).is_none() {
- return;
- }
+ self.check_cfg(&item);
}
}
}
let prev_statements = std::mem::take(&mut self.statements_in_scope);
block.statements().for_each(|s| self.collect_stmt(s));
-
- let tail = block.tail_expr().map(|e| self.collect_expr(e));
+ block.tail_expr().and_then(|e| {
+ let expr = self.maybe_collect_expr(e)?;
+ self.statements_in_scope.push(Statement::Expr { expr, has_semi: false });
+ Some(())
+ });
+
+ let mut tail = None;
+ if let Some(Statement::Expr { expr, has_semi: false }) = self.statements_in_scope.last() {
+ tail = Some(*expr);
+ self.statements_in_scope.pop();
+ }
+ let tail = tail;
let statements = std::mem::replace(&mut self.statements_in_scope, prev_statements);
let syntax_node_ptr = AstPtr::new(&block.into());
let expr_id = self.alloc_expr(
}
fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId {
- if let Some(block) = expr {
- self.collect_block(block)
- } else {
- self.missing_expr()
+ match expr {
+ Some(block) => self.collect_block(block),
+ None => self.missing_expr(),
}
}
}
}
ast::Pat::TupleStructPat(p) => {
- let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
+ let path =
+ p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
let (args, ellipsis) = self.collect_tuple_pat(p.fields());
Pat::TupleStruct { path, args, ellipsis }
}
Pat::Ref { pat, mutability }
}
ast::Pat::PathPat(p) => {
- let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
+ let path =
+ p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
path.map(Pat::Path).unwrap_or(Pat::Missing)
}
ast::Pat::OrPat(p) => {
}
ast::Pat::WildcardPat(_) => Pat::Wild,
ast::Pat::RecordPat(p) => {
- let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
+ let path =
+ p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
let args: Vec<_> = p
.record_pat_field_list()
.expect("every struct should have a field list")
Some(call) => {
let macro_ptr = AstPtr::new(&call);
let mut pat = None;
- self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
+ self.collect_macro_call(call, macro_ptr, |this, expanded_pat| {
pat = Some(this.collect_pat_opt(expanded_pat));
});
}
fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
- if let Some(pat) = pat {
- self.collect_pat(pat)
- } else {
- self.missing_pat()
+ match pat {
+ Some(pat) => self.collect_pat(pat),
+ None => self.missing_pat(),
}
}
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
/// not.
- fn check_cfg(&mut self, owner: &dyn ast::AttrsOwner) -> Option<()> {
+ fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> {
match self.expander.parse_attrs(self.db, owner).cfg() {
Some(cfg) => {
if self.expander.cfg_options().check(&cfg) != Some(false) {
return Some(());
}
- self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode(InactiveCode {
- file: self.expander.current_file_id,
- node: SyntaxNodePtr::new(owner.syntax()),
+ self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode {
+ node: InFile::new(
+ self.expander.current_file_id,
+ SyntaxNodePtr::new(owner.syntax()),
+ ),
cfg,
opts: self.expander.cfg_options().clone(),
- }));
+ });
None
}
}
}
-impl From<ast::BinOp> for BinaryOp {
- fn from(ast_op: ast::BinOp) -> Self {
- match ast_op {
- ast::BinOp::BooleanOr => BinaryOp::LogicOp(LogicOp::Or),
- ast::BinOp::BooleanAnd => BinaryOp::LogicOp(LogicOp::And),
- ast::BinOp::EqualityTest => BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
- ast::BinOp::NegatedEqualityTest => BinaryOp::CmpOp(CmpOp::Eq { negated: true }),
- ast::BinOp::LesserEqualTest => {
- BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: false })
- }
- ast::BinOp::GreaterEqualTest => {
- BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: false })
- }
- ast::BinOp::LesserTest => {
- BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: true })
- }
- ast::BinOp::GreaterTest => {
- BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: true })
- }
- ast::BinOp::Addition => BinaryOp::ArithOp(ArithOp::Add),
- ast::BinOp::Multiplication => BinaryOp::ArithOp(ArithOp::Mul),
- ast::BinOp::Subtraction => BinaryOp::ArithOp(ArithOp::Sub),
- ast::BinOp::Division => BinaryOp::ArithOp(ArithOp::Div),
- ast::BinOp::Remainder => BinaryOp::ArithOp(ArithOp::Rem),
- ast::BinOp::LeftShift => BinaryOp::ArithOp(ArithOp::Shl),
- ast::BinOp::RightShift => BinaryOp::ArithOp(ArithOp::Shr),
- ast::BinOp::BitwiseXor => BinaryOp::ArithOp(ArithOp::BitXor),
- ast::BinOp::BitwiseOr => BinaryOp::ArithOp(ArithOp::BitOr),
- ast::BinOp::BitwiseAnd => BinaryOp::ArithOp(ArithOp::BitAnd),
- ast::BinOp::Assignment => BinaryOp::Assignment { op: None },
- ast::BinOp::AddAssign => BinaryOp::Assignment { op: Some(ArithOp::Add) },
- ast::BinOp::DivAssign => BinaryOp::Assignment { op: Some(ArithOp::Div) },
- ast::BinOp::MulAssign => BinaryOp::Assignment { op: Some(ArithOp::Mul) },
- ast::BinOp::RemAssign => BinaryOp::Assignment { op: Some(ArithOp::Rem) },
- ast::BinOp::ShlAssign => BinaryOp::Assignment { op: Some(ArithOp::Shl) },
- ast::BinOp::ShrAssign => BinaryOp::Assignment { op: Some(ArithOp::Shr) },
- ast::BinOp::SubAssign => BinaryOp::Assignment { op: Some(ArithOp::Sub) },
- ast::BinOp::BitOrAssign => BinaryOp::Assignment { op: Some(ArithOp::BitOr) },
- ast::BinOp::BitAndAssign => BinaryOp::Assignment { op: Some(ArithOp::BitAnd) },
- ast::BinOp::BitXorAssign => BinaryOp::Assignment { op: Some(ArithOp::BitXor) },
- }
- }
-}
-
impl From<ast::LiteralKind> for Literal {
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
match ast_lit_kind {
+ // FIXME: these should have actual values filled in, but unsure on perf impact
LiteralKind::IntNumber(lit) => {
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
- return Literal::Float(Default::default(), builtin);
+ Literal::Float(Default::default(), builtin)
} else if let builtin @ Some(_) =
- lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it))
+ lit.suffix().and_then(|it| BuiltinInt::from_suffix(it))
{
- Literal::Int(Default::default(), builtin)
+ Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
} else {
- let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(&it));
- Literal::Uint(Default::default(), builtin)
+ let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(it));
+ Literal::Uint(lit.value().unwrap_or(0), builtin)
}
}
LiteralKind::FloatNumber(lit) => {
- let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it));
+ let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(it));
Literal::Float(Default::default(), ty)
}
- LiteralKind::ByteString(_) => Literal::ByteString(Default::default()),
+ LiteralKind::ByteString(bs) => {
+ let text = bs.value().map(Vec::from).unwrap_or_else(Default::default);
+ Literal::ByteString(text)
+ }
LiteralKind::String(_) => Literal::String(Default::default()),
LiteralKind::Byte => Literal::Uint(Default::default(), Some(BuiltinUint::U8)),
LiteralKind::Bool(val) => Literal::Bool(val),