1
1
use clippy_utils:: consts:: { ConstEvalCtxt , Constant , FullInt } ;
2
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
3
use 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} ;
5
5
use 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 } ;
7
8
use rustc_lint:: LateContext ;
8
9
use rustc_middle:: ty;
9
- use rustc_span:: { Span , sym } ;
10
+ use rustc_span:: { Span , kw } ;
10
11
11
12
use super :: IDENTITY_OP ;
12
13
@@ -165,11 +166,19 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, child: &Expr<'_>)
165
166
Parens :: Needed
166
167
}
167
168
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
+ {
173
182
return false ;
174
183
}
175
184
@@ -182,20 +191,6 @@ fn is_allowed(cx: &LateContext<'_>, expr: &Expr<'_>, cmp: BinOpKind, left: &Expr
182
191
&& ConstEvalCtxt :: new ( cx) . eval_simple ( left) == Some ( Constant :: Int ( 1 ) ) )
183
192
}
184
193
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
-
199
194
fn check_remainder ( cx : & LateContext < ' _ > , left : & Expr < ' _ > , right : & Expr < ' _ > , span : Span , arg : Span ) {
200
195
let ecx = ConstEvalCtxt :: new ( cx) ;
201
196
if match ( ecx. eval_full_int ( left) , ecx. eval_full_int ( right) ) {
@@ -256,38 +251,40 @@ fn span_ineffective_operation(
256
251
) ;
257
252
}
258
253
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 ) ,
284
279
..
285
- } ) => {
286
- // Every function has a return type, so we can return true.
287
- return true ;
288
280
} ,
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 ;
291
288
}
292
289
false
293
290
}
0 commit comments