1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
+ use clippy_utils:: msrvs:: { self , Msrv } ;
3
+ use clippy_utils:: source:: indent_of;
2
4
use clippy_utils:: { is_default_equivalent, peel_blocks} ;
3
5
use rustc_errors:: Applicability ;
4
6
use rustc_hir:: {
5
- def:: { DefKind , Res } ,
6
- Body , Expr , ExprKind , GenericArg , Impl , ImplItemKind , Item , ItemKind , Node , PathSegment , QPath , TyKind ,
7
+ def:: { CtorKind , CtorOf , DefKind , Res } ,
8
+ Body , Expr , ExprKind , GenericArg , Impl , ImplItemKind , Item , ItemKind , Node , PathSegment , QPath , Ty , TyKind ,
7
9
} ;
8
10
use rustc_lint:: { LateContext , LateLintPass } ;
9
- use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
11
+ use rustc_middle:: ty:: { AdtDef , DefIdTree } ;
12
+ use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
10
13
use rustc_span:: sym;
11
14
12
15
declare_clippy_lint ! {
@@ -51,7 +54,18 @@ declare_clippy_lint! {
51
54
"manual implementation of the `Default` trait which is equal to a derive"
52
55
}
53
56
54
- declare_lint_pass ! ( DerivableImpls => [ DERIVABLE_IMPLS ] ) ;
57
+ pub struct DerivableImpls {
58
+ msrv : Msrv ,
59
+ }
60
+
61
+ impl DerivableImpls {
62
+ #[ must_use]
63
+ pub fn new ( msrv : Msrv ) -> Self {
64
+ DerivableImpls { msrv }
65
+ }
66
+ }
67
+
68
+ impl_lint_pass ! ( DerivableImpls => [ DERIVABLE_IMPLS ] ) ;
55
69
56
70
fn is_path_self ( e : & Expr < ' _ > ) -> bool {
57
71
if let ExprKind :: Path ( QPath :: Resolved ( _, p) ) = e. kind {
@@ -61,6 +75,98 @@ fn is_path_self(e: &Expr<'_>) -> bool {
61
75
}
62
76
}
63
77
78
+ fn check_struct < ' tcx > (
79
+ cx : & LateContext < ' tcx > ,
80
+ item : & ' tcx Item < ' _ > ,
81
+ self_ty : & Ty < ' _ > ,
82
+ func_expr : & Expr < ' _ > ,
83
+ adt_def : AdtDef < ' _ > ,
84
+ ) {
85
+ if let TyKind :: Path ( QPath :: Resolved ( _, p) ) = self_ty. kind {
86
+ if let Some ( PathSegment { args : Some ( a) , .. } ) = p. segments . last ( ) {
87
+ for arg in a. args {
88
+ if !matches ! ( arg, GenericArg :: Lifetime ( _) ) {
89
+ return ;
90
+ }
91
+ }
92
+ }
93
+ }
94
+ let should_emit = match peel_blocks ( func_expr) . kind {
95
+ ExprKind :: Tup ( fields) => fields. iter ( ) . all ( |e| is_default_equivalent ( cx, e) ) ,
96
+ ExprKind :: Call ( callee, args) if is_path_self ( callee) => args. iter ( ) . all ( |e| is_default_equivalent ( cx, e) ) ,
97
+ ExprKind :: Struct ( _, fields, _) => fields. iter ( ) . all ( |ef| is_default_equivalent ( cx, ef. expr ) ) ,
98
+ _ => false ,
99
+ } ;
100
+
101
+ if should_emit {
102
+ let struct_span = cx. tcx . def_span ( adt_def. did ( ) ) ;
103
+ span_lint_and_then ( cx, DERIVABLE_IMPLS , item. span , "this `impl` can be derived" , |diag| {
104
+ diag. span_suggestion_hidden (
105
+ item. span ,
106
+ "remove the manual implementation..." ,
107
+ String :: new ( ) ,
108
+ Applicability :: MachineApplicable ,
109
+ ) ;
110
+ diag. span_suggestion (
111
+ struct_span. shrink_to_lo ( ) ,
112
+ "...and instead derive it" ,
113
+ "#[derive(Default)]\n " . to_string ( ) ,
114
+ Applicability :: MachineApplicable ,
115
+ ) ;
116
+ } ) ;
117
+ }
118
+ }
119
+
120
+ fn check_enum < ' tcx > ( cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > , func_expr : & Expr < ' _ > , adt_def : AdtDef < ' _ > ) {
121
+ if_chain ! {
122
+ if let ExprKind :: Path ( QPath :: Resolved ( None , p) ) = & peel_blocks( func_expr) . kind;
123
+ if let Res :: Def ( DefKind :: Ctor ( CtorOf :: Variant , CtorKind :: Const ) , id) = p. res;
124
+ if let variant_id = cx. tcx. parent( id) ;
125
+ if let Some ( variant_def) = adt_def. variants( ) . iter( ) . find( |v| v. def_id == variant_id) ;
126
+ if variant_def. fields. is_empty( ) ;
127
+ if !variant_def. is_field_list_non_exhaustive( ) ;
128
+
129
+ then {
130
+ let enum_span = cx. tcx. def_span( adt_def. did( ) ) ;
131
+ let indent_enum = indent_of( cx, enum_span) . unwrap_or( 0 ) ;
132
+ let variant_span = cx. tcx. def_span( variant_def. def_id) ;
133
+ let indent_variant = indent_of( cx, variant_span) . unwrap_or( 0 ) ;
134
+ span_lint_and_then(
135
+ cx,
136
+ DERIVABLE_IMPLS ,
137
+ item. span,
138
+ "this `impl` can be derived" ,
139
+ |diag| {
140
+ diag. span_suggestion_hidden(
141
+ item. span,
142
+ "remove the manual implementation..." ,
143
+ String :: new( ) ,
144
+ Applicability :: MachineApplicable
145
+ ) ;
146
+ diag. span_suggestion(
147
+ enum_span. shrink_to_lo( ) ,
148
+ "...and instead derive it..." ,
149
+ format!(
150
+ "#[derive(Default)]\n {indent}" ,
151
+ indent = " " . repeat( indent_enum) ,
152
+ ) ,
153
+ Applicability :: MachineApplicable
154
+ ) ;
155
+ diag. span_suggestion(
156
+ variant_span. shrink_to_lo( ) ,
157
+ "...and mark the default variant" ,
158
+ format!(
159
+ "#[default]\n {indent}" ,
160
+ indent = " " . repeat( indent_variant) ,
161
+ ) ,
162
+ Applicability :: MachineApplicable
163
+ ) ;
164
+ }
165
+ ) ;
166
+ }
167
+ }
168
+ }
169
+
64
170
impl < ' tcx > LateLintPass < ' tcx > for DerivableImpls {
65
171
fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
66
172
if_chain ! {
@@ -83,49 +189,16 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
83
189
if !attrs. iter( ) . any( |attr| attr. doc_str( ) . is_some( ) ) ;
84
190
if let child_attrs = cx. tcx. hir( ) . attrs( impl_item_hir) ;
85
191
if !child_attrs. iter( ) . any( |attr| attr. doc_str( ) . is_some( ) ) ;
86
- if adt_def. is_struct( ) ;
87
- then {
88
- if let TyKind :: Path ( QPath :: Resolved ( _, p) ) = self_ty. kind {
89
- if let Some ( PathSegment { args: Some ( a) , .. } ) = p. segments. last( ) {
90
- for arg in a. args {
91
- if !matches!( arg, GenericArg :: Lifetime ( _) ) {
92
- return ;
93
- }
94
- }
95
- }
96
- }
97
- let should_emit = match peel_blocks( func_expr) . kind {
98
- ExprKind :: Tup ( fields) => fields. iter( ) . all( |e| is_default_equivalent( cx, e) ) ,
99
- ExprKind :: Call ( callee, args)
100
- if is_path_self( callee) => args. iter( ) . all( |e| is_default_equivalent( cx, e) ) ,
101
- ExprKind :: Struct ( _, fields, _) => fields. iter( ) . all( |ef| is_default_equivalent( cx, ef. expr) ) ,
102
- _ => false ,
103
- } ;
104
192
105
- if should_emit {
106
- let struct_span = cx. tcx. def_span( adt_def. did( ) ) ;
107
- span_lint_and_then(
108
- cx,
109
- DERIVABLE_IMPLS ,
110
- item. span,
111
- "this `impl` can be derived" ,
112
- |diag| {
113
- diag. span_suggestion_hidden(
114
- item. span,
115
- "remove the manual implementation..." ,
116
- String :: new( ) ,
117
- Applicability :: MachineApplicable
118
- ) ;
119
- diag. span_suggestion(
120
- struct_span. shrink_to_lo( ) ,
121
- "...and instead derive it" ,
122
- "#[derive(Default)]\n " . to_string( ) ,
123
- Applicability :: MachineApplicable
124
- ) ;
125
- }
126
- ) ;
193
+ then {
194
+ if adt_def. is_struct( ) {
195
+ check_struct( cx, item, self_ty, func_expr, adt_def) ;
196
+ } else if adt_def. is_enum( ) && self . msrv. meets( msrvs:: DEFAULT_ENUM_ATTRIBUTE ) {
197
+ check_enum( cx, item, func_expr, adt_def) ;
127
198
}
128
199
}
129
200
}
130
201
}
202
+
203
+ extract_msrv_attr ! ( LateContext ) ;
131
204
}
0 commit comments