diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 727e9b172a87..679fb983d532 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::trait_ref_of_method; +use clippy_utils::{is_from_proc_macro, trait_ref_of_method}; use itertools::Itertools; use rustc_ast::visit::{try_visit, walk_list}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; @@ -149,9 +149,12 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { .. } = item.kind { - check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv); + check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv, || { + is_from_proc_macro(cx, item) + }); } else if let ItemKind::Impl(impl_) = &item.kind && !item.span.from_expansion() + && !is_from_proc_macro(cx, item) { report_extra_impl_lifetimes(cx, impl_); } @@ -169,6 +172,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { item.span, report_extra_lifetimes, self.msrv, + || is_from_proc_macro(cx, item), ); } } @@ -179,7 +183,17 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, self.msrv); + check_fn_inner( + cx, + sig, + body, + trait_sig, + item.generics, + item.span, + true, + self.msrv, + || is_from_proc_macro(cx, item), + ); } } } @@ -194,6 +208,7 @@ fn check_fn_inner<'tcx>( span: Span, report_extra_lifetimes: bool, msrv: Msrv, + is_from_proc_macro: impl FnOnce() -> bool, ) { if span.in_external_macro(cx.sess().source_map()) || has_where_lifetimes(cx, generics) { return; @@ -245,10 +260,19 @@ fn check_fn_inner<'tcx>( } } - if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params, msrv) { - if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) { - return; - } + let elidable = could_use_elision(cx, sig.decl, body, trait_sig, generics.params, msrv); + let has_elidable_lts = elidable + .as_ref() + .is_some_and(|(_, usages)| !usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span))); + + // Only check is_from_proc_macro if we're about to emit a lint (it's an expensive check) + if (has_elidable_lts || report_extra_lifetimes) && is_from_proc_macro() { + return; + } + + if let Some((elidable_lts, usages)) = elidable + && has_elidable_lts + { // async functions have usages whose spans point at the lifetime declaration which messes up // suggestions let include_suggestions = !sig.header.is_async(); diff --git a/tests/ui/extra_unused_lifetimes.rs b/tests/ui/extra_unused_lifetimes.rs index 5fdcd5a7e078..677523489599 100644 --- a/tests/ui/extra_unused_lifetimes.rs +++ b/tests/ui/extra_unused_lifetimes.rs @@ -1,4 +1,5 @@ //@aux-build:proc_macro_derive.rs +//@aux-build:proc_macros.rs #![allow( unused, @@ -11,6 +12,7 @@ #[macro_use] extern crate proc_macro_derive; +extern crate proc_macros; fn empty() {} @@ -148,4 +150,34 @@ mod issue_13578 { impl<'a, T: 'a> Foo for Option where &'a T: Foo {} } +// no lint on proc macro generated code +mod proc_macro_generated { + use proc_macros::external; + + // no lint on external macro (extra unused lifetimes in impl block) + external! { + struct ExternalImplStruct; + + impl<'a> ExternalImplStruct { + fn foo() {} + } + } + + // no lint on external macro (extra unused lifetimes in method) + external! { + struct ExternalMethodStruct; + + impl ExternalMethodStruct { + fn bar<'a>(&self) {} + } + } + + // no lint on external macro (extra unused lifetimes in trait method) + external! { + trait ExternalUnusedLifetimeTrait { + fn unused_lt<'a>(x: u8) {} + } + } +} + fn main() {} diff --git a/tests/ui/extra_unused_lifetimes.stderr b/tests/ui/extra_unused_lifetimes.stderr index 0cecbbe80f76..d748376d476c 100644 --- a/tests/ui/extra_unused_lifetimes.stderr +++ b/tests/ui/extra_unused_lifetimes.stderr @@ -1,5 +1,5 @@ error: this lifetime isn't used in the function definition - --> tests/ui/extra_unused_lifetimes.rs:19:14 + --> tests/ui/extra_unused_lifetimes.rs:21:14 | LL | fn unused_lt<'a>(x: u8) {} | ^^ @@ -8,37 +8,37 @@ LL | fn unused_lt<'a>(x: u8) {} = help: to override `-D warnings` add `#[allow(clippy::extra_unused_lifetimes)]` error: this lifetime isn't used in the function definition - --> tests/ui/extra_unused_lifetimes.rs:47:10 + --> tests/ui/extra_unused_lifetimes.rs:49:10 | LL | fn x<'a>(&self) {} | ^^ error: this lifetime isn't used in the function definition - --> tests/ui/extra_unused_lifetimes.rs:74:22 + --> tests/ui/extra_unused_lifetimes.rs:76:22 | LL | fn unused_lt<'a>(x: u8) {} | ^^ error: this lifetime isn't used in the impl - --> tests/ui/extra_unused_lifetimes.rs:86:10 + --> tests/ui/extra_unused_lifetimes.rs:88:10 | LL | impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar { | ^^ error: this lifetime isn't used in the impl - --> tests/ui/extra_unused_lifetimes.rs:93:10 + --> tests/ui/extra_unused_lifetimes.rs:95:10 | LL | impl<'b> Scalar { | ^^ error: this lifetime isn't used in the function definition - --> tests/ui/extra_unused_lifetimes.rs:95:26 + --> tests/ui/extra_unused_lifetimes.rs:97:26 | LL | pub fn something<'c>() -> Self { | ^^ error: this lifetime isn't used in the impl - --> tests/ui/extra_unused_lifetimes.rs:125:10 + --> tests/ui/extra_unused_lifetimes.rs:127:10 | LL | impl<'a, T: Source + ?Sized + 'a> Source for Box { | ^^ diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed index 15ca409c95bd..90a07454b4d7 100644 --- a/tests/ui/needless_lifetimes.fixed +++ b/tests/ui/needless_lifetimes.fixed @@ -470,13 +470,36 @@ mod in_macro { } } - // no lint on external macro + // no lint on external macro (standalone function) external! { fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 { unimplemented!() } } + // no lint on external macro (method in impl block) + external! { + struct ExternalStruct; + + impl ExternalStruct { + fn needless_lifetime_method<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + } + } + + // no lint on external macro (trait method) + external! { + trait ExternalTrait { + fn needless_lifetime_trait_method<'a>(x: &'a u8) -> &'a u8; + } + } + + // no lint on external macro (extra unused lifetimes in function) + external! { + fn extra_unused_lifetime<'a>(x: u8) {} + } + inline! { fn f<$'a>(arg: &$'a str) -> &$'a str { arg diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index af9649d72987..6df38897f42d 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -470,13 +470,36 @@ mod in_macro { } } - // no lint on external macro + // no lint on external macro (standalone function) external! { fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 { unimplemented!() } } + // no lint on external macro (method in impl block) + external! { + struct ExternalStruct; + + impl ExternalStruct { + fn needless_lifetime_method<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + } + } + + // no lint on external macro (trait method) + external! { + trait ExternalTrait { + fn needless_lifetime_trait_method<'a>(x: &'a u8) -> &'a u8; + } + } + + // no lint on external macro (extra unused lifetimes in function) + external! { + fn extra_unused_lifetime<'a>(x: u8) {} + } + inline! { fn f<$'a>(arg: &$'a str) -> &$'a str { arg