Skip to content

Commit 03ada72

Browse files
committed
Split needless_lifetime '_ suggestions into elidable_lifetime_names
1 parent dcbe3ad commit 03ada72

14 files changed

+671
-601
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
@@ -61,6 +61,40 @@ declare_clippy_lint! {
6161
would allow omitting them"
6262
}
6363

64+
declare_clippy_lint! {
65+
/// ### What it does
66+
/// Checks for lifetime annotations which can be replaced with anonymous lifetimes (`'_`).
67+
///
68+
/// ### Why is this bad?
69+
/// The additional lifetimes can make the code look more complicated.
70+
///
71+
/// ### Known problems
72+
/// - We bail out if the function has a `where` clause where lifetimes
73+
/// are mentioned due to potential false positives.
74+
///
75+
/// ### Example
76+
/// ```no_run
77+
/// use std::str::Chars;
78+
///
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+
///
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+
6498
declare_clippy_lint! {
6599
/// ### What it does
66100
/// Checks for lifetimes in generics that are never used
@@ -91,7 +125,11 @@ declare_clippy_lint! {
91125
"unused lifetimes in function definitions"
92126
}
93127

94-
declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
128+
declare_lint_pass!(Lifetimes => [
129+
NEEDLESS_LIFETIMES,
130+
ELIDABLE_LIFETIME_NAMES,
131+
EXTRA_UNUSED_LIFETIMES,
132+
]);
95133

96134
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
97135
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -721,6 +759,15 @@ fn report_elidable_impl_lifetimes<'tcx>(
721759
report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true);
722760
}
723761

762+
#[derive(Copy, Clone)]
763+
enum ElidableUsage {
764+
/// Used in a ref (`&'a T`), can be removed
765+
Ref(Span),
766+
/// Used as a generic param (`T<'a>`) or an impl lifetime (`impl T + 'a`), can be replaced
767+
/// with `'_`
768+
Other(Span),
769+
}
770+
724771
/// Generate diagnostic messages for elidable lifetimes.
725772
fn report_elidable_lifetimes(
726773
cx: &LateContext<'_>,
@@ -738,9 +785,29 @@ fn report_elidable_lifetimes(
738785
.collect::<Vec<_>>()
739786
.join(", ");
740787

788+
let elidable_usages: Vec<ElidableUsage> = usages
789+
.iter()
790+
.filter(|usage| named_lifetime(usage).is_some_and(|id| elidable_lts.contains(&id)))
791+
.map(|usage| match cx.tcx.parent_hir_node(usage.hir_id) {
792+
Node::Ty(Ty {
793+
kind: TyKind::Ref(..), ..
794+
}) => ElidableUsage::Ref(usage.ident.span),
795+
_ => ElidableUsage::Other(usage.ident.span),
796+
})
797+
.collect();
798+
799+
let lint = if elidable_usages
800+
.iter()
801+
.any(|usage| matches!(usage, ElidableUsage::Other(_)))
802+
{
803+
ELIDABLE_LIFETIME_NAMES
804+
} else {
805+
NEEDLESS_LIFETIMES
806+
};
807+
741808
span_lint_and_then(
742809
cx,
743-
NEEDLESS_LIFETIMES,
810+
lint,
744811
elidable_lts
745812
.iter()
746813
.map(|&lt| cx.tcx.def_span(lt))
@@ -760,7 +827,7 @@ fn report_elidable_lifetimes(
760827
return;
761828
};
762829

763-
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) {
830+
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, &elidable_usages) {
764831
diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);
765832
}
766833
},
@@ -771,7 +838,7 @@ fn elision_suggestions(
771838
cx: &LateContext<'_>,
772839
generics: &Generics<'_>,
773840
elidable_lts: &[LocalDefId],
774-
usages: &[Lifetime],
841+
usages: &[ElidableUsage],
775842
) -> Option<Vec<(Span, String)>> {
776843
let explicit_params = generics
777844
.params
@@ -811,26 +878,21 @@ fn elision_suggestions(
811878
.collect::<Option<Vec<_>>>()?
812879
};
813880

814-
suggestions.extend(
815-
usages
816-
.iter()
817-
.filter(|usage| named_lifetime(usage).is_some_and(|id| elidable_lts.contains(&id)))
818-
.map(|usage| {
819-
match cx.tcx.parent_hir_node(usage.hir_id) {
820-
Node::Ty(Ty {
821-
kind: TyKind::Ref(..), ..
822-
}) => {
823-
// expand `&'a T` to `&'a T`
824-
// ^^ ^^^
825-
let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span);
826-
827-
(span, String::new())
828-
},
829-
// `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
830-
_ => (usage.ident.span, String::from("'_")),
831-
}
832-
}),
833-
);
881+
suggestions.extend(usages.iter().map(|&usage| {
882+
match usage {
883+
ElidableUsage::Ref(span) => {
884+
// expand `&'a T` to `&'a T`
885+
// ^^ ^^^
886+
let span = cx.sess().source_map().span_extend_while_whitespace(span);
887+
888+
(span, String::new())
889+
},
890+
ElidableUsage::Other(span) => {
891+
// `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
892+
(span, String::from("'_"))
893+
},
894+
}
895+
}));
834896

835897
Some(suggestions)
836898
}

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)