Skip to content

Commit 170b64f

Browse files
committed
resolve: show later defined note in late resolve
1 parent ea06b77 commit 170b64f

9 files changed

+332
-186
lines changed

compiler/rustc_resolve/src/ident.rs

Lines changed: 171 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ enum Shadowing {
4040
Unrestricted,
4141
}
4242

43+
pub(crate) enum ResolveIdentInBlockRes<'ra> {
44+
Res(Res),
45+
Item(NameBinding<'ra>),
46+
DefinedLater { def_site: Span },
47+
NotFound,
48+
}
49+
4350
impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
4451
/// A generic scope visitor.
4552
/// Visits scopes in order to resolve some identifier in them or perform other actions.
@@ -270,6 +277,158 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
270277
None
271278
}
272279

280+
pub(crate) fn resolve_ident_in_block_lexical_scope(
281+
&mut self,
282+
ident: &mut Ident,
283+
ns: Namespace,
284+
parent_scope: &ParentScope<'ra>,
285+
finalize: Option<Finalize>,
286+
rib_index: usize,
287+
ribs: &[Rib<'ra>],
288+
ignore_binding: Option<NameBinding<'ra>>,
289+
) -> ResolveIdentInBlockRes<'ra> {
290+
fn resolve_ident_in_forward_macro_of_block<'ra>(
291+
r: &mut Resolver<'ra, '_>,
292+
expansion: &mut Option<NodeId>, // macro_def_id
293+
module: Module<'ra>,
294+
resolving_block: NodeId,
295+
ident: &mut Ident,
296+
rib_index: usize,
297+
finalize: Option<Finalize>,
298+
ribs: &[Rib<'ra>],
299+
) -> Option<Res> {
300+
let items = r.lookahead_items_in_block.get(&resolving_block)?;
301+
for (node_id, item) in items.iter().rev() {
302+
match item {
303+
LookaheadItemInBlock::MacroDef { def_id } => {
304+
if *def_id != r.macro_def(ident.span.ctxt()) {
305+
continue;
306+
}
307+
expansion.get_or_insert(*node_id);
308+
ident.span.remove_mark();
309+
if let Some((original_rib_ident_def, (module_of_res, res, _))) =
310+
r.bindings_of_macro_def[def_id].get_key_value(ident)
311+
&& module_of_res.is_ancestor_of(module)
312+
{
313+
// The ident resolves to a type parameter or local variable.
314+
return Some(r.validate_res_from_ribs(
315+
rib_index,
316+
*ident,
317+
*res,
318+
finalize.map(|finalize| finalize.path_span),
319+
*original_rib_ident_def,
320+
ribs,
321+
));
322+
}
323+
}
324+
LookaheadItemInBlock::Block => {
325+
// resolve child block later
326+
}
327+
LookaheadItemInBlock::Binding { .. } => {}
328+
}
329+
}
330+
331+
let subs = items
332+
.iter()
333+
.filter_map(|(node_id, item)| {
334+
if matches!(item, LookaheadItemInBlock::Block) { Some(*node_id) } else { None }
335+
})
336+
.collect::<Vec<_>>();
337+
for node_id in subs {
338+
if let Some(res) = resolve_ident_in_forward_macro_of_block(
339+
r, expansion, module, node_id, ident, rib_index, finalize, ribs,
340+
) {
341+
return Some(res);
342+
}
343+
}
344+
345+
None
346+
}
347+
348+
fn is_defined_later(
349+
r: &Resolver<'_, '_>,
350+
macro_def_node: NodeId,
351+
resolving_block: NodeId,
352+
ident: &Ident,
353+
) -> Option<Span> {
354+
let Some(items) = r.lookahead_items_in_block.get(&resolving_block) else {
355+
return None;
356+
};
357+
for (node_id, item) in items {
358+
match item {
359+
LookaheadItemInBlock::Binding { name } => {
360+
if name.name == ident.name {
361+
return Some(name.span);
362+
}
363+
}
364+
LookaheadItemInBlock::Block => {
365+
if let Some(def_span) = is_defined_later(r, macro_def_node, *node_id, ident)
366+
{
367+
return Some(def_span);
368+
}
369+
}
370+
LookaheadItemInBlock::MacroDef { .. } => {
371+
if macro_def_node.eq(node_id) {
372+
return None;
373+
}
374+
}
375+
}
376+
}
377+
378+
None
379+
}
380+
381+
let RibKind::Block { module: block_module, id: resolving_block } = ribs[rib_index].kind
382+
else {
383+
bug!("expected a block rib");
384+
};
385+
let mut expansion = None;
386+
if let Some(res) = resolve_ident_in_forward_macro_of_block(
387+
self,
388+
&mut expansion,
389+
parent_scope.module,
390+
resolving_block,
391+
ident,
392+
rib_index,
393+
finalize,
394+
ribs,
395+
) {
396+
return ResolveIdentInBlockRes::Res(res);
397+
}
398+
399+
if let Some(expansion) = expansion
400+
&& let Some(def_site) = is_defined_later(self, expansion, resolving_block, &ident)
401+
{
402+
// return `None` for this case:
403+
//
404+
// ```
405+
// let a = m!();
406+
// let b = 1;
407+
// macro_rules! m { () => { b } }
408+
// use b;
409+
// ```
410+
return ResolveIdentInBlockRes::DefinedLater { def_site };
411+
}
412+
413+
if let Some(module) = block_module
414+
&& let Ok(binding) = self.cm().resolve_ident_in_module_unadjusted(
415+
ModuleOrUniformRoot::Module(module),
416+
*ident,
417+
ns,
418+
parent_scope,
419+
Shadowing::Unrestricted,
420+
finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }),
421+
ignore_binding,
422+
None,
423+
)
424+
{
425+
// The ident resolves to an item in a block.
426+
return ResolveIdentInBlockRes::Item(binding);
427+
}
428+
429+
ResolveIdentInBlockRes::NotFound
430+
}
431+
273432
/// This resolves the identifier `ident` in the namespace `ns` in the current lexical scope.
274433
/// More specifically, we proceed up the hierarchy of scopes and return the binding for
275434
/// `ident` in the first scope that defines it (or None if no scopes define it).
@@ -328,143 +487,22 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
328487
*original_rib_ident_def,
329488
ribs,
330489
)));
331-
} else if let RibKind::Block { module, id: block_id } = rib.kind {
332-
fn resolve_ident_in_forward_macro_of_block<'ra>(
333-
r: &mut Resolver<'ra, '_>,
334-
expansion: &mut Option<NodeId>, // macro_def_id
335-
module: Module<'ra>,
336-
resolving_block: NodeId,
337-
ident: &mut Ident,
338-
i: usize,
339-
finalize: Option<Finalize>,
340-
ribs: &[Rib<'ra>],
341-
) -> Option<Res> {
342-
let items = r.lookahead_items_in_block.get(&resolving_block)?;
343-
for (node_id, item) in items.iter().rev() {
344-
match item {
345-
LookaheadItemInBlock::MacroDef { def_id } => {
346-
if *def_id != r.macro_def(ident.span.ctxt()) {
347-
continue;
348-
}
349-
expansion.get_or_insert(*node_id);
350-
ident.span.remove_mark();
351-
if let Some((original_rib_ident_def, (module_of_res, res, _))) =
352-
r.bindings_of_macro_def[def_id].get_key_value(ident)
353-
&& module_of_res.is_ancestor_of(module)
354-
{
355-
// The ident resolves to a type parameter or local variable.
356-
return Some(r.validate_res_from_ribs(
357-
i,
358-
*ident,
359-
*res,
360-
finalize.map(|finalize| finalize.path_span),
361-
*original_rib_ident_def,
362-
ribs,
363-
));
364-
}
365-
}
366-
LookaheadItemInBlock::Block => {
367-
// resolve child block later
368-
}
369-
LookaheadItemInBlock::Binding { .. } => {}
370-
}
371-
}
372-
373-
let subs = items
374-
.iter()
375-
.filter_map(|(node_id, item)| {
376-
if matches!(item, LookaheadItemInBlock::Block) {
377-
Some(*node_id)
378-
} else {
379-
None
380-
}
381-
})
382-
.collect::<Vec<_>>();
383-
for node_id in subs {
384-
if let Some(res) = resolve_ident_in_forward_macro_of_block(
385-
r, expansion, module, node_id, ident, i, finalize, ribs,
386-
) {
387-
return Some(res);
388-
}
389-
}
390-
391-
None
392-
}
393-
394-
fn is_defined_later(
395-
r: &Resolver<'_, '_>,
396-
expansion: NodeId, // macro_def_id
397-
block_id: NodeId,
398-
ident: &Ident,
399-
) -> bool {
400-
let Some(items) = r.lookahead_items_in_block.get(&block_id) else {
401-
return false;
402-
};
403-
for (node_id, item) in items {
404-
match item {
405-
LookaheadItemInBlock::Binding { name } => {
406-
if name.name == ident.name {
407-
return true;
408-
}
409-
}
410-
LookaheadItemInBlock::Block => {
411-
if is_defined_later(r, expansion, *node_id, ident) {
412-
return true;
413-
}
414-
}
415-
LookaheadItemInBlock::MacroDef { .. } => {
416-
if expansion.eq(node_id) {
417-
return false;
418-
}
419-
}
420-
}
421-
}
422-
423-
false
424-
}
425-
426-
let mut expansion = None;
427-
if let Some(res) = resolve_ident_in_forward_macro_of_block(
428-
self,
429-
&mut expansion,
430-
parent_scope.module,
431-
block_id,
490+
} else if let RibKind::Block { .. } = rib.kind {
491+
match self.resolve_ident_in_block_lexical_scope(
432492
&mut ident,
433-
i,
493+
ns,
494+
parent_scope,
434495
finalize,
496+
i,
435497
ribs,
498+
ignore_binding,
436499
) {
437-
return Some(LexicalScopeBinding::Res(res));
438-
}
439-
440-
if let Some(expansion) = expansion
441-
&& is_defined_later(self, expansion, block_id, &ident)
442-
{
443-
// return `None` for this case:
444-
//
445-
// ```
446-
// let a = m!();
447-
// let b = 1;
448-
// macro_rules! m { () => { b } }
449-
// use b;
450-
// ```
451-
return None;
452-
}
453-
454-
if let Some(module) = module
455-
&& let Ok(binding) = self.cm().resolve_ident_in_module_unadjusted(
456-
ModuleOrUniformRoot::Module(module),
457-
ident,
458-
ns,
459-
parent_scope,
460-
Shadowing::Unrestricted,
461-
finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }),
462-
ignore_binding,
463-
None,
464-
)
465-
{
466-
// The ident resolves to an item in a block.
467-
return Some(LexicalScopeBinding::Item(binding));
500+
ResolveIdentInBlockRes::Res(res) => return Some(LexicalScopeBinding::Res(res)),
501+
ResolveIdentInBlockRes::Item(item) => {
502+
return Some(LexicalScopeBinding::Item(item));
503+
}
504+
ResolveIdentInBlockRes::DefinedLater { .. } => return None,
505+
ResolveIdentInBlockRes::NotFound => {}
468506
}
469507
} else if let RibKind::Module(module) = rib.kind {
470508
// Encountered a module item, abandon ribs and look into that module and preludes.

compiler/rustc_resolve/src/late/diagnostics.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use tracing::debug;
3232

3333
use super::NoConstantGenericsReason;
3434
use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
35+
use crate::ident::ResolveIdentInBlockRes;
3536
use crate::late::{
3637
AliasPossibility, LateResolutionVisitor, LifetimeBinderKind, LifetimeRes, LifetimeRibKind,
3738
LifetimeUseSet, QSelf, RibKind,
@@ -438,6 +439,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
438439
}
439440

440441
self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg);
442+
self.detect_is_defined_later_in_block_for_macro_expansion(&mut err, path, source);
441443
self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);
442444
self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);
443445

@@ -1249,6 +1251,37 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
12491251
true
12501252
}
12511253

1254+
fn detect_is_defined_later_in_block_for_macro_expansion(
1255+
&mut self,
1256+
err: &mut Diag<'_>,
1257+
path: &[Segment],
1258+
source: PathSource<'_, 'ast, 'ra>,
1259+
) {
1260+
let ns = source.namespace();
1261+
if ns != Namespace::ValueNS {
1262+
return;
1263+
}
1264+
let [segment] = path else { return };
1265+
let mut ident = segment.ident;
1266+
for (rib_index, rib) in self.ribs[ns].iter().enumerate().rev() {
1267+
if let RibKind::Block { .. } = rib.kind
1268+
&& let ResolveIdentInBlockRes::DefinedLater { def_site } =
1269+
self.r.resolve_ident_in_block_lexical_scope(
1270+
&mut ident,
1271+
ns,
1272+
&self.parent_scope,
1273+
None,
1274+
rib_index,
1275+
&self.ribs[ns],
1276+
None,
1277+
)
1278+
{
1279+
err.span_label(def_site, format!("`{}` is defined here", segment.ident.name));
1280+
break;
1281+
}
1282+
}
1283+
}
1284+
12521285
fn detect_missing_binding_available_from_pattern(
12531286
&self,
12541287
err: &mut Diag<'_>,

tests/ui/proc-macro/weird-hygiene.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ error[E0425]: cannot find value `hidden_ident` in this scope
44
LL | Value = (stringify!($tokens + hidden_ident), 1).1
55
| ^^^^^^^^^^^^ not found in this scope
66
...
7+
LL | let hidden_ident = "Hello1";
8+
| ------------ `hidden_ident` is defined here
79
LL | other!(50);
810
| ---------- in this macro invocation
911
|

0 commit comments

Comments
 (0)