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!(); + } + } +} "#, ); }