Skip to content

Commit c7a6643

Browse files
committed
Diagnose shadowing on AST.
1 parent f9d0091 commit c7a6643

File tree

3 files changed

+209
-328
lines changed

3 files changed

+209
-328
lines changed

compiler/rustc_resolve/src/late.rs

Lines changed: 85 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ use crate::{path_names_to_string, BindingError, CrateLint, LexicalScopeBinding};
1111
use crate::{Module, ModuleOrUniformRoot, ParentScope, PathResult};
1212
use crate::{ResolutionError, Resolver, Segment, UseError};
1313

14+
use diagnostics::{
15+
original_label, original_lifetime, original_lifetime_param, shadower_label, shadower_lifetime,
16+
};
17+
1418
use rustc_ast::ptr::P;
1519
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
1620
use rustc_ast::*;
@@ -160,6 +164,22 @@ impl RibKind<'_> {
160164
AssocItemRibKind | ItemRibKind(..) | ForwardGenericParamBanRibKind => true,
161165
}
162166
}
167+
168+
/// This rib forbids referring to labels defined in upwards ribs.
169+
fn is_label_barrier(self) -> bool {
170+
match self {
171+
NormalRibKind | MacroDefinition(..) => false,
172+
173+
AssocItemRibKind
174+
| ClosureOrAsyncRibKind
175+
| FnItemRibKind
176+
| ItemRibKind(..)
177+
| ConstantItemRibKind(..)
178+
| ModuleRibKind(..)
179+
| ForwardGenericParamBanRibKind
180+
| ConstParamTyRibKind => true,
181+
}
182+
}
163183
}
164184

165185
/// A single local scope.
@@ -706,7 +726,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
706726
// Create a value rib for the function.
707727
self.with_rib(ValueNS, rib_kind, |this| {
708728
// Create a label rib for the function.
709-
this.with_label_rib(rib_kind, |this| {
729+
this.with_label_rib(FnItemRibKind, |this| {
710730
let async_node_id = fn_kind.header().and_then(|h| h.asyncness.opt_return_id());
711731

712732
if let FnKind::Fn(_, _, _, _, generics, _) = fn_kind {
@@ -1400,21 +1420,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
14001420
let ribs = &self.label_ribs[rib_index + 1..];
14011421

14021422
for rib in ribs {
1403-
match rib.kind {
1404-
NormalRibKind | MacroDefinition(..) => {
1405-
// Nothing to do. Continue.
1406-
}
1407-
1408-
AssocItemRibKind
1409-
| ClosureOrAsyncRibKind
1410-
| FnItemRibKind
1411-
| ItemRibKind(..)
1412-
| ConstantItemRibKind(..)
1413-
| ModuleRibKind(..)
1414-
| ForwardGenericParamBanRibKind
1415-
| ConstParamTyRibKind => {
1416-
return false;
1417-
}
1423+
if rib.kind.is_label_barrier() {
1424+
return false;
14181425
}
14191426
}
14201427

@@ -1693,6 +1700,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
16931700
let mut function_value_rib = Rib::new(kind);
16941701
let mut function_lifetime_rib = LifetimeRib::new(lifetime_kind);
16951702
let mut seen_bindings = FxHashMap::default();
1703+
let mut seen_lifetimes = FxHashMap::default();
16961704

16971705
// We also can't shadow bindings from the parent item
16981706
if let AssocItemRibKind = kind {
@@ -1710,23 +1718,47 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
17101718

17111719
// Forbid shadowing lifetime bindings
17121720
for rib in self.lifetime_ribs.iter().rev() {
1713-
seen_bindings.extend(rib.bindings.iter().map(|(ident, _)| (*ident, ident.span)));
1721+
seen_lifetimes.extend(
1722+
rib.bindings.iter().map(|(ident, _)| (*ident, original_lifetime(ident.span))),
1723+
);
1724+
}
1725+
for rib in self.label_ribs.iter().rev() {
1726+
if rib.kind.is_label_barrier() {
1727+
break;
1728+
}
1729+
seen_lifetimes
1730+
.extend(rib.bindings.iter().map(|(ident, _)| (*ident, original_label(ident.span))));
17141731
}
17151732

17161733
for param in params {
17171734
let ident = param.ident.normalize_to_macros_2_0();
17181735
debug!("with_generic_param_rib: {}", param.id);
17191736

1720-
match seen_bindings.entry(ident) {
1721-
Entry::Occupied(entry) => {
1722-
let span = *entry.get();
1723-
let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span);
1724-
if !matches!(param.kind, GenericParamKind::Lifetime) {
1725-
self.report_error(param.ident.span, err);
1737+
if let GenericParamKind::Lifetime = param.kind {
1738+
match seen_lifetimes.entry(ident) {
1739+
Entry::Occupied(entry) => {
1740+
let original = *entry.get();
1741+
diagnostics::signal_shadowing_problem(
1742+
self.r.session,
1743+
ident.name,
1744+
original,
1745+
shadower_lifetime(param.ident.span),
1746+
)
1747+
}
1748+
Entry::Vacant(entry) => {
1749+
entry.insert(original_lifetime_param(param.ident.span));
17261750
}
17271751
}
1728-
Entry::Vacant(entry) => {
1729-
entry.insert(param.ident.span);
1752+
} else {
1753+
match seen_bindings.entry(ident) {
1754+
Entry::Occupied(entry) => {
1755+
let span = *entry.get();
1756+
let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span);
1757+
self.report_error(param.ident.span, err);
1758+
}
1759+
Entry::Vacant(entry) => {
1760+
entry.insert(param.ident.span);
1761+
}
17301762
}
17311763
}
17321764

@@ -2911,8 +2943,35 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
29112943
if label.ident.as_str().as_bytes()[1] != b'_' {
29122944
self.diagnostic_metadata.unused_labels.insert(id, label.ident.span);
29132945
}
2946+
2947+
// Forbid shadowing lifetime bindings
2948+
let ident = label.ident.normalize_to_macro_rules();
2949+
for rib in self.lifetime_ribs.iter().rev() {
2950+
if let Some((orig_ident, _)) = rib.bindings.get_key_value(&ident) {
2951+
diagnostics::signal_shadowing_problem(
2952+
self.r.session,
2953+
label.ident.name,
2954+
original_lifetime(orig_ident.span),
2955+
shadower_label(label.ident.span),
2956+
)
2957+
}
2958+
}
2959+
for rib in self.label_ribs.iter_mut().rev() {
2960+
if let Some((orig_ident, _)) = rib.bindings.get_key_value(&ident) {
2961+
diagnostics::signal_shadowing_problem(
2962+
self.r.session,
2963+
label.ident.name,
2964+
original_label(orig_ident.span),
2965+
shadower_label(label.ident.span),
2966+
)
2967+
}
2968+
if rib.kind.is_label_barrier() {
2969+
rib.bindings.insert(ident, id);
2970+
break;
2971+
}
2972+
}
2973+
29142974
self.with_label_rib(NormalRibKind, |this| {
2915-
let ident = label.ident.normalize_to_macro_rules();
29162975
this.label_ribs.last_mut().unwrap().bindings.insert(ident, id);
29172976
f(this);
29182977
});

compiler/rustc_resolve/src/late/diagnostics.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
2020
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
2121
use rustc_hir::PrimTy;
2222
use rustc_session::parse::feature_err;
23+
use rustc_session::Session;
2324
use rustc_span::edition::Edition;
2425
use rustc_span::hygiene::MacroKind;
2526
use rustc_span::lev_distance::find_best_match_for_name;
@@ -1868,6 +1869,86 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
18681869
}
18691870
}
18701871

1872+
#[derive(Copy, Clone, PartialEq)]
1873+
enum ShadowKind {
1874+
Label,
1875+
Lifetime,
1876+
}
1877+
#[derive(Copy, Clone)]
1878+
pub struct Original {
1879+
kind: ShadowKind,
1880+
span: Span,
1881+
param: bool,
1882+
}
1883+
#[derive(Copy, Clone)]
1884+
pub struct Shadower {
1885+
kind: ShadowKind,
1886+
span: Span,
1887+
}
1888+
1889+
pub fn original_label(span: Span) -> Original {
1890+
Original { kind: ShadowKind::Label, span, param: false }
1891+
}
1892+
pub fn shadower_label(span: Span) -> Shadower {
1893+
Shadower { kind: ShadowKind::Label, span }
1894+
}
1895+
pub fn original_lifetime(span: Span) -> Original {
1896+
Original { kind: ShadowKind::Lifetime, span, param: false }
1897+
}
1898+
pub fn original_lifetime_param(span: Span) -> Original {
1899+
Original { kind: ShadowKind::Lifetime, span, param: true }
1900+
}
1901+
pub fn shadower_lifetime(span: Span) -> Shadower {
1902+
Shadower { kind: ShadowKind::Lifetime, span }
1903+
}
1904+
1905+
impl ShadowKind {
1906+
fn desc(&self) -> &'static str {
1907+
match *self {
1908+
ShadowKind::Label => "label",
1909+
ShadowKind::Lifetime => "lifetime",
1910+
}
1911+
}
1912+
}
1913+
1914+
pub fn signal_shadowing_problem(sess: &Session, name: Symbol, orig: Original, shadower: Shadower) {
1915+
let mut err = if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) {
1916+
// lifetime/lifetime shadowing is an error
1917+
if orig.param {
1918+
struct_span_err!(
1919+
sess,
1920+
shadower.span,
1921+
E0263,
1922+
"lifetime name `{}` declared twice in the same scope",
1923+
name,
1924+
)
1925+
} else {
1926+
struct_span_err!(
1927+
sess,
1928+
shadower.span,
1929+
E0496,
1930+
"lifetime name `{}` shadows a lifetime name that is already in scope",
1931+
name,
1932+
)
1933+
}
1934+
} else {
1935+
// shadowing involving a label is only a warning, due to issues with
1936+
// labels and lifetimes not being macro-hygienic.
1937+
sess.struct_span_warn(
1938+
shadower.span,
1939+
&format!(
1940+
"{} name `{}` shadows a {} name that is already in scope",
1941+
shadower.kind.desc(),
1942+
name,
1943+
orig.kind.desc()
1944+
),
1945+
)
1946+
};
1947+
err.span_label(orig.span, "first declared here");
1948+
err.span_label(shadower.span, format!("{} `{}` already in scope", orig.kind.desc(), name));
1949+
err.emit();
1950+
}
1951+
18711952
impl<'tcx> LifetimeContext<'_, 'tcx> {
18721953
crate fn report_missing_lifetime_specifiers(
18731954
&self,

0 commit comments

Comments
 (0)