1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
+ use clippy_utils:: source:: indent_of;
2
3
use clippy_utils:: { is_default_equivalent, peel_blocks} ;
3
4
use rustc_errors:: Applicability ;
4
5
use rustc_hir:: {
5
- def:: { DefKind , Res } ,
6
- Body , Expr , ExprKind , GenericArg , Impl , ImplItemKind , Item , ItemKind , Node , PathSegment , QPath , TyKind ,
6
+ def:: { CtorKind , CtorOf , DefKind , Res } ,
7
+ Body , Expr , ExprKind , GenericArg , Impl , ImplItemKind , Item , ItemKind , Node , PathSegment , QPath , Ty , TyKind ,
7
8
} ;
8
9
use rustc_lint:: { LateContext , LateLintPass } ;
10
+ use rustc_middle:: ty:: { AdtDef , DefIdTree } ;
9
11
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
10
12
use rustc_span:: sym;
11
13
@@ -61,6 +63,98 @@ fn is_path_self(e: &Expr<'_>) -> bool {
61
63
}
62
64
}
63
65
66
+ fn check_struct < ' tcx > (
67
+ cx : & LateContext < ' tcx > ,
68
+ item : & ' tcx Item < ' _ > ,
69
+ self_ty : & Ty < ' _ > ,
70
+ func_expr : & Expr < ' _ > ,
71
+ adt_def : AdtDef < ' _ > ,
72
+ ) {
73
+ if let TyKind :: Path ( QPath :: Resolved ( _, p) ) = self_ty. kind {
74
+ if let Some ( PathSegment { args : Some ( a) , .. } ) = p. segments . last ( ) {
75
+ for arg in a. args {
76
+ if !matches ! ( arg, GenericArg :: Lifetime ( _) ) {
77
+ return ;
78
+ }
79
+ }
80
+ }
81
+ }
82
+ let should_emit = match peel_blocks ( func_expr) . kind {
83
+ ExprKind :: Tup ( fields) => fields. iter ( ) . all ( |e| is_default_equivalent ( cx, e) ) ,
84
+ ExprKind :: Call ( callee, args) if is_path_self ( callee) => args. iter ( ) . all ( |e| is_default_equivalent ( cx, e) ) ,
85
+ ExprKind :: Struct ( _, fields, _) => fields. iter ( ) . all ( |ef| is_default_equivalent ( cx, ef. expr ) ) ,
86
+ _ => false ,
87
+ } ;
88
+
89
+ if should_emit {
90
+ let struct_span = cx. tcx . def_span ( adt_def. did ( ) ) ;
91
+ span_lint_and_then ( cx, DERIVABLE_IMPLS , item. span , "this `impl` can be derived" , |diag| {
92
+ diag. span_suggestion_hidden (
93
+ item. span ,
94
+ "remove the manual implementation..." ,
95
+ String :: new ( ) ,
96
+ Applicability :: MachineApplicable ,
97
+ ) ;
98
+ diag. span_suggestion (
99
+ struct_span. shrink_to_lo ( ) ,
100
+ "...and instead derive it" ,
101
+ "#[derive(Default)]\n " . to_string ( ) ,
102
+ Applicability :: MachineApplicable ,
103
+ ) ;
104
+ } ) ;
105
+ }
106
+ }
107
+
108
+ fn check_enum < ' tcx > ( cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > , func_expr : & Expr < ' _ > , adt_def : AdtDef < ' _ > ) {
109
+ if_chain ! {
110
+ if let ExprKind :: Path ( QPath :: Resolved ( None , p) ) = & peel_blocks( func_expr) . kind;
111
+ if let Res :: Def ( DefKind :: Ctor ( CtorOf :: Variant , CtorKind :: Const ) , id) = p. res;
112
+ if let variant_id = cx. tcx. parent( id) ;
113
+ if let Some ( variant_def) = adt_def. variants( ) . iter( ) . find( |v| v. def_id == variant_id) ;
114
+ if variant_def. fields. is_empty( ) ;
115
+ if !variant_def. is_field_list_non_exhaustive( ) ;
116
+
117
+ then {
118
+ let enum_span = cx. tcx. def_span( adt_def. did( ) ) ;
119
+ let indent_enum = indent_of( cx, enum_span) . unwrap_or( 0 ) ;
120
+ let variant_span = cx. tcx. def_span( variant_def. def_id) ;
121
+ let indent_variant = indent_of( cx, variant_span) . unwrap_or( 0 ) ;
122
+ span_lint_and_then(
123
+ cx,
124
+ DERIVABLE_IMPLS ,
125
+ item. span,
126
+ "this `impl` can be derived" ,
127
+ |diag| {
128
+ diag. span_suggestion_hidden(
129
+ item. span,
130
+ "remove the manual implementation..." ,
131
+ String :: new( ) ,
132
+ Applicability :: MachineApplicable
133
+ ) ;
134
+ diag. span_suggestion(
135
+ enum_span. shrink_to_lo( ) ,
136
+ "...and instead derive it..." ,
137
+ format!(
138
+ "#[derive(Default)]\n {indent}" ,
139
+ indent = " " . repeat( indent_enum) ,
140
+ ) ,
141
+ Applicability :: MachineApplicable
142
+ ) ;
143
+ diag. span_suggestion(
144
+ variant_span. shrink_to_lo( ) ,
145
+ "...and mark the default variant" ,
146
+ format!(
147
+ "#[default]\n {indent}" ,
148
+ indent = " " . repeat( indent_variant) ,
149
+ ) ,
150
+ Applicability :: MachineApplicable
151
+ ) ;
152
+ }
153
+ ) ;
154
+ }
155
+ }
156
+ }
157
+
64
158
impl < ' tcx > LateLintPass < ' tcx > for DerivableImpls {
65
159
fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
66
160
if_chain ! {
@@ -83,47 +177,12 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
83
177
if !attrs. iter( ) . any( |attr| attr. doc_str( ) . is_some( ) ) ;
84
178
if let child_attrs = cx. tcx. hir( ) . attrs( impl_item_hir) ;
85
179
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
180
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
- ) ;
181
+ then {
182
+ if adt_def. is_struct( ) {
183
+ check_struct( cx, item, self_ty, func_expr, adt_def) ;
184
+ } else if adt_def. is_enum( ) {
185
+ check_enum( cx, item, func_expr, adt_def) ;
127
186
}
128
187
}
129
188
}
0 commit comments