@@ -3,10 +3,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
3
3
use clippy_utils:: source:: snippet_with_applicability;
4
4
use clippy_utils:: { clip, peel_hir_expr_refs, unsext} ;
5
5
use rustc_errors:: Applicability ;
6
- use rustc_hir:: { BinOpKind , Expr , ExprKind , Node } ;
6
+ use rustc_hir:: { BinOpKind , Expr , ExprKind , HirId , Item , ItemKind , Node , QPath } ;
7
7
use rustc_lint:: LateContext ;
8
8
use rustc_middle:: ty;
9
- use rustc_span:: Span ;
9
+ use rustc_span:: { Span , sym } ;
10
10
11
11
use super :: IDENTITY_OP ;
12
12
@@ -17,7 +17,7 @@ pub(crate) fn check<'tcx>(
17
17
left : & ' tcx Expr < ' _ > ,
18
18
right : & ' tcx Expr < ' _ > ,
19
19
) {
20
- if !is_allowed ( cx, op, left, right) {
20
+ if !is_allowed ( cx, expr , op, left, right) {
21
21
return ;
22
22
}
23
23
@@ -165,7 +165,14 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, child: &Expr<'_>)
165
165
Parens :: Needed
166
166
}
167
167
168
- fn is_allowed ( cx : & LateContext < ' _ > , cmp : BinOpKind , left : & Expr < ' _ > , right : & Expr < ' _ > ) -> bool {
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 ) {
173
+ return false ;
174
+ }
175
+
169
176
// This lint applies to integers and their references
170
177
cx. typeck_results ( ) . expr_ty ( left) . peel_refs ( ) . is_integral ( )
171
178
&& cx. typeck_results ( ) . expr_ty ( right) . peel_refs ( ) . is_integral ( )
@@ -175,6 +182,20 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Exp
175
182
&& ConstEvalCtxt :: new ( cx) . eval_simple ( left) == Some ( Constant :: Int ( 1 ) ) )
176
183
}
177
184
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
+
178
199
fn check_remainder ( cx : & LateContext < ' _ > , left : & Expr < ' _ > , right : & Expr < ' _ > , span : Span , arg : Span ) {
179
200
let ecx = ConstEvalCtxt :: new ( cx) ;
180
201
if match ( ecx. eval_full_int ( left) , ecx. eval_full_int ( right) ) {
@@ -234,3 +255,39 @@ fn span_ineffective_operation(
234
255
applicability,
235
256
) ;
236
257
}
258
+
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 { .. } ,
284
+ ..
285
+ } ) => {
286
+ // Every function has a return type, so we can return true.
287
+ return true ;
288
+ } ,
289
+ _ => { } ,
290
+ }
291
+ }
292
+ false
293
+ }
0 commit comments