|
1 | 1 | //! Borrow checker diagnostics.
|
2 | 2 |
|
3 |
| -use rustc_const_eval::util::call_kind; |
4 |
| -use rustc_errors::Diagnostic; |
| 3 | +use rustc_const_eval::util::{call_kind, CallDesugaringKind}; |
| 4 | +use rustc_errors::{Applicability, Diagnostic}; |
5 | 5 | use rustc_hir as hir;
|
6 | 6 | use rustc_hir::def::Namespace;
|
7 | 7 | use rustc_hir::def_id::DefId;
|
8 | 8 | use rustc_hir::GeneratorKind;
|
| 9 | +use rustc_infer::infer::TyCtxtInferExt; |
9 | 10 | use rustc_middle::mir::{
|
10 | 11 | AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand,
|
11 | 12 | Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
|
12 | 13 | };
|
13 | 14 | use rustc_middle::ty::print::Print;
|
14 | 15 | use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt};
|
15 | 16 | use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
|
16 |
| -use rustc_span::{symbol::sym, Span}; |
| 17 | +use rustc_span::{symbol::sym, Span, DUMMY_SP}; |
17 | 18 | use rustc_target::abi::VariantIdx;
|
| 19 | +use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; |
18 | 20 |
|
19 | 21 | use super::borrow_set::BorrowData;
|
20 | 22 | use super::MirBorrowckCtxt;
|
@@ -482,9 +484,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
482 | 484 | BorrowedContentSource::DerefSharedRef
|
483 | 485 | }
|
484 | 486 | }
|
485 |
| -} |
486 | 487 |
|
487 |
| -impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { |
488 | 488 | /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
|
489 | 489 | /// name where required.
|
490 | 490 | pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
|
@@ -995,4 +995,173 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
995 | 995 | let span = self.body.source_info(borrow.reserve_location).span;
|
996 | 996 | self.borrow_spans(span, borrow.reserve_location)
|
997 | 997 | }
|
| 998 | + |
| 999 | + fn explain_captures( |
| 1000 | + &mut self, |
| 1001 | + err: &mut Diagnostic, |
| 1002 | + span: Span, |
| 1003 | + move_span: Span, |
| 1004 | + move_spans: UseSpans<'tcx>, |
| 1005 | + moved_place: Place<'tcx>, |
| 1006 | + used_place: Option<PlaceRef<'tcx>>, |
| 1007 | + partially_str: &str, |
| 1008 | + loop_message: &str, |
| 1009 | + move_msg: &str, |
| 1010 | + is_loop_move: bool, |
| 1011 | + maybe_reinitialized_locations_is_empty: bool, |
| 1012 | + ) { |
| 1013 | + if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans { |
| 1014 | + let place_name = self |
| 1015 | + .describe_place(moved_place.as_ref()) |
| 1016 | + .map(|n| format!("`{}`", n)) |
| 1017 | + .unwrap_or_else(|| "value".to_owned()); |
| 1018 | + match kind { |
| 1019 | + CallKind::FnCall { fn_trait_id, .. } |
| 1020 | + if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() => |
| 1021 | + { |
| 1022 | + err.span_label( |
| 1023 | + fn_call_span, |
| 1024 | + &format!( |
| 1025 | + "{} {}moved due to this call{}", |
| 1026 | + place_name, partially_str, loop_message |
| 1027 | + ), |
| 1028 | + ); |
| 1029 | + err.span_note( |
| 1030 | + var_span, |
| 1031 | + "this value implements `FnOnce`, which causes it to be moved when called", |
| 1032 | + ); |
| 1033 | + } |
| 1034 | + CallKind::Operator { self_arg, .. } => { |
| 1035 | + let self_arg = self_arg.unwrap(); |
| 1036 | + err.span_label( |
| 1037 | + fn_call_span, |
| 1038 | + &format!( |
| 1039 | + "{} {}moved due to usage in operator{}", |
| 1040 | + place_name, partially_str, loop_message |
| 1041 | + ), |
| 1042 | + ); |
| 1043 | + if self.fn_self_span_reported.insert(fn_span) { |
| 1044 | + err.span_note( |
| 1045 | + // Check whether the source is accessible |
| 1046 | + if self |
| 1047 | + .infcx |
| 1048 | + .tcx |
| 1049 | + .sess |
| 1050 | + .source_map() |
| 1051 | + .span_to_snippet(self_arg.span) |
| 1052 | + .is_ok() |
| 1053 | + { |
| 1054 | + self_arg.span |
| 1055 | + } else { |
| 1056 | + fn_call_span |
| 1057 | + }, |
| 1058 | + "calling this operator moves the left-hand side", |
| 1059 | + ); |
| 1060 | + } |
| 1061 | + } |
| 1062 | + CallKind::Normal { self_arg, desugaring, is_option_or_result } => { |
| 1063 | + let self_arg = self_arg.unwrap(); |
| 1064 | + if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring { |
| 1065 | + let ty = moved_place.ty(self.body, self.infcx.tcx).ty; |
| 1066 | + let suggest = match self.infcx.tcx.get_diagnostic_item(sym::IntoIterator) { |
| 1067 | + Some(def_id) => self.infcx.tcx.infer_ctxt().enter(|infcx| { |
| 1068 | + type_known_to_meet_bound_modulo_regions( |
| 1069 | + &infcx, |
| 1070 | + self.param_env, |
| 1071 | + infcx.tcx.mk_imm_ref( |
| 1072 | + infcx.tcx.lifetimes.re_erased, |
| 1073 | + infcx.tcx.erase_regions(ty), |
| 1074 | + ), |
| 1075 | + def_id, |
| 1076 | + DUMMY_SP, |
| 1077 | + ) |
| 1078 | + }), |
| 1079 | + _ => false, |
| 1080 | + }; |
| 1081 | + if suggest { |
| 1082 | + err.span_suggestion_verbose( |
| 1083 | + move_span.shrink_to_lo(), |
| 1084 | + &format!( |
| 1085 | + "consider iterating over a slice of the `{}`'s content to \ |
| 1086 | + avoid moving into the `for` loop", |
| 1087 | + ty, |
| 1088 | + ), |
| 1089 | + "&".to_string(), |
| 1090 | + Applicability::MaybeIncorrect, |
| 1091 | + ); |
| 1092 | + } |
| 1093 | + |
| 1094 | + err.span_label( |
| 1095 | + fn_call_span, |
| 1096 | + &format!( |
| 1097 | + "{} {}moved due to this implicit call to `.into_iter()`{}", |
| 1098 | + place_name, partially_str, loop_message |
| 1099 | + ), |
| 1100 | + ); |
| 1101 | + // If we have a `&mut` ref, we need to reborrow. |
| 1102 | + if let Some(ty::Ref(_, _, hir::Mutability::Mut)) = used_place |
| 1103 | + .map(|used_place| used_place.ty(self.body, self.infcx.tcx).ty.kind()) |
| 1104 | + { |
| 1105 | + // If we are in a loop this will be suggested later. |
| 1106 | + if !is_loop_move { |
| 1107 | + err.span_suggestion_verbose( |
| 1108 | + move_span.shrink_to_lo(), |
| 1109 | + &format!( |
| 1110 | + "consider creating a fresh reborrow of {} here", |
| 1111 | + self.describe_place(moved_place.as_ref()) |
| 1112 | + .map(|n| format!("`{}`", n)) |
| 1113 | + .unwrap_or_else(|| "the mutable reference".to_string()), |
| 1114 | + ), |
| 1115 | + "&mut *".to_string(), |
| 1116 | + Applicability::MachineApplicable, |
| 1117 | + ); |
| 1118 | + } |
| 1119 | + } |
| 1120 | + } else { |
| 1121 | + err.span_label( |
| 1122 | + fn_call_span, |
| 1123 | + &format!( |
| 1124 | + "{} {}moved due to this method call{}", |
| 1125 | + place_name, partially_str, loop_message |
| 1126 | + ), |
| 1127 | + ); |
| 1128 | + } |
| 1129 | + if is_option_or_result && maybe_reinitialized_locations_is_empty { |
| 1130 | + err.span_suggestion_verbose( |
| 1131 | + fn_call_span.shrink_to_lo(), |
| 1132 | + "consider calling `.as_ref()` to borrow the type's contents", |
| 1133 | + "as_ref().".to_string(), |
| 1134 | + Applicability::MachineApplicable, |
| 1135 | + ); |
| 1136 | + } |
| 1137 | + // Avoid pointing to the same function in multiple different |
| 1138 | + // error messages. |
| 1139 | + if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) { |
| 1140 | + err.span_note( |
| 1141 | + self_arg.span, |
| 1142 | + &format!("this function takes ownership of the receiver `self`, which moves {}", place_name) |
| 1143 | + ); |
| 1144 | + } |
| 1145 | + } |
| 1146 | + // Other desugarings takes &self, which cannot cause a move |
| 1147 | + _ => {} |
| 1148 | + } |
| 1149 | + } else { |
| 1150 | + if move_span != span || !loop_message.is_empty() { |
| 1151 | + err.span_label( |
| 1152 | + move_span, |
| 1153 | + format!("value {}moved{} here{}", partially_str, move_msg, loop_message), |
| 1154 | + ); |
| 1155 | + } |
| 1156 | + // If the move error occurs due to a loop, don't show |
| 1157 | + // another message for the same span |
| 1158 | + if loop_message.is_empty() { |
| 1159 | + move_spans.var_span_label( |
| 1160 | + err, |
| 1161 | + format!("variable {}moved due to use{}", partially_str, move_spans.describe()), |
| 1162 | + "moved", |
| 1163 | + ); |
| 1164 | + } |
| 1165 | + } |
| 1166 | + } |
998 | 1167 | }
|
0 commit comments