Skip to content

Commit b5caa5a

Browse files
committed
Auto merge of #96833 - cjgillot:ast-lifetimes-single, r=petrochenkov
Lint single-use lifetimes during AST resolution This PR rewrites `single_use_lifetime` and `unused_lifetime` lints to be based on the AST. We have more information at our disposal, so we can reduce the amount of false positives. Remaining false positive: single-use lifetimes in argument-position impl-trait. I'm waiting for #96529 to be fixed to have a clean and proper solution here. Closes #54079 Closes #55057 Closes #55058 Closes #60554 Closes #69952 r? `@petrochenkov`
2 parents 22ee395 + 563916d commit b5caa5a

26 files changed

+396
-493
lines changed

compiler/rustc_ast/src/ast.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1976,6 +1976,8 @@ pub struct BareFnTy {
19761976
pub ext: Extern,
19771977
pub generic_params: Vec<GenericParam>,
19781978
pub decl: P<FnDecl>,
1979+
/// Span of the `fn(...) -> ...` part.
1980+
pub decl_span: Span,
19791981
}
19801982

19811983
/// The various kinds of type recognized by the compiler.

compiler/rustc_ast/src/mut_visit.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,11 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
460460
vis.visit_mt(mt);
461461
}
462462
TyKind::BareFn(bft) => {
463-
let BareFnTy { unsafety, ext: _, generic_params, decl } = bft.deref_mut();
463+
let BareFnTy { unsafety, ext: _, generic_params, decl, decl_span } = bft.deref_mut();
464464
visit_unsafety(unsafety, vis);
465465
generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
466466
vis.visit_fn_decl(decl);
467+
vis.visit_span(decl_span);
467468
}
468469
TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)),
469470
TyKind::Paren(ty) => vis.visit_ty(ty),

compiler/rustc_ast/src/visit.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ impl<'a> FnKind<'a> {
8989
}
9090
}
9191

92+
#[derive(Copy, Clone, Debug)]
93+
pub enum LifetimeCtxt {
94+
/// Appears in a reference type.
95+
Rptr,
96+
/// Appears as a bound on a type or another lifetime.
97+
Bound,
98+
/// Appears as a generic argument.
99+
GenericArg,
100+
}
101+
92102
/// Each method of the `Visitor` trait is a hook to be potentially
93103
/// overridden. Each method's default implementation recursively visits
94104
/// the substructure of the input via the corresponding `walk` method;
@@ -184,7 +194,7 @@ pub trait Visitor<'ast>: Sized {
184194
fn visit_label(&mut self, label: &'ast Label) {
185195
walk_label(self, label)
186196
}
187-
fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) {
197+
fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: LifetimeCtxt) {
188198
walk_lifetime(self, lifetime)
189199
}
190200
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) {
414424
TyKind::Slice(ref ty) | TyKind::Paren(ref ty) => visitor.visit_ty(ty),
415425
TyKind::Ptr(ref mutable_type) => visitor.visit_ty(&mutable_type.ty),
416426
TyKind::Rptr(ref opt_lifetime, ref mutable_type) => {
417-
walk_list!(visitor, visit_lifetime, opt_lifetime);
427+
walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Rptr);
418428
visitor.visit_ty(&mutable_type.ty)
419429
}
420430
TyKind::Tup(ref tuple_element_types) => {
@@ -507,7 +517,7 @@ where
507517
V: Visitor<'a>,
508518
{
509519
match generic_arg {
510-
GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt),
520+
GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt, LifetimeCtxt::GenericArg),
511521
GenericArg::Type(ty) => visitor.visit_ty(ty),
512522
GenericArg::Const(ct) => visitor.visit_anon_const(ct),
513523
}
@@ -599,7 +609,9 @@ pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignI
599609
pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericBound) {
600610
match *bound {
601611
GenericBound::Trait(ref typ, ref modifier) => visitor.visit_poly_trait_ref(typ, modifier),
602-
GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime),
612+
GenericBound::Outlives(ref lifetime) => {
613+
visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound)
614+
}
603615
}
604616
}
605617

@@ -639,7 +651,7 @@ pub fn walk_where_predicate<'a, V: Visitor<'a>>(visitor: &mut V, predicate: &'a
639651
WherePredicate::RegionPredicate(WhereRegionPredicate {
640652
ref lifetime, ref bounds, ..
641653
}) => {
642-
visitor.visit_lifetime(lifetime);
654+
visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound);
643655
walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
644656
}
645657
WherePredicate::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. }) => {

compiler/rustc_ast_passes/src/ast_validation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1070,7 +1070,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10701070
visit::walk_label(self, label);
10711071
}
10721072

1073-
fn visit_lifetime(&mut self, lifetime: &'a Lifetime) {
1073+
fn visit_lifetime(&mut self, lifetime: &'a Lifetime, _: visit::LifetimeCtxt) {
10741074
self.check_lifetime(lifetime.ident);
10751075
visit::walk_lifetime(self, lifetime);
10761076
}

compiler/rustc_ast_passes/src/node_count.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ impl<'ast> Visitor<'ast> for NodeCounter {
106106
self.count += 1;
107107
walk_variant(self, v)
108108
}
109-
fn visit_lifetime(&mut self, lifetime: &Lifetime) {
109+
fn visit_lifetime(&mut self, lifetime: &Lifetime, _: visit::LifetimeCtxt) {
110110
self.count += 1;
111111
walk_lifetime(self, lifetime)
112112
}

compiler/rustc_lint/src/context.rs

+37
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,43 @@ pub trait LintContext: Sized {
819819
"see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information",
820820
);
821821
},
822+
BuiltinLintDiagnostics::SingleUseLifetime {
823+
param_span,
824+
use_span: Some((use_span, elide)),
825+
deletion_span,
826+
} => {
827+
debug!(?param_span, ?use_span, ?deletion_span);
828+
db.span_label(param_span, "this lifetime...");
829+
db.span_label(use_span, "...is used only here");
830+
let msg = "elide the single-use lifetime";
831+
let (use_span, replace_lt) = if elide {
832+
let use_span = sess.source_map().span_extend_while(
833+
use_span,
834+
char::is_whitespace,
835+
).unwrap_or(use_span);
836+
(use_span, String::new())
837+
} else {
838+
(use_span, "'_".to_owned())
839+
};
840+
db.multipart_suggestion(
841+
msg,
842+
vec![(deletion_span, String::new()), (use_span, replace_lt)],
843+
Applicability::MachineApplicable,
844+
);
845+
},
846+
BuiltinLintDiagnostics::SingleUseLifetime {
847+
param_span: _,
848+
use_span: None,
849+
deletion_span,
850+
} => {
851+
debug!(?deletion_span);
852+
db.span_suggestion(
853+
deletion_span,
854+
"elide the unused lifetime",
855+
String::new(),
856+
Applicability::MachineApplicable,
857+
);
858+
},
822859
}
823860
// Rewrap `db`, and pass control to the user.
824861
decorate(LintDiagnosticBuilder::new(db));

compiler/rustc_lint/src/early.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
239239

240240
fn visit_generic_param(&mut self, param: &'a ast::GenericParam) {
241241
run_early_pass!(self, check_generic_param, param);
242+
self.check_id(param.id);
242243
ast_visit::walk_generic_param(self, param);
243244
}
244245

@@ -272,7 +273,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
272273
});
273274
}
274275

275-
fn visit_lifetime(&mut self, lt: &'a ast::Lifetime) {
276+
fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) {
276277
run_early_pass!(self, check_lifetime, lt);
277278
self.check_id(lt.id);
278279
}

compiler/rustc_lint/src/levels.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_middle::lint::{
1414
use rustc_middle::ty::query::Providers;
1515
use rustc_middle::ty::{RegisteredTools, TyCtxt};
1616
use rustc_session::lint::{
17-
builtin::{self, FORBIDDEN_LINT_GROUPS, UNFULFILLED_LINT_EXPECTATIONS},
17+
builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS},
1818
Level, Lint, LintExpectationId, LintId,
1919
};
2020
use rustc_session::parse::{add_feature_diagnostics, feature_err};
@@ -259,6 +259,14 @@ impl<'s> LintLevelsBuilder<'s> {
259259
let sess = self.sess;
260260
let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
261261
for (attr_index, attr) in attrs.iter().enumerate() {
262+
if attr.has_name(sym::automatically_derived) {
263+
self.current_specs_mut().insert(
264+
LintId::of(SINGLE_USE_LIFETIMES),
265+
(Level::Allow, LintLevelSource::Default),
266+
);
267+
continue;
268+
}
269+
262270
let level = match Level::from_attr(attr) {
263271
None => continue,
264272
Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {

compiler/rustc_lint_defs/src/lib.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,11 @@ pub enum BuiltinLintDiagnostics {
423423
DeprecatedMacro(Option<Symbol>, Span),
424424
MissingAbi(Span, Abi),
425425
UnusedDocComment(Span),
426-
UnusedBuiltinAttribute { attr_name: Symbol, macro_name: String, invoc_span: Span },
426+
UnusedBuiltinAttribute {
427+
attr_name: Symbol,
428+
macro_name: String,
429+
invoc_span: Span,
430+
},
427431
PatternsInFnsWithoutBody(Span, Ident),
428432
LegacyDeriveHelpers(Span),
429433
ProcMacroBackCompat(String),
@@ -435,6 +439,16 @@ pub enum BuiltinLintDiagnostics {
435439
UnicodeTextFlow(Span, String),
436440
UnexpectedCfg((Symbol, Span), Option<(Symbol, Span)>),
437441
DeprecatedWhereclauseLocation(Span, String),
442+
SingleUseLifetime {
443+
/// Span of the parameter which declares this lifetime.
444+
param_span: Span,
445+
/// Span of the code that should be removed when eliding this lifetime.
446+
/// This span should include leading or trailing comma.
447+
deletion_span: Span,
448+
/// Span of the single use, or None if the lifetime is never used.
449+
/// If true, the lifetime will be fully elided.
450+
use_span: Option<(Span, bool)>,
451+
},
438452
}
439453

440454
/// Lints that are buffered up early on in the `Session` before the

compiler/rustc_parse/src/parser/ty.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ impl<'a> Parser<'a> {
518518
kind: rustc_ast::VisibilityKind::Inherited,
519519
tokens: None,
520520
};
521+
let span_start = self.token.span;
521522
let ast::FnHeader { ext, unsafety, constness, asyncness } =
522523
self.parse_fn_front_matter(&inherited_vis)?;
523524
let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?;
@@ -531,7 +532,8 @@ impl<'a> Parser<'a> {
531532
if let ast::Async::Yes { span, .. } = asyncness {
532533
self.error_fn_ptr_bad_qualifier(whole_span, span, "async");
533534
}
534-
Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl })))
535+
let decl_span = span_start.to(self.token.span);
536+
Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span })))
535537
}
536538

537539
/// Emit an error for the given bad function pointer qualifier.

compiler/rustc_passes/src/hir_stats.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
318318
ast_visit::walk_variant(self, v)
319319
}
320320

321-
fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime) {
321+
fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime, _: ast_visit::LifetimeCtxt) {
322322
self.record("Lifetime", Id::None, lifetime);
323323
ast_visit::walk_lifetime(self, lifetime)
324324
}

0 commit comments

Comments
 (0)