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