Skip to content

Commit 9d98207

Browse files
committed
Split needless_lifetime '_ suggestions into elidable_lifetime_names
1 parent e692cd4 commit 9d98207

14 files changed

+675
-605
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5548,6 +5548,7 @@ Released 2018-09-13
55485548
[`duplicated_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicated_attributes
55495549
[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
55505550
[`eager_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#eager_transmute
5551+
[`elidable_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#elidable_lifetime_names
55515552
[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
55525553
[`empty_docs`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_docs
55535554
[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
271271
crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
272272
crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO,
273273
crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO,
274+
crate::lifetimes::ELIDABLE_LIFETIME_NAMES_INFO,
274275
crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
275276
crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
276277
crate::lines_filter_map_ok::LINES_FILTER_MAP_OK_INFO,

clippy_lints/src/lifetimes.rs

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,40 @@ declare_clippy_lint! {
6363
would allow omitting them"
6464
}
6565

66+
declare_clippy_lint! {
67+
/// ### What it does
68+
/// Checks for lifetime annotations which can be replaced with anonymous lifetimes (`'_`).
69+
///
70+
/// ### Why is this bad?
71+
/// The additional lifetimes can make the code look more complicated.
72+
///
73+
/// ### Known problems
74+
/// - We bail out if the function has a `where` clause where lifetimes
75+
/// are mentioned due to potential false positives.
76+
///
77+
/// ### Example
78+
/// ```no_run
79+
/// use std::str::Chars;
80+
///
81+
/// fn f<'a>(x: &'a str) -> Chars<'a> {
82+
/// x.chars()
83+
/// }
84+
/// ```
85+
///
86+
/// Use instead:
87+
/// ```no_run
88+
/// use std::str::Chars;
89+
///
90+
/// fn f(x: &str) -> Chars<'_> {
91+
/// x.chars()
92+
/// }
93+
/// ```
94+
#[clippy::version = "1.84.0"]
95+
pub ELIDABLE_LIFETIME_NAMES,
96+
pedantic,
97+
"lifetime name that can be replaced with the anonymous lifetime"
98+
}
99+
66100
declare_clippy_lint! {
67101
/// ### What it does
68102
/// Checks for lifetimes in generics that are never used
@@ -105,7 +139,11 @@ impl Lifetimes {
105139
}
106140
}
107141

108-
impl_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
142+
impl_lint_pass!(Lifetimes => [
143+
NEEDLESS_LIFETIMES,
144+
ELIDABLE_LIFETIME_NAMES,
145+
EXTRA_UNUSED_LIFETIMES,
146+
]);
109147

110148
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
111149
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -747,6 +785,15 @@ fn report_elidable_impl_lifetimes<'tcx>(
747785
report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true);
748786
}
749787

788+
#[derive(Copy, Clone)]
789+
enum ElidableUsage {
790+
/// Used in a ref (`&'a T`), can be removed
791+
Ref(Span),
792+
/// Used as a generic param (`T<'a>`) or an impl lifetime (`impl T + 'a`), can be replaced
793+
/// with `'_`
794+
Other(Span),
795+
}
796+
750797
/// Generate diagnostic messages for elidable lifetimes.
751798
fn report_elidable_lifetimes(
752799
cx: &LateContext<'_>,
@@ -764,9 +811,29 @@ fn report_elidable_lifetimes(
764811
.collect::<Vec<_>>()
765812
.join(", ");
766813

814+
let elidable_usages: Vec<ElidableUsage> = usages
815+
.iter()
816+
.filter(|usage| named_lifetime(usage).is_some_and(|id| elidable_lts.contains(&id)))
817+
.map(|usage| match cx.tcx.parent_hir_node(usage.hir_id) {
818+
Node::Ty(Ty {
819+
kind: TyKind::Ref(..), ..
820+
}) => ElidableUsage::Ref(usage.ident.span),
821+
_ => ElidableUsage::Other(usage.ident.span),
822+
})
823+
.collect();
824+
825+
let lint = if elidable_usages
826+
.iter()
827+
.any(|usage| matches!(usage, ElidableUsage::Other(_)))
828+
{
829+
ELIDABLE_LIFETIME_NAMES
830+
} else {
831+
NEEDLESS_LIFETIMES
832+
};
833+
767834
span_lint_and_then(
768835
cx,
769-
NEEDLESS_LIFETIMES,
836+
lint,
770837
elidable_lts
771838
.iter()
772839
.map(|&lt| cx.tcx.def_span(lt))
@@ -786,7 +853,7 @@ fn report_elidable_lifetimes(
786853
return;
787854
};
788855

789-
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) {
856+
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, &elidable_usages) {
790857
diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);
791858
}
792859
},
@@ -797,7 +864,7 @@ fn elision_suggestions(
797864
cx: &LateContext<'_>,
798865
generics: &Generics<'_>,
799866
elidable_lts: &[LocalDefId],
800-
usages: &[Lifetime],
867+
usages: &[ElidableUsage],
801868
) -> Option<Vec<(Span, String)>> {
802869
let explicit_params = generics
803870
.params
@@ -837,26 +904,21 @@ fn elision_suggestions(
837904
.collect::<Option<Vec<_>>>()?
838905
};
839906

840-
suggestions.extend(
841-
usages
842-
.iter()
843-
.filter(|usage| named_lifetime(usage).is_some_and(|id| elidable_lts.contains(&id)))
844-
.map(|usage| {
845-
match cx.tcx.parent_hir_node(usage.hir_id) {
846-
Node::Ty(Ty {
847-
kind: TyKind::Ref(..), ..
848-
}) => {
849-
// expand `&'a T` to `&'a T`
850-
// ^^ ^^^
851-
let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span);
852-
853-
(span, String::new())
854-
},
855-
// `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
856-
_ => (usage.ident.span, String::from("'_")),
857-
}
858-
}),
859-
);
907+
suggestions.extend(usages.iter().map(|&usage| {
908+
match usage {
909+
ElidableUsage::Ref(span) => {
910+
// expand `&'a T` to `&'a T`
911+
// ^^ ^^^
912+
let span = cx.sess().source_map().span_extend_while_whitespace(span);
913+
914+
(span, String::new())
915+
},
916+
ElidableUsage::Other(span) => {
917+
// `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
918+
(span, String::from("'_"))
919+
},
920+
}
921+
}));
860922

861923
Some(suggestions)
862924
}

tests/ui/crashes/needless_lifetimes_impl_trait.fixed renamed to tests/ui/crashes/elidable_lifetime_names_impl_trait.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![deny(clippy::needless_lifetimes)]
1+
#![deny(clippy::elidable_lifetime_names)]
22
#![allow(dead_code)]
33

44
trait Foo {}

tests/ui/crashes/needless_lifetimes_impl_trait.rs renamed to tests/ui/crashes/elidable_lifetime_names_impl_trait.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![deny(clippy::needless_lifetimes)]
1+
#![deny(clippy::elidable_lifetime_names)]
22
#![allow(dead_code)]
33

44
trait Foo {}

tests/ui/crashes/needless_lifetimes_impl_trait.stderr renamed to tests/ui/crashes/elidable_lifetime_names_impl_trait.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
error: the following explicit lifetimes could be elided: 'a
2-
--> tests/ui/crashes/needless_lifetimes_impl_trait.rs:12:6
2+
--> tests/ui/crashes/elidable_lifetime_names_impl_trait.rs:12:6
33
|
44
LL | impl<'a> Foo for Baz<'a> {}
55
| ^^ ^^
66
|
77
note: the lint level is defined here
8-
--> tests/ui/crashes/needless_lifetimes_impl_trait.rs:1:9
8+
--> tests/ui/crashes/elidable_lifetime_names_impl_trait.rs:1:9
99
|
10-
LL | #![deny(clippy::needless_lifetimes)]
11-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
10+
LL | #![deny(clippy::elidable_lifetime_names)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212
help: elide the lifetimes
1313
|
1414
LL - impl<'a> Foo for Baz<'a> {}
1515
LL + impl Foo for Baz<'_> {}
1616
|
1717

1818
error: the following explicit lifetimes could be elided: 'a
19-
--> tests/ui/crashes/needless_lifetimes_impl_trait.rs:15:12
19+
--> tests/ui/crashes/elidable_lifetime_names_impl_trait.rs:15:12
2020
|
2121
LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
2222
| ^^ ^^ ^^

0 commit comments

Comments
 (0)