From 5953c57f27cdb41d86530e7038d7f191e3f1c74e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 10 May 2022 19:56:46 +0200 Subject: [PATCH 1/3] Introduce LifetimeCtxt. --- compiler/rustc_ast/src/visit.rs | 22 ++++++++++++++----- .../rustc_ast_passes/src/ast_validation.rs | 2 +- compiler/rustc_ast_passes/src/node_count.rs | 2 +- compiler/rustc_lint/src/early.rs | 2 +- compiler/rustc_passes/src/hir_stats.rs | 2 +- compiler/rustc_resolve/src/late.rs | 4 ++-- 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 42213cf69661b..2ce8590d7718f 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -89,6 +89,16 @@ impl<'a> FnKind<'a> { } } +#[derive(Copy, Clone, Debug)] +pub enum LifetimeCtxt { + /// Appears in a reference type. + Rptr, + /// Appears as a bound on a type or another lifetime. + Bound, + /// Appears as a generic argument. + GenericArg, +} + /// Each method of the `Visitor` trait is a hook to be potentially /// overridden. Each method's default implementation recursively visits /// the substructure of the input via the corresponding `walk` method; @@ -184,7 +194,7 @@ pub trait Visitor<'ast>: Sized { fn visit_label(&mut self, label: &'ast Label) { walk_label(self, label) } - fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) { + fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: LifetimeCtxt) { walk_lifetime(self, lifetime) } fn visit_mac_call(&mut self, mac: &'ast MacCall) { @@ -414,7 +424,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { TyKind::Slice(ref ty) | TyKind::Paren(ref ty) => visitor.visit_ty(ty), TyKind::Ptr(ref mutable_type) => visitor.visit_ty(&mutable_type.ty), TyKind::Rptr(ref opt_lifetime, ref mutable_type) => { - walk_list!(visitor, visit_lifetime, opt_lifetime); + walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Rptr); visitor.visit_ty(&mutable_type.ty) } TyKind::Tup(ref tuple_element_types) => { @@ -507,7 +517,7 @@ where V: Visitor<'a>, { match generic_arg { - GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt), + GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt, LifetimeCtxt::GenericArg), GenericArg::Type(ty) => visitor.visit_ty(ty), GenericArg::Const(ct) => visitor.visit_anon_const(ct), } @@ -599,7 +609,9 @@ pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignI pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) { match *bound { GenericBound::Trait(ref typ, ref modifier) => visitor.visit_poly_trait_ref(typ, modifier), - GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime), + GenericBound::Outlives(ref lifetime) => { + visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound) + } } } @@ -639,7 +651,7 @@ pub fn walk_where_predicate<'a, V: Visitor<'a>>(visitor: &mut V, predicate: &'a WherePredicate::RegionPredicate(WhereRegionPredicate { ref lifetime, ref bounds, .. }) => { - visitor.visit_lifetime(lifetime); + visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound); walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); } WherePredicate::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. }) => { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 058a0f975a7b3..14e5d2ae62337 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1070,7 +1070,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_label(self, label); } - fn visit_lifetime(&mut self, lifetime: &'a Lifetime) { + fn visit_lifetime(&mut self, lifetime: &'a Lifetime, _: visit::LifetimeCtxt) { self.check_lifetime(lifetime.ident); visit::walk_lifetime(self, lifetime); } diff --git a/compiler/rustc_ast_passes/src/node_count.rs b/compiler/rustc_ast_passes/src/node_count.rs index 48b79809c1b97..ee166f7570307 100644 --- a/compiler/rustc_ast_passes/src/node_count.rs +++ b/compiler/rustc_ast_passes/src/node_count.rs @@ -106,7 +106,7 @@ impl<'ast> Visitor<'ast> for NodeCounter { self.count += 1; walk_variant(self, v) } - fn visit_lifetime(&mut self, lifetime: &Lifetime) { + fn visit_lifetime(&mut self, lifetime: &Lifetime, _: visit::LifetimeCtxt) { self.count += 1; walk_lifetime(self, lifetime) } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 3ea68aea3cb5d..fecbd4ae544ba 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -272,7 +272,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> }); } - fn visit_lifetime(&mut self, lt: &'a ast::Lifetime) { + fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) { run_early_pass!(self, check_lifetime, lt); self.check_id(lt.id); } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 237a8abfabe7e..6a234294ed161 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -318,7 +318,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_variant(self, v) } - fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime) { + fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime, _: ast_visit::LifetimeCtxt) { self.record("Lifetime", Id::None, lifetime); ast_visit::walk_lifetime(self, lifetime) } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2712bfeb7b311..33449a1ef9ed1 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -793,7 +793,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }); self.diagnostic_metadata.current_function = previous_value; } - fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) { + fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: visit::LifetimeCtxt) { self.resolve_lifetime(lifetime) } @@ -859,7 +859,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self.visit_ty(ty); } - GenericArg::Lifetime(lt) => self.visit_lifetime(lt), + GenericArg::Lifetime(lt) => self.visit_lifetime(lt, visit::LifetimeCtxt::GenericArg), GenericArg::Const(ct) => self.visit_anon_const(ct), } self.diagnostic_metadata.currently_processing_generics = prev; From db8a9274a9e3feb90c3db5f7046f9b3566867f5a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 10 May 2022 21:17:21 +0200 Subject: [PATCH 2/3] Introduce BareFnTy::decl_span and fix generics span. --- compiler/rustc_ast/src/ast.rs | 2 ++ compiler/rustc_ast/src/mut_visit.rs | 3 ++- compiler/rustc_parse/src/parser/ty.rs | 4 +++- compiler/rustc_resolve/src/late.rs | 15 +++------------ 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5a4c997ed9bfc..0deeb6849a216 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1976,6 +1976,8 @@ pub struct BareFnTy { pub ext: Extern, pub generic_params: Vec, pub decl: P, + /// Span of the `fn(...) -> ...` part. + pub decl_span: Span, } /// The various kinds of type recognized by the compiler. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index b425b5e2cca52..1a93da8788a1f 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -460,10 +460,11 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { vis.visit_mt(mt); } TyKind::BareFn(bft) => { - let BareFnTy { unsafety, ext: _, generic_params, decl } = bft.deref_mut(); + let BareFnTy { unsafety, ext: _, generic_params, decl, decl_span } = bft.deref_mut(); visit_unsafety(unsafety, vis); generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param)); vis.visit_fn_decl(decl); + vis.visit_span(decl_span); } TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)), TyKind::Paren(ty) => vis.visit_ty(ty), diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index b0439a5987afa..fb3f5eb3f9fe1 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -518,6 +518,7 @@ impl<'a> Parser<'a> { kind: rustc_ast::VisibilityKind::Inherited, tokens: None, }; + let span_start = self.token.span; let ast::FnHeader { ext, unsafety, constness, asyncness } = self.parse_fn_front_matter(&inherited_vis)?; let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; @@ -531,7 +532,8 @@ impl<'a> Parser<'a> { if let ast::Async::Yes { span, .. } = asyncness { self.error_fn_ptr_bad_qualifier(whole_span, span, "async"); } - Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl }))) + let decl_span = span_start.to(self.token.span); + Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span }))) } /// Emit an error for the given bad function pointer qualifier. diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 33449a1ef9ed1..bf7bdecf1f4dc 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -594,11 +594,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self.diagnostic_metadata.current_trait_object = Some(&bounds[..]); } TyKind::BareFn(ref bare_fn) => { - let span = if bare_fn.generic_params.is_empty() { - ty.span.shrink_to_lo() - } else { - ty.span - }; + let span = ty.span.shrink_to_lo().to(bare_fn.decl_span.shrink_to_lo()); self.with_generic_param_rib( &bare_fn.generic_params, NormalRibKind, @@ -627,8 +623,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self.diagnostic_metadata.current_type_path = prev_ty; } fn visit_poly_trait_ref(&mut self, tref: &'ast PolyTraitRef, _: &'ast TraitBoundModifier) { - let span = - if tref.bound_generic_params.is_empty() { tref.span.shrink_to_lo() } else { tref.span }; + let span = tref.span.shrink_to_lo().to(tref.trait_ref.path.span.shrink_to_lo()); self.with_generic_param_rib( &tref.bound_generic_params, NormalRibKind, @@ -890,11 +885,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { .. }) = p { - let span = if bound_generic_params.is_empty() { - predicate_span.shrink_to_lo() - } else { - *predicate_span - }; + let span = predicate_span.shrink_to_lo().to(bounded_ty.span.shrink_to_lo()); this.with_generic_param_rib( &bound_generic_params, NormalRibKind, From 563916d698380f3773da38b1ebff00ef2842e781 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 10 May 2022 21:15:30 +0200 Subject: [PATCH 3/3] Lint single-use-lifetimes on the AST. --- compiler/rustc_lint/src/context.rs | 37 ++ compiler/rustc_lint/src/early.rs | 1 + compiler/rustc_lint/src/levels.rs | 10 +- compiler/rustc_lint_defs/src/lib.rs | 16 +- compiler/rustc_resolve/src/late.rs | 178 +++++--- .../rustc_resolve/src/late/diagnostics.rs | 78 +++- compiler/rustc_resolve/src/late/lifetimes.rs | 379 +----------------- src/test/ui/async-await/unused-lifetime.rs | 16 +- .../ui/async-await/unused-lifetime.stderr | 34 +- .../ui/single-use-lifetime/fn-types.stderr | 5 + .../one-use-in-fn-argument.rs | 13 +- .../one-use-in-fn-return.rs | 6 + .../one-use-in-inherent-impl-header.stderr | 5 + .../one-use-in-inherent-method-argument.rs | 1 + ...one-use-in-inherent-method-argument.stderr | 34 +- .../one-use-in-inherent-method-return.stderr | 5 + .../single-use-lifetime/one-use-in-struct.rs | 3 +- .../one-use-in-trait-method-argument.rs | 7 +- ...inherent-method-argument-and-return.stderr | 5 + 19 files changed, 364 insertions(+), 469 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d7cd5ec04f323..2c6bdef361aab 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -819,6 +819,43 @@ pub trait LintContext: Sized { "see issue #89122 for more information", ); }, + BuiltinLintDiagnostics::SingleUseLifetime { + param_span, + use_span: Some((use_span, elide)), + deletion_span, + } => { + debug!(?param_span, ?use_span, ?deletion_span); + db.span_label(param_span, "this lifetime..."); + db.span_label(use_span, "...is used only here"); + let msg = "elide the single-use lifetime"; + let (use_span, replace_lt) = if elide { + let use_span = sess.source_map().span_extend_while( + use_span, + char::is_whitespace, + ).unwrap_or(use_span); + (use_span, String::new()) + } else { + (use_span, "'_".to_owned()) + }; + db.multipart_suggestion( + msg, + vec![(deletion_span, String::new()), (use_span, replace_lt)], + Applicability::MachineApplicable, + ); + }, + BuiltinLintDiagnostics::SingleUseLifetime { + param_span: _, + use_span: None, + deletion_span, + } => { + debug!(?deletion_span); + db.span_suggestion( + deletion_span, + "elide the unused lifetime", + String::new(), + Applicability::MachineApplicable, + ); + }, } // Rewrap `db`, and pass control to the user. decorate(LintDiagnosticBuilder::new(db)); diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index fecbd4ae544ba..8e505152fc4a0 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -239,6 +239,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_generic_param(&mut self, param: &'a ast::GenericParam) { run_early_pass!(self, check_generic_param, param); + self.check_id(param.id); ast_visit::walk_generic_param(self, param); } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 257549bf1a1a4..54f2c72527924 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -14,7 +14,7 @@ use rustc_middle::lint::{ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::lint::{ - builtin::{self, FORBIDDEN_LINT_GROUPS, UNFULFILLED_LINT_EXPECTATIONS}, + builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS}, Level, Lint, LintExpectationId, LintId, }; use rustc_session::parse::{add_feature_diagnostics, feature_err}; @@ -259,6 +259,14 @@ impl<'s> LintLevelsBuilder<'s> { let sess = self.sess; let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input"); for (attr_index, attr) in attrs.iter().enumerate() { + if attr.has_name(sym::automatically_derived) { + self.current_specs_mut().insert( + LintId::of(SINGLE_USE_LIFETIMES), + (Level::Allow, LintLevelSource::Default), + ); + continue; + } + let level = match Level::from_attr(attr) { None => continue, Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 57b4f96dc100d..e50abf6cf2559 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -423,7 +423,11 @@ pub enum BuiltinLintDiagnostics { DeprecatedMacro(Option, Span), MissingAbi(Span, Abi), UnusedDocComment(Span), - UnusedBuiltinAttribute { attr_name: Symbol, macro_name: String, invoc_span: Span }, + UnusedBuiltinAttribute { + attr_name: Symbol, + macro_name: String, + invoc_span: Span, + }, PatternsInFnsWithoutBody(Span, Ident), LegacyDeriveHelpers(Span), ProcMacroBackCompat(String), @@ -435,6 +439,16 @@ pub enum BuiltinLintDiagnostics { UnicodeTextFlow(Span, String), UnexpectedCfg((Symbol, Span), Option<(Symbol, Span)>), DeprecatedWhereclauseLocation(Span, String), + SingleUseLifetime { + /// Span of the parameter which declares this lifetime. + param_span: Span, + /// Span of the code that should be removed when eliding this lifetime. + /// This span should include leading or trailing comma. + deletion_span: Span, + /// Span of the single use, or None if the lifetime is never used. + /// If true, the lifetime will be fully elided. + use_span: Option<(Span, bool)>, + }, } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index bf7bdecf1f4dc..f05090d046f39 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! "Late resolution" is the pass that resolves most of names in a crate beside imports and macros. //! It runs when the crate is fully expanded and its module structure is fully built. //! So it just walks through the crate and resolves all the expressions, types, etc. @@ -19,7 +20,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::DiagnosticId; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, PartialRes, PerNS}; -use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; +use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::definitions::DefPathData; use rustc_hir::{PrimTy, TraitCandidate}; use rustc_index::vec::Idx; @@ -197,13 +198,19 @@ impl<'a, R> Rib<'a, R> { } } +#[derive(Clone, Copy, Debug)] +enum LifetimeUseSet { + One { use_span: Span, use_ctxt: visit::LifetimeCtxt }, + Many, +} + #[derive(Copy, Clone, Debug)] enum LifetimeRibKind { /// This rib acts as a barrier to forbid reference to lifetimes of a parent item. Item, /// This rib declares generic parameters. - Generics { parent: NodeId, span: Span, kind: LifetimeBinderKind }, + Generics { binder: NodeId, span: Span, kind: LifetimeBinderKind }, /// FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const /// generics. We are disallowing this until we can decide on how we want to handle non-'static @@ -230,7 +237,7 @@ enum LifetimeRibKind { AnonymousReportError, /// Pass responsibility to `resolve_lifetime` code for all cases. - AnonymousPassThrough(NodeId), + AnonymousPassThrough(NodeId, /* in_fn_return */ bool), } #[derive(Copy, Clone, Debug)] @@ -519,6 +526,9 @@ struct LateResolutionVisitor<'a, 'b, 'ast> { /// In most cases this will be `None`, in which case errors will always be reported. /// If it is `true`, then it will be updated when entering a nested function or trait body. in_func_body: bool, + + /// Count the number of places a lifetime is used. + lifetime_uses: FxHashMap, } /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes. @@ -599,17 +609,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { &bare_fn.generic_params, NormalRibKind, LifetimeRibKind::Generics { - parent: ty.id, + binder: ty.id, kind: LifetimeBinderKind::BareFnType, span, }, |this| { + this.visit_generic_param_vec(&bare_fn.generic_params, false); this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(ty.id), - |this| { - this.visit_generic_param_vec(&bare_fn.generic_params, false); - visit::walk_fn_decl(this, &bare_fn.decl); - }, + LifetimeRibKind::AnonymousPassThrough(ty.id, false), + |this| walk_list!(this, visit_param, &bare_fn.decl.inputs), + ); + this.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(ty.id, true), + |this| this.visit_fn_ret_ty(&bare_fn.decl.output), ); }, ); @@ -628,7 +640,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { &tref.bound_generic_params, NormalRibKind, LifetimeRibKind::Generics { - parent: tref.trait_ref.ref_id, + binder: tref.trait_ref.ref_id, kind: LifetimeBinderKind::PolyTrait, span, }, @@ -652,7 +664,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: foreign_item.id, + binder: foreign_item.id, kind: LifetimeBinderKind::Item, span: generics.span, }, @@ -666,7 +678,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: foreign_item.id, + binder: foreign_item.id, kind: LifetimeBinderKind::Function, span: generics.span, }, @@ -690,13 +702,20 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // a body, or if there's no body for some other reason. FnKind::Fn(FnCtxt::Foreign, _, sig, _, generics, _) | FnKind::Fn(_, _, sig, _, generics, None) => { - self.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| { - // We don't need to deal with patterns in parameters, because - // they are not possible for foreign or bodiless functions. - this.visit_fn_header(&sig.header); - this.visit_generics(generics); - visit::walk_fn_decl(this, &sig.decl); - }); + // We don't need to deal with patterns in parameters, because + // they are not possible for foreign or bodiless functions. + self.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(fn_id, false), + |this| { + this.visit_fn_header(&sig.header); + this.visit_generics(generics); + walk_list!(this, visit_param, &sig.decl.inputs); + }, + ); + self.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(fn_id, true), + |this| this.visit_fn_ret_ty(&sig.decl.output), + ); return; } FnKind::Fn(FnCtxt::Free, ..) => FnItemRibKind, @@ -759,28 +778,32 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { this.r.extra_lifetime_params_map.insert(async_node_id, extra_lifetime_params); this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(async_node_id), + LifetimeRibKind::AnonymousPassThrough(async_node_id, true), |this| visit::walk_fn_ret_ty(this, &declaration.output), ); } else { - this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| { - // Add each argument to the rib. - this.resolve_params(&declaration.inputs); - - visit::walk_fn_ret_ty(this, &declaration.output); - }); + // Add each argument to the rib. + this.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(fn_id, false), + |this| this.resolve_params(&declaration.inputs), + ); + this.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(fn_id, true), + |this| visit::walk_fn_ret_ty(this, &declaration.output), + ); }; // Ignore errors in function bodies if this is rustdoc // Be sure not to set this until the function signature has been resolved. let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure - this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| { - match fn_kind { + this.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(fn_id, false), + |this| match fn_kind { FnKind::Fn(.., body) => walk_list!(this, visit_block, body), FnKind::Closure(_, body) => this.visit_expr(body), - } - }); + }, + ); debug!("(resolving function) leaving function"); this.in_func_body = previous_state; @@ -788,8 +811,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }); self.diagnostic_metadata.current_function = previous_value; } - fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: visit::LifetimeCtxt) { - self.resolve_lifetime(lifetime) + fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) { + self.resolve_lifetime(lifetime, use_ctxt) } fn visit_generics(&mut self, generics: &'ast Generics) { @@ -864,10 +887,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { if let Some(ref args) = path_segment.args { match &**args { GenericArgs::AngleBracketed(..) => visit::walk_generic_args(self, path_span, args), - GenericArgs::Parenthesized(..) => self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(path_segment.id), - |this| visit::walk_generic_args(this, path_span, args), - ), + GenericArgs::Parenthesized(ref data) => { + self.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(path_segment.id, false), + |this| walk_list!(this, visit_ty, &data.inputs), + ); + self.with_lifetime_rib( + LifetimeRibKind::AnonymousPassThrough(path_segment.id, true), + |this| visit::walk_fn_ret_ty(this, &data.output), + ) + } } } } @@ -890,7 +919,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { &bound_generic_params, NormalRibKind, LifetimeRibKind::Generics { - parent: bounded_ty.id, + binder: bounded_ty.id, kind: LifetimeBinderKind::WhereBound, span, }, @@ -971,6 +1000,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { diagnostic_metadata: DiagnosticMetadata::default(), // errors at module scope should always be reported in_func_body: false, + lifetime_uses: Default::default(), } } @@ -1178,7 +1208,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } #[tracing::instrument(level = "debug", skip(self))] - fn resolve_lifetime(&mut self, lifetime: &'ast Lifetime) { + fn resolve_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) { let ident = lifetime.ident; if ident.name == kw::StaticLifetime { @@ -1196,6 +1226,40 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let normalized_ident = ident.normalize_to_macros_2_0(); if let Some(&(_, region)) = rib.bindings.get(&normalized_ident) { self.record_lifetime_res(lifetime.id, region); + + if let LifetimeRes::Param { param, .. } = region { + match self.lifetime_uses.entry(param) { + Entry::Vacant(v) => { + debug!("First use of {:?} at {:?}", region, ident.span); + let use_set = self + .lifetime_ribs + .iter() + .rev() + .find_map(|rib| match rib.kind { + // Do not suggest eliding a lifetime where an anonymous + // lifetime would be illegal. + LifetimeRibKind::Item + | LifetimeRibKind::AnonymousPassThrough(_, true) + | LifetimeRibKind::AnonymousReportError => { + Some(LifetimeUseSet::Many) + } + // An anonymous lifetime is legal here, go ahead. + LifetimeRibKind::AnonymousPassThrough(_, false) + | LifetimeRibKind::AnonymousCreateParameter(_) => { + Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt }) + } + _ => None, + }) + .unwrap_or(LifetimeUseSet::Many); + debug!(?use_ctxt, ?use_set); + v.insert(use_set); + } + Entry::Occupied(mut o) => { + debug!("Many uses of {:?} at {:?}", region, ident.span); + *o.get_mut() = LifetimeUseSet::Many; + } + } + } return; } @@ -1262,7 +1326,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.record_lifetime_res(lifetime.id, LifetimeRes::Error); return; } - LifetimeRibKind::AnonymousPassThrough(node_id) => { + LifetimeRibKind::AnonymousPassThrough(node_id, _) => { self.record_lifetime_res( lifetime.id, LifetimeRes::Anonymous { binder: node_id, elided }, @@ -1382,7 +1446,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // `PathSegment`, for which there is no associated `'_` or `&T` with no explicit // lifetime. Instead, we simply create an implicit lifetime, which will be checked // later, at which point a suitable error will be emitted. - LifetimeRibKind::AnonymousPassThrough(binder) => { + LifetimeRibKind::AnonymousPassThrough(binder, _) => { res = LifetimeRes::Anonymous { binder, elided: true }; break; } @@ -1550,7 +1614,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: item.id, + binder: item.id, kind: LifetimeBinderKind::Item, span: generics.span, }, @@ -1620,7 +1684,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: item.id, + binder: item.id, kind: LifetimeBinderKind::Item, span: generics.span, }, @@ -1633,7 +1697,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: item.id, + binder: item.id, kind: LifetimeBinderKind::Function, span: generics.span, }, @@ -1665,7 +1729,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: item.id, + binder: item.id, kind: LifetimeBinderKind::Item, span: generics.span, }, @@ -1686,7 +1750,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &generics.params, AssocItemRibKind, LifetimeRibKind::Generics { - parent: item.id, + binder: item.id, span: generics.span, kind, }, @@ -1754,7 +1818,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { - parent: item.id, + binder: item.id, kind: LifetimeBinderKind::Item, span: generics.span, }, @@ -1824,6 +1888,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { F: FnOnce(&mut Self), { debug!("with_generic_param_rib"); + let LifetimeRibKind::Generics { binder, span: generics_span, kind: generics_kind, .. } + = lifetime_kind else { panic!() }; + let mut function_type_rib = Rib::new(kind); let mut function_value_rib = Rib::new(kind); let mut function_lifetime_rib = LifetimeRib::new(lifetime_kind); @@ -1892,8 +1959,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam), GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam), GenericParamKind::Lifetime => { - let LifetimeRibKind::Generics { parent, .. } = lifetime_kind else { panic!() }; - let res = LifetimeRes::Param { param: def_id, binder: parent }; + let res = LifetimeRes::Param { param: def_id, binder }; self.record_lifetime_res(param.id, res); function_lifetime_rib.bindings.insert(ident, (param.id, res)); continue; @@ -1913,6 +1979,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.ribs[TypeNS].pop(); self.ribs[ValueNS].pop(); self.lifetime_ribs.pop(); + + if let LifetimeBinderKind::BareFnType + | LifetimeBinderKind::WhereBound + | LifetimeBinderKind::Function + | LifetimeBinderKind::ImplBlock = generics_kind + { + self.maybe_report_lifetime_uses(generics_span, params) + } } fn with_label_rib(&mut self, kind: RibKind<'a>, f: impl FnOnce(&mut Self)) { @@ -2039,7 +2113,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ) { debug!("resolve_implementation"); // If applicable, create a rib for the type parameters. - self.with_generic_param_rib(&generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { span: generics.span, parent: item_id, kind: LifetimeBinderKind::ImplBlock }, |this| { + self.with_generic_param_rib(&generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { span: generics.span, binder: item_id, kind: LifetimeBinderKind::ImplBlock }, |this| { // Dummy self type for better errors if `Self` is used in the trait path. this.with_self_rib(Res::SelfTy { trait_: None, alias_to: None }, |this| { this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter(item_id), |this| { @@ -2066,7 +2140,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.visit_generics(generics); // Resolve the items within the impl. - this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(item_id), + this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(item_id,false), |this| { this.with_current_self_type(self_type, |this| { this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| { @@ -2111,7 +2185,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.with_generic_param_rib( &generics.params, AssocItemRibKind, - LifetimeRibKind::Generics { parent: item.id, span: generics.span, kind: LifetimeBinderKind::Function }, + LifetimeRibKind::Generics { binder: item.id, span: generics.span, kind: LifetimeBinderKind::Function }, |this| { // If this is a trait impl, ensure the method // exists in trait @@ -2140,7 +2214,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.with_generic_param_rib( &generics.params, AssocItemRibKind, - LifetimeRibKind::Generics { parent: item.id, span: generics.span, kind: LifetimeBinderKind::Item }, + LifetimeRibKind::Generics { binder: item.id, span: generics.span, kind: LifetimeBinderKind::Item }, |this| { // If this is a trait impl, ensure the type // exists in trait diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 4f07d0076f140..318bb8280ecec 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1,16 +1,17 @@ use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext}; use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; -use crate::late::{LifetimeBinderKind, LifetimeRibKind}; +use crate::late::{LifetimeBinderKind, LifetimeRibKind, LifetimeUseSet}; use crate::path_names_to_string; use crate::{Module, ModuleKind, ModuleOrUniformRoot}; use crate::{PathResult, PathSource, Segment}; -use rustc_ast::visit::{FnCtxt, FnKind}; +use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt}; use rustc_ast::{ self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, NodeId, Path, Ty, TyKind, }; +use rustc_ast_lowering::ResolverAstLowering; use rustc_ast_pretty::pprust::path_segment_to_string; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ @@ -22,6 +23,7 @@ use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; 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_span::edition::Edition; use rustc_span::hygiene::MacroKind; @@ -1832,6 +1834,76 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { }) } + crate fn maybe_report_lifetime_uses( + &mut self, + generics_span: Span, + params: &[ast::GenericParam], + ) { + for (param_index, param) in params.iter().enumerate() { + let GenericParamKind::Lifetime = param.kind else { continue }; + + let def_id = self.r.local_def_id(param.id); + + let use_set = self.lifetime_uses.remove(&def_id); + debug!( + "Use set for {:?}({:?} at {:?}) is {:?}", + def_id, param.ident, param.ident.span, use_set + ); + + let deletion_span = || { + if params.len() == 1 { + // if sole lifetime, remove the entire `<>` brackets + generics_span + } else if param_index == 0 { + // if removing within `<>` brackets, we also want to + // delete a leading or trailing comma as appropriate + param.span().to(params[param_index + 1].span().shrink_to_lo()) + } else { + // if removing within `<>` brackets, we also want to + // delete a leading or trailing comma as appropriate + params[param_index - 1].span().shrink_to_hi().to(param.span()) + } + }; + match use_set { + Some(LifetimeUseSet::Many) => {} + Some(LifetimeUseSet::One { use_span, use_ctxt }) => { + debug!(?param.ident, ?param.ident.span, ?use_span); + + let elidable = matches!(use_ctxt, LifetimeCtxt::Rptr); + + let deletion_span = deletion_span(); + self.r.lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::SINGLE_USE_LIFETIMES, + param.id, + param.ident.span, + &format!("lifetime parameter `{}` only used once", param.ident), + lint::BuiltinLintDiagnostics::SingleUseLifetime { + param_span: param.ident.span, + use_span: Some((use_span, elidable)), + deletion_span, + }, + ); + } + None => { + debug!(?param.ident, ?param.ident.span); + + let deletion_span = deletion_span(); + self.r.lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::UNUSED_LIFETIMES, + param.id, + param.ident.span, + &format!("lifetime parameter `{}` never used", param.ident), + lint::BuiltinLintDiagnostics::SingleUseLifetime { + param_span: param.ident.span, + use_span: None, + deletion_span, + }, + ); + } + } + } + } + crate fn emit_undeclared_lifetime_error( &self, lifetime_ref: &ast::Lifetime, @@ -1863,7 +1935,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { for rib in self.lifetime_ribs.iter().rev() { match rib.kind { - LifetimeRibKind::Generics { parent: _, span, kind } => { + LifetimeRibKind::Generics { binder: _, span, kind } => { if !span.can_be_used_for_suggestions() && suggest_note { suggest_note = false; // Avoid displaying the same help multiple times. err.span_label( diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 700d7c3bfb6fe..cacb851c4f04d 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -9,20 +9,19 @@ use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot}; use rustc_ast::walk_list; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_errors::{struct_span_err, Applicability, Diagnostic}; +use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefIdMap, LocalDefId}; use rustc_hir::hir_id::ItemLocalId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName, QPath}; +use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName}; use rustc_hir::{GenericParamKind, HirIdMap, HirIdSet}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_session::lint; use rustc_span::def_id::DefId; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -33,13 +32,6 @@ use std::mem::take; use tracing::{debug, span, Level}; -// This counts the no of times a lifetime is used -#[derive(Clone, Copy, Debug)] -pub enum LifetimeUseSet<'tcx> { - One(&'tcx hir::Lifetime), - Many, -} - trait RegionExt { fn early(hir_map: Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (ParamName, Region); @@ -175,8 +167,6 @@ crate struct LifetimeContext<'a, 'tcx> { /// Cache for cross-crate per-definition object lifetime defaults. xcrate_object_lifetime_defaults: DefIdMap>, - lifetime_uses: &'a mut DefIdMap>, - /// When encountering an undefined named lifetime, we will suggest introducing it in these /// places. crate missing_named_lifetime_spots: Vec>, @@ -197,11 +187,6 @@ enum Scope<'a> { /// we should use for an early-bound region? next_early_index: u32, - /// Flag is set to true if, in this binder, `'_` would be - /// equivalent to a "single-use region". This is true on - /// impls, but not other kinds of items. - track_lifetime_uses: bool, - /// Whether or not this binder would serve as the parent /// binder for opaque types introduced within. For example: /// @@ -297,7 +282,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { Scope::Binder { lifetimes, next_early_index, - track_lifetime_uses, opaque_type_parent, scope_type, hir_id, @@ -307,7 +291,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .debug_struct("Binder") .field("lifetimes", lifetimes) .field("next_early_index", next_early_index) - .field("track_lifetime_uses", track_lifetime_uses) .field("opaque_type_parent", opaque_type_parent) .field("scope_type", scope_type) .field("hir_id", hir_id) @@ -453,7 +436,6 @@ fn do_resolve( trait_definition_only, labels_in_fn: vec![], xcrate_object_lifetime_defaults: Default::default(), - lifetime_uses: &mut Default::default(), missing_named_lifetime_spots: vec![], }; visitor.visit_item(item); @@ -697,7 +679,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes: FxIndexMap::default(), next_early_index: self.next_early_index(), s: self.scope, - track_lifetime_uses: true, opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: true, @@ -796,9 +777,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => { self.missing_named_lifetime_spots.push(generics.into()); - // Impls permit `'_` to be used and it is equivalent to "some fresh lifetime name". - // This is not true for other kinds of items. - let track_lifetime_uses = matches!(item.kind, hir::ItemKind::Impl { .. }); // These kinds of items have only early-bound lifetime parameters. let mut index = if sub_items_have_self_param(&item.kind) { 1 // Self comes before lifetimes @@ -825,7 +803,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index: index + non_lifetime_count, opaque_type_parent: true, - track_lifetime_uses, scope_type: BinderScopeType::Normal, s: ROOT_SCOPE, allow_late_bound: false, @@ -892,7 +869,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, s: self.scope, next_early_index, - track_lifetime_uses: true, opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: true, @@ -1053,11 +1029,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { match param.kind { GenericParamKind::Lifetime { .. } => { let (name, reg) = Region::early(self.tcx.hir(), &mut index, ¶m); - let Region::EarlyBound(_, def_id) = reg else { - bug!(); - }; - // We cannot predict what lifetimes are unused in opaque type. - self.lifetime_uses.insert(def_id, LifetimeUseSet::Many); if let hir::ParamName::Plain(Ident { name: kw::UnderscoreLifetime, .. @@ -1087,7 +1058,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index, s: this.scope, - track_lifetime_uses: true, opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: false, @@ -1108,7 +1078,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index, s: self.scope, - track_lifetime_uses: true, opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: false, @@ -1168,7 +1137,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index: index + non_lifetime_count, s: self.scope, - track_lifetime_uses: true, opaque_type_parent: true, scope_type: BinderScopeType::Normal, allow_late_bound: false, @@ -1238,7 +1206,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, next_early_index: index + non_lifetime_count, s: self.scope, - track_lifetime_uses: true, opaque_type_parent: true, scope_type: BinderScopeType::Normal, allow_late_bound: true, @@ -1383,7 +1350,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, s: this.scope, next_early_index, - track_lifetime_uses: true, opaque_type_parent: false, scope_type: BinderScopeType::Normal, allow_late_bound: true, @@ -1457,7 +1423,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes: FxIndexMap::default(), s: self.scope, next_early_index: self.next_early_index(), - track_lifetime_uses: true, opaque_type_parent: false, scope_type, allow_late_bound: true, @@ -1510,7 +1475,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { lifetimes, s: self.scope, next_early_index, - track_lifetime_uses: true, opaque_type_parent: false, scope_type, allow_late_bound: true, @@ -1812,7 +1776,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { where F: for<'b> FnOnce(ScopeRef<'_>, &mut LifetimeContext<'b, 'tcx>), { - let LifetimeContext { tcx, map, lifetime_uses, .. } = self; + 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); @@ -1823,298 +1787,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { trait_definition_only: self.trait_definition_only, labels_in_fn, xcrate_object_lifetime_defaults, - lifetime_uses, missing_named_lifetime_spots, }; let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope)); { let _enter = span.enter(); f(self.scope, &mut this); - if !self.trait_definition_only { - this.check_uses_for_lifetimes_defined_by_scope(); - } } 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; } - /// helper method to determine the span to remove when suggesting the - /// deletion of a lifetime - fn lifetime_deletion_span(&self, name: Ident, generics: &hir::Generics<'_>) -> Option { - generics.params.iter().enumerate().find_map(|(i, param)| { - if param.name.ident() == name { - if generics.params.len() == 1 { - // if sole lifetime, remove the entire `<>` brackets - Some(generics.span) - } else { - // if removing within `<>` brackets, we also want to - // delete a leading or trailing comma as appropriate - if i >= generics.params.len() - 1 { - Some(generics.params[i - 1].span.shrink_to_hi().to(param.span)) - } else { - Some(param.span.to(generics.params[i + 1].span.shrink_to_lo())) - } - } - } else { - None - } - }) - } - - // helper method to issue suggestions from `fn rah<'a>(&'a T)` to `fn rah(&T)` - // or from `fn rah<'a>(T<'a>)` to `fn rah(T<'_>)` - fn suggest_eliding_single_use_lifetime( - &self, - err: &mut Diagnostic, - def_id: DefId, - lifetime: &hir::Lifetime, - ) { - let name = lifetime.name.ident(); - let remove_decl = self - .tcx - .parent(def_id) - .as_local() - .and_then(|parent_def_id| self.tcx.hir().get_generics(parent_def_id)) - .and_then(|generics| self.lifetime_deletion_span(name, generics)); - - let mut remove_use = None; - let mut elide_use = None; - let mut find_arg_use_span = |inputs: &[hir::Ty<'_>]| { - for input in inputs { - match input.kind { - hir::TyKind::Rptr(lt, _) => { - if lt.name.ident() == name { - // include the trailing whitespace between the lifetime and type names - let lt_through_ty_span = lifetime.span.to(input.span.shrink_to_hi()); - remove_use = Some( - self.tcx - .sess - .source_map() - .span_until_non_whitespace(lt_through_ty_span), - ); - break; - } - } - hir::TyKind::Path(QPath::Resolved(_, path)) => { - let last_segment = &path.segments[path.segments.len() - 1]; - let generics = last_segment.args(); - for arg in generics.args.iter() { - if let GenericArg::Lifetime(lt) = arg { - if lt.name.ident() == name { - elide_use = Some(lt.span); - break; - } - } - } - break; - } - _ => {} - } - } - }; - if let Node::Lifetime(hir_lifetime) = self.tcx.hir().get(lifetime.hir_id) { - if let Some(parent) = - self.tcx.hir().find_by_def_id(self.tcx.hir().get_parent_item(hir_lifetime.hir_id)) - { - match parent { - Node::Item(item) => { - if let hir::ItemKind::Fn(sig, _, _) = &item.kind { - find_arg_use_span(sig.decl.inputs); - } - } - Node::ImplItem(impl_item) => { - if let hir::ImplItemKind::Fn(sig, _) = &impl_item.kind { - find_arg_use_span(sig.decl.inputs); - } - } - _ => {} - } - } - } - - let msg = "elide the single-use lifetime"; - match (remove_decl, remove_use, elide_use) { - (Some(decl_span), Some(use_span), None) => { - // if both declaration and use deletion spans start at the same - // place ("start at" because the latter includes trailing - // whitespace), then this is an in-band lifetime - if decl_span.shrink_to_lo() == use_span.shrink_to_lo() { - err.span_suggestion( - use_span, - msg, - String::new(), - Applicability::MachineApplicable, - ); - } else { - err.multipart_suggestion( - msg, - vec![(decl_span, String::new()), (use_span, String::new())], - Applicability::MachineApplicable, - ); - } - } - (Some(decl_span), None, Some(use_span)) => { - err.multipart_suggestion( - msg, - vec![(decl_span, String::new()), (use_span, "'_".to_owned())], - Applicability::MachineApplicable, - ); - } - _ => {} - } - } - - fn check_uses_for_lifetimes_defined_by_scope(&mut self) { - let Scope::Binder { lifetimes: defined_by, .. } = self.scope else { - debug!("check_uses_for_lifetimes_defined_by_scope: not in a binder scope"); - return; - }; - - let def_ids: Vec<_> = defined_by - .values() - .flat_map(|region| match region { - Region::EarlyBound(_, def_id) - | Region::LateBound(_, _, def_id) - | Region::Free(_, def_id) => Some(*def_id), - - Region::LateBoundAnon(..) | Region::Static => None, - }) - .collect(); - - 'lifetimes: for def_id in def_ids { - debug!("check_uses_for_lifetimes_defined_by_scope: def_id = {:?}", def_id); - - let lifetimeuseset = self.lifetime_uses.remove(&def_id); - - debug!( - "check_uses_for_lifetimes_defined_by_scope: lifetimeuseset = {:?}", - lifetimeuseset - ); - - match lifetimeuseset { - Some(LifetimeUseSet::One(lifetime)) => { - debug!(?def_id); - if let Some((id, span, name)) = - match self.tcx.hir().get_by_def_id(def_id.expect_local()) { - Node::Lifetime(hir_lifetime) => Some(( - hir_lifetime.hir_id, - hir_lifetime.span, - hir_lifetime.name.ident(), - )), - Node::GenericParam(param) => { - Some((param.hir_id, param.span, param.name.ident())) - } - _ => None, - } - { - debug!("id = {:?} span = {:?} name = {:?}", id, span, name); - if name.name == kw::UnderscoreLifetime { - continue; - } - - let parent_def_id = self.tcx.parent(def_id); - if let Some(def_id) = parent_def_id.as_local() { - // lifetimes in `derive` expansions don't count (Issue #53738) - if self.tcx.has_attr(def_id.to_def_id(), sym::automatically_derived) { - continue; - } - - // opaque types generated when desugaring an async function can have a single - // use lifetime even if it is explicitly denied (Issue #77175) - if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(ref opaque), - .. - }) = self.tcx.hir().get_by_def_id(def_id) - { - if !matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(..)) { - continue 'lifetimes; - } - // We want to do this only if the lifetime identifier is already defined - // in the async function that generated this. Otherwise it could be - // an opaque type defined by the developer and we still want this - // lint to fail compilation - for p in opaque.generics.params { - if defined_by.contains_key(&p.name) { - continue 'lifetimes; - } - } - } - } - - self.tcx.struct_span_lint_hir( - lint::builtin::SINGLE_USE_LIFETIMES, - id, - span, - |lint| { - let mut err = lint.build(&format!( - "lifetime parameter `{}` only used once", - name - )); - if span == lifetime.span { - // spans are the same for in-band lifetime declarations - err.span_label(span, "this lifetime is only used here"); - } else { - err.span_label(span, "this lifetime..."); - err.span_label(lifetime.span, "...is used only here"); - } - self.suggest_eliding_single_use_lifetime( - &mut err, def_id, lifetime, - ); - err.emit(); - }, - ); - } - } - Some(LifetimeUseSet::Many) => { - debug!("not one use lifetime"); - } - None => { - if let Some((id, span, name)) = - match self.tcx.hir().get_by_def_id(def_id.expect_local()) { - Node::Lifetime(hir_lifetime) => Some(( - hir_lifetime.hir_id, - hir_lifetime.span, - hir_lifetime.name.ident(), - )), - Node::GenericParam(param) => { - Some((param.hir_id, param.span, param.name.ident())) - } - _ => None, - } - { - debug!("id ={:?} span = {:?} name = {:?}", id, span, name); - self.tcx.struct_span_lint_hir( - lint::builtin::UNUSED_LIFETIMES, - id, - span, - |lint| { - let mut err = lint - .build(&format!("lifetime parameter `{}` never used", name)); - let parent_def_id = self.tcx.parent(def_id); - if let Some(generics) = - self.tcx.hir().get_generics(parent_def_id.expect_local()) - { - let unused_lt_span = - self.lifetime_deletion_span(name, generics); - if let Some(span) = unused_lt_span { - err.span_suggestion( - span, - "elide the unused lifetime", - String::new(), - Applicability::MachineApplicable, - ); - } - } - err.emit(); - }, - ); - } - } - } - } - } - /// Visits self by adding a scope and handling recursive walk over the contents with `walk`. /// /// Handles visiting fns and methods. These are a bit complicated because we must distinguish @@ -2204,7 +1888,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { next_early_index, s: self.scope, opaque_type_parent: true, - track_lifetime_uses: false, scope_type: BinderScopeType::Normal, allow_late_bound: true, }; @@ -3201,41 +2884,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } } - /// Returns `true` if, in the current scope, replacing `'_` would be - /// equivalent to a single-use lifetime. - fn track_lifetime_uses(&self) -> bool { - let mut scope = self.scope; - loop { - match *scope { - Scope::Root => break false, - - // Inside of items, it depends on the kind of item. - Scope::Binder { track_lifetime_uses, .. } => break track_lifetime_uses, - - // Inside a body, `'_` will use an inference variable, - // should be fine. - Scope::Body { .. } => break true, - - // A lifetime only used in a fn argument could as well - // be replaced with `'_`, as that would generate a - // fresh name, too. - Scope::Elision { elide: Elide::FreshLateAnon(..), .. } => break true, - - // In the return type or other such place, `'_` is not - // going to make a fresh name, so we cannot - // necessarily replace a single-use lifetime with - // `'_`. - Scope::Elision { - elide: Elide::Exact(_) | Elide::Error(_) | Elide::Forbid, .. - } => break false, - - Scope::ObjectLifetimeDefault { s, .. } - | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } => scope = s, - } - } - } - #[tracing::instrument(level = "debug", skip(self))] fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) { debug!( @@ -3243,27 +2891,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { span = ?self.tcx.sess.source_map().span_to_diagnostic_string(lifetime_ref.span) ); self.map.defs.insert(lifetime_ref.hir_id, def); - - match def { - Region::LateBoundAnon(..) | Region::Static => { - // These are anonymous lifetimes or lifetimes that are not declared. - } - - Region::Free(_, def_id) - | Region::LateBound(_, _, def_id) - | Region::EarlyBound(_, def_id) => { - // A lifetime declared by the user. - let track_lifetime_uses = self.track_lifetime_uses(); - debug!(?track_lifetime_uses); - if track_lifetime_uses && !self.lifetime_uses.contains_key(&def_id) { - debug!("first use of {:?}", def_id); - self.lifetime_uses.insert(def_id, LifetimeUseSet::One(lifetime_ref)); - } else { - debug!("many uses of {:?}", def_id); - self.lifetime_uses.insert(def_id, LifetimeUseSet::Many); - } - } - } } /// Sometimes we resolve a lifetime, but later find that it is an diff --git a/src/test/ui/async-await/unused-lifetime.rs b/src/test/ui/async-await/unused-lifetime.rs index 5bd6ae8d3a42d..6cfd36ba9e843 100644 --- a/src/test/ui/async-await/unused-lifetime.rs +++ b/src/test/ui/async-await/unused-lifetime.rs @@ -1,19 +1,15 @@ // Check "unused_lifetimes" lint on both async and sync functions +// Both cases should be diagnosed the same way. // edition:2018 #![deny(unused_lifetimes)] +async fn async_wrong_without_args<'a>() {} //~ ERROR lifetime parameter `'a` never used -// Async part with unused lifetimes -// -// Even wrong cases don't cause errors because async functions are desugared with all lifetimes -// involved in the signature. So, we cannot predict what lifetimes are unused in async function. -async fn async_wrong_without_args<'a>() {} +async fn async_wrong_1_lifetime<'a>(_: &i32) {} //~ ERROR lifetime parameter `'a` never used -async fn async_wrong_1_lifetime<'a>(_: &i32) {} - -async fn async_wrong_2_lifetimes<'a, 'b>(_: &'a i32, _: &i32) {} +async fn async_wrong_2_lifetimes<'a, 'b>(_: &'a i32, _: &i32) {} //~ ERROR lifetime parameter `'b` never used async fn async_right_1_lifetime<'a>(_: &'a i32) {} @@ -24,10 +20,6 @@ where I: Iterator {} - -// Sync part with unused lifetimes -// -// These functions are compiled as supposed fn wrong_without_args<'a>() {} //~ ERROR lifetime parameter `'a` never used fn wrong_1_lifetime<'a>(_: &i32) {} //~ ERROR lifetime parameter `'a` never used diff --git a/src/test/ui/async-await/unused-lifetime.stderr b/src/test/ui/async-await/unused-lifetime.stderr index 4e90f43fdd07b..5c00501a62fe5 100644 --- a/src/test/ui/async-await/unused-lifetime.stderr +++ b/src/test/ui/async-await/unused-lifetime.stderr @@ -1,28 +1,48 @@ error: lifetime parameter `'a` never used - --> $DIR/unused-lifetime.rs:31:23 + --> $DIR/unused-lifetime.rs:8:35 | -LL | fn wrong_without_args<'a>() {} - | -^^- help: elide the unused lifetime +LL | async fn async_wrong_without_args<'a>() {} + | -^^- help: elide the unused lifetime | note: the lint level is defined here - --> $DIR/unused-lifetime.rs:5:9 + --> $DIR/unused-lifetime.rs:6:9 | LL | #![deny(unused_lifetimes)] | ^^^^^^^^^^^^^^^^ error: lifetime parameter `'a` never used - --> $DIR/unused-lifetime.rs:33:21 + --> $DIR/unused-lifetime.rs:10:33 + | +LL | async fn async_wrong_1_lifetime<'a>(_: &i32) {} + | -^^- help: elide the unused lifetime + +error: lifetime parameter `'b` never used + --> $DIR/unused-lifetime.rs:12:38 + | +LL | async fn async_wrong_2_lifetimes<'a, 'b>(_: &'a i32, _: &i32) {} + | --^^ + | | + | help: elide the unused lifetime + +error: lifetime parameter `'a` never used + --> $DIR/unused-lifetime.rs:23:23 + | +LL | fn wrong_without_args<'a>() {} + | -^^- help: elide the unused lifetime + +error: lifetime parameter `'a` never used + --> $DIR/unused-lifetime.rs:25:21 | LL | fn wrong_1_lifetime<'a>(_: &i32) {} | -^^- help: elide the unused lifetime error: lifetime parameter `'b` never used - --> $DIR/unused-lifetime.rs:35:26 + --> $DIR/unused-lifetime.rs:27:26 | LL | fn wrong_2_lifetimes<'a, 'b>(_: &'a i32, _: &i32) {} | --^^ | | | help: elide the unused lifetime -error: aborting due to 3 previous errors +error: aborting due to 6 previous errors diff --git a/src/test/ui/single-use-lifetime/fn-types.stderr b/src/test/ui/single-use-lifetime/fn-types.stderr index 584c889506ba5..9290c21620e90 100644 --- a/src/test/ui/single-use-lifetime/fn-types.stderr +++ b/src/test/ui/single-use-lifetime/fn-types.stderr @@ -11,6 +11,11 @@ note: the lint level is defined here | LL | #![deny(single_use_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^ +help: elide the single-use lifetime + | +LL - a: for<'a> fn(&'a u32), +LL + a: fn(&u32), + | error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types --> $DIR/fn-types.rs:12:22 diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs index ff9d6bd01c670..7919ef820f6e1 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-argument.rs @@ -19,4 +19,15 @@ fn left<'x, 'y>(foo: Double<'x, 'y>) -> &'x u32 { foo.f } //~ ERROR `'y` only us fn right<'x, 'y>(foo: Double<'x, 'y>) -> &'y u32 { foo.f } //~ ERROR `'x` only used once //~^ HELP elide the single-use lifetime -fn main() { } +pub trait Tfv<'a> {} + +// Do NOT lint in an HRTB. +pub fn g Tfv<'a>>() {} + +// Do NOT lint for trait bounds. +pub fn h<'a, S>(_: S) +where + S: Tfv<'a>, +{} + +fn main() {} diff --git a/src/test/ui/single-use-lifetime/one-use-in-fn-return.rs b/src/test/ui/single-use-lifetime/one-use-in-fn-return.rs index 7b7ff08da7cac..1ade01eed36e4 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-fn-return.rs +++ b/src/test/ui/single-use-lifetime/one-use-in-fn-return.rs @@ -14,4 +14,10 @@ fn b<'a>() -> &'a u32 { &22 } +pub trait Tfv<'a> {} +impl Tfv<'_> for () {} + +// Do NOT lint if used in return type. +pub fn i<'a>() -> impl Tfv<'a> {} + fn main() {} diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr b/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr index 35fd782e13320..cf34a1ca299ac 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-impl-header.stderr @@ -11,6 +11,11 @@ note: the lint level is defined here | LL | #![deny(single_use_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^ +help: elide the single-use lifetime + | +LL - impl<'f> Foo<'f> { +LL + impl Foo<'_> { + | error: aborting due to previous error diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs index e7bdbb2207a81..eecd715efc168 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.rs @@ -9,6 +9,7 @@ struct Foo<'f> { } impl<'f> Foo<'f> { //~ ERROR `'f` only used once + //~^ HELP elide the single-use lifetime fn inherent_a<'a>(&self, data: &'a u32) { //~ ERROR `'a` only used once //~^ HELP elide the single-use lifetime } diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr index b8b78cd87b003..846c1bf41a29c 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-argument.stderr @@ -1,10 +1,10 @@ -error: lifetime parameter `'a` only used once - --> $DIR/one-use-in-inherent-method-argument.rs:12:19 +error: lifetime parameter `'f` only used once + --> $DIR/one-use-in-inherent-method-argument.rs:11:6 | -LL | fn inherent_a<'a>(&self, data: &'a u32) { - | ^^ -- ...is used only here - | | - | this lifetime... +LL | impl<'f> Foo<'f> { + | ^^ -- ...is used only here + | | + | this lifetime... | note: the lint level is defined here --> $DIR/one-use-in-inherent-method-argument.rs:1:9 @@ -13,17 +13,23 @@ LL | #![deny(single_use_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^ help: elide the single-use lifetime | -LL - fn inherent_a<'a>(&self, data: &'a u32) { -LL + fn inherent_a(&self, data: &u32) { +LL - impl<'f> Foo<'f> { +LL + impl Foo<'_> { | -error: lifetime parameter `'f` only used once - --> $DIR/one-use-in-inherent-method-argument.rs:11:6 +error: lifetime parameter `'a` only used once + --> $DIR/one-use-in-inherent-method-argument.rs:13:19 | -LL | impl<'f> Foo<'f> { - | ^^ -- ...is used only here - | | - | this lifetime... +LL | fn inherent_a<'a>(&self, data: &'a u32) { + | ^^ -- ...is used only here + | | + | this lifetime... + | +help: elide the single-use lifetime + | +LL - fn inherent_a<'a>(&self, data: &'a u32) { +LL + fn inherent_a(&self, data: &u32) { + | error: aborting due to 2 previous errors diff --git a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-return.stderr b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-return.stderr index da9e253461191..790fcaa409caf 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-inherent-method-return.stderr +++ b/src/test/ui/single-use-lifetime/one-use-in-inherent-method-return.stderr @@ -11,6 +11,11 @@ note: the lint level is defined here | LL | #![deny(single_use_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^ +help: elide the single-use lifetime + | +LL - impl<'f> Foo<'f> { +LL + impl Foo<'_> { + | error: aborting due to previous error diff --git a/src/test/ui/single-use-lifetime/one-use-in-struct.rs b/src/test/ui/single-use-lifetime/one-use-in-struct.rs index 9082aa68ed22b..9cad942e7a2e7 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-struct.rs +++ b/src/test/ui/single-use-lifetime/one-use-in-struct.rs @@ -4,7 +4,8 @@ // // check-pass -#![deny(single_use_lifetimes)] +// Use forbid to verify that `automatically_derived` is handled correctly. +#![forbid(single_use_lifetimes)] #![allow(dead_code)] #![allow(unused_variables)] diff --git a/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs b/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs index 6a66c17538ad7..1848fc91c629b 100644 --- a/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs +++ b/src/test/ui/single-use-lifetime/one-use-in-trait-method-argument.rs @@ -18,4 +18,9 @@ impl<'f> Iterator for Foo<'f> { } } -fn main() { } +trait Bar<'a> { + // But we should not warn here. + fn bar(x: Foo<'a>); +} + +fn main() {} diff --git a/src/test/ui/single-use-lifetime/two-uses-in-inherent-method-argument-and-return.stderr b/src/test/ui/single-use-lifetime/two-uses-in-inherent-method-argument-and-return.stderr index c16b244fafd00..b50975a189e4f 100644 --- a/src/test/ui/single-use-lifetime/two-uses-in-inherent-method-argument-and-return.stderr +++ b/src/test/ui/single-use-lifetime/two-uses-in-inherent-method-argument-and-return.stderr @@ -11,6 +11,11 @@ note: the lint level is defined here | LL | #![deny(single_use_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^ +help: elide the single-use lifetime + | +LL - impl<'f> Foo<'f> { +LL + impl Foo<'_> { + | error: aborting due to previous error