Auto merge of #32756 - nikomatsakis:borrowck-snippet, r=nrc

Overhaul borrowck error messages and compiler error formatting generally

This is a major overhaul of how the compiler reports errors. The primary goal is to be able to give many spans within the same overall context, such as this:

```
./borrow-errors.rs:73:17: 73:20: error: cannot borrow `*vec` as immutable because previous closure requires unique access [E0501]
70     let append = |e| {
                    ~~~ closure construction occurs here
71         vec.push(e)
           ~~~ previous borrow occurs due to use of `vec` in closure
72     };
73     let data = &vec[3];
                   ~~~ borrow occurs here
74 }
   ~ borrow from closure ends here
```

However, in the process we made a number of other changes:

- Removed the repetitive filenames from snippets and just give the line number.
- Color the line numbers blue so they "fade away"
- Remove the file name and line number from the error code suggestions since they don't seem to fit anymore. (This should probably happen in more places, like existing notes.)
- Newlines in between errors to help group them better.

This PR is not quite ready to land, but we thought it made sense to stop here and get some feedback from people at large. It'd be great if people can check out the branch and play with it. We'd be especially interested in hearing about cases that don't look good with the new formatting (I suspect they exist).

Here is a checklist of some pending work items for this PR. Some of them may be best left for follow-up PRs:

- [x] Accommodate multiple files in a `MultiSpan` (this should be easy)
  - In this case, we want to print filenames though.
- [x] Remove duplicate E0500 code.
- [x] Make the header message bold, rather than current hack that makes all errors/warnings bold
- [x] Update warning text color (yellow is hard to read w/ a white background)

Moved numerous follow-ups to: https://github.com/rust-lang/rust/issues/33240

Joint work with @jonathandturner.

Fixes https://github.com/rust-lang/rust/issues/3533
This commit is contained in:
bors 2016-05-02 19:21:56 -07:00
commit 44b3cd8c46
169 changed files with 2963 additions and 2208 deletions

View File

@ -1569,5 +1569,5 @@ register_diagnostics! {
E0490, // a value of type `..` is borrowed for too long
E0491, // in type `..`, reference has a longer lifetime than the data it...
E0495, // cannot infer an appropriate lifetime due to conflicting requirements
E0524, // expected a closure that implements `..` but this closure only implements `..`
E0525, // expected a closure that implements `..` but this closure only implements `..`
}

View File

@ -249,12 +249,12 @@ pub trait ErrorReporting<'tcx> {
terr: &TypeError<'tcx>)
-> DiagnosticBuilder<'tcx>;
fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<String>;
fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<(String, String)>;
fn expected_found_str<T: fmt::Display + Resolvable<'tcx> + TypeFoldable<'tcx>>(
&self,
exp_found: &ty::error::ExpectedFound<T>)
-> Option<String>;
-> Option<(String, String)>;
fn report_concrete_failure(&self,
origin: SubregionOrigin<'tcx>,
@ -535,7 +535,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
trace: TypeTrace<'tcx>,
terr: &TypeError<'tcx>)
-> DiagnosticBuilder<'tcx> {
let expected_found_str = match self.values_str(&trace.values) {
let (expected, found) = match self.values_str(&trace.values) {
Some(v) => v,
None => {
return self.tcx.sess.diagnostic().struct_dummy(); /* derived error */
@ -548,18 +548,17 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
false
};
let expected_found_str = if is_simple_error {
expected_found_str
} else {
format!("{} ({})", expected_found_str, terr)
};
let mut err = struct_span_err!(self.tcx.sess,
trace.origin.span(),
E0308,
"{}: {}",
trace.origin,
expected_found_str);
"{}",
trace.origin);
if !is_simple_error {
err = err.note_expected_found(&"type", &expected, &found);
}
err = err.span_label(trace.origin.span(), &terr);
self.check_and_note_conflicting_crates(&mut err, terr, trace.origin.span());
@ -574,6 +573,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
},
_ => ()
}
err
}
@ -631,7 +631,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
/// Returns a string of the form "expected `{}`, found `{}`", or None if this is a derived
/// error.
fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<String> {
fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<(String, String)> {
match *values {
infer::Types(ref exp_found) => self.expected_found_str(exp_found),
infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found),
@ -642,7 +642,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
fn expected_found_str<T: fmt::Display + Resolvable<'tcx> + TypeFoldable<'tcx>>(
&self,
exp_found: &ty::error::ExpectedFound<T>)
-> Option<String>
-> Option<(String, String)>
{
let expected = exp_found.expected.resolve(self);
if expected.references_error() {
@ -654,9 +654,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
return None;
}
Some(format!("expected `{}`, found `{}`",
expected,
found))
Some((format!("{}", expected), format!("{}", found)))
}
fn report_generic_bound_failure(&self,
@ -684,10 +682,9 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
E0309,
"{} may not live long enough",
labeled_user_string);
err.fileline_help(origin.span(),
&format!("consider adding an explicit lifetime bound `{}: {}`...",
bound_kind,
sub));
err.help(&format!("consider adding an explicit lifetime bound `{}: {}`...",
bound_kind,
sub));
err
}
@ -698,10 +695,9 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
E0310,
"{} may not live long enough",
labeled_user_string);
err.fileline_help(origin.span(),
&format!("consider adding an explicit lifetime \
bound `{}: 'static`...",
bound_kind));
err.help(&format!("consider adding an explicit lifetime \
bound `{}: 'static`...",
bound_kind));
err
}
@ -712,9 +708,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
E0311,
"{} may not live long enough",
labeled_user_string);
err.fileline_help(origin.span(),
&format!("consider adding an explicit lifetime bound for `{}`",
bound_kind));
err.help(&format!("consider adding an explicit lifetime bound for `{}`",
bound_kind));
self.tcx.note_and_explain_region(
&mut err,
&format!("{} must be valid for ", labeled_user_string),
@ -1751,11 +1746,11 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
};
match self.values_str(&trace.values) {
Some(values_str) => {
Some((expected, found)) => {
err.span_note(
trace.origin.span(),
&format!("...so that {} ({})",
desc, values_str));
&format!("...so that {} (expected {}, found {})",
desc, expected, found));
}
None => {
// Really should avoid printing this error at

View File

@ -456,17 +456,13 @@ pub fn raw_struct_lint<'a>(sess: &'a Session,
it will become a hard error in a future release!");
let citation = format!("for more information, see {}",
future_incompatible.reference);
if let Some(sp) = span {
err.fileline_warn(sp, &explanation);
err.fileline_note(sp, &citation);
} else {
err.warn(&explanation);
err.note(&citation);
}
err.warn(&explanation);
err.note(&citation);
}
if let Some(span) = def {
err.span_note(span, "lint level defined here");
let explanation = "lint level defined here";
err.span_note(span, &explanation);
}
err
@ -542,7 +538,7 @@ pub trait LintContext: Sized {
let mut err = self.lookup(lint, Some(span), msg);
if self.current_level(lint) != Level::Allow {
if note_span == span {
err.fileline_note(note_span, note);
err.note(note);
} else {
err.span_note(note_span, note);
}

View File

@ -567,7 +567,7 @@ pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
}
config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()),
};
emitter.emit(None, msg, None, errors::Level::Fatal);
emitter.emit(&MultiSpan::new(), msg, None, errors::Level::Fatal);
panic!(errors::FatalError);
}
@ -578,7 +578,7 @@ pub fn early_warn(output: config::ErrorOutputType, msg: &str) {
}
config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()),
};
emitter.emit(None, msg, None, errors::Level::Warning);
emitter.emit(&MultiSpan::new(), msg, None, errors::Level::Warning);
}
// Err(0) means compilation was stopped, but no errors were found.

View File

@ -206,18 +206,17 @@ fn find_similar_impl_candidates<'a, 'tcx>(
impl_candidates
}
fn report_similar_impl_candidates(span: Span,
err: &mut DiagnosticBuilder,
fn report_similar_impl_candidates(err: &mut DiagnosticBuilder,
impl_candidates: &[ty::TraitRef])
{
err.fileline_help(span, &format!("the following implementations were found:"));
err.help(&format!("the following implementations were found:"));
let end = cmp::min(4, impl_candidates.len());
for candidate in &impl_candidates[0..end] {
err.fileline_help(span, &format!(" {:?}", candidate));
err.help(&format!(" {:?}", candidate));
}
if impl_candidates.len() > 4 {
err.fileline_help(span, &format!("and {} others", impl_candidates.len()-4));
err.help(&format!("and {} others", impl_candidates.len()-4));
}
}
@ -240,7 +239,7 @@ pub fn report_overflow_error<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
predicate);
if suggest_increasing_limit {
suggest_new_overflow_limit(infcx.tcx, &mut err, obligation.cause.span);
suggest_new_overflow_limit(infcx.tcx, &mut err);
}
note_obligation_cause(infcx, &mut err, obligation);
@ -353,19 +352,15 @@ pub fn try_report_overflow_error_type_of_infinite_size<'a, 'tcx>(
let mut err = recursive_type_with_infinite_size_error(tcx, main_def_id);
let len = struct_enum_tys.len();
if len > 2 {
let span = tcx.map.span_if_local(main_def_id).unwrap();
err.fileline_note(span,
&format!("type `{}` is embedded within `{}`...",
struct_enum_tys[0],
struct_enum_tys[1]));
err.note(&format!("type `{}` is embedded within `{}`...",
struct_enum_tys[0],
struct_enum_tys[1]));
for &next_ty in &struct_enum_tys[1..len-1] {
err.fileline_note(span,
&format!("...which in turn is embedded within `{}`...", next_ty));
err.note(&format!("...which in turn is embedded within `{}`...", next_ty));
}
err.fileline_note(span,
&format!("...which in turn is embedded within `{}`, \
completing the cycle.",
struct_enum_tys[len-1]));
err.note(&format!("...which in turn is embedded within `{}`, \
completing the cycle.",
struct_enum_tys[len-1]));
}
err.emit();
infcx.tcx.sess.abort_if_errors();
@ -380,9 +375,9 @@ pub fn recursive_type_with_infinite_size_error<'tcx>(tcx: &TyCtxt<'tcx>,
let span = tcx.map.span_if_local(type_def_id).unwrap();
let mut err = struct_span_err!(tcx.sess, span, E0072, "recursive type `{}` has infinite size",
tcx.item_path_str(type_def_id));
err.fileline_help(span, &format!("insert indirection (e.g., a `Box`, `Rc`, or `&`) \
at some point to make `{}` representable",
tcx.item_path_str(type_def_id)));
err.help(&format!("insert indirection (e.g., a `Box`, `Rc`, or `&`) \
at some point to make `{}` representable",
tcx.item_path_str(type_def_id)));
err
}
@ -423,15 +418,14 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
// these notes will often be of the form
// "the type `T` can't be frobnicated"
// which is somewhat confusing.
err.fileline_help(obligation.cause.span, &format!(
"consider adding a `where {}` bound",
err.help(&format!("consider adding a `where {}` bound",
trait_ref.to_predicate()
));
} else if let Some(s) = on_unimplemented_note(infcx, trait_ref,
obligation.cause.span) {
// Otherwise, if there is an on-unimplemented note,
// display it.
err.fileline_note(obligation.cause.span, &s);
err.note(&s);
} else {
// If we can't show anything useful, try to find
// similar impls.
@ -439,8 +433,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
let impl_candidates =
find_similar_impl_candidates(infcx, trait_ref);
if impl_candidates.len() > 0 {
report_similar_impl_candidates(obligation.cause.span,
&mut err, &impl_candidates);
report_similar_impl_candidates(&mut err, &impl_candidates);
}
}
note_obligation_cause(infcx, &mut err, obligation);
@ -499,7 +492,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
let found_kind = infcx.closure_kind(closure_def_id).unwrap();
let closure_span = infcx.tcx.map.span_if_local(closure_def_id).unwrap();
let mut err = struct_span_err!(
infcx.tcx.sess, closure_span, E0524,
infcx.tcx.sess, closure_span, E0525,
"expected a closure that implements the `{}` trait, but this closure \
only implements `{}`",
kind,
@ -570,41 +563,31 @@ pub fn report_object_safety_error<'tcx>(tcx: &TyCtxt<'tcx>,
}
match violation {
ObjectSafetyViolation::SizedSelf => {
err.fileline_note(
span,
"the trait cannot require that `Self : Sized`");
err.note("the trait cannot require that `Self : Sized`");
}
ObjectSafetyViolation::SupertraitSelf => {
err.fileline_note(
span,
"the trait cannot use `Self` as a type parameter \
in the supertrait listing");
err.note("the trait cannot use `Self` as a type parameter \
in the supertrait listing");
}
ObjectSafetyViolation::Method(method,
MethodViolationCode::StaticMethod) => {
err.fileline_note(
span,
&format!("method `{}` has no receiver",
method.name));
err.note(&format!("method `{}` has no receiver",
method.name));
}
ObjectSafetyViolation::Method(method,
MethodViolationCode::ReferencesSelf) => {
err.fileline_note(
span,
&format!("method `{}` references the `Self` type \
in its arguments or return type",
method.name));
err.note(&format!("method `{}` references the `Self` type \
in its arguments or return type",
method.name));
}
ObjectSafetyViolation::Method(method,
MethodViolationCode::Generic) => {
err.fileline_note(
span,
&format!("method `{}` has generic type parameters",
method.name));
err.note(&format!("method `{}` has generic type parameters",
method.name));
}
}
}
@ -766,14 +749,12 @@ fn note_obligation_cause<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
note_obligation_cause_code(infcx,
err,
&obligation.predicate,
obligation.cause.span,
&obligation.cause.code);
}
fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
err: &mut DiagnosticBuilder,
predicate: &T,
cause_span: Span,
cause_code: &ObligationCauseCode<'tcx>)
where T: fmt::Display
{
@ -781,101 +762,71 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
match *cause_code {
ObligationCauseCode::MiscObligation => { }
ObligationCauseCode::SliceOrArrayElem => {
err.fileline_note(
cause_span,
"slice and array elements must have `Sized` type");
err.note("slice and array elements must have `Sized` type");
}
ObligationCauseCode::ProjectionWf(data) => {
err.fileline_note(
cause_span,
&format!("required so that the projection `{}` is well-formed",
data));
err.note(&format!("required so that the projection `{}` is well-formed",
data));
}
ObligationCauseCode::ReferenceOutlivesReferent(ref_ty) => {
err.fileline_note(
cause_span,
&format!("required so that reference `{}` does not outlive its referent",
ref_ty));
err.note(&format!("required so that reference `{}` does not outlive its referent",
ref_ty));
}
ObligationCauseCode::ItemObligation(item_def_id) => {
let item_name = tcx.item_path_str(item_def_id);
err.fileline_note(
cause_span,
&format!("required by `{}`", item_name));
err.note(&format!("required by `{}`", item_name));
}
ObligationCauseCode::ObjectCastObligation(object_ty) => {
err.fileline_note(
cause_span,
&format!(
"required for the cast to the object type `{}`",
infcx.ty_to_string(object_ty)));
err.note(&format!("required for the cast to the object type `{}`",
infcx.ty_to_string(object_ty)));
}
ObligationCauseCode::RepeatVec => {
err.fileline_note(
cause_span,
"the `Copy` trait is required because the \
repeated element will be copied");
err.note("the `Copy` trait is required because the \
repeated element will be copied");
}
ObligationCauseCode::VariableType(_) => {
err.fileline_note(
cause_span,
"all local variables must have a statically known size");
err.note("all local variables must have a statically known size");
}
ObligationCauseCode::ReturnType => {
err.fileline_note(
cause_span,
"the return type of a function must have a \
statically known size");
err.note("the return type of a function must have a \
statically known size");
}
ObligationCauseCode::AssignmentLhsSized => {
err.fileline_note(
cause_span,
"the left-hand-side of an assignment must have a statically known size");
err.note("the left-hand-side of an assignment must have a statically known size");
}
ObligationCauseCode::StructInitializerSized => {
err.fileline_note(
cause_span,
"structs must have a statically known size to be initialized");
err.note("structs must have a statically known size to be initialized");
}
ObligationCauseCode::ClosureCapture(var_id, _, builtin_bound) => {
let def_id = tcx.lang_items.from_builtin_kind(builtin_bound).unwrap();
let trait_name = tcx.item_path_str(def_id);
let name = tcx.local_var_name_str(var_id);
err.fileline_note(
cause_span,
err.note(
&format!("the closure that captures `{}` requires that all captured variables \
implement the trait `{}`",
name,
trait_name));
}
ObligationCauseCode::FieldSized => {
err.fileline_note(
cause_span,
"only the last field of a struct or enum variant \
may have a dynamically sized type");
err.note("only the last field of a struct or enum variant \
may have a dynamically sized type");
}
ObligationCauseCode::SharedStatic => {
err.fileline_note(
cause_span,
"shared static variables must have a type that implements `Sync`");
err.note("shared static variables must have a type that implements `Sync`");
}
ObligationCauseCode::BuiltinDerivedObligation(ref data) => {
let parent_trait_ref = infcx.resolve_type_vars_if_possible(&data.parent_trait_ref);
err.fileline_note(
cause_span,
&format!("required because it appears within the type `{}`",
parent_trait_ref.0.self_ty()));
err.note(&format!("required because it appears within the type `{}`",
parent_trait_ref.0.self_ty()));
let parent_predicate = parent_trait_ref.to_predicate();
note_obligation_cause_code(infcx,
err,
&parent_predicate,
cause_span,
&data.parent_code);
}
ObligationCauseCode::ImplDerivedObligation(ref data) => {
let parent_trait_ref = infcx.resolve_type_vars_if_possible(&data.parent_trait_ref);
err.fileline_note(
cause_span,
err.note(
&format!("required because of the requirements on the impl of `{}` for `{}`",
parent_trait_ref,
parent_trait_ref.0.self_ty()));
@ -883,12 +834,10 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
note_obligation_cause_code(infcx,
err,
&parent_predicate,
cause_span,
&data.parent_code);
}
ObligationCauseCode::CompareImplMethodObligation => {
err.fileline_note(
cause_span,
err.note(
&format!("the requirement `{}` appears on the impl method \
but not on the corresponding trait method",
predicate));
@ -896,12 +845,10 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
}
}
fn suggest_new_overflow_limit(tcx: &TyCtxt, err:&mut DiagnosticBuilder, span: Span) {
fn suggest_new_overflow_limit(tcx: &TyCtxt, err:&mut DiagnosticBuilder) {
let current_limit = tcx.sess.recursion_limit.get();
let suggested_limit = current_limit * 2;
err.fileline_note(
span,
&format!(
"consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
suggested_limit));
err.note(&format!(
"consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
suggested_limit));
}

View File

@ -447,22 +447,24 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
// borrow ends
let common = new_loan.loan_path.common(&old_loan.loan_path);
let (nl, ol, new_loan_msg, old_loan_msg) =
let (nl, ol, new_loan_msg, old_loan_msg) = {
if new_loan.loan_path.has_fork(&old_loan.loan_path) && common.is_some() {
let nl = self.bccx.loan_path_to_string(&common.unwrap());
let ol = nl.clone();
let new_loan_msg = format!(" (here through borrowing `{}`)",
let new_loan_msg = format!(" (via `{}`)",
self.bccx.loan_path_to_string(
&new_loan.loan_path));
let old_loan_msg = format!(" (through borrowing `{}`)",
let old_loan_msg = format!(" (via `{}`)",
self.bccx.loan_path_to_string(
&old_loan.loan_path));
(nl, ol, new_loan_msg, old_loan_msg)
} else {
(self.bccx.loan_path_to_string(&new_loan.loan_path),
self.bccx.loan_path_to_string(&old_loan.loan_path),
String::new(), String::new())
};
String::new(),
String::new())
}
};
let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
"it".to_string()
@ -470,12 +472,48 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
format!("`{}`", ol)
};
// We want to assemble all the relevant locations for the error.
//
// 1. Where did the new loan occur.
// - if due to closure creation, where was the variable used in closure?
// 2. Where did old loan occur.
// 3. Where does old loan expire.
let previous_end_span =
self.tcx().map.span(old_loan.kill_scope.node_id(&self.tcx().region_maps))
.end_point();
let mut err = match (new_loan.kind, old_loan.kind) {
(ty::MutBorrow, ty::MutBorrow) => {
struct_span_err!(self.bccx, new_loan.span, E0499,
"cannot borrow `{}`{} as mutable \
more than once at a time",
nl, new_loan_msg)
.span_label(
old_loan.span,
&format!("first mutable borrow occurs here{}", old_loan_msg))
.span_label(
new_loan.span,
&format!("second mutable borrow occurs here{}", new_loan_msg))
.span_label(
previous_end_span,
&format!("first borrow ends here"))
}
(ty::UniqueImmBorrow, ty::UniqueImmBorrow) => {
struct_span_err!(self.bccx, new_loan.span, E0524,
"two closures require unique access to `{}` \
at the same time",
nl)
.span_label(
old_loan.span,
&format!("first closure is constructed here"))
.span_label(
new_loan.span,
&format!("second closure is constructed here"))
.span_label(
previous_end_span,
&format!("borrow from first closure ends here"))
}
(ty::UniqueImmBorrow, _) => {
@ -483,6 +521,15 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
"closure requires unique access to `{}` \
but {} is already borrowed{}",
nl, ol_pronoun, old_loan_msg)
.span_label(
new_loan.span,
&format!("closure construction occurs here{}", new_loan_msg))
.span_label(
old_loan.span,
&format!("borrow occurs here{}", old_loan_msg))
.span_label(
previous_end_span,
&format!("borrow ends here"))
}
(_, ty::UniqueImmBorrow) => {
@ -490,6 +537,15 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
"cannot borrow `{}`{} as {} because \
previous closure requires unique access",
nl, new_loan_msg, new_loan.kind.to_user_str())
.span_label(
new_loan.span,
&format!("borrow occurs here{}", new_loan_msg))
.span_label(
old_loan.span,
&format!("closure construction occurs here{}", old_loan_msg))
.span_label(
previous_end_span,
&format!("borrow from closure ends here"))
}
(_, _) => {
@ -502,70 +558,42 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
ol_pronoun,
old_loan.kind.to_user_str(),
old_loan_msg)
.span_label(
new_loan.span,
&format!("{} borrow occurs here{}",
new_loan.kind.to_user_str(),
new_loan_msg))
.span_label(
old_loan.span,
&format!("{} borrow occurs here{}",
old_loan.kind.to_user_str(),
old_loan_msg))
.span_label(
previous_end_span,
&format!("{} borrow ends here",
old_loan.kind.to_user_str()))
}
};
match new_loan.cause {
euv::ClosureCapture(span) => {
err.span_note(
err = err.span_label(
span,
&format!("borrow occurs due to use of `{}` in closure",
nl));
&format!("borrow occurs due to use of `{}` in closure", nl));
}
_ => { }
}
let rule_summary = match old_loan.kind {
ty::MutBorrow => {
format!("the mutable borrow prevents subsequent \
moves, borrows, or modification of `{0}` \
until the borrow ends",
ol)
match old_loan.cause {
euv::ClosureCapture(span) => {
err = err.span_label(
span,
&format!("previous borrow occurs due to use of `{}` in closure",
ol));
}
_ => { }
}
ty::ImmBorrow => {
format!("the immutable borrow prevents subsequent \
moves or mutable borrows of `{0}` \
until the borrow ends",
ol)
}
ty::UniqueImmBorrow => {
format!("the unique capture prevents subsequent \
moves or borrows of `{0}` \
until the borrow ends",
ol)
}
};
let borrow_summary = match old_loan.cause {
euv::ClosureCapture(_) => {
format!("previous borrow of `{}` occurs here{} due to \
use in closure",
ol, old_loan_msg)
}
euv::OverloadedOperator |
euv::AddrOf |
euv::AutoRef |
euv::AutoUnsafe |
euv::ClosureInvocation |
euv::ForLoop |
euv::RefBinding |
euv::MatchDiscriminant => {
format!("previous borrow of `{}` occurs here{}",
ol, old_loan_msg)
}
};
err.span_note(
old_loan.span,
&format!("{}; {}", borrow_summary, rule_summary));
let old_loan_span = self.tcx().map.span(
old_loan.kill_scope.node_id(&self.tcx().region_maps));
err.span_end_note(old_loan_span,
"previous borrow ends here");
err.emit();
return false;
}

View File

@ -167,8 +167,7 @@ fn note_move_destination(err: &mut DiagnosticBuilder,
err.span_note(
move_to_span,
"attempting to move value to here");
err.fileline_help(
move_to_span,
err.help(
&format!("to prevent the move, \
use `ref {0}` or `ref mut {0}` to capture value by \
reference",

View File

@ -34,14 +34,14 @@ use rustc::middle::free_region::FreeRegionMap;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
use rustc::middle::region;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::{self, TyCtxt};
use std::fmt;
use std::mem;
use std::rc::Rc;
use syntax::ast;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::Span;
use syntax::codemap::{MultiSpan, Span};
use syntax::errors::DiagnosticBuilder;
use rustc::hir;
@ -633,23 +633,22 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
lp: &LoanPath<'tcx>,
the_move: &move_data::Move,
moved_lp: &LoanPath<'tcx>,
param_env: &ty::ParameterEnvironment<'b,'tcx>) {
let verb = match use_kind {
MovedInUse => "use",
MovedInCapture => "capture",
_param_env: &ty::ParameterEnvironment<'b,'tcx>) {
let (verb, verb_participle) = match use_kind {
MovedInUse => ("use", "used"),
MovedInCapture => ("capture", "captured"),
};
let (ol, moved_lp_msg, mut err) = match the_move.kind {
let (_ol, _moved_lp_msg, mut err) = match the_move.kind {
move_data::Declared => {
let err = struct_span_err!(
// If this is an uninitialized variable, just emit a simple warning
// and return.
struct_span_err!(
self.tcx.sess, use_span, E0381,
"{} of possibly uninitialized variable: `{}`",
verb,
self.loan_path_to_string(lp));
(self.loan_path_to_string(moved_lp),
String::new(),
err)
self.loan_path_to_string(lp)).emit();
return;
}
_ => {
// If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
@ -688,122 +687,52 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
self.tcx.sess, use_span, E0382,
"{} of {}moved value: `{}`",
verb, msg, nl);
(ol, moved_lp_msg, err)
}
(ol, moved_lp_msg, err)}
};
match the_move.kind {
move_data::Declared => {}
move_data::MoveExpr => {
let (expr_ty, expr_span) = match self.tcx
.map
.find(the_move.id) {
Some(hir_map::NodeExpr(expr)) => {
(self.tcx.expr_ty_adjusted(&expr), expr.span)
}
r => {
bug!("MoveExpr({}) maps to {:?}, not Expr",
the_move.id,
r)
}
};
let (suggestion, _) =
move_suggestion(param_env, expr_span, expr_ty, ("moved by default", ""));
// If the two spans are the same, it's because the expression will be evaluated
// multiple times. Avoid printing the same span and adjust the wording so it makes
// more sense that it's from multiple evalutations.
if expr_span == use_span {
err.note(
&format!("`{}` was previously moved here{} because it has type `{}`, \
which is {}",
ol,
moved_lp_msg,
expr_ty,
suggestion));
} else {
err.span_note(
expr_span,
&format!("`{}` moved here{} because it has type `{}`, which is {}",
ol,
moved_lp_msg,
expr_ty,
suggestion));
}
// Get type of value and span where it was previously
// moved.
let (move_span, move_note) = match the_move.kind {
move_data::Declared => {
unreachable!();
}
move_data::MovePat => {
let pat_ty = self.tcx.node_id_to_type(the_move.id);
let span = self.tcx.map.span(the_move.id);
err.span_note(span,
&format!("`{}` moved here{} because it has type `{}`, \
which is moved by default",
ol,
moved_lp_msg,
pat_ty));
match self.tcx.sess.codemap().span_to_snippet(span) {
Ok(string) => {
err.span_suggestion(
span,
&format!("if you would like to borrow the value instead, \
use a `ref` binding as shown:"),
format!("ref {}", string));
},
Err(_) => {
err.fileline_help(span,
"use `ref` to override");
},
}
}
move_data::MoveExpr |
move_data::MovePat =>
(self.tcx.map.span(the_move.id), ""),
move_data::Captured =>
(match self.tcx.map.expect_expr(the_move.id).node {
hir::ExprClosure(_, _, _, fn_decl_span) => fn_decl_span,
ref r => bug!("Captured({}) maps to non-closure: {:?}",
the_move.id, r),
}, " (into closure)"),
};
// Annotate the use and the move in the span. Watch out for
// the case where the use and the move are the same. This
// means the use is in a loop.
err = if use_span == move_span {
err.span_label(
use_span,
&format!("value moved{} here in previous iteration of loop",
move_note))
} else {
err.span_label(use_span, &format!("value {} here after move", verb_participle))
.span_label(move_span, &format!("value moved{} here", move_note))
};
err.note(&format!("move occurs because `{}` has type `{}`, \
which does not implement the `Copy` trait",
self.loan_path_to_string(moved_lp),
moved_lp.ty));
// Note: we used to suggest adding a `ref binding` or calling
// `clone` but those suggestions have been removed because
// they are often not what you actually want to do, and were
// not considered particularly helpful.
move_data::Captured => {
let (expr_ty, expr_span) = match self.tcx
.map
.find(the_move.id) {
Some(hir_map::NodeExpr(expr)) => {
(self.tcx.expr_ty_adjusted(&expr), expr.span)
}
r => {
bug!("Captured({}) maps to {:?}, not Expr",
the_move.id,
r)
}
};
let (suggestion, help) =
move_suggestion(param_env,
expr_span,
expr_ty,
("moved by default",
"make a copy and capture that instead to override"));
err.span_note(
expr_span,
&format!("`{}` moved into closure environment here{} because it \
has type `{}`, which is {}",
ol,
moved_lp_msg,
moved_lp.ty,
suggestion));
err.fileline_help(expr_span, help);
}
}
err.emit();
fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
span: Span,
ty: Ty<'tcx>,
default_msgs: (&'static str, &'static str))
-> (&'static str, &'static str) {
match ty.sty {
_ => {
if ty.moves_by_default(param_env, span) {
("non-copyable",
"perhaps you meant to use `clone()`?")
} else {
default_msgs
}
}
}
}
}
pub fn report_partial_reinitialization_of_uninitialized_structure(
@ -833,19 +762,20 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
self.tcx.sess.span_err(s, m);
}
pub fn struct_span_err(&self, s: Span, m: &str) -> DiagnosticBuilder<'a> {
pub fn struct_span_err<S: Into<MultiSpan>>(&self, s: S, m: &str)
-> DiagnosticBuilder<'a> {
self.tcx.sess.struct_span_err(s, m)
}
pub fn struct_span_err_with_code(&self,
s: Span,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
pub fn struct_span_err_with_code<S: Into<MultiSpan>>(&self,
s: S,
msg: &str,
code: &str)
-> DiagnosticBuilder<'a> {
self.tcx.sess.struct_span_err_with_code(s, msg, code)
}
pub fn span_err_with_code(&self, s: Span, msg: &str, code: &str) {
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, s: S, msg: &str, code: &str) {
self.tcx.sess.span_err_with_code(s, msg, code);
}
@ -982,8 +912,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
};
if is_closure {
err.fileline_help(span,
"closures behind references must be called via `&mut`");
err.help("closures behind references must be called via `&mut`");
}
err.emit();
}

View File

@ -444,4 +444,5 @@ register_diagnostics! {
E0506, // cannot assign to `..` because it is borrowed
E0508, // cannot move out of type `..`, a non-copy fixed-size array
E0509, // cannot move out of type `..`, which defines the `Drop` trait
E0524, // two closures require unique access to `..` at the same time
}

View File

@ -255,7 +255,7 @@ fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat)
"pattern binding `{}` is named the same as one \
of the variants of the type `{}`",
ident.node, ty_path);
fileline_help!(err, p.span,
help!(err,
"if you meant to match on a variant, \
consider making the path in the pattern qualified: `{}::{}`",
ty_path, ident.node);

View File

@ -92,7 +92,7 @@ use std::thread;
use rustc::session::early_error;
use syntax::{ast, errors, diagnostics};
use syntax::codemap::{CodeMap, FileLoader, RealFileLoader};
use syntax::codemap::{CodeMap, FileLoader, RealFileLoader, MultiSpan};
use syntax::errors::emitter::Emitter;
use syntax::feature_gate::{GatedCfg, UnstableFeatures};
use syntax::parse::{self, PResult, token};
@ -136,7 +136,8 @@ pub fn run(args: Vec<String>) -> isize {
None => {
let mut emitter =
errors::emitter::BasicEmitter::stderr(errors::ColorConfig::Auto);
emitter.emit(None, &abort_msg(err_count), None, errors::Level::Fatal);
emitter.emit(&MultiSpan::new(), &abort_msg(err_count), None,
errors::Level::Fatal);
exit_on_err();
}
}
@ -379,7 +380,7 @@ fn check_cfg(sopts: &config::Options,
match item.node {
ast::MetaItemKind::List(ref pred, _) => {
saw_invalid_predicate = true;
emitter.emit(None,
emitter.emit(&MultiSpan::new(),
&format!("invalid predicate in --cfg command line argument: `{}`",
pred),
None,
@ -1028,19 +1029,19 @@ pub fn monitor<F: FnOnce() + Send + 'static>(f: F) {
// a .span_bug or .bug call has already printed what
// it wants to print.
if !value.is::<errors::ExplicitBug>() {
emitter.emit(None, "unexpected panic", None, errors::Level::Bug);
emitter.emit(&MultiSpan::new(), "unexpected panic", None, errors::Level::Bug);
}
let xs = ["the compiler unexpectedly panicked. this is a bug.".to_string(),
format!("we would appreciate a bug report: {}", BUG_REPORT_URL)];
for note in &xs {
emitter.emit(None, &note[..], None, errors::Level::Note)
emitter.emit(&MultiSpan::new(), &note[..], None, errors::Level::Note)
}
if match env::var_os("RUST_BACKTRACE") {
Some(val) => &val != "0",
None => false,
} {
emitter.emit(None,
emitter.emit(&MultiSpan::new(),
"run with `RUST_BACKTRACE=1` for a backtrace",
None,
errors::Level::Note);

View File

@ -34,9 +34,9 @@ use std::cell::RefCell;
use std::rc::Rc;
use syntax::ast;
use syntax::abi::Abi;
use syntax::codemap::{MultiSpan, CodeMap, DUMMY_SP};
use syntax::codemap::{CodeMap, DUMMY_SP};
use syntax::errors;
use syntax::errors::emitter::Emitter;
use syntax::errors::emitter::{CoreEmitter, Emitter};
use syntax::errors::{Level, RenderSpan};
use syntax::parse::token;
use syntax::feature_gate::UnstableFeatures;
@ -73,21 +73,20 @@ fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) {
e.messages.remove(i);
}
None => {
debug!("Unexpected error: {} Expected: {:?}", msg, e.messages);
panic!("Unexpected error: {} Expected: {:?}", msg, e.messages);
}
}
}
impl Emitter for ExpectErrorEmitter {
fn emit(&mut self,
_sp: Option<&MultiSpan>,
msg: &str,
_: Option<&str>,
lvl: Level) {
remove_message(self, msg, lvl);
}
fn custom_emit(&mut self, _sp: &RenderSpan, msg: &str, lvl: Level) {
impl CoreEmitter for ExpectErrorEmitter {
fn emit_message(&mut self,
_sp: &RenderSpan,
msg: &str,
_: Option<&str>,
lvl: Level,
_is_header: bool,
_show_snippet: bool) {
remove_message(self, msg, lvl);
}
}
@ -449,7 +448,7 @@ fn contravariant_region_ptr_ok() {
#[test]
fn contravariant_region_ptr_err() {
test_env(EMPTY_SOURCE_STR, errors(&["lifetime mismatch"]), |env| {
test_env(EMPTY_SOURCE_STR, errors(&["mismatched types"]), |env| {
env.create_simple_region_hierarchy();
let t_rptr1 = env.t_rptr_scope(1);
let t_rptr10 = env.t_rptr_scope(10);

View File

@ -764,8 +764,8 @@ impl LateLintPass for UnconditionalRecursion {
for call in &self_call_spans {
db.span_note(*call, "recursive call site");
}
db.fileline_help(sp, "a `loop` may express intention \
better if this is on purpose");
db.help("a `loop` may express intention \
better if this is on purpose");
}
db.emit();
}

View File

@ -241,8 +241,8 @@ impl<'a> CrateReader<'a> {
crate_rustc_version
.as_ref().map(|s| &**s)
.unwrap_or("an old version of rustc"));
err.fileline_help(span, "consider removing the compiled binaries and recompiling \
with your current version of rustc");
err.help("consider removing the compiled binaries and recompiling \
with your current version of rustc");
err.emit();
}
}

View File

@ -346,39 +346,33 @@ impl<'a> Context<'a> {
if !self.rejected_via_triple.is_empty() {
let mismatches = self.rejected_via_triple.iter();
for (i, &CrateMismatch{ ref path, ref got }) in mismatches.enumerate() {
err.fileline_note(self.span,
&format!("crate `{}`, path #{}, triple {}: {}",
self.ident, i+1, got, path.display()));
err.note(&format!("crate `{}`, path #{}, triple {}: {}",
self.ident, i+1, got, path.display()));
}
}
if !self.rejected_via_hash.is_empty() {
err.span_note(self.span, "perhaps this crate needs \
to be recompiled?");
err.note("perhaps this crate needs to be recompiled?");
let mismatches = self.rejected_via_hash.iter();
for (i, &CrateMismatch{ ref path, .. }) in mismatches.enumerate() {
err.fileline_note(self.span,
&format!("crate `{}` path #{}: {}",
self.ident, i+1, path.display()));
err.note(&format!("crate `{}` path #{}: {}",
self.ident, i+1, path.display()));
}
match self.root {
&None => {}
&Some(ref r) => {
for (i, path) in r.paths().iter().enumerate() {
err.fileline_note(self.span,
&format!("crate `{}` path #{}: {}",
r.ident, i+1, path.display()));
err.note(&format!("crate `{}` path #{}: {}",
r.ident, i+1, path.display()));
}
}
}
}
if !self.rejected_via_kind.is_empty() {
err.fileline_help(self.span, "please recompile this crate using \
--crate-type lib");
err.help("please recompile this crate using --crate-type lib");
let mismatches = self.rejected_via_kind.iter();
for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() {
err.fileline_note(self.span,
&format!("crate `{}` path #{}: {}",
self.ident, i+1, path.display()));
err.note(&format!("crate `{}` path #{}: {}",
self.ident, i+1, path.display()));
}
}

View File

@ -198,9 +198,8 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
let mut err = self.tcx.sess.struct_span_err(
expr.span,
"const fns are an unstable feature");
fileline_help!(
help!(
&mut err,
expr.span,
"in Nightly builds, add `#![feature(const_fn)]` to the crate \
attributes to enable");
err.emit();

View File

@ -244,7 +244,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
E0405,
"trait `{}` is not in scope",
name);
show_candidates(&mut err, span, &candidates);
show_candidates(&mut err, &candidates);
err
}
ResolutionError::UndeclaredAssociatedType => {
@ -312,7 +312,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
"{} `{}` is undefined or not in scope",
kind,
name);
show_candidates(&mut err, span, &candidates);
show_candidates(&mut err, &candidates);
err
}
ResolutionError::DeclarationShadowsEnumVariantOrUnitLikeStruct(name) => {
@ -420,7 +420,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
match context {
UnresolvedNameContext::Other => { } // no help available
UnresolvedNameContext::PathIsMod(parent) => {
err.fileline_help(span, &match parent.map(|parent| &parent.node) {
err.help(&match parent.map(|parent| &parent.node) {
Some(&ExprField(_, ident)) => {
format!("To reference an item from the `{module}` module, \
use `{module}::{ident}`",
@ -1784,8 +1784,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// If it's a typedef, give a note
if let Def::TyAlias(..) = path_res.base_def {
err.fileline_note(trait_path.span,
"`type` aliases cannot be used for traits");
err.note("`type` aliases cannot be used for traits");
let definition_site = {
let segments = &trait_path.segments;
@ -2880,7 +2879,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let msg = format!("did you mean to write: `{} {{ /* fields */ }}`?",
path_name);
if self.emit_errors {
err.fileline_help(expr.span, &msg);
err.help(&msg);
} else {
err.span_help(expr.span, &msg);
}
@ -2922,7 +2921,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
path_name);
if self.emit_errors {
err.fileline_help(expr.span, &msg);
err.help(&msg);
} else {
err.span_help(expr.span, &msg);
}
@ -3420,7 +3419,6 @@ fn path_names_to_string(path: &Path, depth: usize) -> String {
/// entities with that name in all crates. This method allows outputting the
/// results of this search in a programmer-friendly way
fn show_candidates(session: &mut DiagnosticBuilder,
span: syntax::codemap::Span,
candidates: &SuggestedCandidates) {
let paths = &candidates.candidates;
@ -3440,26 +3438,23 @@ fn show_candidates(session: &mut DiagnosticBuilder,
// behave differently based on how many candidates we have:
if !paths.is_empty() {
if paths.len() == 1 {
session.fileline_help(
span,
session.help(
&format!("you can import it into scope: `use {};`.",
&path_strings[0]),
);
} else {
session.fileline_help(span, "you can import several candidates \
session.help("you can import several candidates \
into scope (`use ...;`):");
let count = path_strings.len() as isize - MAX_CANDIDATES as isize + 1;
for (idx, path_string) in path_strings.iter().enumerate() {
if idx == MAX_CANDIDATES - 1 && count > 1 {
session.fileline_help(
span,
session.help(
&format!(" and {} other candidates", count).to_string(),
);
break;
} else {
session.fileline_help(
span,
session.help(
&format!(" `{}`", path_string).to_string(),
);
}
@ -3468,8 +3463,7 @@ fn show_candidates(session: &mut DiagnosticBuilder,
}
} else {
// nothing found:
session.fileline_help(
span,
session.help(
&format!("no candidates by the name of `{}` found in your \
project; maybe you misspelled the name or forgot to import \
an external crate?", candidates.name.to_string()),

View File

@ -19,9 +19,9 @@ use llvm::SMDiagnosticRef;
use {CrateTranslation, ModuleTranslation};
use util::common::time;
use util::common::path2cstr;
use syntax::codemap;
use syntax::errors::{self, Handler, Level};
use syntax::errors::emitter::Emitter;
use syntax::codemap::MultiSpan;
use syntax::errors::{self, Handler, Level, RenderSpan};
use syntax::errors::emitter::CoreEmitter;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
@ -84,13 +84,13 @@ impl SharedEmitter {
for diag in &*buffer {
match diag.code {
Some(ref code) => {
handler.emit_with_code(None,
handler.emit_with_code(&MultiSpan::new(),
&diag.msg,
&code[..],
diag.lvl);
},
None => {
handler.emit(None,
handler.emit(&MultiSpan::new(),
&diag.msg,
diag.lvl);
},
@ -100,21 +100,20 @@ impl SharedEmitter {
}
}
impl Emitter for SharedEmitter {
fn emit(&mut self, sp: Option<&codemap::MultiSpan>,
msg: &str, code: Option<&str>, lvl: Level) {
assert!(sp.is_none(), "SharedEmitter doesn't support spans");
impl CoreEmitter for SharedEmitter {
fn emit_message(&mut self,
_rsp: &RenderSpan,
msg: &str,
code: Option<&str>,
lvl: Level,
_is_header: bool,
_show_snippet: bool) {
self.buffer.lock().unwrap().push(Diagnostic {
msg: msg.to_string(),
code: code.map(|s| s.to_string()),
lvl: lvl,
});
}
fn custom_emit(&mut self, _sp: &errors::RenderSpan, _msg: &str, _lvl: Level) {
bug!("SharedEmitter doesn't support custom_emit");
}
}

View File

@ -206,7 +206,6 @@ pub fn ast_region_to_region(tcx: &TyCtxt, lifetime: &hir::Lifetime)
fn report_elision_failure(
db: &mut DiagnosticBuilder,
default_span: Span,
params: Vec<ElisionFailureInfo>)
{
let mut m = String::new();
@ -243,29 +242,29 @@ fn report_elision_failure(
}
if len == 0 {
fileline_help!(db, default_span,
"this function's return type contains a borrowed value, but \
there is no value for it to be borrowed from");
fileline_help!(db, default_span,
"consider giving it a 'static lifetime");
help!(db,
"this function's return type contains a borrowed value, but \
there is no value for it to be borrowed from");
help!(db,
"consider giving it a 'static lifetime");
} else if !any_lifetimes {
fileline_help!(db, default_span,
"this function's return type contains a borrowed value with \
an elided lifetime, but the lifetime cannot be derived from \
the arguments");
fileline_help!(db, default_span,
"consider giving it an explicit bounded or 'static \
lifetime");
help!(db,
"this function's return type contains a borrowed value with \
an elided lifetime, but the lifetime cannot be derived from \
the arguments");
help!(db,
"consider giving it an explicit bounded or 'static \
lifetime");
} else if len == 1 {
fileline_help!(db, default_span,
"this function's return type contains a borrowed value, but \
the signature does not say which {} it is borrowed from",
m);
help!(db,
"this function's return type contains a borrowed value, but \
the signature does not say which {} it is borrowed from",
m);
} else {
fileline_help!(db, default_span,
"this function's return type contains a borrowed value, but \
the signature does not say whether it is borrowed from {}",
m);
help!(db,
"this function's return type contains a borrowed value, but \
the signature does not say whether it is borrowed from {}",
m);
}
}
@ -286,7 +285,7 @@ pub fn opt_ast_region_to_region<'tcx>(
let mut err = struct_span_err!(this.tcx().sess, default_span, E0106,
"missing lifetime specifier");
if let Some(params) = params {
report_elision_failure(&mut err, default_span, params);
report_elision_failure(&mut err, params);
}
err.emit();
ty::ReStatic
@ -1087,7 +1086,7 @@ fn ast_ty_to_trait_ref<'tcx>(this: &AstConv<'tcx>,
}
_ => {
fileline_help!(&mut err, ty.span,
help!(&mut err,
"perhaps you forgot parentheses? (per RFC 438)");
}
}

View File

@ -116,8 +116,8 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
// Check that the types of the end-points can be unified.
let types_unify = require_same_types(
tcx, Some(fcx.infcx()), false, pat.span, rhs_ty, lhs_ty,
|| "mismatched types in range".to_string()
tcx, Some(fcx.infcx()), false, pat.span, rhs_ty, lhs_ty,
"mismatched types in range",
);
// It's ok to return without a message as `require_same_types` prints an error.

View File

@ -64,8 +64,8 @@ pub fn check_legal_trait_for_method_call(ccx: &CrateCtxt, span: Span, trait_id:
struct_span_err!(tcx.sess, span, E0174,
"explicit use of unboxed closure method `{}` is experimental",
method)
.fileline_help(span, "add `#![feature(unboxed_closures)]` to the crate \
attributes to enable")
.help("add `#![feature(unboxed_closures)]` to the crate \
attributes to enable")
.emit();
}
}

View File

@ -155,8 +155,7 @@ impl<'tcx> CastCheck<'tcx> {
actual,
fcx.infcx().ty_to_string(self.cast_ty))
}, self.expr_ty, None)
.fileline_help(self.span,
&format!("cast through {} first", match e {
.help(&format!("cast through {} first", match e {
CastError::NeedViaPtr => "a raw pointer",
CastError::NeedViaThinPtr => "a thin pointer",
CastError::NeedViaInt => "an integer",
@ -167,7 +166,7 @@ impl<'tcx> CastCheck<'tcx> {
}
CastError::CastToBool => {
struct_span_err!(fcx.tcx().sess, self.span, E0054, "cannot cast as `bool`")
.fileline_help(self.span, "compare with zero instead")
.help("compare with zero instead")
.emit();
}
CastError::CastToChar => {
@ -202,7 +201,7 @@ impl<'tcx> CastCheck<'tcx> {
actual,
fcx.infcx().ty_to_string(self.cast_ty))
}, self.expr_ty, None)
.fileline_note(self.span, "vtable kinds may not match")
.note("vtable kinds may not match")
.emit();
}
}

View File

@ -61,10 +61,7 @@ fn equate_intrinsic_type<'a, 'tcx>(tcx: &TyCtxt<'tcx>, it: &hir::ForeignItem,
it.span,
i_ty.ty,
fty,
|| {
format!("intrinsic has wrong type: expected `{}`",
fty)
});
"intrinsic has wrong type");
}
}

View File

@ -148,9 +148,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
if is_fn_ty(&rcvr_ty, &fcx, span) {
macro_rules! report_function {
($span:expr, $name:expr) => {
err.fileline_note(
$span,
&format!("{} is a function, perhaps you wish to call it",
err.note(&format!("{} is a function, perhaps you wish to call it",
$name));
}
}
@ -172,8 +170,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
if !static_sources.is_empty() {
err.fileline_note(
span,
err.note(
"found the following associated functions; to be used as \
methods, functions must have a `self` parameter");
@ -187,8 +184,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
p))
.collect::<Vec<_>>()
.join(", ");
err.fileline_note(
span,
err.note(
&format!("the method `{}` exists but the \
following trait bounds were not satisfied: {}",
item_name,
@ -306,13 +302,12 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
err.fileline_help(span, &msg[..]);
err.help(&msg[..]);
for (i, trait_did) in candidates.iter().enumerate() {
err.fileline_help(span,
&format!("candidate #{}: `use {}`",
i + 1,
fcx.tcx().item_path_str(*trait_did)));
err.help(&format!("candidate #{}: `use {}`",
i + 1,
fcx.tcx().item_path_str(*trait_did)));
}
return
}
@ -351,13 +346,12 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
name = item_name);
err.fileline_help(span, &msg[..]);
err.help(&msg[..]);
for (i, trait_info) in candidates.iter().enumerate() {
err.fileline_help(span,
&format!("candidate #{}: `{}`",
i + 1,
fcx.tcx().item_path_str(trait_info.def_id)));
err.help(&format!("candidate #{}: `{}`",
i + 1,
fcx.tcx().item_path_str(trait_info.def_id)));
}
}
}

View File

@ -2955,9 +2955,9 @@ fn check_expr_with_expectation_and_lvalue_pref<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
`{}`", field.node, actual)
},
expr_t, None)
.fileline_help(field.span,
"maybe a `()` to call it is missing? \
If not, try an anonymous function")
.help(
"maybe a `()` to call it is missing? \
If not, try an anonymous function")
.emit();
fcx.write_error(expr.id);
} else {

View File

@ -418,8 +418,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
let _ = ::require_same_types(
fcx.tcx(), Some(fcx.infcx()), false, span,
sig.inputs[0], rcvr_ty,
|| "mismatched method receiver".to_owned()
);
"mismatched method receiver");
}
fn check_variances_for_type_defn(&self,
@ -502,8 +501,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
let suggested_marker_id = self.tcx().lang_items.phantom_data();
match suggested_marker_id {
Some(def_id) => {
err.fileline_help(
span,
err.help(
&format!("consider removing `{}` or using a marker such as `{}`",
param_name,
self.tcx().item_path_str(def_id)));

View File

@ -510,8 +510,7 @@ fn enforce_trait_manually_implementable(tcx: &TyCtxt, sp: Span, trait_def_id: De
E0183,
"manual implementations of `{}` are experimental",
trait_name);
fileline_help!(&mut err, sp,
"add `#![feature(unboxed_closures)]` to the crate attributes to enable");
help!(&mut err, "add `#![feature(unboxed_closures)]` to the crate attributes to enable");
err.emit();
}

View File

@ -1044,8 +1044,9 @@ fn convert_enum_def<'tcx>(tcx: &TyCtxt<'tcx>,
-> ty::AdtDefMaster<'tcx>
{
fn print_err(tcx: &TyCtxt, span: Span, ty: ty::Ty, cv: ConstVal) {
span_err!(tcx.sess, span, E0079, "mismatched types: expected `{}` got `{}`",
ty, cv.description());
struct_span_err!(tcx.sess, span, E0079, "mismatched types")
.note_expected_found(&"type", &ty, &format!("{}", cv.description()))
.emit();
}
fn evaluate_disr_expr<'tcx>(tcx: &TyCtxt<'tcx>,
repr_ty: attr::IntType,
@ -1257,9 +1258,9 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
it.span,
"the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \
which traits can use parenthetical notation");
fileline_help!(&mut err, it.span,
"add `#![feature(unboxed_closures)]` to \
the crate attributes to use it");
help!(&mut err,
"add `#![feature(unboxed_closures)]` to \
the crate attributes to use it");
err.emit();
}
@ -2195,8 +2196,7 @@ fn compute_type_scheme_of_foreign_fn_decl<'a, 'tcx>(
&format!("use of SIMD type `{}` in FFI is highly experimental and \
may result in invalid code",
pprust::ty_to_string(ast_ty)))
.fileline_help(ast_ty.span,
"add #![feature(simd_ffi)] to the crate attributes to enable")
.help("add #![feature(simd_ffi)] to the crate attributes to enable")
.emit();
}
};

View File

@ -185,15 +185,14 @@ fn require_c_abi_if_variadic(tcx: &TyCtxt,
}
}
fn require_same_types<'a, 'tcx, M>(tcx: &TyCtxt<'tcx>,
maybe_infcx: Option<&infer::InferCtxt<'a, 'tcx>>,
t1_is_expected: bool,
span: Span,
t1: Ty<'tcx>,
t2: Ty<'tcx>,
msg: M)
-> bool where
M: FnOnce() -> String,
fn require_same_types<'a, 'tcx>(tcx: &TyCtxt<'tcx>,
maybe_infcx: Option<&infer::InferCtxt<'a, 'tcx>>,
t1_is_expected: bool,
span: Span,
t1: Ty<'tcx>,
t2: Ty<'tcx>,
msg: &str)
-> bool
{
let result = match maybe_infcx {
None => {
@ -208,7 +207,17 @@ fn require_same_types<'a, 'tcx, M>(tcx: &TyCtxt<'tcx>,
match result {
Ok(_) => true,
Err(ref terr) => {
let mut err = struct_span_err!(tcx.sess, span, E0211, "{}: {}", msg(), terr);
let mut err = struct_span_err!(tcx.sess, span, E0211, "{}", msg);
err = err.span_label(span, &terr);
let (mut expected_ty, mut found_ty) =
if t1_is_expected {(t1, t2)} else {(t2, t1)};
if let Some(infcx) = maybe_infcx {
expected_ty = infcx.resolve_type_vars_if_possible(&expected_ty);
found_ty = infcx.resolve_type_vars_if_possible(&found_ty);
}
err = err.note_expected_found(&"type",
&expected_ty,
&found_ty);
tcx.note_and_explain_type_err(&mut err, terr, span);
err.emit();
false
@ -250,10 +259,7 @@ fn check_main_fn_ty(ccx: &CrateCtxt,
});
require_same_types(tcx, None, false, main_span, main_t, se_ty,
|| {
format!("main function expects type: `{}`",
se_ty)
});
"main function has wrong type");
}
_ => {
span_bug!(main_span,
@ -301,11 +307,7 @@ fn check_start_fn_ty(ccx: &CrateCtxt,
});
require_same_types(tcx, None, false, start_span, start_t, se_ty,
|| {
format!("start function expects type: `{}`",
se_ty)
});
"start function has wrong type");
}
_ => {
span_bug!(start_span,

View File

@ -32,8 +32,6 @@ use serialize::{Encodable, Decodable, Encoder, Decoder};
use ast::Name;
use errors::emitter::MAX_HIGHLIGHT_LINES;
// _____________________________________________________________________________
// Pos, BytePos, CharPos
//
@ -51,7 +49,7 @@ pub struct BytePos(pub u32);
/// A character offset. Because of multibyte utf8 characters, a byte offset
/// is not equivalent to a character offset. The CodeMap will convert BytePos
/// values to CharPos values as necessary.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct CharPos(pub usize);
// FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
@ -132,13 +130,29 @@ pub struct Span {
pub expn_id: ExpnId
}
/// Spans are converted to MultiSpans just before error reporting, either automatically,
/// generated by line grouping, or manually constructed.
/// In the latter case care should be taken to ensure that spans are ordered, disjoint,
/// and point into the same FileMap.
/// A collection of spans. Spans have two orthogonal attributes:
///
/// - they can be *primary spans*. In this case they are the locus of
/// the error, and would be rendered with `^^^`.
/// - they can have a *label*. In this case, the label is written next
/// to the mark in the snippet when we render.
#[derive(Clone)]
pub struct MultiSpan {
pub spans: Vec<Span>
primary_spans: Vec<Span>,
span_labels: Vec<(Span, String)>,
}
#[derive(Clone, Debug)]
pub struct SpanLabel {
/// The span we are going to include in the final snippet.
pub span: Span,
/// Is this a primary span? This is the "locus" of the message,
/// and is indicated with a `^^^^` underline, versus `----`.
pub is_primary: bool,
/// What label should we attach to this span (if any)?
pub label: Option<String>,
}
pub const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0), expn_id: NO_EXPANSION };
@ -149,6 +163,12 @@ pub const COMMAND_LINE_SP: Span = Span { lo: BytePos(0),
expn_id: COMMAND_LINE_EXPN };
impl Span {
/// Returns a new span representing just the end-point of this span
pub fn end_point(self) -> Span {
let lo = cmp::max(self.hi.0 - 1, self.lo.0);
Span { lo: BytePos(lo), hi: self.hi, expn_id: self.expn_id}
}
/// Returns `self` if `self` is not the dummy span, and `other` otherwise.
pub fn substitute_dummy(self, other: Span) -> Span {
if self.source_equal(&DUMMY_SP) { other } else { self }
@ -276,97 +296,74 @@ pub fn original_sp(cm: &CodeMap, sp: Span, enclosing_sp: Span) -> Span {
impl MultiSpan {
pub fn new() -> MultiSpan {
MultiSpan { spans: Vec::new() }
}
pub fn to_span_bounds(&self) -> Span {
assert!(!self.spans.is_empty());
let Span { lo, expn_id, .. } = *self.spans.first().unwrap();
let Span { hi, .. } = *self.spans.last().unwrap();
Span { lo: lo, hi: hi, expn_id: expn_id }
}
/// Merges or inserts the given span into itself.
pub fn push_merge(&mut self, mut sp: Span) {
let mut idx_merged = None;
for idx in 0.. {
let cur = match self.spans.get(idx) {
Some(s) => *s,
None => break,
};
// Try to merge with a contained Span
if let Some(union) = cur.merge(sp) {
self.spans[idx] = union;
sp = union;
idx_merged = Some(idx);
break;
}
// Or insert into the first sorted position
if sp.hi <= cur.lo {
self.spans.insert(idx, sp);
idx_merged = Some(idx);
break;
}
}
if let Some(idx) = idx_merged {
// Merge with spans trailing the insertion/merging position
while (idx + 1) < self.spans.len() {
if let Some(union) = self.spans[idx + 1].merge(sp) {
self.spans[idx] = union;
self.spans.remove(idx + 1);
} else {
break;
}
}
} else {
self.spans.push(sp);
MultiSpan {
primary_spans: vec![],
span_labels: vec![]
}
}
/// Inserts the given span into itself, for use with `end_highlight_lines`.
pub fn push_trim(&mut self, mut sp: Span) {
let mut prev = mk_sp(BytePos(0), BytePos(0));
pub fn from_span(primary_span: Span) -> MultiSpan {
MultiSpan {
primary_spans: vec![primary_span],
span_labels: vec![]
}
}
if let Some(first) = self.spans.get_mut(0) {
if first.lo > sp.lo {
// Prevent us here from spanning fewer lines
// because of trimming the start of the span
// (this should not be visible, because this method ought
// to not be used in conjunction with `highlight_lines`)
first.lo = sp.lo;
pub fn from_spans(vec: Vec<Span>) -> MultiSpan {
MultiSpan {
primary_spans: vec,
span_labels: vec![]
}
}
pub fn push_span_label(&mut self, span: Span, label: String) {
self.span_labels.push((span, label));
}
/// Selects the first primary span (if any)
pub fn primary_span(&self) -> Option<Span> {
self.primary_spans.first().cloned()
}
/// Returns all primary spans.
pub fn primary_spans(&self) -> &[Span] {
&self.primary_spans
}
/// Returns the strings to highlight. We always ensure that there
/// is an entry for each of the primary spans -- for each primary
/// span P, if there is at least one label with span P, we return
/// those labels (marked as primary). But otherwise we return
/// `SpanLabel` instances with empty labels.
pub fn span_labels(&self) -> Vec<SpanLabel> {
let is_primary = |span| self.primary_spans.contains(&span);
let mut span_labels = vec![];
for &(span, ref label) in &self.span_labels {
span_labels.push(SpanLabel {
span: span,
is_primary: is_primary(span),
label: Some(label.clone())
});
}
for &span in &self.primary_spans {
if !span_labels.iter().any(|sl| sl.span == span) {
span_labels.push(SpanLabel {
span: span,
is_primary: true,
label: None
});
}
}
for idx in 0.. {
if let Some(sp_trim) = sp.trim_start(prev) {
// Implies `sp.hi > prev.hi`
let cur = match self.spans.get(idx) {
Some(s) => *s,
None => {
sp = sp_trim;
break;
}
};
// `cur` may overlap with `sp_trim`
if let Some(cur_trim) = cur.trim_start(sp_trim) {
// Implies `sp.hi < cur.hi`
self.spans.insert(idx, sp_trim);
self.spans[idx + 1] = cur_trim;
return;
} else if sp.hi == cur.hi {
return;
}
prev = cur;
}
}
self.spans.push(sp);
span_labels
}
}
impl From<Span> for MultiSpan {
fn from(span: Span) -> MultiSpan {
MultiSpan { spans: vec![span] }
MultiSpan::from_span(span)
}
}
@ -801,7 +798,7 @@ impl CodeMap {
/// Creates a new filemap and sets its line information.
pub fn new_filemap_and_lines(&self, filename: &str, src: &str) -> Rc<FileMap> {
let fm = self.new_filemap(filename.to_string(), src.to_owned());
let mut byte_pos: u32 = 0;
let mut byte_pos: u32 = fm.start_pos.0;
for line in src.lines() {
// register the start of this line
fm.next_line(BytePos(byte_pos));
@ -929,6 +926,10 @@ impl CodeMap {
}
pub fn span_to_string(&self, sp: Span) -> String {
if sp == COMMAND_LINE_SP {
return "<command line option>".to_string();
}
if self.files.borrow().is_empty() && sp.source_equal(&DUMMY_SP) {
return "no-location".to_string();
}
@ -1099,12 +1100,16 @@ impl CodeMap {
}
pub fn span_to_lines(&self, sp: Span) -> FileLinesResult {
debug!("span_to_lines(sp={:?})", sp);
if sp.lo > sp.hi {
return Err(SpanLinesError::IllFormedSpan(sp));
}
let lo = self.lookup_char_pos(sp.lo);
debug!("span_to_lines: lo={:?}", lo);
let hi = self.lookup_char_pos(sp.hi);
debug!("span_to_lines: hi={:?}", hi);
if lo.file.start_pos != hi.file.start_pos {
return Err(SpanLinesError::DistinctSources(DistinctSources {
@ -1125,7 +1130,9 @@ impl CodeMap {
// numbers in Loc are 1-based, so we subtract 1 to get 0-based
// lines.
for line_index in lo.line-1 .. hi.line-1 {
let line_len = lo.file.get_line(line_index).map(|s| s.len()).unwrap_or(0);
let line_len = lo.file.get_line(line_index)
.map(|s| s.chars().count())
.unwrap_or(0);
lines.push(LineInfo { line_index: line_index,
start_col: start_col,
end_col: CharPos::from_usize(line_len) });
@ -1184,59 +1191,6 @@ impl CodeMap {
}
}
/// Groups and sorts spans by lines into `MultiSpan`s, where `push` adds them to their group,
/// specifying the unification behaviour for overlapping spans.
/// Spans overflowing a line are put into their own one-element-group.
pub fn custom_group_spans<F>(&self, mut spans: Vec<Span>, push: F) -> Vec<MultiSpan>
where F: Fn(&mut MultiSpan, Span)
{
spans.sort_by(|a, b| a.lo.cmp(&b.lo));
let mut groups = Vec::<MultiSpan>::new();
let mut overflowing = vec![];
let mut prev_expn = ExpnId(!2u32);
let mut prev_file = !0usize;
let mut prev_line = !0usize;
let mut err_size = 0;
for sp in spans {
let line = self.lookup_char_pos(sp.lo).line;
let line_hi = self.lookup_char_pos(sp.hi).line;
if line != line_hi {
overflowing.push(sp.into());
continue
}
let file = self.lookup_filemap_idx(sp.lo);
if err_size < MAX_HIGHLIGHT_LINES && sp.expn_id == prev_expn && file == prev_file {
// `push` takes care of sorting, trimming, and merging
push(&mut groups.last_mut().unwrap(), sp);
if line != prev_line {
err_size += 1;
}
} else {
groups.push(sp.into());
err_size = 1;
}
prev_expn = sp.expn_id;
prev_file = file;
prev_line = line;
}
groups.extend(overflowing);
groups
}
/// Groups and sorts spans by lines into `MultiSpan`s, merging overlapping spans.
/// Spans overflowing a line are put into their own one-element-group.
pub fn group_spans(&self, spans: Vec<Span>) -> Vec<MultiSpan> {
self.custom_group_spans(spans, |msp, sp| msp.push_merge(sp))
}
/// Like `group_spans`, but trims overlapping spans instead of
/// merging them (for use with `end_highlight_lines`)
pub fn end_group_spans(&self, spans: Vec<Span>) -> Vec<MultiSpan> {
self.custom_group_spans(spans, |msp, sp| msp.push_trim(sp))
}
pub fn get_filemap(&self, filename: &str) -> Rc<FileMap> {
for fm in self.files.borrow().iter() {
if filename == fm.name {
@ -1636,13 +1590,13 @@ mod tests {
assert_eq!(file_lines.lines[0].line_index, 1);
}
/// Given a string like " ^~~~~~~~~~~~ ", produces a span
/// Given a string like " ~~~~~~~~~~~~ ", produces a span
/// coverting that range. The idea is that the string has the same
/// length as the input, and we uncover the byte positions. Note
/// that this can span lines and so on.
fn span_from_selection(input: &str, selection: &str) -> Span {
assert_eq!(input.len(), selection.len());
let left_index = selection.find('^').unwrap() as u32;
let left_index = selection.find('~').unwrap() as u32;
let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index);
Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION }
}
@ -1653,7 +1607,7 @@ mod tests {
fn span_to_snippet_and_lines_spanning_multiple_lines() {
let cm = CodeMap::new();
let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
let selection = " \n ^~\n~~~\n~~~~~ \n \n";
let selection = " \n ~~\n~~~\n~~~~~ \n \n";
cm.new_filemap_and_lines("blork.rs", inputtext);
let span = span_from_selection(inputtext, selection);
@ -1803,73 +1757,4 @@ r"blork2.rs:2:1: 2:12
";
assert_eq!(sstr, res_str);
}
#[test]
fn t13() {
// Test that collecting multiple spans into line-groups works correctly
let cm = CodeMap::new();
let inp = "_aaaaa__bbb\nvv\nw\nx\ny\nz\ncccccc__ddddee__";
let sp1 = " ^~~~~ \n \n \n \n \n \n ";
let sp2 = " \n \n \n \n \n^\n ";
let sp3 = " ^~~\n~~\n \n \n \n \n ";
let sp4 = " \n \n \n \n \n \n^~~~~~ ";
let sp5 = " \n \n \n \n \n \n ^~~~ ";
let sp6 = " \n \n \n \n \n \n ^~~~ ";
let sp_trim = " \n \n \n \n \n \n ^~ ";
let sp_merge = " \n \n \n \n \n \n ^~~~~~ ";
let sp7 = " \n ^\n \n \n \n \n ";
let sp8 = " \n \n^\n \n \n \n ";
let sp9 = " \n \n \n^\n \n \n ";
let sp10 = " \n \n \n \n^\n \n ";
let span = |sp, expected| {
let sp = span_from_selection(inp, sp);
assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected);
sp
};
cm.new_filemap_and_lines("blork.rs", inp);
let sp1 = span(sp1, "aaaaa");
let sp2 = span(sp2, "z");
let sp3 = span(sp3, "bbb\nvv");
let sp4 = span(sp4, "cccccc");
let sp5 = span(sp5, "dddd");
let sp6 = span(sp6, "ddee");
let sp7 = span(sp7, "v");
let sp8 = span(sp8, "w");
let sp9 = span(sp9, "x");
let sp10 = span(sp10, "y");
let sp_trim = span(sp_trim, "ee");
let sp_merge = span(sp_merge, "ddddee");
let spans = vec![sp5, sp2, sp4, sp9, sp10, sp7, sp3, sp8, sp1, sp6];
macro_rules! check_next {
($groups: expr, $expected: expr) => ({
let actual = $groups.next().map(|g|&g.spans[..]);
let expected = $expected;
println!("actual:\n{:?}\n", actual);
println!("expected:\n{:?}\n", expected);
assert_eq!(actual, expected.as_ref().map(|x|&x[..]));
});
}
let _groups = cm.group_spans(spans.clone());
let it = &mut _groups.iter();
check_next!(it, Some([sp1, sp7, sp8, sp9, sp10, sp2]));
// New group because we're exceeding MAX_HIGHLIGHT_LINES
check_next!(it, Some([sp4, sp_merge]));
check_next!(it, Some([sp3]));
check_next!(it, None::<[Span; 0]>);
let _groups = cm.end_group_spans(spans);
let it = &mut _groups.iter();
check_next!(it, Some([sp1, sp7, sp8, sp9, sp10, sp2]));
// New group because we're exceeding MAX_HIGHLIGHT_LINES
check_next!(it, Some([sp4, sp5, sp_trim]));
check_next!(it, Some([sp3]));
check_next!(it, None::<[Span; 0]>);
}
}

View File

@ -101,9 +101,9 @@ macro_rules! span_help {
}
#[macro_export]
macro_rules! fileline_help {
($err:expr, $span:expr, $($message:tt)*) => ({
($err).fileline_help($span, &format!($($message)*));
macro_rules! help {
($err:expr, $($message:tt)*) => ({
($err).help(&format!($($message)*));
})
}

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@
// FIXME spec the JSON output properly.
use codemap::{self, Span, MacroBacktrace, MultiSpan, CodeMap};
use codemap::{self, MacroBacktrace, Span, SpanLabel, MultiSpan, CodeMap};
use diagnostics::registry::Registry;
use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion};
use errors::emitter::Emitter;
@ -53,20 +53,13 @@ impl JsonEmitter {
}
impl Emitter for JsonEmitter {
fn emit(&mut self, span: Option<&MultiSpan>, msg: &str, code: Option<&str>, level: Level) {
fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, level: Level) {
let data = Diagnostic::new(span, msg, code, level, self);
if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
panic!("failed to print diagnostics: {:?}", e);
}
}
fn custom_emit(&mut self, sp: &RenderSpan, msg: &str, level: Level) {
let data = Diagnostic::from_render_span(sp, msg, level, self);
if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
panic!("failed to print diagnostics: {:?}", e);
}
}
fn emit_struct(&mut self, db: &DiagnosticBuilder) {
let data = Diagnostic::from_diagnostic_builder(db, self);
if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
@ -104,8 +97,13 @@ struct DiagnosticSpan {
/// 1-based, character offset.
column_start: usize,
column_end: usize,
/// Is this a "primary" span -- meaning the point, or one of the points,
/// where the error occurred?
is_primary: bool,
/// Source text from the start of line_start to the end of line_end.
text: Vec<DiagnosticSpanLine>,
/// Label that should be placed at this location (if any)
label: Option<String>,
/// If we are suggesting a replacement, this will contain text
/// that should be sliced in atop this span. You may prefer to
/// load the fully rendered version from the parent `Diagnostic`,
@ -148,7 +146,7 @@ struct DiagnosticCode {
}
impl<'a> Diagnostic<'a> {
fn new(msp: Option<&MultiSpan>,
fn new(msp: &MultiSpan,
msg: &'a str,
code: Option<&str>,
level: Level,
@ -158,27 +156,12 @@ impl<'a> Diagnostic<'a> {
message: msg,
code: DiagnosticCode::map_opt_string(code.map(|c| c.to_owned()), je),
level: level.to_str(),
spans: msp.map_or(vec![], |msp| DiagnosticSpan::from_multispan(msp, je)),
spans: DiagnosticSpan::from_multispan(msp, je),
children: vec![],
rendered: None,
}
}
fn from_render_span(span: &RenderSpan,
msg: &'a str,
level: Level,
je: &JsonEmitter)
-> Diagnostic<'a> {
Diagnostic {
message: msg,
code: None,
level: level.to_str(),
spans: DiagnosticSpan::from_render_span(span, je),
children: vec![],
rendered: je.render(span),
}
}
fn from_diagnostic_builder<'c>(db: &'c DiagnosticBuilder,
je: &JsonEmitter)
-> Diagnostic<'c> {
@ -186,7 +169,7 @@ impl<'a> Diagnostic<'a> {
message: &db.message,
code: DiagnosticCode::map_opt_string(db.code.clone(), je),
level: db.level.to_str(),
spans: db.span.as_ref().map_or(vec![], |sp| DiagnosticSpan::from_multispan(sp, je)),
spans: DiagnosticSpan::from_multispan(&db.span, je),
children: db.children.iter().map(|c| {
Diagnostic::from_sub_diagnostic(c, je)
}).collect(),
@ -201,8 +184,7 @@ impl<'a> Diagnostic<'a> {
level: db.level.to_str(),
spans: db.render_span.as_ref()
.map(|sp| DiagnosticSpan::from_render_span(sp, je))
.or_else(|| db.span.as_ref().map(|s| DiagnosticSpan::from_multispan(s, je)))
.unwrap_or(vec![]),
.unwrap_or_else(|| DiagnosticSpan::from_multispan(&db.span, je)),
children: vec![],
rendered: db.render_span.as_ref()
.and_then(|rsp| je.render(rsp)),
@ -211,44 +193,68 @@ impl<'a> Diagnostic<'a> {
}
impl DiagnosticSpan {
fn from_span(span: Span, suggestion: Option<&String>, je: &JsonEmitter)
-> DiagnosticSpan {
fn from_span_label(span: SpanLabel,
suggestion: Option<&String>,
je: &JsonEmitter)
-> DiagnosticSpan {
Self::from_span_etc(span.span,
span.is_primary,
span.label,
suggestion,
je)
}
fn from_span_etc(span: Span,
is_primary: bool,
label: Option<String>,
suggestion: Option<&String>,
je: &JsonEmitter)
-> DiagnosticSpan {
// obtain the full backtrace from the `macro_backtrace`
// helper; in some ways, it'd be better to expand the
// backtrace ourselves, but the `macro_backtrace` helper makes
// some decision, such as dropping some frames, and I don't
// want to duplicate that logic here.
let backtrace = je.cm.macro_backtrace(span).into_iter();
DiagnosticSpan::from_span_and_backtrace(span, suggestion, backtrace, je)
DiagnosticSpan::from_span_full(span,
is_primary,
label,
suggestion,
backtrace,
je)
}
fn from_span_and_backtrace(span: Span,
suggestion: Option<&String>,
mut backtrace: vec::IntoIter<MacroBacktrace>,
je: &JsonEmitter)
-> DiagnosticSpan {
fn from_span_full(span: Span,
is_primary: bool,
label: Option<String>,
suggestion: Option<&String>,
mut backtrace: vec::IntoIter<MacroBacktrace>,
je: &JsonEmitter)
-> DiagnosticSpan {
let start = je.cm.lookup_char_pos(span.lo);
let end = je.cm.lookup_char_pos(span.hi);
let backtrace_step =
backtrace.next()
.map(|bt| {
let call_site =
Self::from_span_and_backtrace(bt.call_site,
None,
backtrace,
je);
let def_site_span = bt.def_site_span.map(|sp| {
Self::from_span_and_backtrace(sp,
None,
vec![].into_iter(),
je)
});
Box::new(DiagnosticSpanMacroExpansion {
span: call_site,
macro_decl_name: bt.macro_decl_name,
def_site_span: def_site_span,
})
});
let backtrace_step = backtrace.next().map(|bt| {
let call_site =
Self::from_span_full(bt.call_site,
false,
None,
None,
backtrace,
je);
let def_site_span = bt.def_site_span.map(|sp| {
Self::from_span_full(sp,
false,
None,
None,
vec![].into_iter(),
je)
});
Box::new(DiagnosticSpanMacroExpansion {
span: call_site,
macro_decl_name: bt.macro_decl_name,
def_site_span: def_site_span,
})
});
DiagnosticSpan {
file_name: start.file.name.clone(),
byte_start: span.lo.0,
@ -257,53 +263,41 @@ impl DiagnosticSpan {
line_end: end.line,
column_start: start.col.0 + 1,
column_end: end.col.0 + 1,
is_primary: is_primary,
text: DiagnosticSpanLine::from_span(span, je),
suggested_replacement: suggestion.cloned(),
expansion: backtrace_step,
label: label,
}
}
fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
msp.spans.iter().map(|&span| Self::from_span(span, None, je)).collect()
msp.span_labels()
.into_iter()
.map(|span_str| Self::from_span_label(span_str, None, je))
.collect()
}
fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter)
-> Vec<DiagnosticSpan> {
assert_eq!(suggestion.msp.spans.len(), suggestion.substitutes.len());
suggestion.msp.spans.iter()
.zip(&suggestion.substitutes)
.map(|(&span, suggestion)| {
DiagnosticSpan::from_span(span, Some(suggestion), je)
})
.collect()
assert_eq!(suggestion.msp.span_labels().len(), suggestion.substitutes.len());
suggestion.msp.span_labels()
.into_iter()
.zip(&suggestion.substitutes)
.map(|(span_label, suggestion)| {
DiagnosticSpan::from_span_label(span_label,
Some(suggestion),
je)
})
.collect()
}
fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
match *rsp {
RenderSpan::FileLine(ref msp) |
RenderSpan::FullSpan(ref msp) => {
DiagnosticSpan::from_multispan(msp, je)
}
RenderSpan::Suggestion(ref suggestion) => {
DiagnosticSpan::from_suggestion(suggestion, je)
}
RenderSpan::EndSpan(ref msp) => {
msp.spans.iter().map(|&span| {
let end = je.cm.lookup_char_pos(span.hi);
DiagnosticSpan {
file_name: end.file.name.clone(),
byte_start: span.hi.0,
byte_end: span.hi.0,
line_start: end.line,
line_end: end.line,
column_start: end.col.0 + 1,
column_end: end.col.0 + 1,
text: DiagnosticSpanLine::from_span_end(span, je),
suggested_replacement: None,
expansion: None,
}
}).collect()
}
RenderSpan::FullSpan(ref msp) =>
DiagnosticSpan::from_multispan(msp, je),
RenderSpan::Suggestion(ref suggestion) =>
DiagnosticSpan::from_suggestion(suggestion, je),
}
}
}
@ -340,34 +334,6 @@ impl DiagnosticSpanLine {
})
.unwrap_or(vec![])
}
/// Create a list of DiagnosticSpanLines from span - the result covers all
/// of `span`, but the highlight is zero-length and at the end of `span`.
fn from_span_end(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
je.cm.span_to_lines(span)
.map(|lines| {
let fm = &*lines.file;
lines.lines.iter()
.enumerate()
.map(|(i, line)| {
// Invariant - CodeMap::span_to_lines
// will not return extra context lines
// - the last line returned is the last
// line of `span`.
let highlight = if i == lines.lines.len() - 1 {
(line.end_col.0 + 1, line.end_col.0 + 1)
} else {
(0, 0)
};
DiagnosticSpanLine::line_from_filemap(fm,
line.line_index,
highlight.0,
highlight.1)
})
.collect()
})
.unwrap_or(vec![])
}
}
impl DiagnosticCode {
@ -389,16 +355,12 @@ impl DiagnosticCode {
impl JsonEmitter {
fn render(&self, render_span: &RenderSpan) -> Option<String> {
match *render_span {
RenderSpan::FileLine(_) |
RenderSpan::FullSpan(_) => {
None
}
RenderSpan::Suggestion(ref suggestion) => {
Some(suggestion.splice_lines(&self.cm))
}
RenderSpan::EndSpan(_) => {
None
}
}
}
}

View File

@ -13,17 +13,19 @@ pub use errors::emitter::ColorConfig;
use self::Level::*;
use self::RenderSpan::*;
use codemap::{self, CodeMap, MultiSpan};
use codemap::{self, CodeMap, MultiSpan, NO_EXPANSION, Span};
use diagnostics;
use errors::emitter::{Emitter, EmitterWriter};
use std::cell::{RefCell, Cell};
use std::{error, fmt};
use std::rc::Rc;
use std::thread::panicking;
use term;
pub mod emitter;
pub mod json;
pub mod snippet;
#[derive(Clone)]
pub enum RenderSpan {
@ -32,22 +34,11 @@ pub enum RenderSpan {
/// the source code covered by the span.
FullSpan(MultiSpan),
/// Similar to a FullSpan, but the cited position is the end of
/// the span, instead of the start. Used, at least, for telling
/// compiletest/runtest to look at the last line of the span
/// (since `end_highlight_lines` displays an arrow to the end
/// of the span).
EndSpan(MultiSpan),
/// A suggestion renders with both with an initial line for the
/// message, prefixed by file:linenum, followed by a summary
/// of hypothetical source code, where each `String` is spliced
/// into the lines in place of the code covered by each span.
Suggestion(CodeSuggestion),
/// A FileLine renders with just a line for the message prefixed
/// by file:linenum.
FileLine(MultiSpan),
}
#[derive(Clone)]
@ -60,9 +51,7 @@ impl RenderSpan {
fn span(&self) -> &MultiSpan {
match *self {
FullSpan(ref msp) |
Suggestion(CodeSuggestion { ref msp, .. }) |
EndSpan(ref msp) |
FileLine(ref msp) =>
Suggestion(CodeSuggestion { ref msp, .. }) =>
msp
}
}
@ -88,12 +77,24 @@ impl CodeSuggestion {
}
}
}
let bounds = self.msp.to_span_bounds();
let lines = cm.span_to_lines(bounds).unwrap();
assert!(!lines.lines.is_empty());
// This isn't strictly necessary, but would in all likelyhood be an error
assert_eq!(self.msp.spans.len(), self.substitutes.len());
let mut primary_spans = self.msp.primary_spans().to_owned();
assert_eq!(primary_spans.len(), self.substitutes.len());
if primary_spans.is_empty() {
return format!("");
}
// Assumption: all spans are in the same file, and all spans
// are disjoint. Sort in ascending order.
primary_spans.sort_by_key(|sp| sp.lo);
// Find the bounding span.
let lo = primary_spans.iter().map(|sp| sp.lo).min().unwrap();
let hi = primary_spans.iter().map(|sp| sp.hi).min().unwrap();
let bounding_span = Span { lo: lo, hi: hi, expn_id: NO_EXPANSION };
let lines = cm.span_to_lines(bounding_span).unwrap();
assert!(!lines.lines.is_empty());
// To build up the result, we do this for each span:
// - push the line segment trailing the previous span
@ -105,13 +106,13 @@ impl CodeSuggestion {
//
// Finally push the trailing line segment of the last span
let fm = &lines.file;
let mut prev_hi = cm.lookup_char_pos(bounds.lo);
let mut prev_hi = cm.lookup_char_pos(bounding_span.lo);
prev_hi.col = CharPos::from_usize(0);
let mut prev_line = fm.get_line(lines.lines[0].line_index);
let mut buf = String::new();
for (sp, substitute) in self.msp.spans.iter().zip(self.substitutes.iter()) {
for (sp, substitute) in primary_spans.iter().zip(self.substitutes.iter()) {
let cur_lo = cm.lookup_char_pos(sp.lo);
if prev_hi.line == cur_lo.line {
push_trailing(&mut buf, prev_line, &prev_hi, Some(&cur_lo));
@ -183,7 +184,7 @@ pub struct DiagnosticBuilder<'a> {
level: Level,
message: String,
code: Option<String>,
span: Option<MultiSpan>,
span: MultiSpan,
children: Vec<SubDiagnostic>,
}
@ -192,7 +193,7 @@ pub struct DiagnosticBuilder<'a> {
struct SubDiagnostic {
level: Level,
message: String,
span: Option<MultiSpan>,
span: MultiSpan,
render_span: Option<RenderSpan>,
}
@ -228,37 +229,61 @@ impl<'a> DiagnosticBuilder<'a> {
self.level == Level::Fatal
}
pub fn note(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> {
self.sub(Level::Note, msg, None, None);
/// Add a span/label to be included in the resulting snippet.
/// This is pushed onto the `MultiSpan` that was created when the
/// diagnostic was first built. If you don't call this function at
/// all, and you just supplied a `Span` to create the diagnostic,
/// then the snippet will just include that `Span`, which is
/// called the primary span.
pub fn span_label(mut self, span: Span, label: &fmt::Display)
-> DiagnosticBuilder<'a> {
self.span.push_span_label(span, format!("{}", label));
self
}
pub fn note_expected_found(mut self,
label: &fmt::Display,
expected: &fmt::Display,
found: &fmt::Display)
-> DiagnosticBuilder<'a>
{
// For now, just attach these as notes
self.note(&format!("expected {} `{}`", label, expected));
self.note(&format!(" found {} `{}`", label, found));
self
}
pub fn note(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> {
self.sub(Level::Note, msg, MultiSpan::new(), None);
self
}
pub fn span_note<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Note, msg, Some(sp.into()), None);
self.sub(Level::Note, msg, sp.into(), None);
self
}
pub fn warn(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> {
self.sub(Level::Warning, msg, None, None);
self.sub(Level::Warning, msg, MultiSpan::new(), None);
self
}
pub fn span_warn<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Warning, msg, Some(sp.into()), None);
self.sub(Level::Warning, msg, sp.into(), None);
self
}
pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> {
self.sub(Level::Help, msg, None, None);
self.sub(Level::Help, msg, MultiSpan::new(), None);
self
}
pub fn span_help<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Help, msg, Some(sp.into()), None);
self.sub(Level::Help, msg, sp.into(), None);
self
}
/// Prints out a message with a suggested edit of the code.
@ -269,43 +294,15 @@ impl<'a> DiagnosticBuilder<'a> {
msg: &str,
suggestion: String)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Help, msg, None, Some(Suggestion(CodeSuggestion {
self.sub(Level::Help, msg, MultiSpan::new(), Some(Suggestion(CodeSuggestion {
msp: sp.into(),
substitutes: vec![suggestion],
})));
self
}
pub fn span_end_note<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Note, msg, None, Some(EndSpan(sp.into())));
self
}
pub fn fileline_warn<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Warning, msg, None, Some(FileLine(sp.into())));
self
}
pub fn fileline_note<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Note, msg, None, Some(FileLine(sp.into())));
self
}
pub fn fileline_help<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str)
-> &mut DiagnosticBuilder<'a> {
self.sub(Level::Help, msg, None, Some(FileLine(sp.into())));
self
}
pub fn span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
self.span = Some(sp.into());
pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
self.span = sp.into();
self
}
@ -324,7 +321,7 @@ impl<'a> DiagnosticBuilder<'a> {
level: level,
message: message.to_owned(),
code: None,
span: None,
span: MultiSpan::new(),
children: vec![],
}
}
@ -334,7 +331,7 @@ impl<'a> DiagnosticBuilder<'a> {
fn sub(&mut self,
level: Level,
message: &str,
span: Option<MultiSpan>,
span: MultiSpan,
render_span: Option<RenderSpan>) {
let sub = SubDiagnostic {
level: level,
@ -356,8 +353,11 @@ impl<'a> fmt::Debug for DiagnosticBuilder<'a> {
/// we emit a bug.
impl<'a> Drop for DiagnosticBuilder<'a> {
fn drop(&mut self) {
if !self.cancelled() {
self.emitter.borrow_mut().emit(None, "Error constructed but not emitted", None, Bug);
if !panicking() && !self.cancelled() {
self.emitter.borrow_mut().emit(&MultiSpan::new(),
"Error constructed but not emitted",
None,
Bug);
panic!();
}
}
@ -412,7 +412,7 @@ impl Handler {
msg: &str)
-> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
result.span(sp);
result.set_span(sp);
if !self.can_emit_warnings {
result.cancel();
}
@ -424,7 +424,7 @@ impl Handler {
code: &str)
-> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
result.span(sp);
result.set_span(sp);
result.code(code.to_owned());
if !self.can_emit_warnings {
result.cancel();
@ -444,7 +444,7 @@ impl Handler {
-> DiagnosticBuilder<'a> {
self.bump_err_count();
let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
result.span(sp);
result.set_span(sp);
result
}
pub fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
@ -454,7 +454,7 @@ impl Handler {
-> DiagnosticBuilder<'a> {
self.bump_err_count();
let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
result.span(sp);
result.set_span(sp);
result.code(code.to_owned());
result
}
@ -468,7 +468,7 @@ impl Handler {
-> DiagnosticBuilder<'a> {
self.bump_err_count();
let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
result.span(sp);
result.set_span(sp);
result
}
pub fn struct_span_fatal_with_code<'a, S: Into<MultiSpan>>(&'a self,
@ -478,7 +478,7 @@ impl Handler {
-> DiagnosticBuilder<'a> {
self.bump_err_count();
let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
result.span(sp);
result.set_span(sp);
result.code(code.to_owned());
result
}
@ -499,7 +499,7 @@ impl Handler {
if self.treat_err_as_bug {
self.span_bug(sp, msg);
}
self.emit(Some(&sp.into()), msg, Fatal);
self.emit(&sp.into(), msg, Fatal);
self.bump_err_count();
return FatalError;
}
@ -508,7 +508,7 @@ impl Handler {
if self.treat_err_as_bug {
self.span_bug(sp, msg);
}
self.emit_with_code(Some(&sp.into()), msg, code, Fatal);
self.emit_with_code(&sp.into(), msg, code, Fatal);
self.bump_err_count();
return FatalError;
}
@ -516,24 +516,24 @@ impl Handler {
if self.treat_err_as_bug {
self.span_bug(sp, msg);
}
self.emit(Some(&sp.into()), msg, Error);
self.emit(&sp.into(), msg, Error);
self.bump_err_count();
}
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
if self.treat_err_as_bug {
self.span_bug(sp, msg);
}
self.emit_with_code(Some(&sp.into()), msg, code, Error);
self.emit_with_code(&sp.into(), msg, code, Error);
self.bump_err_count();
}
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.emit(Some(&sp.into()), msg, Warning);
self.emit(&sp.into(), msg, Warning);
}
pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
self.emit_with_code(Some(&sp.into()), msg, code, Warning);
self.emit_with_code(&sp.into(), msg, code, Warning);
}
pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
self.emit(Some(&sp.into()), msg, Bug);
self.emit(&sp.into(), msg, Bug);
panic!(ExplicitBug);
}
pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
@ -541,11 +541,11 @@ impl Handler {
*delayed = Some((sp.into(), msg.to_string()));
}
pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.emit(Some(&sp.into()), msg, Bug);
self.emit(&sp.into(), msg, Bug);
self.bump_err_count();
}
pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.emit.borrow_mut().emit(Some(&sp.into()), msg, None, Note);
self.emit.borrow_mut().emit(&sp.into(), msg, None, Note);
}
pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
self.span_bug(sp, &format!("unimplemented {}", msg));
@ -554,7 +554,7 @@ impl Handler {
if self.treat_err_as_bug {
self.bug(msg);
}
self.emit.borrow_mut().emit(None, msg, None, Fatal);
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Fatal);
self.bump_err_count();
FatalError
}
@ -562,17 +562,17 @@ impl Handler {
if self.treat_err_as_bug {
self.bug(msg);
}
self.emit.borrow_mut().emit(None, msg, None, Error);
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Error);
self.bump_err_count();
}
pub fn warn(&self, msg: &str) {
self.emit.borrow_mut().emit(None, msg, None, Warning);
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Warning);
}
pub fn note_without_error(&self, msg: &str) {
self.emit.borrow_mut().emit(None, msg, None, Note);
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Note);
}
pub fn bug(&self, msg: &str) -> ! {
self.emit.borrow_mut().emit(None, msg, None, Bug);
self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Bug);
panic!(ExplicitBug);
}
pub fn unimpl(&self, msg: &str) -> ! {
@ -614,25 +614,20 @@ impl Handler {
panic!(self.fatal(&s));
}
pub fn emit(&self,
msp: Option<&MultiSpan>,
msp: &MultiSpan,
msg: &str,
lvl: Level) {
if lvl == Warning && !self.can_emit_warnings { return }
self.emit.borrow_mut().emit(msp, msg, None, lvl);
self.emit.borrow_mut().emit(&msp, msg, None, lvl);
if !self.continue_after_error.get() { self.abort_if_errors(); }
}
pub fn emit_with_code(&self,
msp: Option<&MultiSpan>,
msp: &MultiSpan,
msg: &str,
code: &str,
lvl: Level) {
if lvl == Warning && !self.can_emit_warnings { return }
self.emit.borrow_mut().emit(msp, msg, Some(code), lvl);
if !self.continue_after_error.get() { self.abort_if_errors(); }
}
pub fn custom_emit(&self, rsp: RenderSpan, msg: &str, lvl: Level) {
if lvl == Warning && !self.can_emit_warnings { return }
self.emit.borrow_mut().custom_emit(&rsp, msg, lvl);
self.emit.borrow_mut().emit(&msp, msg, Some(code), lvl);
if !self.continue_after_error.get() { self.abort_if_errors(); }
}
}
@ -662,7 +657,7 @@ impl Level {
fn color(self) -> term::color::Color {
match self {
Bug | Fatal | PhaseFatal | Error => term::color::BRIGHT_RED,
Warning => term::color::BRIGHT_YELLOW,
Warning => term::color::YELLOW,
Note => term::color::BRIGHT_GREEN,
Help => term::color::BRIGHT_CYAN,
Cancelled => unreachable!(),
@ -689,3 +684,20 @@ pub fn expect<T, M>(diag: &Handler, opt: Option<T>, msg: M) -> T where
None => diag.bug(&msg()),
}
}
/// True if we should use the old-skool error format style. This is
/// the default setting until the new errors are deemed stable enough
/// for general use.
///
/// FIXME(#33240)
#[cfg(not(test))]
fn check_old_skool() -> bool {
use std::env;
env::var("RUST_NEW_ERROR_FORMAT").is_err()
}
/// For unit tests, use the new format.
#[cfg(test)]
fn check_old_skool() -> bool {
false
}

View File

@ -0,0 +1,821 @@
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Code for annotating snippets.
use codemap::{CharPos, CodeMap, FileMap, LineInfo, Span};
use errors::check_old_skool;
use std::cmp;
use std::rc::Rc;
use std::mem;
mod test;
#[derive(Clone)]
pub struct SnippetData {
codemap: Rc<CodeMap>,
files: Vec<FileInfo>,
}
#[derive(Clone)]
pub struct FileInfo {
file: Rc<FileMap>,
/// The "primary file", if any, gets a `-->` marker instead of
/// `>>>`, and has a line-number/column printed and not just a
/// filename. It appears first in the listing. It is known to
/// contain at least one primary span, though primary spans (which
/// are designated with `^^^`) may also occur in other files.
primary_span: Option<Span>,
lines: Vec<Line>,
}
#[derive(Clone, Debug)]
struct Line {
line_index: usize,
annotations: Vec<Annotation>,
}
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
struct Annotation {
/// Start column, 0-based indexing -- counting *characters*, not
/// utf-8 bytes. Note that it is important that this field goes
/// first, so that when we sort, we sort orderings by start
/// column.
start_col: usize,
/// End column within the line (exclusive)
end_col: usize,
/// Is this annotation derived from primary span
is_primary: bool,
/// Optional label to display adjacent to the annotation.
label: Option<String>,
}
#[derive(Debug)]
pub struct RenderedLine {
pub text: Vec<StyledString>,
pub kind: RenderedLineKind,
}
#[derive(Debug)]
pub struct StyledString {
pub text: String,
pub style: Style,
}
#[derive(Debug)]
pub struct StyledBuffer {
text: Vec<Vec<char>>,
styles: Vec<Vec<Style>>
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Style {
FileNameStyle,
LineAndColumn,
LineNumber,
Quotation,
UnderlinePrimary,
UnderlineSecondary,
LabelPrimary,
LabelSecondary,
NoStyle,
}
#[derive(Debug, Clone)]
pub enum RenderedLineKind {
PrimaryFileName,
OtherFileName,
SourceText {
file: Rc<FileMap>,
line_index: usize,
},
Annotations,
Elision,
}
impl SnippetData {
pub fn new(codemap: Rc<CodeMap>,
primary_span: Option<Span>) // (*)
-> Self {
// (*) The primary span indicates the file that must appear
// first, and which will have a line number etc in its
// name. Outside of tests, this is always `Some`, but for many
// tests it's not relevant to test this portion of the logic,
// and it's tedious to pick a primary span (read: tedious to
// port older tests that predate the existence of a primary
// span).
debug!("SnippetData::new(primary_span={:?})", primary_span);
let mut data = SnippetData {
codemap: codemap.clone(),
files: vec![]
};
if let Some(primary_span) = primary_span {
let lo = codemap.lookup_char_pos(primary_span.lo);
data.files.push(
FileInfo {
file: lo.file,
primary_span: Some(primary_span),
lines: vec![],
});
}
data
}
pub fn push(&mut self, span: Span, is_primary: bool, label: Option<String>) {
debug!("SnippetData::push(span={:?}, is_primary={}, label={:?})",
span, is_primary, label);
let file_lines = match self.codemap.span_to_lines(span) {
Ok(file_lines) => file_lines,
Err(_) => {
// ignore unprintable spans completely.
return;
}
};
self.file(&file_lines.file)
.push_lines(&file_lines.lines, is_primary, label);
}
fn file(&mut self, file_map: &Rc<FileMap>) -> &mut FileInfo {
let index = self.files.iter().position(|f| f.file.name == file_map.name);
if let Some(index) = index {
return &mut self.files[index];
}
self.files.push(
FileInfo {
file: file_map.clone(),
lines: vec![],
primary_span: None,
});
self.files.last_mut().unwrap()
}
pub fn render_lines(&self) -> Vec<RenderedLine> {
debug!("SnippetData::render_lines()");
let mut rendered_lines: Vec<_> =
self.files.iter()
.flat_map(|f| f.render_file_lines(&self.codemap))
.collect();
prepend_prefixes(&mut rendered_lines);
trim_lines(&mut rendered_lines);
rendered_lines
}
}
pub trait StringSource {
fn make_string(self) -> String;
}
impl StringSource for String {
fn make_string(self) -> String {
self
}
}
impl StringSource for Vec<char> {
fn make_string(self) -> String {
self.into_iter().collect()
}
}
impl<S> From<(S, Style, RenderedLineKind)> for RenderedLine
where S: StringSource
{
fn from((text, style, kind): (S, Style, RenderedLineKind)) -> Self {
RenderedLine {
text: vec![StyledString {
text: text.make_string(),
style: style,
}],
kind: kind,
}
}
}
impl<S1,S2> From<(S1, Style, S2, Style, RenderedLineKind)> for RenderedLine
where S1: StringSource, S2: StringSource
{
fn from(tuple: (S1, Style, S2, Style, RenderedLineKind)) -> Self {
let (text1, style1, text2, style2, kind) = tuple;
RenderedLine {
text: vec![
StyledString {
text: text1.make_string(),
style: style1,
},
StyledString {
text: text2.make_string(),
style: style2,
}
],
kind: kind,
}
}
}
impl RenderedLine {
fn trim_last(&mut self) {
if let Some(last_text) = self.text.last_mut() {
let len = last_text.text.trim_right().len();
last_text.text.truncate(len);
}
}
}
impl RenderedLineKind {
fn prefix(&self) -> StyledString {
match *self {
RenderedLineKind::SourceText { file: _, line_index } =>
StyledString {
text: format!("{}", line_index + 1),
style: Style::LineNumber,
},
RenderedLineKind::Elision =>
StyledString {
text: String::from("..."),
style: Style::LineNumber,
},
RenderedLineKind::PrimaryFileName |
RenderedLineKind::OtherFileName |
RenderedLineKind::Annotations =>
StyledString {
text: String::from(""),
style: Style::LineNumber,
},
}
}
}
impl StyledBuffer {
fn new() -> StyledBuffer {
StyledBuffer { text: vec![], styles: vec![] }
}
fn render(&self, source_kind: RenderedLineKind) -> Vec<RenderedLine> {
let mut output: Vec<RenderedLine> = vec![];
let mut styled_vec: Vec<StyledString> = vec![];
for (row, row_style) in self.text.iter().zip(&self.styles) {
let mut current_style = Style::NoStyle;
let mut current_text = String::new();
for (&c, &s) in row.iter().zip(row_style) {
if s != current_style {
if !current_text.is_empty() {
styled_vec.push(StyledString { text: current_text, style: current_style });
}
current_style = s;
current_text = String::new();
}
current_text.push(c);
}
if !current_text.is_empty() {
styled_vec.push(StyledString { text: current_text, style: current_style });
}
if output.is_empty() {
//We know our first output line is source and the rest are highlights and labels
output.push(RenderedLine { text: styled_vec, kind: source_kind.clone() });
} else {
output.push(RenderedLine { text: styled_vec, kind: RenderedLineKind::Annotations });
}
styled_vec = vec![];
}
output
}
fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
while line >= self.text.len() {
self.text.push(vec![]);
self.styles.push(vec![]);
}
if col < self.text[line].len() {
self.text[line][col] = chr;
self.styles[line][col] = style;
} else {
while self.text[line].len() < col {
self.text[line].push(' ');
self.styles[line].push(Style::NoStyle);
}
self.text[line].push(chr);
self.styles[line].push(style);
}
}
fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
let mut n = col;
for c in string.chars() {
self.putc(line, n, c, style);
n += 1;
}
}
fn set_style(&mut self, line: usize, col: usize, style: Style) {
if self.styles.len() > line && self.styles[line].len() > col {
self.styles[line][col] = style;
}
}
fn append(&mut self, line: usize, string: &str, style: Style) {
if line >= self.text.len() {
self.puts(line, 0, string, style);
} else {
let col = self.text[line].len();
self.puts(line, col, string, style);
}
}
}
impl FileInfo {
fn push_lines(&mut self,
lines: &[LineInfo],
is_primary: bool,
label: Option<String>) {
assert!(lines.len() > 0);
// If a span covers multiple lines, we reduce it to a single
// point at the start of the span. This means that instead
// of producing output like this:
//
// ```
// --> foo.rs:2:1
// 2 |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>)
// |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// 3 |> -> Set<LR0Item<'grammar>>
// |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// (and so on)
// ```
//
// we produce:
//
// ```
// --> foo.rs:2:1
// 2 |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>)
// ^
// ```
//
// Basically, although this loses information, multi-line spans just
// never look good.
let (line, start_col, end_col) = if lines.len() == 1 {
(lines[0].line_index, lines[0].start_col, lines[0].end_col)
} else {
(lines[0].line_index, lines[0].start_col, CharPos(lines[0].start_col.0 + 1))
};
let index = self.ensure_source_line(line);
self.lines[index].push_annotation(start_col,
end_col,
is_primary,
label);
}
/// Ensure that we have a `Line` struct corresponding to
/// `line_index` in the file. If we already have some other lines,
/// then this will add the intervening lines to ensure that we
/// have a complete snippet. (Note that when we finally display,
/// some of those lines may be elided.)
fn ensure_source_line(&mut self, line_index: usize) -> usize {
if self.lines.is_empty() {
self.lines.push(Line::new(line_index));
return 0;
}
// Find the range of lines we have thus far.
let first_line_index = self.lines.first().unwrap().line_index;
let last_line_index = self.lines.last().unwrap().line_index;
assert!(first_line_index <= last_line_index);
// If the new line is lower than all the lines we have thus
// far, then insert the new line and any intervening lines at
// the front. In a silly attempt at micro-optimization, we
// don't just call `insert` repeatedly, but instead make a new
// (empty) vector, pushing the new lines onto it, and then
// appending the old vector.
if line_index < first_line_index {
let lines = mem::replace(&mut self.lines, vec![]);
self.lines.extend(
(line_index .. first_line_index)
.map(|line| Line::new(line))
.chain(lines));
return 0;
}
// If the new line comes after the ones we have so far, insert
// lines for it.
if line_index > last_line_index {
self.lines.extend(
(last_line_index+1 .. line_index+1)
.map(|line| Line::new(line)));
return self.lines.len() - 1;
}
// Otherwise it should already exist.
return line_index - first_line_index;
}
fn render_file_lines(&self, codemap: &Rc<CodeMap>) -> Vec<RenderedLine> {
let old_school = check_old_skool();
// As a first step, we elide any instance of more than one
// continuous unannotated line.
let mut lines_iter = self.lines.iter();
let mut output = vec![];
// First insert the name of the file.
if !old_school {
match self.primary_span {
Some(span) => {
let lo = codemap.lookup_char_pos(span.lo);
output.push(RenderedLine {
text: vec![StyledString {
text: lo.file.name.clone(),
style: Style::FileNameStyle,
}, StyledString {
text: format!(":{}:{}", lo.line, lo.col.0 + 1),
style: Style::LineAndColumn,
}],
kind: RenderedLineKind::PrimaryFileName,
});
}
None => {
output.push(RenderedLine {
text: vec![StyledString {
text: self.file.name.clone(),
style: Style::FileNameStyle,
}],
kind: RenderedLineKind::OtherFileName,
});
}
}
}
let mut next_line = lines_iter.next();
while next_line.is_some() {
// Consume lines with annotations.
while let Some(line) = next_line {
if line.annotations.is_empty() { break; }
let mut rendered_lines = self.render_line(line);
assert!(!rendered_lines.is_empty());
if old_school {
match self.primary_span {
Some(span) => {
let lo = codemap.lookup_char_pos(span.lo);
rendered_lines[0].text.insert(0, StyledString {
text: format!(":{} ", lo.line),
style: Style::LineAndColumn,
});
rendered_lines[0].text.insert(0, StyledString {
text: lo.file.name.clone(),
style: Style::FileNameStyle,
});
let gap_amount =
rendered_lines[0].text[0].text.len() +
rendered_lines[0].text[1].text.len();
assert!(rendered_lines.len() >= 2,
"no annotations resulted from: {:?}",
line);
for i in 1..rendered_lines.len() {
rendered_lines[i].text.insert(0, StyledString {
text: vec![" "; gap_amount].join(""),
style: Style::NoStyle
});
}
}
_ =>()
}
}
output.append(&mut rendered_lines);
next_line = lines_iter.next();
}
// Emit lines without annotations, but only if they are
// followed by a line with an annotation.
let unannotated_line = next_line;
let mut unannotated_lines = 0;
while let Some(line) = next_line {
if !line.annotations.is_empty() { break; }
unannotated_lines += 1;
next_line = lines_iter.next();
}
if unannotated_lines > 1 {
output.push(RenderedLine::from((String::new(),
Style::NoStyle,
RenderedLineKind::Elision)));
} else if let Some(line) = unannotated_line {
output.append(&mut self.render_line(line));
}
}
output
}
fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
let old_school = check_old_skool();
let source_string = self.file.get_line(line.line_index)
.unwrap_or("");
let source_kind = RenderedLineKind::SourceText {
file: self.file.clone(),
line_index: line.line_index,
};
let mut styled_buffer = StyledBuffer::new();
// First create the source line we will highlight.
styled_buffer.append(0, &source_string, Style::Quotation);
if line.annotations.is_empty() {
return styled_buffer.render(source_kind);
}
// We want to display like this:
//
// vec.push(vec.pop().unwrap());
// --- ^^^ _ previous borrow ends here
// | |
// | error occurs here
// previous borrow of `vec` occurs here
//
// But there are some weird edge cases to be aware of:
//
// vec.push(vec.pop().unwrap());
// -------- - previous borrow ends here
// ||
// |this makes no sense
// previous borrow of `vec` occurs here
//
// For this reason, we group the lines into "highlight lines"
// and "annotations lines", where the highlight lines have the `~`.
//let mut highlight_line = Self::whitespace(&source_string);
// Sort the annotations by (start, end col)
let mut annotations = line.annotations.clone();
annotations.sort();
// Next, create the highlight line.
for annotation in &annotations {
if old_school {
for p in annotation.start_col .. annotation.end_col {
if p == annotation.start_col {
styled_buffer.putc(1, p, '^',
if annotation.is_primary {
Style::UnderlinePrimary
} else {
Style::UnderlineSecondary
});
}
else {
styled_buffer.putc(1, p, '~',
if annotation.is_primary {
Style::UnderlinePrimary
} else {
Style::UnderlineSecondary
});
}
}
}
else {
for p in annotation.start_col .. annotation.end_col {
if annotation.is_primary {
styled_buffer.putc(1, p, '^', Style::UnderlinePrimary);
styled_buffer.set_style(0, p, Style::UnderlinePrimary);
} else {
styled_buffer.putc(1, p, '-', Style::UnderlineSecondary);
}
}
}
}
// Now we are going to write labels in. To start, we'll exclude
// the annotations with no labels.
let (labeled_annotations, unlabeled_annotations): (Vec<_>, _) =
annotations.into_iter()
.partition(|a| a.label.is_some());
// If there are no annotations that need text, we're done.
if labeled_annotations.is_empty() {
return styled_buffer.render(source_kind);
}
if old_school {
return styled_buffer.render(source_kind);
}
// Now add the text labels. We try, when possible, to stick the rightmost
// annotation at the end of the highlight line:
//
// vec.push(vec.pop().unwrap());
// --- --- - previous borrow ends here
//
// But sometimes that's not possible because one of the other
// annotations overlaps it. For example, from the test
// `span_overlap_label`, we have the following annotations
// (written on distinct lines for clarity):
//
// fn foo(x: u32) {
// --------------
// -
//
// In this case, we can't stick the rightmost-most label on
// the highlight line, or we would get:
//
// fn foo(x: u32) {
// -------- x_span
// |
// fn_span
//
// which is totally weird. Instead we want:
//
// fn foo(x: u32) {
// --------------
// | |
// | x_span
// fn_span
//
// which is...less weird, at least. In fact, in general, if
// the rightmost span overlaps with any other span, we should
// use the "hang below" version, so we can at least make it
// clear where the span *starts*.
let mut labeled_annotations = &labeled_annotations[..];
match labeled_annotations.split_last().unwrap() {
(last, previous) => {
if previous.iter()
.chain(&unlabeled_annotations)
.all(|a| !overlaps(a, last))
{
// append the label afterwards; we keep it in a separate
// string
let highlight_label: String = format!(" {}", last.label.as_ref().unwrap());
if last.is_primary {
styled_buffer.append(1, &highlight_label, Style::LabelPrimary);
} else {
styled_buffer.append(1, &highlight_label, Style::LabelSecondary);
}
labeled_annotations = previous;
}
}
}
// If that's the last annotation, we're done
if labeled_annotations.is_empty() {
return styled_buffer.render(source_kind);
}
for (index, annotation) in labeled_annotations.iter().enumerate() {
// Leave:
// - 1 extra line
// - One line for each thing that comes after
let comes_after = labeled_annotations.len() - index - 1;
let blank_lines = 3 + comes_after;
// For each blank line, draw a `|` at our column. The
// text ought to be long enough for this.
for index in 2..blank_lines {
if annotation.is_primary {
styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlinePrimary);
} else {
styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlineSecondary);
}
}
if annotation.is_primary {
styled_buffer.puts(blank_lines, annotation.start_col,
annotation.label.as_ref().unwrap(), Style::LabelPrimary);
} else {
styled_buffer.puts(blank_lines, annotation.start_col,
annotation.label.as_ref().unwrap(), Style::LabelSecondary);
}
}
styled_buffer.render(source_kind)
}
}
fn prepend_prefixes(rendered_lines: &mut [RenderedLine]) {
let old_school = check_old_skool();
if old_school {
return;
}
let prefixes: Vec<_> =
rendered_lines.iter()
.map(|rl| rl.kind.prefix())
.collect();
// find the max amount of spacing we need; add 1 to
// p.text.len() to leave space between the prefix and the
// source text
let padding_len =
prefixes.iter()
.map(|p| if p.text.len() == 0 { 0 } else { p.text.len() + 1 })
.max()
.unwrap_or(0);
// Ensure we insert at least one character of padding, so that the
// `-->` arrows can fit etc.
let padding_len = cmp::max(padding_len, 1);
for (mut prefix, line) in prefixes.into_iter().zip(rendered_lines) {
let extra_spaces = (prefix.text.len() .. padding_len).map(|_| ' ');
prefix.text.extend(extra_spaces);
match line.kind {
RenderedLineKind::Elision => {
line.text.insert(0, prefix);
}
RenderedLineKind::PrimaryFileName => {
// --> filename
// 22 |>
// ^
// padding_len
let dashes = (0..padding_len - 1).map(|_| ' ')
.chain(Some('-'))
.chain(Some('-'))
.chain(Some('>'))
.chain(Some(' '));
line.text.insert(0, StyledString {text: dashes.collect(),
style: Style::LineNumber})
}
RenderedLineKind::OtherFileName => {
// ::: filename
// 22 |>
// ^
// padding_len
let dashes = (0..padding_len - 1).map(|_| ' ')
.chain(Some(':'))
.chain(Some(':'))
.chain(Some(':'))
.chain(Some(' '));
line.text.insert(0, StyledString {text: dashes.collect(),
style: Style::LineNumber})
}
_ => {
line.text.insert(0, prefix);
line.text.insert(1, StyledString {text: String::from("|> "),
style: Style::LineNumber})
}
}
}
}
fn trim_lines(rendered_lines: &mut [RenderedLine]) {
for line in rendered_lines {
while !line.text.is_empty() {
line.trim_last();
if line.text.last().unwrap().text.is_empty() {
line.text.pop();
} else {
break;
}
}
}
}
impl Line {
fn new(line_index: usize) -> Line {
Line {
line_index: line_index,
annotations: vec![]
}
}
fn push_annotation(&mut self,
start: CharPos,
end: CharPos,
is_primary: bool,
label: Option<String>) {
self.annotations.push(Annotation {
start_col: start.0,
end_col: end.0,
is_primary: is_primary,
label: label,
});
}
}
fn overlaps(a1: &Annotation,
a2: &Annotation)
-> bool
{
(a2.start_col .. a2.end_col).contains(a1.start_col) ||
(a1.start_col .. a1.end_col).contains(a2.start_col)
}

View File

@ -0,0 +1,521 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Code for testing annotated snippets.
#![cfg(test)]
use codemap::{BytePos, CodeMap, FileMap, NO_EXPANSION, Span};
use std::rc::Rc;
use super::{RenderedLine, SnippetData};
/// Returns the span corresponding to the `n`th occurrence of
/// `substring` in `source_text`.
trait CodeMapExtension {
fn span_substr(&self,
file: &Rc<FileMap>,
source_text: &str,
substring: &str,
n: usize)
-> Span;
}
impl CodeMapExtension for CodeMap {
fn span_substr(&self,
file: &Rc<FileMap>,
source_text: &str,
substring: &str,
n: usize)
-> Span
{
println!("span_substr(file={:?}/{:?}, substring={:?}, n={})",
file.name, file.start_pos, substring, n);
let mut i = 0;
let mut hi = 0;
loop {
let offset = source_text[hi..].find(substring).unwrap_or_else(|| {
panic!("source_text `{}` does not have {} occurrences of `{}`, only {}",
source_text, n, substring, i);
});
let lo = hi + offset;
hi = lo + substring.len();
if i == n {
let span = Span {
lo: BytePos(lo as u32 + file.start_pos.0),
hi: BytePos(hi as u32 + file.start_pos.0),
expn_id: NO_EXPANSION,
};
assert_eq!(&self.span_to_snippet(span).unwrap()[..],
substring);
return span;
}
i += 1;
}
}
}
fn splice(start: Span, end: Span) -> Span {
Span {
lo: start.lo,
hi: end.hi,
expn_id: NO_EXPANSION,
}
}
fn make_string(lines: &[RenderedLine]) -> String {
lines.iter()
.flat_map(|rl| {
rl.text.iter()
.map(|s| &s.text[..])
.chain(Some("\n"))
})
.collect()
}
#[test]
fn one_line() {
let file_text = r#"
fn foo() {
vec.push(vec.pop().unwrap());
}
"#;
let cm = Rc::new(CodeMap::new());
let foo = cm.new_filemap_and_lines("foo.rs", file_text);
let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
let span_semi = cm.span_substr(&foo, file_text, ";", 0);
let mut snippet = SnippetData::new(cm, None);
snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
snippet.push(span_vec1, false, Some(format!("error occurs here")));
snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
let lines = snippet.render_lines();
println!("{:#?}", lines);
let text: String = make_string(&lines);
println!("text=\n{}", text);
assert_eq!(&text[..], &r#"
::: foo.rs
3 |> vec.push(vec.pop().unwrap());
|> --- --- - previous borrow ends here
|> | |
|> | error occurs here
|> previous borrow of `vec` occurs here
"#[1..]);
}
#[test]
fn two_files() {
let file_text_foo = r#"
fn foo() {
vec.push(vec.pop().unwrap());
}
"#;
let file_text_bar = r#"
fn bar() {
// these blank links here
// serve to ensure that the line numbers
// from bar.rs
// require more digits
vec.push();
// this line will get elided
vec.pop().unwrap());
}
"#;
let cm = Rc::new(CodeMap::new());
let foo_map = cm.new_filemap_and_lines("foo.rs", file_text_foo);
let span_foo_vec0 = cm.span_substr(&foo_map, file_text_foo, "vec", 0);
let span_foo_vec1 = cm.span_substr(&foo_map, file_text_foo, "vec", 1);
let span_foo_semi = cm.span_substr(&foo_map, file_text_foo, ";", 0);
let bar_map = cm.new_filemap_and_lines("bar.rs", file_text_bar);
let span_bar_vec0 = cm.span_substr(&bar_map, file_text_bar, "vec", 0);
let span_bar_vec1 = cm.span_substr(&bar_map, file_text_bar, "vec", 1);
let span_bar_semi = cm.span_substr(&bar_map, file_text_bar, ";", 0);
let mut snippet = SnippetData::new(cm, Some(span_foo_vec1));
snippet.push(span_foo_vec0, false, Some(format!("a")));
snippet.push(span_foo_vec1, true, Some(format!("b")));
snippet.push(span_foo_semi, false, Some(format!("c")));
snippet.push(span_bar_vec0, false, Some(format!("d")));
snippet.push(span_bar_vec1, false, Some(format!("e")));
snippet.push(span_bar_semi, false, Some(format!("f")));
let lines = snippet.render_lines();
println!("{:#?}", lines);
let text: String = make_string(&lines);
println!("text=\n{}", text);
// Note that the `|>` remain aligned across both files:
assert_eq!(&text[..], &r#"
--> foo.rs:3:14
3 |> vec.push(vec.pop().unwrap());
|> --- ^^^ - c
|> | |
|> | b
|> a
::: bar.rs
17 |> vec.push();
|> --- - f
|> |
|> d
...
21 |> vec.pop().unwrap());
|> --- e
"#[1..]);
}
#[test]
fn multi_line() {
let file_text = r#"
fn foo() {
let name = find_id(&data, 22).unwrap();
// Add one more item we forgot to the vector. Silly us.
data.push(Data { name: format!("Hera"), id: 66 });
// Print everything out.
println!("Name: {:?}", name);
println!("Data: {:?}", data);
}
"#;
let cm = Rc::new(CodeMap::new());
let foo = cm.new_filemap_and_lines("foo.rs", file_text);
let span_data0 = cm.span_substr(&foo, file_text, "data", 0);
let span_data1 = cm.span_substr(&foo, file_text, "data", 1);
let span_rbrace = cm.span_substr(&foo, file_text, "}", 3);
let mut snippet = SnippetData::new(cm, None);
snippet.push(span_data0, false, Some(format!("immutable borrow begins here")));
snippet.push(span_data1, false, Some(format!("mutable borrow occurs here")));
snippet.push(span_rbrace, false, Some(format!("immutable borrow ends here")));
let lines = snippet.render_lines();
println!("{:#?}", lines);
let text: String = make_string(&lines);
println!("text=\n{}", text);
assert_eq!(&text[..], &r#"
::: foo.rs
3 |> let name = find_id(&data, 22).unwrap();
|> ---- immutable borrow begins here
...
6 |> data.push(Data { name: format!("Hera"), id: 66 });
|> ---- mutable borrow occurs here
...
11 |> }
|> - immutable borrow ends here
"#[1..]);
}
#[test]
fn overlapping() {
let file_text = r#"
fn foo() {
vec.push(vec.pop().unwrap());
}
"#;
let cm = Rc::new(CodeMap::new());
let foo = cm.new_filemap_and_lines("foo.rs", file_text);
let span0 = cm.span_substr(&foo, file_text, "vec.push", 0);
let span1 = cm.span_substr(&foo, file_text, "vec", 0);
let span2 = cm.span_substr(&foo, file_text, "ec.push", 0);
let span3 = cm.span_substr(&foo, file_text, "unwrap", 0);
let mut snippet = SnippetData::new(cm, None);
snippet.push(span0, false, Some(format!("A")));
snippet.push(span1, false, Some(format!("B")));
snippet.push(span2, false, Some(format!("C")));
snippet.push(span3, false, Some(format!("D")));
let lines = snippet.render_lines();
println!("{:#?}", lines);
let text: String = make_string(&lines);
println!("text=r#\"\n{}\".trim_left()", text);
assert_eq!(&text[..], &r#"
::: foo.rs
3 |> vec.push(vec.pop().unwrap());
|> -------- ------ D
|> ||
|> |C
|> A
|> B
"#[1..]);
}
#[test]
fn one_line_out_of_order() {
let file_text = r#"
fn foo() {
vec.push(vec.pop().unwrap());
}
"#;
let cm = Rc::new(CodeMap::new());
let foo = cm.new_filemap_and_lines("foo.rs", file_text);
let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
let span_semi = cm.span_substr(&foo, file_text, ";", 0);
// intentionally don't push the snippets left to right
let mut snippet = SnippetData::new(cm, None);
snippet.push(span_vec1, false, Some(format!("error occurs here")));
snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
let lines = snippet.render_lines();
println!("{:#?}", lines);
let text: String = make_string(&lines);
println!("text=r#\"\n{}\".trim_left()", text);
assert_eq!(&text[..], &r#"
::: foo.rs
3 |> vec.push(vec.pop().unwrap());
|> --- --- - previous borrow ends here
|> | |
|> | error occurs here
|> previous borrow of `vec` occurs here
"#[1..]);
}
#[test]
fn elide_unnecessary_lines() {
let file_text = r#"
fn foo() {
let mut vec = vec![0, 1, 2];
let mut vec2 = vec;
vec2.push(3);
vec2.push(4);
vec2.push(5);
vec2.push(6);
vec.push(7);
}
"#;
let cm = Rc::new(CodeMap::new());
let foo = cm.new_filemap_and_lines("foo.rs", file_text);
let span_vec0 = cm.span_substr(&foo, file_text, "vec", 3);
let span_vec1 = cm.span_substr(&foo, file_text, "vec", 8);
let mut snippet = SnippetData::new(cm, None);
snippet.push(span_vec0, false, Some(format!("`vec` moved here because it \
has type `collections::vec::Vec<i32>`")));
snippet.push(span_vec1, false, Some(format!("use of moved value: `vec`")));
let lines = snippet.render_lines();
println!("{:#?}", lines);
let text: String = make_string(&lines);
println!("text=r#\"\n{}\".trim_left()", text);
assert_eq!(&text[..], &r#"
::: foo.rs
4 |> let mut vec2 = vec;
|> --- `vec` moved here because it has type `collections::vec::Vec<i32>`
...
9 |> vec.push(7);
|> --- use of moved value: `vec`
"#[1..]);
}
#[test]
fn spans_without_labels() {
let file_text = r#"
fn foo() {
let mut vec = vec![0, 1, 2];
let mut vec2 = vec;
vec2.push(3);
vec2.push(4);
vec2.push(5);
vec2.push(6);
vec.push(7);
}
"#;
let cm = Rc::new(CodeMap::new());
let foo = cm.new_filemap_and_lines("foo.rs", file_text);
let mut snippet = SnippetData::new(cm.clone(), None);
for i in 0..4 {
let span_veci = cm.span_substr(&foo, file_text, "vec", i);
snippet.push(span_veci, false, None);
}
let lines = snippet.render_lines();
let text: String = make_string(&lines);
println!("text=&r#\"\n{}\n\"#[1..]", text);
assert_eq!(text, &r#"
::: foo.rs
3 |> let mut vec = vec![0, 1, 2];
|> --- ---
4 |> let mut vec2 = vec;
|> --- ---
"#[1..]);
}
#[test]
fn span_long_selection() {
let file_text = r#"
impl SomeTrait for () {
fn foo(x: u32) {
// impl 1
// impl 2
// impl 3
}
}
"#;
let cm = Rc::new(CodeMap::new());
let foo = cm.new_filemap_and_lines("foo.rs", file_text);
let mut snippet = SnippetData::new(cm.clone(), None);
let fn_span = cm.span_substr(&foo, file_text, "fn", 0);
let rbrace_span = cm.span_substr(&foo, file_text, "}", 0);
snippet.push(splice(fn_span, rbrace_span), false, None);
let lines = snippet.render_lines();
let text: String = make_string(&lines);
println!("r#\"\n{}\"", text);
assert_eq!(text, &r#"
::: foo.rs
3 |> fn foo(x: u32) {
|> -
"#[1..]);
}
#[test]
fn span_overlap_label() {
// Test that we don't put `x_span` to the right of its highlight,
// since there is another highlight that overlaps it.
let file_text = r#"
fn foo(x: u32) {
}
}
"#;
let cm = Rc::new(CodeMap::new());
let foo = cm.new_filemap_and_lines("foo.rs", file_text);
let mut snippet = SnippetData::new(cm.clone(), None);
let fn_span = cm.span_substr(&foo, file_text, "fn foo(x: u32)", 0);
let x_span = cm.span_substr(&foo, file_text, "x", 0);
snippet.push(fn_span, false, Some(format!("fn_span")));
snippet.push(x_span, false, Some(format!("x_span")));
let lines = snippet.render_lines();
let text: String = make_string(&lines);
println!("r#\"\n{}\"", text);
assert_eq!(text, &r#"
::: foo.rs
2 |> fn foo(x: u32) {
|> --------------
|> | |
|> | x_span
|> fn_span
"#[1..]);
}
#[test]
fn span_overlap_label2() {
// Test that we don't put `x_span` to the right of its highlight,
// since there is another highlight that overlaps it. In this
// case, the overlap is only at the beginning, but it's still
// better to show the beginning more clearly.
let file_text = r#"
fn foo(x: u32) {
}
}
"#;
let cm = Rc::new(CodeMap::new());
let foo = cm.new_filemap_and_lines("foo.rs", file_text);
let mut snippet = SnippetData::new(cm.clone(), None);
let fn_span = cm.span_substr(&foo, file_text, "fn foo(x", 0);
let x_span = cm.span_substr(&foo, file_text, "x: u32)", 0);
snippet.push(fn_span, false, Some(format!("fn_span")));
snippet.push(x_span, false, Some(format!("x_span")));
let lines = snippet.render_lines();
let text: String = make_string(&lines);
println!("r#\"\n{}\"", text);
assert_eq!(text, &r#"
::: foo.rs
2 |> fn foo(x: u32) {
|> --------------
|> | |
|> | x_span
|> fn_span
"#[1..]);
}
#[test]
fn span_overlap_label3() {
// Test that we don't put `x_span` to the right of its highlight,
// since there is another highlight that overlaps it. In this
// case, the overlap is only at the beginning, but it's still
// better to show the beginning more clearly.
let file_text = r#"
fn foo() {
let closure = || {
inner
};
}
}
"#;
let cm = Rc::new(CodeMap::new());
let foo = cm.new_filemap_and_lines("foo.rs", file_text);
let mut snippet = SnippetData::new(cm.clone(), None);
let closure_span = {
let closure_start_span = cm.span_substr(&foo, file_text, "||", 0);
let closure_end_span = cm.span_substr(&foo, file_text, "}", 0);
splice(closure_start_span, closure_end_span)
};
let inner_span = cm.span_substr(&foo, file_text, "inner", 0);
snippet.push(closure_span, false, Some(format!("foo")));
snippet.push(inner_span, false, Some(format!("bar")));
let lines = snippet.render_lines();
let text: String = make_string(&lines);
println!("r#\"\n{}\"", text);
assert_eq!(text, &r#"
::: foo.rs
3 |> let closure = || {
|> - foo
4 |> inner
|> ----- bar
"#[1..]);
}

View File

@ -764,15 +764,14 @@ impl<'a> ExtCtxt<'a> {
pub fn suggest_macro_name(&mut self,
name: &str,
span: Span,
err: &mut DiagnosticBuilder<'a>) {
let names = &self.syntax_env.names;
if let Some(suggestion) = find_best_match_for_name(names.iter(), name, None) {
if suggestion != name {
err.fileline_help(span, &format!("did you mean `{}!`?", suggestion));
err.help(&format!("did you mean `{}!`?", suggestion));
} else {
err.fileline_help(span, &format!("have you added the `#[macro_use]` on the \
module/import?"));
err.help(&format!("have you added the `#[macro_use]` on the \
module/import?"));
}
}
}

View File

@ -222,7 +222,7 @@ fn expand_mac_invoc<T, F, G>(mac: ast::Mac,
pth.span,
&format!("macro undefined: '{}!'",
&extname));
fld.cx.suggest_macro_name(&extname.as_str(), pth.span, &mut err);
fld.cx.suggest_macro_name(&extname.as_str(), &mut err);
err.emit();
// let compilation continue
@ -355,8 +355,8 @@ fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool
"macro_escape is a deprecated synonym for macro_use");
is_use = true;
if let ast::AttrStyle::Inner = attr.node.style {
err.fileline_help(attr.span, "consider an outer attribute, \
#[macro_use] mod ...").emit();
err.help("consider an outer attribute, \
#[macro_use] mod ...").emit();
} else {
err.emit();
}

View File

@ -770,9 +770,9 @@ pub fn emit_feature_err(diag: &Handler, feature: &str, span: Span, issue: GateIs
err.emit();
return;
}
err.fileline_help(span, &format!("add #![feature({})] to the \
crate attributes to enable",
feature));
err.help(&format!("add #![feature({})] to the \
crate attributes to enable",
feature));
err.emit();
}

View File

@ -33,6 +33,7 @@
#![feature(str_escape)]
#![feature(unicode)]
#![feature(question_mark)]
#![feature(range_contains)]
extern crate serialize;
extern crate term;

View File

@ -69,9 +69,8 @@ impl<'a> Parser<'a> {
self.diagnostic()
.struct_span_err(span,
"an inner attribute is not permitted in this context")
.fileline_help(span,
"place inner attribute at the top of the module or \
block")
.help("place inner attribute at the top of the module or \
block")
.emit()
}
ast::AttrStyle::Inner

View File

@ -445,11 +445,11 @@ fn filtered_float_lit(data: token::InternedString, suffix: Option<&str>,
if suf.len() >= 2 && looks_like_width_suffix(&['f'], suf) {
// if it looks like a width, lets try to be helpful.
sd.struct_span_err(sp, &format!("invalid width `{}` for float literal", &suf[1..]))
.fileline_help(sp, "valid widths are 32 and 64")
.help("valid widths are 32 and 64")
.emit();
} else {
sd.struct_span_err(sp, &format!("invalid suffix `{}` for float literal", suf))
.fileline_help(sp, "valid suffixes are `f32` and `f64`")
.help("valid suffixes are `f32` and `f64`")
.emit();
}
@ -621,12 +621,12 @@ pub fn integer_lit(s: &str,
if looks_like_width_suffix(&['i', 'u'], suf) {
sd.struct_span_err(sp, &format!("invalid width `{}` for integer literal",
&suf[1..]))
.fileline_help(sp, "valid widths are 8, 16, 32 and 64")
.help("valid widths are 8, 16, 32 and 64")
.emit();
} else {
sd.struct_span_err(sp, &format!("invalid suffix `{}` for numeric literal", suf))
.fileline_help(sp, "the suffix must be one of the integral types \
(`u32`, `isize`, etc)")
.help("the suffix must be one of the integral types \
(`u32`, `isize`, etc)")
.emit();
}

View File

@ -583,7 +583,7 @@ impl<'a> Parser<'a> {
let mut err = self.fatal(&format!("expected identifier, found `{}`",
self.this_token_to_string()));
if self.token == token::Underscore {
err.fileline_note(self.span, "`_` is a wildcard pattern, not an identifier");
err.note("`_` is a wildcard pattern, not an identifier");
}
Err(err)
}
@ -1082,7 +1082,7 @@ impl<'a> Parser<'a> {
}
pub fn span_fatal_help(&self, sp: Span, m: &str, help: &str) -> DiagnosticBuilder<'a> {
let mut err = self.sess.span_diagnostic.struct_span_fatal(sp, m);
err.fileline_help(sp, help);
err.help(help);
err
}
pub fn bug(&self, m: &str) -> ! {
@ -2622,10 +2622,9 @@ impl<'a> Parser<'a> {
Some(f) => f,
None => continue,
};
err.fileline_help(last_span,
&format!("try parenthesizing the first index; e.g., `(foo.{}){}`",
float.trunc() as usize,
format!(".{}", fstr.splitn(2, ".").last().unwrap())));
err.help(&format!("try parenthesizing the first index; e.g., `(foo.{}){}`",
float.trunc() as usize,
format!(".{}", fstr.splitn(2, ".").last().unwrap())));
}
return Err(err);
@ -3134,7 +3133,7 @@ impl<'a> Parser<'a> {
let mut err = self.diagnostic().struct_span_err(op_span,
"chained comparison operators require parentheses");
if op.node == BinOpKind::Lt && *outer_op == AssocOp::Greater {
err.fileline_help(op_span,
err.help(
"use `::<...>` instead of `<...>` if you meant to specify type arguments");
}
err.emit();
@ -4951,13 +4950,13 @@ impl<'a> Parser<'a> {
if is_macro_rules {
self.diagnostic().struct_span_err(span, "can't qualify macro_rules \
invocation with `pub`")
.fileline_help(span, "did you mean #[macro_export]?")
.help("did you mean #[macro_export]?")
.emit();
} else {
self.diagnostic().struct_span_err(span, "can't qualify macro \
invocation with `pub`")
.fileline_help(span, "try adjusting the macro to put `pub` \
inside the invocation")
.help("try adjusting the macro to put `pub` \
inside the invocation")
.emit();
}
}
@ -5857,7 +5856,7 @@ impl<'a> Parser<'a> {
if self.eat_keyword(keywords::Mut) {
let last_span = self.last_span;
self.diagnostic().struct_span_err(last_span, "const globals cannot be mutable")
.fileline_help(last_span, "did you mean to declare a static?")
.help("did you mean to declare a static?")
.emit();
}
let (ident, item_, extra_attrs) = self.parse_item_const(None)?;

View File

@ -204,9 +204,9 @@ macro_rules! derive_traits {
sp, feature_gate::EXPLAIN_DERIVE_UNDERSCORE,
);
if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_none() {
w.fileline_help(
sp, &format!("add #![feature(custom_derive)] to \
the crate attributes to enable")
w.help(
&format!("add #![feature(custom_derive)] to \
the crate attributes to enable")
);
}
w.emit();

View File

@ -11,16 +11,14 @@
fn main() {
let _x: i32 = [1, 2, 3];
//~^ ERROR mismatched types
//~| expected `i32`
//~| found `[_; 3]`
//~| expected i32
//~| found array of 3 elements
//~| expected type `i32`
//~| found type `[_; 3]`
//~| expected i32, found array of 3 elements
let x: &[i32] = &[1, 2, 3];
let _y: &i32 = x;
//~^ ERROR mismatched types
//~| expected `&i32`
//~| found `&[i32]`
//~| expected i32
//~| found slice
//~| expected type `&i32`
//~| found type `&[i32]`
//~| expected i32, found slice
}

View File

@ -32,10 +32,9 @@ fn foo1<I: Foo<A=Bar>>(x: I) {
fn foo2<I: Foo>(x: I) {
let _: Bar = x.boo();
//~^ ERROR mismatched types
//~| expected `Bar`
//~| found `<I as Foo>::A`
//~| expected struct `Bar`
//~| found associated type
//~| expected type `Bar`
//~| found type `<I as Foo>::A`
//~| expected struct `Bar`, found associated type
}

View File

@ -28,8 +28,7 @@ pub fn f2<T: Foo>(a: T) -> T::A {
pub fn f1_int_int() {
f1(2i32, 4i32);
//~^ ERROR mismatched types
//~| expected `u32`
//~| found `i32`
//~| expected u32, found i32
}
pub fn f1_int_uint() {
@ -49,8 +48,7 @@ pub fn f1_uint_int() {
pub fn f2_int() {
let _: i32 = f2(2i32);
//~^ ERROR mismatched types
//~| expected `i32`
//~| found `u32`
//~| expected i32, found u32
}
pub fn main() { }

View File

@ -21,8 +21,10 @@ impl AddAssign for Int {
fn main() {
let mut x = Int(1);
x //~ error: use of moved value: `x`
//~^ value used here after move
//~| note: move occurs because `x` has type `Int`
+=
x; //~ note: `x` moved here because it has type `Int`, which is non-copyable
x; //~ value moved here
let y = Int(2);
y //~ error: cannot borrow immutable local variable `y` as mutable

View File

@ -10,8 +10,7 @@
static i: String = 10;
//~^ ERROR mismatched types
//~| expected `std::string::String`
//~| found `_`
//~| expected struct `std::string::String`
//~| found integral variable
//~| expected type `std::string::String`
//~| found type `_`
//~| expected struct `std::string::String`, found integral variable
fn main() { println!("{}", i); }

View File

@ -8,4 +8,4 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main(x: isize) { } //~ ERROR: main function expects type
fn main(x: isize) { } //~ ERROR: main function has wrong type

View File

@ -62,6 +62,7 @@ fn mut_plus_immut() {
&mut f
+
&f; //~ ERROR: cannot borrow `f` as immutable because it is also borrowed as mutable
//~^ cannot borrow `f` as immutable because it is also borrowed as mutable
}
fn immut_plus_mut() {
@ -70,6 +71,7 @@ fn immut_plus_mut() {
&f
+
&mut f; //~ ERROR: cannot borrow `f` as mutable because it is also borrowed as immutable
//~^ cannot borrow `f` as mutable because it is also borrowed as immutable
}
fn main() {}

View File

@ -11,9 +11,8 @@
fn main() {
while true {
true //~ ERROR mismatched types
//~| expected `()`
//~| found `bool`
//~| expected ()
//~| found bool
//~| expected type `()`
//~| found type `bool`
//~| expected (), found bool
}
}

View File

@ -33,22 +33,28 @@ struct D {
fn copy_after_move() {
let a: Box<_> = box A { x: box 0, y: 1 };
let _x = a.x;
//~^ value moved here
let _y = a.y; //~ ERROR use of moved
//~^^ NOTE `a` moved here (through moving `a.x`)
//~^ move occurs because `a.x` has type `Box<isize>`
//~| value used here after move
}
fn move_after_move() {
let a: Box<_> = box B { x: box 0, y: box 1 };
let _x = a.x;
//~^ value moved here
let _y = a.y; //~ ERROR use of moved
//~^^ NOTE `a` moved here (through moving `a.x`)
//~^ move occurs because `a.x` has type `Box<isize>`
//~| value used here after move
}
fn borrow_after_move() {
let a: Box<_> = box A { x: box 0, y: 1 };
let _x = a.x;
//~^ value moved here
let _y = &a.y; //~ ERROR use of moved
//~^^ NOTE `a` moved here (through moving `a.x`)
//~^ move occurs because `a.x` has type `Box<isize>`
//~| value used here after move
}
fn move_after_borrow() {
@ -75,44 +81,52 @@ fn move_after_mut_borrow() {
fn borrow_after_mut_borrow() {
let mut a: Box<_> = box A { x: box 0, y: 1 };
let _x = &mut a.x;
//~^ NOTE previous borrow of `a` occurs here (through borrowing `a.x`);
//~^ NOTE mutable borrow occurs here (via `a.x`)
let _y = &a.y; //~ ERROR cannot borrow
//~^ immutable borrow occurs here (via `a.y`)
}
//~^ NOTE previous borrow ends here
//~^ NOTE mutable borrow ends here
fn mut_borrow_after_borrow() {
let mut a: Box<_> = box A { x: box 0, y: 1 };
let _x = &a.x;
//~^ NOTE previous borrow of `a` occurs here (through borrowing `a.x`)
//~^ NOTE immutable borrow occurs here (via `a.x`)
let _y = &mut a.y; //~ ERROR cannot borrow
//~^ mutable borrow occurs here (via `a.y`)
}
//~^ NOTE previous borrow ends here
//~^ NOTE immutable borrow ends here
fn copy_after_move_nested() {
let a: Box<_> = box C { x: box A { x: box 0, y: 1 }, y: 2 };
let _x = a.x.x;
//~^ NOTE `a.x.x` moved here because it has type `Box<isize>`, which is moved by default
//~^ value moved here
let _y = a.y; //~ ERROR use of collaterally moved
//~^ NOTE move occurs because `a.x.x` has type `Box<isize>`
//~| value used here after move
}
fn move_after_move_nested() {
let a: Box<_> = box D { x: box A { x: box 0, y: 1 }, y: box 2 };
let _x = a.x.x;
//~^ NOTE `a.x.x` moved here because it has type `Box<isize>`, which is moved by default
//~^ value moved here
let _y = a.y; //~ ERROR use of collaterally moved
//~^ NOTE move occurs because `a.x.x` has type `Box<isize>`
//~| value used here after move
}
fn borrow_after_move_nested() {
let a: Box<_> = box C { x: box A { x: box 0, y: 1 }, y: 2 };
let _x = a.x.x;
//~^ NOTE `a.x.x` moved here because it has type `Box<isize>`, which is moved by default
//~^ value moved here
let _y = &a.y; //~ ERROR use of collaterally moved
//~^ NOTE move occurs because `a.x.x` has type `Box<isize>`
//~| value used here after move
}
fn move_after_borrow_nested() {
let a: Box<_> = box D { x: box A { x: box 0, y: 1 }, y: box 2 };
let _x = &a.x.x;
//~^ NOTE borrow of `a.x.x` occurs here
//~^ borrow of `a.x.x` occurs here
let _y = a.y; //~ ERROR cannot move
}
@ -133,18 +147,20 @@ fn move_after_mut_borrow_nested() {
fn borrow_after_mut_borrow_nested() {
let mut a: Box<_> = box C { x: box A { x: box 0, y: 1 }, y: 2 };
let _x = &mut a.x.x;
//~^ NOTE previous borrow of `a.x.x` occurs here; the mutable borrow prevents
//~^ mutable borrow occurs here
let _y = &a.y; //~ ERROR cannot borrow
//~^ immutable borrow occurs here
}
//~^ NOTE previous borrow ends here
//~^ NOTE mutable borrow ends here
fn mut_borrow_after_borrow_nested() {
let mut a: Box<_> = box C { x: box A { x: box 0, y: 1 }, y: 2 };
let _x = &a.x.x;
//~^ NOTE previous borrow of `a.x.x` occurs here; the immutable borrow prevents
//~^ immutable borrow occurs here
let _y = &mut a.y; //~ ERROR cannot borrow
//~^ mutable borrow occurs here
}
//~^ NOTE previous borrow ends here
//~^ NOTE immutable borrow ends here
fn main() {
copy_after_move();

View File

@ -24,7 +24,7 @@ fn a(x: &isize) {
//~^ ERROR cannot borrow
let c2 = || set(&mut *x);
//~^ ERROR cannot borrow
//~| ERROR closure requires unique access
//~| ERROR two closures require unique access to `x` at the same time
}
fn main() {

View File

@ -39,7 +39,7 @@ fn c(x: &mut isize) {
fn d(x: &mut isize) {
let c1 = || set(x);
let c2 = || set(x); //~ ERROR closure requires unique access to `x`
let c2 = || set(x); //~ ERROR two closures require unique access to `x` at the same time
}
fn e(x: &mut isize) {

View File

@ -109,6 +109,7 @@ fn while_aliased_mut_cond(cond: bool, cond2: bool) {
borrow(&*v); //~ ERROR cannot borrow
if cond2 {
x = &mut v; //~ ERROR cannot borrow
//~^ ERROR cannot borrow
}
}
}

View File

@ -19,6 +19,7 @@ fn main() {
match 1 {
1 => { addr = &mut x; }
//~^ ERROR cannot borrow `x` as mutable more than once at a time
//~| ERROR cannot borrow `x` as mutable more than once at a time
2 => { addr = &mut x; }
//~^ ERROR cannot borrow `x` as mutable more than once at a time
_ => { addr = &mut x; }

View File

@ -13,10 +13,11 @@ fn main() {
// Original borrow ends at end of function
let mut x = 1;
let y = &mut x;
//~^ previous borrow of `x` occurs here; the mutable borrow prevents
//~^ mutable borrow occurs here
let z = &x; //~ ERROR cannot borrow
//~^ immutable borrow occurs here
}
//~^ NOTE previous borrow ends here
//~^ NOTE mutable borrow ends here
fn foo() {
match true {
@ -24,10 +25,11 @@ fn foo() {
// Original borrow ends at end of match arm
let mut x = 1;
let y = &x;
//~^ previous borrow of `x` occurs here; the immutable borrow prevents
//~^ immutable borrow occurs here
let z = &mut x; //~ ERROR cannot borrow
//~^ mutable borrow occurs here
}
//~^ NOTE previous borrow ends here
//~^ NOTE immutable borrow ends here
false => ()
}
}
@ -37,8 +39,9 @@ fn bar() {
|| {
let mut x = 1;
let y = &mut x;
//~^ previous borrow of `x` occurs here; the mutable borrow prevents
//~^ first mutable borrow occurs here
let z = &mut x; //~ ERROR cannot borrow
//~^ second mutable borrow occurs here
};
//~^ NOTE previous borrow ends here
//~^ NOTE first borrow ends here
}

View File

@ -17,6 +17,6 @@ fn bar<T: Fn(u32)>(_: T) {}
fn main() {
let x = X;
let closure = |_| foo(x); //~ ERROR E0524
let closure = |_| foo(x); //~ ERROR E0525
bar(closure);
}

View File

@ -14,7 +14,7 @@ fn main() {
let x = 0;
f(&x);
//~^ ERROR mismatched types
//~| expected `&mut i32`
//~| found `&_`
//~| expected type `&mut i32`
//~| found type `&_`
//~| values differ in mutability
}

View File

@ -13,8 +13,7 @@
fn main() {
let _: &[i32] = [0];
//~^ ERROR mismatched types
//~| expected `&[i32]`
//~| found `[_; 1]`
//~| expected &-ptr
//~| found array of 1 elements
//~| expected type `&[i32]`
//~| found type `[_; 1]`
//~| expected &-ptr, found array of 1 elements
}

View File

@ -19,8 +19,7 @@ pub fn main() {
// FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
let x: Box<Trait> = Box::new(Foo);
let _y: &Trait = x; //~ ERROR mismatched types
//~| expected `&Trait`
//~| found `Box<Trait>`
//~| expected &-ptr
//~| found box
//~| expected type `&Trait`
//~| found type `Box<Trait>`
//~| expected &-ptr, found box
}

View File

@ -23,6 +23,9 @@ fn main() {
// Here, F is instantiated with $0=uint
let x = foo();
//~^ ERROR: mismatched types
//~| expected type `usize`
//~| found type `isize`
//~| NOTE: conflicting type parameter defaults `usize` and `isize`
//~| NOTE: conflicting type parameter defaults `usize` and `isize`
//~| NOTE: ...that was applied to an unconstrained type variable here

View File

@ -24,7 +24,11 @@ fn main() {
//~^ NOTE: ...that also applies to the same type variable here
meh(foo);
//~^ ERROR: mismatched types:
//~^ ERROR: mismatched types
//~| NOTE: conflicting type parameter defaults `bool` and `char`
//~| NOTE: conflicting type parameter defaults `bool` and `char`
//~| a second default is defined on `default_param_test::bleh`
//~| NOTE: ...that was applied to an unconstrained type variable here
//~| expected type `bool`
//~| found type `char`
}

View File

@ -40,20 +40,17 @@ fn main() {
// n > m
let &&x = &1isize as &T;
//~^ ERROR mismatched types
//~| expected `T`
//~| found `&_`
//~| expected trait T
//~| found &-ptr
//~| expected type `T`
//~| found type `&_`
//~| expected trait T, found &-ptr
let &&&x = &(&1isize as &T);
//~^ ERROR mismatched types
//~| expected `T`
//~| found `&_`
//~| expected trait T
//~| found &-ptr
//~| expected type `T`
//~| found type `&_`
//~| expected trait T, found &-ptr
let box box x = box 1isize as Box<T>;
//~^ ERROR mismatched types
//~| expected `T`
//~| found `Box<_>`
//~| expected trait T
//~| found box
//~| expected type `T`
//~| found type `Box<_>`
//~| expected trait T, found box
}

View File

@ -45,9 +45,8 @@ pub fn main() {
let z: Box<ToBar> = Box::new(Bar1 {f: 36});
f5.ptr = Bar1 {f: 36};
//~^ ERROR mismatched types
//~| expected `ToBar`
//~| found `Bar1`
//~| expected trait ToBar
//~| found struct `Bar1`
//~| expected type `ToBar`
//~| found type `Bar1`
//~| expected trait ToBar, found struct `Bar1`
//~| ERROR `ToBar: std::marker::Sized` is not satisfied
}

View File

@ -19,8 +19,7 @@ pub fn main() {
let f1: &Fat<[isize]> = &Fat { ptr: [1, 2, 3] };
let f2: &Fat<[isize; 3]> = f1;
//~^ ERROR mismatched types
//~| expected `&Fat<[isize; 3]>`
//~| found `&Fat<[isize]>`
//~| expected array of 3 elements
//~| found slice
//~| expected type `&Fat<[isize; 3]>`
//~| found type `&Fat<[isize]>`
//~| expected array of 3 elements, found slice
}

View File

@ -16,12 +16,12 @@ struct Foo<'a,'b> {
impl<'a,'b> Foo<'a,'b> {
fn bar(self: Foo<'b,'a>) {}
//~^ ERROR mismatched types
//~| expected `Foo<'a, 'b>`
//~| found `Foo<'b, 'a>`
//~| expected type `Foo<'a, 'b>`
//~| found type `Foo<'b, 'a>`
//~| lifetime mismatch
//~| ERROR mismatched types
//~| expected `Foo<'a, 'b>`
//~| found `Foo<'b, 'a>`
//~| expected type `Foo<'a, 'b>`
//~| found type `Foo<'b, 'a>`
//~| lifetime mismatch
}

View File

@ -8,4 +8,4 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
extern fn main() {} //~ ERROR: main function expects type
extern fn main() {} //~ ERROR: main function has wrong type

View File

@ -22,26 +22,22 @@ impl<T> Foo for T { /* `foo` is still default here */ }
fn main() {
eq(foo::<u8>, bar::<u8>);
//~^ ERROR mismatched types
//~| expected `fn(isize) -> isize {foo::<u8>}`
//~| found `fn(isize) -> isize {bar::<u8>}`
//~| expected fn item
//~| found a different fn item
//~| expected type `fn(isize) -> isize {foo::<u8>}`
//~| found type `fn(isize) -> isize {bar::<u8>}`
//~| expected fn item, found a different fn item
eq(foo::<u8>, foo::<i8>);
//~^ ERROR mismatched types
//~| expected `fn(isize) -> isize {foo::<u8>}`
//~| found `fn(isize) -> isize {foo::<i8>}`
//~| expected u8, found i8
eq(bar::<String>, bar::<Vec<u8>>);
//~^ ERROR mismatched types
//~| expected `fn(isize) -> isize {bar::<std::string::String>}`
//~| found `fn(isize) -> isize {bar::<std::vec::Vec<u8>>}`
//~| expected struct `std::string::String`
//~| found struct `std::vec::Vec`
//~| expected type `fn(isize) -> isize {bar::<std::string::String>}`
//~| found type `fn(isize) -> isize {bar::<std::vec::Vec<u8>>}`
//~| expected struct `std::string::String`, found struct `std::vec::Vec`
// Make sure we distinguish between trait methods correctly.
eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
//~^ ERROR mismatched types
//~| expected `fn() {<u8 as Foo>::foo}`
//~| found `fn() {<u16 as Foo>::foo}`
//~| expected u8, found u16
}

View File

@ -16,22 +16,19 @@ fn needs_fn<F>(x: F) where F: Fn(isize) -> isize {}
fn main() {
let _: () = (box |_: isize| {}) as Box<FnOnce(isize)>;
//~^ ERROR mismatched types
//~| expected `()`
//~| found `Box<std::ops::FnOnce(isize)>`
//~| expected ()
//~| found box
//~| expected type `()`
//~| found type `Box<std::ops::FnOnce(isize)>`
//~| expected (), found box
let _: () = (box |_: isize, isize| {}) as Box<Fn(isize, isize)>;
//~^ ERROR mismatched types
//~| expected `()`
//~| found `Box<std::ops::Fn(isize, isize)>`
//~| expected ()
//~| found box
//~| expected type `()`
//~| found type `Box<std::ops::Fn(isize, isize)>`
//~| expected (), found box
let _: () = (box || -> isize { unimplemented!() }) as Box<FnMut() -> isize>;
//~^ ERROR mismatched types
//~| expected `()`
//~| found `Box<std::ops::FnMut() -> isize>`
//~| expected ()
//~| found box
//~| expected type `()`
//~| found type `Box<std::ops::FnMut() -> isize>`
//~| expected (), found box
needs_fn(1);
//~^ ERROR : std::ops::Fn<(isize,)>`

View File

@ -14,8 +14,7 @@ fn main() {
let x: Option<usize>;
x = 5;
//~^ ERROR mismatched types
//~| expected `std::option::Option<usize>`
//~| found `_`
//~| expected enum `std::option::Option`
//~| found integral variable
//~| expected type `std::option::Option<usize>`
//~| found type `_`
//~| expected enum `std::option::Option`, found integral variable
}

View File

@ -21,10 +21,9 @@ mod y {
fn bar(x: x::foo) -> y::foo {
return x;
//~^ ERROR mismatched types
//~| expected `y::foo`
//~| found `x::foo`
//~| expected enum `y::foo`
//~| found enum `x::foo`
//~| expected type `y::foo`
//~| found type `x::foo`
//~| expected enum `y::foo`, found enum `x::foo`
}
fn main() {

View File

@ -15,10 +15,9 @@ use std::option::Option;
fn bar(x: usize) -> Option<usize> {
return x;
//~^ ERROR mismatched types
//~| expected `std::option::Option<usize>`
//~| found `usize`
//~| expected enum `std::option::Option`
//~| found usize
//~| expected type `std::option::Option<usize>`
//~| found type `usize`
//~| expected enum `std::option::Option`, found usize
}
fn main() {

View File

@ -22,46 +22,40 @@ fn main() {
// Ensure that the printed type doesn't include the default type params...
let _: Foo<isize> = ();
//~^ ERROR mismatched types
//~| expected `Foo<isize>`
//~| found `()`
//~| expected struct `Foo`
//~| found ()
//~| expected type `Foo<isize>`
//~| found type `()`
//~| expected struct `Foo`, found ()
// ...even when they're present, but the same types as the defaults.
let _: Foo<isize, B, C> = ();
//~^ ERROR mismatched types
//~| expected `Foo<isize>`
//~| found `()`
//~| expected struct `Foo`
//~| found ()
//~| expected type `Foo<isize>`
//~| found type `()`
//~| expected struct `Foo`, found ()
// Including cases where the default is using previous type params.
let _: HashMap<String, isize> = ();
//~^ ERROR mismatched types
//~| expected `HashMap<std::string::String, isize>`
//~| found `()`
//~| expected struct `HashMap`
//~| found ()
//~| expected type `HashMap<std::string::String, isize>`
//~| found type `()`
//~| expected struct `HashMap`, found ()
let _: HashMap<String, isize, Hash<String>> = ();
//~^ ERROR mismatched types
//~| expected `HashMap<std::string::String, isize>`
//~| found `()`
//~| expected struct `HashMap`
//~| found ()
//~| expected type `HashMap<std::string::String, isize>`
//~| found type `()`
//~| expected struct `HashMap`, found ()
// But not when there's a different type in between.
let _: Foo<A, isize, C> = ();
//~^ ERROR mismatched types
//~| expected `Foo<A, isize>`
//~| found `()`
//~| expected struct `Foo`
//~| found ()
//~| expected type `Foo<A, isize>`
//~| found type `()`
//~| expected struct `Foo`, found ()
// And don't print <> at all when there's just defaults.
let _: Foo<A, B, C> = ();
//~^ ERROR mismatched types
//~| expected `Foo`
//~| found `()`
//~| expected struct `Foo`
//~| found ()
//~| expected type `Foo`
//~| found type `()`
//~| expected struct `Foo`, found ()
}

View File

@ -11,6 +11,5 @@
fn main() {
let x = if true { 10i32 } else { 10u32 };
//~^ ERROR if and else have incompatible types
//~| expected `i32`
//~| found `u32`
//~| expected i32, found u32
}

View File

@ -10,6 +10,9 @@
fn main() {
if let Some(b) = None { //~ ERROR: `if let` arms have incompatible types
//~^ expected (), found integral variable
//~| expected type `()`
//~| found type `_`
()
} else { //~ NOTE: `if let` arm with an incompatible type
1

View File

@ -11,9 +11,8 @@
fn main() {
let a = if true { true };
//~^ ERROR if may be missing an else clause
//~| expected `()`
//~| found `bool`
//~| expected ()
//~| found bool
//~| expected type `()`
//~| found type `bool`
//~| expected (), found bool
println!("{}", a);
}

View File

@ -41,168 +41,132 @@ fn main() {
id_i8(a8); // ok
id_i8(a16);
//~^ ERROR mismatched types
//~| expected `i8`
//~| found `i16`
//~| expected i8, found i16
id_i8(a32);
//~^ ERROR mismatched types
//~| expected `i8`
//~| found `i32`
//~| expected i8, found i32
id_i8(a64);
//~^ ERROR mismatched types
//~| expected `i8`
//~| found `i64`
//~| expected i8, found i64
id_i16(a8);
//~^ ERROR mismatched types
//~| expected `i16`
//~| found `i8`
//~| expected i16, found i8
id_i16(a16); // ok
id_i16(a32);
//~^ ERROR mismatched types
//~| expected `i16`
//~| found `i32`
//~| expected i16, found i32
id_i16(a64);
//~^ ERROR mismatched types
//~| expected `i16`
//~| found `i64`
//~| expected i16, found i64
id_i32(a8);
//~^ ERROR mismatched types
//~| expected `i32`
//~| found `i8`
//~| expected i32, found i8
id_i32(a16);
//~^ ERROR mismatched types
//~| expected `i32`
//~| found `i16`
//~| expected i32, found i16
id_i32(a32); // ok
id_i32(a64);
//~^ ERROR mismatched types
//~| expected `i32`
//~| found `i64`
//~| expected i32, found i64
id_i64(a8);
//~^ ERROR mismatched types
//~| expected `i64`
//~| found `i8`
//~| expected i64, found i8
id_i64(a16);
//~^ ERROR mismatched types
//~| expected `i64`
//~| found `i16`
//~| expected i64, found i16
id_i64(a32);
//~^ ERROR mismatched types
//~| expected `i64`
//~| found `i32`
//~| expected i64, found i32
id_i64(a64); // ok
id_i8(c8); // ok
id_i8(c16);
//~^ ERROR mismatched types
//~| expected `i8`
//~| found `i16`
//~| expected i8, found i16
id_i8(c32);
//~^ ERROR mismatched types
//~| expected `i8`
//~| found `i32`
//~| expected i8, found i32
id_i8(c64);
//~^ ERROR mismatched types
//~| expected `i8`
//~| found `i64`
//~| expected i8, found i64
id_i16(c8);
//~^ ERROR mismatched types
//~| expected `i16`
//~| found `i8`
//~| expected i16, found i8
id_i16(c16); // ok
id_i16(c32);
//~^ ERROR mismatched types
//~| expected `i16`
//~| found `i32`
//~| expected i16, found i32
id_i16(c64);
//~^ ERROR mismatched types
//~| expected `i16`
//~| found `i64`
//~| expected i16, found i64
id_i32(c8);
//~^ ERROR mismatched types
//~| expected `i32`
//~| found `i8`
//~| expected i32, found i8
id_i32(c16);
//~^ ERROR mismatched types
//~| expected `i32`
//~| found `i16`
//~| expected i32, found i16
id_i32(c32); // ok
id_i32(c64);
//~^ ERROR mismatched types
//~| expected `i32`
//~| found `i64`
//~| expected i32, found i64
id_i64(a8);
//~^ ERROR mismatched types
//~| expected `i64`
//~| found `i8`
//~| expected i64, found i8
id_i64(a16);
//~^ ERROR mismatched types
//~| expected `i64`
//~| found `i16`
//~| expected i64, found i16
id_i64(a32);
//~^ ERROR mismatched types
//~| expected `i64`
//~| found `i32`
//~| expected i64, found i32
id_i64(a64); // ok
id_u8(b8); // ok
id_u8(b16);
//~^ ERROR mismatched types
//~| expected `u8`
//~| found `u16`
//~| expected u8, found u16
id_u8(b32);
//~^ ERROR mismatched types
//~| expected `u8`
//~| found `u32`
//~| expected u8, found u32
id_u8(b64);
//~^ ERROR mismatched types
//~| expected `u8`
//~| found `u64`
//~| expected u8, found u64
id_u16(b8);
//~^ ERROR mismatched types
//~| expected `u16`
//~| found `u8`
//~| expected u16, found u8
id_u16(b16); // ok
id_u16(b32);
//~^ ERROR mismatched types
//~| expected `u16`
//~| found `u32`
//~| expected u16, found u32
id_u16(b64);
//~^ ERROR mismatched types
//~| expected `u16`
//~| found `u64`
//~| expected u16, found u64
id_u32(b8);
//~^ ERROR mismatched types
//~| expected `u32`
//~| found `u8`
//~| expected u32, found u8
id_u32(b16);
//~^ ERROR mismatched types
//~| expected `u32`
//~| found `u16`
//~| expected u32, found u16
id_u32(b32); // ok
id_u32(b64);
//~^ ERROR mismatched types
//~| expected `u32`
//~| found `u64`
//~| expected u32, found u64
id_u64(b8);
//~^ ERROR mismatched types
//~| expected `u64`
//~| found `u8`
//~| expected u64, found u8
id_u64(b16);
//~^ ERROR mismatched types
//~| expected `u64`
//~| found `u16`
//~| expected u64, found u16
id_u64(b32);
//~^ ERROR mismatched types
//~| expected `u64`
//~| found `u32`
//~| expected u64, found u32
id_u64(b64); // ok
}

View File

@ -12,8 +12,7 @@ fn main() {
let mut x = 2;
x = 5.0;
//~^ ERROR mismatched types
//~| expected `_`
//~| found `_`
//~| expected integral variable
//~| found floating-point variable
//~| expected type `_`
//~| found type `_`
//~| expected integral variable, found floating-point variable
}

View File

@ -11,10 +11,9 @@
fn f() -> isize {
(return 1, return 2)
//~^ ERROR mismatched types
//~| expected `isize`
//~| found `(_, _)`
//~| expected isize
//~| found tuple
//~| expected type `isize`
//~| found type `(_, _)`
//~| expected isize, found tuple
}
fn main() {}

View File

@ -10,11 +10,10 @@
fn main() {
match Some(10) {
//~^ ERROR match arms have incompatible types:
//~| expected `bool`
//~| found `()`
//~| expected bool
//~| found ()
//~^ ERROR match arms have incompatible types
//~| expected type `bool`
//~| found type `()`
//~| expected bool, found ()
Some(5) => false,
Some(2) => true,
None => (), //~ NOTE match arm with an incompatible type

View File

@ -15,7 +15,6 @@
#[bench]
fn bar(x: isize) { }
//~^ ERROR mismatched types
//~| expected `fn(&mut __test::test::Bencher)`
//~| found `fn(isize) {bar}`
//~| expected &-ptr
//~| found isize
//~| expected type `fn(&mut __test::test::Bencher)`
//~| found type `fn(isize) {bar}`
//~| expected &-ptr, found isize

View File

@ -15,11 +15,9 @@ fn bar(_s: u32) { }
fn main() {
foo(1*(1 as isize));
//~^ ERROR mismatched types
//~| expected `i16`
//~| found `isize`
//~| expected i16, found isize
bar(1*(1 as usize));
//~^ ERROR mismatched types
//~| expected `u32`
//~| found `usize`
//~| expected u32, found usize
}

View File

@ -17,16 +17,14 @@ pub fn main() {
let _x: usize = match Some(1) {
Ok(u) => u,
//~^ ERROR mismatched types
//~| expected `std::option::Option<_>`
//~| found `std::result::Result<_, _>`
//~| expected enum `std::option::Option`
//~| found enum `std::result::Result`
//~| expected type `std::option::Option<_>`
//~| found type `std::result::Result<_, _>`
//~| expected enum `std::option::Option`, found enum `std::result::Result`
Err(e) => panic!(e)
//~^ ERROR mismatched types
//~| expected `std::option::Option<_>`
//~| found `std::result::Result<_, _>`
//~| expected enum `std::option::Option`
//~| found enum `std::result::Result`
//~| expected type `std::option::Option<_>`
//~| found type `std::result::Result<_, _>`
//~| expected enum `std::option::Option`, found enum `std::result::Result`
};
}

View File

@ -17,10 +17,9 @@ fn main() {
let y = match x {
[] => None,
//~^ ERROR mismatched types
//~| expected `[_#1i; 2]`
//~| found `[_#7t; 0]`
//~| expected an array with a fixed size of 2 elements
//~| found one with 0 elements
//~| expected type `[_#1i; 2]`
//~| found type `[_#7t; 0]`
//~| expected an array with a fixed size of 2 elements, found one with 0 elements
[a,_] => Some(a)
};
}

View File

@ -15,8 +15,8 @@ fn main() {
let y = match x {
[] => None,
//~^ ERROR mismatched types
//~| expected `[_; 2]`
//~| found `[_; 0]`
//~| expected type `[_; 2]`
//~| found type `[_; 0]`
//~| expected an array with a fixed size of 2 elements
[a,_] => Some(a)
};

View File

@ -16,10 +16,9 @@ mod a {
pub fn get_enum_struct_variant() -> () {
Enum::EnumStructVariant { x: 1, y: 2, z: 3 }
//~^ ERROR mismatched types
//~| expected `()`
//~| found `a::Enum`
//~| expected ()
//~| found enum `a::Enum`
//~| expected type `()`
//~| found type `a::Enum`
//~| expected (), found enum `a::Enum`
}
}
@ -32,10 +31,9 @@ mod b {
match enum_struct_variant {
a::Enum::EnumStructVariant { x, y, z } => {
//~^ ERROR mismatched types
//~| expected `()`
//~| found `a::Enum`
//~| expected ()
// found enum `a::Enum`
//~| expected type `()`
//~| found type `a::Enum`
//~| expected (), found enum `a::Enum`
}
}
}

View File

@ -9,9 +9,5 @@
// except according to those terms.
// error-pattern:mismatched types
// error-pattern:expected `bool`
// error-pattern:found `_`
// error-pattern:expected bool
// error-pattern:found integral variable
fn main(){assert!(1,1);}

View File

@ -14,10 +14,9 @@ struct vec3 { y: f32, z: f32 }
fn make(v: vec2) {
let vec3 { y: _, z: _ } = v;
//~^ ERROR mismatched types
//~| expected `vec2`
//~| found `vec3`
//~| expected struct `vec2`
//~| found struct `vec3`
//~| expected type `vec2`
//~| found type `vec3`
//~| expected struct `vec2`, found struct `vec3`
}
fn main() { }

View File

@ -17,9 +17,8 @@ fn main() {
let x = Some(&[name]);
let msg = foo(x);
//~^ ERROR mismatched types
//~| expected `std::option::Option<&[&str]>`
//~| found `std::option::Option<&[&str; 1]>`
//~| expected slice
//~| found array of 1 elements
//~| expected type `std::option::Option<&[&str]>`
//~| found type `std::option::Option<&[&str; 1]>`
//~| expected slice, found array of 1 elements
assert_eq!(msg, 3);
}

View File

@ -20,10 +20,9 @@ fn main() {
E::B(
Tau{t: x},
//~^ ERROR mismatched types
//~| expected `main::R`
//~| found `main::Tau`
//~| expected enum `main::R`
//~| found struct `main::Tau`
//~| expected type `main::R`
//~| found type `main::Tau`
//~| expected enum `main::R`, found struct `main::Tau`
_) => x,
};
}

View File

@ -13,8 +13,7 @@ use std::raw::Slice;
fn main() {
let Slice { data: data, len: len } = "foo";
//~^ ERROR mismatched types
//~| expected `&str`
//~| found `std::raw::Slice<_>`
//~| expected &-ptr
//~| found struct `std::raw::Slice`
//~| expected type `&str`
//~| found type `std::raw::Slice<_>`
//~| expected &-ptr, found struct `std::raw::Slice`
}

View File

@ -14,10 +14,9 @@ fn main() {
match () {
Slice { data: data, len: len } => (),
//~^ ERROR mismatched types
//~| expected `()`
//~| found `std::raw::Slice<_>`
//~| expected ()
//~| found struct `std::raw::Slice`
//~| expected type `()`
//~| found type `std::raw::Slice<_>`
//~| expected (), found struct `std::raw::Slice`
_ => unreachable!()
}
}

View File

@ -12,10 +12,9 @@
fn f<'r>(p: &'r mut fn(p: &mut ())) {
(*p)(()) //~ ERROR mismatched types
//~| expected `&mut ()`
//~| found `()`
//~| expected &-ptr
//~| found ()
//~| expected type `&mut ()`
//~| found type `()`
//~| expected &-ptr, found ()
}
fn main() {}

View File

@ -15,13 +15,15 @@ struct Foo { a: isize, b: isize }
fn main() {
let mut x: Box<_> = box Foo { a: 1, b: 2 };
let (a, b) = (&mut x.a, &mut x.b);
//~^ ERROR cannot borrow `x` (here through borrowing `x.b`) as mutable more than once at a time
//~^^ NOTE previous borrow of `x` occurs here (through borrowing `x.a`)
//~^ ERROR cannot borrow `x` (via `x.b`) as mutable more than once at a time
//~| NOTE first mutable borrow occurs here (via `x.a`)
//~| NOTE second mutable borrow occurs here (via `x.b`)
let mut foo: Box<_> = box Foo { a: 1, b: 2 };
let (c, d) = (&mut foo.a, &foo.b);
//~^ ERROR cannot borrow `foo` (here through borrowing `foo.b`) as immutable
//~^^ NOTE previous borrow of `foo` occurs here (through borrowing `foo.a`)
//~^ ERROR cannot borrow `foo` (via `foo.b`) as immutable
//~| NOTE mutable borrow occurs here (via `foo.a`)
//~| NOTE immutable borrow occurs here (via `foo.b`)
}
//~^ NOTE previous borrow ends here
//~^^ NOTE previous borrow ends here
//~^ NOTE first borrow ends here
//~^^ NOTE mutable borrow ends here

View File

@ -24,28 +24,25 @@ fn main() {
// `x { ... }` should not be interpreted as a struct literal here
if x = x {
//~^ ERROR mismatched types
//~| expected `bool`
//~| found `()`
//~| expected bool
//~| found ()
//~| expected type `bool`
//~| found type `()`
//~| expected bool, found ()
println!("{}", x);
}
// Explicit parentheses on the left should match behavior of above
if (x = x) {
//~^ ERROR mismatched types
//~| expected `bool`
//~| found `()`
//~| expected bool
//~| found ()
//~| expected type `bool`
//~| found type `()`
//~| expected bool, found ()
println!("{}", x);
}
// The struct literal interpretation is fine with explicit parentheses on the right
if y = (Foo { foo: x }) {
//~^ ERROR mismatched types
//~| expected `bool`
//~| found `()`
//~| expected bool
//~| found ()
//~| expected type `bool`
//~| found type `()`
//~| expected bool, found ()
println!("{}", x);
}
}

View File

@ -108,6 +108,9 @@ impl Debug for Player {
fn str_to_direction(to_parse: &str) -> RoomDirection {
match to_parse { //~ ERROR match arms have incompatible types
//~^ expected enum `RoomDirection`, found enum `std::option::Option`
//~| expected type `RoomDirection`
//~| found type `std::option::Option<_>`
"w" | "west" => RoomDirection::West,
"e" | "east" => RoomDirection::East,
"n" | "north" => RoomDirection::North,

View File

@ -15,12 +15,12 @@ struct Foo<'a> {
impl <'a> Foo<'a>{
fn bar(self: &mut Foo) {
//~^ mismatched types
//~| expected `&mut Foo<'a>`
//~| found `&mut Foo<'_>`
//~| expected type `&mut Foo<'a>`
//~| found type `&mut Foo<'_>`
//~| lifetime mismatch
//~| mismatched types
//~| expected `&mut Foo<'a>`
//~| found `&mut Foo<'_>`
//~| expected type `&mut Foo<'a>`
//~| found type `&mut Foo<'_>`
//~| lifetime mismatch
}
}

View File

@ -12,11 +12,10 @@ trait Trait { }
fn function(t: &mut Trait) {
t as *mut Trait
//~^ ERROR: mismatched types:
//~| expected `()`,
//~| found `*mut Trait`
//~| (expected (),
//~| found *-ptr) [E0308]
//~^ ERROR: mismatched types
//~| NOTE: expected type `()`
//~| NOTE: found type `*mut Trait`
//~| NOTE: expected (), found *-ptr
}
fn main() { }

Some files were not shown because too many files have changed in this diff Show More