Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Correctly resolve variables and labels from before macro definition i…
Browse files Browse the repository at this point in the history
…n 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.
ChayimFriedman2 committed Oct 14, 2024
1 parent 058ab53 commit df94afc
Showing 15 changed files with 300 additions and 50 deletions.
4 changes: 2 additions & 2 deletions crates/hir-def/src/body.rs
Original file line number Diff line number Diff line change
@@ -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()
}
}
134 changes: 106 additions & 28 deletions crates/hir-def/src/body/lower.rs
Original file line number Diff line number Diff line change
@@ -39,16 +39,16 @@ 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,
lower::LowerCtx,
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>>;
@@ -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<Name, usize>,

current_span_map: Option<Arc<ExpansionSpanMap>>,

current_try_block_label: Option<LabelId>,
@@ -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<Statement>, 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<ast::Lifetime>,
) -> Result<Option<LabelId>, 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<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
}

@@ -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
}

2 changes: 1 addition & 1 deletion crates/hir-def/src/body/pretty.rs
Original file line number Diff line number Diff line change
@@ -748,7 +748,7 @@ impl Printer<'_> {
}
wln!(self);
}
Statement::Item => (),
Statement::Item(_) => (),
}
}

30 changes: 27 additions & 3 deletions crates/hir-def/src/body/scope.rs
Original file line number Diff line number Diff line change
@@ -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<ScopeId>,
block: Option<BlockId>,
label: Option<(LabelId, Name)>,
// FIXME: We can compress this with an enum for this and `label`/`block` if memory usage matters.
macro_def: Option<MacroId>,
entries: IdxRange<ScopeEntry>,
}

@@ -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<MacroId> {
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 {
3 changes: 3 additions & 0 deletions crates/hir-def/src/db.rs
Original file line number Diff line number Diff line change
@@ -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),
12 changes: 8 additions & 4 deletions crates/hir-def/src/hir.rs
Original file line number Diff line number Diff line change
@@ -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 {
40 changes: 40 additions & 0 deletions crates/hir-def/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<Function>;
@@ -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<MacroId> 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<hir_expand::MacroId> 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 {
44 changes: 38 additions & 6 deletions crates/hir-def/src/resolver.rs
Original file line number Diff line number Diff line change
@@ -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<ResolveValueResult> {
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<TypeOwnerId> {
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<ExprScopes>,
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);
}
26 changes: 24 additions & 2 deletions crates/hir-expand/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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,
2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer/closure.rs
Original file line number Diff line number Diff line change
@@ -714,7 +714,7 @@ impl InferenceContext<'_> {
Statement::Expr { expr, has_semi: _ } => {
self.consume_expr(*expr);
}
Statement::Item => (),
Statement::Item(_) => (),
}
}
if let Some(tail) = tail {
2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
@@ -1526,7 +1526,7 @@ impl InferenceContext<'_> {
);
}
}
Statement::Item => (),
Statement::Item(_) => (),
}
}

2 changes: 1 addition & 1 deletion crates/hir-ty/src/infer/mutability.rs
Original file line number Diff line number Diff line change
@@ -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 {
2 changes: 1 addition & 1 deletion crates/hir-ty/src/mir/lower.rs
Original file line number Diff line number Diff line change
@@ -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 {
17 changes: 17 additions & 0 deletions crates/hir-ty/src/tests/simple.rs
Original file line number Diff line number Diff line change
@@ -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
}
"#,
);
}
30 changes: 30 additions & 0 deletions crates/ide-diagnostics/src/handlers/undeclared_label.rs
Original file line number Diff line number Diff line change
@@ -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!();
}
}
}
"#,
);
}

0 comments on commit df94afc

Please sign in to comment.