11use rustc_abi:: FieldIdx ;
22use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap , IndexEntry } ;
33use 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 } ;
66use rustc_hir:: find_attr;
77use rustc_index:: IndexVec ;
88use rustc_index:: bit_set:: DenseBitSet ;
@@ -11,11 +11,13 @@ use rustc_middle::mir::visit::{
1111 MutatingUseContext , NonMutatingUseContext , NonUseContext , PlaceContext , Visitor ,
1212} ;
1313use rustc_middle:: mir:: * ;
14+ use rustc_middle:: ty:: print:: with_no_trimmed_paths;
1415use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
1516use rustc_mir_dataflow:: fmt:: DebugWithContext ;
1617use rustc_mir_dataflow:: { Analysis , Backward , ResultsCursor } ;
1718use rustc_session:: lint;
1819use rustc_span:: Span ;
20+ use rustc_span:: edit_distance:: find_best_match_for_name;
1921use rustc_span:: symbol:: { Symbol , kw, sym} ;
2022
2123use crate :: errors;
@@ -201,6 +203,62 @@ fn maybe_suggest_literal_matching_name(
201203 finder. found
202204}
203205
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+
204262/// Return whether we should consider the current place as a drop guard and skip reporting.
205263fn maybe_drop_guard < ' tcx > (
206264 tcx : TyCtxt < ' tcx > ,
@@ -855,12 +913,27 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
855913 let from_macro = def_span. from_expansion ( )
856914 && introductions. iter ( ) . any ( |intro| intro. span . eq_ctxt ( def_span) ) ;
857915
916+ let maybe_suggest_typo = || {
917+ if let LocalKind :: Arg = self . body . local_kind ( local) {
918+ None
919+ } else {
920+ maybe_suggest_unit_pattern_typo (
921+ tcx,
922+ self . body . source . def_id ( ) ,
923+ name,
924+ def_span,
925+ decl. ty ,
926+ )
927+ }
928+ } ;
929+
858930 let statements = & mut self . assignments [ index] ;
859931 if statements. is_empty ( ) {
860932 let sugg = if from_macro {
861933 errors:: UnusedVariableSugg :: NoSugg { span : def_span, name }
862934 } else {
863- errors:: UnusedVariableSugg :: TryPrefix { spans : vec ! [ def_span] , name }
935+ let typo = maybe_suggest_typo ( ) ;
936+ errors:: UnusedVariableSugg :: TryPrefix { spans : vec ! [ def_span] , name, typo }
864937 } ;
865938 tcx. emit_node_span_lint (
866939 lint:: builtin:: UNUSED_VARIABLES ,
@@ -909,11 +982,12 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
909982 continue ;
910983 }
911984
985+ let typo = maybe_suggest_typo ( ) ;
912986 tcx. emit_node_span_lint (
913987 lint:: builtin:: UNUSED_VARIABLES ,
914988 hir_id,
915989 def_span,
916- errors:: UnusedVarAssignedOnly { name } ,
990+ errors:: UnusedVarAssignedOnly { name, typo } ,
917991 ) ;
918992 continue ;
919993 }
@@ -944,9 +1018,11 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
9441018 } else if from_macro {
9451019 errors:: UnusedVariableSugg :: NoSugg { span : def_span, name }
9461020 } else if !introductions. is_empty ( ) {
947- errors:: UnusedVariableSugg :: TryPrefix { name, spans : spans. clone ( ) }
1021+ let typo = maybe_suggest_typo ( ) ;
1022+ errors:: UnusedVariableSugg :: TryPrefix { name, typo, spans : spans. clone ( ) }
9481023 } else {
949- errors:: UnusedVariableSugg :: TryPrefix { name, spans : vec ! [ def_span] }
1024+ let typo = maybe_suggest_typo ( ) ;
1025+ errors:: UnusedVariableSugg :: TryPrefix { name, typo, spans : vec ! [ def_span] }
9501026 } ;
9511027
9521028 tcx. emit_node_span_lint (
0 commit comments