FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
};
-use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TypeFoldable};
+use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
use rustc_span::source_map::DesugaringKind;
use rustc_span::symbol::sym;
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{MultiSpan, Span, DUMMY_SP};
+use rustc_trait_selection::infer::InferCtxtExt;
use crate::dataflow::drop_flag_effects;
use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex};
self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
let span = use_spans.args_or_use();
- let move_site_vec = self.get_moved_indexes(location, mpi);
+ let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
debug!(
"report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
move_site_vec, use_spans
self.describe_place_with_options(moved_place, IncludingDowncast(true)),
);
+ let reinit_spans = maybe_reinitialized_locations
+ .iter()
+ .take(3)
+ .map(|loc| {
+ self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
+ .args_or_use()
+ })
+ .collect::<Vec<Span>>();
+ let reinits = maybe_reinitialized_locations.len();
+ if reinits == 1 {
+ err.span_label(reinit_spans[0], "this reinitialization might get skipped");
+ } else if reinits > 1 {
+ err.span_note(
+ MultiSpan::from_spans(reinit_spans),
+ &if reinits <= 3 {
+ format!("these {} reinitializations might get skipped", reinits)
+ } else {
+ format!(
+ "these 3 reinitializations and {} other{} might get skipped",
+ reinits - 3,
+ if reinits == 4 { "" } else { "s" }
+ )
+ },
+ );
+ }
+
self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
let mut is_loop_move = false;
),
);
}
- if is_option_or_result {
+ if is_option_or_result && maybe_reinitialized_locations.is_empty() {
err.span_suggestion_verbose(
fn_call_span.shrink_to_lo(),
"consider calling `.as_ref()` to borrow the type's contents",
}
}
- if let UseSpans::PatUse(span) = move_spans {
- err.span_suggestion_verbose(
- span.shrink_to_lo(),
- &format!(
- "borrow this field in the pattern to avoid moving {}",
- self.describe_place(moved_place.as_ref())
- .map(|n| format!("`{}`", n))
- .unwrap_or_else(|| "the value".to_string())
- ),
- "ref ".to_string(),
- Applicability::MachineApplicable,
- );
- in_pattern = true;
+ if let (UseSpans::PatUse(span), []) =
+ (move_spans, &maybe_reinitialized_locations[..])
+ {
+ if maybe_reinitialized_locations.is_empty() {
+ err.span_suggestion_verbose(
+ span.shrink_to_lo(),
+ &format!(
+ "borrow this field in the pattern to avoid moving {}",
+ self.describe_place(moved_place.as_ref())
+ .map(|n| format!("`{}`", n))
+ .unwrap_or_else(|| "the value".to_string())
+ ),
+ "ref ".to_string(),
+ Applicability::MachineApplicable,
+ );
+ in_pattern = true;
+ }
}
if let Some(DesugaringKind::ForLoop(_)) = move_span.desugaring_kind() {
&mut err,
"",
Some(borrow_span),
+ None,
);
err.buffer(&mut self.errors_buffer);
}
&mut err,
"",
None,
+ None,
);
err
}
&mut err,
first_borrow_desc,
None,
+ Some((issued_span, span)),
);
err
&mut err,
"",
None,
+ None,
);
}
} else {
&mut err,
"",
None,
+ None,
);
}
&mut err,
"",
None,
+ None,
);
err.buffer(&mut self.errors_buffer);
&mut err,
"",
None,
+ None,
);
let within = if borrow_spans.for_generator() { " by generator" } else { "" };
let return_ty = tcx.erase_regions(return_ty);
// to avoid panics
- if !return_ty.has_infer_types() {
- if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
- if tcx.type_implements_trait((iter_trait, return_ty, ty_params, self.param_env))
- {
- if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
- err.span_suggestion_hidden(
- return_span,
- "use `.collect()` to allocate the iterator",
- format!("{}{}", snippet, ".collect::<Vec<_>>()"),
- Applicability::MaybeIncorrect,
- );
- }
+ if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
+ if self
+ .infcx
+ .type_implements_trait(iter_trait, return_ty, ty_params, self.param_env)
+ .must_apply_modulo_regions()
+ {
+ if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
+ err.span_suggestion_hidden(
+ return_span,
+ "use `.collect()` to allocate the iterator",
+ format!("{}{}", snippet, ".collect::<Vec<_>>()"),
+ Applicability::MaybeIncorrect,
+ );
}
}
}
err
}
- fn get_moved_indexes(&mut self, location: Location, mpi: MovePathIndex) -> Vec<MoveSite> {
+ fn get_moved_indexes(
+ &mut self,
+ location: Location,
+ mpi: MovePathIndex,
+ ) -> (Vec<MoveSite>, Vec<Location>) {
fn predecessor_locations(
body: &'a mir::Body<'tcx>,
location: Location,
}));
let mut visited = FxHashSet::default();
+ let mut move_locations = FxHashSet::default();
+ let mut reinits = vec![];
let mut result = vec![];
'dfs: while let Some((location, is_back_edge)) = stack.pop() {
move_paths[path].place
);
result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
+ move_locations.insert(location);
// Strictly speaking, we could continue our DFS here. There may be
// other moves that can reach the point of error. But it is kind of
},
);
if any_match {
+ reinits.push(location);
continue 'dfs;
}
}));
}
- result
+ // Check if we can reach these reinits from a move location.
+ let reinits_reachable = reinits
+ .into_iter()
+ .filter(|reinit| {
+ let mut visited = FxHashSet::default();
+ let mut stack = vec![*reinit];
+ while let Some(location) = stack.pop() {
+ if !visited.insert(location) {
+ continue;
+ }
+ if move_locations.contains(&location) {
+ return true;
+ }
+ stack.extend(predecessor_locations(self.body, location));
+ }
+ false
+ })
+ .collect::<Vec<Location>>();
+ (result, reinits_reachable)
}
pub(in crate::borrow_check) fn report_illegal_mutation_of_borrowed(
&mut err,
"",
None,
+ None,
);
self.explain_deref_coercion(loan, &mut err);