1
1
use rustc_abi:: FieldIdx ;
2
2
use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap , IndexEntry } ;
3
3
use rustc_hir:: attrs:: AttributeKind ;
4
- use rustc_hir:: def:: DefKind ;
5
- use rustc_hir:: def_id:: LocalDefId ;
4
+ use rustc_hir:: def:: { CtorKind , DefKind } ;
5
+ use rustc_hir:: def_id:: { DefId , LocalDefId } ;
6
6
use rustc_hir:: find_attr;
7
7
use rustc_index:: IndexVec ;
8
8
use rustc_index:: bit_set:: DenseBitSet ;
@@ -11,11 +11,13 @@ use rustc_middle::mir::visit::{
11
11
MutatingUseContext , NonMutatingUseContext , NonUseContext , PlaceContext , Visitor ,
12
12
} ;
13
13
use rustc_middle:: mir:: * ;
14
+ use rustc_middle:: ty:: print:: with_no_trimmed_paths;
14
15
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
15
16
use rustc_mir_dataflow:: fmt:: DebugWithContext ;
16
17
use rustc_mir_dataflow:: { Analysis , Backward , ResultsCursor } ;
17
18
use rustc_session:: lint;
18
19
use rustc_span:: Span ;
20
+ use rustc_span:: edit_distance:: find_best_match_for_name;
19
21
use rustc_span:: symbol:: { Symbol , kw, sym} ;
20
22
21
23
use crate :: errors;
@@ -201,6 +203,62 @@ fn maybe_suggest_literal_matching_name(
201
203
finder. found
202
204
}
203
205
206
+ /// Give a diagnostic when an unused variable may be a typo of a unit variant or a struct.
207
+ fn maybe_suggest_unit_pattern_typo < ' tcx > (
208
+ tcx : TyCtxt < ' tcx > ,
209
+ body_def_id : DefId ,
210
+ name : Symbol ,
211
+ span : Span ,
212
+ ty : Ty < ' tcx > ,
213
+ ) -> Option < errors:: PatternTypo > {
214
+ if let ty:: Adt ( adt_def, _) = ty. peel_refs ( ) . kind ( ) {
215
+ let variant_names: Vec < _ > = adt_def
216
+ . variants ( )
217
+ . iter ( )
218
+ . filter ( |v| matches ! ( v. ctor, Some ( ( CtorKind :: Const , _) ) ) )
219
+ . map ( |v| v. name )
220
+ . collect ( ) ;
221
+ if let Some ( name) = find_best_match_for_name ( & variant_names, name, None )
222
+ && let Some ( variant) = adt_def
223
+ . variants ( )
224
+ . iter ( )
225
+ . find ( |v| v. name == name && matches ! ( v. ctor, Some ( ( CtorKind :: Const , _) ) ) )
226
+ {
227
+ return Some ( errors:: PatternTypo {
228
+ span,
229
+ code : with_no_trimmed_paths ! ( tcx. def_path_str( variant. def_id) ) ,
230
+ kind : tcx. def_descr ( variant. def_id ) ,
231
+ item_name : variant. name ,
232
+ } ) ;
233
+ }
234
+ }
235
+
236
+ // Look for consts of the same type with similar names as well,
237
+ // not just unit structs and variants.
238
+ let constants = tcx
239
+ . hir_body_owners ( )
240
+ . filter ( |& def_id| {
241
+ matches ! ( tcx. def_kind( def_id) , DefKind :: Const )
242
+ && tcx. type_of ( def_id) . instantiate_identity ( ) == ty
243
+ && tcx. visibility ( def_id) . is_accessible_from ( body_def_id, tcx)
244
+ } )
245
+ . collect :: < Vec < _ > > ( ) ;
246
+ let names = constants. iter ( ) . map ( |& def_id| tcx. item_name ( def_id) ) . collect :: < Vec < _ > > ( ) ;
247
+ if let Some ( item_name) = find_best_match_for_name ( & names, name, None )
248
+ && let Some ( position) = names. iter ( ) . position ( |& n| n == item_name)
249
+ && let Some ( & def_id) = constants. get ( position)
250
+ {
251
+ return Some ( errors:: PatternTypo {
252
+ span,
253
+ code : with_no_trimmed_paths ! ( tcx. def_path_str( def_id) ) ,
254
+ kind : "constant" ,
255
+ item_name,
256
+ } ) ;
257
+ }
258
+
259
+ None
260
+ }
261
+
204
262
/// Return whether we should consider the current place as a drop guard and skip reporting.
205
263
fn maybe_drop_guard < ' tcx > (
206
264
tcx : TyCtxt < ' tcx > ,
@@ -860,7 +918,14 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
860
918
let sugg = if from_macro {
861
919
errors:: UnusedVariableSugg :: NoSugg { span : def_span, name }
862
920
} else {
863
- errors:: UnusedVariableSugg :: TryPrefix { spans : vec ! [ def_span] , name }
921
+ let typo = maybe_suggest_unit_pattern_typo (
922
+ tcx,
923
+ self . body . source . def_id ( ) ,
924
+ name,
925
+ def_span,
926
+ decl. ty ,
927
+ ) ;
928
+ errors:: UnusedVariableSugg :: TryPrefix { spans : vec ! [ def_span] , name, typo }
864
929
} ;
865
930
tcx. emit_node_span_lint (
866
931
lint:: builtin:: UNUSED_VARIABLES ,
@@ -909,11 +974,18 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
909
974
continue ;
910
975
}
911
976
977
+ let typo = maybe_suggest_unit_pattern_typo (
978
+ tcx,
979
+ self . body . source . def_id ( ) ,
980
+ name,
981
+ def_span,
982
+ decl. ty ,
983
+ ) ;
912
984
tcx. emit_node_span_lint (
913
985
lint:: builtin:: UNUSED_VARIABLES ,
914
986
hir_id,
915
987
def_span,
916
- errors:: UnusedVarAssignedOnly { name } ,
988
+ errors:: UnusedVarAssignedOnly { name, typo } ,
917
989
) ;
918
990
continue ;
919
991
}
@@ -944,9 +1016,23 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
944
1016
} else if from_macro {
945
1017
errors:: UnusedVariableSugg :: NoSugg { span : def_span, name }
946
1018
} else if !introductions. is_empty ( ) {
947
- errors:: UnusedVariableSugg :: TryPrefix { name, spans : spans. clone ( ) }
1019
+ let typo = maybe_suggest_unit_pattern_typo (
1020
+ tcx,
1021
+ self . body . source . def_id ( ) ,
1022
+ name,
1023
+ def_span,
1024
+ decl. ty ,
1025
+ ) ;
1026
+ errors:: UnusedVariableSugg :: TryPrefix { name, typo, spans : spans. clone ( ) }
948
1027
} else {
949
- errors:: UnusedVariableSugg :: TryPrefix { name, spans : vec ! [ def_span] }
1028
+ let typo = maybe_suggest_unit_pattern_typo (
1029
+ tcx,
1030
+ self . body . source . def_id ( ) ,
1031
+ name,
1032
+ def_span,
1033
+ decl. ty ,
1034
+ ) ;
1035
+ errors:: UnusedVariableSugg :: TryPrefix { name, typo, spans : vec ! [ def_span] }
950
1036
} ;
951
1037
952
1038
tcx. emit_node_span_lint (
0 commit comments