1
- use clippy_utils:: diagnostics:: span_lint_and_sugg;
1
+ use clippy_config:: Conf ;
2
+ use clippy_utils:: diagnostics:: span_lint_and_then;
2
3
use clippy_utils:: fulfill_or_allowed;
3
4
use clippy_utils:: source:: snippet;
4
5
use rustc_data_structures:: fx:: FxHashMap ;
5
6
use rustc_errors:: Applicability ;
6
- use rustc_hir:: { self as hir, ExprKind , StructTailExpr } ;
7
+ use rustc_hir:: { self as hir, ExprKind } ;
7
8
use rustc_lint:: { LateContext , LateLintPass } ;
8
- use rustc_session:: declare_lint_pass;
9
+ use rustc_middle:: ty:: TyCtxt ;
10
+ use rustc_session:: impl_lint_pass;
11
+ use rustc_span:: Span ;
9
12
use rustc_span:: symbol:: Symbol ;
10
- use std:: fmt:: { self , Write as _} ;
11
13
12
14
declare_clippy_lint ! {
13
15
/// ### What it does
14
- /// Checks for struct constructors where all fields are shorthand and
15
- /// the order of the field init shorthand in the constructor is inconsistent
16
- /// with the order in the struct definition.
16
+ /// Checks for struct constructors where the order of the field
17
+ /// init in the constructor is inconsistent with the order in the
18
+ /// struct definition.
17
19
///
18
20
/// ### Why is this bad?
19
21
/// Since the order of fields in a constructor doesn't affect the
@@ -59,16 +61,37 @@ declare_clippy_lint! {
59
61
#[ clippy:: version = "1.52.0" ]
60
62
pub INCONSISTENT_STRUCT_CONSTRUCTOR ,
61
63
pedantic,
62
- "the order of the field init shorthand is inconsistent with the order in the struct definition"
64
+ "the order of the field init is inconsistent with the order in the struct definition"
63
65
}
64
66
65
- declare_lint_pass ! ( InconsistentStructConstructor => [ INCONSISTENT_STRUCT_CONSTRUCTOR ] ) ;
67
+ pub struct InconsistentStructConstructor {
68
+ lint_inconsistent_struct_field_initializers : bool ,
69
+ }
70
+
71
+ impl InconsistentStructConstructor {
72
+ pub fn new ( conf : & ' static Conf ) -> Self {
73
+ Self {
74
+ lint_inconsistent_struct_field_initializers : conf. lint_inconsistent_struct_field_initializers ,
75
+ }
76
+ }
77
+ }
78
+
79
+ impl_lint_pass ! ( InconsistentStructConstructor => [ INCONSISTENT_STRUCT_CONSTRUCTOR ] ) ;
66
80
67
81
impl < ' tcx > LateLintPass < ' tcx > for InconsistentStructConstructor {
68
82
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' _ > ) {
69
- if let ExprKind :: Struct ( qpath, fields, base) = expr. kind
70
- && fields. iter ( ) . all ( |f| f. is_shorthand )
71
- && !expr. span . from_expansion ( )
83
+ let ExprKind :: Struct ( _, fields, _) = expr. kind else {
84
+ return ;
85
+ } ;
86
+ let all_fields_are_shorthand = fields. iter ( ) . all ( |f| f. is_shorthand ) ;
87
+ let applicability = if all_fields_are_shorthand {
88
+ Applicability :: MachineApplicable
89
+ } else if self . lint_inconsistent_struct_field_initializers {
90
+ Applicability :: MaybeIncorrect
91
+ } else {
92
+ return ;
93
+ } ;
94
+ if !expr. span . from_expansion ( )
72
95
&& let ty = cx. typeck_results ( ) . expr_ty ( expr)
73
96
&& let Some ( adt_def) = ty. ty_adt_def ( )
74
97
&& adt_def. is_struct ( )
@@ -85,36 +108,24 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
85
108
return ;
86
109
}
87
110
88
- let mut ordered_fields: Vec < _ > = fields. iter ( ) . map ( |f| f. ident . name ) . collect ( ) ;
89
- ordered_fields. sort_unstable_by_key ( |id| def_order_map[ id] ) ;
90
-
91
- let mut fields_snippet = String :: new ( ) ;
92
- let ( last_ident, idents) = ordered_fields. split_last ( ) . unwrap ( ) ;
93
- for ident in idents {
94
- let _: fmt:: Result = write ! ( fields_snippet, "{ident}, " ) ;
95
- }
96
- fields_snippet. push_str ( & last_ident. to_string ( ) ) ;
97
-
98
- let base_snippet = if let StructTailExpr :: Base ( base) = base {
99
- format ! ( ", ..{}" , snippet( cx, base. span, ".." ) )
100
- } else {
101
- String :: new ( )
102
- } ;
103
-
104
- let sugg = format ! (
105
- "{} {{ {fields_snippet}{base_snippet} }}" ,
106
- snippet( cx, qpath. span( ) , ".." ) ,
107
- ) ;
111
+ let span = field_with_attrs_span ( cx. tcx , fields. first ( ) . unwrap ( ) )
112
+ . with_hi ( field_with_attrs_span ( cx. tcx , fields. last ( ) . unwrap ( ) ) . hi ( ) ) ;
108
113
109
114
if !fulfill_or_allowed ( cx, INCONSISTENT_STRUCT_CONSTRUCTOR , Some ( ty_hir_id) ) {
110
- span_lint_and_sugg (
115
+ span_lint_and_then (
111
116
cx,
112
117
INCONSISTENT_STRUCT_CONSTRUCTOR ,
113
- expr . span ,
118
+ span,
114
119
"struct constructor field order is inconsistent with struct definition field order" ,
115
- "try" ,
116
- sugg,
117
- Applicability :: MachineApplicable ,
120
+ |diag| {
121
+ let msg = if all_fields_are_shorthand {
122
+ "try"
123
+ } else {
124
+ "if the field evaluation order doesn't matter, try"
125
+ } ;
126
+ let sugg = suggestion ( cx, fields, & def_order_map) ;
127
+ diag. span_suggestion ( span, msg, sugg, applicability) ;
128
+ } ,
118
129
) ;
119
130
}
120
131
}
@@ -135,3 +146,45 @@ fn is_consistent_order<'tcx>(fields: &'tcx [hir::ExprField<'tcx>], def_order_map
135
146
136
147
true
137
148
}
149
+
150
+ fn suggestion < ' tcx > (
151
+ cx : & LateContext < ' _ > ,
152
+ fields : & ' tcx [ hir:: ExprField < ' tcx > ] ,
153
+ def_order_map : & FxHashMap < Symbol , usize > ,
154
+ ) -> String {
155
+ let ws = fields
156
+ . windows ( 2 )
157
+ . map ( |w| {
158
+ let w0_span = field_with_attrs_span ( cx. tcx , & w[ 0 ] ) ;
159
+ let w1_span = field_with_attrs_span ( cx. tcx , & w[ 1 ] ) ;
160
+ let span = w0_span. between ( w1_span) ;
161
+ snippet ( cx, span, " " )
162
+ } )
163
+ . collect :: < Vec < _ > > ( ) ;
164
+
165
+ let mut fields = fields. to_vec ( ) ;
166
+ fields. sort_unstable_by_key ( |field| def_order_map[ & field. ident . name ] ) ;
167
+ let field_snippets = fields
168
+ . iter ( )
169
+ . map ( |field| snippet ( cx, field_with_attrs_span ( cx. tcx , field) , ".." ) )
170
+ . collect :: < Vec < _ > > ( ) ;
171
+
172
+ assert_eq ! ( field_snippets. len( ) , ws. len( ) + 1 ) ;
173
+
174
+ let mut sugg = String :: new ( ) ;
175
+ for i in 0 ..field_snippets. len ( ) {
176
+ sugg += & field_snippets[ i] ;
177
+ if i < ws. len ( ) {
178
+ sugg += & ws[ i] ;
179
+ }
180
+ }
181
+ sugg
182
+ }
183
+
184
+ fn field_with_attrs_span ( tcx : TyCtxt < ' _ > , field : & hir:: ExprField < ' _ > ) -> Span {
185
+ if let Some ( attr) = tcx. hir ( ) . attrs ( field. hir_id ) . first ( ) {
186
+ field. span . with_lo ( attr. span . lo ( ) )
187
+ } else {
188
+ field. span
189
+ }
190
+ }
0 commit comments