Diagnose shadowing on AST.
This commit is contained in:
parent
20976bae5c
commit
6e1b0105c6
3 changed files with 209 additions and 325 deletions
|
@ -12,6 +12,10 @@ use crate::{path_names_to_string, BindingError, Finalize, LexicalScopeBinding};
|
|||
use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult};
|
||||
use crate::{ResolutionError, Resolver, Segment, UseError};
|
||||
|
||||
use diagnostics::{
|
||||
original_label, original_lifetime, original_lifetime_param, shadower_label, shadower_lifetime,
|
||||
};
|
||||
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
|
||||
use rustc_ast::*;
|
||||
|
@ -172,6 +176,23 @@ impl RibKind<'_> {
|
|||
AssocItemRibKind | ItemRibKind(_) | ForwardGenericParamBanRibKind => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// This rib forbids referring to labels defined in upwards ribs.
|
||||
fn is_label_barrier(self) -> bool {
|
||||
match self {
|
||||
NormalRibKind | MacroDefinition(..) => false,
|
||||
|
||||
AssocItemRibKind
|
||||
| ClosureOrAsyncRibKind
|
||||
| FnItemRibKind
|
||||
| ItemRibKind(..)
|
||||
| ConstantItemRibKind(..)
|
||||
| ModuleRibKind(..)
|
||||
| ForwardGenericParamBanRibKind
|
||||
| ConstParamTyRibKind
|
||||
| InlineAsmSymRibKind => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A single local scope.
|
||||
|
@ -732,7 +753,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
|||
// Create a value rib for the function.
|
||||
self.with_rib(ValueNS, rib_kind, |this| {
|
||||
// Create a label rib for the function.
|
||||
this.with_label_rib(rib_kind, |this| {
|
||||
this.with_label_rib(FnItemRibKind, |this| {
|
||||
let async_node_id = fn_kind.header().and_then(|h| h.asyncness.opt_return_id());
|
||||
|
||||
if let FnKind::Fn(_, _, _, _, generics, _) = fn_kind {
|
||||
|
@ -1585,22 +1606,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
let ribs = &self.label_ribs[rib_index + 1..];
|
||||
|
||||
for rib in ribs {
|
||||
match rib.kind {
|
||||
NormalRibKind | MacroDefinition(..) => {
|
||||
// Nothing to do. Continue.
|
||||
}
|
||||
|
||||
AssocItemRibKind
|
||||
| ClosureOrAsyncRibKind
|
||||
| FnItemRibKind
|
||||
| ItemRibKind(..)
|
||||
| ConstantItemRibKind(..)
|
||||
| ModuleRibKind(..)
|
||||
| ForwardGenericParamBanRibKind
|
||||
| ConstParamTyRibKind
|
||||
| InlineAsmSymRibKind => {
|
||||
return false;
|
||||
}
|
||||
if rib.kind.is_label_barrier() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1895,6 +1902,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
let mut function_value_rib = Rib::new(kind);
|
||||
let mut function_lifetime_rib = LifetimeRib::new(lifetime_kind);
|
||||
let mut seen_bindings = FxHashMap::default();
|
||||
let mut seen_lifetimes = FxHashMap::default();
|
||||
|
||||
// We also can't shadow bindings from the parent item
|
||||
if let AssocItemRibKind = kind {
|
||||
|
@ -1910,20 +1918,52 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
add_bindings_for_ns(TypeNS);
|
||||
}
|
||||
|
||||
// Forbid shadowing lifetime bindings
|
||||
for rib in self.lifetime_ribs.iter().rev() {
|
||||
seen_lifetimes.extend(
|
||||
rib.bindings.iter().map(|(ident, _)| (*ident, original_lifetime(ident.span))),
|
||||
);
|
||||
if let LifetimeRibKind::Item = rib.kind {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for rib in self.label_ribs.iter().rev() {
|
||||
if rib.kind.is_label_barrier() {
|
||||
break;
|
||||
}
|
||||
seen_lifetimes
|
||||
.extend(rib.bindings.iter().map(|(ident, _)| (*ident, original_label(ident.span))));
|
||||
}
|
||||
|
||||
for param in params {
|
||||
let ident = param.ident.normalize_to_macros_2_0();
|
||||
debug!("with_generic_param_rib: {}", param.id);
|
||||
|
||||
match seen_bindings.entry(ident) {
|
||||
Entry::Occupied(entry) => {
|
||||
let span = *entry.get();
|
||||
let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span);
|
||||
if !matches!(param.kind, GenericParamKind::Lifetime) {
|
||||
self.report_error(param.ident.span, err);
|
||||
if let GenericParamKind::Lifetime = param.kind {
|
||||
match seen_lifetimes.entry(ident) {
|
||||
Entry::Occupied(entry) => {
|
||||
let original = *entry.get();
|
||||
diagnostics::signal_shadowing_problem(
|
||||
self.r.session,
|
||||
ident.name,
|
||||
original,
|
||||
shadower_lifetime(param.ident.span),
|
||||
)
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(original_lifetime_param(param.ident.span));
|
||||
}
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(param.ident.span);
|
||||
} else {
|
||||
match seen_bindings.entry(ident) {
|
||||
Entry::Occupied(entry) => {
|
||||
let span = *entry.get();
|
||||
let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span);
|
||||
self.report_error(param.ident.span, err);
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(param.ident.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3114,8 +3154,35 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
if label.ident.as_str().as_bytes()[1] != b'_' {
|
||||
self.diagnostic_metadata.unused_labels.insert(id, label.ident.span);
|
||||
}
|
||||
|
||||
// Forbid shadowing lifetime bindings
|
||||
let ident = label.ident.normalize_to_macro_rules();
|
||||
for rib in self.lifetime_ribs.iter().rev() {
|
||||
if let Some((orig_ident, _)) = rib.bindings.get_key_value(&ident) {
|
||||
diagnostics::signal_shadowing_problem(
|
||||
self.r.session,
|
||||
label.ident.name,
|
||||
original_lifetime(orig_ident.span),
|
||||
shadower_label(label.ident.span),
|
||||
)
|
||||
}
|
||||
}
|
||||
for rib in self.label_ribs.iter_mut().rev() {
|
||||
if let Some((orig_ident, _)) = rib.bindings.get_key_value(&ident) {
|
||||
diagnostics::signal_shadowing_problem(
|
||||
self.r.session,
|
||||
label.ident.name,
|
||||
original_label(orig_ident.span),
|
||||
shadower_label(label.ident.span),
|
||||
)
|
||||
}
|
||||
if rib.kind.is_label_barrier() {
|
||||
rib.bindings.insert(ident, id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.with_label_rib(NormalRibKind, |this| {
|
||||
let ident = label.ident.normalize_to_macro_rules();
|
||||
this.label_ribs.last_mut().unwrap().bindings.insert(ident, id);
|
||||
f(this);
|
||||
});
|
||||
|
|
|
@ -25,6 +25,7 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE};
|
|||
use rustc_hir::PrimTy;
|
||||
use rustc_session::lint;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
use rustc_span::lev_distance::find_best_match_for_name;
|
||||
|
@ -2036,6 +2037,87 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
enum ShadowKind {
|
||||
Label,
|
||||
Lifetime,
|
||||
}
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Original {
|
||||
kind: ShadowKind,
|
||||
span: Span,
|
||||
param: bool,
|
||||
}
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Shadower {
|
||||
kind: ShadowKind,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
pub fn original_label(span: Span) -> Original {
|
||||
Original { kind: ShadowKind::Label, span, param: false }
|
||||
}
|
||||
pub fn shadower_label(span: Span) -> Shadower {
|
||||
Shadower { kind: ShadowKind::Label, span }
|
||||
}
|
||||
pub fn original_lifetime(span: Span) -> Original {
|
||||
Original { kind: ShadowKind::Lifetime, span, param: false }
|
||||
}
|
||||
pub fn original_lifetime_param(span: Span) -> Original {
|
||||
Original { kind: ShadowKind::Lifetime, span, param: true }
|
||||
}
|
||||
pub fn shadower_lifetime(span: Span) -> Shadower {
|
||||
Shadower { kind: ShadowKind::Lifetime, span }
|
||||
}
|
||||
|
||||
impl ShadowKind {
|
||||
fn desc(&self) -> &'static str {
|
||||
match *self {
|
||||
ShadowKind::Label => "label",
|
||||
ShadowKind::Lifetime => "lifetime",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signal_shadowing_problem(sess: &Session, name: Symbol, orig: Original, shadower: Shadower) {
|
||||
let mut err = if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) {
|
||||
// lifetime/lifetime shadowing is an error
|
||||
if orig.param {
|
||||
struct_span_err!(
|
||||
sess,
|
||||
shadower.span,
|
||||
E0263,
|
||||
"lifetime name `{}` declared twice in the same scope",
|
||||
name,
|
||||
)
|
||||
} else {
|
||||
struct_span_err!(
|
||||
sess,
|
||||
shadower.span,
|
||||
E0496,
|
||||
"lifetime name `{}` shadows a lifetime name that is already in scope",
|
||||
name,
|
||||
)
|
||||
}
|
||||
.forget_guarantee()
|
||||
} else {
|
||||
// shadowing involving a label is only a warning, due to issues with
|
||||
// labels and lifetimes not being macro-hygienic.
|
||||
sess.struct_span_warn(
|
||||
shadower.span,
|
||||
&format!(
|
||||
"{} name `{}` shadows a {} name that is already in scope",
|
||||
shadower.kind.desc(),
|
||||
name,
|
||||
orig.kind.desc()
|
||||
),
|
||||
)
|
||||
};
|
||||
err.span_label(orig.span, "first declared here");
|
||||
err.span_label(shadower.span, format!("{} `{}` already in scope", orig.kind.desc(), name));
|
||||
err.emit();
|
||||
}
|
||||
|
||||
impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||
pub(crate) fn report_missing_lifetime_specifiers(
|
||||
&self,
|
||||
|
|
|
@ -23,7 +23,7 @@ use rustc_middle::middle::resolve_lifetime::*;
|
|||
use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::symbol::{kw, sym, Ident};
|
||||
use rustc_span::Span;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
|
@ -161,9 +161,6 @@ pub(crate) struct LifetimeContext<'a, 'tcx> {
|
|||
/// we eventually need lifetimes resolve for trait items.
|
||||
trait_definition_only: bool,
|
||||
|
||||
/// List of labels in the function/method currently under analysis.
|
||||
labels_in_fn: Vec<Ident>,
|
||||
|
||||
/// Cache for cross-crate per-definition object lifetime defaults.
|
||||
xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>,
|
||||
|
||||
|
@ -434,7 +431,6 @@ fn do_resolve(
|
|||
map: &mut named_region_map,
|
||||
scope: ROOT_SCOPE,
|
||||
trait_definition_only,
|
||||
labels_in_fn: vec![],
|
||||
xcrate_object_lifetime_defaults: Default::default(),
|
||||
missing_named_lifetime_spots: vec![],
|
||||
};
|
||||
|
@ -641,14 +637,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn visit_nested_body(&mut self, body: hir::BodyId) {
|
||||
// Each body has their own set of labels, save labels.
|
||||
let saved = take(&mut self.labels_in_fn);
|
||||
let body = self.tcx.hir().body(body);
|
||||
extract_labels(self, body);
|
||||
self.with(Scope::Body { id: body.id(), s: self.scope }, |_, this| {
|
||||
self.with(Scope::Body { id: body.id(), s: self.scope }, |this| {
|
||||
this.visit_body(body);
|
||||
});
|
||||
self.labels_in_fn = saved;
|
||||
}
|
||||
|
||||
fn visit_fn(
|
||||
|
@ -683,9 +675,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: true,
|
||||
};
|
||||
self.with(scope, move |_old_scope, this| {
|
||||
intravisit::walk_fn(this, fk, fd, b, s, hir_id)
|
||||
});
|
||||
self.with(scope, move |this| intravisit::walk_fn(this, fk, fd, b, s, hir_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -720,7 +710,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => {
|
||||
// No lifetime parameters, but implied 'static.
|
||||
let scope = Scope::Elision { elide: Elide::Exact(Region::Static), s: ROOT_SCOPE };
|
||||
self.with(scope, |_, this| intravisit::walk_item(this, item));
|
||||
self.with(scope, |this| intravisit::walk_item(this, item));
|
||||
}
|
||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => {
|
||||
// Opaque types are visited when we visit the
|
||||
|
@ -807,10 +797,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
s: ROOT_SCOPE,
|
||||
allow_late_bound: false,
|
||||
};
|
||||
self.with(scope, |old_scope, this| {
|
||||
this.check_lifetime_params(old_scope, &generics.params);
|
||||
self.with(scope, |this| {
|
||||
let scope = Scope::TraitRefBoundary { s: this.scope };
|
||||
this.with(scope, |_, this| {
|
||||
this.with(scope, |this| {
|
||||
intravisit::walk_item(this, item);
|
||||
});
|
||||
});
|
||||
|
@ -873,10 +862,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: true,
|
||||
};
|
||||
self.with(scope, |old_scope, this| {
|
||||
self.with(scope, |this| {
|
||||
// a bare fn has no bounds, so everything
|
||||
// contained within is scoped within its binder.
|
||||
this.check_lifetime_params(old_scope, &c.generic_params);
|
||||
intravisit::walk_ty(this, ty);
|
||||
});
|
||||
self.missing_named_lifetime_spots.pop();
|
||||
|
@ -884,7 +872,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
|
||||
debug!(?bounds, ?lifetime, "TraitObject");
|
||||
let scope = Scope::TraitRefBoundary { s: self.scope };
|
||||
self.with(scope, |_, this| {
|
||||
self.with(scope, |this| {
|
||||
for bound in bounds {
|
||||
this.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
|
||||
}
|
||||
|
@ -923,7 +911,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
lifetime: self.map.defs.get(&lifetime_ref.hir_id).cloned(),
|
||||
s: self.scope,
|
||||
};
|
||||
self.with(scope, |_, this| this.visit_ty(&mt.ty));
|
||||
self.with(scope, |this| this.visit_ty(&mt.ty));
|
||||
}
|
||||
hir::TyKind::OpaqueDef(item_id, lifetimes) => {
|
||||
// Resolve the lifetimes in the bounds to the lifetime defs in the generics.
|
||||
|
@ -944,9 +932,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
// Elided lifetimes are not allowed in non-return
|
||||
// position impl Trait
|
||||
let scope = Scope::TraitRefBoundary { s: self.scope };
|
||||
self.with(scope, |_, this| {
|
||||
self.with(scope, |this| {
|
||||
let scope = Scope::Elision { elide: Elide::Forbid, s: this.scope };
|
||||
this.with(scope, |_, this| {
|
||||
this.with(scope, |this| {
|
||||
intravisit::walk_item(this, opaque_ty);
|
||||
})
|
||||
});
|
||||
|
@ -1052,7 +1040,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
if let Some(elision_region) = elision {
|
||||
let scope =
|
||||
Scope::Elision { elide: Elide::Exact(elision_region), s: self.scope };
|
||||
self.with(scope, |_old_scope, this| {
|
||||
self.with(scope, |this| {
|
||||
let scope = Scope::Binder {
|
||||
hir_id: ty.hir_id,
|
||||
lifetimes,
|
||||
|
@ -1062,10 +1050,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: false,
|
||||
};
|
||||
this.with(scope, |_old_scope, this| {
|
||||
this.with(scope, |this| {
|
||||
this.visit_generics(generics);
|
||||
let scope = Scope::TraitRefBoundary { s: this.scope };
|
||||
this.with(scope, |_, this| {
|
||||
this.with(scope, |this| {
|
||||
for bound in bounds {
|
||||
this.visit_param_bound(bound);
|
||||
}
|
||||
|
@ -1082,9 +1070,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: false,
|
||||
};
|
||||
self.with(scope, |_old_scope, this| {
|
||||
self.with(scope, |this| {
|
||||
let scope = Scope::TraitRefBoundary { s: this.scope };
|
||||
this.with(scope, |_, this| {
|
||||
this.with(scope, |this| {
|
||||
this.visit_generics(generics);
|
||||
for bound in bounds {
|
||||
this.visit_param_bound(bound);
|
||||
|
@ -1141,10 +1129,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: false,
|
||||
};
|
||||
self.with(scope, |old_scope, this| {
|
||||
this.check_lifetime_params(old_scope, &generics.params);
|
||||
self.with(scope, |this| {
|
||||
let scope = Scope::TraitRefBoundary { s: this.scope };
|
||||
this.with(scope, |_, this| {
|
||||
this.with(scope, |this| {
|
||||
this.visit_generics(generics);
|
||||
for bound in bounds {
|
||||
this.visit_param_bound(bound);
|
||||
|
@ -1210,10 +1197,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: true,
|
||||
};
|
||||
self.with(scope, |old_scope, this| {
|
||||
this.check_lifetime_params(old_scope, &generics.params);
|
||||
self.with(scope, |this| {
|
||||
let scope = Scope::TraitRefBoundary { s: this.scope };
|
||||
this.with(scope, |_, this| {
|
||||
this.with(scope, |this| {
|
||||
this.visit_generics(generics);
|
||||
this.visit_ty(ty);
|
||||
})
|
||||
|
@ -1300,7 +1286,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
|
||||
fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
|
||||
let scope = Scope::TraitRefBoundary { s: self.scope };
|
||||
self.with(scope, |_, this| {
|
||||
self.with(scope, |this| {
|
||||
for param in generics.params {
|
||||
match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => {}
|
||||
|
@ -1354,8 +1340,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: true,
|
||||
};
|
||||
this.with(scope, |old_scope, this| {
|
||||
this.check_lifetime_params(old_scope, &bound_generic_params);
|
||||
this.with(scope, |this| {
|
||||
this.visit_ty(&bounded_ty);
|
||||
walk_list!(this, visit_param_bound, bounds);
|
||||
})
|
||||
|
@ -1427,7 +1412,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
scope_type,
|
||||
allow_late_bound: true,
|
||||
};
|
||||
self.with(scope, |_, this| {
|
||||
self.with(scope, |this| {
|
||||
intravisit::walk_param_bound(this, bound);
|
||||
});
|
||||
}
|
||||
|
@ -1479,8 +1464,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
scope_type,
|
||||
allow_late_bound: true,
|
||||
};
|
||||
self.with(scope, |old_scope, this| {
|
||||
this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
|
||||
self.with(scope, |this| {
|
||||
walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
|
||||
this.visit_trait_ref(&trait_ref.trait_ref);
|
||||
});
|
||||
|
@ -1491,154 +1475,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
enum ShadowKind {
|
||||
Label,
|
||||
Lifetime,
|
||||
}
|
||||
struct Original {
|
||||
kind: ShadowKind,
|
||||
span: Span,
|
||||
}
|
||||
struct Shadower {
|
||||
kind: ShadowKind,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
fn original_label(span: Span) -> Original {
|
||||
Original { kind: ShadowKind::Label, span }
|
||||
}
|
||||
fn shadower_label(span: Span) -> Shadower {
|
||||
Shadower { kind: ShadowKind::Label, span }
|
||||
}
|
||||
fn original_lifetime(span: Span) -> Original {
|
||||
Original { kind: ShadowKind::Lifetime, span }
|
||||
}
|
||||
fn shadower_lifetime(param: &hir::GenericParam<'_>) -> Shadower {
|
||||
Shadower { kind: ShadowKind::Lifetime, span: param.span }
|
||||
}
|
||||
|
||||
impl ShadowKind {
|
||||
fn desc(&self) -> &'static str {
|
||||
match *self {
|
||||
ShadowKind::Label => "label",
|
||||
ShadowKind::Lifetime => "lifetime",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn signal_shadowing_problem(tcx: TyCtxt<'_>, name: Symbol, orig: Original, shadower: Shadower) {
|
||||
let mut err = if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) {
|
||||
// lifetime/lifetime shadowing is an error
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
shadower.span,
|
||||
E0496,
|
||||
"{} name `{}` shadows a \
|
||||
{} name that is already in scope",
|
||||
shadower.kind.desc(),
|
||||
name,
|
||||
orig.kind.desc()
|
||||
)
|
||||
.forget_guarantee()
|
||||
} else {
|
||||
// shadowing involving a label is only a warning, due to issues with
|
||||
// labels and lifetimes not being macro-hygienic.
|
||||
tcx.sess.struct_span_warn(
|
||||
shadower.span,
|
||||
&format!(
|
||||
"{} name `{}` shadows a \
|
||||
{} name that is already in scope",
|
||||
shadower.kind.desc(),
|
||||
name,
|
||||
orig.kind.desc()
|
||||
),
|
||||
)
|
||||
};
|
||||
err.span_label(orig.span, "first declared here");
|
||||
err.span_label(shadower.span, format!("{} `{}` already in scope", orig.kind.desc(), name));
|
||||
err.emit();
|
||||
}
|
||||
|
||||
// Adds all labels in `b` to `ctxt.labels_in_fn`, signalling a warning
|
||||
// if one of the label shadows a lifetime or another label.
|
||||
fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body<'_>) {
|
||||
struct GatherLabels<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
scope: ScopeRef<'a>,
|
||||
labels_in_fn: &'a mut Vec<Ident>,
|
||||
}
|
||||
|
||||
let mut gather =
|
||||
GatherLabels { tcx: ctxt.tcx, scope: ctxt.scope, labels_in_fn: &mut ctxt.labels_in_fn };
|
||||
gather.visit_body(body);
|
||||
|
||||
impl<'v, 'a, 'tcx> Visitor<'v> for GatherLabels<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, ex: &hir::Expr<'_>) {
|
||||
if let Some(label) = expression_label(ex) {
|
||||
for prior_label in &self.labels_in_fn[..] {
|
||||
// FIXME (#24278): non-hygienic comparison
|
||||
if label.name == prior_label.name {
|
||||
signal_shadowing_problem(
|
||||
self.tcx,
|
||||
label.name,
|
||||
original_label(prior_label.span),
|
||||
shadower_label(label.span),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
check_if_label_shadows_lifetime(self.tcx, self.scope, label);
|
||||
|
||||
self.labels_in_fn.push(label);
|
||||
}
|
||||
intravisit::walk_expr(self, ex)
|
||||
}
|
||||
}
|
||||
|
||||
fn expression_label(ex: &hir::Expr<'_>) -> Option<Ident> {
|
||||
match ex.kind {
|
||||
hir::ExprKind::Loop(_, Some(label), ..) => Some(label.ident),
|
||||
hir::ExprKind::Block(_, Some(label)) => Some(label.ident),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_if_label_shadows_lifetime(tcx: TyCtxt<'_>, mut scope: ScopeRef<'_>, label: Ident) {
|
||||
loop {
|
||||
match *scope {
|
||||
Scope::Body { s, .. }
|
||||
| Scope::Elision { s, .. }
|
||||
| Scope::ObjectLifetimeDefault { s, .. }
|
||||
| Scope::Supertrait { s, .. }
|
||||
| Scope::TraitRefBoundary { s, .. } => {
|
||||
scope = s;
|
||||
}
|
||||
|
||||
Scope::Root => {
|
||||
return;
|
||||
}
|
||||
|
||||
Scope::Binder { ref lifetimes, s, .. } => {
|
||||
// FIXME (#24278): non-hygienic comparison
|
||||
if let Some(def) =
|
||||
lifetimes.get(&hir::ParamName::Plain(label.normalize_to_macros_2_0()))
|
||||
{
|
||||
signal_shadowing_problem(
|
||||
tcx,
|
||||
label.name,
|
||||
original_lifetime(tcx.def_span(def.id().unwrap().expect_local())),
|
||||
shadower_label(label.span),
|
||||
);
|
||||
return;
|
||||
}
|
||||
scope = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_object_lifetime_defaults<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item: &hir::Item<'_>,
|
||||
|
@ -1774,10 +1610,9 @@ fn object_lifetime_defaults_for_item<'tcx>(
|
|||
impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
fn with<F>(&mut self, wrap_scope: Scope<'_>, f: F)
|
||||
where
|
||||
F: for<'b> FnOnce(ScopeRef<'_>, &mut LifetimeContext<'b, 'tcx>),
|
||||
F: for<'b> FnOnce(&mut LifetimeContext<'b, 'tcx>),
|
||||
{
|
||||
let LifetimeContext { tcx, map, .. } = self;
|
||||
let labels_in_fn = take(&mut self.labels_in_fn);
|
||||
let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults);
|
||||
let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots);
|
||||
let mut this = LifetimeContext {
|
||||
|
@ -1785,16 +1620,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
map,
|
||||
scope: &wrap_scope,
|
||||
trait_definition_only: self.trait_definition_only,
|
||||
labels_in_fn,
|
||||
xcrate_object_lifetime_defaults,
|
||||
missing_named_lifetime_spots,
|
||||
};
|
||||
let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope));
|
||||
{
|
||||
let _enter = span.enter();
|
||||
f(self.scope, &mut this);
|
||||
f(&mut this);
|
||||
}
|
||||
self.labels_in_fn = this.labels_in_fn;
|
||||
self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
|
||||
self.missing_named_lifetime_spots = this.missing_named_lifetime_spots;
|
||||
}
|
||||
|
@ -1891,10 +1724,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
scope_type: BinderScopeType::Normal,
|
||||
allow_late_bound: true,
|
||||
};
|
||||
self.with(scope, move |old_scope, this| {
|
||||
this.check_lifetime_params(old_scope, &generics.params);
|
||||
walk(this);
|
||||
});
|
||||
self.with(scope, walk);
|
||||
}
|
||||
|
||||
fn next_early_index_helper(&self, only_opaque_type_parent: bool) -> u32 {
|
||||
|
@ -2165,7 +1995,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
GenericArg::Type(ty) => {
|
||||
if let Some(<) = object_lifetime_defaults.get(i) {
|
||||
let scope = Scope::ObjectLifetimeDefault { lifetime: lt, s: self.scope };
|
||||
self.with(scope, |_, this| this.visit_ty(ty));
|
||||
self.with(scope, |this| this.visit_ty(ty));
|
||||
} else {
|
||||
self.visit_ty(ty);
|
||||
}
|
||||
|
@ -2222,15 +2052,15 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
type_def_id,
|
||||
binding.ident,
|
||||
);
|
||||
self.with(scope, |_, this| {
|
||||
self.with(scope, |this| {
|
||||
let scope = Scope::Supertrait {
|
||||
lifetimes: lifetimes.unwrap_or_default(),
|
||||
s: this.scope,
|
||||
};
|
||||
this.with(scope, |_, this| this.visit_assoc_type_binding(binding));
|
||||
this.with(scope, |this| this.visit_assoc_type_binding(binding));
|
||||
});
|
||||
} else {
|
||||
self.with(scope, |_, this| this.visit_assoc_type_binding(binding));
|
||||
self.with(scope, |this| this.visit_assoc_type_binding(binding));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2346,7 +2176,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
elide: Elide::FreshLateAnon(named_late_bound_vars, Cell::new(0)),
|
||||
s: self.scope,
|
||||
};
|
||||
self.with(arg_scope, |_, this| {
|
||||
self.with(arg_scope, |this| {
|
||||
for input in inputs {
|
||||
this.visit_ty(input);
|
||||
}
|
||||
|
@ -2466,7 +2296,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
visitor.visit_ty(&inputs[0]);
|
||||
if let Set1::One(lifetime) = visitor.lifetime {
|
||||
let scope = Scope::Elision { elide: Elide::Exact(lifetime), s: self.scope };
|
||||
self.with(scope, |_, this| this.visit_ty(output));
|
||||
self.with(scope, |this| this.visit_ty(output));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2517,7 +2347,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
debug!(?elide);
|
||||
|
||||
let scope = Scope::Elision { elide, s: self.scope };
|
||||
self.with(scope, |_, this| this.visit_ty(output));
|
||||
self.with(scope, |this| this.visit_ty(output));
|
||||
|
||||
struct GatherLifetimes<'a> {
|
||||
map: &'a NamedRegionMap,
|
||||
|
@ -2789,101 +2619,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth));
|
||||
}
|
||||
|
||||
fn check_lifetime_params(
|
||||
&mut self,
|
||||
old_scope: ScopeRef<'_>,
|
||||
params: &'tcx [hir::GenericParam<'tcx>],
|
||||
) {
|
||||
let lifetimes: Vec<_> = params
|
||||
.iter()
|
||||
.filter_map(|param| match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => {
|
||||
Some((param, param.name.normalize_to_macros_2_0()))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
for (i, (lifetime_i, lifetime_i_name)) in lifetimes.iter().enumerate() {
|
||||
if let hir::ParamName::Plain(_) = lifetime_i_name {
|
||||
let name = lifetime_i_name.ident().name;
|
||||
if name == kw::UnderscoreLifetime || name == kw::StaticLifetime {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
lifetime_i.span,
|
||||
&format!("invalid lifetime parameter name: `{}`", lifetime_i.name.ident()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// It is a hard error to shadow a lifetime within the same scope.
|
||||
for (lifetime_j, lifetime_j_name) in lifetimes.iter().skip(i + 1) {
|
||||
if lifetime_i_name == lifetime_j_name {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
lifetime_j.span,
|
||||
E0263,
|
||||
"lifetime name `{}` declared twice in the same scope",
|
||||
lifetime_j.name.ident()
|
||||
)
|
||||
.span_label(lifetime_j.span, "declared twice")
|
||||
.span_label(lifetime_i.span, "previous declaration here")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
// It is a soft error to shadow a lifetime within a parent scope.
|
||||
self.check_lifetime_param_for_shadowing(old_scope, &lifetime_i);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_lifetime_param_for_shadowing(
|
||||
&self,
|
||||
mut old_scope: ScopeRef<'_>,
|
||||
param: &'tcx hir::GenericParam<'tcx>,
|
||||
) {
|
||||
for label in &self.labels_in_fn {
|
||||
// FIXME (#24278): non-hygienic comparison
|
||||
if param.name.ident().name == label.name {
|
||||
signal_shadowing_problem(
|
||||
self.tcx,
|
||||
label.name,
|
||||
original_label(label.span),
|
||||
shadower_lifetime(¶m),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
match *old_scope {
|
||||
Scope::Body { s, .. }
|
||||
| Scope::Elision { s, .. }
|
||||
| Scope::ObjectLifetimeDefault { s, .. }
|
||||
| Scope::Supertrait { s, .. }
|
||||
| Scope::TraitRefBoundary { s, .. } => {
|
||||
old_scope = s;
|
||||
}
|
||||
|
||||
Scope::Root => {
|
||||
return;
|
||||
}
|
||||
|
||||
Scope::Binder { ref lifetimes, s, .. } => {
|
||||
if let Some(&def) = lifetimes.get(¶m.name.normalize_to_macros_2_0()) {
|
||||
signal_shadowing_problem(
|
||||
self.tcx,
|
||||
param.name.ident().name,
|
||||
original_lifetime(self.tcx.def_span(def.id().unwrap())),
|
||||
shadower_lifetime(¶m),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
old_scope = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) {
|
||||
debug!(
|
||||
|
|
Loading…
Add table
Reference in a new issue