1 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
2 use rustc_hir as hir;
3 use rustc_hir::intravisit::Visitor;
4 use rustc_hir::Node;
5 use rustc_middle::hir::map::Map;
6 use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
7 use rustc_middle::ty::{self, Ty, TyCtxt};
8 use rustc_middle::{
9 hir::place::PlaceBase,
10 mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location},
11 };
12 use rustc_span::source_map::DesugaringKind;
13 use rustc_span::symbol::{kw, Symbol};
14 use rustc_span::{sym, BytePos, Span};
15 use rustc_target::abi::FieldIdx;
16
17 use crate::diagnostics::BorrowedContentSource;
18 use crate::util::FindAssignments;
19 use crate::MirBorrowckCtxt;
20
21 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
22 pub(crate) enum AccessKind {
23 MutableBorrow,
24 Mutate,
25 }
26
27 impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
report_mutability_error( &mut self, access_place: Place<'tcx>, span: Span, the_place_err: PlaceRef<'tcx>, error_access: AccessKind, location: Location, )28 pub(crate) fn report_mutability_error(
29 &mut self,
30 access_place: Place<'tcx>,
31 span: Span,
32 the_place_err: PlaceRef<'tcx>,
33 error_access: AccessKind,
34 location: Location,
35 ) {
36 debug!(
37 "report_mutability_error(\
38 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
39 )",
40 access_place, span, the_place_err, error_access, location,
41 );
42
43 let mut err;
44 let item_msg;
45 let reason;
46 let mut opt_source = None;
47 let access_place_desc = self.describe_any_place(access_place.as_ref());
48 debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
49
50 match the_place_err {
51 PlaceRef { local, projection: [] } => {
52 item_msg = access_place_desc;
53 if access_place.as_local().is_some() {
54 reason = ", as it is not declared as mutable".to_string();
55 } else {
56 let name = self.local_names[local].expect("immutable unnamed local");
57 reason = format!(", as `{name}` is not declared as mutable");
58 }
59 }
60
61 PlaceRef {
62 local,
63 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
64 } => {
65 debug_assert!(is_closure_or_generator(
66 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
67 ));
68
69 let imm_borrow_derefed = self.upvars[upvar_index.index()]
70 .place
71 .place
72 .deref_tys()
73 .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));
74
75 // If the place is immutable then:
76 //
77 // - Either we deref an immutable ref to get to our final place.
78 // - We don't capture derefs of raw ptrs
79 // - Or the final place is immut because the root variable of the capture
80 // isn't marked mut and we should suggest that to the user.
81 if imm_borrow_derefed {
82 // If we deref an immutable ref then the suggestion here doesn't help.
83 return;
84 } else {
85 item_msg = access_place_desc;
86 if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
87 reason = ", as it is not declared as mutable".to_string();
88 } else {
89 let name = self.upvars[upvar_index.index()].place.to_string(self.infcx.tcx);
90 reason = format!(", as `{name}` is not declared as mutable");
91 }
92 }
93 }
94
95 PlaceRef { local, projection: [ProjectionElem::Deref] }
96 if self.body.local_decls[local].is_ref_for_guard() =>
97 {
98 item_msg = access_place_desc;
99 reason = ", as it is immutable for the pattern guard".to_string();
100 }
101 PlaceRef { local, projection: [ProjectionElem::Deref] }
102 if self.body.local_decls[local].is_ref_to_static() =>
103 {
104 if access_place.projection.len() == 1 {
105 item_msg = format!("immutable static item {access_place_desc}");
106 reason = String::new();
107 } else {
108 item_msg = access_place_desc;
109 let local_info = self.body.local_decls[local].local_info();
110 if let LocalInfo::StaticRef { def_id, .. } = *local_info {
111 let static_name = &self.infcx.tcx.item_name(def_id);
112 reason = format!(", as `{static_name}` is an immutable static item");
113 } else {
114 bug!("is_ref_to_static return true, but not ref to static?");
115 }
116 }
117 }
118 PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] } => {
119 if the_place_err.local == ty::CAPTURE_STRUCT_LOCAL
120 && proj_base.is_empty()
121 && !self.upvars.is_empty()
122 {
123 item_msg = access_place_desc;
124 debug_assert!(self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_ref());
125 debug_assert!(is_closure_or_generator(
126 the_place_err.ty(self.body, self.infcx.tcx).ty
127 ));
128
129 reason = if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
130 ", as it is a captured variable in a `Fn` closure".to_string()
131 } else {
132 ", as `Fn` closures cannot mutate their captured variables".to_string()
133 }
134 } else {
135 let source = self.borrowed_content_source(PlaceRef {
136 local: the_place_err.local,
137 projection: proj_base,
138 });
139 let pointer_type = source.describe_for_immutable_place(self.infcx.tcx);
140 opt_source = Some(source);
141 if let Some(desc) = self.describe_place(access_place.as_ref()) {
142 item_msg = format!("`{desc}`");
143 reason = match error_access {
144 AccessKind::Mutate => format!(", which is behind {pointer_type}"),
145 AccessKind::MutableBorrow => {
146 format!(", as it is behind {pointer_type}")
147 }
148 }
149 } else {
150 item_msg = format!("data in {pointer_type}");
151 reason = String::new();
152 }
153 }
154 }
155
156 PlaceRef {
157 local: _,
158 projection:
159 [
160 ..,
161 ProjectionElem::Index(_)
162 | ProjectionElem::ConstantIndex { .. }
163 | ProjectionElem::OpaqueCast { .. }
164 | ProjectionElem::Subslice { .. }
165 | ProjectionElem::Downcast(..),
166 ],
167 } => bug!("Unexpected immutable place."),
168 }
169
170 debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
171
172 // `act` and `acted_on` are strings that let us abstract over
173 // the verbs used in some diagnostic messages.
174 let act;
175 let acted_on;
176 let mut suggest = true;
177 let mut mut_error = None;
178 let mut count = 1;
179
180 let span = match error_access {
181 AccessKind::Mutate => {
182 err = self.cannot_assign(span, &(item_msg + &reason));
183 act = "assign";
184 acted_on = "written";
185 span
186 }
187 AccessKind::MutableBorrow => {
188 act = "borrow as mutable";
189 acted_on = "borrowed as mutable";
190
191 let borrow_spans = self.borrow_spans(span, location);
192 let borrow_span = borrow_spans.args_or_use();
193 match the_place_err {
194 PlaceRef { local, projection: [] }
195 if self.body.local_decls[local].can_be_made_mutable() =>
196 {
197 let span = self.body.local_decls[local].source_info.span;
198 mut_error = Some(span);
199 if let Some((buffer, c)) = self.get_buffered_mut_error(span) {
200 // We've encountered a second (or more) attempt to mutably borrow an
201 // immutable binding, so the likely problem is with the binding
202 // declaration, not the use. We collect these in a single diagnostic
203 // and make the binding the primary span of the error.
204 err = buffer;
205 count = c + 1;
206 if count == 2 {
207 err.replace_span_with(span, false);
208 err.span_label(span, "not mutable");
209 }
210 suggest = false;
211 } else {
212 err = self.cannot_borrow_path_as_mutable_because(
213 borrow_span,
214 &item_msg,
215 &reason,
216 );
217 }
218 }
219 _ => {
220 err = self.cannot_borrow_path_as_mutable_because(
221 borrow_span,
222 &item_msg,
223 &reason,
224 );
225 }
226 }
227 if suggest {
228 borrow_spans.var_subdiag(
229 None,
230 &mut err,
231 Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
232 |_kind, var_span| {
233 let place = self.describe_any_place(access_place.as_ref());
234 crate::session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
235 place,
236 var_span,
237 }
238 },
239 );
240 }
241 borrow_span
242 }
243 };
244
245 debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
246
247 match the_place_err {
248 // Suggest making an existing shared borrow in a struct definition a mutable borrow.
249 //
250 // This is applicable when we have a deref of a field access to a deref of a local -
251 // something like `*((*_1).0`. The local that we get will be a reference to the
252 // struct we've got a field access of (it must be a reference since there's a deref
253 // after the field access).
254 PlaceRef {
255 local,
256 projection:
257 [
258 proj_base @ ..,
259 ProjectionElem::Deref,
260 ProjectionElem::Field(field, _),
261 ProjectionElem::Deref,
262 ],
263 } => {
264 err.span_label(span, format!("cannot {act}"));
265
266 if let Some(span) = get_mut_span_in_struct_field(
267 self.infcx.tcx,
268 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty,
269 *field,
270 ) {
271 err.span_suggestion_verbose(
272 span,
273 "consider changing this to be mutable",
274 " mut ",
275 Applicability::MaybeIncorrect,
276 );
277 }
278 }
279
280 // Suggest removing a `&mut` from the use of a mutable reference.
281 PlaceRef { local, projection: [] }
282 if self
283 .body
284 .local_decls
285 .get(local)
286 .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) =>
287 {
288 let decl = &self.body.local_decls[local];
289 err.span_label(span, format!("cannot {act}"));
290 if let Some(mir::Statement {
291 source_info,
292 kind:
293 mir::StatementKind::Assign(box (
294 _,
295 mir::Rvalue::Ref(
296 _,
297 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
298 _,
299 ),
300 )),
301 ..
302 }) = &self.body[location.block].statements.get(location.statement_index)
303 {
304 match *decl.local_info() {
305 LocalInfo::User(BindingForm::Var(mir::VarBindingForm {
306 binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
307 opt_ty_info: Some(sp),
308 opt_match_place: _,
309 pat_span: _,
310 })) => {
311 if suggest {
312 err.span_note(sp, "the binding is already a mutable borrow");
313 }
314 }
315 _ => {
316 err.span_note(
317 decl.source_info.span,
318 "the binding is already a mutable borrow",
319 );
320 }
321 }
322 if let Ok(snippet) =
323 self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
324 {
325 if snippet.starts_with("&mut ") {
326 // We don't have access to the HIR to get accurate spans, but we can
327 // give a best effort structured suggestion.
328 err.span_suggestion_verbose(
329 source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
330 "try removing `&mut` here",
331 "",
332 Applicability::MachineApplicable,
333 );
334 } else {
335 // This can occur with things like `(&mut self).foo()`.
336 err.span_help(source_info.span, "try removing `&mut` here");
337 }
338 } else {
339 err.span_help(source_info.span, "try removing `&mut` here");
340 }
341 } else if decl.mutability.is_not() {
342 if matches!(
343 decl.local_info(),
344 LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::MutRef))
345 ) {
346 err.note(
347 "as `Self` may be unsized, this call attempts to take `&mut &mut self`",
348 );
349 err.note("however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably");
350 } else {
351 err.span_suggestion_verbose(
352 decl.source_info.span.shrink_to_lo(),
353 "consider making the binding mutable",
354 "mut ",
355 Applicability::MachineApplicable,
356 );
357 };
358 }
359 }
360
361 // We want to suggest users use `let mut` for local (user
362 // variable) mutations...
363 PlaceRef { local, projection: [] }
364 if self.body.local_decls[local].can_be_made_mutable() =>
365 {
366 // ... but it doesn't make sense to suggest it on
367 // variables that are `ref x`, `ref mut x`, `&self`,
368 // or `&mut self` (such variables are simply not
369 // mutable).
370 let local_decl = &self.body.local_decls[local];
371 assert_eq!(local_decl.mutability, Mutability::Not);
372
373 if count < 10 {
374 err.span_label(span, format!("cannot {act}"));
375 }
376 if suggest {
377 err.span_suggestion_verbose(
378 local_decl.source_info.span.shrink_to_lo(),
379 "consider changing this to be mutable",
380 "mut ",
381 Applicability::MachineApplicable,
382 );
383 let tcx = self.infcx.tcx;
384 if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
385 self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
386 }
387 }
388 }
389
390 // Also suggest adding mut for upvars
391 PlaceRef {
392 local,
393 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
394 } => {
395 debug_assert!(is_closure_or_generator(
396 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
397 ));
398
399 let captured_place = &self.upvars[upvar_index.index()].place;
400
401 err.span_label(span, format!("cannot {act}"));
402
403 let upvar_hir_id = captured_place.get_root_variable();
404
405 if let Some(Node::Pat(pat)) = self.infcx.tcx.hir().find(upvar_hir_id)
406 && let hir::PatKind::Binding(
407 hir::BindingAnnotation::NONE,
408 _,
409 upvar_ident,
410 _,
411 ) = pat.kind
412 {
413 if upvar_ident.name == kw::SelfLower {
414 for (_, node) in self.infcx.tcx.hir().parent_iter(upvar_hir_id) {
415 if let Some(fn_decl) = node.fn_decl() {
416 if !matches!(fn_decl.implicit_self, hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef) {
417 err.span_suggestion(
418 upvar_ident.span,
419 "consider changing this to be mutable",
420 format!("mut {}", upvar_ident.name),
421 Applicability::MachineApplicable,
422 );
423 break;
424 }
425 }
426 }
427 } else {
428 err.span_suggestion(
429 upvar_ident.span,
430 "consider changing this to be mutable",
431 format!("mut {}", upvar_ident.name),
432 Applicability::MachineApplicable,
433 );
434 }
435 }
436
437 let tcx = self.infcx.tcx;
438 if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
439 && let ty::Closure(id, _) = *ty.kind()
440 {
441 self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
442 }
443 }
444
445 // complete hack to approximate old AST-borrowck
446 // diagnostic: if the span starts with a mutable borrow of
447 // a local variable, then just suggest the user remove it.
448 PlaceRef { local: _, projection: [] }
449 if self
450 .infcx
451 .tcx
452 .sess
453 .source_map()
454 .span_to_snippet(span)
455 .is_ok_and(|snippet| snippet.starts_with("&mut ")) =>
456 {
457 err.span_label(span, format!("cannot {act}"));
458 err.span_suggestion(
459 span,
460 "try removing `&mut` here",
461 "",
462 Applicability::MaybeIncorrect,
463 );
464 }
465
466 PlaceRef { local, projection: [ProjectionElem::Deref] }
467 if self.body.local_decls[local].is_ref_for_guard() =>
468 {
469 err.span_label(span, format!("cannot {act}"));
470 err.note(
471 "variables bound in patterns are immutable until the end of the pattern guard",
472 );
473 }
474
475 // We want to point out when a `&` can be readily replaced
476 // with an `&mut`.
477 //
478 // FIXME: can this case be generalized to work for an
479 // arbitrary base for the projection?
480 PlaceRef { local, projection: [ProjectionElem::Deref] }
481 if self.body.local_decls[local].is_user_variable() =>
482 {
483 let local_decl = &self.body.local_decls[local];
484
485 let (pointer_sigil, pointer_desc) =
486 if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
487
488 match self.local_names[local] {
489 Some(name) if !local_decl.from_compiler_desugaring() => {
490 err.span_label(
491 span,
492 format!(
493 "`{name}` is a `{pointer_sigil}` {pointer_desc}, \
494 so the data it refers to cannot be {acted_on}",
495 ),
496 );
497
498 self.suggest_make_local_mut(&mut err, local, name);
499 }
500 _ => {
501 err.span_label(
502 span,
503 format!("cannot {act} through `{pointer_sigil}` {pointer_desc}"),
504 );
505 }
506 }
507 }
508
509 PlaceRef { local, projection: [ProjectionElem::Deref] }
510 if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
511 {
512 self.expected_fn_found_fn_mut_call(&mut err, span, act);
513 }
514
515 PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
516 err.span_label(span, format!("cannot {act}"));
517
518 match opt_source {
519 Some(BorrowedContentSource::OverloadedDeref(ty)) => {
520 err.help(format!(
521 "trait `DerefMut` is required to modify through a dereference, \
522 but it is not implemented for `{ty}`",
523 ));
524 }
525 Some(BorrowedContentSource::OverloadedIndex(ty)) => {
526 err.help(format!(
527 "trait `IndexMut` is required to modify indexed content, \
528 but it is not implemented for `{ty}`",
529 ));
530 self.suggest_map_index_mut_alternatives(ty, &mut err, span);
531 }
532 _ => (),
533 }
534 }
535
536 _ => {
537 err.span_label(span, format!("cannot {act}"));
538 }
539 }
540
541 if let Some(span) = mut_error {
542 self.buffer_mut_error(span, err, count);
543 } else {
544 self.buffer_error(err);
545 }
546 }
547
suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diagnostic, span: Span)548 fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diagnostic, span: Span) {
549 let Some(adt) = ty.ty_adt_def() else { return };
550 let did = adt.did();
551 if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did)
552 || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did)
553 {
554 struct V<'a, 'tcx> {
555 assign_span: Span,
556 err: &'a mut Diagnostic,
557 ty: Ty<'tcx>,
558 suggested: bool,
559 }
560 impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
561 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
562 hir::intravisit::walk_stmt(self, stmt);
563 let expr = match stmt.kind {
564 hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
565 hir::StmtKind::Local(hir::Local { init: Some(expr), .. }) => expr,
566 _ => {
567 return;
568 }
569 };
570 if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind
571 && let hir::ExprKind::Index(val, index) = place.kind
572 && (expr.span == self.assign_span || place.span == self.assign_span)
573 {
574 // val[index] = rv;
575 // ---------- place
576 self.err.multipart_suggestions(
577 format!(
578 "to modify a `{}`, use `.get_mut()`, `.insert()` or the entry API",
579 self.ty,
580 ),
581 vec![
582 vec![ // val.insert(index, rv);
583 (
584 val.span.shrink_to_hi().with_hi(index.span.lo()),
585 ".insert(".to_string(),
586 ),
587 (
588 index.span.shrink_to_hi().with_hi(rv.span.lo()),
589 ", ".to_string(),
590 ),
591 (rv.span.shrink_to_hi(), ")".to_string()),
592 ],
593 vec![ // val.get_mut(index).map(|v| { *v = rv; });
594 (
595 val.span.shrink_to_hi().with_hi(index.span.lo()),
596 ".get_mut(".to_string(),
597 ),
598 (
599 index.span.shrink_to_hi().with_hi(place.span.hi()),
600 ").map(|val| { *val".to_string(),
601 ),
602 (
603 rv.span.shrink_to_hi(),
604 "; })".to_string(),
605 ),
606 ],
607 vec![ // let x = val.entry(index).or_insert(rv);
608 (val.span.shrink_to_lo(), "let val = ".to_string()),
609 (
610 val.span.shrink_to_hi().with_hi(index.span.lo()),
611 ".entry(".to_string(),
612 ),
613 (
614 index.span.shrink_to_hi().with_hi(rv.span.lo()),
615 ").or_insert(".to_string(),
616 ),
617 (rv.span.shrink_to_hi(), ")".to_string()),
618 ],
619 ],
620 Applicability::MachineApplicable,
621 );
622 self.suggested = true;
623 } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind
624 && let hir::ExprKind::Index(val, index) = receiver.kind
625 && expr.span == self.assign_span
626 {
627 // val[index].path(args..);
628 self.err.multipart_suggestion(
629 format!("to modify a `{}` use `.get_mut()`", self.ty),
630 vec![
631 (
632 val.span.shrink_to_hi().with_hi(index.span.lo()),
633 ".get_mut(".to_string(),
634 ),
635 (
636 index.span.shrink_to_hi().with_hi(receiver.span.hi()),
637 ").map(|val| val".to_string(),
638 ),
639 (sp.shrink_to_hi(), ")".to_string()),
640 ],
641 Applicability::MachineApplicable,
642 );
643 self.suggested = true;
644 }
645 }
646 }
647 let hir_map = self.infcx.tcx.hir();
648 let def_id = self.body.source.def_id();
649 let hir_id = hir_map.local_def_id_to_hir_id(def_id.as_local().unwrap());
650 let node = hir_map.find(hir_id);
651 let Some(hir::Node::Item(item)) = node else { return; };
652 let hir::ItemKind::Fn(.., body_id) = item.kind else { return; };
653 let body = self.infcx.tcx.hir().body(body_id);
654
655 let mut v = V { assign_span: span, err, ty, suggested: false };
656 v.visit_body(body);
657 if !v.suggested {
658 err.help(format!(
659 "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API",
660 ));
661 }
662 }
663 }
664
665 /// User cannot make signature of a trait mutable without changing the
666 /// trait. So we find if this error belongs to a trait and if so we move
667 /// suggestion to the trait or disable it if it is out of scope of this crate
is_error_in_trait(&self, local: Local) -> (bool, Option<Span>)668 fn is_error_in_trait(&self, local: Local) -> (bool, Option<Span>) {
669 if self.body.local_kind(local) != LocalKind::Arg {
670 return (false, None);
671 }
672 let hir_map = self.infcx.tcx.hir();
673 let my_def = self.body.source.def_id();
674 let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap());
675 let Some(td) =
676 self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
677 else {
678 return (false, None);
679 };
680 (
681 true,
682 td.as_local().and_then(|tld| match hir_map.find_by_def_id(tld) {
683 Some(Node::Item(hir::Item {
684 kind: hir::ItemKind::Trait(_, _, _, _, items),
685 ..
686 })) => {
687 let mut f_in_trait_opt = None;
688 for hir::TraitItemRef { id: fi, kind: k, .. } in *items {
689 let hi = fi.hir_id();
690 if !matches!(k, hir::AssocItemKind::Fn { .. }) {
691 continue;
692 }
693 if hir_map.name(hi) != hir_map.name(my_hir) {
694 continue;
695 }
696 f_in_trait_opt = Some(hi);
697 break;
698 }
699 f_in_trait_opt.and_then(|f_in_trait| match hir_map.find(f_in_trait) {
700 Some(Node::TraitItem(hir::TraitItem {
701 kind:
702 hir::TraitItemKind::Fn(
703 hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. },
704 _,
705 ),
706 ..
707 })) => {
708 let hir::Ty { span, .. } = inputs[local.index() - 1];
709 Some(span)
710 }
711 _ => None,
712 })
713 }
714 _ => None,
715 }),
716 )
717 }
718
719 // point to span of upvar making closure call require mutable borrow
show_mutating_upvar( &self, tcx: TyCtxt<'_>, closure_local_def_id: hir::def_id::LocalDefId, the_place_err: PlaceRef<'tcx>, err: &mut Diagnostic, )720 fn show_mutating_upvar(
721 &self,
722 tcx: TyCtxt<'_>,
723 closure_local_def_id: hir::def_id::LocalDefId,
724 the_place_err: PlaceRef<'tcx>,
725 err: &mut Diagnostic,
726 ) {
727 let tables = tcx.typeck(closure_local_def_id);
728 if let Some((span, closure_kind_origin)) = tcx.closure_kind_origin(closure_local_def_id) {
729 let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
730 let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
731 let root_hir_id = upvar_id.var_path.hir_id;
732 // we have an origin for this closure kind starting at this root variable so it's safe to unwrap here
733 let captured_places =
734 tables.closure_min_captures[&closure_local_def_id].get(&root_hir_id).unwrap();
735
736 let origin_projection = closure_kind_origin
737 .projections
738 .iter()
739 .map(|proj| proj.kind)
740 .collect::<Vec<_>>();
741 let mut capture_reason = String::new();
742 for captured_place in captured_places {
743 let captured_place_kinds = captured_place
744 .place
745 .projections
746 .iter()
747 .map(|proj| proj.kind)
748 .collect::<Vec<_>>();
749 if rustc_middle::ty::is_ancestor_or_same_capture(
750 &captured_place_kinds,
751 &origin_projection,
752 ) {
753 match captured_place.info.capture_kind {
754 ty::UpvarCapture::ByRef(
755 ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
756 ) => {
757 capture_reason = format!("mutable borrow of `{upvar}`");
758 }
759 ty::UpvarCapture::ByValue => {
760 capture_reason = format!("possible mutation of `{upvar}`");
761 }
762 _ => bug!("upvar `{upvar}` borrowed, but not mutably"),
763 }
764 break;
765 }
766 }
767 if capture_reason.is_empty() {
768 bug!("upvar `{upvar}` borrowed, but cannot find reason");
769 }
770 capture_reason
771 } else {
772 bug!("not an upvar")
773 };
774 err.span_label(
775 *span,
776 format!(
777 "calling `{}` requires mutable binding due to {}",
778 self.describe_place(the_place_err).unwrap(),
779 reason
780 ),
781 );
782 }
783 }
784
785 // Attempt to search similar mutable associated items for suggestion.
786 // In the future, attempt in all path but initially for RHS of for_loop
suggest_similar_mut_method_for_for_loop(&self, err: &mut Diagnostic)787 fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diagnostic) {
788 use hir::{
789 BodyId, Expr,
790 ExprKind::{Block, Call, DropTemps, Match, MethodCall},
791 HirId, ImplItem, ImplItemKind, Item, ItemKind,
792 };
793
794 fn maybe_body_id_of_fn(hir_map: Map<'_>, id: HirId) -> Option<BodyId> {
795 match hir_map.find(id) {
796 Some(Node::Item(Item { kind: ItemKind::Fn(_, _, body_id), .. }))
797 | Some(Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. })) => {
798 Some(*body_id)
799 }
800 _ => None,
801 }
802 }
803 let hir_map = self.infcx.tcx.hir();
804 let mir_body_hir_id = self.mir_hir_id();
805 if let Some(fn_body_id) = maybe_body_id_of_fn(hir_map, mir_body_hir_id) {
806 if let Block(
807 hir::Block {
808 expr:
809 Some(Expr {
810 kind:
811 DropTemps(Expr {
812 kind:
813 Match(
814 Expr {
815 kind:
816 Call(
817 _,
818 [
819 Expr {
820 kind:
821 MethodCall(path_segment, _, _, span),
822 hir_id,
823 ..
824 },
825 ..,
826 ],
827 ),
828 ..
829 },
830 ..,
831 ),
832 ..
833 }),
834 ..
835 }),
836 ..
837 },
838 _,
839 ) = hir_map.body(fn_body_id).value.kind
840 {
841 let opt_suggestions = self
842 .infcx
843 .tcx
844 .typeck(path_segment.hir_id.owner.def_id)
845 .type_dependent_def_id(*hir_id)
846 .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
847 .map(|def_id| self.infcx.tcx.associated_items(def_id))
848 .map(|assoc_items| {
849 assoc_items
850 .in_definition_order()
851 .map(|assoc_item_def| assoc_item_def.ident(self.infcx.tcx))
852 .filter(|&ident| {
853 let original_method_ident = path_segment.ident;
854 original_method_ident != ident
855 && ident
856 .as_str()
857 .starts_with(&original_method_ident.name.to_string())
858 })
859 .map(|ident| format!("{ident}()"))
860 .peekable()
861 });
862
863 if let Some(mut suggestions) = opt_suggestions
864 && suggestions.peek().is_some()
865 {
866 err.span_suggestions(
867 *span,
868 "use mutable method",
869 suggestions,
870 Applicability::MaybeIncorrect,
871 );
872 }
873 }
874 };
875 }
876
877 /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
expected_fn_found_fn_mut_call(&self, err: &mut Diagnostic, sp: Span, act: &str)878 fn expected_fn_found_fn_mut_call(&self, err: &mut Diagnostic, sp: Span, act: &str) {
879 err.span_label(sp, format!("cannot {act}"));
880
881 let hir = self.infcx.tcx.hir();
882 let closure_id = self.mir_hir_id();
883 let closure_span = self.infcx.tcx.def_span(self.mir_def_id());
884 let fn_call_id = hir.parent_id(closure_id);
885 let node = hir.get(fn_call_id);
886 let def_id = hir.enclosing_body_owner(fn_call_id);
887 let mut look_at_return = true;
888 // If we can detect the expression to be an `fn` call where the closure was an argument,
889 // we point at the `fn` definition argument...
890 if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(func, args), .. }) = node {
891 let arg_pos = args
892 .iter()
893 .enumerate()
894 .filter(|(_, arg)| arg.hir_id == closure_id)
895 .map(|(pos, _)| pos)
896 .next();
897 let tables = self.infcx.tcx.typeck(def_id);
898 if let Some(ty::FnDef(def_id, _)) =
899 tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind())
900 {
901 let arg = match hir.get_if_local(*def_id) {
902 Some(
903 hir::Node::Item(hir::Item {
904 ident, kind: hir::ItemKind::Fn(sig, ..), ..
905 })
906 | hir::Node::TraitItem(hir::TraitItem {
907 ident,
908 kind: hir::TraitItemKind::Fn(sig, _),
909 ..
910 })
911 | hir::Node::ImplItem(hir::ImplItem {
912 ident,
913 kind: hir::ImplItemKind::Fn(sig, _),
914 ..
915 }),
916 ) => Some(
917 arg_pos
918 .and_then(|pos| {
919 sig.decl.inputs.get(
920 pos + if sig.decl.implicit_self.has_implicit_self() {
921 1
922 } else {
923 0
924 },
925 )
926 })
927 .map(|arg| arg.span)
928 .unwrap_or(ident.span),
929 ),
930 _ => None,
931 };
932 if let Some(span) = arg {
933 err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
934 err.span_label(func.span, "expects `Fn` instead of `FnMut`");
935 err.span_label(closure_span, "in this closure");
936 look_at_return = false;
937 }
938 }
939 }
940
941 if look_at_return && hir.get_return_block(closure_id).is_some() {
942 // ...otherwise we are probably in the tail expression of the function, point at the
943 // return type.
944 match hir.get_by_def_id(hir.get_parent_item(fn_call_id).def_id) {
945 hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. })
946 | hir::Node::TraitItem(hir::TraitItem {
947 ident,
948 kind: hir::TraitItemKind::Fn(sig, _),
949 ..
950 })
951 | hir::Node::ImplItem(hir::ImplItem {
952 ident,
953 kind: hir::ImplItemKind::Fn(sig, _),
954 ..
955 }) => {
956 err.span_label(ident.span, "");
957 err.span_label(
958 sig.decl.output.span(),
959 "change this to return `FnMut` instead of `Fn`",
960 );
961 err.span_label(closure_span, "in this closure");
962 }
963 _ => {}
964 }
965 }
966 }
967
suggest_make_local_mut( &self, err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, local: Local, name: Symbol, )968 fn suggest_make_local_mut(
969 &self,
970 err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
971 local: Local,
972 name: Symbol,
973 ) {
974 let local_decl = &self.body.local_decls[local];
975
976 let (pointer_sigil, pointer_desc) =
977 if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
978
979 let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
980 if is_trait_sig && local_trait.is_none() {
981 return;
982 }
983
984 let decl_span = match local_trait {
985 Some(span) => span,
986 None => local_decl.source_info.span,
987 };
988
989 let label = match *local_decl.local_info() {
990 LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
991 let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span);
992 Some((true, decl_span, suggestion))
993 }
994
995 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
996 binding_mode: ty::BindingMode::BindByValue(_),
997 opt_ty_info,
998 ..
999 })) => {
1000 // check if the RHS is from desugaring
1001 let opt_assignment_rhs_span =
1002 self.body.find_assignments(local).first().map(|&location| {
1003 if let Some(mir::Statement {
1004 source_info: _,
1005 kind:
1006 mir::StatementKind::Assign(box (
1007 _,
1008 mir::Rvalue::Use(mir::Operand::Copy(place)),
1009 )),
1010 }) = self.body[location.block].statements.get(location.statement_index)
1011 {
1012 self.body.local_decls[place.local].source_info.span
1013 } else {
1014 self.body.source_info(location).span
1015 }
1016 });
1017 match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
1018 // on for loops, RHS points to the iterator part
1019 Some(DesugaringKind::ForLoop) => {
1020 self.suggest_similar_mut_method_for_for_loop(err);
1021 err.span_label(
1022 opt_assignment_rhs_span.unwrap(),
1023 format!("this iterator yields `{pointer_sigil}` {pointer_desc}s",),
1024 );
1025 None
1026 }
1027 // don't create labels for compiler-generated spans
1028 Some(_) => None,
1029 None => {
1030 let label = if name != kw::SelfLower {
1031 suggest_ampmut(
1032 self.infcx.tcx,
1033 local_decl.ty,
1034 decl_span,
1035 opt_assignment_rhs_span,
1036 opt_ty_info,
1037 )
1038 } else {
1039 match local_decl.local_info() {
1040 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1041 opt_ty_info: None,
1042 ..
1043 })) => {
1044 let sugg = suggest_ampmut_self(self.infcx.tcx, decl_span);
1045 (true, decl_span, sugg)
1046 }
1047 // explicit self (eg `self: &'a Self`)
1048 _ => suggest_ampmut(
1049 self.infcx.tcx,
1050 local_decl.ty,
1051 decl_span,
1052 opt_assignment_rhs_span,
1053 opt_ty_info,
1054 ),
1055 }
1056 };
1057 Some(label)
1058 }
1059 }
1060 }
1061
1062 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1063 binding_mode: ty::BindingMode::BindByReference(_),
1064 ..
1065 })) => {
1066 let pattern_span: Span = local_decl.source_info.span;
1067 suggest_ref_mut(self.infcx.tcx, pattern_span)
1068 .map(|span| (true, span, "mut ".to_owned()))
1069 }
1070
1071 _ => unreachable!(),
1072 };
1073
1074 match label {
1075 Some((true, err_help_span, suggested_code)) => {
1076 err.span_suggestion_verbose(
1077 err_help_span,
1078 format!("consider changing this to be a mutable {pointer_desc}"),
1079 suggested_code,
1080 Applicability::MachineApplicable,
1081 );
1082 }
1083 Some((false, err_label_span, message)) => {
1084 struct BindingFinder {
1085 span: Span,
1086 hir_id: Option<hir::HirId>,
1087 }
1088
1089 impl<'tcx> Visitor<'tcx> for BindingFinder {
1090 fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
1091 if let hir::StmtKind::Local(local) = s.kind {
1092 if local.pat.span == self.span {
1093 self.hir_id = Some(local.hir_id);
1094 }
1095 }
1096 hir::intravisit::walk_stmt(self, s);
1097 }
1098 }
1099 let hir_map = self.infcx.tcx.hir();
1100 let def_id = self.body.source.def_id();
1101 let hir_id = hir_map.local_def_id_to_hir_id(def_id.expect_local());
1102 let node = hir_map.find(hir_id);
1103 let hir_id = if let Some(hir::Node::Item(item)) = node
1104 && let hir::ItemKind::Fn(.., body_id) = item.kind
1105 {
1106 let body = hir_map.body(body_id);
1107 let mut v = BindingFinder {
1108 span: err_label_span,
1109 hir_id: None,
1110 };
1111 v.visit_body(body);
1112 v.hir_id
1113 } else {
1114 None
1115 };
1116 if let Some(hir_id) = hir_id
1117 && let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
1118 {
1119 let (changing, span, sugg) = match local.ty {
1120 Some(ty) => ("changing", ty.span, message),
1121 None => (
1122 "specifying",
1123 local.pat.span.shrink_to_hi(),
1124 format!(": {message}"),
1125 ),
1126 };
1127 err.span_suggestion_verbose(
1128 span,
1129 format!("consider {changing} this binding's type"),
1130 sugg,
1131 Applicability::HasPlaceholders,
1132 );
1133 } else {
1134 err.span_label(
1135 err_label_span,
1136 format!(
1137 "consider changing this binding's type to be: `{message}`"
1138 ),
1139 );
1140 }
1141 }
1142 None => {}
1143 }
1144 }
1145 }
1146
mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool1147 pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
1148 debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
1149
1150 match *local_decl.local_info() {
1151 // Check if mutably borrowing a mutable reference.
1152 LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
1153 binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
1154 ..
1155 })) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
1156 LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => {
1157 // Check if the user variable is a `&mut self` and we can therefore
1158 // suggest removing the `&mut`.
1159 //
1160 // Deliberately fall into this case for all implicit self types,
1161 // so that we don't fall into the next case with them.
1162 kind == hir::ImplicitSelfKind::MutRef
1163 }
1164 _ if Some(kw::SelfLower) == local_name => {
1165 // Otherwise, check if the name is the `self` keyword - in which case
1166 // we have an explicit self. Do the same thing in this case and check
1167 // for a `self: &mut Self` to suggest removing the `&mut`.
1168 matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut))
1169 }
1170 _ => false,
1171 }
1172 }
1173
suggest_ampmut_self<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String1174 fn suggest_ampmut_self<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String {
1175 match tcx.sess.source_map().span_to_snippet(span) {
1176 Ok(snippet) => {
1177 let lt_pos = snippet.find('\'');
1178 if let Some(lt_pos) = lt_pos {
1179 format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
1180 } else {
1181 "&mut self".to_string()
1182 }
1183 }
1184 _ => "&mut self".to_string(),
1185 }
1186 }
1187
1188 // When we want to suggest a user change a local variable to be a `&mut`, there
1189 // are three potential "obvious" things to highlight:
1190 //
1191 // let ident [: Type] [= RightHandSideExpression];
1192 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
1193 // (1.) (2.) (3.)
1194 //
1195 // We can always fallback on highlighting the first. But chances are good that
1196 // the user experience will be better if we highlight one of the others if possible;
1197 // for example, if the RHS is present and the Type is not, then the type is going to
1198 // be inferred *from* the RHS, which means we should highlight that (and suggest
1199 // that they borrow the RHS mutably).
1200 //
1201 // This implementation attempts to emulate AST-borrowck prioritization
1202 // by trying (3.), then (2.) and finally falling back on (1.).
suggest_ampmut<'tcx>( tcx: TyCtxt<'tcx>, decl_ty: Ty<'tcx>, decl_span: Span, opt_assignment_rhs_span: Option<Span>, opt_ty_info: Option<Span>, ) -> (bool, Span, String)1203 fn suggest_ampmut<'tcx>(
1204 tcx: TyCtxt<'tcx>,
1205 decl_ty: Ty<'tcx>,
1206 decl_span: Span,
1207 opt_assignment_rhs_span: Option<Span>,
1208 opt_ty_info: Option<Span>,
1209 ) -> (bool, Span, String) {
1210 // if there is a RHS and it starts with a `&` from it, then check if it is
1211 // mutable, and if not, put suggest putting `mut ` to make it mutable.
1212 // we don't have to worry about lifetime annotations here because they are
1213 // not valid when taking a reference. For example, the following is not valid Rust:
1214 //
1215 // let x: &i32 = &'a 5;
1216 // ^^ lifetime annotation not allowed
1217 //
1218 if let Some(assignment_rhs_span) = opt_assignment_rhs_span
1219 && let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span)
1220 && let Some(stripped) = src.strip_prefix('&')
1221 {
1222 let is_mut = if let Some(rest) = stripped.trim_start().strip_prefix("mut") {
1223 match rest.chars().next() {
1224 // e.g. `&mut x`
1225 Some(c) if c.is_whitespace() => true,
1226 // e.g. `&mut(x)`
1227 Some('(') => true,
1228 // e.g. `&mut{x}`
1229 Some('{') => true,
1230 // e.g. `&mutablevar`
1231 _ => false,
1232 }
1233 } else {
1234 false
1235 };
1236 // if the reference is already mutable then there is nothing we can do
1237 // here.
1238 if !is_mut {
1239 let span = assignment_rhs_span;
1240 // shrink the span to just after the `&` in `&variable`
1241 let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
1242
1243 // FIXME(Ezrashaw): returning is bad because we still might want to
1244 // update the annotated type, see #106857.
1245 return (true, span, "mut ".to_owned());
1246 }
1247 }
1248
1249 let (binding_exists, span) = match opt_ty_info {
1250 // if this is a variable binding with an explicit type,
1251 // then we will suggest changing it to be mutable.
1252 // this is `Applicability::MachineApplicable`.
1253 Some(ty_span) => (true, ty_span),
1254
1255 // otherwise, we'll suggest *adding* an annotated type, we'll suggest
1256 // the RHS's type for that.
1257 // this is `Applicability::HasPlaceholders`.
1258 None => (false, decl_span),
1259 };
1260
1261 // if the binding already exists and is a reference with a explicit
1262 // lifetime, then we can suggest adding ` mut`. this is special-cased from
1263 // the path without a explicit lifetime.
1264 if let Ok(src) = tcx.sess.source_map().span_to_snippet(span)
1265 && src.starts_with("&'")
1266 // note that `& 'a T` is invalid so this is correct.
1267 && let Some(ws_pos) = src.find(char::is_whitespace)
1268 {
1269 let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo();
1270 (true, span, " mut".to_owned())
1271 // if there is already a binding, we modify it to be `mut`
1272 } else if binding_exists {
1273 // shrink the span to just after the `&` in `&variable`
1274 let span = span.with_lo(span.lo() + BytePos(1)).shrink_to_lo();
1275 (true, span, "mut ".to_owned())
1276 } else {
1277 // otherwise, suggest that the user annotates the binding; we provide the
1278 // type of the local.
1279 let ty_mut = decl_ty.builtin_deref(true).unwrap();
1280 assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
1281
1282 (
1283 false,
1284 span,
1285 format!("{}mut {}", if decl_ty.is_ref() {"&"} else {"*"}, ty_mut.ty)
1286 )
1287 }
1288 }
1289
is_closure_or_generator(ty: Ty<'_>) -> bool1290 fn is_closure_or_generator(ty: Ty<'_>) -> bool {
1291 ty.is_closure() || ty.is_generator()
1292 }
1293
1294 /// Given a field that needs to be mutable, returns a span where the " mut " could go.
1295 /// This function expects the local to be a reference to a struct in order to produce a span.
1296 ///
1297 /// ```text
1298 /// LL | s: &'a String
1299 /// | ^^^ returns a span taking up the space here
1300 /// ```
get_mut_span_in_struct_field<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, field: FieldIdx, ) -> Option<Span>1301 fn get_mut_span_in_struct_field<'tcx>(
1302 tcx: TyCtxt<'tcx>,
1303 ty: Ty<'tcx>,
1304 field: FieldIdx,
1305 ) -> Option<Span> {
1306 // Expect our local to be a reference to a struct of some kind.
1307 if let ty::Ref(_, ty, _) = ty.kind()
1308 && let ty::Adt(def, _) = ty.kind()
1309 && let field = def.all_fields().nth(field.index())?
1310 // Use the HIR types to construct the diagnostic message.
1311 && let node = tcx.hir().find_by_def_id(field.did.as_local()?)?
1312 // Now we're dealing with the actual struct that we're going to suggest a change to,
1313 // we can expect a field that is an immutable reference to a type.
1314 && let hir::Node::Field(field) = node
1315 && let hir::TyKind::Ref(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind
1316 {
1317 return Some(lt.ident.span.between(ty.span));
1318 }
1319
1320 None
1321 }
1322
1323 /// If possible, suggest replacing `ref` with `ref mut`.
suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span>1324 fn suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span> {
1325 let pattern_str = tcx.sess.source_map().span_to_snippet(span).ok()?;
1326 if pattern_str.starts_with("ref")
1327 && pattern_str["ref".len()..].starts_with(rustc_lexer::is_whitespace)
1328 {
1329 let span = span.with_lo(span.lo() + BytePos(4)).shrink_to_lo();
1330 Some(span)
1331 } else {
1332 None
1333 }
1334 }
1335