2021-03-25 13:29:11 -05:00
use clippy_utils ::diagnostics ::span_lint_and_help ;
use clippy_utils ::ty ::{ implements_trait , is_type_diagnostic_item } ;
use clippy_utils ::{ get_trait_def_id , paths , return_ty , trait_ref_of_method } ;
2019-07-06 13:13:38 -05:00
use if_chain ::if_chain ;
2020-01-06 10:39:50 -06:00
use rustc_hir ::{ ImplItem , ImplItemKind } ;
2020-01-12 00:08:41 -06:00
use rustc_lint ::{ LateContext , LateLintPass } ;
2020-01-11 05:37:08 -06:00
use rustc_session ::{ declare_lint_pass , declare_tool_lint } ;
2020-11-05 07:29:48 -06:00
use rustc_span ::sym ;
2019-07-06 13:13:38 -05:00
declare_clippy_lint! {
2021-07-29 05:16:06 -05:00
/// ### What it does
/// Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`.
2019-07-06 13:13:38 -05:00
///
2021-07-29 05:16:06 -05:00
/// ### Why is this bad?
/// This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.
2019-07-06 13:13:38 -05:00
///
2021-07-29 05:16:06 -05:00
/// ### Known problems
/// None
2019-07-06 13:13:38 -05:00
///
2021-07-29 05:16:06 -05:00
/// ### Example
2019-07-15 11:43:47 -05:00
/// ```rust
2019-07-06 13:13:38 -05:00
/// // Bad
/// pub struct A;
///
/// impl A {
/// pub fn to_string(&self) -> String {
/// "I am A".to_string()
/// }
/// }
2019-07-15 11:43:47 -05:00
/// ```
///
/// ```rust
2019-07-06 13:13:38 -05:00
/// // Good
/// use std::fmt;
///
/// pub struct A;
///
/// impl fmt::Display for A {
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// write!(f, "I am A")
/// }
/// }
/// ```
Added `clippy::version` attribute to all normal lints
So, some context for this, well, more a story. I'm not used to scripting, I've never really scripted anything, even if it's a valuable skill. I just never really needed it. Now, `@flip1995` correctly suggested using a script for this in `rust-clippy#7813`...
And I decided to write a script using nushell because why not? This was a mistake... I spend way more time on this than I would like to admit. It has definitely been more than 4 hours. It shouldn't take that long, but me being new to scripting and nushell just wasn't a good mixture... Anyway, here is the script that creates another script which adds the versions. Fun...
Just execute this on the `gh-pages` branch and the resulting `replacer.sh` in `clippy_lints` and it should all work.
```nu
mv v0.0.212 rust-1.00.0;
mv beta rust-1.57.0;
mv master rust-1.58.0;
let paths = (open ./rust-1.58.0/lints.json | select id id_span | flatten | select id path);
let versions = (
ls | where name =~ "rust-" | select name | format {name}/lints.json |
each { open $it | select id | insert version $it | str substring "5,11" version} |
group-by id | rotate counter-clockwise id version |
update version {get version | first 1} | flatten | select id version);
$paths | each { |row|
let version = ($versions | where id == ($row.id) | format {version})
let idu = ($row.id | str upcase)
$"sed -i '0,/($idu),/{s/pub ($idu),/#[clippy::version = "($version)"]\n pub ($idu),/}' ($row.path)"
} | str collect ";" | str find-replace --all '1.00.0' 'pre 1.29.0' | save "replacer.sh";
```
And this still has some problems, but at this point I just want to be done -.-
2021-10-21 14:06:26 -05:00
#[ clippy::version = " 1.38.0 " ]
2019-07-06 13:13:38 -05:00
pub INHERENT_TO_STRING ,
style ,
2019-07-15 11:43:47 -05:00
" type implements inherent method `to_string()`, but should instead implement the `Display` trait "
2019-07-06 13:13:38 -05:00
}
declare_clippy_lint! {
2021-07-29 05:16:06 -05:00
/// ### What it does
/// Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait.
2019-07-06 13:13:38 -05:00
///
2021-07-29 05:16:06 -05:00
/// ### Why is this bad?
/// This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.
2019-07-06 13:13:38 -05:00
///
2021-07-29 05:16:06 -05:00
/// ### Known problems
/// None
2019-07-06 13:13:38 -05:00
///
2021-07-29 05:16:06 -05:00
/// ### Example
2019-07-15 11:43:47 -05:00
/// ```rust
2019-07-06 13:13:38 -05:00
/// // Bad
/// use std::fmt;
///
/// pub struct A;
///
/// impl A {
/// pub fn to_string(&self) -> String {
/// "I am A".to_string()
/// }
/// }
///
/// impl fmt::Display for A {
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// write!(f, "I am A, too")
/// }
/// }
2019-07-15 11:43:47 -05:00
/// ```
///
/// ```rust
2019-07-06 13:13:38 -05:00
/// // Good
/// use std::fmt;
///
/// pub struct A;
///
/// impl fmt::Display for A {
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// write!(f, "I am A")
/// }
/// }
/// ```
Added `clippy::version` attribute to all normal lints
So, some context for this, well, more a story. I'm not used to scripting, I've never really scripted anything, even if it's a valuable skill. I just never really needed it. Now, `@flip1995` correctly suggested using a script for this in `rust-clippy#7813`...
And I decided to write a script using nushell because why not? This was a mistake... I spend way more time on this than I would like to admit. It has definitely been more than 4 hours. It shouldn't take that long, but me being new to scripting and nushell just wasn't a good mixture... Anyway, here is the script that creates another script which adds the versions. Fun...
Just execute this on the `gh-pages` branch and the resulting `replacer.sh` in `clippy_lints` and it should all work.
```nu
mv v0.0.212 rust-1.00.0;
mv beta rust-1.57.0;
mv master rust-1.58.0;
let paths = (open ./rust-1.58.0/lints.json | select id id_span | flatten | select id path);
let versions = (
ls | where name =~ "rust-" | select name | format {name}/lints.json |
each { open $it | select id | insert version $it | str substring "5,11" version} |
group-by id | rotate counter-clockwise id version |
update version {get version | first 1} | flatten | select id version);
$paths | each { |row|
let version = ($versions | where id == ($row.id) | format {version})
let idu = ($row.id | str upcase)
$"sed -i '0,/($idu),/{s/pub ($idu),/#[clippy::version = "($version)"]\n pub ($idu),/}' ($row.path)"
} | str collect ";" | str find-replace --all '1.00.0' 'pre 1.29.0' | save "replacer.sh";
```
And this still has some problems, but at this point I just want to be done -.-
2021-10-21 14:06:26 -05:00
#[ clippy::version = " 1.38.0 " ]
2019-07-06 13:13:38 -05:00
pub INHERENT_TO_STRING_SHADOW_DISPLAY ,
correctness ,
2020-01-06 00:30:43 -06:00
" type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait "
2019-07-06 13:13:38 -05:00
}
declare_lint_pass! ( InherentToString = > [ INHERENT_TO_STRING , INHERENT_TO_STRING_SHADOW_DISPLAY ] ) ;
2020-06-25 15:41:36 -05:00
impl < ' tcx > LateLintPass < ' tcx > for InherentToString {
fn check_impl_item ( & mut self , cx : & LateContext < ' tcx > , impl_item : & ' tcx ImplItem < '_ > ) {
2019-08-19 11:30:32 -05:00
if impl_item . span . from_expansion ( ) {
2019-07-06 13:13:38 -05:00
return ;
}
if_chain! {
// Check if item is a method, called to_string and has a parameter 'self'
2020-03-16 10:00:16 -05:00
if let ImplItemKind ::Fn ( ref signature , _ ) = impl_item . kind ;
2019-07-06 13:13:38 -05:00
if impl_item . ident . name . as_str ( ) = = " to_string " ;
let decl = & signature . decl ;
if decl . implicit_self . has_implicit_self ( ) ;
2019-08-27 14:23:26 -05:00
if decl . inputs . len ( ) = = 1 ;
2021-02-25 04:25:22 -06:00
if impl_item . generics . params . is_empty ( ) ;
2019-07-06 13:13:38 -05:00
// Check if return type is String
2021-10-02 18:51:01 -05:00
if is_type_diagnostic_item ( cx , return_ty ( cx , impl_item . hir_id ( ) ) , sym ::String ) ;
2019-07-06 13:13:38 -05:00
// Filters instances of to_string which are required by a trait
2021-01-30 16:25:03 -06:00
if trait_ref_of_method ( cx , impl_item . hir_id ( ) ) . is_none ( ) ;
2019-07-06 13:13:38 -05:00
then {
show_lint ( cx , impl_item ) ;
}
}
}
}
2020-06-25 15:41:36 -05:00
fn show_lint ( cx : & LateContext < '_ > , item : & ImplItem < '_ > ) {
2020-03-03 03:53:14 -06:00
let display_trait_id = get_trait_def_id ( cx , & paths ::DISPLAY_TRAIT ) . expect ( " Failed to get trait ID of `Display`! " ) ;
2019-07-06 13:13:38 -05:00
// Get the real type of 'self'
2021-01-30 16:25:03 -06:00
let self_type = cx . tcx . fn_sig ( item . def_id ) . input ( 0 ) ;
2020-09-24 07:49:22 -05:00
let self_type = self_type . skip_binder ( ) . peel_refs ( ) ;
2019-07-06 13:13:38 -05:00
// Emit either a warning or an error
if implements_trait ( cx , self_type , display_trait_id , & [ ] ) {
2020-01-26 19:56:22 -06:00
span_lint_and_help (
2019-07-06 13:13:38 -05:00
cx ,
INHERENT_TO_STRING_SHADOW_DISPLAY ,
item . span ,
& format! (
2019-07-15 11:43:47 -05:00
" type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display` " ,
2021-09-30 06:38:03 -05:00
self_type
2019-07-06 13:13:38 -05:00
) ,
2020-04-18 05:28:29 -05:00
None ,
2021-09-30 06:38:03 -05:00
& format! ( " remove the inherent method from type ` {} ` " , self_type ) ,
2019-07-06 13:13:38 -05:00
) ;
} else {
2020-01-26 19:56:22 -06:00
span_lint_and_help (
2019-07-06 13:13:38 -05:00
cx ,
INHERENT_TO_STRING ,
item . span ,
& format! (
2019-07-15 11:43:47 -05:00
" implementation of inherent method `to_string(&self) -> String` for type `{}` " ,
2021-09-30 06:38:03 -05:00
self_type
2019-07-06 13:13:38 -05:00
) ,
2020-04-18 05:28:29 -05:00
None ,
2021-09-30 06:38:03 -05:00
& format! ( " implement trait `Display` for type ` {} ` instead " , self_type ) ,
2019-07-06 13:13:38 -05:00
) ;
}
}