Skip to content

Commit 345d698

Browse files
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.
1 parent 8adcbdc commit 345d698

File tree

12 files changed

+286
-50
lines changed

12 files changed

+286
-50
lines changed

crates/hir-def/src/body.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use crate::{
3535

3636
/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
3737
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38-
pub struct HygieneId(span::SyntaxContextId);
38+
pub struct HygieneId(pub(crate) span::SyntaxContextId);
3939

4040
impl HygieneId {
4141
pub const ROOT: Self = Self(span::SyntaxContextId::ROOT);
@@ -44,7 +44,7 @@ impl HygieneId {
4444
Self(ctx)
4545
}
4646

47-
fn is_root(self) -> bool {
47+
pub(crate) fn is_root(self) -> bool {
4848
self.0.is_root()
4949
}
5050
}
@@ -420,7 +420,7 @@ impl Body {
420420
self.walk_exprs_in_pat(*pat, &mut f);
421421
}
422422
Statement::Expr { expr: expression, .. } => f(*expression),
423-
Statement::Item => (),
423+
Statement::Item(_) => (),
424424
}
425425
}
426426
if let &Some(expr) = tail {

crates/hir-def/src/body/lower.rs

Lines changed: 109 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use either::Either;
1010
use hir_expand::{
1111
name::{AsName, Name},
1212
span_map::{ExpansionSpanMap, SpanMap},
13-
InFile,
13+
InFile, MacroDefId,
1414
};
1515
use intern::{sym, Interned, Symbol};
1616
use rustc_hash::FxHashMap;
@@ -39,16 +39,16 @@ use crate::{
3939
FormatPlaceholder, FormatSign, FormatTrait,
4040
},
4141
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
42-
Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat,
43-
PatId, RecordFieldPat, RecordLitField, Statement,
42+
Expr, ExprId, Item, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
43+
OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
4444
},
4545
item_scope::BuiltinShadowMode,
4646
lang_item::LangItem,
4747
lower::LowerCtx,
4848
nameres::{DefMap, MacroSubNs},
4949
path::{GenericArgs, Path},
5050
type_ref::{Mutability, Rawness, TypeRef},
51-
AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
51+
AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, MacroId, ModuleDefId, UnresolvedMacro,
5252
};
5353

5454
type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
@@ -88,6 +88,7 @@ pub(super) fn lower(
8888
current_binding_owner: None,
8989
awaitable_context: None,
9090
current_span_map: span_map,
91+
current_block_legacy_macro_defs_count: FxHashMap::default(),
9192
}
9293
.collect(params, body, is_async_fn)
9394
}
@@ -104,6 +105,10 @@ struct ExprCollector<'a> {
104105

105106
is_lowering_coroutine: bool,
106107

108+
/// Legacy (`macro_rules!`) macros can have multiple definitions and shadow each other,
109+
/// and we need to find the current definition. So we track the number of definitions we saw.
110+
current_block_legacy_macro_defs_count: FxHashMap<Name, usize>,
111+
107112
current_span_map: Option<Arc<ExpansionSpanMap>>,
108113

109114
current_try_block_label: Option<LabelId>,
@@ -124,31 +129,27 @@ struct ExprCollector<'a> {
124129
#[derive(Clone, Debug)]
125130
struct LabelRib {
126131
kind: RibKind,
127-
// Once we handle macro hygiene this will need to be a map
128-
label: Option<(Name, LabelId, HygieneId)>,
129132
}
130133

131134
impl LabelRib {
132135
fn new(kind: RibKind) -> Self {
133-
LabelRib { kind, label: None }
134-
}
135-
fn new_normal(label: (Name, LabelId, HygieneId)) -> Self {
136-
LabelRib { kind: RibKind::Normal, label: Some(label) }
136+
LabelRib { kind }
137137
}
138138
}
139139

140-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
140+
#[derive(Clone, Debug, PartialEq, Eq)]
141141
enum RibKind {
142-
Normal,
142+
Normal(Name, LabelId, HygieneId),
143143
Closure,
144144
Constant,
145+
MacroDef(Box<MacroDefId>),
145146
}
146147

147148
impl RibKind {
148149
/// This rib forbids referring to labels defined in upwards ribs.
149-
fn is_label_barrier(self) -> bool {
150+
fn is_label_barrier(&self) -> bool {
150151
match self {
151-
RibKind::Normal => false,
152+
RibKind::Normal(..) | RibKind::MacroDef(_) => false,
152153
RibKind::Closure | RibKind::Constant => true,
153154
}
154155
}
@@ -1350,10 +1351,46 @@ impl ExprCollector<'_> {
13501351
statements.push(Statement::Expr { expr, has_semi });
13511352
}
13521353
}
1353-
ast::Stmt::Item(_item) => statements.push(Statement::Item),
1354+
ast::Stmt::Item(ast::Item::MacroDef(macro_)) => {
1355+
let Some(name) = macro_.name() else {
1356+
statements.push(Statement::Item(Item::Other));
1357+
return;
1358+
};
1359+
let name = name.as_name();
1360+
let macro_id = self.def_map.modules[DefMap::ROOT].scope.get(&name).take_macros();
1361+
self.collect_macro_def(statements, macro_id);
1362+
}
1363+
ast::Stmt::Item(ast::Item::MacroRules(macro_)) => {
1364+
let Some(name) = macro_.name() else {
1365+
statements.push(Statement::Item(Item::Other));
1366+
return;
1367+
};
1368+
let name = name.as_name();
1369+
let macro_defs_count =
1370+
self.current_block_legacy_macro_defs_count.entry(name.clone()).or_insert(0);
1371+
let macro_id = self.def_map.modules[DefMap::ROOT]
1372+
.scope
1373+
.get_legacy_macro(&name)
1374+
.and_then(|it| it.get(*macro_defs_count))
1375+
.copied();
1376+
*macro_defs_count += 1;
1377+
self.collect_macro_def(statements, macro_id);
1378+
}
1379+
ast::Stmt::Item(_item) => statements.push(Statement::Item(Item::Other)),
13541380
}
13551381
}
13561382

1383+
fn collect_macro_def(&mut self, statements: &mut Vec<Statement>, macro_id: Option<MacroId>) {
1384+
let Some(macro_id) = macro_id else {
1385+
never!("def map should have macro definition, but it doesn't");
1386+
statements.push(Statement::Item(Item::Other));
1387+
return;
1388+
};
1389+
let macro_id = self.db.macro_def(macro_id);
1390+
statements.push(Statement::Item(Item::MacroDef(Box::new(macro_id))));
1391+
self.label_ribs.push(LabelRib::new(RibKind::MacroDef(Box::new(macro_id))));
1392+
}
1393+
13571394
fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
13581395
self.collect_block_(block, |id, statements, tail| Expr::Block {
13591396
id,
@@ -1399,6 +1436,7 @@ impl ExprCollector<'_> {
13991436
};
14001437
let prev_def_map = mem::replace(&mut self.def_map, def_map);
14011438
let prev_local_module = mem::replace(&mut self.expander.module, module);
1439+
let prev_legacy_macros_count = mem::take(&mut self.current_block_legacy_macro_defs_count);
14021440

14031441
let mut statements = Vec::new();
14041442
block.statements().for_each(|s| self.collect_stmt(&mut statements, s));
@@ -1421,6 +1459,7 @@ impl ExprCollector<'_> {
14211459

14221460
self.def_map = prev_def_map;
14231461
self.expander.module = prev_local_module;
1462+
self.current_block_legacy_macro_defs_count = prev_legacy_macros_count;
14241463
expr_id
14251464
}
14261465

@@ -1780,21 +1819,51 @@ impl ExprCollector<'_> {
17801819
lifetime: Option<ast::Lifetime>,
17811820
) -> Result<Option<LabelId>, BodyDiagnostic> {
17821821
let Some(lifetime) = lifetime else { return Ok(None) };
1783-
let hygiene = self.hygiene_id_for(lifetime.syntax().text_range().start());
1822+
let (mut hygiene_id, mut hygiene_info) = match &self.current_span_map {
1823+
None => (HygieneId::ROOT, None),
1824+
Some(span_map) => {
1825+
let span = span_map.span_at(lifetime.syntax().text_range().start());
1826+
let ctx = self.db.lookup_intern_syntax_context(span.ctx);
1827+
let hygiene_id = HygieneId::new(ctx.opaque_and_semitransparent);
1828+
let hygiene_info = ctx.outer_expn.map(|expansion| {
1829+
let expansion = self.db.lookup_intern_macro_call(expansion);
1830+
(ctx.parent, expansion.def)
1831+
});
1832+
(hygiene_id, hygiene_info)
1833+
}
1834+
};
17841835
let name = Name::new_lifetime(&lifetime);
17851836

17861837
for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
1787-
if let Some((label_name, id, label_hygiene)) = &rib.label {
1788-
if *label_name == name && *label_hygiene == hygiene {
1789-
return if self.is_label_valid_from_rib(rib_idx) {
1790-
Ok(Some(*id))
1791-
} else {
1792-
Err(BodyDiagnostic::UnreachableLabel {
1793-
name,
1794-
node: self.expander.in_file(AstPtr::new(&lifetime)),
1795-
})
1796-
};
1838+
match &rib.kind {
1839+
RibKind::Normal(label_name, id, label_hygiene) => {
1840+
if *label_name == name && *label_hygiene == hygiene_id {
1841+
return if self.is_label_valid_from_rib(rib_idx) {
1842+
Ok(Some(*id))
1843+
} else {
1844+
Err(BodyDiagnostic::UnreachableLabel {
1845+
name,
1846+
node: self.expander.in_file(AstPtr::new(&lifetime)),
1847+
})
1848+
};
1849+
}
1850+
}
1851+
RibKind::MacroDef(macro_id) => {
1852+
if let Some((parent_ctx, label_macro_id)) = hygiene_info {
1853+
if label_macro_id == **macro_id {
1854+
// A macro is allowed to refer to labels from before its declaration.
1855+
// Therefore, if we got to the rib of its declaration, give up its hygiene
1856+
// and use its parent expansion.
1857+
let parent_ctx = self.db.lookup_intern_syntax_context(parent_ctx);
1858+
hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent);
1859+
hygiene_info = parent_ctx.outer_expn.map(|expansion| {
1860+
let expansion = self.db.lookup_intern_macro_call(expansion);
1861+
(parent_ctx.parent, expansion.def)
1862+
});
1863+
}
1864+
}
17971865
}
1866+
_ => {}
17981867
}
17991868
}
18001869

@@ -1808,10 +1877,17 @@ impl ExprCollector<'_> {
18081877
!self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
18091878
}
18101879

1880+
fn pop_label_rib(&mut self) {
1881+
// We need to pop all macro defs, plus one rib.
1882+
while let Some(LabelRib { kind: RibKind::MacroDef(_) }) = self.label_ribs.pop() {
1883+
// Do nothing.
1884+
}
1885+
}
1886+
18111887
fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
18121888
self.label_ribs.push(LabelRib::new(kind));
18131889
let res = f(self);
1814-
self.label_ribs.pop();
1890+
self.pop_label_rib();
18151891
res
18161892
}
18171893

@@ -1821,9 +1897,13 @@ impl ExprCollector<'_> {
18211897
hygiene: HygieneId,
18221898
f: impl FnOnce(&mut Self) -> T,
18231899
) -> T {
1824-
self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label, hygiene)));
1900+
self.label_ribs.push(LabelRib::new(RibKind::Normal(
1901+
self.body[label].name.clone(),
1902+
label,
1903+
hygiene,
1904+
)));
18251905
let res = f(self);
1826-
self.label_ribs.pop();
1906+
self.pop_label_rib();
18271907
res
18281908
}
18291909

crates/hir-def/src/body/pretty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ impl Printer<'_> {
753753
}
754754
wln!(self);
755755
}
756-
Statement::Item => (),
756+
Statement::Item(_) => (),
757757
}
758758
}
759759

crates/hir-def/src/body/scope.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
//! Name resolution for expressions.
2-
use hir_expand::name::Name;
2+
use hir_expand::{name::Name, MacroDefId};
33
use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx};
44
use triomphe::Arc;
55

66
use crate::{
77
body::{Body, HygieneId},
88
db::DefDatabase,
9-
hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
9+
hir::{Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement},
1010
BlockId, ConstBlockId, DefWithBodyId,
1111
};
1212

@@ -45,6 +45,8 @@ pub struct ScopeData {
4545
parent: Option<ScopeId>,
4646
block: Option<BlockId>,
4747
label: Option<(LabelId, Name)>,
48+
// FIXME: We can compress this with an enum for this and `label`/`block` if memory usage matters.
49+
macro_def: Option<Box<MacroDefId>>,
4850
entries: IdxRange<ScopeEntry>,
4951
}
5052

@@ -67,6 +69,11 @@ impl ExprScopes {
6769
self.scopes[scope].block
6870
}
6971

72+
/// If `scope` refers to a macro def scope, returns the corresponding `MacroId`.
73+
pub fn macro_def(&self, scope: ScopeId) -> Option<&Box<MacroDefId>> {
74+
self.scopes[scope].macro_def.as_ref()
75+
}
76+
7077
/// If `scope` refers to a labeled expression scope, returns the corresponding `Label`.
7178
pub fn label(&self, scope: ScopeId) -> Option<(LabelId, Name)> {
7279
self.scopes[scope].label.clone()
@@ -119,6 +126,7 @@ impl ExprScopes {
119126
parent: None,
120127
block: None,
121128
label: None,
129+
macro_def: None,
122130
entries: empty_entries(self.scope_entries.len()),
123131
})
124132
}
@@ -128,6 +136,7 @@ impl ExprScopes {
128136
parent: Some(parent),
129137
block: None,
130138
label: None,
139+
macro_def: None,
131140
entries: empty_entries(self.scope_entries.len()),
132141
})
133142
}
@@ -137,6 +146,7 @@ impl ExprScopes {
137146
parent: Some(parent),
138147
block: None,
139148
label,
149+
macro_def: None,
140150
entries: empty_entries(self.scope_entries.len()),
141151
})
142152
}
@@ -151,6 +161,17 @@ impl ExprScopes {
151161
parent: Some(parent),
152162
block,
153163
label,
164+
macro_def: None,
165+
entries: empty_entries(self.scope_entries.len()),
166+
})
167+
}
168+
169+
fn new_macro_def_scope(&mut self, parent: ScopeId, macro_id: Box<MacroDefId>) -> ScopeId {
170+
self.scopes.alloc(ScopeData {
171+
parent: Some(parent),
172+
block: None,
173+
label: None,
174+
macro_def: Some(macro_id),
154175
entries: empty_entries(self.scope_entries.len()),
155176
})
156177
}
@@ -217,7 +238,10 @@ fn compute_block_scopes(
217238
Statement::Expr { expr, .. } => {
218239
compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
219240
}
220-
Statement::Item => (),
241+
Statement::Item(Item::MacroDef(macro_id)) => {
242+
*scope = scopes.new_macro_def_scope(*scope, macro_id.clone());
243+
}
244+
Statement::Item(Item::Other) => (),
221245
}
222246
}
223247
if let Some(expr) = tail {

crates/hir-def/src/hir.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub mod type_ref;
1717

1818
use std::fmt;
1919

20-
use hir_expand::name::Name;
20+
use hir_expand::{name::Name, MacroDefId};
2121
use intern::{Interned, Symbol};
2222
use la_arena::{Idx, RawIdx};
2323
use rustc_apfloat::ieee::{Half as f16, Quad as f128};
@@ -492,9 +492,13 @@ pub enum Statement {
492492
expr: ExprId,
493493
has_semi: bool,
494494
},
495-
// At the moment, we only use this to figure out if a return expression
496-
// is really the last statement of a block. See #16566
497-
Item,
495+
Item(Item),
496+
}
497+
498+
#[derive(Debug, Clone, PartialEq, Eq)]
499+
pub enum Item {
500+
MacroDef(Box<MacroDefId>),
501+
Other,
498502
}
499503

500504
/// Explicit binding annotations given in the HIR for a binding. Note

0 commit comments

Comments
 (0)