Skip to content

Commit 8b7cfc7

Browse files
committed
Rust 1.81 and later support elision with explicit self types
Commit 9ef6e21 introduced a check to ensure that Clippy doesn't consider a lifetime present in an explicit self type as being the default for an elided output lifetime. For example, elision did not work in the case like: ```rust fn func(self: &Rc<Self>, &str) -> &str { … } ``` Since Rust 1.81.0, the lifetime in the self type is now considered the default for elision. Elision should then be suggested when appropriate.
1 parent 8f257c7 commit 8b7cfc7

File tree

6 files changed

+184
-74
lines changed

6 files changed

+184
-74
lines changed

clippy_lints/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
681681
store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
682682
store.register_late_pass(move |_| Box::new(loops::Loops::new(conf)));
683683
store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
684-
store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
684+
store.register_late_pass(move |_| Box::new(lifetimes::Lifetimes::new(conf)));
685685
store.register_late_pass(|_| Box::new(entry::HashMapPass));
686686
store.register_late_pass(|_| Box::new(minmax::MinMaxPass));
687687
store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv));

clippy_lints/src/lifetimes.rs

+39-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
use clippy_config::Conf;
12
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
3+
use clippy_utils::msrvs::{self, Msrv};
24
use clippy_utils::trait_ref_of_method;
35
use itertools::Itertools;
46
use rustc_ast::visit::{try_visit, walk_list};
@@ -20,7 +22,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
2022
use rustc_middle::hir::map::Map;
2123
use rustc_middle::hir::nested_filter as middle_nested_filter;
2224
use rustc_middle::lint::in_external_macro;
23-
use rustc_session::declare_lint_pass;
25+
use rustc_session::impl_lint_pass;
2426
use rustc_span::Span;
2527
use rustc_span::def_id::LocalDefId;
2628
use rustc_span::symbol::{Ident, kw};
@@ -91,7 +93,19 @@ declare_clippy_lint! {
9193
"unused lifetimes in function definitions"
9294
}
9395

94-
declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
96+
pub struct Lifetimes {
97+
msrv: Msrv,
98+
}
99+
100+
impl Lifetimes {
101+
pub fn new(conf: &'static Conf) -> Self {
102+
Self {
103+
msrv: conf.msrv.clone(),
104+
}
105+
}
106+
}
107+
108+
impl_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
95109

96110
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
97111
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -102,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
102116
..
103117
} = item.kind
104118
{
105-
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true);
119+
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, &self.msrv);
106120
} else if let ItemKind::Impl(impl_) = item.kind {
107121
if !item.span.from_expansion() {
108122
report_extra_impl_lifetimes(cx, impl_);
@@ -121,6 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
121135
item.generics,
122136
item.span,
123137
report_extra_lifetimes,
138+
&self.msrv,
124139
);
125140
}
126141
}
@@ -131,11 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
131146
TraitFn::Required(sig) => (None, Some(sig)),
132147
TraitFn::Provided(id) => (Some(id), None),
133148
};
134-
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true);
149+
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, &self.msrv);
135150
}
136151
}
152+
153+
extract_msrv_attr!(LateContext);
137154
}
138155

156+
#[allow(clippy::too_many_arguments)]
139157
fn check_fn_inner<'tcx>(
140158
cx: &LateContext<'tcx>,
141159
sig: &'tcx FnSig<'_>,
@@ -144,6 +162,7 @@ fn check_fn_inner<'tcx>(
144162
generics: &'tcx Generics<'_>,
145163
span: Span,
146164
report_extra_lifetimes: bool,
165+
msrv: &Msrv,
147166
) {
148167
if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) {
149168
return;
@@ -195,7 +214,7 @@ fn check_fn_inner<'tcx>(
195214
}
196215
}
197216

198-
if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) {
217+
if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params, msrv) {
199218
if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) {
200219
return;
201220
}
@@ -216,6 +235,7 @@ fn could_use_elision<'tcx>(
216235
body: Option<BodyId>,
217236
trait_sig: Option<&[Ident]>,
218237
named_generics: &'tcx [GenericParam<'_>],
238+
msrv: &Msrv,
219239
) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> {
220240
// There are two scenarios where elision works:
221241
// * no output references, all input references have different LT
@@ -249,17 +269,17 @@ fn could_use_elision<'tcx>(
249269
let input_lts = input_visitor.lts;
250270
let output_lts = output_visitor.lts;
251271

252-
if let Some(trait_sig) = trait_sig {
253-
if explicit_self_type(cx, func, trait_sig.first().copied()) {
254-
return None;
255-
}
272+
if let Some(trait_sig) = trait_sig
273+
&& non_elidable_self_type(cx, func, trait_sig.first().copied(), msrv)
274+
{
275+
return None;
256276
}
257277

258278
if let Some(body_id) = body {
259279
let body = cx.tcx.hir().body(body_id);
260280

261281
let first_ident = body.params.first().and_then(|param| param.pat.simple_ident());
262-
if explicit_self_type(cx, func, first_ident) {
282+
if non_elidable_self_type(cx, func, first_ident, msrv) {
263283
return None;
264284
}
265285

@@ -332,9 +352,15 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet<LocalDefI
332352
.collect()
333353
}
334354

335-
// elision doesn't work for explicit self types, see rust-lang/rust#69064
336-
fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
337-
if let Some(ident) = ident
355+
// elision doesn't work for explicit self types before Rust 1.81, see rust-lang/rust#69064
356+
fn non_elidable_self_type<'tcx>(
357+
cx: &LateContext<'tcx>,
358+
func: &FnDecl<'tcx>,
359+
ident: Option<Ident>,
360+
msrv: &Msrv,
361+
) -> bool {
362+
if !msrv.meets(msrvs::EXPLICIT_SELF_TYPE_ELISION)
363+
&& let Some(ident) = ident
338364
&& ident.name == kw::SelfLower
339365
&& !func.implicit_self.has_implicit_self()
340366
&& let Some(self_ty) = func.inputs.first()

clippy_utils/src/msrvs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ macro_rules! msrv_aliases {
2020
msrv_aliases! {
2121
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY }
2222
1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP }
23-
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE }
23+
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION }
2424
1,80,0 { BOX_INTO_ITER }
2525
1,77,0 { C_STR_LITERALS }
2626
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }

tests/ui/needless_lifetimes.fixed

+23-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
clippy::boxed_local,
77
clippy::extra_unused_type_parameters,
88
clippy::needless_pass_by_value,
9+
clippy::redundant_allocation,
910
clippy::unnecessary_wraps,
1011
dyn_drop,
1112
clippy::get_first
@@ -443,11 +444,20 @@ mod issue7296 {
443444
fn implicit_mut(&mut self) -> &() {
444445
&()
445446
}
446-
447-
fn explicit<'a>(self: &'a Arc<Self>) -> &'a () {
447+
#[clippy::msrv = "1.81"]
448+
fn explicit(self: &Arc<Self>) -> &() {
449+
&()
450+
}
451+
#[clippy::msrv = "1.81"]
452+
fn explicit_mut(self: &mut Rc<Self>) -> &() {
448453
&()
449454
}
450-
fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () {
455+
#[clippy::msrv = "1.80"]
456+
fn explicit_older<'a>(self: &'a Arc<Self>) -> &'a () {
457+
&()
458+
}
459+
#[clippy::msrv = "1.80"]
460+
fn explicit_mut_older<'a>(self: &'a mut Rc<Self>) -> &'a () {
451461
&()
452462
}
453463

@@ -462,8 +472,16 @@ mod issue7296 {
462472
&()
463473
}
464474

465-
fn explicit<'a>(self: &'a Arc<Self>) -> &'a ();
466-
fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () {
475+
#[clippy::msrv = "1.81"]
476+
fn explicit(self: &Arc<Self>) -> &();
477+
#[clippy::msrv = "1.81"]
478+
fn explicit_provided(self: &Arc<Self>) -> &() {
479+
&()
480+
}
481+
#[clippy::msrv = "1.80"]
482+
fn explicit_older<'a>(self: &'a Arc<Self>) -> &'a ();
483+
#[clippy::msrv = "1.80"]
484+
fn explicit_provided_older<'a>(self: &'a Arc<Self>) -> &'a () {
467485
&()
468486
}
469487

tests/ui/needless_lifetimes.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
clippy::boxed_local,
77
clippy::extra_unused_type_parameters,
88
clippy::needless_pass_by_value,
9+
clippy::redundant_allocation,
910
clippy::unnecessary_wraps,
1011
dyn_drop,
1112
clippy::get_first
@@ -443,13 +444,22 @@ mod issue7296 {
443444
fn implicit_mut<'a>(&'a mut self) -> &'a () {
444445
&()
445446
}
446-
447+
#[clippy::msrv = "1.81"]
447448
fn explicit<'a>(self: &'a Arc<Self>) -> &'a () {
448449
&()
449450
}
451+
#[clippy::msrv = "1.81"]
450452
fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () {
451453
&()
452454
}
455+
#[clippy::msrv = "1.80"]
456+
fn explicit_older<'a>(self: &'a Arc<Self>) -> &'a () {
457+
&()
458+
}
459+
#[clippy::msrv = "1.80"]
460+
fn explicit_mut_older<'a>(self: &'a mut Rc<Self>) -> &'a () {
461+
&()
462+
}
453463

454464
fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
455465
&()
@@ -462,10 +472,18 @@ mod issue7296 {
462472
&()
463473
}
464474

475+
#[clippy::msrv = "1.81"]
465476
fn explicit<'a>(self: &'a Arc<Self>) -> &'a ();
477+
#[clippy::msrv = "1.81"]
466478
fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () {
467479
&()
468480
}
481+
#[clippy::msrv = "1.80"]
482+
fn explicit_older<'a>(self: &'a Arc<Self>) -> &'a ();
483+
#[clippy::msrv = "1.80"]
484+
fn explicit_provided_older<'a>(self: &'a Arc<Self>) -> &'a () {
485+
&()
486+
}
469487

470488
fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
471489
fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {

0 commit comments

Comments
 (0)