]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | use rustc_hir as hir; |
2 | use rustc_hir::Node; | |
6a06907d | 3 | use rustc_middle::hir::map::Map; |
ba9703b0 XL |
4 | use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; |
5 | use rustc_middle::ty::{self, Ty, TyCtxt}; | |
5869c6ff XL |
6 | use 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 | 13 | use rustc_span::source_map::DesugaringKind; |
29967ef6 | 14 | use rustc_span::symbol::{kw, Symbol}; |
94222f64 | 15 | use rustc_span::{BytePos, Span}; |
8faf50e0 | 16 | |
c295e0f8 XL |
17 | use crate::diagnostics::BorrowedContentSource; |
18 | use crate::MirBorrowckCtxt; | |
19 | use rustc_const_eval::util::collect_writes::FindAssignments; | |
5e7ed085 | 20 | use rustc_errors::{Applicability, Diagnostic}; |
8faf50e0 | 21 | |
b7449926 | 22 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
60c5eb7d | 23 | pub(crate) enum AccessKind { |
8faf50e0 XL |
24 | MutableBorrow, |
25 | Mutate, | |
8faf50e0 XL |
26 | } |
27 | ||
dc9dc135 | 28 | impl<'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 |
944 | fn 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 |
972 | fn 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 |
1008 | fn 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 | 1077 | fn 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 | 1088 | fn 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 |
1111 | fn 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 | } |