mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
needless arbitrary self: handle macros
This commit is contained in:
parent
db6fb90bc0
commit
101e76f117
@ -1,4 +1,4 @@
|
|||||||
use crate::utils::span_lint_and_sugg;
|
use crate::utils::{in_macro, span_lint_and_sugg};
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
|
use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
@ -69,11 +69,30 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
|
|||||||
if let [segment] = &path.segments[..];
|
if let [segment] = &path.segments[..];
|
||||||
if segment.ident.name == kw::SelfUpper;
|
if segment.ident.name == kw::SelfUpper;
|
||||||
then {
|
then {
|
||||||
|
// In case we have a named lifetime, we check if the name comes from expansion.
|
||||||
|
// If it does, at this point we know the rest of the parameter was written by the user,
|
||||||
|
// so let them decide what the name of the lifetime should be.
|
||||||
|
// See #6089 for more details.
|
||||||
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let self_param = match (binding_mode, mutbl) {
|
let self_param = match (binding_mode, mutbl) {
|
||||||
(Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
|
(Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
|
||||||
(Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name),
|
(Mode::Ref(Some(lifetime)), Mutability::Mut) => {
|
||||||
|
if in_macro(lifetime.ident.span) {
|
||||||
|
applicability = Applicability::HasPlaceholders;
|
||||||
|
"&'_ mut self".to_string()
|
||||||
|
} else {
|
||||||
|
format!("&{} mut self", &lifetime.ident.name)
|
||||||
|
}
|
||||||
|
},
|
||||||
(Mode::Ref(None), Mutability::Not) => "&self".to_string(),
|
(Mode::Ref(None), Mutability::Not) => "&self".to_string(),
|
||||||
(Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name),
|
(Mode::Ref(Some(lifetime)), Mutability::Not) => {
|
||||||
|
if in_macro(lifetime.ident.span) {
|
||||||
|
applicability = Applicability::HasPlaceholders;
|
||||||
|
"&'_ self".to_string()
|
||||||
|
} else {
|
||||||
|
format!("&{} self", &lifetime.ident.name)
|
||||||
|
}
|
||||||
|
},
|
||||||
(Mode::Value, Mutability::Mut) => "mut self".to_string(),
|
(Mode::Value, Mutability::Mut) => "mut self".to_string(),
|
||||||
(Mode::Value, Mutability::Not) => "self".to_string(),
|
(Mode::Value, Mutability::Not) => "self".to_string(),
|
||||||
};
|
};
|
||||||
@ -85,7 +104,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
|
|||||||
"the type of the `self` parameter does not need to be arbitrary",
|
"the type of the `self` parameter does not need to be arbitrary",
|
||||||
"consider to change this parameter to",
|
"consider to change this parameter to",
|
||||||
self_param,
|
self_param,
|
||||||
Applicability::MachineApplicable,
|
applicability,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,7 +112,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
|
|||||||
|
|
||||||
impl EarlyLintPass for NeedlessArbitrarySelfType {
|
impl EarlyLintPass for NeedlessArbitrarySelfType {
|
||||||
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
|
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
|
||||||
if !p.is_self() {
|
// Bail out if the parameter it's not a receiver or was not written by the user
|
||||||
|
if !p.is_self() || in_macro(p.span) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// no-prefer-dynamic
|
// no-prefer-dynamic
|
||||||
|
|
||||||
#![crate_type = "proc-macro"]
|
#![crate_type = "proc-macro"]
|
||||||
#![feature(repr128, proc_macro_hygiene, proc_macro_quote)]
|
#![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)]
|
||||||
#![allow(clippy::useless_conversion)]
|
#![allow(clippy::useless_conversion)]
|
||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
@ -12,7 +12,11 @@ extern crate syn;
|
|||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::{quote, quote_spanned};
|
use quote::{quote, quote_spanned};
|
||||||
use syn::parse_macro_input;
|
use syn::parse_macro_input;
|
||||||
use syn::{parse_quote, ItemTrait, TraitItem};
|
use syn::spanned::Spanned;
|
||||||
|
use syn::token::Star;
|
||||||
|
use syn::{
|
||||||
|
parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type,
|
||||||
|
};
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
@ -36,3 +40,56 @@ pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
}
|
}
|
||||||
TokenStream::from(quote!(#item))
|
TokenStream::from(quote!(#item))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
fn make_name(count: usize) -> String {
|
||||||
|
format!("'life{}", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> {
|
||||||
|
let arg = sig.inputs.first_mut()?;
|
||||||
|
if let FnArg::Typed(PatType { pat, .. }) = arg {
|
||||||
|
if let Pat::Ident(PatIdent { ident, .. }) = &**pat {
|
||||||
|
if ident == "self" {
|
||||||
|
return Some(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut elided = 0;
|
||||||
|
let mut item = parse_macro_input!(input as ItemImpl);
|
||||||
|
|
||||||
|
// Look for methods having arbitrary self type taken by &mut ref
|
||||||
|
for inner in &mut item.items {
|
||||||
|
if let ImplItem::Method(method) = inner {
|
||||||
|
if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) {
|
||||||
|
if let box Type::Reference(reference) = &mut pat_type.ty {
|
||||||
|
// Target only unnamed lifetimes
|
||||||
|
let name = match &reference.lifetime {
|
||||||
|
Some(lt) if lt.ident == "_" => make_name(elided),
|
||||||
|
None => make_name(elided),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
elided += 1;
|
||||||
|
|
||||||
|
// HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it.
|
||||||
|
// In order to avoid adding the dependency, get a default span from a non-existent token.
|
||||||
|
// A default span is needed to mark the code as coming from expansion.
|
||||||
|
let span = Star::default().span();
|
||||||
|
|
||||||
|
// Replace old lifetime with the named one
|
||||||
|
let lifetime = Lifetime::new(&name, span);
|
||||||
|
reference.lifetime = Some(parse_quote!(#lifetime));
|
||||||
|
|
||||||
|
// Add lifetime to the generics of the method
|
||||||
|
method.sig.generics.params.push(parse_quote!(#lifetime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenStream::from(quote!(#item))
|
||||||
|
}
|
||||||
|
45
tests/ui/needless_arbitrary_self_type_unfixable.rs
Normal file
45
tests/ui/needless_arbitrary_self_type_unfixable.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// aux-build:proc_macro_attr.rs
|
||||||
|
|
||||||
|
#![warn(clippy::needless_arbitrary_self_type)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate proc_macro_attr;
|
||||||
|
|
||||||
|
mod issue_6089 {
|
||||||
|
// Check that we don't lint if the `self` parameter comes from expansion
|
||||||
|
|
||||||
|
macro_rules! test_from_expansion {
|
||||||
|
() => {
|
||||||
|
trait T1 {
|
||||||
|
fn test(self: &Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S1 {}
|
||||||
|
|
||||||
|
impl T1 for S1 {
|
||||||
|
fn test(self: &Self) {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test_from_expansion!();
|
||||||
|
|
||||||
|
// If only the lifetime name comes from expansion we will lint, but the suggestion will have
|
||||||
|
// placeholders and will not be applied automatically, as we can't reliably know the original name.
|
||||||
|
// This specific case happened with async_trait.
|
||||||
|
|
||||||
|
trait T2 {
|
||||||
|
fn call_with_mut_self(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S2 {}
|
||||||
|
|
||||||
|
// The method's signature will be expanded to:
|
||||||
|
// fn call_with_mut_self<'life0>(self: &'life0 mut Self) {}
|
||||||
|
#[rename_my_lifetimes]
|
||||||
|
impl T2 for S2 {
|
||||||
|
fn call_with_mut_self(self: &mut Self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
10
tests/ui/needless_arbitrary_self_type_unfixable.stderr
Normal file
10
tests/ui/needless_arbitrary_self_type_unfixable.stderr
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
error: the type of the `self` parameter does not need to be arbitrary
|
||||||
|
--> $DIR/needless_arbitrary_self_type_unfixable.rs:41:31
|
||||||
|
|
|
||||||
|
LL | fn call_with_mut_self(self: &mut Self) {}
|
||||||
|
| ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'_ mut self`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
Loading…
Reference in New Issue
Block a user