1
1
use rustc:: hir:: intravisit:: FnKind ;
2
+ use rustc:: hir:: def_id:: DefId ;
2
3
use rustc:: hir;
3
4
use rustc:: lint:: * ;
5
+ use rustc:: ty;
4
6
use syntax:: ast;
5
7
use syntax:: codemap:: Span ;
6
8
use utils:: paths;
@@ -20,41 +22,70 @@ use utils::{get_trait_def_id, implements_trait, in_external_macro, return_ty, sa
20
22
/// **Example:**
21
23
///
22
24
/// ```rust,ignore
23
- /// struct Foo;
25
+ /// struct Foo(Bar) ;
24
26
///
25
27
/// impl Foo {
26
28
/// fn new() -> Self {
27
- /// Foo
29
+ /// Foo(Bar::new())
28
30
/// }
29
31
/// }
30
32
/// ```
31
33
///
32
34
/// Instead, use:
33
35
///
34
36
/// ```rust
35
- /// struct Foo;
37
+ /// struct Foo(Bar) ;
36
38
///
37
39
/// impl Default for Foo {
38
40
/// fn default() -> Self {
39
- /// Foo
41
+ /// Foo(Bar::new())
40
42
/// }
41
43
/// }
42
44
/// ```
43
45
///
44
46
/// You can also have `new()` call `Default::default()`
45
- ///
46
47
declare_lint ! {
47
48
pub NEW_WITHOUT_DEFAULT ,
48
49
Warn ,
49
50
"`fn new() -> Self` method without `Default` implementation"
50
51
}
51
52
53
+ /// **What it does:** This lints about type with a `fn new() -> Self` method
54
+ /// and no implementation of
55
+ /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
56
+ ///
57
+ /// **Why is this bad?** User might expect to be able to use
58
+ /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
59
+ /// as the type can be
60
+ /// constructed without arguments.
61
+ ///
62
+ /// **Known problems:** Hopefully none.
63
+ ///
64
+ /// **Example:**
65
+ ///
66
+ /// ```rust,ignore
67
+ /// struct Foo;
68
+ ///
69
+ /// impl Foo {
70
+ /// fn new() -> Self {
71
+ /// Foo
72
+ /// }
73
+ /// }
74
+ /// ```
75
+ ///
76
+ /// Just prepend `#[derive(Default)]` before the `struct` definition
77
+ declare_lint ! {
78
+ pub NEW_WITHOUT_DEFAULT_DERIVE ,
79
+ Warn ,
80
+ "`fn new() -> Self` without `#[derive]`able `Default` implementation"
81
+ }
82
+
52
83
#[ derive( Copy , Clone ) ]
53
84
pub struct NewWithoutDefault ;
54
85
55
86
impl LintPass for NewWithoutDefault {
56
87
fn get_lints ( & self ) -> LintArray {
57
- lint_array ! ( NEW_WITHOUT_DEFAULT )
88
+ lint_array ! ( NEW_WITHOUT_DEFAULT , NEW_WITHOUT_DEFAULT_DERIVE )
58
89
}
59
90
}
60
91
@@ -66,19 +97,52 @@ impl LateLintPass for NewWithoutDefault {
66
97
67
98
if let FnKind :: Method ( name, _, _, _) = kind {
68
99
if decl. inputs . is_empty ( ) && name. as_str ( ) == "new" {
69
- let self_ty = cx. tcx . lookup_item_type ( cx. tcx . map . local_def_id ( cx . tcx . map . get_parent ( id ) ) ) . ty ;
70
-
100
+ let self_ty = cx. tcx . lookup_item_type ( cx. tcx . map . local_def_id (
101
+ cx . tcx . map . get_parent ( id ) ) ) . ty ;
71
102
if_let_chain ! { [
72
103
self_ty. walk_shallow( ) . next( ) . is_none( ) , // implements_trait does not work with generics
73
104
let Some ( ret_ty) = return_ty( cx, id) ,
74
105
same_tys( cx, self_ty, ret_ty, id) ,
75
106
let Some ( default_trait_id) = get_trait_def_id( cx, & paths:: DEFAULT_TRAIT ) ,
76
107
!implements_trait( cx, self_ty, default_trait_id, Vec :: new( ) )
77
108
] , {
78
- span_lint( cx, NEW_WITHOUT_DEFAULT , span,
79
- & format!( "you should consider adding a `Default` implementation for `{}`" , self_ty) ) ;
109
+ if can_derive_default( self_ty, cx, default_trait_id) {
110
+ span_lint( cx,
111
+ NEW_WITHOUT_DEFAULT_DERIVE , span,
112
+ & format!( "you should consider deriving a \
113
+ `Default` implementation for `{}`",
114
+ self_ty) ) .
115
+ span_suggestion( span,
116
+ "try this" ,
117
+ "#[derive(Default)]" . into( ) ) ;
118
+ } else {
119
+ span_lint( cx,
120
+ NEW_WITHOUT_DEFAULT , span,
121
+ & format!( "you should consider adding a \
122
+ `Default` implementation for `{}`",
123
+ self_ty) ) .
124
+ span_suggestion( span,
125
+ "try this" ,
126
+ format!( "impl Default for {} {{ fn default() -> \
127
+ Self {{ {}::new() }} }}", self_ty, self_ty) ) ;
128
+ }
80
129
} }
81
130
}
82
131
}
83
132
}
84
133
}
134
+
135
+ fn can_derive_default < ' t , ' c > ( ty : ty:: Ty < ' t > , cx : & LateContext < ' c , ' t > , default_trait_id : DefId ) -> bool {
136
+ match ty. sty {
137
+ ty:: TyStruct ( ref adt_def, ref substs) => {
138
+ for field in adt_def. all_fields ( ) {
139
+ let f_ty = field. ty ( cx. tcx , substs) ;
140
+ if !implements_trait ( cx, f_ty, default_trait_id, Vec :: new ( ) ) {
141
+ return false
142
+ }
143
+ }
144
+ true
145
+ } ,
146
+ _ => false
147
+ }
148
+ }
0 commit comments