Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/body/lower.rs')
| -rw-r--r-- | crates/hir-def/src/body/lower.rs | 693 |
1 files changed, 496 insertions, 197 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 9c547574ec..0b108b54e6 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -9,9 +9,10 @@ use base_db::CrateId; use either::Either; use hir_expand::{ name::{AsName, Name}, - InFile, + span_map::{ExpansionSpanMap, SpanMap}, + InFile, MacroDefId, }; -use intern::{sym, Interned, Symbol}; +use intern::{sym, Symbol}; use rustc_hash::FxHashMap; use span::AstIdMap; use stdx::never; @@ -22,10 +23,11 @@ use syntax::{ }, AstNode, AstPtr, AstToken as _, SyntaxNodePtr, }; +use text_size::TextSize; use triomphe::Arc; use crate::{ - body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr}, + body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr}, builtin_type::BuiltinUint, data::adt::StructKind, db::DefDatabase, @@ -37,8 +39,8 @@ use crate::{ FormatPlaceholder, FormatSign, FormatTrait, }, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, - Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat, - PatId, RecordFieldPat, RecordLitField, Statement, + Expr, ExprId, Item, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, + OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -46,7 +48,7 @@ use crate::{ nameres::{DefMap, MacroSubNs}, path::{GenericArgs, Path}, type_ref::{Mutability, Rawness, TypeRef}, - AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro, + AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, MacroId, ModuleDefId, UnresolvedMacro, }; type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>; @@ -60,6 +62,17 @@ pub(super) fn lower( krate: CrateId, is_async_fn: bool, ) -> (Body, BodySourceMap) { + // We cannot leave the root span map empty and let any identifier from it be treated as root, + // because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved + // with the inner macro, and that will cause confusion because they won't be the same as `ROOT` + // even though they should be the same. Also, when the body comes from multiple expansions, their + // hygiene is different. + let span_map = expander.current_file_id().macro_file().map(|_| { + let SpanMap::ExpansionSpanMap(span_map) = expander.span_map(db) else { + panic!("in a macro file there should be `ExpansionSpanMap`"); + }; + Arc::clone(span_map) + }); ExprCollector { db, owner, @@ -70,11 +83,12 @@ pub(super) fn lower( body: Body::default(), expander, current_try_block_label: None, - is_lowering_assignee_expr: false, is_lowering_coroutine: false, label_ribs: Vec::new(), current_binding_owner: None, awaitable_context: None, + current_span_map: span_map, + current_block_legacy_macro_defs_count: FxHashMap::default(), } .collect(params, body, is_async_fn) } @@ -89,9 +103,14 @@ struct ExprCollector<'a> { body: Body, source_map: BodySourceMap, - is_lowering_assignee_expr: bool, is_lowering_coroutine: bool, + /// Legacy (`macro_rules!`) macros can have multiple definitions and shadow each other, + /// and we need to find the current definition. So we track the number of definitions we saw. + current_block_legacy_macro_defs_count: FxHashMap<Name, usize>, + + current_span_map: Option<Arc<ExpansionSpanMap>>, + current_try_block_label: Option<LabelId>, // points to the expression that a try expression will target (replaces current_try_block_label) // catch_scope: Option<ExprId>, @@ -110,31 +129,27 @@ struct ExprCollector<'a> { #[derive(Clone, Debug)] struct LabelRib { kind: RibKind, - // Once we handle macro hygiene this will need to be a map - label: Option<(Name, LabelId)>, } impl LabelRib { fn new(kind: RibKind) -> Self { - LabelRib { kind, label: None } - } - fn new_normal(label: (Name, LabelId)) -> Self { - LabelRib { kind: RibKind::Normal, label: Some(label) } + LabelRib { kind } } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] enum RibKind { - Normal, + Normal(Name, LabelId, HygieneId), Closure, Constant, + MacroDef(Box<MacroDefId>), } impl RibKind { /// This rib forbids referring to labels defined in upwards ribs. - fn is_label_barrier(self) -> bool { + fn is_label_barrier(&self) -> bool { match self { - RibKind::Normal => false, + RibKind::Normal(..) | RibKind::MacroDef(_) => false, RibKind::Closure | RibKind::Constant => true, } } @@ -147,7 +162,7 @@ enum Awaitable { #[derive(Debug, Default)] struct BindingList { - map: FxHashMap<Name, BindingId>, + map: FxHashMap<(Name, HygieneId), BindingId>, is_used: FxHashMap<BindingId, bool>, reject_new: bool, } @@ -157,9 +172,16 @@ impl BindingList { &mut self, ec: &mut ExprCollector<'_>, name: Name, + hygiene: HygieneId, mode: BindingAnnotation, ) -> BindingId { - let id = *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode)); + let id = *self.map.entry((name, hygiene)).or_insert_with_key(|(name, _)| { + let id = ec.alloc_binding(name.clone(), mode); + if !hygiene.is_root() { + ec.body.binding_hygiene.insert(id, hygiene); + } + id + }); if ec.body.bindings[id].mode != mode { ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently); } @@ -213,6 +235,13 @@ impl ExprCollector<'_> { Name::new_symbol_root(sym::self_.clone()), BindingAnnotation::new(is_mutable, false), ); + let hygiene = self_param + .name() + .map(|name| self.hygiene_id_for(name.syntax().text_range().start())) + .unwrap_or(HygieneId::ROOT); + if !hygiene.is_root() { + self.body.binding_hygiene.insert(binding_id, hygiene); + } self.body.self_param = Some(binding_id); self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param))); } @@ -245,8 +274,8 @@ impl ExprCollector<'_> { (self.body, self.source_map) } - fn ctx(&self) -> LowerCtx<'_> { - self.expander.ctx(self.db) + fn ctx(&mut self) -> LowerCtx<'_> { + self.expander.ctx(self.db, &mut self.body.types, &mut self.source_map.types) } fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { @@ -290,13 +319,14 @@ impl ExprCollector<'_> { }) } Some(ast::BlockModifier::Label(label)) => { - let label = self.collect_label(label); - self.with_labeled_rib(label, |this| { + let label_hygiene = self.hygiene_id_for(label.syntax().text_range().start()); + let label_id = self.collect_label(label); + self.with_labeled_rib(label_id, label_hygiene, |this| { this.collect_block_(e, |id, statements, tail| Expr::Block { id, statements, tail, - label: Some(label), + label: Some(label_id), }) }) } @@ -338,9 +368,14 @@ impl ExprCollector<'_> { None => self.collect_block(e), }, ast::Expr::LoopExpr(e) => { - let label = e.label().map(|label| self.collect_label(label)); + let label = e.label().map(|label| { + ( + self.hygiene_id_for(label.syntax().text_range().start()), + self.collect_label(label), + ) + }); let body = self.collect_labelled_block_opt(label, e.loop_body()); - self.alloc_expr(Expr::Loop { body, label }, syntax_ptr) + self.alloc_expr(Expr::Loop { body, label: label.map(|it| it.1) }, syntax_ptr) } ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e), ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e), @@ -359,14 +394,7 @@ impl ExprCollector<'_> { } else { Box::default() }; - self.alloc_expr( - Expr::Call { - callee, - args, - is_assignee_expr: self.is_lowering_assignee_expr, - }, - syntax_ptr, - ) + self.alloc_expr(Expr::Call { callee, args }, syntax_ptr) } } ast::Expr::MethodCallExpr(e) => { @@ -407,12 +435,15 @@ impl ExprCollector<'_> { self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr) } ast::Expr::PathExpr(e) => { - let path = e - .path() - .and_then(|path| self.expander.parse_path(self.db, path)) - .map(Expr::Path) - .unwrap_or(Expr::Missing); - self.alloc_expr(path, syntax_ptr) + let (path, hygiene) = self + .collect_expr_path(e) + .map(|(path, hygiene)| (Expr::Path(path), hygiene)) + .unwrap_or((Expr::Missing, HygieneId::ROOT)); + let expr_id = self.alloc_expr(path, syntax_ptr); + if !hygiene.is_root() { + self.body.expr_hygiene.insert(expr_id, hygiene); + } + expr_id } ast::Expr::ContinueExpr(e) => { let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| { @@ -433,7 +464,7 @@ impl ExprCollector<'_> { let inner = self.collect_expr_opt(e.expr()); // make the paren expr point to the inner expression as well for IDE resolution let src = self.expander.in_file(syntax_ptr); - self.source_map.expr_map.insert(src, inner); + self.source_map.expr_map.insert(src, inner.into()); inner } ast::Expr::ReturnExpr(e) => { @@ -455,9 +486,7 @@ impl ExprCollector<'_> { self.alloc_expr(Expr::Yeet { expr }, syntax_ptr) } ast::Expr::RecordExpr(e) => { - let path = - e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); - let is_assignee_expr = self.is_lowering_assignee_expr; + let path = e.path().and_then(|path| self.parse_path(path)).map(Box::new); let record_lit = if let Some(nfl) = e.record_expr_field_list() { let fields = nfl .fields() @@ -476,16 +505,9 @@ impl ExprCollector<'_> { }) .collect(); let spread = nfl.spread().map(|s| self.collect_expr(s)); - let ellipsis = nfl.dotdot_token().is_some(); - Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr } + Expr::RecordLit { path, fields, spread } } else { - Expr::RecordLit { - path, - fields: Box::default(), - spread: None, - ellipsis: false, - is_assignee_expr, - } + Expr::RecordLit { path, fields: Box::default(), spread: None } }; self.alloc_expr(record_lit, syntax_ptr) @@ -511,7 +533,7 @@ impl ExprCollector<'_> { ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e), ast::Expr::CastExpr(e) => { let expr = self.collect_expr_opt(e.expr()); - let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); + let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.ty()); self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) } ast::Expr::RefExpr(e) => { @@ -550,16 +572,13 @@ impl ExprCollector<'_> { arg_types.reserve_exact(num_params); for param in pl.params() { let pat = this.collect_pat_top(param.pat()); - let type_ref = - param.ty().map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it))); + let type_ref = param.ty().map(|it| TypeRef::from_ast(&this.ctx(), it)); args.push(pat); arg_types.push(type_ref); } } - let ret_type = e - .ret_type() - .and_then(|r| r.ty()) - .map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it))); + let ret_type = + e.ret_type().and_then(|r| r.ty()).map(|it| TypeRef::from_ast(&this.ctx(), it)); let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine); let prev_try_block_label = this.current_try_block_label.take(); @@ -602,12 +621,14 @@ impl ExprCollector<'_> { ast::Expr::BinExpr(e) => { let op = e.op_kind(); if let Some(ast::BinaryOp::Assignment { op: None }) = op { - self.is_lowering_assignee_expr = true; + let target = self.collect_expr_as_pat_opt(e.lhs()); + let value = self.collect_expr_opt(e.rhs()); + self.alloc_expr(Expr::Assignment { target, value }, syntax_ptr) + } else { + let lhs = self.collect_expr_opt(e.lhs()); + let rhs = self.collect_expr_opt(e.rhs()); + self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr) } - let lhs = self.collect_expr_opt(e.lhs()); - self.is_lowering_assignee_expr = false; - let rhs = self.collect_expr_opt(e.rhs()); - self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr) } ast::Expr::TupleExpr(e) => { let mut exprs: Vec<_> = e.fields().map(|expr| self.collect_expr(expr)).collect(); @@ -617,13 +638,7 @@ impl ExprCollector<'_> { exprs.insert(0, self.missing_expr()); } - self.alloc_expr( - Expr::Tuple { - exprs: exprs.into_boxed_slice(), - is_assignee_expr: self.is_lowering_assignee_expr, - }, - syntax_ptr, - ) + self.alloc_expr(Expr::Tuple { exprs: exprs.into_boxed_slice() }, syntax_ptr) } ast::Expr::ArrayExpr(e) => { let kind = e.kind(); @@ -631,13 +646,7 @@ impl ExprCollector<'_> { match kind { ArrayExprKind::ElementList(e) => { let elements = e.map(|expr| self.collect_expr(expr)).collect(); - self.alloc_expr( - Expr::Array(Array::ElementList { - elements, - is_assignee_expr: self.is_lowering_assignee_expr, - }), - syntax_ptr, - ) + self.alloc_expr(Expr::Array(Array::ElementList { elements }), syntax_ptr) } ArrayExprKind::Repeat { initializer, repeat } => { let initializer = self.collect_expr_opt(initializer); @@ -664,8 +673,7 @@ impl ExprCollector<'_> { ast::Expr::IndexExpr(e) => { let base = self.collect_expr_opt(e.base()); let index = self.collect_expr_opt(e.index()); - let is_assignee_expr = self.is_lowering_assignee_expr; - self.alloc_expr(Expr::Index { base, index, is_assignee_expr }, syntax_ptr) + self.alloc_expr(Expr::Index { base, index }, syntax_ptr) } ast::Expr::RangeExpr(e) => { let lhs = e.start().map(|lhs| self.collect_expr(lhs)); @@ -688,7 +696,7 @@ impl ExprCollector<'_> { // Make the macro-call point to its expanded expression so we can query // semantics on syntax pointers to the macro let src = self.expander.in_file(syntax_ptr); - self.source_map.expr_map.insert(src, id); + self.source_map.expr_map.insert(src, id.into()); id } None => self.alloc_expr(Expr::Missing, syntax_ptr), @@ -697,7 +705,7 @@ impl ExprCollector<'_> { ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr), ast::Expr::AsmExpr(e) => self.lower_inline_asm(e, syntax_ptr), ast::Expr::OffsetOfExpr(e) => { - let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); + let container = TypeRef::from_ast_opt(&self.ctx(), e.ty()); let fields = e.fields().map(|it| it.as_name()).collect(); self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr) } @@ -705,6 +713,200 @@ impl ExprCollector<'_> { }) } + fn parse_path(&mut self, path: ast::Path) -> Option<Path> { + self.expander.parse_path(self.db, path, &mut self.body.types, &mut self.source_map.types) + } + + fn collect_expr_path(&mut self, e: ast::PathExpr) -> Option<(Path, HygieneId)> { + e.path().and_then(|path| { + let path = self.parse_path(path)?; + // Need to enable `mod_path.len() < 1` for `self`. + let may_be_variable = matches!(&path, Path::BarePath(mod_path) if mod_path.len() <= 1); + let hygiene = if may_be_variable { + self.hygiene_id_for(e.syntax().text_range().start()) + } else { + HygieneId::ROOT + }; + Some((path, hygiene)) + }) + } + + fn collect_expr_as_pat_opt(&mut self, expr: Option<ast::Expr>) -> PatId { + match expr { + Some(expr) => self.collect_expr_as_pat(expr), + _ => self.missing_pat(), + } + } + + fn collect_expr_as_pat(&mut self, expr: ast::Expr) -> PatId { + self.maybe_collect_expr_as_pat(&expr).unwrap_or_else(|| { + let src = self.expander.in_file(AstPtr::new(&expr).wrap_left()); + let expr = self.collect_expr(expr); + // Do not use `alloc_pat_from_expr()` here, it will override the entry in `expr_map`. + let id = self.body.pats.alloc(Pat::Expr(expr)); + self.source_map.pat_map_back.insert(id, src); + id + }) + } + + fn maybe_collect_expr_as_pat(&mut self, expr: &ast::Expr) -> Option<PatId> { + self.check_cfg(expr)?; + let syntax_ptr = AstPtr::new(expr); + + let result = match expr { + ast::Expr::UnderscoreExpr(_) => self.alloc_pat_from_expr(Pat::Wild, syntax_ptr), + ast::Expr::ParenExpr(e) => { + // We special-case `(..)` for consistency with patterns. + if let Some(ast::Expr::RangeExpr(range)) = e.expr() { + if range.is_range_full() { + return Some(self.alloc_pat_from_expr( + Pat::Tuple { args: Box::default(), ellipsis: Some(0) }, + syntax_ptr, + )); + } + } + return e.expr().and_then(|expr| self.maybe_collect_expr_as_pat(&expr)); + } + ast::Expr::TupleExpr(e) => { + let (ellipsis, args) = collect_tuple(self, e.fields()); + self.alloc_pat_from_expr(Pat::Tuple { args, ellipsis }, syntax_ptr) + } + ast::Expr::ArrayExpr(e) => { + if e.semicolon_token().is_some() { + return None; + } + + let mut elements = e.exprs(); + let prefix = elements + .by_ref() + .map_while(|elem| collect_possibly_rest(self, elem).left()) + .collect(); + let suffix = elements.map(|elem| self.collect_expr_as_pat(elem)).collect(); + self.alloc_pat_from_expr(Pat::Slice { prefix, slice: None, suffix }, syntax_ptr) + } + ast::Expr::CallExpr(e) => { + let path = collect_path(self, e.expr()?)?; + let path = path.path().and_then(|path| self.parse_path(path)).map(Box::new); + let (ellipsis, args) = collect_tuple(self, e.arg_list()?.args()); + self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr) + } + ast::Expr::PathExpr(e) => { + let (path, hygiene) = self + .collect_expr_path(e.clone()) + .map(|(path, hygiene)| (Pat::Path(path), hygiene)) + .unwrap_or((Pat::Missing, HygieneId::ROOT)); + let pat_id = self.alloc_pat_from_expr(path, syntax_ptr); + if !hygiene.is_root() { + self.body.pat_hygiene.insert(pat_id, hygiene); + } + pat_id + } + ast::Expr::MacroExpr(e) => { + let e = e.macro_call()?; + let macro_ptr = AstPtr::new(&e); + let src = self.expander.in_file(AstPtr::new(expr)); + let id = self.collect_macro_call(e, macro_ptr, true, |this, expansion| { + this.collect_expr_as_pat_opt(expansion) + }); + self.source_map.expr_map.insert(src, id.into()); + id + } + ast::Expr::RecordExpr(e) => { + let path = e.path().and_then(|path| self.parse_path(path)).map(Box::new); + let record_field_list = e.record_expr_field_list()?; + let ellipsis = record_field_list.dotdot_token().is_some(); + // FIXME: Report an error here if `record_field_list.spread().is_some()`. + let args = record_field_list + .fields() + .filter_map(|f| { + self.check_cfg(&f)?; + let field_expr = f.expr()?; + let pat = self.collect_expr_as_pat(field_expr); + let name = f.field_name()?.as_name(); + let src = self.expander.in_file(AstPtr::new(&f).wrap_left()); + self.source_map.pat_field_map_back.insert(pat, src); + Some(RecordFieldPat { name, pat }) + }) + .collect(); + self.alloc_pat_from_expr(Pat::Record { path, args, ellipsis }, syntax_ptr) + } + _ => return None, + }; + return Some(result); + + fn collect_path(this: &mut ExprCollector<'_>, expr: ast::Expr) -> Option<ast::PathExpr> { + match expr { + ast::Expr::PathExpr(e) => Some(e), + ast::Expr::MacroExpr(mac) => { + let call = mac.macro_call()?; + { + let macro_ptr = AstPtr::new(&call); + this.collect_macro_call(call, macro_ptr, true, |this, expanded_path| { + collect_path(this, expanded_path?) + }) + } + } + _ => None, + } + } + + fn collect_possibly_rest( + this: &mut ExprCollector<'_>, + expr: ast::Expr, + ) -> Either<PatId, ()> { + match &expr { + ast::Expr::RangeExpr(e) if e.is_range_full() => Either::Right(()), + ast::Expr::MacroExpr(mac) => match mac.macro_call() { + Some(call) => { + let macro_ptr = AstPtr::new(&call); + let pat = this.collect_macro_call( + call, + macro_ptr, + true, + |this, expanded_expr| match expanded_expr { + Some(expanded_pat) => collect_possibly_rest(this, expanded_pat), + None => Either::Left(this.missing_pat()), + }, + ); + if let Either::Left(pat) = pat { + let src = this.expander.in_file(AstPtr::new(&expr).wrap_left()); + this.source_map.pat_map_back.insert(pat, src); + } + pat + } + None => { + let ptr = AstPtr::new(&expr); + Either::Left(this.alloc_pat_from_expr(Pat::Missing, ptr)) + } + }, + _ => Either::Left(this.collect_expr_as_pat(expr)), + } + } + + fn collect_tuple( + this: &mut ExprCollector<'_>, + fields: ast::AstChildren<ast::Expr>, + ) -> (Option<u32>, Box<[la_arena::Idx<Pat>]>) { + let mut ellipsis = None; + let args = fields + .enumerate() + .filter_map(|(idx, elem)| { + match collect_possibly_rest(this, elem) { + Either::Left(pat) => Some(pat), + Either::Right(()) => { + if ellipsis.is_none() { + ellipsis = Some(idx as u32); + } + // FIXME: Report an error here otherwise. + None + } + } + }) + .collect(); + (ellipsis, args) + } + } + fn initialize_binding_owner( &mut self, syntax_ptr: AstPtr<ast::Expr>, @@ -744,7 +946,7 @@ impl ExprCollector<'_> { let old_label = self.current_try_block_label.replace(label); let ptr = AstPtr::new(&e).upcast(); - let (btail, expr_id) = self.with_labeled_rib(label, |this| { + let (btail, expr_id) = self.with_labeled_rib(label, HygieneId::ROOT, |this| { let mut btail = None; let block = this.collect_block_(e, |id, statements, tail| { btail = tail; @@ -755,17 +957,13 @@ impl ExprCollector<'_> { let callee = self.alloc_expr_desugared_with_ptr(Expr::Path(try_from_output), ptr); let next_tail = match btail { - Some(tail) => self.alloc_expr_desugared_with_ptr( - Expr::Call { callee, args: Box::new([tail]), is_assignee_expr: false }, - ptr, - ), + Some(tail) => self + .alloc_expr_desugared_with_ptr(Expr::Call { callee, args: Box::new([tail]) }, ptr), None => { - let unit = self.alloc_expr_desugared_with_ptr( - Expr::Tuple { exprs: Box::new([]), is_assignee_expr: false }, - ptr, - ); + let unit = + self.alloc_expr_desugared_with_ptr(Expr::Tuple { exprs: Box::new([]) }, ptr); self.alloc_expr_desugared_with_ptr( - Expr::Call { callee, args: Box::new([unit]), is_assignee_expr: false }, + Expr::Call { callee, args: Box::new([unit]) }, ptr, ) } @@ -792,7 +990,9 @@ impl ExprCollector<'_> { /// FIXME: Rustc wraps the condition in a construct equivalent to `{ let _t = <cond>; _t }` /// to preserve drop semantics. We should probably do the same in future. fn collect_while_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::WhileExpr) -> ExprId { - let label = e.label().map(|label| self.collect_label(label)); + let label = e.label().map(|label| { + (self.hygiene_id_for(label.syntax().text_range().start()), self.collect_label(label)) + }); let body = self.collect_labelled_block_opt(label, e.loop_body()); // Labels can also be used in the condition expression, like this: @@ -809,9 +1009,9 @@ impl ExprCollector<'_> { // } // ``` let condition = match label { - Some(label) => { - self.with_labeled_rib(label, |this| this.collect_expr_opt(e.condition())) - } + Some((label_hygiene, label)) => self.with_labeled_rib(label, label_hygiene, |this| { + this.collect_expr_opt(e.condition()) + }), None => self.collect_expr_opt(e.condition()), }; @@ -820,7 +1020,7 @@ impl ExprCollector<'_> { Expr::If { condition, then_branch: body, else_branch: Some(break_expr) }, syntax_ptr, ); - self.alloc_expr(Expr::Loop { body: if_expr, label }, syntax_ptr) + self.alloc_expr(Expr::Loop { body: if_expr, label: label.map(|it| it.1) }, syntax_ptr) } /// Desugar `ast::ForExpr` from: `[opt_ident]: for <pat> in <head> <body>` into: @@ -851,15 +1051,11 @@ impl ExprCollector<'_> { let head = self.collect_expr_opt(e.iterable()); let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr); let iterator = self.alloc_expr( - Expr::Call { - callee: into_iter_fn_expr, - args: Box::new([head]), - is_assignee_expr: false, - }, + Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) }, syntax_ptr, ); let none_arm = MatchArm { - pat: self.alloc_pat_desugared(Pat::Path(Box::new(option_none))), + pat: self.alloc_pat_desugared(Pat::Path(option_none)), guard: None, expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr), }; @@ -868,7 +1064,9 @@ impl ExprCollector<'_> { args: Box::new([self.collect_pat_top(e.pat())]), ellipsis: None, }; - let label = e.label().map(|label| self.collect_label(label)); + let label = e.label().map(|label| { + (self.hygiene_id_for(label.syntax().text_range().start()), self.collect_label(label)) + }); let some_arm = MatchArm { pat: self.alloc_pat_desugared(some_pat), guard: None, @@ -884,11 +1082,7 @@ impl ExprCollector<'_> { ); let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr); let iter_next_expr = self.alloc_expr( - Expr::Call { - callee: iter_next_fn_expr, - args: Box::new([iter_expr_mut]), - is_assignee_expr: false, - }, + Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) }, syntax_ptr, ); let loop_inner = self.alloc_expr( @@ -904,7 +1098,8 @@ impl ExprCollector<'_> { }, syntax_ptr, ); - let loop_outer = self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr); + let loop_outer = self + .alloc_expr(Expr::Loop { body: loop_inner, label: label.map(|it| it.1) }, syntax_ptr); let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable); let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); self.add_definition_to_binding(iter_binding, iter_pat); @@ -942,10 +1137,8 @@ impl ExprCollector<'_> { }; let operand = self.collect_expr_opt(e.expr()); let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr); - let expr = self.alloc_expr( - Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false }, - syntax_ptr, - ); + let expr = self + .alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr); let continue_name = Name::generate_new_name(self.body.bindings.len()); let continue_binding = self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated); @@ -975,10 +1168,8 @@ impl ExprCollector<'_> { expr: { let it = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr); let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr); - let result = self.alloc_expr( - Expr::Call { callee, args: Box::new([it]), is_assignee_expr: false }, - syntax_ptr, - ); + let result = + self.alloc_expr(Expr::Call { callee, args: Box::new([it]) }, syntax_ptr); self.alloc_expr( match self.current_try_block_label { Some(label) => Expr::Break { expr: Some(result), label: Some(label) }, @@ -1065,7 +1256,14 @@ impl ExprCollector<'_> { // FIXME: Report parse errors here } + let SpanMap::ExpansionSpanMap(new_span_map) = self.expander.span_map(self.db) + else { + panic!("just expanded a macro, ExpansionSpanMap should be available"); + }; + let old_span_map = + mem::replace(&mut self.current_span_map, Some(new_span_map.clone())); let id = collector(self, Some(expansion.tree())); + self.current_span_map = old_span_map; self.ast_id_map = prev_ast_id_map; self.expander.exit(mark); id @@ -1108,7 +1306,7 @@ impl ExprCollector<'_> { // Make the macro-call point to its expanded expression so we can query // semantics on syntax pointers to the macro let src = self.expander.in_file(syntax_ptr); - self.source_map.expr_map.insert(src, tail); + self.source_map.expr_map.insert(src, tail.into()); }) } @@ -1119,8 +1317,7 @@ impl ExprCollector<'_> { return; } let pat = self.collect_pat_top(stmt.pat()); - let type_ref = - stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it))); + let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); let initializer = stmt.initializer().map(|e| self.collect_expr(e)); let else_branch = stmt .let_else() @@ -1145,10 +1342,46 @@ impl ExprCollector<'_> { statements.push(Statement::Expr { expr, has_semi }); } } - ast::Stmt::Item(_item) => statements.push(Statement::Item), + ast::Stmt::Item(ast::Item::MacroDef(macro_)) => { + let Some(name) = macro_.name() else { + statements.push(Statement::Item(Item::Other)); + return; + }; + let name = name.as_name(); + let macro_id = self.def_map.modules[DefMap::ROOT].scope.get(&name).take_macros(); + self.collect_macro_def(statements, macro_id); + } + ast::Stmt::Item(ast::Item::MacroRules(macro_)) => { + let Some(name) = macro_.name() else { + statements.push(Statement::Item(Item::Other)); + return; + }; + let name = name.as_name(); + let macro_defs_count = + self.current_block_legacy_macro_defs_count.entry(name.clone()).or_insert(0); + let macro_id = self.def_map.modules[DefMap::ROOT] + .scope + .get_legacy_macro(&name) + .and_then(|it| it.get(*macro_defs_count)) + .copied(); + *macro_defs_count += 1; + self.collect_macro_def(statements, macro_id); + } + ast::Stmt::Item(_item) => statements.push(Statement::Item(Item::Other)), } } + fn collect_macro_def(&mut self, statements: &mut Vec<Statement>, macro_id: Option<MacroId>) { + let Some(macro_id) = macro_id else { + never!("def map should have macro definition, but it doesn't"); + statements.push(Statement::Item(Item::Other)); + return; + }; + let macro_id = self.db.macro_def(macro_id); + statements.push(Statement::Item(Item::MacroDef(Box::new(macro_id)))); + self.label_ribs.push(LabelRib::new(RibKind::MacroDef(Box::new(macro_id)))); + } + fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId { self.collect_block_(block, |id, statements, tail| Expr::Block { id, @@ -1194,6 +1427,7 @@ impl ExprCollector<'_> { }; let prev_def_map = mem::replace(&mut self.def_map, def_map); let prev_local_module = mem::replace(&mut self.expander.module, module); + let prev_legacy_macros_count = mem::take(&mut self.current_block_legacy_macro_defs_count); let mut statements = Vec::new(); block.statements().for_each(|s| self.collect_stmt(&mut statements, s)); @@ -1216,6 +1450,7 @@ impl ExprCollector<'_> { self.def_map = prev_def_map; self.expander.module = prev_local_module; + self.current_block_legacy_macro_defs_count = prev_legacy_macros_count; expr_id } @@ -1228,11 +1463,13 @@ impl ExprCollector<'_> { fn collect_labelled_block_opt( &mut self, - label: Option<LabelId>, + label: Option<(HygieneId, LabelId)>, expr: Option<ast::BlockExpr>, ) -> ExprId { match label { - Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)), + Some((hygiene, label)) => { + self.with_labeled_rib(label, hygiene, |this| this.collect_block_opt(expr)) + } None => self.collect_block_opt(expr), } } @@ -1250,6 +1487,10 @@ impl ExprCollector<'_> { let pattern = match &pat { ast::Pat::IdentPat(bp) => { let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); + let hygiene = bp + .name() + .map(|name| self.hygiene_id_for(name.syntax().text_range().start())) + .unwrap_or(HygieneId::ROOT); let annotation = BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some()); @@ -1285,12 +1526,12 @@ impl ExprCollector<'_> { } // shadowing statics is an error as well, so we just ignore that case here _ => { - let id = binding_list.find(self, name, annotation); + let id = binding_list.find(self, name, hygiene, annotation); (Some(id), Pat::Bind { id, subpat }) } } } else { - let id = binding_list.find(self, name, annotation); + let id = binding_list.find(self, name, hygiene, annotation); (Some(id), Pat::Bind { id, subpat }) }; @@ -1302,8 +1543,7 @@ impl ExprCollector<'_> { return pat; } ast::Pat::TupleStructPat(p) => { - let path = - p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); + let path = p.path().and_then(|path| self.parse_path(path)).map(Box::new); let (args, ellipsis) = self.collect_tuple_pat( p.fields(), comma_follows_token(p.l_paren_token()), @@ -1317,8 +1557,7 @@ impl ExprCollector<'_> { Pat::Ref { pat, mutability } } ast::Pat::PathPat(p) => { - let path = - p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); + let path = p.path().and_then(|path| self.parse_path(path)); path.map(Pat::Path).unwrap_or(Pat::Missing) } ast::Pat::OrPat(p) => 'b: { @@ -1348,6 +1587,10 @@ impl ExprCollector<'_> { for (id, _) in current_is_used.into_iter() { binding_list.check_is_used(self, id); } + if let &[pat] = &*pats { + // Leading pipe without real OR pattern. Leaving an one-item OR pattern may confuse later stages. + return pat; + } Pat::Or(pats.into()) } ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list), @@ -1361,8 +1604,7 @@ impl ExprCollector<'_> { } ast::Pat::WildcardPat(_) => Pat::Wild, ast::Pat::RecordPat(p) => { - let path = - p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); + let path = p.path().and_then(|path| self.parse_path(path)).map(Box::new); let record_pat_field_list = &p.record_pat_field_list().expect("every struct should have a field list"); let args = record_pat_field_list @@ -1372,7 +1614,7 @@ impl ExprCollector<'_> { let ast_pat = f.pat()?; let pat = self.collect_pat(ast_pat, binding_list); let name = f.field_name()?.as_name(); - let src = self.expander.in_file(AstPtr::new(&f)); + let src = self.expander.in_file(AstPtr::new(&f).wrap_right()); self.source_map.pat_field_map_back.insert(pat, src); Some(RecordFieldPat { name, pat }) }) @@ -1569,20 +1811,51 @@ impl ExprCollector<'_> { lifetime: Option<ast::Lifetime>, ) -> Result<Option<LabelId>, BodyDiagnostic> { let Some(lifetime) = lifetime else { return Ok(None) }; + let (mut hygiene_id, mut hygiene_info) = match &self.current_span_map { + None => (HygieneId::ROOT, None), + Some(span_map) => { + let span = span_map.span_at(lifetime.syntax().text_range().start()); + let ctx = self.db.lookup_intern_syntax_context(span.ctx); + let hygiene_id = HygieneId::new(ctx.opaque_and_semitransparent); + let hygiene_info = ctx.outer_expn.map(|expansion| { + let expansion = self.db.lookup_intern_macro_call(expansion); + (ctx.parent, expansion.def) + }); + (hygiene_id, hygiene_info) + } + }; let name = Name::new_lifetime(&lifetime); for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() { - if let Some((label_name, id)) = &rib.label { - if *label_name == name { - return if self.is_label_valid_from_rib(rib_idx) { - Ok(Some(*id)) - } else { - Err(BodyDiagnostic::UnreachableLabel { - name, - node: self.expander.in_file(AstPtr::new(&lifetime)), - }) - }; + match &rib.kind { + RibKind::Normal(label_name, id, label_hygiene) => { + if *label_name == name && *label_hygiene == hygiene_id { + return if self.is_label_valid_from_rib(rib_idx) { + Ok(Some(*id)) + } else { + Err(BodyDiagnostic::UnreachableLabel { + name, + node: self.expander.in_file(AstPtr::new(&lifetime)), + }) + }; + } } + RibKind::MacroDef(macro_id) => { + if let Some((parent_ctx, label_macro_id)) = hygiene_info { + if label_macro_id == **macro_id { + // A macro is allowed to refer to labels from before its declaration. + // Therefore, if we got to the rib of its declaration, give up its hygiene + // and use its parent expansion. + let parent_ctx = self.db.lookup_intern_syntax_context(parent_ctx); + hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent); + hygiene_info = parent_ctx.outer_expn.map(|expansion| { + let expansion = self.db.lookup_intern_macro_call(expansion); + (parent_ctx.parent, expansion.def) + }); + } + } + } + _ => {} } } @@ -1596,28 +1869,44 @@ impl ExprCollector<'_> { !self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier()) } + fn pop_label_rib(&mut self) { + // We need to pop all macro defs, plus one rib. + while let Some(LabelRib { kind: RibKind::MacroDef(_) }) = self.label_ribs.pop() { + // Do nothing. + } + } + fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T { self.label_ribs.push(LabelRib::new(kind)); let res = f(self); - self.label_ribs.pop(); + self.pop_label_rib(); res } - fn with_labeled_rib<T>(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T { - self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label))); + fn with_labeled_rib<T>( + &mut self, + label: LabelId, + hygiene: HygieneId, + f: impl FnOnce(&mut Self) -> T, + ) -> T { + self.label_ribs.push(LabelRib::new(RibKind::Normal( + self.body[label].name.clone(), + label, + hygiene, + ))); let res = f(self); - self.label_ribs.pop(); + self.pop_label_rib(); res } fn with_opt_labeled_rib<T>( &mut self, - label: Option<LabelId>, + label: Option<(HygieneId, LabelId)>, f: impl FnOnce(&mut Self) -> T, ) -> T { match label { None => f(self), - Some(label) => self.with_labeled_rib(label, f), + Some((hygiene, label)) => self.with_labeled_rib(label, hygiene, f), } } // endregion: labels @@ -1666,28 +1955,39 @@ impl ExprCollector<'_> { _ => None, }); let mut mappings = vec![]; - let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) { + let (fmt, hygiene) = match template.and_then(|it| self.expand_macros_to_string(it)) { Some((s, is_direct_literal)) => { let call_ctx = self.expander.syntax_context(); - format_args::parse( + let hygiene = self.hygiene_id_for(s.syntax().text_range().start()); + let fmt = format_args::parse( &s, fmt_snippet, args, is_direct_literal, - |name| self.alloc_expr_desugared(Expr::Path(Path::from(name))), + |name| { + let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name))); + if !hygiene.is_root() { + self.body.expr_hygiene.insert(expr_id, hygiene); + } + expr_id + }, |name, span| { if let Some(span) = span { mappings.push((span, name)) } }, call_ctx, - ) + ); + (fmt, hygiene) } - None => FormatArgs { - template: Default::default(), - arguments: args.finish(), - orphans: Default::default(), - }, + None => ( + FormatArgs { + template: Default::default(), + arguments: args.finish(), + orphans: Default::default(), + }, + HygieneId::ROOT, + ), }; // Create a list of all _unique_ (argument, format trait) combinations. @@ -1723,10 +2023,8 @@ impl ExprCollector<'_> { } }) .collect(); - let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList { - elements: lit_pieces, - is_assignee_expr: false, - })); + let lit_pieces = + self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: lit_pieces })); let lit_pieces = self.alloc_expr_desugared(Expr::Ref { expr: lit_pieces, rawness: Rawness::Ref, @@ -1743,10 +2041,7 @@ impl ExprCollector<'_> { Some(self.make_format_spec(placeholder, &mut argmap)) }) .collect(); - let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { - elements, - is_assignee_expr: false, - })); + let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements })); self.alloc_expr_desugared(Expr::Ref { expr: array, rawness: Rawness::Ref, @@ -1756,10 +2051,8 @@ impl ExprCollector<'_> { let arguments = &*fmt.arguments.arguments; let args = if arguments.is_empty() { - let expr = self.alloc_expr_desugared(Expr::Array(Array::ElementList { - elements: Box::default(), - is_assignee_expr: false, - })); + let expr = self + .alloc_expr_desugared(Expr::Array(Array::ElementList { elements: Box::default() })); self.alloc_expr_desugared(Expr::Ref { expr, rawness: Rawness::Ref, @@ -1786,10 +2079,8 @@ impl ExprCollector<'_> { self.make_argument(arg, ty) }) .collect(); - let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { - elements: args, - is_assignee_expr: false, - })); + let array = + self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args })); self.alloc_expr_desugared(Expr::Ref { expr: array, rawness: Rawness::Ref, @@ -1822,11 +2113,8 @@ impl ExprCollector<'_> { let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted)); let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new)); - let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call { - callee: unsafe_arg_new, - args: Box::default(), - is_assignee_expr: false, - }); + let unsafe_arg_new = + self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() }); let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe { id: None, // We collect the unused expressions here so that we still infer them instead of @@ -1843,11 +2131,14 @@ impl ExprCollector<'_> { Expr::Call { callee: new_v1_formatted, args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]), - is_assignee_expr: false, }, syntax_ptr, ); - self.source_map.template_map.get_or_insert_with(Default::default).0.insert(idx, mappings); + self.source_map + .template_map + .get_or_insert_with(Default::default) + .0 + .insert(idx, (hygiene, mappings)); idx } @@ -1938,7 +2229,6 @@ impl ExprCollector<'_> { self.alloc_expr_desugared(Expr::Call { callee: format_placeholder_new, args: Box::new([position, fill, align, flags, precision, width]), - is_assignee_expr: false, }) } @@ -1980,11 +2270,7 @@ impl ExprCollector<'_> { Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)), None => self.missing_expr(), }; - self.alloc_expr_desugared(Expr::Call { - callee: count_is, - args: Box::new([args]), - is_assignee_expr: false, - }) + self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]) }) } Some(FormatCount::Argument(arg)) => { if let Ok(arg_index) = arg.index { @@ -2005,7 +2291,6 @@ impl ExprCollector<'_> { self.alloc_expr_desugared(Expr::Call { callee: count_param, args: Box::new([args]), - is_assignee_expr: false, }) } else { // FIXME: This drops arg causing it to potentially not be resolved/type checked @@ -2054,11 +2339,7 @@ impl ExprCollector<'_> { Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)), None => self.missing_expr(), }; - self.alloc_expr_desugared(Expr::Call { - callee: new_fn, - args: Box::new([arg]), - is_assignee_expr: false, - }) + self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) }) } // endregion: format @@ -2082,7 +2363,7 @@ impl ExprCollector<'_> { let src = self.expander.in_file(ptr); let id = self.body.exprs.alloc(expr); self.source_map.expr_map_back.insert(id, src); - self.source_map.expr_map.insert(src, id); + self.source_map.expr_map.insert(src, id.into()); id } // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed. @@ -2110,10 +2391,17 @@ impl ExprCollector<'_> { binding } + fn alloc_pat_from_expr(&mut self, pat: Pat, ptr: ExprPtr) -> PatId { + let src = self.expander.in_file(ptr); + let id = self.body.pats.alloc(pat); + self.source_map.expr_map.insert(src, id.into()); + self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left)); + id + } fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId { let src = self.expander.in_file(ptr); let id = self.body.pats.alloc(pat); - self.source_map.pat_map_back.insert(id, src); + self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right)); self.source_map.pat_map.insert(src, id); id } @@ -2151,6 +2439,17 @@ impl ExprCollector<'_> { self.awaitable_context = orig; res } + + /// If this returns `HygieneId::ROOT`, do not allocate to save space. + fn hygiene_id_for(&self, span_start: TextSize) -> HygieneId { + match &self.current_span_map { + None => HygieneId::ROOT, + Some(span_map) => { + let ctx = span_map.span_at(span_start).ctx; + HygieneId(self.db.lookup_intern_syntax_context(ctx).opaque_and_semitransparent) + } + } + } } fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool { |