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