Skip to content

Commit 1e546c5

Browse files
committed
Auto merge of #8278 - Alexendoo:needless-lifetime-explicit-self-ty, r=xFrednet
`needless_lifetimes`: ignore lifetimes in explicit self types changelog: false positive fix: [`needless_lifetimes`] no longer lints lifetimes in explicit self types They're not currently elidable (rust-lang/rust#69064) Fixes #7296
2 parents acfc161 + 9ef6e21 commit 1e546c5

File tree

3 files changed

+162
-35
lines changed

3 files changed

+162
-35
lines changed

clippy_lints/src/lifetimes.rs

+44-8
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_lint::{LateContext, LateLintPass};
1515
use rustc_middle::hir::map::Map;
1616
use rustc_session::{declare_lint_pass, declare_tool_lint};
1717
use rustc_span::source_map::Span;
18-
use rustc_span::symbol::{kw, Symbol};
18+
use rustc_span::symbol::{kw, Ident, Symbol};
1919

2020
declare_clippy_lint! {
2121
/// ### What it does
@@ -85,7 +85,7 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
8585
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
8686
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
8787
if let ItemKind::Fn(ref sig, ref generics, id) = item.kind {
88-
check_fn_inner(cx, sig.decl, Some(id), generics, item.span, true);
88+
check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true);
8989
}
9090
}
9191

@@ -96,6 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
9696
cx,
9797
sig.decl,
9898
Some(id),
99+
None,
99100
&item.generics,
100101
item.span,
101102
report_extra_lifetimes,
@@ -105,11 +106,11 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
105106

106107
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
107108
if let TraitItemKind::Fn(ref sig, ref body) = item.kind {
108-
let body = match *body {
109-
TraitFn::Required(_) => None,
110-
TraitFn::Provided(id) => Some(id),
109+
let (body, trait_sig) = match *body {
110+
TraitFn::Required(sig) => (None, Some(sig)),
111+
TraitFn::Provided(id) => (Some(id), None),
111112
};
112-
check_fn_inner(cx, sig.decl, body, &item.generics, item.span, true);
113+
check_fn_inner(cx, sig.decl, body, trait_sig, &item.generics, item.span, true);
113114
}
114115
}
115116
}
@@ -126,6 +127,7 @@ fn check_fn_inner<'tcx>(
126127
cx: &LateContext<'tcx>,
127128
decl: &'tcx FnDecl<'_>,
128129
body: Option<BodyId>,
130+
trait_sig: Option<&[Ident]>,
129131
generics: &'tcx Generics<'_>,
130132
span: Span,
131133
report_extra_lifetimes: bool,
@@ -167,7 +169,7 @@ fn check_fn_inner<'tcx>(
167169
}
168170
}
169171
}
170-
if could_use_elision(cx, decl, body, generics.params) {
172+
if could_use_elision(cx, decl, body, trait_sig, generics.params) {
171173
span_lint(
172174
cx,
173175
NEEDLESS_LIFETIMES,
@@ -181,10 +183,31 @@ fn check_fn_inner<'tcx>(
181183
}
182184
}
183185

186+
// elision doesn't work for explicit self types, see rust-lang/rust#69064
187+
fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
188+
if_chain! {
189+
if let Some(ident) = ident;
190+
if ident.name == kw::SelfLower;
191+
if !func.implicit_self.has_implicit_self();
192+
193+
if let Some(self_ty) = func.inputs.first();
194+
then {
195+
let mut visitor = RefVisitor::new(cx);
196+
visitor.visit_ty(self_ty);
197+
198+
!visitor.all_lts().is_empty()
199+
}
200+
else {
201+
false
202+
}
203+
}
204+
}
205+
184206
fn could_use_elision<'tcx>(
185207
cx: &LateContext<'tcx>,
186208
func: &'tcx FnDecl<'_>,
187209
body: Option<BodyId>,
210+
trait_sig: Option<&[Ident]>,
188211
named_generics: &'tcx [GenericParam<'_>],
189212
) -> bool {
190213
// There are two scenarios where elision works:
@@ -235,11 +258,24 @@ fn could_use_elision<'tcx>(
235258
let input_lts = input_visitor.lts;
236259
let output_lts = output_visitor.lts;
237260

261+
if let Some(trait_sig) = trait_sig {
262+
if explicit_self_type(cx, func, trait_sig.first().copied()) {
263+
return false;
264+
}
265+
}
266+
238267
if let Some(body_id) = body {
268+
let body = cx.tcx.hir().body(body_id);
269+
270+
let first_ident = body.params.first().and_then(|param| param.pat.simple_ident());
271+
if explicit_self_type(cx, func, first_ident) {
272+
return false;
273+
}
274+
239275
let mut checker = BodyLifetimeChecker {
240276
lifetimes_used_in_body: false,
241277
};
242-
checker.visit_expr(&cx.tcx.hir().body(body_id).value);
278+
checker.visit_expr(&body.value);
243279
if checker.lifetimes_used_in_body {
244280
return false;
245281
}

tests/ui/needless_lifetimes.rs

+50-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
#![warn(clippy::needless_lifetimes)]
2-
#![allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wraps, dyn_drop)]
2+
#![allow(
3+
dead_code,
4+
clippy::boxed_local,
5+
clippy::needless_pass_by_value,
6+
clippy::unnecessary_wraps,
7+
dyn_drop
8+
)]
39

410
fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
511

@@ -369,4 +375,47 @@ mod issue6159 {
369375
}
370376
}
371377

378+
mod issue7296 {
379+
use std::rc::Rc;
380+
use std::sync::Arc;
381+
382+
struct Foo;
383+
impl Foo {
384+
fn implicit<'a>(&'a self) -> &'a () {
385+
&()
386+
}
387+
fn implicit_mut<'a>(&'a mut self) -> &'a () {
388+
&()
389+
}
390+
391+
fn explicit<'a>(self: &'a Arc<Self>) -> &'a () {
392+
&()
393+
}
394+
fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () {
395+
&()
396+
}
397+
398+
fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
399+
&()
400+
}
401+
}
402+
403+
trait Bar {
404+
fn implicit<'a>(&'a self) -> &'a ();
405+
fn implicit_provided<'a>(&'a self) -> &'a () {
406+
&()
407+
}
408+
409+
fn explicit<'a>(self: &'a Arc<Self>) -> &'a ();
410+
fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () {
411+
&()
412+
}
413+
414+
fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
415+
fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
416+
&()
417+
}
418+
}
419+
}
420+
372421
fn main() {}

0 commit comments

Comments
 (0)