Diagnose shadowing on AST.

This commit is contained in:
Camille GILLOT 2021-12-04 21:20:58 +01:00
parent 20976bae5c
commit 6e1b0105c6
3 changed files with 209 additions and 325 deletions

View file

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

View file

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

View file

@ -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(&lt) = 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(&param),
);
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(&param.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(&param),
);
return;
}
old_scope = s;
}
}
}
}
#[tracing::instrument(level = "debug", skip(self))]
fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) {
debug!(