11use clippy_utils:: consts:: { ConstEvalCtxt , Constant , FullInt } ;
22use clippy_utils:: diagnostics:: span_lint_and_sugg;
33use clippy_utils:: source:: snippet_with_applicability;
4- use clippy_utils:: { clip, peel_hir_expr_refs, unsext} ;
4+ use clippy_utils:: { ExprUseNode , clip, expr_use_ctxt , peel_hir_expr_refs, unsext} ;
55use rustc_errors:: Applicability ;
6- use rustc_hir:: { BinOpKind , Expr , ExprKind , HirId , Item , ItemKind , Node , QPath } ;
6+ use rustc_hir:: def:: { DefKind , Res } ;
7+ use rustc_hir:: { BinOpKind , Expr , ExprKind , Node , Path , QPath } ;
78use rustc_lint:: LateContext ;
89use rustc_middle:: ty;
9- use rustc_span:: { Span , sym } ;
10+ use rustc_span:: { Span , kw } ;
1011
1112use super :: IDENTITY_OP ;
1213
@@ -165,11 +166,19 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, child: &Expr<'_>)
165166 Parens :: Needed
166167}
167168
168- fn is_allowed ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , cmp : BinOpKind , left : & Expr < ' _ > , right : & Expr < ' _ > ) -> bool {
169- // Exclude case where the left or right side is a call to `Default::default()`
170- // and the expression is not a let binding's init expression and the let binding has a type
171- // annotation, or a function's return value.
172- if ( is_default_call ( cx, left) || is_default_call ( cx, right) ) && !is_expr_with_type_annotation ( cx, expr. hir_id ) {
169+ fn is_allowed < ' tcx > (
170+ cx : & LateContext < ' tcx > ,
171+ expr : & ' tcx Expr < ' tcx > ,
172+ cmp : BinOpKind ,
173+ left : & Expr < ' tcx > ,
174+ right : & Expr < ' tcx > ,
175+ ) -> bool {
176+ // Exclude case where the left or right side is associated function call returns a type which is
177+ // `Self` that is not given explicitly, and the expression is not a let binding's init
178+ // expression and the let binding has a type annotation, or a function's return value.
179+ if ( is_assoc_fn_without_type_instance ( cx, left) || is_assoc_fn_without_type_instance ( cx, right) )
180+ && !is_expr_used_with_type_annotation ( cx, expr)
181+ {
173182 return false ;
174183 }
175184
@@ -182,20 +191,6 @@ fn is_allowed(cx: &LateContext<'_>, expr: &Expr<'_>, cmp: BinOpKind, left: &Expr
182191 && ConstEvalCtxt :: new ( cx) . eval_simple ( left) == Some ( Constant :: Int ( 1 ) ) )
183192}
184193
185- /// Check if the expression is a call to `Default::default()`
186- fn is_default_call ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
187- if let ExprKind :: Call ( func, [ ] ) = peel_hir_expr_refs ( expr) . 0 . kind
188- && let ExprKind :: Path ( qpath) = func. kind
189- // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
190- && let QPath :: Resolved ( None , _path) = qpath
191- && let Some ( def_id) = cx. qpath_res ( & qpath, func. hir_id ) . opt_def_id ( )
192- && cx. tcx . is_diagnostic_item ( sym:: default_fn, def_id)
193- {
194- return true ;
195- }
196- false
197- }
198-
199194fn check_remainder ( cx : & LateContext < ' _ > , left : & Expr < ' _ > , right : & Expr < ' _ > , span : Span , arg : Span ) {
200195 let ecx = ConstEvalCtxt :: new ( cx) ;
201196 if match ( ecx. eval_full_int ( left) , ecx. eval_full_int ( right) ) {
@@ -256,38 +251,40 @@ fn span_ineffective_operation(
256251 ) ;
257252}
258253
259- /// Check if the expression is a let binding's init expression and the let binding has a type
260- /// annotation. Also check if the expression is a function's return value.
261- fn is_expr_with_type_annotation ( cx : & LateContext < ' _ > , hir_id : HirId ) -> bool {
262- // Get the parent node of the expression
263- if let Some ( ( _ , parent ) ) = cx . tcx . hir_parent_iter ( hir_id ) . next ( ) {
264- match parent {
265- Node :: LetStmt ( local ) => {
266- // Check if this expression is the init expression of the let binding
267- if let Some ( init ) = local . init
268- && init . hir_id == hir_id
269- {
270- // Check if the let binding has an explicit type annotation
271- return local . ty . is_some ( ) ;
272- }
273- } ,
274- Node :: Block ( block ) => {
275- // If the parent node is a block, we can make sure the expression is the last expression in the
276- // block.
277- return is_expr_with_type_annotation ( cx , block . hir_id ) ;
278- } ,
279- Node :: Expr ( expr ) => {
280- return is_expr_with_type_annotation ( cx , expr . hir_id ) ;
281- } ,
282- Node :: Item ( Item {
283- kind : ItemKind :: Fn { .. } ,
254+ fn is_expr_used_with_type_annotation < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
255+ match expr_use_ctxt ( cx , expr ) . use_node ( cx ) {
256+ ExprUseNode :: LetStmt ( letstmt ) => letstmt . ty . is_some ( ) ,
257+ ExprUseNode :: Return ( _ ) => true ,
258+ _ => false ,
259+ }
260+ }
261+
262+ /// Check if the expression is an associated function without a type instance.
263+ /// Example:
264+ /// ```
265+ /// Default::default()
266+ /// // Or
267+ /// trait Def {
268+ /// fn def() -> Self;
269+ /// }
270+ /// Def::def()
271+ /// ```
272+ fn is_assoc_fn_without_type_instance < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' tcx > ) -> bool {
273+ if let ExprKind :: Call ( func , _ ) = peel_hir_expr_refs ( expr ) . 0 . kind
274+ && let ExprKind :: Path ( QPath :: Resolved (
275+ // If it's not None, don't need to go further.
276+ None ,
277+ Path {
278+ res : Res :: Def ( DefKind :: AssocFn , def_id ) ,
284279 ..
285- } ) => {
286- // Every function has a return type, so we can return true.
287- return true ;
288280 } ,
289- _ => { } ,
290- }
281+ ) ) = func. kind
282+ && let output_ty = cx. tcx . fn_sig ( def_id) . instantiate_identity ( ) . skip_binder ( ) . output ( )
283+ && let ty:: Param ( ty:: ParamTy {
284+ name : kw:: SelfUpper , ..
285+ } ) = output_ty. kind ( )
286+ {
287+ return true ;
291288 }
292289 false
293290}
0 commit comments