Skip to content

Commit 50cc391

Browse files
committed
Split needless_lifetime '_ suggestions into elidable_lifetime_names
1 parent e692cd4 commit 50cc391

14 files changed

+685
-607
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 & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ declare_clippy_lint! {
3939
/// them leads to more readable code.
4040
///
4141
/// ### Known problems
42-
/// - We bail out if the function has a `where` clause where lifetimes
43-
/// are mentioned due to potential false positives.
42+
/// This lint ignores functions with `where` clauses that reference
43+
/// lifetimes to prevent false positives.
4444
///
4545
/// ### Example
4646
/// ```no_run
@@ -63,6 +63,38 @@ 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+
/// This lint ignores functions with `where` clauses that reference
75+
/// lifetimes to prevent false positives.
76+
///
77+
/// ### Example
78+
/// ```no_run
79+
/// # use std::str::Chars;
80+
/// fn f<'a>(x: &'a str) -> Chars<'a> {
81+
/// x.chars()
82+
/// }
83+
/// ```
84+
///
85+
/// Use instead:
86+
/// ```no_run
87+
/// # use std::str::Chars;
88+
/// fn f(x: &str) -> Chars<'_> {
89+
/// x.chars()
90+
/// }
91+
/// ```
92+
#[clippy::version = "1.84.0"]
93+
pub ELIDABLE_LIFETIME_NAMES,
94+
pedantic,
95+
"lifetime name that can be replaced with the anonymous lifetime"
96+
}
97+
6698
declare_clippy_lint! {
6799
/// ### What it does
68100
/// Checks for lifetimes in generics that are never used
@@ -105,7 +137,11 @@ impl Lifetimes {
105137
}
106138
}
107139

108-
impl_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
140+
impl_lint_pass!(Lifetimes => [
141+
NEEDLESS_LIFETIMES,
142+
ELIDABLE_LIFETIME_NAMES,
143+
EXTRA_UNUSED_LIFETIMES,
144+
]);
109145

110146
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
111147
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -747,6 +783,15 @@ fn report_elidable_impl_lifetimes<'tcx>(
747783
report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true);
748784
}
749785

786+
#[derive(Copy, Clone)]
787+
enum ElidableUsage {
788+
/// Used in a ref (`&'a T`), can be removed
789+
Ref(Span),
790+
/// Used as a generic param (`T<'a>`) or an impl lifetime (`impl T + 'a`), can be replaced
791+
/// with `'_`
792+
Other(Span),
793+
}
794+
750795
/// Generate diagnostic messages for elidable lifetimes.
751796
fn report_elidable_lifetimes(
752797
cx: &LateContext<'_>,
@@ -764,9 +809,29 @@ fn report_elidable_lifetimes(
764809
.collect::<Vec<_>>()
765810
.join(", ");
766811

812+
let elidable_usages: Vec<ElidableUsage> = usages
813+
.iter()
814+
.filter(|usage| named_lifetime(usage).is_some_and(|id| elidable_lts.contains(&id)))
815+
.map(|usage| match cx.tcx.parent_hir_node(usage.hir_id) {
816+
Node::Ty(Ty {
817+
kind: TyKind::Ref(..), ..
818+
}) => ElidableUsage::Ref(usage.ident.span),
819+
_ => ElidableUsage::Other(usage.ident.span),
820+
})
821+
.collect();
822+
823+
let lint = if elidable_usages
824+
.iter()
825+
.any(|usage| matches!(usage, ElidableUsage::Other(_)))
826+
{
827+
ELIDABLE_LIFETIME_NAMES
828+
} else {
829+
NEEDLESS_LIFETIMES
830+
};
831+
767832
span_lint_and_then(
768833
cx,
769-
NEEDLESS_LIFETIMES,
834+
lint,
770835
elidable_lts
771836
.iter()
772837
.map(|&lt| cx.tcx.def_span(lt))
@@ -786,7 +851,7 @@ fn report_elidable_lifetimes(
786851
return;
787852
};
788853

789-
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) {
854+
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, &elidable_usages) {
790855
diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);
791856
}
792857
},
@@ -797,7 +862,7 @@ fn elision_suggestions(
797862
cx: &LateContext<'_>,
798863
generics: &Generics<'_>,
799864
elidable_lts: &[LocalDefId],
800-
usages: &[Lifetime],
865+
usages: &[ElidableUsage],
801866
) -> Option<Vec<(Span, String)>> {
802867
let explicit_params = generics
803868
.params
@@ -837,26 +902,21 @@ fn elision_suggestions(
837902
.collect::<Option<Vec<_>>>()?
838903
};
839904

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-
);
905+
suggestions.extend(usages.iter().map(|&usage| {
906+
match usage {
907+
ElidableUsage::Ref(span) => {
908+
// expand `&'a T` to `&'a T`
909+
// ^^ ^^^
910+
let span = cx.sess().source_map().span_extend_while_whitespace(span);
911+
912+
(span, String::new())
913+
},
914+
ElidableUsage::Other(span) => {
915+
// `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
916+
(span, String::from("'_"))
917+
},
918+
}
919+
}));
860920

861921
Some(suggestions)
862922
}

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)