@@ -55,6 +55,7 @@ mod iter_skip_zero;
5555mod iter_with_drain;
5656mod iterator_step_by_zero;
5757mod join_absolute_paths;
58+ mod lib;
5859mod manual_c_str_literals;
5960mod manual_contains;
6061mod manual_inspect;
@@ -102,6 +103,7 @@ mod return_and_then;
102103mod search_is_some;
103104mod seek_from_current;
104105mod seek_to_start_instead_of_rewind;
106+ mod should_implement_trait;
105107mod single_char_add_str;
106108mod skip_while_next;
107109mod sliced_string_as_bytes;
@@ -146,19 +148,18 @@ mod zst_offset;
146148
147149use clippy_config:: Conf ;
148150use clippy_utils:: consts:: { ConstEvalCtxt , Constant } ;
149- use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help } ;
151+ use clippy_utils:: diagnostics:: span_lint;
150152use clippy_utils:: macros:: FormatArgsStorage ;
151153use clippy_utils:: msrvs:: { self , Msrv } ;
152- use clippy_utils:: ty:: { contains_ty_adt_constructor_opaque, implements_trait , is_copy } ;
153- use clippy_utils:: { contains_return, is_bool , is_trait_method, iter_input_pats, peel_blocks, return_ty, sym} ;
154+ use clippy_utils:: ty:: contains_ty_adt_constructor_opaque;
155+ use clippy_utils:: { contains_return, is_trait_method, iter_input_pats, peel_blocks, return_ty, sym} ;
154156pub use path_ends_with_ext:: DEFAULT_ALLOWED_DOTFILES ;
155- use rustc_abi:: ExternAbi ;
156157use rustc_data_structures:: fx:: FxHashSet ;
157158use rustc_hir:: { self as hir, Expr , ExprKind , Node , Stmt , StmtKind , TraitItem , TraitItemKind } ;
158159use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
159- use rustc_middle:: ty:: { self , TraitRef , Ty } ;
160+ use rustc_middle:: ty:: TraitRef ;
160161use rustc_session:: impl_lint_pass;
161- use rustc_span:: { Span , Symbol , kw } ;
162+ use rustc_span:: { Span , Symbol } ;
162163
163164declare_clippy_lint ! {
164165 /// ### What it does
@@ -4888,47 +4889,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
48884889 if impl_item. span . in_external_macro ( cx. sess ( ) . source_map ( ) ) {
48894890 return ;
48904891 }
4892+
48914893 if let hir:: ImplItemKind :: Fn ( ref sig, id) = impl_item. kind {
4892- let name = impl_item. ident . name ;
48934894 let parent = cx. tcx . hir_get_parent_item ( impl_item. hir_id ( ) ) . def_id ;
48944895 let item = cx. tcx . hir_expect_item ( parent) ;
48954896 let self_ty = cx. tcx . type_of ( item. owner_id ) . instantiate_identity ( ) ;
4896-
48974897 let implements_trait = matches ! ( item. kind, hir:: ItemKind :: Impl ( hir:: Impl { of_trait: Some ( _) , .. } ) ) ;
4898+
48984899 let method_sig = cx. tcx . fn_sig ( impl_item. owner_id ) . instantiate_identity ( ) ;
48994900 let method_sig = cx. tcx . instantiate_bound_regions_with_erased ( method_sig) ;
49004901 let first_arg_ty_opt = method_sig. inputs ( ) . iter ( ) . next ( ) . copied ( ) ;
4901- // if this impl block implements a trait, lint in trait definition instead
4902- if !implements_trait && cx. effective_visibilities . is_exported ( impl_item. owner_id . def_id ) {
4903- // check missing trait implementations
4904- for method_config in & TRAIT_METHODS {
4905- if name == method_config. method_name
4906- && sig. decl . inputs . len ( ) == method_config. param_count
4907- && method_config. output_type . matches ( & sig. decl . output )
4908- // in case there is no first arg, since we already have checked the number of arguments
4909- // it's should be always true
4910- && first_arg_ty_opt
4911- . is_none_or ( |first_arg_ty| method_config. self_kind . matches ( cx, self_ty, first_arg_ty) )
4912- && fn_header_equals ( method_config. fn_header , sig. header )
4913- && method_config. lifetime_param_cond ( impl_item)
4914- {
4915- span_lint_and_help (
4916- cx,
4917- SHOULD_IMPLEMENT_TRAIT ,
4918- impl_item. span ,
4919- format ! (
4920- "method `{}` can be confused for the standard trait method `{}::{}`" ,
4921- method_config. method_name, method_config. trait_name, method_config. method_name
4922- ) ,
4923- None ,
4924- format ! (
4925- "consider implementing the trait `{}` or choosing a less ambiguous method name" ,
4926- method_config. trait_name
4927- ) ,
4928- ) ;
4929- }
4930- }
4931- }
4902+ should_implement_trait:: check_impl_item ( cx, impl_item, self_ty, implements_trait, first_arg_ty_opt, sig) ;
49324903
49334904 if sig. decl . implicit_self . has_implicit_self ( )
49344905 && !( self . avoid_breaking_exported_api
@@ -4938,7 +4909,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
49384909 {
49394910 wrong_self_convention:: check (
49404911 cx,
4941- name,
4912+ impl_item . ident . name ,
49424913 self_ty,
49434914 first_arg_ty,
49444915 first_arg. pat . span ,
@@ -5711,181 +5682,3 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr
57115682 lint_with_both_lhs_and_rhs ! ( chars_next_cmp_with_unwrap:: check, cx, info) ;
57125683 lint_with_both_lhs_and_rhs ! ( chars_last_cmp_with_unwrap:: check, cx, info) ;
57135684}
5714-
5715- const FN_HEADER : hir:: FnHeader = hir:: FnHeader {
5716- safety : hir:: HeaderSafety :: Normal ( hir:: Safety :: Safe ) ,
5717- constness : hir:: Constness :: NotConst ,
5718- asyncness : hir:: IsAsync :: NotAsync ,
5719- abi : ExternAbi :: Rust ,
5720- } ;
5721-
5722- struct ShouldImplTraitCase {
5723- trait_name : & ' static str ,
5724- method_name : Symbol ,
5725- param_count : usize ,
5726- fn_header : hir:: FnHeader ,
5727- // implicit self kind expected (none, self, &self, ...)
5728- self_kind : SelfKind ,
5729- // checks against the output type
5730- output_type : OutType ,
5731- // certain methods with explicit lifetimes can't implement the equivalent trait method
5732- lint_explicit_lifetime : bool ,
5733- }
5734- impl ShouldImplTraitCase {
5735- const fn new (
5736- trait_name : & ' static str ,
5737- method_name : Symbol ,
5738- param_count : usize ,
5739- fn_header : hir:: FnHeader ,
5740- self_kind : SelfKind ,
5741- output_type : OutType ,
5742- lint_explicit_lifetime : bool ,
5743- ) -> ShouldImplTraitCase {
5744- ShouldImplTraitCase {
5745- trait_name,
5746- method_name,
5747- param_count,
5748- fn_header,
5749- self_kind,
5750- output_type,
5751- lint_explicit_lifetime,
5752- }
5753- }
5754-
5755- fn lifetime_param_cond ( & self , impl_item : & hir:: ImplItem < ' _ > ) -> bool {
5756- self . lint_explicit_lifetime
5757- || !impl_item. generics . params . iter ( ) . any ( |p| {
5758- matches ! (
5759- p. kind,
5760- hir:: GenericParamKind :: Lifetime {
5761- kind: hir:: LifetimeParamKind :: Explicit
5762- }
5763- )
5764- } )
5765- }
5766- }
5767-
5768- #[ rustfmt:: skip]
5769- const TRAIT_METHODS : [ ShouldImplTraitCase ; 30 ] = [
5770- ShouldImplTraitCase :: new ( "std::ops::Add" , sym:: add, 2 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5771- ShouldImplTraitCase :: new ( "std::convert::AsMut" , sym:: as_mut, 1 , FN_HEADER , SelfKind :: RefMut , OutType :: Ref , true ) ,
5772- ShouldImplTraitCase :: new ( "std::convert::AsRef" , sym:: as_ref, 1 , FN_HEADER , SelfKind :: Ref , OutType :: Ref , true ) ,
5773- ShouldImplTraitCase :: new ( "std::ops::BitAnd" , sym:: bitand, 2 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5774- ShouldImplTraitCase :: new ( "std::ops::BitOr" , sym:: bitor, 2 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5775- ShouldImplTraitCase :: new ( "std::ops::BitXor" , sym:: bitxor, 2 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5776- ShouldImplTraitCase :: new ( "std::borrow::Borrow" , sym:: borrow, 1 , FN_HEADER , SelfKind :: Ref , OutType :: Ref , true ) ,
5777- ShouldImplTraitCase :: new ( "std::borrow::BorrowMut" , sym:: borrow_mut, 1 , FN_HEADER , SelfKind :: RefMut , OutType :: Ref , true ) ,
5778- ShouldImplTraitCase :: new ( "std::clone::Clone" , sym:: clone, 1 , FN_HEADER , SelfKind :: Ref , OutType :: Any , true ) ,
5779- ShouldImplTraitCase :: new ( "std::cmp::Ord" , sym:: cmp, 2 , FN_HEADER , SelfKind :: Ref , OutType :: Any , true ) ,
5780- ShouldImplTraitCase :: new ( "std::default::Default" , kw:: Default , 0 , FN_HEADER , SelfKind :: No , OutType :: Any , true ) ,
5781- ShouldImplTraitCase :: new ( "std::ops::Deref" , sym:: deref, 1 , FN_HEADER , SelfKind :: Ref , OutType :: Ref , true ) ,
5782- ShouldImplTraitCase :: new ( "std::ops::DerefMut" , sym:: deref_mut, 1 , FN_HEADER , SelfKind :: RefMut , OutType :: Ref , true ) ,
5783- ShouldImplTraitCase :: new ( "std::ops::Div" , sym:: div, 2 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5784- ShouldImplTraitCase :: new ( "std::ops::Drop" , sym:: drop, 1 , FN_HEADER , SelfKind :: RefMut , OutType :: Unit , true ) ,
5785- ShouldImplTraitCase :: new ( "std::cmp::PartialEq" , sym:: eq, 2 , FN_HEADER , SelfKind :: Ref , OutType :: Bool , true ) ,
5786- ShouldImplTraitCase :: new ( "std::iter::FromIterator" , sym:: from_iter, 1 , FN_HEADER , SelfKind :: No , OutType :: Any , true ) ,
5787- ShouldImplTraitCase :: new ( "std::str::FromStr" , sym:: from_str, 1 , FN_HEADER , SelfKind :: No , OutType :: Any , true ) ,
5788- ShouldImplTraitCase :: new ( "std::hash::Hash" , sym:: hash, 2 , FN_HEADER , SelfKind :: Ref , OutType :: Unit , true ) ,
5789- ShouldImplTraitCase :: new ( "std::ops::Index" , sym:: index, 2 , FN_HEADER , SelfKind :: Ref , OutType :: Ref , true ) ,
5790- ShouldImplTraitCase :: new ( "std::ops::IndexMut" , sym:: index_mut, 2 , FN_HEADER , SelfKind :: RefMut , OutType :: Ref , true ) ,
5791- ShouldImplTraitCase :: new ( "std::iter::IntoIterator" , sym:: into_iter, 1 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5792- ShouldImplTraitCase :: new ( "std::ops::Mul" , sym:: mul, 2 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5793- ShouldImplTraitCase :: new ( "std::ops::Neg" , sym:: neg, 1 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5794- ShouldImplTraitCase :: new ( "std::iter::Iterator" , sym:: next, 1 , FN_HEADER , SelfKind :: RefMut , OutType :: Any , false ) ,
5795- ShouldImplTraitCase :: new ( "std::ops::Not" , sym:: not, 1 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5796- ShouldImplTraitCase :: new ( "std::ops::Rem" , sym:: rem, 2 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5797- ShouldImplTraitCase :: new ( "std::ops::Shl" , sym:: shl, 2 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5798- ShouldImplTraitCase :: new ( "std::ops::Shr" , sym:: shr, 2 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5799- ShouldImplTraitCase :: new ( "std::ops::Sub" , sym:: sub, 2 , FN_HEADER , SelfKind :: Value , OutType :: Any , true ) ,
5800- ] ;
5801-
5802- #[ derive( Clone , Copy , PartialEq , Eq , Debug ) ]
5803- enum SelfKind {
5804- Value ,
5805- Ref ,
5806- RefMut ,
5807- No , // When we want the first argument type to be different than `Self`
5808- }
5809-
5810- impl SelfKind {
5811- fn matches < ' a > ( self , cx : & LateContext < ' a > , parent_ty : Ty < ' a > , ty : Ty < ' a > ) -> bool {
5812- fn matches_value < ' a > ( cx : & LateContext < ' a > , parent_ty : Ty < ' a > , ty : Ty < ' a > ) -> bool {
5813- if ty == parent_ty {
5814- true
5815- } else if let Some ( boxed_ty) = ty. boxed_ty ( ) {
5816- boxed_ty == parent_ty
5817- } else if let ty:: Adt ( adt, args) = ty. kind ( )
5818- && matches ! ( cx. tcx. get_diagnostic_name( adt. did( ) ) , Some ( sym:: Rc | sym:: Arc ) )
5819- {
5820- args. types ( ) . next ( ) == Some ( parent_ty)
5821- } else {
5822- false
5823- }
5824- }
5825-
5826- fn matches_ref < ' a > ( cx : & LateContext < ' a > , mutability : hir:: Mutability , parent_ty : Ty < ' a > , ty : Ty < ' a > ) -> bool {
5827- if let ty:: Ref ( _, t, m) = * ty. kind ( ) {
5828- return m == mutability && t == parent_ty;
5829- }
5830-
5831- let trait_sym = match mutability {
5832- hir:: Mutability :: Not => sym:: AsRef ,
5833- hir:: Mutability :: Mut => sym:: AsMut ,
5834- } ;
5835-
5836- let Some ( trait_def_id) = cx. tcx . get_diagnostic_item ( trait_sym) else {
5837- return false ;
5838- } ;
5839- implements_trait ( cx, ty, trait_def_id, & [ parent_ty. into ( ) ] )
5840- }
5841-
5842- fn matches_none < ' a > ( cx : & LateContext < ' a > , parent_ty : Ty < ' a > , ty : Ty < ' a > ) -> bool {
5843- !matches_value ( cx, parent_ty, ty)
5844- && !matches_ref ( cx, hir:: Mutability :: Not , parent_ty, ty)
5845- && !matches_ref ( cx, hir:: Mutability :: Mut , parent_ty, ty)
5846- }
5847-
5848- match self {
5849- Self :: Value => matches_value ( cx, parent_ty, ty) ,
5850- Self :: Ref => matches_ref ( cx, hir:: Mutability :: Not , parent_ty, ty) || ty == parent_ty && is_copy ( cx, ty) ,
5851- Self :: RefMut => matches_ref ( cx, hir:: Mutability :: Mut , parent_ty, ty) ,
5852- Self :: No => matches_none ( cx, parent_ty, ty) ,
5853- }
5854- }
5855-
5856- #[ must_use]
5857- fn description ( self ) -> & ' static str {
5858- match self {
5859- Self :: Value => "`self` by value" ,
5860- Self :: Ref => "`self` by reference" ,
5861- Self :: RefMut => "`self` by mutable reference" ,
5862- Self :: No => "no `self`" ,
5863- }
5864- }
5865- }
5866-
5867- #[ derive( Clone , Copy ) ]
5868- enum OutType {
5869- Unit ,
5870- Bool ,
5871- Any ,
5872- Ref ,
5873- }
5874-
5875- impl OutType {
5876- fn matches ( self , ty : & hir:: FnRetTy < ' _ > ) -> bool {
5877- let is_unit = |ty : & hir:: Ty < ' _ > | matches ! ( ty. kind, hir:: TyKind :: Tup ( & [ ] ) ) ;
5878- match ( self , ty) {
5879- ( Self :: Unit , & hir:: FnRetTy :: DefaultReturn ( _) ) => true ,
5880- ( Self :: Unit , & hir:: FnRetTy :: Return ( ty) ) if is_unit ( ty) => true ,
5881- ( Self :: Bool , & hir:: FnRetTy :: Return ( ty) ) if is_bool ( ty) => true ,
5882- ( Self :: Any , & hir:: FnRetTy :: Return ( ty) ) if !is_unit ( ty) => true ,
5883- ( Self :: Ref , & hir:: FnRetTy :: Return ( ty) ) => matches ! ( ty. kind, hir:: TyKind :: Ref ( _, _) ) ,
5884- _ => false ,
5885- }
5886- }
5887- }
5888-
5889- fn fn_header_equals ( expected : hir:: FnHeader , actual : hir:: FnHeader ) -> bool {
5890- expected. constness == actual. constness && expected. safety == actual. safety && expected. asyncness == actual. asyncness
5891- }
0 commit comments