blob: f1aaa857dd3f3e6df354f2e84047294e16cb2856 [file] [log] [blame]
use rustc::session::config::BorrowckMode;
use rustc::ty::{self, Ty, TyCtxt};
use rustc_errors::{DiagnosticBuilder, DiagnosticId};
use syntax_pos::{MultiSpan, Span};
use std::fmt;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Origin {
Ast,
Mir,
}
impl fmt::Display for Origin {
fn fmt(&self, _w: &mut fmt::Formatter<'_>) -> fmt::Result {
// FIXME(chrisvittal) remove Origin entirely
// Print no origin info
Ok(())
}
}
impl Origin {
/// Whether we should emit errors for the origin in the given mode
pub fn should_emit_errors(self, mode: BorrowckMode) -> bool {
match self {
Origin::Ast => mode.use_ast(),
Origin::Mir => true,
}
}
}
pub trait BorrowckErrors<'cx>: Sized + Copy {
fn struct_span_err_with_code<S: Into<MultiSpan>>(
self,
sp: S,
msg: &str,
code: DiagnosticId,
) -> DiagnosticBuilder<'cx>;
fn struct_span_err<S: Into<MultiSpan>>(self, sp: S, msg: &str) -> DiagnosticBuilder<'cx>;
/// Cancels the given error if we shouldn't emit errors for a given
/// origin in the current mode.
///
/// Always make sure that the error gets passed through this function
/// before you return it.
fn cancel_if_wrong_origin(
self,
diag: DiagnosticBuilder<'cx>,
o: Origin,
) -> DiagnosticBuilder<'cx>;
fn cannot_move_when_borrowed(
self,
span: Span,
desc: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
span,
E0505,
"cannot move out of `{}` because it is borrowed{OGN}",
desc,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
fn cannot_use_when_mutably_borrowed(
self,
span: Span,
desc: &str,
borrow_span: Span,
borrow_desc: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
span,
E0503,
"cannot use `{}` because it was mutably borrowed{OGN}",
desc,
OGN = o
);
err.span_label(
borrow_span,
format!("borrow of `{}` occurs here", borrow_desc),
);
err.span_label(span, format!("use of borrowed `{}`", borrow_desc));
self.cancel_if_wrong_origin(err, o)
}
fn cannot_act_on_uninitialized_variable(
self,
span: Span,
verb: &str,
desc: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
span,
E0381,
"{} of possibly uninitialized variable: `{}`{OGN}",
verb,
desc,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
fn cannot_mutably_borrow_multiply(
self,
new_loan_span: Span,
desc: &str,
opt_via: &str,
old_loan_span: Span,
old_opt_via: &str,
old_load_end_span: Option<Span>,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let via = |msg: &str|
if msg.is_empty() { msg.to_string() } else { format!(" (via `{}`)", msg) };
let mut err = struct_span_err!(
self,
new_loan_span,
E0499,
"cannot borrow `{}`{} as mutable more than once at a time{OGN}",
desc,
via(opt_via),
OGN = o
);
if old_loan_span == new_loan_span {
// Both borrows are happening in the same place
// Meaning the borrow is occurring in a loop
err.span_label(
new_loan_span,
format!(
"mutable borrow starts here in previous \
iteration of loop{}",
opt_via
),
);
if let Some(old_load_end_span) = old_load_end_span {
err.span_label(old_load_end_span, "mutable borrow ends here");
}
} else {
err.span_label(
old_loan_span,
format!("first mutable borrow occurs here{}", via(old_opt_via)),
);
err.span_label(
new_loan_span,
format!("second mutable borrow occurs here{}", via(opt_via)),
);
if let Some(old_load_end_span) = old_load_end_span {
err.span_label(old_load_end_span, "first borrow ends here");
}
}
self.cancel_if_wrong_origin(err, o)
}
fn cannot_uniquely_borrow_by_two_closures(
self,
new_loan_span: Span,
desc: &str,
old_loan_span: Span,
old_load_end_span: Option<Span>,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
new_loan_span,
E0524,
"two closures require unique access to `{}` at the same time{OGN}",
desc,
OGN = o
);
if old_loan_span == new_loan_span {
err.span_label(
old_loan_span,
"closures are constructed here in different iterations of loop"
);
} else {
err.span_label(old_loan_span, "first closure is constructed here");
err.span_label(new_loan_span, "second closure is constructed here");
}
if let Some(old_load_end_span) = old_load_end_span {
err.span_label(old_load_end_span, "borrow from first closure ends here");
}
self.cancel_if_wrong_origin(err, o)
}
fn cannot_uniquely_borrow_by_one_closure(
self,
new_loan_span: Span,
container_name: &str,
desc_new: &str,
opt_via: &str,
old_loan_span: Span,
noun_old: &str,
old_opt_via: &str,
previous_end_span: Option<Span>,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
new_loan_span,
E0500,
"closure requires unique access to `{}` but {} is already borrowed{}{OGN}",
desc_new,
noun_old,
old_opt_via,
OGN = o
);
err.span_label(
new_loan_span,
format!("{} construction occurs here{}", container_name, opt_via),
);
err.span_label(old_loan_span, format!("borrow occurs here{}", old_opt_via));
if let Some(previous_end_span) = previous_end_span {
err.span_label(previous_end_span, "borrow ends here");
}
self.cancel_if_wrong_origin(err, o)
}
fn cannot_reborrow_already_uniquely_borrowed(
self,
new_loan_span: Span,
container_name: &str,
desc_new: &str,
opt_via: &str,
kind_new: &str,
old_loan_span: Span,
old_opt_via: &str,
previous_end_span: Option<Span>,
second_borrow_desc: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
new_loan_span,
E0501,
"cannot borrow `{}`{} as {} because previous closure \
requires unique access{OGN}",
desc_new,
opt_via,
kind_new,
OGN = o
);
err.span_label(
new_loan_span,
format!("{}borrow occurs here{}", second_borrow_desc, opt_via),
);
err.span_label(
old_loan_span,
format!("{} construction occurs here{}", container_name, old_opt_via),
);
if let Some(previous_end_span) = previous_end_span {
err.span_label(previous_end_span, "borrow from closure ends here");
}
self.cancel_if_wrong_origin(err, o)
}
fn cannot_reborrow_already_borrowed(
self,
span: Span,
desc_new: &str,
msg_new: &str,
kind_new: &str,
old_span: Span,
noun_old: &str,
kind_old: &str,
msg_old: &str,
old_load_end_span: Option<Span>,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let via = |msg: &str|
if msg.is_empty() { msg.to_string() } else { format!(" (via `{}`)", msg) };
let mut err = struct_span_err!(
self,
span,
E0502,
"cannot borrow `{}`{} as {} because {} is also borrowed \
as {}{}{OGN}",
desc_new,
via(msg_new),
kind_new,
noun_old,
kind_old,
via(msg_old),
OGN = o
);
if msg_new == "" {
// If `msg_new` is empty, then this isn't a borrow of a union field.
err.span_label(span, format!("{} borrow occurs here", kind_new));
err.span_label(old_span, format!("{} borrow occurs here", kind_old));
} else {
// If `msg_new` isn't empty, then this a borrow of a union field.
err.span_label(
span,
format!(
"{} borrow of `{}` -- which overlaps with `{}` -- occurs here",
kind_new, msg_new, msg_old,
)
);
err.span_label(
old_span,
format!("{} borrow occurs here{}", kind_old, via(msg_old)),
);
}
if let Some(old_load_end_span) = old_load_end_span {
err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old));
}
self.cancel_if_wrong_origin(err, o)
}
fn cannot_assign_to_borrowed(
self,
span: Span,
borrow_span: Span,
desc: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
span,
E0506,
"cannot assign to `{}` because it is borrowed{OGN}",
desc,
OGN = o
);
err.span_label(borrow_span, format!("borrow of `{}` occurs here", desc));
err.span_label(
span,
format!("assignment to borrowed `{}` occurs here", desc),
);
self.cancel_if_wrong_origin(err, o)
}
fn cannot_move_into_closure(self, span: Span, desc: &str, o: Origin) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
span,
E0504,
"cannot move `{}` into closure because it is borrowed{OGN}",
desc,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
fn cannot_reassign_immutable(
self,
span: Span,
desc: &str,
is_arg: bool,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let msg = if is_arg {
"to immutable argument"
} else {
"twice to immutable variable"
};
let err = struct_span_err!(
self,
span,
E0384,
"cannot assign {} `{}`{OGN}",
msg,
desc,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
fn cannot_assign(self, span: Span, desc: &str, o: Origin) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(self, span, E0594, "cannot assign to {}{OGN}", desc, OGN = o);
self.cancel_if_wrong_origin(err, o)
}
fn cannot_assign_static(self, span: Span, desc: &str, o: Origin) -> DiagnosticBuilder<'cx> {
self.cannot_assign(span, &format!("immutable static item `{}`", desc), o)
}
fn cannot_move_out_of(
self,
move_from_span: Span,
move_from_desc: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
move_from_span,
E0507,
"cannot move out of {}{OGN}",
move_from_desc,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
/// Signal an error due to an attempt to move out of the interior
/// of an array or slice. `is_index` is None when error origin
/// didn't capture whether there was an indexing operation or not.
fn cannot_move_out_of_interior_noncopy(
self,
move_from_span: Span,
ty: Ty<'_>,
is_index: Option<bool>,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let type_name = match (&ty.sty, is_index) {
(&ty::Array(_, _), Some(true)) | (&ty::Array(_, _), None) => "array",
(&ty::Slice(_), _) => "slice",
_ => span_bug!(move_from_span, "this path should not cause illegal move"),
};
let mut err = struct_span_err!(
self,
move_from_span,
E0508,
"cannot move out of type `{}`, a non-copy {}{OGN}",
ty,
type_name,
OGN = o
);
err.span_label(move_from_span, "cannot move out of here");
self.cancel_if_wrong_origin(err, o)
}
fn cannot_move_out_of_interior_of_drop(
self,
move_from_span: Span,
container_ty: Ty<'_>,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
move_from_span,
E0509,
"cannot move out of type `{}`, which implements the `Drop` trait{OGN}",
container_ty,
OGN = o
);
err.span_label(move_from_span, "cannot move out of here");
self.cancel_if_wrong_origin(err, o)
}
fn cannot_act_on_moved_value(
self,
use_span: Span,
verb: &str,
optional_adverb_for_moved: &str,
moved_path: Option<String>,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let moved_path = moved_path
.map(|mp| format!(": `{}`", mp))
.unwrap_or_default();
let err = struct_span_err!(
self,
use_span,
E0382,
"{} of {}moved value{}{OGN}",
verb,
optional_adverb_for_moved,
moved_path,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
fn cannot_partially_reinit_an_uninit_struct(
self,
span: Span,
uninit_path: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
span,
E0383,
"partial reinitialization of uninitialized structure `{}`{OGN}",
uninit_path,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
fn closure_cannot_assign_to_borrowed(
self,
span: Span,
descr: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
span,
E0595,
"closure cannot assign to {}{OGN}",
descr,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
fn cannot_borrow_path_as_mutable_because(
self,
span: Span,
path: &str,
reason: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
span,
E0596,
"cannot borrow {} as mutable{}{OGN}",
path,
reason,
OGN = o,
);
self.cancel_if_wrong_origin(err, o)
}
fn cannot_borrow_path_as_mutable(
self,
span: Span,
path: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
self.cannot_borrow_path_as_mutable_because(span, path, "", o)
}
fn cannot_mutate_in_match_guard(
self,
mutate_span: Span,
match_span: Span,
match_place: &str,
action: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
mutate_span,
E0510,
"cannot {} `{}` in match guard{OGN}",
action,
match_place,
OGN = o
);
err.span_label(mutate_span, format!("cannot {}", action));
err.span_label(match_span, String::from("value is immutable in match guard"));
self.cancel_if_wrong_origin(err, o)
}
fn cannot_borrow_across_generator_yield(
self,
span: Span,
yield_span: Span,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
span,
E0626,
"borrow may still be in use when generator yields{OGN}",
OGN = o
);
err.span_label(yield_span, "possible yield occurs here");
self.cancel_if_wrong_origin(err, o)
}
fn cannot_borrow_across_destructor(
self,
borrow_span: Span,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
borrow_span,
E0713,
"borrow may still be in use when destructor runs{OGN}",
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
fn path_does_not_live_long_enough(
self,
span: Span,
path: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
span,
E0597,
"{} does not live long enough{OGN}",
path,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
fn cannot_return_reference_to_local(
self,
span: Span,
return_kind: &str,
reference_desc: &str,
path_desc: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
span,
E0515,
"cannot {RETURN} {REFERENCE} {LOCAL}{OGN}",
RETURN=return_kind,
REFERENCE=reference_desc,
LOCAL=path_desc,
OGN = o
);
err.span_label(
span,
format!("{}s a {} data owned by the current function", return_kind, reference_desc),
);
self.cancel_if_wrong_origin(err, o)
}
fn lifetime_too_short_for_reborrow(
self,
span: Span,
path: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
span,
E0598,
"lifetime of {} is too short to guarantee \
its contents can be safely reborrowed{OGN}",
path,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
fn cannot_act_on_capture_in_sharable_fn(
self,
span: Span,
bad_thing: &str,
help: (Span, &str),
o: Origin,
) -> DiagnosticBuilder<'cx> {
let (help_span, help_msg) = help;
let mut err = struct_span_err!(
self,
span,
E0387,
"{} in a captured outer variable in an `Fn` closure{OGN}",
bad_thing,
OGN = o
);
err.span_help(help_span, help_msg);
self.cancel_if_wrong_origin(err, o)
}
fn cannot_assign_into_immutable_reference(
self,
span: Span,
bad_thing: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
span,
E0389,
"{} in a `&` reference{OGN}",
bad_thing,
OGN = o
);
err.span_label(span, "assignment into an immutable reference");
self.cancel_if_wrong_origin(err, o)
}
fn cannot_capture_in_long_lived_closure(
self,
closure_span: Span,
borrowed_path: &str,
capture_span: Span,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
closure_span,
E0373,
"closure may outlive the current function, \
but it borrows {}, \
which is owned by the current function{OGN}",
borrowed_path,
OGN = o
);
err.span_label(capture_span, format!("{} is borrowed here", borrowed_path))
.span_label(
closure_span,
format!("may outlive borrowed value {}", borrowed_path),
);
self.cancel_if_wrong_origin(err, o)
}
fn borrowed_data_escapes_closure(
self,
escape_span: Span,
escapes_from: &str,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
escape_span,
E0521,
"borrowed data escapes outside of {}{OGN}",
escapes_from,
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
fn thread_local_value_does_not_live_long_enough(
self,
span: Span,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
span,
E0712,
"thread-local variable borrowed past end of function{OGN}",
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
fn temporary_value_borrowed_for_too_long(
self,
span: Span,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
span,
E0716,
"temporary value dropped while borrowed{OGN}",
OGN = o
);
self.cancel_if_wrong_origin(err, o)
}
}
impl BorrowckErrors<'tcx> for TyCtxt<'tcx> {
fn struct_span_err_with_code<S: Into<MultiSpan>>(
self,
sp: S,
msg: &str,
code: DiagnosticId,
) -> DiagnosticBuilder<'tcx> {
self.sess.struct_span_err_with_code(sp, msg, code)
}
fn struct_span_err<S: Into<MultiSpan>>(self, sp: S, msg: &str) -> DiagnosticBuilder<'tcx> {
self.sess.struct_span_err(sp, msg)
}
fn cancel_if_wrong_origin(
self,
mut diag: DiagnosticBuilder<'tcx>,
o: Origin,
) -> DiagnosticBuilder<'tcx> {
if !o.should_emit_errors(self.borrowck_mode()) {
self.sess.diagnostic().cancel(&mut diag);
}
diag
}
}