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