Skip elidable_lifetime_names lint for proc-macro generated code (#16402)
When linting code generated by proc macros (like `derivative`), the `elidable_lifetime_names` lint can produce fix suggestions with overlapping spans. This causes `cargo clippy --fix` to fail with "cannot replace slice of data that was already replaced". This change adds `is_from_proc_macro` checks to the three lint entry points (`check_item`, `check_impl_item`, `check_trait_item`) to skip linting proc-macro generated code entirely. Fixes rust-lang/rust-clippy#16316 --- changelog: [`elidable_lifetime_names`]: skip linting proc-macro generated code to avoid broken fix suggestions
This commit is contained in:
commit
d25a26df71
5 changed files with 118 additions and 16 deletions
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<T> 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() {}
|
||||
|
|
|
|||
|
|
@ -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<T> {
|
||||
| ^^
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue