@@ -53,16 +53,29 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
5353 suggestion : Vec :: new ( ) ,
5454 ref_pattern_count : 0 ,
5555 binding_mode_count : 0 ,
56+ default_mode_span : None ,
57+ default_mode_labels : Default :: default ( ) ,
5658 } ) ) ,
5759 } ;
5860 let result = pcx. lower_pattern ( pat) ;
5961 debug ! ( "pat_from_hir({:?}) = {:?}" , pat, result) ;
6062 if let Some ( info) = migration_info {
6163 let sugg = pcx. rust_2024_migration_suggestion . expect ( "suggestion should be present" ) ;
62- let mut spans = MultiSpan :: from_spans ( info. primary_spans . clone ( ) ) ;
63- for ( span, label) in & info. span_labels {
64+ let mut spans =
65+ MultiSpan :: from_spans ( info. primary_labels . iter ( ) . map ( |( span, _) | * span) . collect ( ) ) ;
66+ for ( span, label) in & info. primary_labels {
6467 spans. push_span_label ( * span, label. clone ( ) ) ;
6568 }
69+ for ( span, label_mutbl) in & sugg. default_mode_labels {
70+ // Don't point to a macro call site.
71+ if !span. from_expansion ( ) {
72+ let label = match label_mutbl {
73+ Mutability :: Not => "default binding mode is `ref`" ,
74+ Mutability :: Mut => "default binding mode is `ref mut`" ,
75+ } ;
76+ spans. push_span_label ( * span, label. to_owned ( ) )
77+ }
78+ }
6679 // If a relevant span is from at least edition 2024, this is a hard error.
6780 let is_hard_error = spans. primary_spans ( ) . iter ( ) . any ( |span| span. at_least_rust_2024 ( ) ) ;
6881 if is_hard_error {
@@ -96,6 +109,40 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
96109
97110impl < ' a , ' tcx > PatCtxt < ' a , ' tcx > {
98111 fn lower_pattern ( & mut self , pat : & ' tcx hir:: Pat < ' tcx > ) -> Box < Pat < ' tcx > > {
112+ let adjustments: & [ Ty < ' tcx > ] =
113+ self . typeck_results . pat_adjustments ( ) . get ( pat. hir_id ) . map_or ( & [ ] , |v| & * * v) ;
114+
115+ let mut opt_old_mode_span = None ;
116+ if let Some ( s) = & mut self . rust_2024_migration_suggestion
117+ && !adjustments. is_empty ( )
118+ {
119+ let mut min_mutbl = Mutability :: Mut ;
120+ let suggestion_str: String = adjustments
121+ . iter ( )
122+ . map ( |ref_ty| {
123+ let & ty:: Ref ( _, _, mutbl) = ref_ty. kind ( ) else {
124+ span_bug ! ( pat. span, "pattern implicitly dereferences a non-ref type" ) ;
125+ } ;
126+
127+ match mutbl {
128+ Mutability :: Not => {
129+ min_mutbl = Mutability :: Not ;
130+ "&"
131+ }
132+ Mutability :: Mut => "&mut " ,
133+ }
134+ } )
135+ . collect ( ) ;
136+ s. suggestion . push ( ( pat. span . shrink_to_lo ( ) , suggestion_str) ) ;
137+ s. ref_pattern_count += adjustments. len ( ) ;
138+
139+ // Remember if this changed the default binding mode, in case we want to label it.
140+ if s. default_mode_span . is_none_or ( |( _, old_mutbl) | min_mutbl < old_mutbl) {
141+ opt_old_mode_span = Some ( s. default_mode_span ) ;
142+ s. default_mode_span = Some ( ( pat. span , min_mutbl) ) ;
143+ }
144+ } ;
145+
99146 // When implicit dereferences have been inserted in this pattern, the unadjusted lowered
100147 // pattern has the type that results *after* dereferencing. For example, in this code:
101148 //
@@ -124,8 +171,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
124171 _ => self . lower_pattern_unadjusted ( pat) ,
125172 } ;
126173
127- let adjustments: & [ Ty < ' tcx > ] =
128- self . typeck_results . pat_adjustments ( ) . get ( pat. hir_id ) . map_or ( & [ ] , |v| & * * v) ;
129174 let adjusted_pat = adjustments. iter ( ) . rev ( ) . fold ( unadjusted_pat, |thir_pat, ref_ty| {
130175 debug ! ( "{:?}: wrapping pattern with type {:?}" , thir_pat, ref_ty) ;
131176 Box :: new ( Pat {
@@ -136,24 +181,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
136181 } ) ;
137182
138183 if let Some ( s) = & mut self . rust_2024_migration_suggestion
139- && !adjustments . is_empty ( )
184+ && let Some ( old_mode_span ) = opt_old_mode_span
140185 {
141- let suggestion_str: String = adjustments
142- . iter ( )
143- . map ( |ref_ty| {
144- let & ty:: Ref ( _, _, mutbl) = ref_ty. kind ( ) else {
145- span_bug ! ( pat. span, "pattern implicitly dereferences a non-ref type" ) ;
146- } ;
147-
148- match mutbl {
149- ty:: Mutability :: Not => "&" ,
150- ty:: Mutability :: Mut => "&mut " ,
151- }
152- } )
153- . collect ( ) ;
154- s. suggestion . push ( ( pat. span . shrink_to_lo ( ) , suggestion_str) ) ;
155- s. ref_pattern_count += adjustments. len ( ) ;
156- } ;
186+ s. default_mode_span = old_mode_span;
187+ }
157188
158189 adjusted_pat
159190 }
@@ -353,7 +384,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
353384 let mutability = if mutable { hir:: Mutability :: Mut } else { hir:: Mutability :: Not } ;
354385 PatKind :: DerefPattern { subpattern : self . lower_pattern ( subpattern) , mutability }
355386 }
356- hir:: PatKind :: Ref ( subpattern, _) | hir:: PatKind :: Box ( subpattern) => {
387+ hir:: PatKind :: Ref ( subpattern, _) => {
388+ // Track the default binding mode for the Rust 2024 migration suggestion.
389+ let old_mode_span = self . rust_2024_migration_suggestion . as_mut ( ) . and_then ( |s| {
390+ if let Some ( ( default_mode_span, default_ref_mutbl) ) = s. default_mode_span {
391+ // If this eats a by-ref default binding mode, label the binding mode.
392+ s. default_mode_labels . insert ( default_mode_span, default_ref_mutbl) ;
393+ }
394+ s. default_mode_span . take ( )
395+ } ) ;
396+ let subpattern = self . lower_pattern ( subpattern) ;
397+ if let Some ( s) = & mut self . rust_2024_migration_suggestion {
398+ s. default_mode_span = old_mode_span;
399+ }
400+ PatKind :: Deref { subpattern }
401+ }
402+ hir:: PatKind :: Box ( subpattern) => {
357403 PatKind :: Deref { subpattern : self . lower_pattern ( subpattern) }
358404 }
359405
@@ -380,19 +426,26 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
380426 . get ( pat. hir_id )
381427 . expect ( "missing binding mode" ) ;
382428
383- if let Some ( s) = & mut self . rust_2024_migration_suggestion
384- && explicit_ba. 0 == ByRef :: No
385- && let ByRef :: Yes ( mutbl) = mode. 0
386- {
387- let sugg_str = match mutbl {
388- Mutability :: Not => "ref " ,
389- Mutability :: Mut => "ref mut " ,
390- } ;
391- s. suggestion . push ( (
392- pat. span . with_lo ( ident. span . lo ( ) ) . shrink_to_lo ( ) ,
393- sugg_str. to_owned ( ) ,
394- ) ) ;
395- s. binding_mode_count += 1 ;
429+ if let Some ( s) = & mut self . rust_2024_migration_suggestion {
430+ if explicit_ba != hir:: BindingMode :: NONE
431+ && let Some ( ( default_mode_span, default_ref_mutbl) ) = s. default_mode_span
432+ {
433+ // If this overrides a by-ref default binding mode, label the binding mode.
434+ s. default_mode_labels . insert ( default_mode_span, default_ref_mutbl) ;
435+ }
436+ if explicit_ba. 0 == ByRef :: No
437+ && let ByRef :: Yes ( mutbl) = mode. 0
438+ {
439+ let sugg_str = match mutbl {
440+ Mutability :: Not => "ref " ,
441+ Mutability :: Mut => "ref mut " ,
442+ } ;
443+ s. suggestion . push ( (
444+ pat. span . with_lo ( ident. span . lo ( ) ) . shrink_to_lo ( ) ,
445+ sugg_str. to_owned ( ) ,
446+ ) ) ;
447+ s. binding_mode_count += 1 ;
448+ }
396449 }
397450
398451 // A ref x pattern is the same node used for x, and as such it has
0 commit comments