@@ -53,16 +53,29 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
53
53
suggestion : Vec :: new ( ) ,
54
54
ref_pattern_count : 0 ,
55
55
binding_mode_count : 0 ,
56
+ default_mode_span : None ,
57
+ default_mode_labels : Default :: default ( ) ,
56
58
} ) ) ,
57
59
} ;
58
60
let result = pcx. lower_pattern ( pat) ;
59
61
debug ! ( "pat_from_hir({:?}) = {:?}" , pat, result) ;
60
62
if let Some ( info) = migration_info {
61
63
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 {
64
67
spans. push_span_label ( * span, label. clone ( ) ) ;
65
68
}
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
+ }
66
79
// If a relevant span is from at least edition 2024, this is a hard error.
67
80
let is_hard_error = spans. primary_spans ( ) . iter ( ) . any ( |span| span. at_least_rust_2024 ( ) ) ;
68
81
if is_hard_error {
@@ -96,6 +109,40 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
96
109
97
110
impl < ' a , ' tcx > PatCtxt < ' a , ' tcx > {
98
111
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
+
99
146
// When implicit dereferences have been inserted in this pattern, the unadjusted lowered
100
147
// pattern has the type that results *after* dereferencing. For example, in this code:
101
148
//
@@ -124,8 +171,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
124
171
_ => self . lower_pattern_unadjusted ( pat) ,
125
172
} ;
126
173
127
- let adjustments: & [ Ty < ' tcx > ] =
128
- self . typeck_results . pat_adjustments ( ) . get ( pat. hir_id ) . map_or ( & [ ] , |v| & * * v) ;
129
174
let adjusted_pat = adjustments. iter ( ) . rev ( ) . fold ( unadjusted_pat, |thir_pat, ref_ty| {
130
175
debug ! ( "{:?}: wrapping pattern with type {:?}" , thir_pat, ref_ty) ;
131
176
Box :: new ( Pat {
@@ -136,24 +181,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
136
181
} ) ;
137
182
138
183
if let Some ( s) = & mut self . rust_2024_migration_suggestion
139
- && !adjustments . is_empty ( )
184
+ && let Some ( old_mode_span ) = opt_old_mode_span
140
185
{
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
+ }
157
188
158
189
adjusted_pat
159
190
}
@@ -353,7 +384,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
353
384
let mutability = if mutable { hir:: Mutability :: Mut } else { hir:: Mutability :: Not } ;
354
385
PatKind :: DerefPattern { subpattern : self . lower_pattern ( subpattern) , mutability }
355
386
}
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) => {
357
403
PatKind :: Deref { subpattern : self . lower_pattern ( subpattern) }
358
404
}
359
405
@@ -380,19 +426,26 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
380
426
. get ( pat. hir_id )
381
427
. expect ( "missing binding mode" ) ;
382
428
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
+ }
396
449
}
397
450
398
451
// A ref x pattern is the same node used for x, and as such it has
0 commit comments