Skip to content

Commit 99483be

Browse files
committed
Auto merge of rust-lang#6093 - ebroto:6089_renamed_lifetimes, r=Manishearth
needless arbitrary self: handle macros This fixes two cases related to macros: * If the parameter comes from expansion, do not lint as the user has no possibility of changing it. This is not directly related to the fixed issue, but we should probably do that. * If *only* the lifetime name comes from expansion, lint, but allow the user decide the name of the lifetime. In the related issue, the lifetime was unnamed and then renamed by `async_trait`, so just removing the name in the suggestion would work, but in the general case a macro can rename a lifetime that was named differently, and we can't reliably know that name anymore. As a hint for the reviewer, the expanded code for the test can be checked with this command (from the root dir of the repo): ```sh rustc -L target/debug/test_build_base/needless_arbitrary_self_type_unfixable.stage-id.aux -Zunpretty=expanded tests/ui/needless_arbitrary_self_type_unfixable.rs ``` changelog: [`needless_arbitrary_self_type`]: handle macros Fixes rust-lang#6089
2 parents ce678b9 + 101e76f commit 99483be

File tree

4 files changed

+139
-7
lines changed

4 files changed

+139
-7
lines changed

clippy_lints/src/needless_arbitrary_self_type.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::utils::span_lint_and_sugg;
1+
use crate::utils::{in_macro, span_lint_and_sugg};
22
use if_chain::if_chain;
33
use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
44
use rustc_errors::Applicability;
@@ -69,11 +69,30 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
6969
if let [segment] = &path.segments[..];
7070
if segment.ident.name == kw::SelfUpper;
7171
then {
72+
// In case we have a named lifetime, we check if the name comes from expansion.
73+
// If it does, at this point we know the rest of the parameter was written by the user,
74+
// so let them decide what the name of the lifetime should be.
75+
// See #6089 for more details.
76+
let mut applicability = Applicability::MachineApplicable;
7277
let self_param = match (binding_mode, mutbl) {
7378
(Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
74-
(Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name),
79+
(Mode::Ref(Some(lifetime)), Mutability::Mut) => {
80+
if in_macro(lifetime.ident.span) {
81+
applicability = Applicability::HasPlaceholders;
82+
"&'_ mut self".to_string()
83+
} else {
84+
format!("&{} mut self", &lifetime.ident.name)
85+
}
86+
},
7587
(Mode::Ref(None), Mutability::Not) => "&self".to_string(),
76-
(Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name),
88+
(Mode::Ref(Some(lifetime)), Mutability::Not) => {
89+
if in_macro(lifetime.ident.span) {
90+
applicability = Applicability::HasPlaceholders;
91+
"&'_ self".to_string()
92+
} else {
93+
format!("&{} self", &lifetime.ident.name)
94+
}
95+
},
7796
(Mode::Value, Mutability::Mut) => "mut self".to_string(),
7897
(Mode::Value, Mutability::Not) => "self".to_string(),
7998
};
@@ -85,15 +104,16 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
85104
"the type of the `self` parameter does not need to be arbitrary",
86105
"consider to change this parameter to",
87106
self_param,
88-
Applicability::MachineApplicable,
107+
applicability,
89108
)
90109
}
91110
}
92111
}
93112

94113
impl EarlyLintPass for NeedlessArbitrarySelfType {
95114
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
96-
if !p.is_self() {
115+
// Bail out if the parameter it's not a receiver or was not written by the user
116+
if !p.is_self() || in_macro(p.span) {
97117
return;
98118
}
99119

tests/ui/auxiliary/proc_macro_attr.rs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// no-prefer-dynamic
33

44
#![crate_type = "proc-macro"]
5-
#![feature(repr128, proc_macro_hygiene, proc_macro_quote)]
5+
#![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)]
66
#![allow(clippy::useless_conversion)]
77

88
extern crate proc_macro;
@@ -12,7 +12,11 @@ extern crate syn;
1212
use proc_macro::TokenStream;
1313
use quote::{quote, quote_spanned};
1414
use syn::parse_macro_input;
15-
use syn::{parse_quote, ItemTrait, TraitItem};
15+
use syn::spanned::Spanned;
16+
use syn::token::Star;
17+
use syn::{
18+
parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type,
19+
};
1620

1721
#[proc_macro_attribute]
1822
pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
@@ -36,3 +40,56 @@ pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
3640
}
3741
TokenStream::from(quote!(#item))
3842
}
43+
44+
#[proc_macro_attribute]
45+
pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStream {
46+
fn make_name(count: usize) -> String {
47+
format!("'life{}", count)
48+
}
49+
50+
fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> {
51+
let arg = sig.inputs.first_mut()?;
52+
if let FnArg::Typed(PatType { pat, .. }) = arg {
53+
if let Pat::Ident(PatIdent { ident, .. }) = &**pat {
54+
if ident == "self" {
55+
return Some(arg);
56+
}
57+
}
58+
}
59+
None
60+
}
61+
62+
let mut elided = 0;
63+
let mut item = parse_macro_input!(input as ItemImpl);
64+
65+
// Look for methods having arbitrary self type taken by &mut ref
66+
for inner in &mut item.items {
67+
if let ImplItem::Method(method) = inner {
68+
if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) {
69+
if let box Type::Reference(reference) = &mut pat_type.ty {
70+
// Target only unnamed lifetimes
71+
let name = match &reference.lifetime {
72+
Some(lt) if lt.ident == "_" => make_name(elided),
73+
None => make_name(elided),
74+
_ => continue,
75+
};
76+
elided += 1;
77+
78+
// HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it.
79+
// In order to avoid adding the dependency, get a default span from a non-existent token.
80+
// A default span is needed to mark the code as coming from expansion.
81+
let span = Star::default().span();
82+
83+
// Replace old lifetime with the named one
84+
let lifetime = Lifetime::new(&name, span);
85+
reference.lifetime = Some(parse_quote!(#lifetime));
86+
87+
// Add lifetime to the generics of the method
88+
method.sig.generics.params.push(parse_quote!(#lifetime));
89+
}
90+
}
91+
}
92+
}
93+
94+
TokenStream::from(quote!(#item))
95+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// aux-build:proc_macro_attr.rs
2+
3+
#![warn(clippy::needless_arbitrary_self_type)]
4+
5+
#[macro_use]
6+
extern crate proc_macro_attr;
7+
8+
mod issue_6089 {
9+
// Check that we don't lint if the `self` parameter comes from expansion
10+
11+
macro_rules! test_from_expansion {
12+
() => {
13+
trait T1 {
14+
fn test(self: &Self);
15+
}
16+
17+
struct S1 {}
18+
19+
impl T1 for S1 {
20+
fn test(self: &Self) {}
21+
}
22+
};
23+
}
24+
25+
test_from_expansion!();
26+
27+
// If only the lifetime name comes from expansion we will lint, but the suggestion will have
28+
// placeholders and will not be applied automatically, as we can't reliably know the original name.
29+
// This specific case happened with async_trait.
30+
31+
trait T2 {
32+
fn call_with_mut_self(&mut self);
33+
}
34+
35+
struct S2 {}
36+
37+
// The method's signature will be expanded to:
38+
// fn call_with_mut_self<'life0>(self: &'life0 mut Self) {}
39+
#[rename_my_lifetimes]
40+
impl T2 for S2 {
41+
fn call_with_mut_self(self: &mut Self) {}
42+
}
43+
}
44+
45+
fn main() {}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: the type of the `self` parameter does not need to be arbitrary
2+
--> $DIR/needless_arbitrary_self_type_unfixable.rs:41:31
3+
|
4+
LL | fn call_with_mut_self(self: &mut Self) {}
5+
| ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'_ mut self`
6+
|
7+
= note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings`
8+
9+
error: aborting due to previous error
10+

0 commit comments

Comments
 (0)