@@ -168,13 +168,15 @@ fn is_capture(place: PlaceRef<'_>) -> bool {
168168 }
169169}
170170
171- /// Give a diagnostic when an unused variable may be a typo of a unit variant or a struct.
171+ /// Give a diagnostic when an unused variable may be a typo of a unit variant,
172+ /// unit struct, or const.
172173fn maybe_suggest_unit_pattern_typo < ' tcx > (
173174 tcx : TyCtxt < ' tcx > ,
174175 body_def_id : DefId ,
175176 name : Symbol ,
176177 span : Span ,
177178 ty : Ty < ' tcx > ,
179+ allow_consts : bool ,
178180) -> Option < errors:: PatternTypo > {
179181 if let ty:: Adt ( adt_def, _) = ty. peel_refs ( ) . kind ( ) {
180182 let variant_names: Vec < _ > = adt_def
@@ -191,13 +193,17 @@ fn maybe_suggest_unit_pattern_typo<'tcx>(
191193 {
192194 return Some ( errors:: PatternTypo {
193195 span,
194- code : with_no_trimmed_paths ! ( tcx. def_path_str ( variant. def_id) ) ,
196+ code : pattern_item_path ( tcx, body_def_id , variant. def_id ) ,
195197 kind : tcx. def_descr ( variant. def_id ) ,
196198 item_name : variant. name ,
197199 } ) ;
198200 }
199201 }
200202
203+ if !allow_consts {
204+ return None ;
205+ }
206+
201207 // Look for consts of the same type with similar names as well,
202208 // not just unit structs and variants.
203209 let constants = tcx
@@ -215,7 +221,7 @@ fn maybe_suggest_unit_pattern_typo<'tcx>(
215221 {
216222 return Some ( errors:: PatternTypo {
217223 span,
218- code : with_no_trimmed_paths ! ( tcx. def_path_str ( def_id ) ) ,
224+ code : pattern_item_path ( tcx, body_def_id , def_id . to_def_id ( ) ) ,
219225 kind : "constant" ,
220226 item_name,
221227 } ) ;
@@ -224,6 +230,64 @@ fn maybe_suggest_unit_pattern_typo<'tcx>(
224230 None
225231}
226232
233+ fn pattern_item_path ( tcx : TyCtxt < ' _ > , body_def_id : DefId , item_def_id : DefId ) -> String {
234+ if is_nested_in ( tcx, item_def_id, body_def_id) {
235+ return relative_item_path ( tcx, body_def_id, item_def_id) ;
236+ }
237+
238+ let body_mod = nearest_parent_module ( tcx, body_def_id) ;
239+ if body_mod == nearest_parent_module ( tcx, item_def_id) {
240+ relative_item_path ( tcx, body_mod, item_def_id)
241+ } else if item_def_id. is_local ( ) && !body_mod. is_top_level_module ( ) {
242+ format ! ( "{}::{}" , kw:: Crate , with_no_trimmed_paths!( tcx. def_path_str( item_def_id) ) )
243+ } else {
244+ with_no_trimmed_paths ! ( tcx. def_path_str( item_def_id) )
245+ }
246+ }
247+
248+ fn is_nested_in ( tcx : TyCtxt < ' _ > , mut def_id : DefId , parent_def_id : DefId ) -> bool {
249+ while let Some ( parent) = tcx. opt_parent ( def_id) {
250+ if parent == parent_def_id {
251+ return true ;
252+ }
253+ def_id = parent;
254+ }
255+
256+ false
257+ }
258+
259+ fn relative_item_path ( tcx : TyCtxt < ' _ > , parent_def_id : DefId , item_def_id : DefId ) -> String {
260+ let mut def_id = item_def_id;
261+ let mut path = Vec :: new ( ) ;
262+
263+ loop {
264+ if def_id == parent_def_id {
265+ break ;
266+ }
267+ if let Some ( name) = tcx. opt_item_name ( def_id) {
268+ path. push ( name. to_string ( ) ) ;
269+ }
270+ let Some ( parent) = tcx. opt_parent ( def_id) else {
271+ return with_no_trimmed_paths ! ( tcx. def_path_str( item_def_id) ) ;
272+ } ;
273+ def_id = parent;
274+ }
275+
276+ path. reverse ( ) ;
277+ path. join ( "::" )
278+ }
279+
280+ fn nearest_parent_module ( tcx : TyCtxt < ' _ > , mut def_id : DefId ) -> DefId {
281+ while let Some ( parent) = tcx. opt_parent ( def_id) {
282+ def_id = parent;
283+ if matches ! ( tcx. def_kind( def_id) , DefKind :: Mod ) {
284+ return def_id;
285+ }
286+ }
287+
288+ def_id
289+ }
290+
227291/// Return whether we should consider the current place as a drop guard and skip reporting.
228292fn maybe_drop_guard < ' tcx > (
229293 tcx : TyCtxt < ' tcx > ,
@@ -977,15 +1041,19 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
9771041 && introductions. iter ( ) . any ( |intro| intro. span . eq_ctxt ( def_span) ) ;
9781042
9791043 let maybe_suggest_typo = || {
980- if let LocalKind :: Arg = self . body . local_kind ( local) {
1044+ if matches ! ( self . body. local_kind( local) , LocalKind :: Arg ) {
9811045 None
9821046 } else {
1047+ // In a bare `let x = ...`, replacing `x` with a const path would make the
1048+ // pattern refutable. Keep the existing unit ADT typo behavior unchanged.
1049+ let allow_consts = !matches ! ( binding. opt_match_place, Some ( ( None , _) ) ) ;
9831050 maybe_suggest_unit_pattern_typo (
9841051 tcx,
9851052 self . body . source . def_id ( ) ,
9861053 name,
9871054 def_span,
9881055 decl. ty ,
1056+ allow_consts,
9891057 )
9901058 }
9911059 } ;
0 commit comments