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