Skip to content

Commit 7a768b8

Browse files
Add redundant_lifetime_args lint
1 parent 5facb42 commit 7a768b8

File tree

5 files changed

+176
-8
lines changed

5 files changed

+176
-8
lines changed

compiler/rustc_errors/src/diagnostic_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ impl<'a, T: Clone + IntoDiagnosticArg> IntoDiagnosticArg for &'a T {
4444
}
4545
}
4646

47+
#[macro_export]
4748
macro_rules! into_diagnostic_arg_using_display {
4849
($( $ty:ty ),+ $(,)?) => {
4950
$(

compiler/rustc_lint/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,8 @@ lint_reason_must_be_string_literal = reason must be a string literal
470470
471471
lint_reason_must_come_last = reason in lint attribute must come last
472472
473+
lint_redundant_lifetime_args = lifetime `{$victim}` is required to be equal to `{$candidate}`, and is redundant and can be removed
474+
473475
lint_redundant_semicolons =
474476
unnecessary trailing {$multiple ->
475477
[true] semicolons

compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ mod opaque_hidden_inferred_bound;
8282
mod pass_by_value;
8383
mod passes;
8484
mod ptr_nulls;
85+
mod redundant_lifetime_args;
8586
mod redundant_semicolon;
8687
mod reference_casting;
8788
mod traits;
@@ -119,6 +120,7 @@ use noop_method_call::*;
119120
use opaque_hidden_inferred_bound::*;
120121
use pass_by_value::*;
121122
use ptr_nulls::*;
123+
use redundant_lifetime_args::RedundantLifetimeArgs;
122124
use redundant_semicolon::*;
123125
use reference_casting::*;
124126
use traits::*;
@@ -240,6 +242,7 @@ late_lint_methods!(
240242
MissingDebugImplementations: MissingDebugImplementations,
241243
MissingDoc: MissingDoc,
242244
AsyncFnInTrait: AsyncFnInTrait,
245+
RedundantLifetimeArgs: RedundantLifetimeArgs,
243246
]
244247
]
245248
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#![allow(rustc::diagnostic_outside_of_impl)]
2+
#![allow(rustc::untranslatable_diagnostic)]
3+
4+
use rustc_data_structures::fx::FxHashSet;
5+
use rustc_hir as hir;
6+
use rustc_hir::def::DefKind;
7+
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
8+
use rustc_infer::infer::{SubregionOrigin, TyCtxtInferExt};
9+
use rustc_macros::LintDiagnostic;
10+
use rustc_middle::ty::{self, TyCtxt};
11+
use rustc_span::DUMMY_SP;
12+
use rustc_trait_selection::traits::{outlives_bounds::InferCtxtExt, ObligationCtxt};
13+
14+
use crate::{LateContext, LateLintPass};
15+
16+
declare_lint! {
17+
/// Docs
18+
pub REDUNDANT_LIFETIME_ARGS,
19+
Allow,
20+
"do something"
21+
}
22+
23+
declare_lint_pass!(RedundantLifetimeArgs => [REDUNDANT_LIFETIME_ARGS]);
24+
25+
impl<'tcx> LateLintPass<'tcx> for RedundantLifetimeArgs {
26+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
27+
check(cx.tcx, cx.param_env, item.owner_id);
28+
}
29+
30+
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) {
31+
check(cx.tcx, cx.param_env, item.owner_id);
32+
}
33+
34+
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) {
35+
if cx
36+
.tcx
37+
.hir()
38+
.expect_item(cx.tcx.local_parent(item.owner_id.def_id))
39+
.expect_impl()
40+
.of_trait
41+
.is_some()
42+
{
43+
// Don't check for redundant lifetimes for trait implementations,
44+
// since the signature is required to be compatible with the trait.
45+
return;
46+
}
47+
48+
check(cx.tcx, cx.param_env, item.owner_id);
49+
}
50+
}
51+
52+
fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::OwnerId) {
53+
let def_kind = tcx.def_kind(owner_id);
54+
match def_kind {
55+
DefKind::Struct
56+
| DefKind::Union
57+
| DefKind::Enum
58+
| DefKind::Trait
59+
| DefKind::TraitAlias
60+
| DefKind::AssocTy
61+
| DefKind::Fn
62+
| DefKind::Const
63+
| DefKind::AssocFn
64+
| DefKind::AssocConst
65+
| DefKind::Impl { of_trait: _ } => {
66+
// Proceed
67+
}
68+
DefKind::Mod
69+
| DefKind::Variant
70+
| DefKind::TyAlias
71+
| DefKind::ForeignTy
72+
| DefKind::TyParam
73+
| DefKind::ConstParam
74+
| DefKind::Static(_)
75+
| DefKind::Ctor(_, _)
76+
| DefKind::Macro(_)
77+
| DefKind::ExternCrate
78+
| DefKind::Use
79+
| DefKind::ForeignMod
80+
| DefKind::AnonConst
81+
| DefKind::InlineConst
82+
| DefKind::OpaqueTy
83+
| DefKind::Field
84+
| DefKind::LifetimeParam
85+
| DefKind::GlobalAsm
86+
| DefKind::Closure => return,
87+
}
88+
89+
let infcx = &tcx.infer_ctxt().build();
90+
let ocx = ObligationCtxt::new(infcx);
91+
let Ok(assumed_wf_types) = ocx.assumed_wf_types(param_env, owner_id.def_id) else {
92+
return;
93+
};
94+
95+
let implied_bounds = infcx.implied_bounds_tys(param_env, owner_id.def_id, assumed_wf_types);
96+
let outlives_env = &OutlivesEnvironment::with_bounds(param_env, implied_bounds);
97+
98+
let mut lifetimes = vec![tcx.lifetimes.re_static];
99+
lifetimes.extend(
100+
ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()),
101+
);
102+
if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) {
103+
for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() {
104+
let ty::BoundVariableKind::Region(kind) = var else { continue };
105+
lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind));
106+
}
107+
}
108+
109+
// Keep track of lifetimes which have already been replaced with other lifetimes.
110+
let mut shadowed = FxHashSet::default();
111+
112+
for (idx, &candidate) in lifetimes.iter().enumerate() {
113+
if shadowed.contains(&candidate) {
114+
// Don't suggest removing a lifetime twice.
115+
continue;
116+
}
117+
118+
if !candidate.has_name() {
119+
// Can't rename a named lifetime with `'_` without ambiguity.
120+
continue;
121+
}
122+
123+
for &victim in &lifetimes[(idx + 1)..] {
124+
let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
125+
| ty::ReLateParam(ty::LateParamRegion {
126+
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
127+
..
128+
})) = victim.kind()
129+
else {
130+
continue;
131+
};
132+
133+
if tcx.parent(def_id) != owner_id.to_def_id() {
134+
// Do not rename generics not local to this item since
135+
// they'll overlap with this lint running on the parent.
136+
continue;
137+
}
138+
139+
let infcx = infcx.fork();
140+
infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), candidate, victim);
141+
infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), victim, candidate);
142+
if infcx.resolve_regions(outlives_env).is_empty() {
143+
shadowed.insert(victim);
144+
tcx.emit_spanned_lint(
145+
REDUNDANT_LIFETIME_ARGS,
146+
tcx.local_def_id_to_hir_id(def_id.expect_local()),
147+
tcx.def_span(def_id),
148+
RedundantLifetimeArgsList { candidate, victim },
149+
);
150+
}
151+
}
152+
}
153+
}
154+
155+
#[derive(LintDiagnostic)]
156+
#[diag(lint_redundant_lifetime_args)]
157+
struct RedundantLifetimeArgsList<'tcx> {
158+
candidate: ty::Region<'tcx>,
159+
victim: ty::Region<'tcx>,
160+
}

compiler/rustc_middle/src/ty/diagnostics.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,26 @@ use std::fmt::Write;
55
use std::ops::ControlFlow;
66

77
use crate::ty::{
8-
AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque, PolyTraitPredicate,
9-
Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
10-
TypeVisitor,
8+
self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque,
9+
PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
10+
TypeSuperVisitable, TypeVisitable, TypeVisitor,
1111
};
1212

1313
use rustc_data_structures::fx::FxHashMap;
14-
use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg};
14+
use rustc_errors::{
15+
into_diagnostic_arg_using_display, Applicability, Diagnostic, DiagnosticArgValue,
16+
IntoDiagnosticArg,
17+
};
1518
use rustc_hir as hir;
1619
use rustc_hir::def::DefKind;
1720
use rustc_hir::def_id::DefId;
1821
use rustc_hir::{PredicateOrigin, WherePredicate};
1922
use rustc_span::{BytePos, Span};
2023
use rustc_type_ir::TyKind::*;
2124

22-
impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
23-
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
24-
self.to_string().into_diagnostic_arg()
25-
}
25+
into_diagnostic_arg_using_display! {
26+
Ty<'_>,
27+
ty::Region<'_>,
2628
}
2729

2830
impl<'tcx> Ty<'tcx> {

0 commit comments

Comments
 (0)