2019-07-06 13:13:38 -05:00
use if_chain ::if_chain ;
2019-12-03 17:16:03 -06:00
use rustc ::declare_lint_pass ;
2019-07-06 13:13:38 -05:00
use rustc ::hir ::{ ImplItem , ImplItemKind } ;
use rustc ::lint ::{ LateContext , LateLintPass , LintArray , LintPass } ;
2019-12-03 17:16:03 -06:00
use rustc_session ::declare_tool_lint ;
2019-07-06 13:13:38 -05:00
use crate ::utils ::{
2019-08-19 11:30:32 -05:00
get_trait_def_id , implements_trait , match_type , paths , return_ty , span_help_and_lint , trait_ref_of_method ,
walk_ptrs_ty ,
2019-07-06 13:13:38 -05:00
} ;
declare_clippy_lint! {
2019-10-04 04:58:53 -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
///
/// **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.
///
/// **Known problems:** None
///
/// ** 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")
/// }
/// }
/// ```
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! {
2019-10-04 04:58:53 -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
///
2019-07-15 11:43:47 -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
///
2019-07-15 11:43:47 -05:00
/// **Known problems:** None
2019-07-06 13:13:38 -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")
/// }
/// }
/// ```
pub INHERENT_TO_STRING_SHADOW_DISPLAY ,
correctness ,
2019-07-15 11:43:47 -05: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 ] ) ;
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for InherentToString {
fn check_impl_item ( & mut self , cx : & LateContext < ' a , ' 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'
2019-09-27 10:16:06 -05:00
if let ImplItemKind ::Method ( 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 ;
2019-07-06 13:13:38 -05:00
// Check if return type is String
if match_type ( cx , return_ty ( cx , impl_item . hir_id ) , & paths ::STRING ) ;
// Filters instances of to_string which are required by a trait
if trait_ref_of_method ( cx , impl_item . hir_id ) . is_none ( ) ;
then {
show_lint ( cx , impl_item ) ;
}
}
}
}
fn show_lint ( cx : & LateContext < '_ , '_ > , item : & ImplItem ) {
let display_trait_id =
2019-07-15 11:43:47 -05:00
get_trait_def_id ( cx , & [ " core " , " fmt " , " Display " ] ) . expect ( " Failed to get trait ID of `Display`! " ) ;
2019-07-06 13:13:38 -05:00
// Get the real type of 'self'
let fn_def_id = cx . tcx . hir ( ) . local_def_id ( item . hir_id ) ;
let self_type = cx . tcx . fn_sig ( fn_def_id ) . input ( 0 ) ;
let self_type = walk_ptrs_ty ( self_type . skip_binder ( ) ) ;
// Emit either a warning or an error
if implements_trait ( cx , self_type , display_trait_id , & [ ] ) {
span_help_and_lint (
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` " ,
2019-07-06 13:13:38 -05:00
self_type . to_string ( )
) ,
2019-07-15 11:43:47 -05:00
& format! ( " remove the inherent method from type ` {} ` " , self_type . to_string ( ) )
2019-07-06 13:13:38 -05:00
) ;
} else {
span_help_and_lint (
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 `{}` " ,
2019-07-06 13:13:38 -05:00
self_type . to_string ( )
) ,
2019-07-15 11:43:47 -05:00
& format! ( " implement trait `Display` for type ` {} ` instead " , self_type . to_string ( ) ) ,
2019-07-06 13:13:38 -05:00
) ;
}
}