Skip to content

Commit c70ec8e

Browse files
committed
Diagnose shadowing on AST.
1 parent 1ec2c13 commit c70ec8e

File tree

3 files changed

+219
-318
lines changed

3 files changed

+219
-318
lines changed

compiler/rustc_resolve/src/late.rs

+93-26
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ use crate::{path_names_to_string, BindingError, Finalize, LexicalScopeBinding};
1111
use crate::{Module, ModuleOrUniformRoot, NameBinding, 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::*;
@@ -163,6 +167,23 @@ impl RibKind<'_> {
163167
AssocItemRibKind | ItemRibKind(_) | ForwardGenericParamBanRibKind => true,
164168
}
165169
}
170+
171+
/// This rib forbids referring to labels defined in upwards ribs.
172+
fn is_label_barrier(self) -> bool {
173+
match self {
174+
NormalRibKind | MacroDefinition(..) => false,
175+
176+
AssocItemRibKind
177+
| ClosureOrAsyncRibKind
178+
| FnItemRibKind
179+
| ItemRibKind(..)
180+
| ConstantItemRibKind(..)
181+
| ModuleRibKind(..)
182+
| ForwardGenericParamBanRibKind
183+
| ConstParamTyRibKind
184+
| InlineAsmSymRibKind => true,
185+
}
186+
}
166187
}
167188

168189
/// A single local scope.
@@ -684,7 +705,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
684705
// Create a value rib for the function.
685706
self.with_rib(ValueNS, rib_kind, |this| {
686707
// Create a label rib for the function.
687-
this.with_label_rib(rib_kind, |this| {
708+
this.with_label_rib(FnItemRibKind, |this| {
688709
let async_node_id = fn_kind.header().and_then(|h| h.asyncness.opt_return_id());
689710

690711
if let FnKind::Fn(_, _, _, _, generics, _) = fn_kind {
@@ -1351,22 +1372,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
13511372
let ribs = &self.label_ribs[rib_index + 1..];
13521373

13531374
for rib in ribs {
1354-
match rib.kind {
1355-
NormalRibKind | MacroDefinition(..) => {
1356-
// Nothing to do. Continue.
1357-
}
1358-
1359-
AssocItemRibKind
1360-
| ClosureOrAsyncRibKind
1361-
| FnItemRibKind
1362-
| ItemRibKind(..)
1363-
| ConstantItemRibKind(..)
1364-
| ModuleRibKind(..)
1365-
| ForwardGenericParamBanRibKind
1366-
| ConstParamTyRibKind
1367-
| InlineAsmSymRibKind => {
1368-
return false;
1369-
}
1375+
if rib.kind.is_label_barrier() {
1376+
return false;
13701377
}
13711378
}
13721379

@@ -1644,6 +1651,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
16441651
let mut function_value_rib = Rib::new(kind);
16451652
let mut function_lifetime_rib = LifetimeRib::new(lifetime_kind);
16461653
let mut seen_bindings = FxHashMap::default();
1654+
let mut seen_lifetimes = FxHashMap::default();
16471655

16481656
// We also can't shadow bindings from the parent item
16491657
if let AssocItemRibKind = kind {
@@ -1659,20 +1667,52 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
16591667
add_bindings_for_ns(TypeNS);
16601668
}
16611669

1670+
// Forbid shadowing lifetime bindings
1671+
for rib in self.lifetime_ribs.iter().rev() {
1672+
seen_lifetimes.extend(
1673+
rib.bindings.iter().map(|(ident, _)| (*ident, original_lifetime(ident.span))),
1674+
);
1675+
if let LifetimeRibKind::Item = rib.kind {
1676+
break;
1677+
}
1678+
}
1679+
for rib in self.label_ribs.iter().rev() {
1680+
if rib.kind.is_label_barrier() {
1681+
break;
1682+
}
1683+
seen_lifetimes
1684+
.extend(rib.bindings.iter().map(|(ident, _)| (*ident, original_label(ident.span))));
1685+
}
1686+
16621687
for param in params {
16631688
let ident = param.ident.normalize_to_macros_2_0();
16641689
debug!("with_generic_param_rib: {}", param.id);
16651690

1666-
match seen_bindings.entry(ident) {
1667-
Entry::Occupied(entry) => {
1668-
let span = *entry.get();
1669-
let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span);
1670-
if !matches!(param.kind, GenericParamKind::Lifetime) {
1671-
self.report_error(param.ident.span, err);
1691+
if let GenericParamKind::Lifetime = param.kind {
1692+
match seen_lifetimes.entry(ident) {
1693+
Entry::Occupied(entry) => {
1694+
let original = *entry.get();
1695+
diagnostics::signal_shadowing_problem(
1696+
self.r.session,
1697+
ident.name,
1698+
original,
1699+
shadower_lifetime(param.ident.span),
1700+
)
1701+
}
1702+
Entry::Vacant(entry) => {
1703+
entry.insert(original_lifetime_param(param.ident.span));
16721704
}
16731705
}
1674-
Entry::Vacant(entry) => {
1675-
entry.insert(param.ident.span);
1706+
} else {
1707+
match seen_bindings.entry(ident) {
1708+
Entry::Occupied(entry) => {
1709+
let span = *entry.get();
1710+
let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span);
1711+
self.report_error(param.ident.span, err);
1712+
}
1713+
Entry::Vacant(entry) => {
1714+
entry.insert(param.ident.span);
1715+
}
16761716
}
16771717
}
16781718

@@ -2852,8 +2892,35 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
28522892
if label.ident.as_str().as_bytes()[1] != b'_' {
28532893
self.diagnostic_metadata.unused_labels.insert(id, label.ident.span);
28542894
}
2895+
2896+
// Forbid shadowing lifetime bindings
2897+
let ident = label.ident.normalize_to_macro_rules();
2898+
for rib in self.lifetime_ribs.iter().rev() {
2899+
if let Some((orig_ident, _)) = rib.bindings.get_key_value(&ident) {
2900+
diagnostics::signal_shadowing_problem(
2901+
self.r.session,
2902+
label.ident.name,
2903+
original_lifetime(orig_ident.span),
2904+
shadower_label(label.ident.span),
2905+
)
2906+
}
2907+
}
2908+
for rib in self.label_ribs.iter_mut().rev() {
2909+
if let Some((orig_ident, _)) = rib.bindings.get_key_value(&ident) {
2910+
diagnostics::signal_shadowing_problem(
2911+
self.r.session,
2912+
label.ident.name,
2913+
original_label(orig_ident.span),
2914+
shadower_label(label.ident.span),
2915+
)
2916+
}
2917+
if rib.kind.is_label_barrier() {
2918+
rib.bindings.insert(ident, id);
2919+
break;
2920+
}
2921+
}
2922+
28552923
self.with_label_rib(NormalRibKind, |this| {
2856-
let ident = label.ident.normalize_to_macro_rules();
28572924
this.label_ribs.last_mut().unwrap().bindings.insert(ident, id);
28582925
f(this);
28592926
});

compiler/rustc_resolve/src/late/diagnostics.rs

+82
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
2323
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
2424
use rustc_hir::PrimTy;
2525
use rustc_session::parse::feature_err;
26+
use rustc_session::Session;
2627
use rustc_span::edition::Edition;
2728
use rustc_span::hygiene::MacroKind;
2829
use rustc_span::lev_distance::find_best_match_for_name;
@@ -1892,6 +1893,87 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
18921893
}
18931894
}
18941895

1896+
#[derive(Copy, Clone, PartialEq)]
1897+
enum ShadowKind {
1898+
Label,
1899+
Lifetime,
1900+
}
1901+
#[derive(Copy, Clone)]
1902+
pub struct Original {
1903+
kind: ShadowKind,
1904+
span: Span,
1905+
param: bool,
1906+
}
1907+
#[derive(Copy, Clone)]
1908+
pub struct Shadower {
1909+
kind: ShadowKind,
1910+
span: Span,
1911+
}
1912+
1913+
pub fn original_label(span: Span) -> Original {
1914+
Original { kind: ShadowKind::Label, span, param: false }
1915+
}
1916+
pub fn shadower_label(span: Span) -> Shadower {
1917+
Shadower { kind: ShadowKind::Label, span }
1918+
}
1919+
pub fn original_lifetime(span: Span) -> Original {
1920+
Original { kind: ShadowKind::Lifetime, span, param: false }
1921+
}
1922+
pub fn original_lifetime_param(span: Span) -> Original {
1923+
Original { kind: ShadowKind::Lifetime, span, param: true }
1924+
}
1925+
pub fn shadower_lifetime(span: Span) -> Shadower {
1926+
Shadower { kind: ShadowKind::Lifetime, span }
1927+
}
1928+
1929+
impl ShadowKind {
1930+
fn desc(&self) -> &'static str {
1931+
match *self {
1932+
ShadowKind::Label => "label",
1933+
ShadowKind::Lifetime => "lifetime",
1934+
}
1935+
}
1936+
}
1937+
1938+
pub fn signal_shadowing_problem(sess: &Session, name: Symbol, orig: Original, shadower: Shadower) {
1939+
let mut err = if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) {
1940+
// lifetime/lifetime shadowing is an error
1941+
if orig.param {
1942+
struct_span_err!(
1943+
sess,
1944+
shadower.span,
1945+
E0263,
1946+
"lifetime name `{}` declared twice in the same scope",
1947+
name,
1948+
)
1949+
} else {
1950+
struct_span_err!(
1951+
sess,
1952+
shadower.span,
1953+
E0496,
1954+
"lifetime name `{}` shadows a lifetime name that is already in scope",
1955+
name,
1956+
)
1957+
}
1958+
.forget_guarantee()
1959+
} else {
1960+
// shadowing involving a label is only a warning, due to issues with
1961+
// labels and lifetimes not being macro-hygienic.
1962+
sess.struct_span_warn(
1963+
shadower.span,
1964+
&format!(
1965+
"{} name `{}` shadows a {} name that is already in scope",
1966+
shadower.kind.desc(),
1967+
name,
1968+
orig.kind.desc()
1969+
),
1970+
)
1971+
};
1972+
err.span_label(orig.span, "first declared here");
1973+
err.span_label(shadower.span, format!("{} `{}` already in scope", orig.kind.desc(), name));
1974+
err.emit();
1975+
}
1976+
18951977
impl<'tcx> LifetimeContext<'_, 'tcx> {
18961978
crate fn report_missing_lifetime_specifiers(
18971979
&self,

0 commit comments

Comments
 (0)