]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / compiler / rustc_mir / src / borrow_check / diagnostics / mutability_errors.rs
1 use rustc_hir as hir;
2 use rustc_hir::Node;
3 use rustc_index::vec::Idx;
4 use rustc_middle::hir::map::Map;
5 use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
6 use rustc_middle::ty::{self, Ty, TyCtxt};
7 use rustc_middle::{
8 hir::place::PlaceBase,
9 mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location},
10 };
11 use rustc_span::source_map::DesugaringKind;
12 use rustc_span::symbol::{kw, Symbol};
13 use rustc_span::Span;
14
15 use crate::borrow_check::diagnostics::BorrowedContentSource;
16 use crate::borrow_check::MirBorrowckCtxt;
17 use crate::util::collect_writes::FindAssignments;
18 use rustc_errors::{Applicability, DiagnosticBuilder};
19
20 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
21 pub(crate) enum AccessKind {
22 MutableBorrow,
23 Mutate,
24 }
25
26 impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
27 pub(crate) fn report_mutability_error(
28 &mut self,
29 access_place: Place<'tcx>,
30 span: Span,
31 the_place_err: PlaceRef<'tcx>,
32 error_access: AccessKind,
33 location: Location,
34 ) {
35 debug!(
36 "report_mutability_error(\
37 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
38 )",
39 access_place, span, the_place_err, error_access, location,
40 );
41
42 let mut err;
43 let item_msg;
44 let reason;
45 let mut opt_source = None;
46 let access_place_desc = self.describe_place(access_place.as_ref());
47 debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
48
49 match the_place_err {
50 PlaceRef { local, projection: [] } => {
51 item_msg = format!("`{}`", access_place_desc.unwrap());
52 if access_place.as_local().is_some() {
53 reason = ", as it is not declared as mutable".to_string();
54 } else {
55 let name = self.local_names[local].expect("immutable unnamed local");
56 reason = format!(", as `{}` is not declared as mutable", name);
57 }
58 }
59
60 PlaceRef {
61 local,
62 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
63 } => {
64 debug_assert!(is_closure_or_generator(
65 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
66 ));
67
68 let imm_borrow_derefed = self.upvars[upvar_index.index()]
69 .place
70 .place
71 .deref_tys()
72 .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));
73
74 // If the place is immutable then:
75 //
76 // - Either we deref a immutable ref to get to our final place.
77 // - We don't capture derefs of raw ptrs
78 // - Or the final place is immut because the root variable of the capture
79 // isn't marked mut and we should suggest that to the user.
80 if imm_borrow_derefed {
81 // If we deref an immutable ref then the suggestion here doesn't help.
82 return;
83 } else {
84 item_msg = format!("`{}`", access_place_desc.unwrap());
85 if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
86 reason = ", as it is not declared as mutable".to_string();
87 } else {
88 let name = self.upvars[upvar_index.index()].name;
89 reason = format!(", as `{}` is not declared as mutable", name);
90 }
91 }
92 }
93
94 PlaceRef { local, projection: [ProjectionElem::Deref] }
95 if self.body.local_decls[local].is_ref_for_guard() =>
96 {
97 item_msg = format!("`{}`", access_place_desc.unwrap());
98 reason = ", as it is immutable for the pattern guard".to_string();
99 }
100 PlaceRef { local, projection: [ProjectionElem::Deref] }
101 if self.body.local_decls[local].is_ref_to_static() =>
102 {
103 if access_place.projection.len() == 1 {
104 item_msg = format!("immutable static item `{}`", access_place_desc.unwrap());
105 reason = String::new();
106 } else {
107 item_msg = format!("`{}`", access_place_desc.unwrap());
108 let local_info = &self.body.local_decls[local].local_info;
109 if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info {
110 let static_name = &self.infcx.tcx.item_name(def_id);
111 reason = format!(", as `{}` is an immutable static item", static_name);
112 } else {
113 bug!("is_ref_to_static return true, but not ref to static?");
114 }
115 }
116 }
117 PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] } => {
118 if the_place_err.local == Local::new(1)
119 && proj_base.is_empty()
120 && !self.upvars.is_empty()
121 {
122 item_msg = format!("`{}`", access_place_desc.unwrap());
123 debug_assert!(self.body.local_decls[Local::new(1)].ty.is_region_ptr());
124 debug_assert!(is_closure_or_generator(
125 Place::ty_from(
126 the_place_err.local,
127 the_place_err.projection,
128 self.body,
129 self.infcx.tcx
130 )
131 .ty
132 ));
133
134 reason = if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
135 ", as it is a captured variable in a `Fn` closure".to_string()
136 } else {
137 ", as `Fn` closures cannot mutate their captured variables".to_string()
138 }
139 } else {
140 let source = self.borrowed_content_source(PlaceRef {
141 local: the_place_err.local,
142 projection: proj_base,
143 });
144 let pointer_type = source.describe_for_immutable_place(self.infcx.tcx);
145 opt_source = Some(source);
146 if let Some(desc) = access_place_desc {
147 item_msg = format!("`{}`", desc);
148 reason = match error_access {
149 AccessKind::Mutate => format!(" which is behind {}", pointer_type),
150 AccessKind::MutableBorrow => {
151 format!(", as it is behind {}", pointer_type)
152 }
153 }
154 } else {
155 item_msg = format!("data in {}", pointer_type);
156 reason = String::new();
157 }
158 }
159 }
160
161 PlaceRef {
162 local: _,
163 projection:
164 [.., ProjectionElem::Index(_)
165 | ProjectionElem::ConstantIndex { .. }
166 | ProjectionElem::Subslice { .. }
167 | ProjectionElem::Downcast(..)],
168 } => bug!("Unexpected immutable place."),
169 }
170
171 debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
172
173 // `act` and `acted_on` are strings that let us abstract over
174 // the verbs used in some diagnostic messages.
175 let act;
176 let acted_on;
177
178 let span = match error_access {
179 AccessKind::Mutate => {
180 err = self.cannot_assign(span, &(item_msg + &reason));
181 act = "assign";
182 acted_on = "written";
183 span
184 }
185 AccessKind::MutableBorrow => {
186 act = "borrow as mutable";
187 acted_on = "borrowed as mutable";
188
189 let borrow_spans = self.borrow_spans(span, location);
190 let borrow_span = borrow_spans.args_or_use();
191 err = self.cannot_borrow_path_as_mutable_because(borrow_span, &item_msg, &reason);
192 borrow_spans.var_span_label(
193 &mut err,
194 format!(
195 "mutable borrow occurs due to use of {} in closure",
196 self.describe_any_place(access_place.as_ref()),
197 ),
198 );
199 borrow_span
200 }
201 };
202
203 debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
204
205 match the_place_err {
206 // Suggest making an existing shared borrow in a struct definition a mutable borrow.
207 //
208 // This is applicable when we have a deref of a field access to a deref of a local -
209 // something like `*((*_1).0`. The local that we get will be a reference to the
210 // struct we've got a field access of (it must be a reference since there's a deref
211 // after the field access).
212 PlaceRef {
213 local,
214 projection:
215 [proj_base @ .., ProjectionElem::Deref, ProjectionElem::Field(field, _), ProjectionElem::Deref],
216 } => {
217 err.span_label(span, format!("cannot {ACT}", ACT = act));
218
219 if let Some((span, message)) = annotate_struct_field(
220 self.infcx.tcx,
221 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty,
222 field,
223 ) {
224 err.span_suggestion(
225 span,
226 "consider changing this to be mutable",
227 message,
228 Applicability::MaybeIncorrect,
229 );
230 }
231 }
232
233 // Suggest removing a `&mut` from the use of a mutable reference.
234 PlaceRef { local, projection: [] }
235 if self
236 .body
237 .local_decls
238 .get(local)
239 .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local]))
240 .unwrap_or(false) =>
241 {
242 err.span_label(span, format!("cannot {ACT}", ACT = act));
243 err.span_label(span, "try removing `&mut` here");
244 }
245
246 // We want to suggest users use `let mut` for local (user
247 // variable) mutations...
248 PlaceRef { local, projection: [] }
249 if self.body.local_decls[local].can_be_made_mutable() =>
250 {
251 // ... but it doesn't make sense to suggest it on
252 // variables that are `ref x`, `ref mut x`, `&self`,
253 // or `&mut self` (such variables are simply not
254 // mutable).
255 let local_decl = &self.body.local_decls[local];
256 assert_eq!(local_decl.mutability, Mutability::Not);
257
258 err.span_label(span, format!("cannot {ACT}", ACT = act));
259 err.span_suggestion(
260 local_decl.source_info.span,
261 "consider changing this to be mutable",
262 format!("mut {}", self.local_names[local].unwrap()),
263 Applicability::MachineApplicable,
264 );
265 let tcx = self.infcx.tcx;
266 if let ty::Closure(id, _) = the_place_err.ty(self.body, tcx).ty.kind() {
267 self.show_mutating_upvar(tcx, id, the_place_err, &mut err);
268 }
269 }
270
271 // Also suggest adding mut for upvars
272 PlaceRef {
273 local,
274 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
275 } => {
276 debug_assert!(is_closure_or_generator(
277 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
278 ));
279
280 let captured_place = &self.upvars[upvar_index.index()].place;
281
282 err.span_label(span, format!("cannot {ACT}", ACT = act));
283
284 let upvar_hir_id = captured_place.get_root_variable();
285
286 if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) {
287 if let hir::PatKind::Binding(
288 hir::BindingAnnotation::Unannotated,
289 _,
290 upvar_ident,
291 _,
292 ) = pat.kind
293 {
294 err.span_suggestion(
295 upvar_ident.span,
296 "consider changing this to be mutable",
297 format!("mut {}", upvar_ident.name),
298 Applicability::MachineApplicable,
299 );
300 }
301 }
302
303 let tcx = self.infcx.tcx;
304 if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
305 {
306 if let ty::Closure(id, _) = ty.kind() {
307 self.show_mutating_upvar(tcx, id, the_place_err, &mut err);
308 }
309 }
310 }
311
312 // complete hack to approximate old AST-borrowck
313 // diagnostic: if the span starts with a mutable borrow of
314 // a local variable, then just suggest the user remove it.
315 PlaceRef { local: _, projection: [] }
316 if {
317 if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
318 snippet.starts_with("&mut ")
319 } else {
320 false
321 }
322 } =>
323 {
324 err.span_label(span, format!("cannot {ACT}", ACT = act));
325 err.span_label(span, "try removing `&mut` here");
326 }
327
328 PlaceRef { local, projection: [ProjectionElem::Deref] }
329 if self.body.local_decls[local].is_ref_for_guard() =>
330 {
331 err.span_label(span, format!("cannot {ACT}", ACT = act));
332 err.note(
333 "variables bound in patterns are immutable until the end of the pattern guard",
334 );
335 }
336
337 // We want to point out when a `&` can be readily replaced
338 // with an `&mut`.
339 //
340 // FIXME: can this case be generalized to work for an
341 // arbitrary base for the projection?
342 PlaceRef { local, projection: [ProjectionElem::Deref] }
343 if self.body.local_decls[local].is_user_variable() =>
344 {
345 let local_decl = &self.body.local_decls[local];
346
347 let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
348 ("&", "reference")
349 } else {
350 ("*const", "pointer")
351 };
352
353 match self.local_names[local] {
354 Some(name) if !local_decl.from_compiler_desugaring() => {
355 let label = match local_decl.local_info.as_ref().unwrap() {
356 box LocalInfo::User(ClearCrossCrate::Set(
357 mir::BindingForm::ImplicitSelf(_),
358 )) => {
359 let (span, suggestion) =
360 suggest_ampmut_self(self.infcx.tcx, local_decl);
361 Some((true, span, suggestion))
362 }
363
364 box LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
365 mir::VarBindingForm {
366 binding_mode: ty::BindingMode::BindByValue(_),
367 opt_ty_info,
368 ..
369 },
370 ))) => {
371 // check if the RHS is from desugaring
372 let locations = self.body.find_assignments(local);
373 let opt_assignment_rhs_span = locations
374 .first()
375 .map(|&location| self.body.source_info(location).span);
376 let opt_desugaring_kind =
377 opt_assignment_rhs_span.and_then(|span| span.desugaring_kind());
378 match opt_desugaring_kind {
379 // on for loops, RHS points to the iterator part
380 Some(DesugaringKind::ForLoop(_)) => {
381 self.suggest_similar_mut_method_for_for_loop(&mut err);
382 Some((
383 false,
384 opt_assignment_rhs_span.unwrap(),
385 format!(
386 "this iterator yields `{SIGIL}` {DESC}s",
387 SIGIL = pointer_sigil,
388 DESC = pointer_desc
389 ),
390 ))
391 }
392 // don't create labels for compiler-generated spans
393 Some(_) => None,
394 None => {
395 let (span, suggestion) = suggest_ampmut(
396 self.infcx.tcx,
397 local_decl,
398 opt_assignment_rhs_span,
399 *opt_ty_info,
400 );
401 Some((true, span, suggestion))
402 }
403 }
404 }
405
406 box LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
407 mir::VarBindingForm {
408 binding_mode: ty::BindingMode::BindByReference(_),
409 ..
410 },
411 ))) => {
412 let pattern_span = local_decl.source_info.span;
413 suggest_ref_mut(self.infcx.tcx, pattern_span)
414 .map(|replacement| (true, pattern_span, replacement))
415 }
416
417 box LocalInfo::User(ClearCrossCrate::Clear) => {
418 bug!("saw cleared local state")
419 }
420
421 _ => unreachable!(),
422 };
423
424 match label {
425 Some((true, err_help_span, suggested_code)) => {
426 err.span_suggestion(
427 err_help_span,
428 &format!(
429 "consider changing this to be a mutable {}",
430 pointer_desc
431 ),
432 suggested_code,
433 Applicability::MachineApplicable,
434 );
435 }
436 Some((false, err_label_span, message)) => {
437 err.span_label(err_label_span, &message);
438 }
439 None => {}
440 }
441 err.span_label(
442 span,
443 format!(
444 "`{NAME}` is a `{SIGIL}` {DESC}, \
445 so the data it refers to cannot be {ACTED_ON}",
446 NAME = name,
447 SIGIL = pointer_sigil,
448 DESC = pointer_desc,
449 ACTED_ON = acted_on
450 ),
451 );
452 }
453 _ => {
454 err.span_label(
455 span,
456 format!(
457 "cannot {ACT} through `{SIGIL}` {DESC}",
458 ACT = act,
459 SIGIL = pointer_sigil,
460 DESC = pointer_desc
461 ),
462 );
463 }
464 }
465 }
466
467 PlaceRef {
468 local,
469 projection: [ProjectionElem::Deref],
470 // FIXME document what is this 1 magic number about
471 } if local == Local::new(1) && !self.upvars.is_empty() => {
472 self.expected_fn_found_fn_mut_call(&mut err, span, act);
473 }
474
475 PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
476 err.span_label(span, format!("cannot {ACT}", ACT = act));
477
478 match opt_source {
479 Some(BorrowedContentSource::OverloadedDeref(ty)) => {
480 err.help(&format!(
481 "trait `DerefMut` is required to modify through a dereference, \
482 but it is not implemented for `{}`",
483 ty,
484 ));
485 }
486 Some(BorrowedContentSource::OverloadedIndex(ty)) => {
487 err.help(&format!(
488 "trait `IndexMut` is required to modify indexed content, \
489 but it is not implemented for `{}`",
490 ty,
491 ));
492 }
493 _ => (),
494 }
495 }
496
497 _ => {
498 err.span_label(span, format!("cannot {ACT}", ACT = act));
499 }
500 }
501
502 err.buffer(&mut self.errors_buffer);
503 }
504
505 // point to span of upvar making closure call require mutable borrow
506 fn show_mutating_upvar(
507 &self,
508 tcx: TyCtxt<'_>,
509 id: &hir::def_id::DefId,
510 the_place_err: PlaceRef<'tcx>,
511 err: &mut DiagnosticBuilder<'_>,
512 ) {
513 let closure_local_def_id = id.expect_local();
514 let tables = tcx.typeck(closure_local_def_id);
515 let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_local_def_id);
516 if let Some((span, closure_kind_origin)) =
517 &tables.closure_kind_origins().get(closure_hir_id)
518 {
519 let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
520 let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
521 let root_hir_id = upvar_id.var_path.hir_id;
522 // we have a origin for this closure kind starting at this root variable so it's safe to unwrap here
523 let captured_places = tables.closure_min_captures[id].get(&root_hir_id).unwrap();
524
525 let origin_projection = closure_kind_origin
526 .projections
527 .iter()
528 .map(|proj| proj.kind)
529 .collect::<Vec<_>>();
530 let mut capture_reason = String::new();
531 for captured_place in captured_places {
532 let captured_place_kinds = captured_place
533 .place
534 .projections
535 .iter()
536 .map(|proj| proj.kind)
537 .collect::<Vec<_>>();
538 if rustc_middle::ty::is_ancestor_or_same_capture(
539 &captured_place_kinds,
540 &origin_projection,
541 ) {
542 match captured_place.info.capture_kind {
543 ty::UpvarCapture::ByRef(ty::UpvarBorrow {
544 kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
545 ..
546 }) => {
547 capture_reason = format!("mutable borrow of `{}`", upvar);
548 }
549 ty::UpvarCapture::ByValue(_) => {
550 capture_reason = format!("possible mutation of `{}`", upvar);
551 }
552 _ => bug!("upvar `{}` borrowed, but not mutably", upvar),
553 }
554 break;
555 }
556 }
557 if capture_reason.is_empty() {
558 bug!("upvar `{}` borrowed, but cannot find reason", upvar);
559 }
560 capture_reason
561 } else {
562 bug!("not an upvar")
563 };
564 err.span_label(
565 *span,
566 format!(
567 "calling `{}` requires mutable binding due to {}",
568 self.describe_place(the_place_err).unwrap(),
569 reason
570 ),
571 );
572 }
573 }
574
575 // Attempt to search similar mutable associated items for suggestion.
576 // In the future, attempt in all path but initially for RHS of for_loop
577 fn suggest_similar_mut_method_for_for_loop(&self, err: &mut DiagnosticBuilder<'_>) {
578 use hir::{
579 BodyId, Expr,
580 ExprKind::{Block, Call, DropTemps, Match, MethodCall},
581 HirId, ImplItem, ImplItemKind, Item, ItemKind,
582 };
583
584 fn maybe_body_id_of_fn(hir_map: &Map<'tcx>, id: HirId) -> Option<BodyId> {
585 match hir_map.find(id) {
586 Some(Node::Item(Item { kind: ItemKind::Fn(_, _, body_id), .. }))
587 | Some(Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. })) => {
588 Some(*body_id)
589 }
590 _ => None,
591 }
592 }
593 let hir_map = self.infcx.tcx.hir();
594 let mir_body_hir_id = self.mir_hir_id();
595 if let Some(fn_body_id) = maybe_body_id_of_fn(&hir_map, mir_body_hir_id) {
596 if let Block(
597 hir::Block {
598 expr:
599 Some(Expr {
600 kind:
601 DropTemps(Expr {
602 kind:
603 Match(
604 Expr {
605 kind:
606 Call(
607 _,
608 [Expr {
609 kind: MethodCall(path_segment, ..),
610 hir_id,
611 ..
612 }, ..],
613 ),
614 ..
615 },
616 ..,
617 ),
618 ..
619 }),
620 ..
621 }),
622 ..
623 },
624 _,
625 ) = hir_map.body(fn_body_id).value.kind
626 {
627 let opt_suggestions = path_segment
628 .hir_id
629 .map(|path_hir_id| self.infcx.tcx.typeck(path_hir_id.owner))
630 .and_then(|typeck| typeck.type_dependent_def_id(*hir_id))
631 .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
632 .map(|def_id| self.infcx.tcx.associated_items(def_id))
633 .map(|assoc_items| {
634 assoc_items
635 .in_definition_order()
636 .map(|assoc_item_def| assoc_item_def.ident)
637 .filter(|&ident| {
638 let original_method_ident = path_segment.ident;
639 original_method_ident != ident
640 && ident
641 .as_str()
642 .starts_with(&original_method_ident.name.to_string())
643 })
644 .map(|ident| format!("{}()", ident))
645 });
646
647 if let Some(suggestions) = opt_suggestions {
648 err.span_suggestions(
649 path_segment.ident.span,
650 &format!("use mutable method"),
651 suggestions,
652 Applicability::MaybeIncorrect,
653 );
654 }
655 }
656 };
657 }
658
659 /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
660 fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Span, act: &str) {
661 err.span_label(sp, format!("cannot {}", act));
662
663 let hir = self.infcx.tcx.hir();
664 let closure_id = self.mir_hir_id();
665 let fn_call_id = hir.get_parent_node(closure_id);
666 let node = hir.get(fn_call_id);
667 let item_id = hir.enclosing_body_owner(fn_call_id);
668 let mut look_at_return = true;
669 // If we can detect the expression to be an `fn` call where the closure was an argument,
670 // we point at the `fn` definition argument...
671 if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(func, args), .. }) = node {
672 let arg_pos = args
673 .iter()
674 .enumerate()
675 .filter(|(_, arg)| arg.span == self.body.span)
676 .map(|(pos, _)| pos)
677 .next();
678 let def_id = hir.local_def_id(item_id);
679 let tables = self.infcx.tcx.typeck(def_id);
680 if let Some(ty::FnDef(def_id, _)) =
681 tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind())
682 {
683 let arg = match hir.get_if_local(*def_id) {
684 Some(
685 hir::Node::Item(hir::Item {
686 ident, kind: hir::ItemKind::Fn(sig, ..), ..
687 })
688 | hir::Node::TraitItem(hir::TraitItem {
689 ident,
690 kind: hir::TraitItemKind::Fn(sig, _),
691 ..
692 })
693 | hir::Node::ImplItem(hir::ImplItem {
694 ident,
695 kind: hir::ImplItemKind::Fn(sig, _),
696 ..
697 }),
698 ) => Some(
699 arg_pos
700 .and_then(|pos| {
701 sig.decl.inputs.get(
702 pos + if sig.decl.implicit_self.has_implicit_self() {
703 1
704 } else {
705 0
706 },
707 )
708 })
709 .map(|arg| arg.span)
710 .unwrap_or(ident.span),
711 ),
712 _ => None,
713 };
714 if let Some(span) = arg {
715 err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
716 err.span_label(func.span, "expects `Fn` instead of `FnMut`");
717 if self.infcx.tcx.sess.source_map().is_multiline(self.body.span) {
718 err.span_label(self.body.span, "in this closure");
719 }
720 look_at_return = false;
721 }
722 }
723 }
724
725 if look_at_return && hir.get_return_block(closure_id).is_some() {
726 // ...otherwise we are probably in the tail expression of the function, point at the
727 // return type.
728 match hir.get(hir.get_parent_item(fn_call_id)) {
729 hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. })
730 | hir::Node::TraitItem(hir::TraitItem {
731 ident,
732 kind: hir::TraitItemKind::Fn(sig, _),
733 ..
734 })
735 | hir::Node::ImplItem(hir::ImplItem {
736 ident,
737 kind: hir::ImplItemKind::Fn(sig, _),
738 ..
739 }) => {
740 err.span_label(ident.span, "");
741 err.span_label(
742 sig.decl.output.span(),
743 "change this to return `FnMut` instead of `Fn`",
744 );
745 err.span_label(self.body.span, "in this closure");
746 }
747 _ => {}
748 }
749 }
750 }
751 }
752
753 fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
754 debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
755
756 match local_decl.local_info.as_deref() {
757 // Check if mutably borrowing a mutable reference.
758 Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
759 mir::VarBindingForm {
760 binding_mode: ty::BindingMode::BindByValue(Mutability::Not), ..
761 },
762 )))) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
763 Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(kind)))) => {
764 // Check if the user variable is a `&mut self` and we can therefore
765 // suggest removing the `&mut`.
766 //
767 // Deliberately fall into this case for all implicit self types,
768 // so that we don't fall in to the next case with them.
769 *kind == mir::ImplicitSelfKind::MutRef
770 }
771 _ if Some(kw::SelfLower) == local_name => {
772 // Otherwise, check if the name is the `self` keyword - in which case
773 // we have an explicit self. Do the same thing in this case and check
774 // for a `self: &mut Self` to suggest removing the `&mut`.
775 matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut))
776 }
777 _ => false,
778 }
779 }
780
781 fn suggest_ampmut_self<'tcx>(
782 tcx: TyCtxt<'tcx>,
783 local_decl: &mir::LocalDecl<'tcx>,
784 ) -> (Span, String) {
785 let sp = local_decl.source_info.span;
786 (
787 sp,
788 match tcx.sess.source_map().span_to_snippet(sp) {
789 Ok(snippet) => {
790 let lt_pos = snippet.find('\'');
791 if let Some(lt_pos) = lt_pos {
792 format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
793 } else {
794 "&mut self".to_string()
795 }
796 }
797 _ => "&mut self".to_string(),
798 },
799 )
800 }
801
802 // When we want to suggest a user change a local variable to be a `&mut`, there
803 // are three potential "obvious" things to highlight:
804 //
805 // let ident [: Type] [= RightHandSideExpression];
806 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
807 // (1.) (2.) (3.)
808 //
809 // We can always fallback on highlighting the first. But chances are good that
810 // the user experience will be better if we highlight one of the others if possible;
811 // for example, if the RHS is present and the Type is not, then the type is going to
812 // be inferred *from* the RHS, which means we should highlight that (and suggest
813 // that they borrow the RHS mutably).
814 //
815 // This implementation attempts to emulate AST-borrowck prioritization
816 // by trying (3.), then (2.) and finally falling back on (1.).
817 fn suggest_ampmut<'tcx>(
818 tcx: TyCtxt<'tcx>,
819 local_decl: &mir::LocalDecl<'tcx>,
820 opt_assignment_rhs_span: Option<Span>,
821 opt_ty_info: Option<Span>,
822 ) -> (Span, String) {
823 if let Some(assignment_rhs_span) = opt_assignment_rhs_span {
824 if let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span) {
825 if let (true, Some(ws_pos)) =
826 (src.starts_with("&'"), src.find(|c: char| -> bool { c.is_whitespace() }))
827 {
828 let lt_name = &src[1..ws_pos];
829 let ty = &src[ws_pos..];
830 return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty));
831 } else if let Some(stripped) = src.strip_prefix('&') {
832 return (assignment_rhs_span, format!("&mut {}", stripped));
833 }
834 }
835 }
836
837 let highlight_span = match opt_ty_info {
838 // if this is a variable binding with an explicit type,
839 // try to highlight that for the suggestion.
840 Some(ty_span) => ty_span,
841
842 // otherwise, just highlight the span associated with
843 // the (MIR) LocalDecl.
844 None => local_decl.source_info.span,
845 };
846
847 if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span) {
848 if let (true, Some(ws_pos)) =
849 (src.starts_with("&'"), src.find(|c: char| -> bool { c.is_whitespace() }))
850 {
851 let lt_name = &src[1..ws_pos];
852 let ty = &src[ws_pos..];
853 return (highlight_span, format!("&{} mut{}", lt_name, ty));
854 }
855 }
856
857 let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
858 assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
859 (
860 highlight_span,
861 if local_decl.ty.is_region_ptr() {
862 format!("&mut {}", ty_mut.ty)
863 } else {
864 format!("*mut {}", ty_mut.ty)
865 },
866 )
867 }
868
869 fn is_closure_or_generator(ty: Ty<'_>) -> bool {
870 ty.is_closure() || ty.is_generator()
871 }
872
873 /// Adds a suggestion to a struct definition given a field access to a local.
874 /// This function expects the local to be a reference to a struct in order to produce a suggestion.
875 ///
876 /// ```text
877 /// LL | s: &'a String
878 /// | ---------- use `&'a mut String` here to make mutable
879 /// ```
880 fn annotate_struct_field(
881 tcx: TyCtxt<'tcx>,
882 ty: Ty<'tcx>,
883 field: &mir::Field,
884 ) -> Option<(Span, String)> {
885 // Expect our local to be a reference to a struct of some kind.
886 if let ty::Ref(_, ty, _) = ty.kind() {
887 if let ty::Adt(def, _) = ty.kind() {
888 let field = def.all_fields().nth(field.index())?;
889 // Use the HIR types to construct the diagnostic message.
890 let hir_id = tcx.hir().local_def_id_to_hir_id(field.did.as_local()?);
891 let node = tcx.hir().find(hir_id)?;
892 // Now we're dealing with the actual struct that we're going to suggest a change to,
893 // we can expect a field that is an immutable reference to a type.
894 if let hir::Node::Field(field) = node {
895 if let hir::TyKind::Rptr(
896 lifetime,
897 hir::MutTy { mutbl: hir::Mutability::Not, ref ty },
898 ) = field.ty.kind
899 {
900 // Get the snippets in two parts - the named lifetime (if there is one) and
901 // type being referenced, that way we can reconstruct the snippet without loss
902 // of detail.
903 let type_snippet = tcx.sess.source_map().span_to_snippet(ty.span).ok()?;
904 let lifetime_snippet = if !lifetime.is_elided() {
905 format!("{} ", tcx.sess.source_map().span_to_snippet(lifetime.span).ok()?)
906 } else {
907 String::new()
908 };
909
910 return Some((
911 field.ty.span,
912 format!("&{}mut {}", lifetime_snippet, &*type_snippet,),
913 ));
914 }
915 }
916 }
917 }
918
919 None
920 }
921
922 /// If possible, suggest replacing `ref` with `ref mut`.
923 fn suggest_ref_mut(tcx: TyCtxt<'_>, binding_span: Span) -> Option<String> {
924 let hi_src = tcx.sess.source_map().span_to_snippet(binding_span).ok()?;
925 if hi_src.starts_with("ref") && hi_src["ref".len()..].starts_with(rustc_lexer::is_whitespace) {
926 let replacement = format!("ref mut{}", &hi_src["ref".len()..]);
927 Some(replacement)
928 } else {
929 None
930 }
931 }