Skip to content

Commit 206b13f

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 206b13f

File tree

12 files changed

+285
-50
lines changed

12 files changed

+285
-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: 108 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,45 @@ 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]
1361+
.scope
1362+
.get(&name)
1363+
.take_macros()
1364+
.expect("def map should have macro definition, but it doesn't");
1365+
self.collect_macro_def(statements, macro_id);
1366+
}
1367+
ast::Stmt::Item(ast::Item::MacroRules(macro_)) => {
1368+
let Some(name) = macro_.name() else {
1369+
statements.push(Statement::Item(Item::Other));
1370+
return;
1371+
};
1372+
let name = name.as_name();
1373+
let macro_defs_count =
1374+
self.current_block_legacy_macro_defs_count.entry(name.clone()).or_insert(0);
1375+
let macro_id = *self.def_map.modules[DefMap::ROOT]
1376+
.scope
1377+
.get_legacy_macro(&name)
1378+
.and_then(|it| it.get(*macro_defs_count))
1379+
.expect("def map should have macro definition, but it doesn't");
1380+
*macro_defs_count += 1;
1381+
self.collect_macro_def(statements, macro_id);
1382+
}
1383+
ast::Stmt::Item(_item) => statements.push(Statement::Item(Item::Other)),
13541384
}
13551385
}
13561386

1387+
fn collect_macro_def(&mut self, statements: &mut Vec<Statement>, macro_id: MacroId) {
1388+
let macro_id = self.db.macro_def(macro_id);
1389+
statements.push(Statement::Item(Item::MacroDef(Box::new(macro_id))));
1390+
self.label_ribs.push(LabelRib::new(RibKind::MacroDef(Box::new(macro_id))));
1391+
}
1392+
13571393
fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
13581394
self.collect_block_(block, |id, statements, tail| Expr::Block {
13591395
id,
@@ -1399,6 +1435,7 @@ impl ExprCollector<'_> {
13991435
};
14001436
let prev_def_map = mem::replace(&mut self.def_map, def_map);
14011437
let prev_local_module = mem::replace(&mut self.expander.module, module);
1438+
let prev_legacy_macros_count = mem::take(&mut self.current_block_legacy_macro_defs_count);
14021439

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

14221459
self.def_map = prev_def_map;
14231460
self.expander.module = prev_local_module;
1461+
self.current_block_legacy_macro_defs_count = prev_legacy_macros_count;
14241462
expr_id
14251463
}
14261464

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

17861836
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-
};
1837+
match &rib.kind {
1838+
RibKind::Normal(label_name, id, label_hygiene) => {
1839+
if *label_name == name && *label_hygiene == hygiene_id {
1840+
return if self.is_label_valid_from_rib(rib_idx) {
1841+
Ok(Some(*id))
1842+
} else {
1843+
Err(BodyDiagnostic::UnreachableLabel {
1844+
name,
1845+
node: self.expander.in_file(AstPtr::new(&lifetime)),
1846+
})
1847+
};
1848+
}
1849+
}
1850+
RibKind::MacroDef(macro_id) => {
1851+
if let Some((parent_ctx, label_macro_id)) = hygiene_info {
1852+
if label_macro_id == **macro_id {
1853+
// A macro is allowed to refer to labels from before its declaration.
1854+
// Therefore, if we got to the rib of its declaration, give up its hygiene
1855+
// and use its parent expansion.
1856+
let parent_ctx = self.db.lookup_intern_syntax_context(parent_ctx);
1857+
hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent);
1858+
hygiene_info = parent_ctx.outer_expn.map(|expansion| {
1859+
let expansion = self.db.lookup_intern_macro_call(expansion);
1860+
(parent_ctx.parent, expansion.def)
1861+
});
1862+
}
1863+
}
17971864
}
1865+
_ => {}
17981866
}
17991867
}
18001868

@@ -1808,10 +1876,17 @@ impl ExprCollector<'_> {
18081876
!self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
18091877
}
18101878

1879+
fn pop_label_rib(&mut self) {
1880+
// We need to pop all macro defs, plus one rib.
1881+
while let Some(LabelRib { kind: RibKind::MacroDef(_) }) = self.label_ribs.pop() {
1882+
// Do nothing.
1883+
}
1884+
}
1885+
18111886
fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
18121887
self.label_ribs.push(LabelRib::new(kind));
18131888
let res = f(self);
1814-
self.label_ribs.pop();
1889+
self.pop_label_rib();
18151890
res
18161891
}
18171892

@@ -1821,9 +1896,13 @@ impl ExprCollector<'_> {
18211896
hygiene: HygieneId,
18221897
f: impl FnOnce(&mut Self) -> T,
18231898
) -> T {
1824-
self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label, hygiene)));
1899+
self.label_ribs.push(LabelRib::new(RibKind::Normal(
1900+
self.body[label].name.clone(),
1901+
label,
1902+
hygiene,
1903+
)));
18251904
let res = f(self);
1826-
self.label_ribs.pop();
1905+
self.pop_label_rib();
18271906
res
18281907
}
18291908

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)