Skip to content

Commit efcf1f5

Browse files
committed
Split needless_lifetime '_ suggestions into elidable_lifetime_names
1 parent 0fa1706 commit efcf1f5

14 files changed

+688
-638
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5592,6 +5592,7 @@ Released 2018-09-13
55925592
[`duplicated_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicated_attributes
55935593
[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
55945594
[`eager_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#eager_transmute
5595+
[`elidable_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#elidable_lifetime_names
55955596
[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
55965597
[`empty_docs`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_docs
55975598
[`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
@@ -273,6 +273,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
273273
crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
274274
crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO,
275275
crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO,
276+
crate::lifetimes::ELIDABLE_LIFETIME_NAMES_INFO,
276277
crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
277278
crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
278279
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
@@ -38,8 +38,8 @@ declare_clippy_lint! {
3838
/// them leads to more readable code.
3939
///
4040
/// ### Known problems
41-
/// - We bail out if the function has a `where` clause where lifetimes
42-
/// are mentioned due to potential false positives.
41+
/// This lint ignores functions with `where` clauses that reference
42+
/// lifetimes to prevent false positives.
4343
///
4444
/// ### Example
4545
/// ```no_run
@@ -62,6 +62,38 @@ declare_clippy_lint! {
6262
would allow omitting them"
6363
}
6464

65+
declare_clippy_lint! {
66+
/// ### What it does
67+
/// Checks for lifetime annotations which can be replaced with anonymous lifetimes (`'_`).
68+
///
69+
/// ### Why is this bad?
70+
/// The additional lifetimes can make the code look more complicated.
71+
///
72+
/// ### Known problems
73+
/// This lint ignores functions with `where` clauses that reference
74+
/// lifetimes to prevent false positives.
75+
///
76+
/// ### Example
77+
/// ```no_run
78+
/// # use std::str::Chars;
79+
/// fn f<'a>(x: &'a str) -> Chars<'a> {
80+
/// x.chars()
81+
/// }
82+
/// ```
83+
///
84+
/// Use instead:
85+
/// ```no_run
86+
/// # use std::str::Chars;
87+
/// fn f(x: &str) -> Chars<'_> {
88+
/// x.chars()
89+
/// }
90+
/// ```
91+
#[clippy::version = "1.84.0"]
92+
pub ELIDABLE_LIFETIME_NAMES,
93+
pedantic,
94+
"lifetime name that can be replaced with the anonymous lifetime"
95+
}
96+
6597
declare_clippy_lint! {
6698
/// ### What it does
6799
/// Checks for lifetimes in generics that are never used
@@ -104,7 +136,11 @@ impl Lifetimes {
104136
}
105137
}
106138

107-
impl_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
139+
impl_lint_pass!(Lifetimes => [
140+
NEEDLESS_LIFETIMES,
141+
ELIDABLE_LIFETIME_NAMES,
142+
EXTRA_UNUSED_LIFETIMES,
143+
]);
108144

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

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

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

788-
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) {
853+
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, &elidable_usages) {
789854
diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);
790855
}
791856
},
@@ -796,7 +861,7 @@ fn elision_suggestions(
796861
cx: &LateContext<'_>,
797862
generics: &Generics<'_>,
798863
elidable_lts: &[LocalDefId],
799-
usages: &[Lifetime],
864+
usages: &[ElidableUsage],
800865
) -> Option<Vec<(Span, String)>> {
801866
let explicit_params = generics
802867
.params
@@ -836,26 +901,21 @@ fn elision_suggestions(
836901
.collect::<Option<Vec<_>>>()?
837902
};
838903

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

860920
Some(suggestions)
861921
}

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

Lines changed: 3 additions & 3 deletions
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 {}
@@ -10,11 +10,11 @@ struct Baz<'a> {
1010
}
1111

1212
impl Foo for Baz<'_> {}
13-
//~^ needless_lifetimes
13+
//~^ elidable_lifetime_names
1414

1515
impl Bar {
1616
fn baz(&self) -> impl Foo + '_ {
17-
//~^ needless_lifetimes
17+
//~^ elidable_lifetime_names
1818

1919
Baz { bar: self }
2020
}

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

Lines changed: 3 additions & 3 deletions
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 {}
@@ -10,11 +10,11 @@ struct Baz<'a> {
1010
}
1111

1212
impl<'a> Foo for Baz<'a> {}
13-
//~^ needless_lifetimes
13+
//~^ elidable_lifetime_names
1414

1515
impl Bar {
1616
fn baz<'a>(&'a self) -> impl Foo + 'a {
17-
//~^ needless_lifetimes
17+
//~^ elidable_lifetime_names
1818

1919
Baz { bar: self }
2020
}

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:16:12
19+
--> tests/ui/crashes/elidable_lifetime_names_impl_trait.rs:16:12
2020
|
2121
LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
2222
| ^^ ^^ ^^

0 commit comments

Comments
 (0)