From df94afcb08ea6fc4e162b5b9fd6814351ea5fc8a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 8 Oct 2024 03:52:49 +0300 Subject: [PATCH] Correctly resolve variables and labels from before macro definition in macro expansion E.g.: ```rust let v; macro_rules! m { () => { v }; } ``` This was an existing bug, but it was less severe because unless the variable was shadowed it would be correctly resolved. With hygiene however, without this fix the variable is never resolved. --- crates/hir-def/src/body.rs | 4 +- crates/hir-def/src/body/lower.rs | 134 ++++++++++++++---- crates/hir-def/src/body/pretty.rs | 2 +- crates/hir-def/src/body/scope.rs | 30 +++- crates/hir-def/src/db.rs | 3 + crates/hir-def/src/hir.rs | 12 +- crates/hir-def/src/lib.rs | 40 ++++++ crates/hir-def/src/resolver.rs | 44 +++++- crates/hir-expand/src/lib.rs | 26 +++- crates/hir-ty/src/infer/closure.rs | 2 +- crates/hir-ty/src/infer/expr.rs | 2 +- crates/hir-ty/src/infer/mutability.rs | 2 +- crates/hir-ty/src/mir/lower.rs | 2 +- crates/hir-ty/src/tests/simple.rs | 17 +++ .../src/handlers/undeclared_label.rs | 30 ++++ 15 files changed, 300 insertions(+), 50 deletions(-) diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 002b3910409b..1909bfd119fd 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -33,7 +33,7 @@ use crate::{ /// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct HygieneId(span::SyntaxContextId); +pub struct HygieneId(pub(crate) span::SyntaxContextId); impl HygieneId { pub const ROOT: Self = Self(span::SyntaxContextId::ROOT); @@ -42,7 +42,7 @@ impl HygieneId { Self(ctx) } - fn is_root(self) -> bool { + pub(crate) fn is_root(self) -> bool { self.0.is_root() } } diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 667babb0d947..94891224dbce 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -39,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, @@ -48,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 = indexmap::IndexSet>; @@ -85,6 +85,7 @@ pub(super) fn lower( 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) } @@ -102,6 +103,10 @@ struct ExprCollector<'a> { 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, + current_span_map: Option>, current_try_block_label: Option, @@ -122,31 +127,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, HygieneId)>, } impl LabelRib { fn new(kind: RibKind) -> Self { - LabelRib { kind, label: None } - } - fn new_normal(label: (Name, LabelId, HygieneId)) -> 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(hir_expand::MacroId), } 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, } } @@ -1205,10 +1206,44 @@ 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() + .expect("def map should have macro definition, but it doesn't"); + 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)) + .expect("def map should have macro definition, but it doesn't"); + *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, macro_id: MacroId) { + statements.push(Statement::Item(Item::MacroDef(macro_id.into()))); + self.label_ribs.push(LabelRib::new(RibKind::MacroDef(macro_id.into()))); + } + fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId { self.collect_block_(block, |id, statements, tail| Expr::Block { id, @@ -1254,6 +1289,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)); @@ -1276,6 +1312,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 } @@ -1635,21 +1672,51 @@ impl ExprCollector<'_> { lifetime: Option, ) -> Result, BodyDiagnostic> { let Some(lifetime) = lifetime else { return Ok(None) }; - let hygiene = self.hygiene_id_for(lifetime.syntax().text_range().start()); + 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| self.db.lookup_intern_macro_call(expansion)) + .map(|expansion| (ctx.parent, expansion.def.id)); + (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, label_hygiene)) = &rib.label { - if *label_name == name && *label_hygiene == hygiene { - 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| self.db.lookup_intern_macro_call(expansion)) + .map(|expansion| (parent_ctx.parent, expansion.def.id)); + } + } + } + _ => {} } } @@ -1663,10 +1730,17 @@ 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(&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 } @@ -1676,9 +1750,13 @@ impl ExprCollector<'_> { hygiene: HygieneId, f: impl FnOnce(&mut Self) -> T, ) -> T { - self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label, hygiene))); + 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 } diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 37167fcb8155..c8271f9da9bf 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -748,7 +748,7 @@ impl Printer<'_> { } wln!(self); } - Statement::Item => (), + Statement::Item(_) => (), } } diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs index 4719850165a6..c5a07b6be85c 100644 --- a/crates/hir-def/src/body/scope.rs +++ b/crates/hir-def/src/body/scope.rs @@ -1,12 +1,12 @@ //! Name resolution for expressions. -use hir_expand::name::Name; +use hir_expand::{name::Name, MacroId}; use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx}; use triomphe::Arc; use crate::{ body::{Body, HygieneId}, db::DefDatabase, - hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement}, + hir::{Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement}, BlockId, ConstBlockId, DefWithBodyId, }; @@ -45,6 +45,8 @@ pub struct ScopeData { parent: Option, block: Option, label: Option<(LabelId, Name)>, + // FIXME: We can compress this with an enum for this and `label`/`block` if memory usage matters. + macro_def: Option, entries: IdxRange, } @@ -67,6 +69,11 @@ impl ExprScopes { self.scopes[scope].block } + /// If `scope` refers to a macro def scope, returns the corresponding `MacroId`. + pub fn macro_def(&self, scope: ScopeId) -> Option { + self.scopes[scope].macro_def + } + /// If `scope` refers to a labeled expression scope, returns the corresponding `Label`. pub fn label(&self, scope: ScopeId) -> Option<(LabelId, Name)> { self.scopes[scope].label.clone() @@ -119,6 +126,7 @@ impl ExprScopes { parent: None, block: None, label: None, + macro_def: None, entries: empty_entries(self.scope_entries.len()), }) } @@ -128,6 +136,7 @@ impl ExprScopes { parent: Some(parent), block: None, label: None, + macro_def: None, entries: empty_entries(self.scope_entries.len()), }) } @@ -137,6 +146,7 @@ impl ExprScopes { parent: Some(parent), block: None, label, + macro_def: None, entries: empty_entries(self.scope_entries.len()), }) } @@ -151,6 +161,17 @@ impl ExprScopes { parent: Some(parent), block, label, + macro_def: None, + entries: empty_entries(self.scope_entries.len()), + }) + } + + fn new_macro_def_scope(&mut self, parent: ScopeId, macro_id: MacroId) -> ScopeId { + self.scopes.alloc(ScopeData { + parent: Some(parent), + block: None, + label: None, + macro_def: Some(macro_id), entries: empty_entries(self.scope_entries.len()), }) } @@ -217,7 +238,10 @@ fn compute_block_scopes( Statement::Expr { expr, .. } => { compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); } - Statement::Item => (), + Statement::Item(Item::MacroDef(macro_id)) => { + *scope = scopes.new_macro_def_scope(*scope, *macro_id); + } + Statement::Item(Item::Other) => (), } } if let Some(expr) = tail { diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index aeda302f35c5..609e6054cc60 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -316,6 +316,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { let item_tree = loc.id.item_tree(db); let makro = &item_tree[loc.id.value]; MacroDefId { + id: id.into(), krate: loc.container.krate, kind: kind(loc.expander, loc.id.file_id(), makro.ast_id.upcast()), local_inner: false, @@ -329,6 +330,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { let item_tree = loc.id.item_tree(db); let makro = &item_tree[loc.id.value]; MacroDefId { + id: id.into(), krate: loc.container.krate, kind: kind(loc.expander, loc.id.file_id(), makro.ast_id.upcast()), local_inner: loc.flags.contains(MacroRulesLocFlags::LOCAL_INNER), @@ -344,6 +346,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { let item_tree = loc.id.item_tree(db); let makro = &item_tree[loc.id.value]; MacroDefId { + id: id.into(), krate: loc.container.krate, kind: MacroDefKind::ProcMacro( InFile::new(loc.id.file_id(), makro.ast_id), diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index d9358a28822e..64933187bae0 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -475,9 +475,13 @@ pub enum Statement { expr: ExprId, has_semi: bool, }, - // At the moment, we only use this to figure out if a return expression - // is really the last statement of a block. See #16566 - Item, + Item(Item), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Item { + MacroDef(hir_expand::MacroId), + Other, } impl Expr { @@ -525,7 +529,7 @@ impl Expr { } } Statement::Expr { expr: expression, .. } => f(*expression), - Statement::Item => (), + Statement::Item(_) => (), } } if let &Some(expr) = tail { diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 157c9ef08057..2e944dd5b5a8 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -205,6 +205,23 @@ macro_rules! impl_loc { }; } +macro_rules! impl_from_field { + ($a:ty, $b:ty) => { + impl From<$a> for $b { + #[inline] + fn from(v: $a) -> Self { + Self(v.0) + } + } + impl From<$b> for $a { + #[inline] + fn from(v: $b) -> Self { + Self(v.0) + } + } + }; +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct FunctionId(ra_salsa::InternId); type FunctionLoc = AssocItemLoc; @@ -307,6 +324,7 @@ pub struct Macro2Loc { } impl_intern!(Macro2Id, Macro2Loc, intern_macro2, lookup_intern_macro2); impl_loc!(Macro2Loc, id: Macro2, container: ModuleId); +impl_from_field!(Macro2Id, hir_expand::Macro2Id); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct MacroRulesId(ra_salsa::InternId); @@ -320,6 +338,7 @@ pub struct MacroRulesLoc { } impl_intern!(MacroRulesId, MacroRulesLoc, intern_macro_rules, lookup_intern_macro_rules); impl_loc!(MacroRulesLoc, id: MacroRules, container: ModuleId); +impl_from_field!(MacroRulesId, hir_expand::MacroRulesId); bitflags::bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -349,6 +368,7 @@ pub struct ProcMacroLoc { } impl_intern!(ProcMacroId, ProcMacroLoc, intern_proc_macro, lookup_intern_proc_macro); impl_loc!(ProcMacroLoc, id: Function, container: CrateRootModuleId); +impl_from_field!(ProcMacroId, hir_expand::ProcMacroId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct BlockId(ra_salsa::InternId); @@ -623,6 +643,26 @@ pub enum MacroId { ProcMacroId(ProcMacroId), } impl_from!(Macro2Id, MacroRulesId, ProcMacroId for MacroId); +impl From for hir_expand::MacroId { + #[inline] + fn from(value: MacroId) -> Self { + match value { + MacroId::Macro2Id(id) => hir_expand::MacroId::Macro2Id(id.into()), + MacroId::MacroRulesId(id) => hir_expand::MacroId::MacroRulesId(id.into()), + MacroId::ProcMacroId(id) => hir_expand::MacroId::ProcMacroId(id.into()), + } + } +} +impl From for MacroId { + #[inline] + fn from(value: hir_expand::MacroId) -> Self { + match value { + hir_expand::MacroId::Macro2Id(id) => MacroId::Macro2Id(id.into()), + hir_expand::MacroId::MacroRulesId(id) => MacroId::MacroRulesId(id.into()), + hir_expand::MacroId::ProcMacroId(id) => MacroId::ProcMacroId(id.into()), + } + } +} impl MacroId { pub fn is_attribute(self, db: &dyn DefDatabase) -> bool { diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index cf93958ecbb2..0891a60a4139 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -83,6 +83,8 @@ enum Scope { AdtScope(AdtId), /// Local bindings ExprScope(ExprScope), + /// Macro definition inside bodies that affects all paths after it in the same block. + MacroDefScope(hir_expand::MacroId), } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -191,7 +193,7 @@ impl Resolver { for scope in self.scopes() { match scope { - Scope::ExprScope(_) => continue, + Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue, Scope::GenericParams { params, def } => { if let Some(id) = params.find_type_by_name(first_name, *def) { return Some((TypeNs::GenericParam(id), remaining_idx(), None)); @@ -260,7 +262,7 @@ impl Resolver { &self, db: &dyn DefDatabase, path: &Path, - hygiene: HygieneId, + mut hygiene_id: HygieneId, ) -> Option { let path = match path { Path::Normal { mod_path, .. } => mod_path, @@ -304,12 +306,20 @@ impl Resolver { } if n_segments <= 1 { + let mut hygiene_info = if !hygiene_id.is_root() { + let ctx = db.lookup_intern_syntax_context(hygiene_id.0); + ctx.outer_expn + .map(|expansion| db.lookup_intern_macro_call(expansion)) + .map(|expansion| (ctx.parent, expansion.def.id)) + } else { + None + }; for scope in self.scopes() { match scope { Scope::ExprScope(scope) => { let entry = scope.expr_scopes.entries(scope.scope_id).iter().find(|entry| { - entry.name() == first_name && entry.hygiene() == hygiene + entry.name() == first_name && entry.hygiene() == hygiene_id }); if let Some(e) = entry { @@ -319,6 +329,21 @@ impl Resolver { )); } } + &Scope::MacroDefScope(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 variables 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 = db.lookup_intern_syntax_context(parent_ctx); + hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent); + hygiene_info = parent_ctx + .outer_expn + .map(|expansion| db.lookup_intern_macro_call(expansion)) + .map(|expansion| (parent_ctx.parent, expansion.def.id)); + } + } + } Scope::GenericParams { params, def } => { if let Some(id) = params.find_const_by_name(first_name, *def) { let val = ValueNs::GenericParam(id); @@ -345,7 +370,7 @@ impl Resolver { } else { for scope in self.scopes() { match scope { - Scope::ExprScope(_) => continue, + Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue, Scope::GenericParams { params, def } => { if let Some(id) = params.find_type_by_name(first_name, *def) { let ty = TypeNs::GenericParam(id); @@ -626,7 +651,7 @@ impl Resolver { pub fn type_owner(&self) -> Option { self.scopes().find_map(|scope| match scope { - Scope::BlockScope(_) => None, + Scope::BlockScope(_) | Scope::MacroDefScope(_) => None, &Scope::GenericParams { def, .. } => Some(def.into()), &Scope::ImplDefScope(id) => Some(id.into()), &Scope::AdtScope(adt) => Some(adt.into()), @@ -657,6 +682,9 @@ impl Resolver { expr_scopes: &Arc, scope_id: ScopeId, ) { + if let Some(macro_id) = expr_scopes.macro_def(scope_id) { + resolver.scopes.push(Scope::MacroDefScope(macro_id)); + } resolver.scopes.push(Scope::ExprScope(ExprScope { owner, expr_scopes: expr_scopes.clone(), @@ -674,7 +702,7 @@ impl Resolver { } let start = self.scopes.len(); - let innermost_scope = self.scopes().next(); + let innermost_scope = self.scopes().find(|scope| !matches!(scope, Scope::MacroDefScope(_))); match innermost_scope { Some(&Scope::ExprScope(ExprScope { scope_id, ref expr_scopes, owner })) => { let expr_scopes = expr_scopes.clone(); @@ -798,6 +826,7 @@ impl Scope { acc.add_local(e.name(), e.binding()); }); } + Scope::MacroDefScope(_) => {} } } } @@ -837,6 +866,9 @@ fn resolver_for_scope_( // already traverses all parents, so this is O(n²). I think we could only store the // innermost module scope instead? } + if let Some(macro_id) = scopes.macro_def(scope) { + r = r.push_scope(Scope::MacroDefScope(macro_id)); + } r = r.push_expr_scope(owner, Arc::clone(&scopes), scope); } diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 5d5f72490d0c..96299d2b677a 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -25,12 +25,15 @@ mod prettify_macro_expansion_; use attrs::collect_attrs; use rustc_hash::FxHashMap; -use stdx::TupleExt; +use stdx::{impl_from, TupleExt}; use triomphe::Arc; use std::hash::Hash; -use base_db::{ra_salsa::InternValueTrivial, CrateId}; +use base_db::{ + ra_salsa::{self, InternValueTrivial}, + CrateId, +}; use either::Either; use span::{ Edition, EditionedFileId, ErasedFileAstId, FileAstId, HirFileIdRepr, Span, SpanAnchor, @@ -217,8 +220,27 @@ pub struct MacroCallLoc { } impl InternValueTrivial for MacroCallLoc {} +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct Macro2Id(pub ra_salsa::InternId); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct MacroRulesId(pub ra_salsa::InternId); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct ProcMacroId(pub ra_salsa::InternId); + +/// A macro +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum MacroId { + Macro2Id(Macro2Id), + MacroRulesId(MacroRulesId), + ProcMacroId(ProcMacroId), +} +impl_from!(Macro2Id, MacroRulesId, ProcMacroId for MacroId); + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct MacroDefId { + pub id: MacroId, pub krate: CrateId, pub edition: Edition, pub kind: MacroDefKind, diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 0d44ee48fd88..0c2b916471a1 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -714,7 +714,7 @@ impl InferenceContext<'_> { Statement::Expr { expr, has_semi: _ } => { self.consume_expr(*expr); } - Statement::Item => (), + Statement::Item(_) => (), } } if let Some(tail) = tail { diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index a04e7b17ae6e..72a831dccda3 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -1526,7 +1526,7 @@ impl InferenceContext<'_> { ); } } - Statement::Item => (), + Statement::Item(_) => (), } } diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 8dcaa9c581ea..b4d06a41cde8 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -89,7 +89,7 @@ impl InferenceContext<'_> { Statement::Expr { expr, has_semi: _ } => { self.infer_mut_expr(*expr, Mutability::Not); } - Statement::Item => (), + Statement::Item(_) => (), } } if let Some(tail) = tail { diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index e144d7beab94..3c28aa954f86 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -1832,7 +1832,7 @@ impl<'ctx> MirLowerCtx<'ctx> { self.push_fake_read(c, p, expr.into()); current = scope2.pop_and_drop(self, c, expr.into()); } - hir_def::hir::Statement::Item => (), + hir_def::hir::Statement::Item(_) => (), } } if let Some(tail) = tail { diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index 4c75b5c38a3f..22952bc1acae 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -3737,3 +3737,20 @@ fn foo() { "#, ); } + +#[test] +fn macro_expansion_can_refer_variables_defined_before_macro_definition() { + check_types( + r#" +fn foo() { + let v: i32 = 0; + macro_rules! m { + () => { v }; + } + let v: bool = true; + m!(); + // ^^^^ i32 +} + "#, + ); +} diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs index 6af36fb9e739..d16bfb800240 100644 --- a/crates/ide-diagnostics/src/handlers/undeclared_label.rs +++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs @@ -104,6 +104,36 @@ async fn foo() { async fn foo() { || None?; } +"#, + ); + } + + #[test] + fn macro_expansion_can_refer_label_defined_before_macro_definition() { + check_diagnostics( + r#" +fn foo() { + 'bar: loop { + macro_rules! m { + () => { break 'bar }; + } + m!(); + } +} +"#, + ); + check_diagnostics( + r#" +fn foo() { + 'bar: loop { + macro_rules! m { + () => { break 'bar }; + } + 'bar: loop { + m!(); + } + } +} "#, ); }