1
- use crate :: utils:: { contains_name, match_def_path, paths, qpath_res, snippet, span_lint_and_note} ;
1
+ use crate :: utils:: { any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet} ;
2
+ use crate :: utils:: { span_lint_and_note, span_lint_and_sugg} ;
2
3
use if_chain:: if_chain;
4
+ use rustc_data_structures:: fx:: FxHashSet ;
5
+ use rustc_errors:: Applicability ;
3
6
use rustc_hir:: def:: Res ;
4
7
use rustc_hir:: { Block , Expr , ExprKind , PatKind , QPath , Stmt , StmtKind } ;
8
+ use rustc_lint:: { LateContext , LateLintPass } ;
5
9
use rustc_middle:: ty:: { self , Adt , Ty } ;
10
+ use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
6
11
use rustc_span:: symbol:: { Ident , Symbol } ;
12
+ use rustc_span:: Span ;
7
13
8
- use rustc_lint:: { LateContext , LateLintPass } ;
9
- use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
14
+ declare_clippy_lint ! {
15
+ /// **What it does:** Checks for literal calls to `Default::default()`.
16
+ ///
17
+ /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
18
+ /// being gotten than the generic `Default`.
19
+ ///
20
+ /// **Known problems:** None.
21
+ ///
22
+ /// **Example:**
23
+ /// ```rust
24
+ /// // Bad
25
+ /// let s: String = Default::default();
26
+ ///
27
+ /// // Good
28
+ /// let s = String::default();
29
+ /// ```
30
+ pub DEFAULT_TRAIT_ACCESS ,
31
+ pedantic,
32
+ "checks for literal calls to `Default::default()`"
33
+ }
10
34
11
35
declare_clippy_lint ! {
12
36
/// **What it does:** Checks for immediate reassignment of fields initialized
@@ -19,12 +43,14 @@ declare_clippy_lint! {
19
43
/// **Example:**
20
44
/// Bad:
21
45
/// ```
46
+ /// # #[derive(Default)]
22
47
/// # struct A { i: i32 }
23
48
/// let mut a: A = Default::default();
24
49
/// a.i = 42;
25
50
/// ```
26
51
/// Use instead:
27
52
/// ```
53
+ /// # #[derive(Default)]
28
54
/// # struct A { i: i32 }
29
55
/// let a = A {
30
56
/// i: 42,
@@ -36,9 +62,46 @@ declare_clippy_lint! {
36
62
"binding initialized with Default should have its fields set in the initializer"
37
63
}
38
64
39
- declare_lint_pass ! ( FieldReassignWithDefault => [ FIELD_REASSIGN_WITH_DEFAULT ] ) ;
65
+ #[ derive( Default ) ]
66
+ pub struct DefaultPass {
67
+ // Spans linted by `field_reassign_with_default`.
68
+ reassigned_linted : FxHashSet < Span > ,
69
+ }
70
+
71
+ impl_lint_pass ! ( DefaultPass => [ DEFAULT_TRAIT_ACCESS , FIELD_REASSIGN_WITH_DEFAULT ] ) ;
72
+
73
+ impl LateLintPass < ' _ > for DefaultPass {
74
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
75
+ if_chain ! {
76
+ // Avoid cases already linted by `field_reassign_with_default`
77
+ if !self . reassigned_linted. contains( & expr. span) ;
78
+ if let ExprKind :: Call ( ref path, ..) = expr. kind;
79
+ if !any_parent_is_automatically_derived( cx. tcx, expr. hir_id) ;
80
+ if let ExprKind :: Path ( ref qpath) = path. kind;
81
+ if let Some ( def_id) = cx. qpath_res( qpath, path. hir_id) . opt_def_id( ) ;
82
+ if match_def_path( cx, def_id, & paths:: DEFAULT_TRAIT_METHOD ) ;
83
+ // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
84
+ if let QPath :: Resolved ( None , _path) = qpath;
85
+ then {
86
+ let expr_ty = cx. typeck_results( ) . expr_ty( expr) ;
87
+ if let ty:: Adt ( def, ..) = expr_ty. kind( ) {
88
+ // TODO: Work out a way to put "whatever the imported way of referencing
89
+ // this type in this file" rather than a fully-qualified type.
90
+ let replacement = format!( "{}::default()" , cx. tcx. def_path_str( def. did) ) ;
91
+ span_lint_and_sugg(
92
+ cx,
93
+ DEFAULT_TRAIT_ACCESS ,
94
+ expr. span,
95
+ & format!( "calling `{}` is more clear than this expression" , replacement) ,
96
+ "try" ,
97
+ replacement,
98
+ Applicability :: Unspecified , // First resolve the TODO above
99
+ ) ;
100
+ }
101
+ }
102
+ }
103
+ }
40
104
41
- impl LateLintPass < ' _ > for FieldReassignWithDefault {
42
105
fn check_block < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , block : & Block < ' tcx > ) {
43
106
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the
44
107
// `default` method of the `Default` trait, and store statement index in current block being
@@ -47,7 +110,7 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
47
110
48
111
// start from the `let mut _ = _::default();` and look at all the following
49
112
// statements, see if they re-assign the fields of the binding
50
- for ( stmt_idx, binding_name, binding_type) in binding_statements_using_default {
113
+ for ( stmt_idx, binding_name, binding_type, span ) in binding_statements_using_default {
51
114
// the last statement of a block cannot trigger the lint
52
115
if stmt_idx == block. stmts . len ( ) - 1 {
53
116
break ;
@@ -145,6 +208,7 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
145
208
sugg
146
209
) ,
147
210
) ;
211
+ self . reassigned_linted . insert ( span) ;
148
212
}
149
213
}
150
214
}
@@ -171,7 +235,7 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool
171
235
fn enumerate_bindings_using_default < ' tcx > (
172
236
cx : & LateContext < ' tcx > ,
173
237
block : & Block < ' tcx > ,
174
- ) -> Vec < ( usize , Symbol , Ty < ' tcx > ) > {
238
+ ) -> Vec < ( usize , Symbol , Ty < ' tcx > , Span ) > {
175
239
block
176
240
. stmts
177
241
. iter ( )
@@ -189,7 +253,7 @@ fn enumerate_bindings_using_default<'tcx>(
189
253
if let Some ( ref expr) = local. init;
190
254
if is_expr_default( expr, cx) ;
191
255
then {
192
- Some ( ( idx, ident. name, ty) )
256
+ Some ( ( idx, ident. name, ty, expr . span ) )
193
257
} else {
194
258
None
195
259
}
0 commit comments