diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index e230836ef4515..efef259dcad9b 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -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 `..` } diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs index abc6ff4a294e3..11d92f8585489 100644 --- a/src/librustc/infer/error_reporting.rs +++ b/src/librustc/infer/error_reporting.rs @@ -249,12 +249,12 @@ pub trait ErrorReporting<'tcx> { terr: &TypeError<'tcx>) -> DiagnosticBuilder<'tcx>; - fn values_str(&self, values: &ValuePairs<'tcx>) -> Option; + fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<(String, String)>; fn expected_found_str + TypeFoldable<'tcx>>( &self, exp_found: &ty::error::ExpectedFound) - -> Option; + -> 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 { + 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 + TypeFoldable<'tcx>>( &self, exp_found: &ty::error::ExpectedFound) - -> Option + -> 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 diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 684cfbea3f5da..43c0e197e16bc 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -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); } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 5b78e4de18b57..edb1c4530c240 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -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. diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index d7ddfc9f1a6d0..531a4fbf8beba 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -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)); } diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index d79ba213aca14..bbd9cd4526d95 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -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) - } - - 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) + match old_loan.cause { + euv::ClosureCapture(span) => { + err = err.span_label( + span, + &format!("previous borrow occurs due to use of `{}` in closure", + ol)); } - }; - - 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; } diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs index 3d94f5b186f0f..5768a441c51b7 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs @@ -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", diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 15db356b1ba95..f65e694939bfc 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -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)} + }; + + // 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::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)"), }; - match the_move.kind { - move_data::Declared => {} + // 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)) + }; - 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)); - } - } + 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)); - 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"); - }, - } - } + // 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>(&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>(&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>(&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(); } diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs index 7f6fd9de3d294..c7ad0b6a6c606 100644 --- a/src/librustc_borrowck/diagnostics.rs +++ b/src/librustc_borrowck/diagnostics.rs @@ -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 } diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 5883013ac72f2..ead6ab099a88b 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -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); diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 2d3363507d06c..d03ae45e83fe8 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -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) -> 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: F) { // a .span_bug or .bug call has already printed what // it wants to print. if !value.is::() { - 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, ¬e[..], None, errors::Level::Note) + emitter.emit(&MultiSpan::new(), ¬e[..], 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); diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index ce92dd158c969..37f7b31b69cf2 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -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); diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 5e3a47701ebbf..4bdd926869a5a 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -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(); } diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 635ef4ab35836..de0de219db2f1 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -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(); } } diff --git a/src/librustc_metadata/loader.rs b/src/librustc_metadata/loader.rs index 28e0e5746a3ee..2316a67d9d3e3 100644 --- a/src/librustc_metadata/loader.rs +++ b/src/librustc_metadata/loader.rs @@ -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())); } } diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 1f9c40856fd19..dede4d2a42a53 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -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(); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index b83d6e9363e9c..e747ed1526061 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -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()), diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 8a915f044053a..10bcf83d7556f 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -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"); - } } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 1110e193e579b..ac7745985e6af 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -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) { 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)"); } } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index a7a04f4a85fe8..544fb117f3614 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -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. diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 21800d91d9458..a96b739ebcf99 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -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(); } } diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 922c411ce8cd8..249ab27ec5956 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -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(); } } diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index eae0cfb0f2267..b71ee8722ab60 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -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"); } } diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index c5195cf8787da..b541ca151c856 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -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::>() .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))); } } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 2d0505d9347d5..385f04b8564f5 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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 { diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 3bfd53ceadae8..25a37b6810e44 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -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))); diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 859fbd974fedc..bfb371be663b5 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -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(); } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index ffcf427715667..c10488a03ef68 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -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(); } }; diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 7f27d10ce1ec3..c51304120a89c 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -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, diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 35aa827782ddf..ca8708fdc8326 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -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 + primary_spans: Vec, + 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, } 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() } + MultiSpan { + primary_spans: vec![], + span_labels: vec![] + } } - 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 } + pub fn from_span(primary_span: Span) -> MultiSpan { + MultiSpan { + primary_spans: vec![primary_span], + span_labels: vec![] + } } - /// 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); + pub fn from_spans(vec: Vec) -> MultiSpan { + 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 push_span_label(&mut self, span: Span, label: String) { + self.span_labels.push((span, label)); + } - 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; - } + /// Selects the first primary span (if any) + pub fn primary_span(&self) -> Option { + 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 { + 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 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; + 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 + }); } } - self.spans.push(sp); + + span_labels } } impl From 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 { 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 "".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(&self, mut spans: Vec, push: F) -> Vec - where F: Fn(&mut MultiSpan, Span) - { - spans.sort_by(|a, b| a.lo.cmp(&b.lo)); - let mut groups = Vec::::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) -> Vec { - 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) -> Vec { - self.custom_group_spans(spans, |msp, sp| msp.push_trim(sp)) - } - pub fn get_filemap(&self, filename: &str) -> Rc { 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]>); - } } diff --git a/src/libsyntax/diagnostics/macros.rs b/src/libsyntax/diagnostics/macros.rs index 95a74d875545f..25e0428248df4 100644 --- a/src/libsyntax/diagnostics/macros.rs +++ b/src/libsyntax/diagnostics/macros.rs @@ -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)*)); }) } diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs index 0b5234769b219..486e2ace0876a 100644 --- a/src/libsyntax/errors/emitter.rs +++ b/src/libsyntax/errors/emitter.rs @@ -13,9 +13,11 @@ use self::Destination::*; use codemap::{self, COMMAND_LINE_SP, DUMMY_SP, Pos, Span, MultiSpan}; use diagnostics; +use errors::check_old_skool; use errors::{Level, RenderSpan, CodeSuggestion, DiagnosticBuilder}; use errors::RenderSpan::*; use errors::Level::*; +use errors::snippet::{RenderedLineKind, SnippetData, Style}; use std::{cmp, fmt}; use std::io::prelude::*; @@ -23,17 +25,73 @@ use std::io; use std::rc::Rc; use term; +/// Emitter trait for emitting errors. Do not implement this directly: +/// implement `CoreEmitter` instead. pub trait Emitter { - fn emit(&mut self, span: Option<&MultiSpan>, msg: &str, code: Option<&str>, lvl: Level); - fn custom_emit(&mut self, sp: &RenderSpan, msg: &str, lvl: Level); + /// Emit a standalone diagnostic message. + fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, lvl: Level); /// Emit a structured diagnostic. + fn emit_struct(&mut self, db: &DiagnosticBuilder); +} + +pub trait CoreEmitter { + fn emit_message(&mut self, + rsp: &RenderSpan, + msg: &str, + code: Option<&str>, + lvl: Level, + is_header: bool, + show_snippet: bool); +} + +impl Emitter for T { + fn emit(&mut self, + msp: &MultiSpan, + msg: &str, + code: Option<&str>, + lvl: Level) { + self.emit_message(&FullSpan(msp.clone()), + msg, + code, + lvl, + true, + true); + } + fn emit_struct(&mut self, db: &DiagnosticBuilder) { - self.emit(db.span.as_ref(), &db.message, db.code.as_ref().map(|s| &**s), db.level); + let old_school = check_old_skool(); + let db_span = FullSpan(db.span.clone()); + self.emit_message(&FullSpan(db.span.clone()), + &db.message, + db.code.as_ref().map(|s| &**s), + db.level, + true, + true); for child in &db.children { - match child.render_span { - Some(ref sp) => self.custom_emit(sp, &child.message, child.level), - None => self.emit(child.span.as_ref(), &child.message, None, child.level), + let render_span = child.render_span + .clone() + .unwrap_or_else( + || FullSpan(child.span.clone())); + + if !old_school { + self.emit_message(&render_span, + &child.message, + None, + child.level, + false, + true); + } else { + let (render_span, show_snippet) = match render_span.span().primary_span() { + None => (db_span.clone(), false), + _ => (render_span, true) + }; + self.emit_message(&render_span, + &child.message, + None, + child.level, + false, + show_snippet); } } } @@ -42,9 +100,6 @@ pub trait Emitter { /// maximum number of lines we will print for each error; arbitrary. pub const MAX_HIGHLIGHT_LINES: usize = 6; -/// maximum number of lines we will print for each span; arbitrary. -const MAX_SP_LINES: usize = 6; - #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ColorConfig { Auto, @@ -68,21 +123,18 @@ pub struct BasicEmitter { dst: Destination, } -impl Emitter for BasicEmitter { - fn emit(&mut self, - msp: Option<&MultiSpan>, - msg: &str, - code: Option<&str>, - lvl: Level) { - assert!(msp.is_none(), "BasicEmitter can't handle spans"); +impl CoreEmitter for BasicEmitter { + fn emit_message(&mut self, + _rsp: &RenderSpan, + msg: &str, + code: Option<&str>, + lvl: Level, + _is_header: bool, + _show_snippet: bool) { + // we ignore the span as we have no access to a codemap at this point if let Err(e) = print_diagnostic(&mut self.dst, "", lvl, msg, code) { panic!("failed to print diagnostics: {:?}", e); } - - } - - fn custom_emit(&mut self, _: &RenderSpan, _: &str, _: Level) { - panic!("BasicEmitter can't handle custom_emit"); } } @@ -101,33 +153,26 @@ pub struct EmitterWriter { dst: Destination, registry: Option, cm: Rc, -} -impl Emitter for EmitterWriter { - fn emit(&mut self, - msp: Option<&MultiSpan>, - msg: &str, - code: Option<&str>, - lvl: Level) { - let error = match msp.map(|s|(s.to_span_bounds(), s)) { - Some((COMMAND_LINE_SP, msp)) => { - self.emit_(&FileLine(msp.clone()), msg, code, lvl) - }, - Some((DUMMY_SP, _)) | None => print_diagnostic(&mut self.dst, "", lvl, msg, code), - Some((_, msp)) => self.emit_(&FullSpan(msp.clone()), msg, code, lvl), - }; + /// Is this the first error emitted thus far? If not, we emit a + /// `\n` before the top-level errors. + first: bool, - if let Err(e) = error { - panic!("failed to print diagnostics: {:?}", e); - } - } + // For now, allow an old-school mode while we transition + old_school: bool, +} - fn custom_emit(&mut self, - rsp: &RenderSpan, - msg: &str, - lvl: Level) { - if let Err(e) = self.emit_(rsp, msg, None, lvl) { - panic!("failed to print diagnostics: {:?}", e); +impl CoreEmitter for EmitterWriter { + fn emit_message(&mut self, + rsp: &RenderSpan, + msg: &str, + code: Option<&str>, + lvl: Level, + is_header: bool, + show_snippet: bool) { + match self.emit_message_(rsp, msg, code, lvl, is_header, show_snippet) { + Ok(()) => { } + Err(e) => panic!("failed to emit error: {}", e) } } } @@ -151,11 +196,20 @@ impl EmitterWriter { registry: Option, code_map: Rc) -> EmitterWriter { + let old_school = check_old_skool(); if color_config.use_color() { let dst = Destination::from_stderr(); - EmitterWriter { dst: dst, registry: registry, cm: code_map } + EmitterWriter { dst: dst, + registry: registry, + cm: code_map, + first: true, + old_school: old_school } } else { - EmitterWriter { dst: Raw(Box::new(io::stderr())), registry: registry, cm: code_map } + EmitterWriter { dst: Raw(Box::new(io::stderr())), + registry: registry, + cm: code_map, + first: true, + old_school: old_school } } } @@ -163,53 +217,108 @@ impl EmitterWriter { registry: Option, code_map: Rc) -> EmitterWriter { - EmitterWriter { dst: Raw(dst), registry: registry, cm: code_map } - } + let old_school = check_old_skool(); + EmitterWriter { dst: Raw(dst), + registry: registry, + cm: code_map, + first: true, + old_school: old_school } + } + + fn emit_message_(&mut self, + rsp: &RenderSpan, + msg: &str, + code: Option<&str>, + lvl: Level, + is_header: bool, + show_snippet: bool) + -> io::Result<()> { + if is_header { + if self.first { + self.first = false; + } else { + if !self.old_school { + write!(self.dst, "\n")?; + } + } + } - fn emit_(&mut self, - rsp: &RenderSpan, - msg: &str, - code: Option<&str>, - lvl: Level) - -> io::Result<()> { - let msp = rsp.span(); - let bounds = msp.to_span_bounds(); - - let ss = if bounds == COMMAND_LINE_SP { - "".to_string() - } else if let EndSpan(_) = *rsp { - let span_end = Span { lo: bounds.hi, hi: bounds.hi, expn_id: bounds.expn_id}; - self.cm.span_to_string(span_end) - } else { - self.cm.span_to_string(bounds) - }; + match code { + Some(code) if self.registry.as_ref() + .and_then(|registry| registry.find_description(code)) + .is_some() => { + let code_with_explain = String::from("--explain ") + code; + if self.old_school { + let loc = match rsp.span().primary_span() { + Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), + Some(ps) => self.cm.span_to_string(ps), + None => "".to_string() + }; + print_diagnostic(&mut self.dst, &loc, lvl, msg, Some(code))? + } + else { + print_diagnostic(&mut self.dst, "", lvl, msg, Some(&code_with_explain))? + } + } + _ => { + if self.old_school { + let loc = match rsp.span().primary_span() { + Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), + Some(ps) => self.cm.span_to_string(ps), + None => "".to_string() + }; + print_diagnostic(&mut self.dst, &loc, lvl, msg, code)? + } + else { + print_diagnostic(&mut self.dst, "", lvl, msg, code)? + } + } + } - print_diagnostic(&mut self.dst, &ss[..], lvl, msg, code)?; + if !show_snippet { + return Ok(()); + } + // Watch out for various nasty special spans; don't try to + // print any filename or anything for those. + match rsp.span().primary_span() { + Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => { + return Ok(()); + } + _ => { } + } + + // Otherwise, print out the snippet etc as needed. match *rsp { - FullSpan(_) => { + FullSpan(ref msp) => { self.highlight_lines(msp, lvl)?; - self.print_macro_backtrace(bounds)?; - } - EndSpan(_) => { - self.end_highlight_lines(msp, lvl)?; - self.print_macro_backtrace(bounds)?; + if let Some(primary_span) = msp.primary_span() { + self.print_macro_backtrace(primary_span)?; + } } Suggestion(ref suggestion) => { self.highlight_suggestion(suggestion)?; - self.print_macro_backtrace(bounds)?; - } - FileLine(..) => { - // no source text in this case! + if let Some(primary_span) = rsp.span().primary_span() { + self.print_macro_backtrace(primary_span)?; + } } } - - if let Some(code) = code { - if let Some(_) = self.registry.as_ref() - .and_then(|registry| registry.find_description(code)) { - print_diagnostic(&mut self.dst, &ss[..], Help, - &format!("run `rustc --explain {}` to see a \ - detailed explanation", code), None)?; + if self.old_school { + match code { + Some(code) if self.registry.as_ref() + .and_then(|registry| registry.find_description(code)) + .is_some() => { + let loc = match rsp.span().primary_span() { + Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(), + Some(ps) => self.cm.span_to_string(ps), + None => "".to_string() + }; + let msg = "run `rustc --explain ".to_string() + &code.to_string() + + "` to see a detailed explanation"; + print_diagnostic(&mut self.dst, &loc, Level::Help, &msg, + None)? + } + _ => () } } Ok(()) @@ -217,7 +326,8 @@ impl EmitterWriter { fn highlight_suggestion(&mut self, suggestion: &CodeSuggestion) -> io::Result<()> { - let lines = self.cm.span_to_lines(suggestion.msp.to_span_bounds()).unwrap(); + let primary_span = suggestion.msp.primary_span().unwrap(); + let lines = self.cm.span_to_lines(primary_span).unwrap(); assert!(!lines.lines.is_empty()); let complete = suggestion.splice_lines(&self.cm); @@ -251,325 +361,52 @@ impl EmitterWriter { lvl: Level) -> io::Result<()> { - let lines = match self.cm.span_to_lines(msp.to_span_bounds()) { - Ok(lines) => lines, - Err(_) => { - write!(&mut self.dst, "(internal compiler error: unprintable span)\n")?; - return Ok(()); - } - }; - - let fm = &*lines.file; - if let None = fm.src { - return Ok(()); - } - - let display_line_infos = &lines.lines[..]; - assert!(display_line_infos.len() > 0); - - // Calculate the widest number to format evenly and fix #11715 - let digits = line_num_max_digits(display_line_infos.last().unwrap()); - let first_line_index = display_line_infos.first().unwrap().line_index; - - let skip = fm.name.chars().count() + digits + 2; - - let mut spans = msp.spans.iter().peekable(); - let mut lines = display_line_infos.iter(); - let mut prev_line_index = first_line_index.wrapping_sub(1); - - // Display at most MAX_HIGHLIGHT_LINES lines. - let mut remaining_err_lines = MAX_HIGHLIGHT_LINES; - - // To emit a overflowed spans code-lines *AFTER* the rendered spans - let mut overflowed_buf = String::new(); - let mut overflowed = false; - - // FIXME (#8706) - 'l: loop { - if remaining_err_lines <= 0 { - break; - } - let line = match lines.next() { - Some(l) => l, - None => break, - }; - - // Skip is the number of characters we need to skip because they are - // part of the 'filename:line ' part of the code line. - let mut s: String = ::std::iter::repeat(' ').take(skip).collect(); - let mut col = skip; - let mut lastc = ' '; - - let cur_line_str = fm.get_line(line.line_index).unwrap(); - let mut line_chars = cur_line_str.chars().enumerate().peekable(); - let mut line_spans = 0; - - // Assemble spans for this line - loop { - // Peek here to preserve the span if it doesn't belong to this line - let sp = match spans.peek() { - Some(sp) => **sp, - None => break, - }; - let lo = self.cm.lookup_char_pos(sp.lo); - let hi = self.cm.lookup_char_pos(sp.hi); - let line_num = line.line_index + 1; - - if !(lo.line <= line_num && hi.line >= line_num) { - // This line is not contained in the span - if overflowed { - // Never elide the final line of an overflowed span - prev_line_index = line.line_index - 1; - overflowed = false; - break; - } - - if line_spans == 0 { - continue 'l; - } else { - // This line is finished, now render the spans we've assembled - break; - } + let mut snippet_data = SnippetData::new(self.cm.clone(), + msp.primary_span()); + if self.old_school { + let mut output_vec = vec![]; + + for span_label in msp.span_labels() { + let mut snippet_data = snippet_data.clone(); + + snippet_data.push(span_label.span, + span_label.is_primary, + span_label.label); + if span_label.is_primary { + output_vec.insert(0, snippet_data); } - spans.next(); - line_spans += 1; - - if lo.line != hi.line { - // Assemble extra code lines to be emitted after this lines spans - // (substract `2` because the first and last line are rendered normally) - let max_lines = cmp::min(remaining_err_lines, MAX_SP_LINES) - 2; - prev_line_index = line.line_index; - let count = cmp::min((hi.line - lo.line - 1), max_lines); - for _ in 0..count { - let line = match lines.next() { - Some(l) => l, - None => break, - }; - let line_str = fm.get_line(line.line_index).unwrap(); - overflowed_buf.push_str(&format!("{}:{:>width$} {}\n", - fm.name, - line.line_index + 1, - line_str, - width=digits)); - remaining_err_lines -= 1; - prev_line_index += 1 - } - // Remember that the span overflowed to ensure - // that we emit its last line exactly once - // (other spans may, or may not, start on it) - overflowed = true; - break; - } - - for (pos, ch) in line_chars.by_ref() { - lastc = ch; - if pos >= lo.col.to_usize() { break; } - // Whenever a tab occurs on the code line, we insert one on - // the error-point-squiggly-line as well (instead of a space). - // That way the squiggly line will usually appear in the correct - // position. - match ch { - '\t' => { - col += 8 - col%8; - s.push('\t'); - }, - _ => { - col += 1; - s.push(' '); - }, - } + else { + output_vec.push(snippet_data); } + } - s.push('^'); - let col_ptr = col; - let count = match lastc { - // Most terminals have a tab stop every eight columns by default - '\t' => 8 - col%8, - _ => 1, - }; - col += count; - s.extend(::std::iter::repeat('~').take(count)); - - let hi = self.cm.lookup_char_pos(sp.hi); - if hi.col != lo.col { - let mut chars = line_chars.by_ref(); - loop { - // We peek here to preserve the value for the next span - let (pos, ch) = match chars.peek() { - Some(elem) => *elem, - None => break, - }; - if pos >= hi.col.to_usize() { break; } - let count = match ch { - '\t' => 8 - col%8, - _ => 1, - }; - col += count; - s.extend(::std::iter::repeat('~').take(count)); - - chars.next(); + for snippet_data in output_vec.iter() { + let rendered_lines = snippet_data.render_lines(); + for rendered_line in &rendered_lines { + for styled_string in &rendered_line.text { + self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?; + write!(&mut self.dst, "{}", styled_string.text)?; + self.dst.reset_attrs()?; } + write!(&mut self.dst, "\n")?; } - if (col - col_ptr) > 0 { - // One extra squiggly is replaced by a "^" - s.pop(); - } - } - - // If we elided something put an ellipsis. - if prev_line_index != line.line_index.wrapping_sub(1) && !overflowed { - write!(&mut self.dst, "{0:1$}...\n", "", skip)?; - } - - // Print offending code-line - remaining_err_lines -= 1; - write!(&mut self.dst, "{}:{:>width$} {}\n", - fm.name, - line.line_index + 1, - cur_line_str, - width=digits)?; - - if s.len() > skip { - // Render the spans we assembled previously (if any). - println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()), - "{}", s)?; - } - - if !overflowed_buf.is_empty() { - // Print code-lines trailing the rendered spans (when a span overflows) - write!(&mut self.dst, "{}", &overflowed_buf)?; - overflowed_buf.clear(); - } else { - prev_line_index = line.line_index; } } - - // If we elided something, put an ellipsis. - if lines.next().is_some() { - write!(&mut self.dst, "{0:1$}...\n", "", skip)?; - } - Ok(()) - } - - /// Here are the differences between this and the normal `highlight_lines`: - /// `end_highlight_lines` will always put arrow on the last byte of each - /// span (instead of the first byte). Also, when a span is too long (more - /// than 6 lines), `end_highlight_lines` will print the first line, then - /// dot dot dot, then last line, whereas `highlight_lines` prints the first - /// six lines. - #[allow(deprecated)] - fn end_highlight_lines(&mut self, - msp: &MultiSpan, - lvl: Level) - -> io::Result<()> { - let lines = match self.cm.span_to_lines(msp.to_span_bounds()) { - Ok(lines) => lines, - Err(_) => { - write!(&mut self.dst, "(internal compiler error: unprintable span)\n")?; - return Ok(()); + else { + for span_label in msp.span_labels() { + snippet_data.push(span_label.span, + span_label.is_primary, + span_label.label); } - }; - - let fm = &*lines.file; - if let None = fm.src { - return Ok(()); - } - - let lines = &lines.lines[..]; - - // Calculate the widest number to format evenly - let first_line = lines.first().unwrap(); - let last_line = lines.last().unwrap(); - let digits = line_num_max_digits(last_line); - - let skip = fm.name.chars().count() + digits + 2; - - let mut spans = msp.spans.iter().peekable(); - let mut lines = lines.iter(); - let mut prev_line_index = first_line.line_index.wrapping_sub(1); - - // Display at most MAX_HIGHLIGHT_LINES lines. - let mut remaining_err_lines = MAX_HIGHLIGHT_LINES; - - 'l: loop { - if remaining_err_lines <= 0 { - break; - } - let line = match lines.next() { - Some(line) => line, - None => break, - }; - - // Skip is the number of characters we need to skip because they are - // part of the 'filename:line ' part of the previous line. - let mut s: String = ::std::iter::repeat(' ').take(skip).collect(); - - let line_str = fm.get_line(line.line_index).unwrap(); - let mut line_chars = line_str.chars().enumerate(); - let mut line_spans = 0; - - loop { - // Peek here to preserve the span if it doesn't belong to this line - let sp = match spans.peek() { - Some(sp) => **sp, - None => break, - }; - let lo = self.cm.lookup_char_pos(sp.lo); - let hi = self.cm.lookup_char_pos(sp.hi); - let elide_sp = (hi.line - lo.line) >= MAX_SP_LINES; - - let line_num = line.line_index + 1; - if !(lo.line <= line_num && hi.line >= line_num) { - // This line is not contained in the span - if line_spans == 0 { - continue 'l; - } else { - // This line is finished, now render the spans we've assembled - break - } - } else if hi.line > line_num { - if elide_sp && lo.line < line_num { - // This line is inbetween the first and last line of the span, - // so we may want to elide it. - continue 'l; - } else { - break - } - } - line_spans += 1; - spans.next(); - - for (pos, ch) in line_chars.by_ref() { - // Span seems to use half-opened interval, so subtract 1 - if pos >= hi.col.to_usize() - 1 { break; } - // Whenever a tab occurs on the previous line, we insert one on - // the error-point-squiggly-line as well (instead of a space). - // That way the squiggly line will usually appear in the correct - // position. - match ch { - '\t' => s.push('\t'), - _ => s.push(' '), - } + let rendered_lines = snippet_data.render_lines(); + for rendered_line in &rendered_lines { + for styled_string in &rendered_line.text { + self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?; + write!(&mut self.dst, "{}", styled_string.text)?; + self.dst.reset_attrs()?; } - s.push('^'); - } - - if prev_line_index != line.line_index.wrapping_sub(1) { - // If we elided something, put an ellipsis. - write!(&mut self.dst, "{0:1$}...\n", "", skip)?; + write!(&mut self.dst, "\n")?; } - - // Print offending code-lines - write!(&mut self.dst, "{}:{:>width$} {}\n", fm.name, - line.line_index + 1, line_str, width=digits)?; - remaining_err_lines -= 1; - - if s.len() > skip { - // Render the spans we assembled previously (if any) - println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()), - "{}", s)?; - } - prev_line_index = line.line_index; } Ok(()) } @@ -609,17 +446,29 @@ fn print_diagnostic(dst: &mut Destination, code: Option<&str>) -> io::Result<()> { if !topic.is_empty() { - write!(dst, "{} ", topic)?; + let old_school = check_old_skool(); + if !old_school { + write!(dst, "{}: ", topic)?; + } + else { + write!(dst, "{} ", topic)?; + } + dst.reset_attrs()?; } - - print_maybe_styled!(dst, term::Attr::ForegroundColor(lvl.color()), - "{}: ", lvl.to_string())?; - print_maybe_styled!(dst, term::Attr::Bold, "{}", msg)?; + dst.start_attr(term::Attr::Bold)?; + dst.start_attr(term::Attr::ForegroundColor(lvl.color()))?; + write!(dst, "{}", lvl.to_string())?; + dst.reset_attrs()?; + write!(dst, ": ")?; + dst.start_attr(term::Attr::Bold)?; + write!(dst, "{}", msg)?; if let Some(code) = code { let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA); print_maybe_styled!(dst, style, " [{}]", code.clone())?; } + + dst.reset_attrs()?; write!(dst, "\n")?; Ok(()) } @@ -660,6 +509,51 @@ impl Destination { } } + fn apply_style(&mut self, + lvl: Level, + _kind: &RenderedLineKind, + style: Style) + -> io::Result<()> { + match style { + Style::FileNameStyle | + Style::LineAndColumn => { + } + Style::LineNumber => { + self.start_attr(term::Attr::Bold)?; + self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?; + } + Style::Quotation => { + } + Style::UnderlinePrimary | Style::LabelPrimary => { + self.start_attr(term::Attr::Bold)?; + self.start_attr(term::Attr::ForegroundColor(lvl.color()))?; + } + Style::UnderlineSecondary | Style::LabelSecondary => { + self.start_attr(term::Attr::Bold)?; + self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?; + } + Style::NoStyle => { + } + } + Ok(()) + } + + fn start_attr(&mut self, attr: term::Attr) -> io::Result<()> { + match *self { + Terminal(ref mut t) => { t.attr(attr)?; } + Raw(_) => { } + } + Ok(()) + } + + fn reset_attrs(&mut self) -> io::Result<()> { + match *self { + Terminal(ref mut t) => { t.reset()?; } + Raw(_) => { } + } + Ok(()) + } + fn print_maybe_styled(&mut self, args: fmt::Arguments, color: term::Attr, @@ -741,7 +635,7 @@ mod test { /// 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 } } @@ -767,7 +661,7 @@ mod test { dreizehn "; let file = cm.new_filemap_and_lines("dummy.txt", content); - let start = file.lines.borrow()[7]; + let start = file.lines.borrow()[10]; let end = file.lines.borrow()[11]; let sp = mk_sp(start, end); let lvl = Level::Error; @@ -777,12 +671,12 @@ mod test { let vec = data.lock().unwrap().clone(); let vec: &[u8] = &vec; let str = from_utf8(vec).unwrap(); - println!("{}", str); - assert_eq!(str, "dummy.txt: 8 line8\n\ - dummy.txt: 9 line9\n\ - dummy.txt:10 line10\n\ - dummy.txt:11 e-lä-vän\n\ - dummy.txt:12 tolv\n"); + println!("r#\"\n{}\"#", str); + assert_eq!(str, &r#" + --> dummy.txt:11:1 +11 |> e-lä-vän + |> ^ +"#[1..]); } #[test] @@ -790,7 +684,7 @@ mod test { // Test that a `MultiSpan` containing a single span splices a substition correctly 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 sp = span_from_selection(inputtext, selection); let msp: MultiSpan = sp.into(); @@ -808,51 +702,25 @@ mod test { } #[test] - fn test_multiple_span_splice() { - // Test that a `MultiSpan` containing multiple spans splices substitions on - // several lines correctly + fn test_multi_span_splice() { + // Test that a `MultiSpan` containing multiple spans splices a substition correctly let cm = CodeMap::new(); - let inp = "aaaaabbbbBB\nZZ\nZZ\nCCCDDDDDdddddeee"; - let sp1 = " ^~~~~~\n \n \n "; - let sp2 = " \n \n \n^~~~~~ "; - let sp3 = " \n \n \n ^~~ "; - let sp4 = " \n \n \n ^~~~ "; - - let span_eq = |sp, eq| assert_eq!(&cm.span_to_snippet(sp).unwrap(), eq); - - cm.new_filemap_and_lines("blork.rs", inp); - let sp1 = span_from_selection(inp, sp1); - let sp2 = span_from_selection(inp, sp2); - let sp3 = span_from_selection(inp, sp3); - let sp4 = span_from_selection(inp, sp4); - span_eq(sp1, "bbbbBB"); - span_eq(sp2, "CCCDDD"); - span_eq(sp3, "ddd"); - span_eq(sp4, "ddee"); - - let substitutes: Vec = ["1", "2", "3", "4"].iter().map(|x|x.to_string()).collect(); - let expected = "aaaaa1\nZZ\nZZ\n2DD34e"; - - let test = |msp| { - let suggest = CodeSuggestion { - msp: msp, - substitutes: substitutes.clone(), - }; - let actual = suggest.splice_lines(&cm); - assert_eq!(actual, expected); + let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; + let selection1 = " \n \n \n \n ~ \n"; // intentionally out of order + let selection2 = " \n ~~\n~~~\n~~~~~ \n \n"; + cm.new_filemap_and_lines("blork.rs", inputtext); + let sp1 = span_from_selection(inputtext, selection1); + let sp2 = span_from_selection(inputtext, selection2); + let msp: MultiSpan = MultiSpan::from_spans(vec![sp1, sp2]); + + let expected = "bbbbZZZZZZddddd\neXYZe"; + let suggest = CodeSuggestion { + msp: msp, + substitutes: vec!["ZZZZZZ".to_owned(), + "XYZ".to_owned()] }; - test(MultiSpan { spans: vec![sp1, sp2, sp3, sp4] }); - - // Test ordering and merging by `MultiSpan::push` - let mut msp = MultiSpan::new(); - msp.push_merge(sp2); - msp.push_merge(sp1); - assert_eq!(&msp.spans, &[sp1, sp2]); - msp.push_merge(sp4); - assert_eq!(&msp.spans, &[sp1, sp2, sp4]); - msp.push_merge(sp3); - assert_eq!(&msp.spans, &[sp1, sp2, sp3, sp4]); - test(msp); + + assert_eq!(suggest.splice_lines(&cm), expected); } #[test] @@ -862,17 +730,17 @@ mod test { let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone()); let inp = "_____aaaaaa____bbbbbb__cccccdd_"; - let sp1 = " ^~~~~~ "; - let sp2 = " ^~~~~~ "; - let sp3 = " ^~~~~ "; - let sp4 = " ^~~~ "; - let sp34 = " ^~~~~~~ "; - let sp4_end = " ^~ "; - - let expect_start = "dummy.txt:1 _____aaaaaa____bbbbbb__cccccdd_\n\ - \x20 ^~~~~~ ^~~~~~ ^~~~~~~\n"; - let expect_end = "dummy.txt:1 _____aaaaaa____bbbbbb__cccccdd_\n\ - \x20 ^ ^ ^ ^\n"; + let sp1 = " ~~~~~~ "; + let sp2 = " ~~~~~~ "; + let sp3 = " ~~~~~ "; + let sp4 = " ~~~~ "; + let sp34 = " ~~~~~~~ "; + + let expect_start = &r#" + --> dummy.txt:1:6 +1 |> _____aaaaaa____bbbbbb__cccccdd_ + |> ^^^^^^ ^^^^^^ ^^^^^^^ +"#[1..]; let span = |sp, expected| { let sp = span_from_selection(inp, sp); @@ -885,7 +753,6 @@ mod test { let sp3 = span(sp3, "ccccc"); let sp4 = span(sp4, "ccdd"); let sp34 = span(sp34, "cccccdd"); - let sp4_end = span(sp4_end, "dd"); let spans = vec![sp1, sp2, sp3, sp4]; @@ -894,26 +761,17 @@ mod test { highlight(); let vec = data.lock().unwrap().clone(); let actual = from_utf8(&vec[..]).unwrap(); + println!("actual=\n{}", actual); assert_eq!(actual, expected); }; - let msp = MultiSpan { spans: vec![sp1, sp2, sp34] }; - let msp_end = MultiSpan { spans: vec![sp1, sp2, sp3, sp4_end] }; + let msp = MultiSpan::from_spans(vec![sp1, sp2, sp34]); test(expect_start, &mut || { diag.highlight_lines(&msp, Level::Error).unwrap(); }); - test(expect_end, &mut || { - diag.end_highlight_lines(&msp_end, Level::Error).unwrap(); - }); test(expect_start, &mut || { - for msp in cm.group_spans(spans.clone()) { - diag.highlight_lines(&msp, Level::Error).unwrap(); - } - }); - test(expect_end, &mut || { - for msp in cm.end_group_spans(spans.clone()) { - diag.end_highlight_lines(&msp, Level::Error).unwrap(); - } + let msp = MultiSpan::from_spans(spans.clone()); + diag.highlight_lines(&msp, Level::Error).unwrap(); }); } @@ -950,75 +808,29 @@ mod test { let sp4 = span(10, 10, (2, 3)); let sp5 = span(10, 10, (4, 6)); - let expect0 = "dummy.txt: 5 ccccc\n\ - dummy.txt: 6 xxxxx\n\ - dummy.txt: 7 yyyyy\n\ - \x20 ...\n\ - dummy.txt: 9 ddd__eee_\n\ - \x20 ^~~ ^~~\n\ - \x20 ...\n\ - dummy.txt:11 __f_gg\n\ - \x20 ^ ^~\n"; - - let expect = "dummy.txt: 1 aaaaa\n\ - dummy.txt: 2 aaaaa\n\ - dummy.txt: 3 aaaaa\n\ - dummy.txt: 4 bbbbb\n\ - dummy.txt: 5 ccccc\n\ - dummy.txt: 6 xxxxx\n\ - \x20 ...\n"; - - let expect_g1 = "dummy.txt:1 aaaaa\n\ - dummy.txt:2 aaaaa\n\ - dummy.txt:3 aaaaa\n\ - dummy.txt:4 bbbbb\n\ - dummy.txt:5 ccccc\n\ - dummy.txt:6 xxxxx\n\ - \x20 ...\n"; - - let expect2 = "dummy.txt: 9 ddd__eee_\n\ - \x20 ^~~ ^~~\n\ - \x20 ...\n\ - dummy.txt:11 __f_gg\n\ - \x20 ^ ^~\n"; - - - let expect_end = "dummy.txt: 1 aaaaa\n\ - \x20 ...\n\ - dummy.txt: 7 yyyyy\n\ - \x20 ^\n\ - \x20 ...\n\ - dummy.txt: 9 ddd__eee_\n\ - \x20 ^ ^\n\ - \x20 ...\n\ - dummy.txt:11 __f_gg\n\ - \x20 ^ ^\n"; - - let expect0_end = "dummy.txt: 5 ccccc\n\ - dummy.txt: 6 xxxxx\n\ - dummy.txt: 7 yyyyy\n\ - \x20 ^\n\ - \x20 ...\n\ - dummy.txt: 9 ddd__eee_\n\ - \x20 ^ ^\n\ - \x20 ...\n\ - dummy.txt:11 __f_gg\n\ - \x20 ^ ^\n"; - - let expect_end_g1 = "dummy.txt:1 aaaaa\n\ - \x20 ...\n\ - dummy.txt:7 yyyyy\n\ - \x20 ^\n"; - - let expect2_end = "dummy.txt: 9 ddd__eee_\n\ - \x20 ^ ^\n\ - \x20 ...\n\ - dummy.txt:11 __f_gg\n\ - \x20 ^ ^\n"; - - let expect_groups = [expect2, expect_g1]; - let expect_end_groups = [expect2_end, expect_end_g1]; - let spans = vec![sp3, sp1, sp4, sp2, sp5]; + let expect0 = &r#" + --> dummy.txt:5:1 +5 |> ccccc + |> ^ +... +9 |> ddd__eee_ + |> ^^^ ^^^ +10 |> elided +11 |> __f_gg + |> ^ ^^ +"#[1..]; + + let expect = &r#" + --> dummy.txt:1:1 +1 |> aaaaa + |> ^ +... +9 |> ddd__eee_ + |> ^^^ ^^^ +10 |> elided +11 |> __f_gg + |> ^ ^^ +"#[1..]; macro_rules! test { ($expected: expr, $highlight: expr) => ({ @@ -1034,37 +846,14 @@ mod test { }); } - let msp0 = MultiSpan { spans: vec![sp0, sp2, sp3, sp4, sp5] }; - let msp = MultiSpan { spans: vec![sp1, sp2, sp3, sp4, sp5] }; - let msp2 = MultiSpan { spans: vec![sp2, sp3, sp4, sp5] }; + let msp0 = MultiSpan::from_spans(vec![sp0, sp2, sp3, sp4, sp5]); + let msp = MultiSpan::from_spans(vec![sp1, sp2, sp3, sp4, sp5]); test!(expect0, || { diag.highlight_lines(&msp0, Level::Error).unwrap(); }); - test!(expect0_end, || { - diag.end_highlight_lines(&msp0, Level::Error).unwrap(); - }); test!(expect, || { diag.highlight_lines(&msp, Level::Error).unwrap(); }); - test!(expect_end, || { - diag.end_highlight_lines(&msp, Level::Error).unwrap(); - }); - test!(expect2, || { - diag.highlight_lines(&msp2, Level::Error).unwrap(); - }); - test!(expect2_end, || { - diag.end_highlight_lines(&msp2, Level::Error).unwrap(); - }); - for (msp, expect) in cm.group_spans(spans.clone()).iter().zip(expect_groups.iter()) { - test!(expect, || { - diag.highlight_lines(&msp, Level::Error).unwrap(); - }); - } - for (msp, expect) in cm.group_spans(spans.clone()).iter().zip(expect_end_groups.iter()) { - test!(expect, || { - diag.end_highlight_lines(&msp, Level::Error).unwrap(); - }); - } } } diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/errors/json.rs index 821617bfe89df..93c6268ccaea1 100644 --- a/src/libsyntax/errors/json.rs +++ b/src/libsyntax/errors/json.rs @@ -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, + /// Label that should be placed at this location (if any) + label: Option, /// 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, + 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, - je: &JsonEmitter) - -> DiagnosticSpan { + fn from_span_full(span: Span, + is_primary: bool, + label: Option, + suggestion: Option<&String>, + mut backtrace: vec::IntoIter, + 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 { - 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 { - 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 { 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 { - 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 { match *render_span { - RenderSpan::FileLine(_) | RenderSpan::FullSpan(_) => { None } RenderSpan::Suggestion(ref suggestion) => { Some(suggestion.splice_lines(&self.cm)) } - RenderSpan::EndSpan(_) => { - None - } } } } diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs index 792828b3054e9..f0c665bcb3cdb 100644 --- a/src/libsyntax/errors/mod.rs +++ b/src/libsyntax/errors/mod.rs @@ -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, - span: Option, + span: MultiSpan, children: Vec, } @@ -192,7 +193,7 @@ pub struct DiagnosticBuilder<'a> { struct SubDiagnostic { level: Level, message: String, - span: Option, + span: MultiSpan, render_span: Option, } @@ -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>(&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>(&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>(&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>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, None, Some(EndSpan(sp.into()))); - self - } - pub fn fileline_warn>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Warning, msg, None, Some(FileLine(sp.into()))); - self - } - pub fn fileline_note>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, None, Some(FileLine(sp.into()))); - self - } - pub fn fileline_help>(&mut self, - sp: S, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, None, Some(FileLine(sp.into()))); - self - } - pub fn span>(&mut self, sp: S) -> &mut Self { - self.span = Some(sp.into()); + pub fn set_span>(&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, + span: MultiSpan, render_span: Option) { 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>(&'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>(&'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>(&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>(&self, sp: S, msg: &str) { - self.emit(Some(&sp.into()), msg, Warning); + self.emit(&sp.into(), msg, Warning); } pub fn span_warn_with_code>(&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>(&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>(&self, sp: S, msg: &str) { @@ -541,11 +541,11 @@ impl Handler { *delayed = Some((sp.into(), msg.to_string())); } pub fn span_bug_no_panic>(&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>(&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>(&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(diag: &Handler, opt: Option, 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 +} diff --git a/src/libsyntax/errors/snippet/mod.rs b/src/libsyntax/errors/snippet/mod.rs new file mode 100644 index 0000000000000..e213f623ab85d --- /dev/null +++ b/src/libsyntax/errors/snippet/mod.rs @@ -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 or the MIT license +// , 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, + files: Vec, +} + +#[derive(Clone)] +pub struct FileInfo { + file: Rc, + + /// 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, + + lines: Vec, +} + +#[derive(Clone, Debug)] +struct Line { + line_index: usize, + annotations: Vec, +} + +#[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, +} + +#[derive(Debug)] +pub struct RenderedLine { + pub text: Vec, + pub kind: RenderedLineKind, +} + +#[derive(Debug)] +pub struct StyledString { + pub text: String, + pub style: Style, +} + +#[derive(Debug)] +pub struct StyledBuffer { + text: Vec>, + styles: Vec> +} + +#[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, + line_index: usize, + }, + Annotations, + Elision, +} + +impl SnippetData { + pub fn new(codemap: Rc, + primary_span: Option) // (*) + -> 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) { + 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) -> &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 { + 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 { + fn make_string(self) -> String { + self.into_iter().collect() + } +} + +impl 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 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 { + let mut output: Vec = vec![]; + let mut styled_vec: Vec = 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) { + 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> + // |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // (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) -> Vec { + 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 { + 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) { + 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) +} diff --git a/src/libsyntax/errors/snippet/test.rs b/src/libsyntax/errors/snippet/test.rs new file mode 100644 index 0000000000000..569d11199190c --- /dev/null +++ b/src/libsyntax/errors/snippet/test.rs @@ -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 or the MIT license +// , 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, + source_text: &str, + substring: &str, + n: usize) + -> Span; +} + +impl CodeMapExtension for CodeMap { + fn span_substr(&self, + file: &Rc, + 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`"))); + 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` +... +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..]); +} diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index c0306b8494be2..303187aeba87d 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -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?")); } } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 63d9a699944ea..65df379781ecd 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -222,7 +222,7 @@ fn expand_mac_invoc(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(); } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ed2371fc34823..e269475d1e2c5 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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(); } diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 6cfa1e9847b88..420a41e03b914 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -33,6 +33,7 @@ #![feature(str_escape)] #![feature(unicode)] #![feature(question_mark)] +#![feature(range_contains)] extern crate serialize; extern crate term; diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index b8e320e36e9b4..3aac12d76ffdf 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -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 diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index c2050d2a8f48b..2a9bcfd658c18 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -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(); } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 671a11b57dec1..b9188f5101d3f 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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)?; diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index 4ca3196b9c5ec..91c272c59c4a6 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -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(); diff --git a/src/test/compile-fail/array-not-vector.rs b/src/test/compile-fail/array-not-vector.rs index 6c9b8f81b2faf..1bbccae53a44d 100644 --- a/src/test/compile-fail/array-not-vector.rs +++ b/src/test/compile-fail/array-not-vector.rs @@ -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 } diff --git a/src/test/compile-fail/associated-types-eq-3.rs b/src/test/compile-fail/associated-types-eq-3.rs index f01f2b111c5c1..8c66160e8a36f 100644 --- a/src/test/compile-fail/associated-types-eq-3.rs +++ b/src/test/compile-fail/associated-types-eq-3.rs @@ -32,10 +32,9 @@ fn foo1>(x: I) { fn foo2(x: I) { let _: Bar = x.boo(); //~^ ERROR mismatched types - //~| expected `Bar` - //~| found `::A` - //~| expected struct `Bar` - //~| found associated type + //~| expected type `Bar` + //~| found type `::A` + //~| expected struct `Bar`, found associated type } diff --git a/src/test/compile-fail/associated-types-path-2.rs b/src/test/compile-fail/associated-types-path-2.rs index 68fba56427cc5..cdb7dff692c14 100644 --- a/src/test/compile-fail/associated-types-path-2.rs +++ b/src/test/compile-fail/associated-types-path-2.rs @@ -28,8 +28,7 @@ pub fn f2(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() { } diff --git a/src/test/compile-fail/augmented-assignments.rs b/src/test/compile-fail/augmented-assignments.rs index 221015d512062..8ac6b419295ee 100644 --- a/src/test/compile-fail/augmented-assignments.rs +++ b/src/test/compile-fail/augmented-assignments.rs @@ -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 diff --git a/src/test/compile-fail/bad-const-type.rs b/src/test/compile-fail/bad-const-type.rs index f05c8c31f1024..ee6ac33072792 100644 --- a/src/test/compile-fail/bad-const-type.rs +++ b/src/test/compile-fail/bad-const-type.rs @@ -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); } diff --git a/src/test/compile-fail/bad-main.rs b/src/test/compile-fail/bad-main.rs index 321dca8989134..1253f7569e7e8 100644 --- a/src/test/compile-fail/bad-main.rs +++ b/src/test/compile-fail/bad-main.rs @@ -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 diff --git a/src/test/compile-fail/binop-move-semantics.rs b/src/test/compile-fail/binop-move-semantics.rs index cff0064497aff..0cc6ea3e984d9 100644 --- a/src/test/compile-fail/binop-move-semantics.rs +++ b/src/test/compile-fail/binop-move-semantics.rs @@ -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() {} diff --git a/src/test/compile-fail/block-must-not-have-result-while.rs b/src/test/compile-fail/block-must-not-have-result-while.rs index ba6340ed395ee..a0fb470e1e4d0 100644 --- a/src/test/compile-fail/block-must-not-have-result-while.rs +++ b/src/test/compile-fail/block-must-not-have-result-while.rs @@ -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 } } diff --git a/src/test/compile-fail/borrowck/borrowck-box-insensitivity.rs b/src/test/compile-fail/borrowck/borrowck-box-insensitivity.rs index 7c3d632078fe2..bde3212c5bc63 100644 --- a/src/test/compile-fail/borrowck/borrowck-box-insensitivity.rs +++ b/src/test/compile-fail/borrowck/borrowck-box-insensitivity.rs @@ -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` + //~| 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` + //~| 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` + //~| 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`, 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` + //~| 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`, 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` + //~| 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`, 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` + //~| 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(); diff --git a/src/test/compile-fail/borrowck/borrowck-closures-mut-of-imm.rs b/src/test/compile-fail/borrowck/borrowck-closures-mut-of-imm.rs index 40f9be2dd8230..dc2f0e8395f08 100644 --- a/src/test/compile-fail/borrowck/borrowck-closures-mut-of-imm.rs +++ b/src/test/compile-fail/borrowck/borrowck-closures-mut-of-imm.rs @@ -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() { diff --git a/src/test/compile-fail/borrowck/borrowck-closures-unique.rs b/src/test/compile-fail/borrowck/borrowck-closures-unique.rs index 3646a68f06fd7..1b22dc4d2c6af 100644 --- a/src/test/compile-fail/borrowck/borrowck-closures-unique.rs +++ b/src/test/compile-fail/borrowck/borrowck-closures-unique.rs @@ -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) { diff --git a/src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs b/src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs index f09e7ffd7e4b7..56cbe0b187867 100644 --- a/src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs +++ b/src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs @@ -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 } } } diff --git a/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs b/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs index 38e0e27a7b98e..f789d44016eb1 100644 --- a/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs +++ b/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs @@ -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; } diff --git a/src/test/compile-fail/borrowck/borrowck-report-with-custom-diagnostic.rs b/src/test/compile-fail/borrowck/borrowck-report-with-custom-diagnostic.rs index 2b1ff47ee3d94..3ca8cc431e098 100644 --- a/src/test/compile-fail/borrowck/borrowck-report-with-custom-diagnostic.rs +++ b/src/test/compile-fail/borrowck/borrowck-report-with-custom-diagnostic.rs @@ -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 } diff --git a/src/test/compile-fail/closure-wrong-kind.rs b/src/test/compile-fail/closure-wrong-kind.rs index 6792414c36790..a387e4c5ece11 100644 --- a/src/test/compile-fail/closure-wrong-kind.rs +++ b/src/test/compile-fail/closure-wrong-kind.rs @@ -17,6 +17,6 @@ fn bar(_: T) {} fn main() { let x = X; - let closure = |_| foo(x); //~ ERROR E0524 + let closure = |_| foo(x); //~ ERROR E0525 bar(closure); } diff --git a/src/test/compile-fail/coerce-mut.rs b/src/test/compile-fail/coerce-mut.rs index 30c1b66a7b81f..634d12441a120 100644 --- a/src/test/compile-fail/coerce-mut.rs +++ b/src/test/compile-fail/coerce-mut.rs @@ -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 } diff --git a/src/test/compile-fail/coercion-slice.rs b/src/test/compile-fail/coercion-slice.rs index bb4d1693af7e3..bd7e6c2a2131d 100644 --- a/src/test/compile-fail/coercion-slice.rs +++ b/src/test/compile-fail/coercion-slice.rs @@ -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 } diff --git a/src/test/compile-fail/cross-borrow-trait.rs b/src/test/compile-fail/cross-borrow-trait.rs index d60fb1d5d1966..ea9a29c0e2ae5 100644 --- a/src/test/compile-fail/cross-borrow-trait.rs +++ b/src/test/compile-fail/cross-borrow-trait.rs @@ -19,8 +19,7 @@ pub fn main() { // FIXME (#22405): Replace `Box::new` with `box` here when/if possible. let x: Box = Box::new(Foo); let _y: &Trait = x; //~ ERROR mismatched types - //~| expected `&Trait` - //~| found `Box` - //~| expected &-ptr - //~| found box + //~| expected type `&Trait` + //~| found type `Box` + //~| expected &-ptr, found box } diff --git a/src/test/compile-fail/default_ty_param_conflict.rs b/src/test/compile-fail/default_ty_param_conflict.rs index 48c5cd1ff7706..4702b504f157d 100644 --- a/src/test/compile-fail/default_ty_param_conflict.rs +++ b/src/test/compile-fail/default_ty_param_conflict.rs @@ -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 diff --git a/src/test/compile-fail/default_ty_param_conflict_cross_crate.rs b/src/test/compile-fail/default_ty_param_conflict_cross_crate.rs index fc2c49d65affe..b608c6c99be89 100644 --- a/src/test/compile-fail/default_ty_param_conflict_cross_crate.rs +++ b/src/test/compile-fail/default_ty_param_conflict_cross_crate.rs @@ -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` } diff --git a/src/test/compile-fail/destructure-trait-ref.rs b/src/test/compile-fail/destructure-trait-ref.rs index 68d9795710245..d0a31fbce91ed 100644 --- a/src/test/compile-fail/destructure-trait-ref.rs +++ b/src/test/compile-fail/destructure-trait-ref.rs @@ -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; //~^ ERROR mismatched types - //~| expected `T` - //~| found `Box<_>` - //~| expected trait T - //~| found box + //~| expected type `T` + //~| found type `Box<_>` + //~| expected trait T, found box } diff --git a/src/test/compile-fail/dst-bad-assign.rs b/src/test/compile-fail/dst-bad-assign.rs index 2d21d0ebc760b..9e71ad2417792 100644 --- a/src/test/compile-fail/dst-bad-assign.rs +++ b/src/test/compile-fail/dst-bad-assign.rs @@ -45,9 +45,8 @@ pub fn main() { let z: Box = 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 } diff --git a/src/test/compile-fail/dst-bad-coerce4.rs b/src/test/compile-fail/dst-bad-coerce4.rs index c1443bdbb309d..9d4d56cf79190 100644 --- a/src/test/compile-fail/dst-bad-coerce4.rs +++ b/src/test/compile-fail/dst-bad-coerce4.rs @@ -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 } diff --git a/src/test/compile-fail/explicit-self-lifetime-mismatch.rs b/src/test/compile-fail/explicit-self-lifetime-mismatch.rs index 922e58698dd75..b5432fafb1b85 100644 --- a/src/test/compile-fail/explicit-self-lifetime-mismatch.rs +++ b/src/test/compile-fail/explicit-self-lifetime-mismatch.rs @@ -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 } diff --git a/src/test/compile-fail/extern-main-fn.rs b/src/test/compile-fail/extern-main-fn.rs index 05ce3eefda86c..11f299acefa87 100644 --- a/src/test/compile-fail/extern-main-fn.rs +++ b/src/test/compile-fail/extern-main-fn.rs @@ -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 diff --git a/src/test/compile-fail/fn-item-type.rs b/src/test/compile-fail/fn-item-type.rs index c90a7113f1b41..6217a9f16b935 100644 --- a/src/test/compile-fail/fn-item-type.rs +++ b/src/test/compile-fail/fn-item-type.rs @@ -22,26 +22,22 @@ impl Foo for T { /* `foo` is still default here */ } fn main() { eq(foo::, bar::); //~^ ERROR mismatched types - //~| expected `fn(isize) -> isize {foo::}` - //~| found `fn(isize) -> isize {bar::}` - //~| expected fn item - //~| found a different fn item + //~| expected type `fn(isize) -> isize {foo::}` + //~| found type `fn(isize) -> isize {bar::}` + //~| expected fn item, found a different fn item eq(foo::, foo::); //~^ ERROR mismatched types - //~| expected `fn(isize) -> isize {foo::}` - //~| found `fn(isize) -> isize {foo::}` + //~| expected u8, found i8 eq(bar::, bar::>); //~^ ERROR mismatched types - //~| expected `fn(isize) -> isize {bar::}` - //~| found `fn(isize) -> isize {bar::>}` - //~| expected struct `std::string::String` - //~| found struct `std::vec::Vec` + //~| expected type `fn(isize) -> isize {bar::}` + //~| found type `fn(isize) -> isize {bar::>}` + //~| expected struct `std::string::String`, found struct `std::vec::Vec` // Make sure we distinguish between trait methods correctly. eq(::foo, ::foo); //~^ ERROR mismatched types - //~| expected `fn() {::foo}` - //~| found `fn() {::foo}` + //~| expected u8, found u16 } diff --git a/src/test/compile-fail/fn-trait-formatting.rs b/src/test/compile-fail/fn-trait-formatting.rs index 8cbfc520ff449..fd140cd1d391a 100644 --- a/src/test/compile-fail/fn-trait-formatting.rs +++ b/src/test/compile-fail/fn-trait-formatting.rs @@ -16,22 +16,19 @@ fn needs_fn(x: F) where F: Fn(isize) -> isize {} fn main() { let _: () = (box |_: isize| {}) as Box; //~^ ERROR mismatched types - //~| expected `()` - //~| found `Box` - //~| expected () - //~| found box + //~| expected type `()` + //~| found type `Box` + //~| expected (), found box let _: () = (box |_: isize, isize| {}) as Box; //~^ ERROR mismatched types - //~| expected `()` - //~| found `Box` - //~| expected () - //~| found box + //~| expected type `()` + //~| found type `Box` + //~| expected (), found box let _: () = (box || -> isize { unimplemented!() }) as Box isize>; //~^ ERROR mismatched types - //~| expected `()` - //~| found `Box isize>` - //~| expected () - //~| found box + //~| expected type `()` + //~| found type `Box isize>` + //~| expected (), found box needs_fn(1); //~^ ERROR : std::ops::Fn<(isize,)>` diff --git a/src/test/compile-fail/fully-qualified-type-name1.rs b/src/test/compile-fail/fully-qualified-type-name1.rs index fb787e8572c20..5ea8ce2264438 100644 --- a/src/test/compile-fail/fully-qualified-type-name1.rs +++ b/src/test/compile-fail/fully-qualified-type-name1.rs @@ -14,8 +14,7 @@ fn main() { let x: Option; x = 5; //~^ ERROR mismatched types - //~| expected `std::option::Option` - //~| found `_` - //~| expected enum `std::option::Option` - //~| found integral variable + //~| expected type `std::option::Option` + //~| found type `_` + //~| expected enum `std::option::Option`, found integral variable } diff --git a/src/test/compile-fail/fully-qualified-type-name2.rs b/src/test/compile-fail/fully-qualified-type-name2.rs index ab542d90800b2..9ba8a11d536be 100644 --- a/src/test/compile-fail/fully-qualified-type-name2.rs +++ b/src/test/compile-fail/fully-qualified-type-name2.rs @@ -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() { diff --git a/src/test/compile-fail/fully-qualified-type-name4.rs b/src/test/compile-fail/fully-qualified-type-name4.rs index 9242849efc746..3c8fde751f123 100644 --- a/src/test/compile-fail/fully-qualified-type-name4.rs +++ b/src/test/compile-fail/fully-qualified-type-name4.rs @@ -15,10 +15,9 @@ use std::option::Option; fn bar(x: usize) -> Option { return x; //~^ ERROR mismatched types - //~| expected `std::option::Option` - //~| found `usize` - //~| expected enum `std::option::Option` - //~| found usize + //~| expected type `std::option::Option` + //~| found type `usize` + //~| expected enum `std::option::Option`, found usize } fn main() { diff --git a/src/test/compile-fail/generic-type-params-name-repr.rs b/src/test/compile-fail/generic-type-params-name-repr.rs index adf9a98a05c2d..71d7cf792e475 100644 --- a/src/test/compile-fail/generic-type-params-name-repr.rs +++ b/src/test/compile-fail/generic-type-params-name-repr.rs @@ -22,46 +22,40 @@ fn main() { // Ensure that the printed type doesn't include the default type params... let _: Foo = (); //~^ ERROR mismatched types - //~| expected `Foo` - //~| found `()` - //~| expected struct `Foo` - //~| found () + //~| expected type `Foo` + //~| found type `()` + //~| expected struct `Foo`, found () // ...even when they're present, but the same types as the defaults. let _: Foo = (); //~^ ERROR mismatched types - //~| expected `Foo` - //~| found `()` - //~| expected struct `Foo` - //~| found () + //~| expected type `Foo` + //~| found type `()` + //~| expected struct `Foo`, found () // Including cases where the default is using previous type params. let _: HashMap = (); //~^ ERROR mismatched types - //~| expected `HashMap` - //~| found `()` - //~| expected struct `HashMap` - //~| found () + //~| expected type `HashMap` + //~| found type `()` + //~| expected struct `HashMap`, found () let _: HashMap> = (); //~^ ERROR mismatched types - //~| expected `HashMap` - //~| found `()` - //~| expected struct `HashMap` - //~| found () + //~| expected type `HashMap` + //~| found type `()` + //~| expected struct `HashMap`, found () // But not when there's a different type in between. let _: Foo = (); //~^ ERROR mismatched types - //~| expected `Foo` - //~| found `()` - //~| expected struct `Foo` - //~| found () + //~| expected type `Foo` + //~| found type `()` + //~| expected struct `Foo`, found () // And don't print <> at all when there's just defaults. let _: Foo = (); //~^ ERROR mismatched types - //~| expected `Foo` - //~| found `()` - //~| expected struct `Foo` - //~| found () + //~| expected type `Foo` + //~| found type `()` + //~| expected struct `Foo`, found () } diff --git a/src/test/compile-fail/if-branch-types.rs b/src/test/compile-fail/if-branch-types.rs index 2c730531b823a..ca9803f66b20d 100644 --- a/src/test/compile-fail/if-branch-types.rs +++ b/src/test/compile-fail/if-branch-types.rs @@ -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 } diff --git a/src/test/compile-fail/if-let-arm-types.rs b/src/test/compile-fail/if-let-arm-types.rs index d179ec015d231..c7b1e1a62c209 100644 --- a/src/test/compile-fail/if-let-arm-types.rs +++ b/src/test/compile-fail/if-let-arm-types.rs @@ -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 diff --git a/src/test/compile-fail/if-without-else-result.rs b/src/test/compile-fail/if-without-else-result.rs index a9567f4272f50..e8aa1f70ea1dc 100644 --- a/src/test/compile-fail/if-without-else-result.rs +++ b/src/test/compile-fail/if-without-else-result.rs @@ -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); } diff --git a/src/test/compile-fail/integer-literal-suffix-inference.rs b/src/test/compile-fail/integer-literal-suffix-inference.rs index a01db18a34a7f..7a850d90a8747 100644 --- a/src/test/compile-fail/integer-literal-suffix-inference.rs +++ b/src/test/compile-fail/integer-literal-suffix-inference.rs @@ -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 } diff --git a/src/test/compile-fail/integral-variable-unification-error.rs b/src/test/compile-fail/integral-variable-unification-error.rs index 3374f715917c2..99f2d25166891 100644 --- a/src/test/compile-fail/integral-variable-unification-error.rs +++ b/src/test/compile-fail/integral-variable-unification-error.rs @@ -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 } diff --git a/src/test/compile-fail/issue-10176.rs b/src/test/compile-fail/issue-10176.rs index 6e84e777898b6..434b795ff31f5 100644 --- a/src/test/compile-fail/issue-10176.rs +++ b/src/test/compile-fail/issue-10176.rs @@ -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() {} diff --git a/src/test/compile-fail/issue-11319.rs b/src/test/compile-fail/issue-11319.rs index d3e44b71b1c89..8242fa1c2e979 100644 --- a/src/test/compile-fail/issue-11319.rs +++ b/src/test/compile-fail/issue-11319.rs @@ -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 diff --git a/src/test/compile-fail/issue-12997-2.rs b/src/test/compile-fail/issue-12997-2.rs index 8b467c2ba11f9..436d9e91dc72f 100644 --- a/src/test/compile-fail/issue-12997-2.rs +++ b/src/test/compile-fail/issue-12997-2.rs @@ -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 diff --git a/src/test/compile-fail/issue-13359.rs b/src/test/compile-fail/issue-13359.rs index 775412a12ca6a..e33859e8c19a3 100644 --- a/src/test/compile-fail/issue-13359.rs +++ b/src/test/compile-fail/issue-13359.rs @@ -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 } diff --git a/src/test/compile-fail/issue-13466.rs b/src/test/compile-fail/issue-13466.rs index d2c8b679ff69f..17b96411603ef 100644 --- a/src/test/compile-fail/issue-13466.rs +++ b/src/test/compile-fail/issue-13466.rs @@ -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` }; } diff --git a/src/test/compile-fail/issue-13482-2.rs b/src/test/compile-fail/issue-13482-2.rs index e1fe2d06993d5..fe03373a45d9f 100644 --- a/src/test/compile-fail/issue-13482-2.rs +++ b/src/test/compile-fail/issue-13482-2.rs @@ -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) }; } diff --git a/src/test/compile-fail/issue-13482.rs b/src/test/compile-fail/issue-13482.rs index 8d98fe313349b..7ed7f5898b1b7 100644 --- a/src/test/compile-fail/issue-13482.rs +++ b/src/test/compile-fail/issue-13482.rs @@ -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) }; diff --git a/src/test/compile-fail/issue-13624.rs b/src/test/compile-fail/issue-13624.rs index 2a5805790a742..e4ed87c3cb0cd 100644 --- a/src/test/compile-fail/issue-13624.rs +++ b/src/test/compile-fail/issue-13624.rs @@ -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` } } } diff --git a/src/test/compile-fail/issue-14091.rs b/src/test/compile-fail/issue-14091.rs index 3ceb465cb4b23..9c594ef485fd7 100644 --- a/src/test/compile-fail/issue-14091.rs +++ b/src/test/compile-fail/issue-14091.rs @@ -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);} diff --git a/src/test/compile-fail/issue-14541.rs b/src/test/compile-fail/issue-14541.rs index deb8f00cd01c9..84c600d2201e3 100644 --- a/src/test/compile-fail/issue-14541.rs +++ b/src/test/compile-fail/issue-14541.rs @@ -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() { } diff --git a/src/test/compile-fail/issue-15783.rs b/src/test/compile-fail/issue-15783.rs index 9a139021e4c6d..37a2f1582bf08 100644 --- a/src/test/compile-fail/issue-15783.rs +++ b/src/test/compile-fail/issue-15783.rs @@ -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); } diff --git a/src/test/compile-fail/issue-15896.rs b/src/test/compile-fail/issue-15896.rs index 7381ade263b20..35ef9ba2b4bea 100644 --- a/src/test/compile-fail/issue-15896.rs +++ b/src/test/compile-fail/issue-15896.rs @@ -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, }; } diff --git a/src/test/compile-fail/issue-16338.rs b/src/test/compile-fail/issue-16338.rs index 30775a958b57d..da6d081a7acb5 100644 --- a/src/test/compile-fail/issue-16338.rs +++ b/src/test/compile-fail/issue-16338.rs @@ -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` } diff --git a/src/test/compile-fail/issue-16401.rs b/src/test/compile-fail/issue-16401.rs index a90f9fe26e48c..df272a71cee4f 100644 --- a/src/test/compile-fail/issue-16401.rs +++ b/src/test/compile-fail/issue-16401.rs @@ -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!() } } diff --git a/src/test/compile-fail/issue-17033.rs b/src/test/compile-fail/issue-17033.rs index 6010e206920e5..f0fe01b415970 100644 --- a/src/test/compile-fail/issue-17033.rs +++ b/src/test/compile-fail/issue-17033.rs @@ -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() {} diff --git a/src/test/compile-fail/issue-17263.rs b/src/test/compile-fail/issue-17263.rs index 2320bc02baf5e..063afe285fad3 100644 --- a/src/test/compile-fail/issue-17263.rs +++ b/src/test/compile-fail/issue-17263.rs @@ -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 diff --git a/src/test/compile-fail/issue-17283.rs b/src/test/compile-fail/issue-17283.rs index c7d6443663241..98208bcfdbdee 100644 --- a/src/test/compile-fail/issue-17283.rs +++ b/src/test/compile-fail/issue-17283.rs @@ -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); } } diff --git a/src/test/compile-fail/issue-17728.rs b/src/test/compile-fail/issue-17728.rs index 787eb7a3b8878..f508d7123d88f 100644 --- a/src/test/compile-fail/issue-17728.rs +++ b/src/test/compile-fail/issue-17728.rs @@ -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, diff --git a/src/test/compile-fail/issue-17740.rs b/src/test/compile-fail/issue-17740.rs index 4381bf22e2ace..6b9294b2038f1 100644 --- a/src/test/compile-fail/issue-17740.rs +++ b/src/test/compile-fail/issue-17740.rs @@ -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 } } diff --git a/src/test/compile-fail/issue-19109.rs b/src/test/compile-fail/issue-19109.rs index 1ffffa9fc748e..580684e2e140b 100644 --- a/src/test/compile-fail/issue-19109.rs +++ b/src/test/compile-fail/issue-19109.rs @@ -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() { } diff --git a/src/test/compile-fail/issue-19991.rs b/src/test/compile-fail/issue-19991.rs index 6c9b0004f7754..b368daaaf587c 100644 --- a/src/test/compile-fail/issue-19991.rs +++ b/src/test/compile-fail/issue-19991.rs @@ -13,10 +13,9 @@ fn main() { if let Some(homura) = Some("madoka") { //~ ERROR missing an else clause - //~| expected `()` - //~| found `_` - //~| expected () - //~| found integral variable + //~| expected type `()` + //~| found type `_` + //~| expected (), found integral variable 765 }; } diff --git a/src/test/compile-fail/issue-24036.rs b/src/test/compile-fail/issue-24036.rs index 06b058cbfe179..ac7e0f2e9a867 100644 --- a/src/test/compile-fail/issue-24036.rs +++ b/src/test/compile-fail/issue-24036.rs @@ -14,6 +14,9 @@ fn closure_to_loc() { //~^ ERROR mismatched types //~| NOTE no two closures, even if identical, have the same type //~| HELP consider boxing your closure and/or using it as a trait object + //~| expected closure, found a different closure + //~| expected type `[closure + //~| found type `[closure } fn closure_from_match() { @@ -26,6 +29,9 @@ fn closure_from_match() { //~^^^^^^ ERROR match arms have incompatible types //~| NOTE no two closures, even if identical, have the same type //~| HELP consider boxing your closure and/or using it as a trait object + //~| expected closure, found a different closure + //~| expected type `[closure + //~| found type `[closure } fn main() { } diff --git a/src/test/compile-fail/issue-24357.rs b/src/test/compile-fail/issue-24357.rs index f193a07b85af9..5d6b989fc968a 100644 --- a/src/test/compile-fail/issue-24357.rs +++ b/src/test/compile-fail/issue-24357.rs @@ -12,7 +12,9 @@ struct NoCopy; fn main() { let x = NoCopy; let f = move || { let y = x; }; - //~^ NOTE `x` moved into closure environment here because it has type `NoCopy` + //~^ value moved (into closure) here let z = x; //~^ ERROR use of moved value: `x` + //~| value used here after move + //~| move occurs because `x` has type `NoCopy` } diff --git a/src/test/compile-fail/issue-24446.rs b/src/test/compile-fail/issue-24446.rs index a2831fd2b5ac8..cbeac77479811 100644 --- a/src/test/compile-fail/issue-24446.rs +++ b/src/test/compile-fail/issue-24446.rs @@ -10,7 +10,7 @@ fn main() { static foo: Fn() -> u32 = || -> u32 { - //~^ ERROR: mismatched types: + //~^ ERROR: mismatched types 0 }; } diff --git a/src/test/compile-fail/issue-26480.rs b/src/test/compile-fail/issue-26480.rs index 23e4ffb1f3076..adcf8484f7828 100644 --- a/src/test/compile-fail/issue-26480.rs +++ b/src/test/compile-fail/issue-26480.rs @@ -25,6 +25,7 @@ macro_rules! write { write(stdout, $arr.as_ptr() as *const i8, $arr.len() * size_of($arr[0])); //~^ ERROR mismatched types + //~| expected u64, found usize } }} } diff --git a/src/test/compile-fail/issue-27008.rs b/src/test/compile-fail/issue-27008.rs index 2a4b98563ab9f..bdcbaf09177fe 100644 --- a/src/test/compile-fail/issue-27008.rs +++ b/src/test/compile-fail/issue-27008.rs @@ -13,9 +13,8 @@ struct S; fn main() { let b = [0; S]; //~^ ERROR mismatched types - //~| expected `usize` - //~| found `S` - //~| expected usize - //~| found struct `S` + //~| expected type `usize` + //~| found type `S` + //~| expected usize, found struct `S` //~| ERROR expected positive integer for repeat count, found struct } diff --git a/src/test/compile-fail/issue-29084.rs b/src/test/compile-fail/issue-29084.rs index 78913e759a1cc..00d2969a0f67d 100644 --- a/src/test/compile-fail/issue-29084.rs +++ b/src/test/compile-fail/issue-29084.rs @@ -13,10 +13,13 @@ macro_rules! foo { fn bar(d: u8) { } bar(&mut $d); //~^ ERROR mismatched types + //~| expected u8, found &-ptr + //~| expected type `u8` + //~| found type `&mut u8` }} } fn main() { foo!(0u8); - //~^ NOTE in this expansion of foo! + //~^ in this expansion of foo! } diff --git a/src/test/compile-fail/issue-2951.rs b/src/test/compile-fail/issue-2951.rs index d0781b5658087..11ff7ab2476b9 100644 --- a/src/test/compile-fail/issue-2951.rs +++ b/src/test/compile-fail/issue-2951.rs @@ -12,10 +12,9 @@ fn foo(x: T, y: U) { let mut xx = x; xx = y; //~^ ERROR mismatched types - //~| expected `T` - //~| found `U` - //~| expected type parameter - //~| found a different type parameter + //~| expected type `T` + //~| found type `U` + //~| expected type parameter, found a different type parameter } fn main() { diff --git a/src/test/compile-fail/issue-3477.rs b/src/test/compile-fail/issue-3477.rs index 43ef1b59ccf27..0bad7372a12d9 100644 --- a/src/test/compile-fail/issue-3477.rs +++ b/src/test/compile-fail/issue-3477.rs @@ -11,6 +11,5 @@ fn main() { let _p: char = 100; //~^ ERROR mismatched types - //~| expected `char` - //~| found `u8` + //~| expected char, found u8 } diff --git a/src/test/compile-fail/issue-3680.rs b/src/test/compile-fail/issue-3680.rs index fc918c278ef5d..e698e6da5294e 100644 --- a/src/test/compile-fail/issue-3680.rs +++ b/src/test/compile-fail/issue-3680.rs @@ -12,9 +12,8 @@ fn main() { match None { Err(_) => () //~^ 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` } } diff --git a/src/test/compile-fail/issue-4201.rs b/src/test/compile-fail/issue-4201.rs index b5af1f03b635b..58423341cc6f0 100644 --- a/src/test/compile-fail/issue-4201.rs +++ b/src/test/compile-fail/issue-4201.rs @@ -13,10 +13,9 @@ fn main() { 0 } else if false { //~^ ERROR if may be missing an else clause -//~| expected `()` -//~| found `_` -//~| expected () -//~| found integral variable +//~| expected type `()` +//~| found type `_` +//~| expected (), found integral variable 1 }; } diff --git a/src/test/compile-fail/issue-4517.rs b/src/test/compile-fail/issue-4517.rs index a1804b5a2689d..fbd8972cbfaa6 100644 --- a/src/test/compile-fail/issue-4517.rs +++ b/src/test/compile-fail/issue-4517.rs @@ -14,8 +14,7 @@ fn main() { let foo: [u8; 4] = [1; 4]; bar(foo); //~^ ERROR mismatched types - //~| expected `usize` - //~| found `[u8; 4]` - //~| expected usize - //~| found array of 4 elements + //~| expected type `usize` + //~| found type `[u8; 4]` + //~| expected usize, found array of 4 elements } diff --git a/src/test/compile-fail/issue-4968.rs b/src/test/compile-fail/issue-4968.rs index e7cd20f38a1d2..7c0905873df89 100644 --- a/src/test/compile-fail/issue-4968.rs +++ b/src/test/compile-fail/issue-4968.rs @@ -14,8 +14,7 @@ const A: (isize,isize) = (4,2); fn main() { match 42 { A => () } //~^ ERROR mismatched types - //~| expected `_` - //~| found `(isize, isize)` - //~| expected integral variable - //~| found tuple + //~| expected type `_` + //~| found type `(isize, isize)` + //~| expected integral variable, found tuple } diff --git a/src/test/compile-fail/issue-5100.rs b/src/test/compile-fail/issue-5100.rs index 304b6f185fe36..9e78b7b947f98 100644 --- a/src/test/compile-fail/issue-5100.rs +++ b/src/test/compile-fail/issue-5100.rs @@ -16,48 +16,43 @@ enum A { B, C } fn main() { match (true, false) { A::B => (), -//~^ ERROR mismatched types: -//~| expected `(bool, bool)` -//~| found `A` -//~| expected tuple -//~| found enum `A` +//~^ ERROR mismatched types +//~| expected type `(bool, bool)` +//~| found type `A` +//~| expected tuple, found enum `A` _ => () } match (true, false) { (true, false, false) => () //~^ ERROR mismatched types -//~| expected `(bool, bool)` -//~| found `(_, _, _)` -//~| expected a tuple with 2 elements -//~| found one with 3 elements +//~| expected type `(bool, bool)` +//~| found type `(_, _, _)` +//~| expected a tuple with 2 elements, found one with 3 elements } match (true, false) { (true, false, false) => () //~^ ERROR mismatched types -//~| expected `(bool, bool)` -//~| found `(_, _, _)` -//~| expected a tuple with 2 elements -//~| found one with 3 elements +//~| expected type `(bool, bool)` +//~| found type `(_, _, _)` +//~| expected a tuple with 2 elements, found one with 3 elements } match (true, false) { box (true, false) => () //~^ ERROR mismatched types -//~| expected `(bool, bool)` -//~| found `Box<_>` -//~| expected tuple -//~| found box +//~| expected type `(bool, bool)` +//~| found type `Box<_>` +//~| expected tuple, found box } match (true, false) { &(true, false) => () //~^ ERROR mismatched types -//~| expected `(bool, bool)` -//~| found `&_` -//~| expected tuple -//~| found &-ptr +//~| expected type `(bool, bool)` +//~| found type `&_` +//~| expected tuple, found &-ptr } @@ -69,6 +64,5 @@ fn main() { // Make sure none of the errors above were fatal let x: char = true; //~ ERROR mismatched types - //~| expected `char` - //~| found `bool` + //~| expected char, found bool } diff --git a/src/test/compile-fail/issue-5358-1.rs b/src/test/compile-fail/issue-5358-1.rs index 32702d3e2f6fe..d8aad54fd3ee9 100644 --- a/src/test/compile-fail/issue-5358-1.rs +++ b/src/test/compile-fail/issue-5358-1.rs @@ -15,10 +15,9 @@ fn main() { match S(Either::Left(5)) { Either::Right(_) => {} //~^ ERROR mismatched types - //~| expected `S` - //~| found `Either<_, _>` - //~| expected struct `S` - //~| found enum `Either` + //~| expected type `S` + //~| found type `Either<_, _>` + //~| expected struct `S`, found enum `Either` _ => {} } } diff --git a/src/test/compile-fail/issue-5500.rs b/src/test/compile-fail/issue-5500.rs index 565634191be9a..cacbf7656def2 100644 --- a/src/test/compile-fail/issue-5500.rs +++ b/src/test/compile-fail/issue-5500.rs @@ -11,8 +11,7 @@ fn main() { &panic!() //~^ ERROR mismatched types - //~| expected `()` - //~| found `&_` - //~| expected () - //~| found &-ptr + //~| expected type `()` + //~| found type `&_` + //~| expected (), found &-ptr } diff --git a/src/test/compile-fail/issue-7061.rs b/src/test/compile-fail/issue-7061.rs index e261249bc998e..1519d71dd3be2 100644 --- a/src/test/compile-fail/issue-7061.rs +++ b/src/test/compile-fail/issue-7061.rs @@ -13,10 +13,9 @@ struct BarStruct; impl<'a> BarStruct { fn foo(&'a mut self) -> Box { self } //~^ ERROR mismatched types - //~| expected `Box` - //~| found `&'a mut BarStruct` - //~| expected box - //~| found &-ptr + //~| expected type `Box` + //~| found type `&'a mut BarStruct` + //~| expected box, found &-ptr } fn main() {} diff --git a/src/test/compile-fail/issue-7092.rs b/src/test/compile-fail/issue-7092.rs index 4acbcb165ff08..26e1597b1db4b 100644 --- a/src/test/compile-fail/issue-7092.rs +++ b/src/test/compile-fail/issue-7092.rs @@ -15,10 +15,9 @@ fn foo(x: Whatever) { match x { Some(field) => //~^ ERROR mismatched types -//~| expected `Whatever` -//~| found `std::option::Option<_>` -//~| expected enum `Whatever` -//~| found enum `std::option::Option` +//~| expected type `Whatever` +//~| found type `std::option::Option<_>` +//~| expected enum `Whatever`, found enum `std::option::Option` field.access(), } } diff --git a/src/test/compile-fail/issue-7867.rs b/src/test/compile-fail/issue-7867.rs index 95513860b084f..e0de860b0eac3 100644 --- a/src/test/compile-fail/issue-7867.rs +++ b/src/test/compile-fail/issue-7867.rs @@ -16,25 +16,22 @@ fn main() { match (true, false) { A::B => (), //~^ ERROR mismatched types - //~| expected `(bool, bool)` - //~| found `A` - //~| expected tuple - //~| found enum `A` + //~| expected type `(bool, bool)` + //~| found type `A` + //~| expected tuple, found enum `A` _ => () } match &Some(42) { Some(x) => (), //~^ ERROR mismatched types - //~| expected `&std::option::Option<_>` - //~| found `std::option::Option<_>` - //~| expected &-ptr - //~| found enum `std::option::Option` + //~| expected type `&std::option::Option<_>` + //~| found type `std::option::Option<_>` + //~| expected &-ptr, found enum `std::option::Option` None => () //~^ ERROR mismatched types - //~| expected `&std::option::Option<_>` - //~| found `std::option::Option<_>` - //~| expected &-ptr - //~| found enum `std::option::Option` + //~| expected type `&std::option::Option<_>` + //~| found type `std::option::Option<_>` + //~| expected &-ptr, found enum `std::option::Option` } } diff --git a/src/test/compile-fail/issue-9575.rs b/src/test/compile-fail/issue-9575.rs index 94dd787f08635..9295eeb1779b0 100644 --- a/src/test/compile-fail/issue-9575.rs +++ b/src/test/compile-fail/issue-9575.rs @@ -12,6 +12,6 @@ #[start] fn start(argc: isize, argv: *const *const u8, crate_map: *const u8) -> isize { - //~^ ERROR incorrect number of function parameters + //~^ start function has wrong type 0 } diff --git a/src/test/compile-fail/main-wrong-type-2.rs b/src/test/compile-fail/main-wrong-type-2.rs index 09d5765a80f3f..7434a6c960b2d 100644 --- a/src/test/compile-fail/main-wrong-type-2.rs +++ b/src/test/compile-fail/main-wrong-type-2.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() -> char { -//~^ ERROR: main function expects type +//~^ ERROR: main function has wrong type } diff --git a/src/test/compile-fail/main-wrong-type.rs b/src/test/compile-fail/main-wrong-type.rs index d9c617a71720e..431b855d51773 100644 --- a/src/test/compile-fail/main-wrong-type.rs +++ b/src/test/compile-fail/main-wrong-type.rs @@ -14,5 +14,5 @@ struct S { } fn main(foo: S) { -//~^ ERROR: main function expects type +//~^ ERROR: main function has wrong type } diff --git a/src/test/compile-fail/match-range-fail.rs b/src/test/compile-fail/match-range-fail.rs index 05b870b8f41cc..526aa83dec7fd 100644 --- a/src/test/compile-fail/match-range-fail.rs +++ b/src/test/compile-fail/match-range-fail.rs @@ -28,6 +28,5 @@ fn main() { _ => { } }; //~^^^ ERROR mismatched types in range - //~| expected char - //~| found integral variable + //~| expected char, found integral variable } diff --git a/src/test/compile-fail/match-struct.rs b/src/test/compile-fail/match-struct.rs index 5bda37896879b..0dbdda1f9ba1e 100644 --- a/src/test/compile-fail/match-struct.rs +++ b/src/test/compile-fail/match-struct.rs @@ -16,10 +16,9 @@ fn main() { match (S { a: 1 }) { E::C(_) => (), //~^ ERROR mismatched types - //~| expected `S` - //~| found `E` - //~| expected struct `S` - //~| found enum `E` + //~| expected type `S` + //~| found type `E` + //~| expected struct `S`, found enum `E` _ => () } } diff --git a/src/test/compile-fail/match-vec-mismatch-2.rs b/src/test/compile-fail/match-vec-mismatch-2.rs index 0bbba8861217d..2831499c73d87 100644 --- a/src/test/compile-fail/match-vec-mismatch-2.rs +++ b/src/test/compile-fail/match-vec-mismatch-2.rs @@ -14,9 +14,8 @@ fn main() { match () { [()] => { } //~^ ERROR mismatched types - //~| expected `()` - //~| found `&[_]` - //~| expected () - //~| found &-ptr + //~| expected type `()` + //~| found type `&[_]` + //~| expected (), found &-ptr } } diff --git a/src/test/compile-fail/method-self-arg-1.rs b/src/test/compile-fail/method-self-arg-1.rs index 57a96bb9a26ea..ffa5287d4b2c3 100644 --- a/src/test/compile-fail/method-self-arg-1.rs +++ b/src/test/compile-fail/method-self-arg-1.rs @@ -19,13 +19,11 @@ impl Foo { fn main() { let x = Foo; Foo::bar(x); //~ ERROR mismatched types - //~| expected `&Foo` - //~| found `Foo` - //~| expected &-ptr - //~| found struct `Foo` + //~| expected type `&Foo` + //~| found type `Foo` + //~| expected &-ptr, found struct `Foo` Foo::bar(&42); //~ ERROR mismatched types - //~| expected `&Foo` - //~| found `&_` - //~| expected struct `Foo` - //~| found integral variable + //~| expected type `&Foo` + //~| found type `&_` + //~| expected struct `Foo`, found integral variable } diff --git a/src/test/compile-fail/moves-based-on-type-distribute-copy-over-paren.rs b/src/test/compile-fail/moves-based-on-type-distribute-copy-over-paren.rs index f30360af46eb4..02c09aa7d69a2 100644 --- a/src/test/compile-fail/moves-based-on-type-distribute-copy-over-paren.rs +++ b/src/test/compile-fail/moves-based-on-type-distribute-copy-over-paren.rs @@ -16,13 +16,17 @@ fn touch(_a: &A) {} fn f00() { let x = "hi".to_string(); - let _y = Foo { f:x }; //~ NOTE `x` moved here + let _y = Foo { f:x }; + //~^ value moved here touch(&x); //~ ERROR use of moved value: `x` + //~^ value used here after move + //~| move occurs because `x` has type `std::string::String` } fn f05() { let x = "hi".to_string(); - let _y = Foo { f:(((x))) }; //~ NOTE `x` moved here + let _y = Foo { f:(((x))) }; + //~^ value moved here touch(&x); //~ ERROR use of moved value: `x` } diff --git a/src/test/compile-fail/moves-based-on-type-match-bindings.rs b/src/test/compile-fail/moves-based-on-type-match-bindings.rs index 7d209467caf2a..bcbb8dbfad121 100644 --- a/src/test/compile-fail/moves-based-on-type-match-bindings.rs +++ b/src/test/compile-fail/moves-based-on-type-match-bindings.rs @@ -24,6 +24,8 @@ fn f10() { }; touch(&x); //~ ERROR use of partially moved value: `x` + //~^ value used here after move + //~| move occurs because `x.f` has type `std::string::String` } fn main() {} diff --git a/src/test/compile-fail/mut-pattern-mismatched.rs b/src/test/compile-fail/mut-pattern-mismatched.rs index 9eb24c81960ca..63e7dbd30def2 100644 --- a/src/test/compile-fail/mut-pattern-mismatched.rs +++ b/src/test/compile-fail/mut-pattern-mismatched.rs @@ -14,8 +14,8 @@ fn main() { // (separate lines to ensure the spans are accurate) let &_ //~ ERROR mismatched types - //~| expected `&mut _` - //~| found `&_` + //~| expected type `&mut _` + //~| found type `&_` //~| values differ in mutability = foo; let &mut _ = foo; @@ -23,8 +23,8 @@ fn main() { let bar = &1; let &_ = bar; let &mut _ //~ ERROR mismatched types - //~| expected `&_` - //~| found `&mut _` + //~| expected type `&_` + //~| found type `&mut _` //~| values differ in mutability = bar; } diff --git a/src/test/compile-fail/noexporttypeexe.rs b/src/test/compile-fail/noexporttypeexe.rs index 687e1e49ee844..c950ef5b68002 100644 --- a/src/test/compile-fail/noexporttypeexe.rs +++ b/src/test/compile-fail/noexporttypeexe.rs @@ -19,8 +19,7 @@ fn main() { // not convertible to a path. let x: isize = noexporttypelib::foo(); //~^ ERROR mismatched types - //~| expected `isize` - //~| found `std::option::Option` - //~| expected isize - //~| found enum `std::option::Option` + //~| expected type `isize` + //~| found type `std::option::Option` + //~| expected isize, found enum `std::option::Option` } diff --git a/src/test/compile-fail/occurs-check-2.rs b/src/test/compile-fail/occurs-check-2.rs index fd2903a85ddb0..5cb60079fa4b8 100644 --- a/src/test/compile-fail/occurs-check-2.rs +++ b/src/test/compile-fail/occurs-check-2.rs @@ -16,7 +16,7 @@ fn main() { g = f; f = box g; //~^ ERROR mismatched types - //~| expected `_` - //~| found `Box<_>` + //~| expected type `_` + //~| found type `Box<_>` //~| cyclic type of infinite size } diff --git a/src/test/compile-fail/occurs-check.rs b/src/test/compile-fail/occurs-check.rs index 036fcc1b9d779..499124cb0573b 100644 --- a/src/test/compile-fail/occurs-check.rs +++ b/src/test/compile-fail/occurs-check.rs @@ -14,7 +14,7 @@ fn main() { let f; f = box f; //~^ ERROR mismatched types - //~| expected `_` - //~| found `Box<_>` + //~| expected type `_` + //~| found type `Box<_>` //~| cyclic type of infinite size } diff --git a/src/test/compile-fail/pattern-error-continue.rs b/src/test/compile-fail/pattern-error-continue.rs index 9b67595800320..d9f3bb3c40f8d 100644 --- a/src/test/compile-fail/pattern-error-continue.rs +++ b/src/test/compile-fail/pattern-error-continue.rs @@ -31,17 +31,15 @@ fn main() { match 'c' { S { .. } => (), //~^ ERROR mismatched types - //~| expected `char` - //~| found `S` - //~| expected char - //~| found struct `S` + //~| expected type `char` + //~| found type `S` + //~| expected char, found struct `S` _ => () } f(true); //~^ ERROR mismatched types - //~| expected `char` - //~| found `bool` + //~| expected char, found bool match () { E::V => {} //~ ERROR failed to resolve. Use of undeclared type or module `E` diff --git a/src/test/compile-fail/pptypedef.rs b/src/test/compile-fail/pptypedef.rs index 1a1c87ff47d46..7ece52e753786 100644 --- a/src/test/compile-fail/pptypedef.rs +++ b/src/test/compile-fail/pptypedef.rs @@ -13,11 +13,9 @@ fn let_in(x: T, f: F) where F: FnOnce(T) {} fn main() { let_in(3u32, |i| { assert!(i == 3i32); }); //~^ ERROR mismatched types - //~| expected `u32` - //~| found `i32` + //~| expected u32, found i32 let_in(3i32, |i| { assert!(i == 3u32); }); //~^ ERROR mismatched types - //~| expected `i32` - //~| found `u32` + //~| expected i32, found u32 } diff --git a/src/test/compile-fail/ptr-coercion.rs b/src/test/compile-fail/ptr-coercion.rs index 18e210076cb55..ff627e69d4c58 100644 --- a/src/test/compile-fail/ptr-coercion.rs +++ b/src/test/compile-fail/ptr-coercion.rs @@ -15,19 +15,19 @@ pub fn main() { // *const -> *mut let x: *const isize = &42; let x: *mut isize = x; //~ ERROR mismatched types - //~| expected `*mut isize` - //~| found `*const isize` + //~| expected type `*mut isize` + //~| found type `*const isize` //~| values differ in mutability // & -> *mut let x: *mut isize = &42; //~ ERROR mismatched types - //~| expected `*mut isize` - //~| found `&isize` + //~| expected type `*mut isize` + //~| found type `&isize` //~| values differ in mutability let x: *const isize = &42; let x: *mut isize = x; //~ ERROR mismatched types - //~| expected `*mut isize` - //~| found `*const isize` + //~| expected type `*mut isize` + //~| found type `*const isize` //~| values differ in mutability } diff --git a/src/test/compile-fail/ref-suggestion.rs b/src/test/compile-fail/ref-suggestion.rs index 815f752663223..0a0867195d976 100644 --- a/src/test/compile-fail/ref-suggestion.rs +++ b/src/test/compile-fail/ref-suggestion.rs @@ -11,22 +11,16 @@ fn main() { let x = vec![1]; let y = x; - //~^ HELP use a `ref` binding as shown - //~| SUGGESTION let ref y = x; x; //~ ERROR use of moved value let x = vec![1]; let mut y = x; - //~^ HELP use a `ref` binding as shown - //~| SUGGESTION let ref mut y = x; x; //~ ERROR use of moved value let x = (Some(vec![1]), ()); match x { (Some(y), ()) => {}, - //~^ HELP use a `ref` binding as shown - //~| SUGGESTION (Some(ref y), ()) => {}, _ => {}, } x; //~ ERROR use of partially moved value diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs index 7f2889a327be0..64dbf27b78e48 100644 --- a/src/test/compile-fail/regions-bounds.rs +++ b/src/test/compile-fail/regions-bounds.rs @@ -17,15 +17,15 @@ struct a_class<'a> { x:&'a isize } fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> { return e; //~ ERROR mismatched types - //~| expected `an_enum<'b>` - //~| found `an_enum<'a>` + //~| expected type `an_enum<'b>` + //~| found type `an_enum<'a>` //~| lifetime mismatch } fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> { return e; //~ ERROR mismatched types - //~| expected `a_class<'b>` - //~| found `a_class<'a>` + //~| expected type `a_class<'b>` + //~| found type `a_class<'a>` //~| lifetime mismatch } diff --git a/src/test/compile-fail/regions-early-bound-error-method.rs b/src/test/compile-fail/regions-early-bound-error-method.rs index 8cc35272282c4..f6a0c86de6626 100644 --- a/src/test/compile-fail/regions-early-bound-error-method.rs +++ b/src/test/compile-fail/regions-early-bound-error-method.rs @@ -29,8 +29,8 @@ impl<'a> Box<'a> { fn or<'b,G:GetRef<'b>>(&self, g2: G) -> &'a isize { g2.get() //~^ ERROR mismatched types - //~| expected `&'a isize` - //~| found `&'b isize` + //~| expected type `&'a isize` + //~| found type `&'b isize` //~| lifetime mismatch } diff --git a/src/test/compile-fail/regions-fn-subtyping-return-static.rs b/src/test/compile-fail/regions-fn-subtyping-return-static.rs index ebf7ca289f889..c0116b21166e0 100644 --- a/src/test/compile-fail/regions-fn-subtyping-return-static.rs +++ b/src/test/compile-fail/regions-fn-subtyping-return-static.rs @@ -55,10 +55,9 @@ fn supply_G() { want_G(bar); want_G(baz); //~^ ERROR mismatched types - //~| expected `fn(&'cx S) -> &'static S` - //~| found `fn(&S) -> &S {baz}` - //~| expected concrete lifetime - //~| found bound lifetime parameter 'cx + //~| expected type `fn(&'cx S) -> &'static S` + //~| found type `fn(&S) -> &S {baz}` + //~| expected concrete lifetime, found bound lifetime parameter 'cx } pub fn main() { diff --git a/src/test/compile-fail/regions-infer-not-param.rs b/src/test/compile-fail/regions-infer-not-param.rs index 83b9d4633dcf3..131b7170951f6 100644 --- a/src/test/compile-fail/regions-infer-not-param.rs +++ b/src/test/compile-fail/regions-infer-not-param.rs @@ -27,10 +27,10 @@ fn take_direct<'a,'b>(p: direct<'a>) -> direct<'b> { p } //~ ERROR mismatched ty fn take_indirect1(p: indirect1) -> indirect1 { p } fn take_indirect2<'a,'b>(p: indirect2<'a>) -> indirect2<'b> { p } //~ ERROR mismatched types -//~| expected `indirect2<'b>` -//~| found `indirect2<'a>` +//~| expected type `indirect2<'b>` +//~| found type `indirect2<'a>` //~| ERROR mismatched types -//~| expected `indirect2<'b>` -//~| found `indirect2<'a>` +//~| expected type `indirect2<'b>` +//~| found type `indirect2<'a>` fn main() {} diff --git a/src/test/compile-fail/regions-infer-paramd-indirect.rs b/src/test/compile-fail/regions-infer-paramd-indirect.rs index 1d32e8fe7b250..fad115c2aedf8 100644 --- a/src/test/compile-fail/regions-infer-paramd-indirect.rs +++ b/src/test/compile-fail/regions-infer-paramd-indirect.rs @@ -32,8 +32,8 @@ impl<'a> set_f<'a> for c<'a> { fn set_f_bad(&mut self, b: Box) { self.f = b; //~^ ERROR mismatched types - //~| expected `Box>` - //~| found `Box>` + //~| expected type `Box>` + //~| found type `Box>` //~| lifetime mismatch } } diff --git a/src/test/compile-fail/reject-specialized-drops-8142.rs b/src/test/compile-fail/reject-specialized-drops-8142.rs index adc8702240378..1ea956bbd5489 100644 --- a/src/test/compile-fail/reject-specialized-drops-8142.rs +++ b/src/test/compile-fail/reject-specialized-drops-8142.rs @@ -38,8 +38,8 @@ impl<'ml> Drop for M<'ml> { fn drop(&mut self) { } } // AC impl Drop for N<'static> { fn drop(&mut self) { } } // REJECT //~^ ERROR mismatched types -//~| expected `N<'n>` -//~| found `N<'static>` +//~| expected type `N<'n>` +//~| found type `N<'static>` impl Drop for O { fn drop(&mut self) { } } // ACCEPT diff --git a/src/test/compile-fail/repeat_count.rs b/src/test/compile-fail/repeat_count.rs index 10b722946a8a8..ab5af64d95c13 100644 --- a/src/test/compile-fail/repeat_count.rs +++ b/src/test/compile-fail/repeat_count.rs @@ -16,52 +16,45 @@ fn main() { //~^ ERROR expected constant integer for repeat count, found variable [E0307] let b = [0; ()]; //~^ ERROR mismatched types - //~| expected `usize` - //~| found `()` - //~| expected usize - //~| found ()) [E0308] + //~| expected type `usize` + //~| found type `()` + //~| expected usize, found () //~| ERROR expected positive integer for repeat count, found tuple [E0306] let c = [0; true]; //~^ ERROR mismatched types - //~| expected `usize` - //~| found `bool` + //~| expected usize, found bool //~| ERROR expected positive integer for repeat count, found boolean [E0306] let d = [0; 0.5]; //~^ ERROR mismatched types - //~| expected `usize` - //~| found `_` - //~| expected usize - //~| found floating-point variable) [E0308] + //~| expected type `usize` + //~| found type `_` + //~| expected usize, found floating-point variable //~| ERROR expected positive integer for repeat count, found float [E0306] let e = [0; "foo"]; //~^ ERROR mismatched types - //~| expected `usize` - //~| found `&'static str` - //~| expected usize - //~| found &-ptr) [E0308] + //~| expected type `usize` + //~| found type `&'static str` + //~| expected usize, found &-ptr //~| ERROR expected positive integer for repeat count, found string literal [E0306] let f = [0; -4_isize]; //~^ ERROR mismatched types //~| expected `usize` - //~| found `isize` [E0308] + //~| found `isize` //~| ERROR mismatched types: - //~| expected `usize`, - //~| found `isize` [E0307] + //~| expected usize, found isize let f = [0_usize; -1_isize]; //~^ ERROR mismatched types //~| expected `usize` - //~| found `isize` [E0308] + //~| found `isize` //~| ERROR mismatched types - //~| expected `usize` - //~| found `isize` [E0307] + //~| expected usize, found isize struct G { g: (), } let g = [0; G { g: () }]; //~^ ERROR mismatched types - //~| expected `usize` - //~| found `main::G` - //~| expected usize - //~| found struct `main::G`) [E0308] + //~| expected type `usize` + //~| found type `main::G` + //~| expected usize, found struct `main::G` //~| ERROR expected positive integer for repeat count, found struct [E0306] } diff --git a/src/test/compile-fail/shift-various-bad-types.rs b/src/test/compile-fail/shift-various-bad-types.rs index 560af9193b35e..2d06161111ef1 100644 --- a/src/test/compile-fail/shift-various-bad-types.rs +++ b/src/test/compile-fail/shift-various-bad-types.rs @@ -34,8 +34,7 @@ fn foo(p: &Panolpy) { // Type of the result follows the LHS, not the RHS: let _: i32 = 22_i64 >> 1_i32; //~^ ERROR mismatched types - //~| expected `i32` - //~| found `i64` + //~| expected i32, found i64 } fn main() { diff --git a/src/test/compile-fail/slice-mut.rs b/src/test/compile-fail/slice-mut.rs index e6acc32545178..874cca8cb3fd0 100644 --- a/src/test/compile-fail/slice-mut.rs +++ b/src/test/compile-fail/slice-mut.rs @@ -16,7 +16,7 @@ fn main() { let y: &mut[_] = &x[2..4]; //~^ ERROR mismatched types - //~| expected `&mut [_]` - //~| found `&[isize]` + //~| expected type `&mut [_]` + //~| found type `&[isize]` //~| values differ in mutability } diff --git a/src/test/compile-fail/slightly-nice-generic-literal-messages.rs b/src/test/compile-fail/slightly-nice-generic-literal-messages.rs index 3c1c3796a246f..3140bb6e5731d 100644 --- a/src/test/compile-fail/slightly-nice-generic-literal-messages.rs +++ b/src/test/compile-fail/slightly-nice-generic-literal-messages.rs @@ -16,10 +16,9 @@ fn main() { match Foo(1.1, marker::PhantomData) { 1 => {} //~^ ERROR mismatched types - //~| expected `Foo<_, _>` - //~| found `_` - //~| expected struct `Foo` - //~| found integral variable + //~| expected type `Foo<_, _>` + //~| found type `_` + //~| expected struct `Foo`, found integral variable } } diff --git a/src/test/compile-fail/struct-base-wrong-type-2.rs b/src/test/compile-fail/struct-base-wrong-type-2.rs index 83e73b6bc3ef4..1250d0dabcd9a 100644 --- a/src/test/compile-fail/struct-base-wrong-type-2.rs +++ b/src/test/compile-fail/struct-base-wrong-type-2.rs @@ -19,13 +19,11 @@ struct Bar { x: isize } fn main() { let b = Bar { x: 5 }; let f = Foo { a: 2, ..b }; //~ ERROR mismatched types - //~| expected `Foo` - //~| found `Bar` - //~| expected struct `Foo` - //~| found struct `Bar` + //~| expected type `Foo` + //~| found type `Bar` + //~| expected struct `Foo`, found struct `Bar` let f__isize = Foo { a: 2, ..4 }; //~ ERROR mismatched types - //~| expected `Foo` - //~| found `_` - //~| expected struct `Foo` - //~| found integral variable + //~| expected type `Foo` + //~| found type `_` + //~| expected struct `Foo`, found integral variable } diff --git a/src/test/compile-fail/struct-base-wrong-type.rs b/src/test/compile-fail/struct-base-wrong-type.rs index c98131560d486..4503e465840fe 100644 --- a/src/test/compile-fail/struct-base-wrong-type.rs +++ b/src/test/compile-fail/struct-base-wrong-type.rs @@ -18,15 +18,13 @@ struct Bar { x: isize } static bar: Bar = Bar { x: 5 }; static foo: Foo = Foo { a: 2, ..bar }; //~ ERROR mismatched types - //~| expected `Foo` - //~| found `Bar` - //~| expected struct `Foo` - //~| found struct `Bar` + //~| expected type `Foo` + //~| found type `Bar` + //~| expected struct `Foo`, found struct `Bar` static foo_i: Foo = Foo { a: 2, ..4 }; //~ ERROR mismatched types - //~| expected `Foo` - //~| found `_` - //~| expected struct `Foo` - //~| found integral variable + //~| expected type `Foo` + //~| found type `_` + //~| expected struct `Foo`, found integral variable fn main() { let b = Bar { x: 5 }; diff --git a/src/test/compile-fail/structure-constructor-type-mismatch.rs b/src/test/compile-fail/structure-constructor-type-mismatch.rs index 7a6b8ff662240..87fc5ba93aeb3 100644 --- a/src/test/compile-fail/structure-constructor-type-mismatch.rs +++ b/src/test/compile-fail/structure-constructor-type-mismatch.rs @@ -26,38 +26,32 @@ fn main() { let pt = PointF { x: 1, //~^ ERROR mismatched types - //~| expected f32 - //~| found integral variable + //~| expected f32, found integral variable y: 2, //~^ ERROR mismatched types - //~| expected f32 - //~| found integral variable + //~| expected f32, found integral variable }; let pt2 = Point:: { x: 3, //~^ ERROR mismatched types - //~| expected f32 - //~| found integral variable + //~| expected f32, found integral variable y: 4, //~^ ERROR mismatched types - //~| expected f32 - //~| found integral variable + //~| expected f32, found integral variable }; let pair = PairF { x: 5, //~^ ERROR mismatched types - //~| expected f32 - //~| found integral variable + //~| expected f32, found integral variable y: 6, }; let pair2 = PairF:: { x: 7, //~^ ERROR mismatched types - //~| expected f32 - //~| found integral variable + //~| expected f32, found integral variable y: 8, }; diff --git a/src/test/compile-fail/substs-ppaux.rs b/src/test/compile-fail/substs-ppaux.rs index 8dd9994b234ba..c857790e342d0 100644 --- a/src/test/compile-fail/substs-ppaux.rs +++ b/src/test/compile-fail/substs-ppaux.rs @@ -24,36 +24,36 @@ fn main() {} fn foo<'z>() where &'z (): Sized { let x: () = >::bar::<'static, char>; //[verbose]~^ ERROR mismatched types - //[verbose]~| expected `()` - //[verbose]~| found `fn() {>::bar::}` + //[verbose]~| expected type `()` + //[verbose]~| found type `fn() {>::bar::}` //[normal]~^^^^ ERROR mismatched types - //[normal]~| expected `()` - //[normal]~| found `fn() {>::bar::<'static, char>}` + //[normal]~| expected type `()` + //[normal]~| found type `fn() {>::bar::<'static, char>}` let x: () = >::bar::<'static, char>; //[verbose]~^ ERROR mismatched types - //[verbose]~| expected `()` - //[verbose]~| found `fn() {>::bar::}` + //[verbose]~| expected type `()` + //[verbose]~| found type `fn() {>::bar::}` //[normal]~^^^^ ERROR mismatched types - //[normal]~| expected `()` - //[normal]~| found `fn() {>::bar::<'static, char>}` + //[normal]~| expected type `()` + //[normal]~| found type `fn() {>::bar::<'static, char>}` let x: () = >::baz; //[verbose]~^ ERROR mismatched types - //[verbose]~| expected `()` - //[verbose]~| found `fn() {>::baz}` + //[verbose]~| expected type `()` + //[verbose]~| found type `fn() {>::baz}` //[normal]~^^^^ ERROR mismatched types - //[normal]~| expected `()` - //[normal]~| found `fn() {>::baz}` + //[normal]~| expected type `()` + //[normal]~| found type `fn() {>::baz}` let x: () = foo::<'static>; //[verbose]~^ ERROR mismatched types - //[verbose]~| expected `()` - //[verbose]~| found `fn() {foo::}` + //[verbose]~| expected type `()` + //[verbose]~| found type `fn() {foo::}` //[normal]~^^^^ ERROR mismatched types - //[normal]~| expected `()` - //[normal]~| found `fn() {foo::<'static>}` + //[normal]~| expected type `()` + //[normal]~| found type `fn() {foo::<'static>}` >::bar; //[verbose]~^ ERROR `str: std::marker::Sized` is not satisfied diff --git a/src/test/compile-fail/suppressed-error.rs b/src/test/compile-fail/suppressed-error.rs index 44de5d8cfe353..9a4a52ced2008 100644 --- a/src/test/compile-fail/suppressed-error.rs +++ b/src/test/compile-fail/suppressed-error.rs @@ -11,9 +11,8 @@ fn main() { let (x, y) = (); //~^ ERROR mismatched types -//~| expected `()` -//~| found `(_, _)` -//~| expected () -//~| found tuple +//~| expected type `()` +//~| found type `(_, _)` +//~| expected (), found tuple return x; } diff --git a/src/test/compile-fail/tag-that-dare-not-speak-its-name.rs b/src/test/compile-fail/tag-that-dare-not-speak-its-name.rs index 725234dfeab56..8f420f1ce4b0d 100644 --- a/src/test/compile-fail/tag-that-dare-not-speak-its-name.rs +++ b/src/test/compile-fail/tag-that-dare-not-speak-its-name.rs @@ -21,8 +21,7 @@ fn main() { let y; let x : char = last(y); //~^ ERROR mismatched types - //~| expected `char` - //~| found `std::option::Option<_>` - //~| expected char - //~| found enum `std::option::Option` + //~| expected type `char` + //~| found type `std::option::Option<_>` + //~| expected char, found enum `std::option::Option` } diff --git a/src/test/compile-fail/terr-in-field.rs b/src/test/compile-fail/terr-in-field.rs index 60db35b879f57..4a21e1339811e 100644 --- a/src/test/compile-fail/terr-in-field.rs +++ b/src/test/compile-fail/terr-in-field.rs @@ -21,10 +21,9 @@ struct bar { fn want_foo(f: foo) {} fn have_bar(b: bar) { want_foo(b); //~ ERROR mismatched types - //~| expected `foo` - //~| found `bar` - //~| expected struct `foo` - //~| found struct `bar` + //~| expected type `foo` + //~| found type `bar` + //~| expected struct `foo`, found struct `bar` } fn main() {} diff --git a/src/test/compile-fail/terr-sorts.rs b/src/test/compile-fail/terr-sorts.rs index 231d2366b48a8..592d7b3929bfc 100644 --- a/src/test/compile-fail/terr-sorts.rs +++ b/src/test/compile-fail/terr-sorts.rs @@ -19,10 +19,9 @@ type bar = Box; fn want_foo(f: foo) {} fn have_bar(b: bar) { want_foo(b); //~ ERROR mismatched types - //~| expected `foo` - //~| found `Box` - //~| expected struct `foo` - //~| found box + //~| expected type `foo` + //~| found type `Box` + //~| expected struct `foo`, found box } fn main() {} diff --git a/src/test/compile-fail/token-error-correct-3.rs b/src/test/compile-fail/token-error-correct-3.rs index f42c8d09a9c09..24627e9420874 100644 --- a/src/test/compile-fail/token-error-correct-3.rs +++ b/src/test/compile-fail/token-error-correct-3.rs @@ -22,6 +22,9 @@ pub mod raw { callback(path.as_ref(); //~ NOTE: unclosed delimiter //~^ ERROR: expected one of fs::create_dir_all(path.as_ref()).map(|()| true) //~ ERROR: mismatched types + //~^ expected (), found enum `std::result::Result` + //~| expected type `()` + //~| found type `std::result::Result` } else { //~ ERROR: incorrect close delimiter: `}` //~^ ERROR: expected one of Ok(false); diff --git a/src/test/compile-fail/trait-bounds-cant-coerce.rs b/src/test/compile-fail/trait-bounds-cant-coerce.rs index 836f08d0e78bb..1fff812af5b8d 100644 --- a/src/test/compile-fail/trait-bounds-cant-coerce.rs +++ b/src/test/compile-fail/trait-bounds-cant-coerce.rs @@ -22,10 +22,9 @@ fn c(x: Box) { fn d(x: Box) { a(x); //~ ERROR mismatched types - //~| expected `Box` - //~| found `Box` - //~| expected bounds `Send` - //~| found no bounds + //~| expected type `Box` + //~| found type `Box` + //~| expected bounds `Send`, found no bounds } fn main() { } diff --git a/src/test/compile-fail/tuple-arity-mismatch.rs b/src/test/compile-fail/tuple-arity-mismatch.rs index 8ad9ca50e3042..e62255a4e77d4 100644 --- a/src/test/compile-fail/tuple-arity-mismatch.rs +++ b/src/test/compile-fail/tuple-arity-mismatch.rs @@ -15,15 +15,13 @@ fn first((value, _): (isize, f64)) -> isize { value } fn main() { let y = first ((1,2.0,3)); //~^ ERROR mismatched types - //~| expected `(isize, f64)` - //~| found `(isize, f64, _)` - //~| expected a tuple with 2 elements - //~| found one with 3 elements + //~| expected type `(isize, f64)` + //~| found type `(isize, f64, _)` + //~| expected a tuple with 2 elements, found one with 3 elements let y = first ((1,)); //~^ ERROR mismatched types - //~| expected `(isize, f64)` - //~| found `(isize,)` - //~| expected a tuple with 2 elements - //~| found one with 1 elements + //~| expected type `(isize, f64)` + //~| found type `(isize,)` + //~| expected a tuple with 2 elements, found one with 1 elements } diff --git a/src/test/compile-fail/tutorial-suffix-inference-test.rs b/src/test/compile-fail/tutorial-suffix-inference-test.rs index 99d6437c02ed4..dadf7eb91d8f4 100644 --- a/src/test/compile-fail/tutorial-suffix-inference-test.rs +++ b/src/test/compile-fail/tutorial-suffix-inference-test.rs @@ -18,12 +18,10 @@ fn main() { identity_u8(x); // after this, `x` is assumed to have type `u8` identity_u16(x); //~^ ERROR mismatched types - //~| expected `u16` - //~| found `u8` + //~| expected u16, found u8 identity_u16(y); //~^ ERROR mismatched types - //~| expected `u16` - //~| found `i32` + //~| expected u16, found i32 let a = 3; @@ -32,6 +30,5 @@ fn main() { identity_i(a); // ok identity_u16(a); //~^ ERROR mismatched types - //~| expected `u16` - //~| found `isize` + //~| expected u16, found isize } diff --git a/src/test/compile-fail/type-mismatch-multiple.rs b/src/test/compile-fail/type-mismatch-multiple.rs index dd8f54cdabb8f..0f174d99fefcb 100644 --- a/src/test/compile-fail/type-mismatch-multiple.rs +++ b/src/test/compile-fail/type-mismatch-multiple.rs @@ -12,11 +12,9 @@ fn main() { let a: bool = 1; let b: i32 = true; } //~^ ERROR mismatched types -//~| expected `bool` -//~| found `_` -//~| expected bool -//~| found integral variable +//~| expected type `bool` +//~| found type `_` +//~| expected bool, found integral variable //~| ERROR mismatched types -//~| expected `i32` -//~| found `bool` +//~| expected i32, found bool diff --git a/src/test/compile-fail/type-mismatch-same-crate-name.rs b/src/test/compile-fail/type-mismatch-same-crate-name.rs index e81ae5d743939..e74acaa71b0f3 100644 --- a/src/test/compile-fail/type-mismatch-same-crate-name.rs +++ b/src/test/compile-fail/type-mismatch-same-crate-name.rs @@ -23,9 +23,17 @@ fn main() { let bar2 = {extern crate crate_a2 as a; a::bar()}; { extern crate crate_a1 as a; - a::try_foo(foo2); //~ ERROR mismatched types - //~^ NOTE Perhaps two different versions of crate `crate_a1` - a::try_bar(bar2); //~ ERROR mismatched types - //~^ NOTE Perhaps two different versions of crate `crate_a1` + a::try_foo(foo2); + //~^ ERROR mismatched types + //~| Perhaps two different versions of crate `crate_a1` + //~| expected struct `main::a::Foo` + //~| expected type `main::a::Foo` + //~| found type `main::a::Foo` + a::try_bar(bar2); + //~^ ERROR mismatched types + //~| Perhaps two different versions of crate `crate_a1` + //~| expected trait `main::a::Bar` + //~| expected type `Box` + //~| found type `Box` } } diff --git a/src/test/compile-fail/type-parameter-names.rs b/src/test/compile-fail/type-parameter-names.rs index 408bf72e97c87..11a2fc2665ca4 100644 --- a/src/test/compile-fail/type-parameter-names.rs +++ b/src/test/compile-fail/type-parameter-names.rs @@ -14,10 +14,9 @@ fn foo(x: Foo) -> Bar { x //~^ ERROR mismatched types -//~| expected `Bar` -//~| found `Foo` -//~| expected type parameter -//~| found a different type parameter +//~| expected type `Bar` +//~| found type `Foo` +//~| expected type parameter, found a different type parameter } fn main() {} diff --git a/src/test/compile-fail/type-params-in-different-spaces-1.rs b/src/test/compile-fail/type-params-in-different-spaces-1.rs index 155b835bbc6e0..26eac6adde221 100644 --- a/src/test/compile-fail/type-params-in-different-spaces-1.rs +++ b/src/test/compile-fail/type-params-in-different-spaces-1.rs @@ -13,10 +13,9 @@ use std::ops::Add; trait BrokenAdd: Copy + Add { fn broken_add(&self, rhs: T) -> Self { *self + rhs //~ ERROR mismatched types - //~| expected `Self` - //~| found `T` - //~| expected Self - //~| found type parameter + //~| expected type `Self` + //~| found type `T` + //~| expected Self, found type parameter } } diff --git a/src/test/compile-fail/typeck_type_placeholder_mismatch.rs b/src/test/compile-fail/typeck_type_placeholder_mismatch.rs index 1daea8f915b3f..91e3c38322e47 100644 --- a/src/test/compile-fail/typeck_type_placeholder_mismatch.rs +++ b/src/test/compile-fail/typeck_type_placeholder_mismatch.rs @@ -22,18 +22,16 @@ pub fn main() { fn test1() { let x: Foo<_> = Bar::(PhantomData); //~^ ERROR mismatched types - //~| expected `Foo<_>` - //~| found `Bar` - //~| expected struct `Foo` - //~| found struct `Bar` + //~| expected type `Foo<_>` + //~| found type `Bar` + //~| expected struct `Foo`, found struct `Bar` let y: Foo = x; } fn test2() { let x: Foo<_> = Bar::(PhantomData); //~^ ERROR mismatched types - //~| expected `Foo<_>` - //~| found `Bar` - //~| expected struct `Foo` - //~| found struct `Bar` + //~| expected type `Foo<_>` + //~| found type `Bar` + //~| expected struct `Foo`, found struct `Bar` } diff --git a/src/test/compile-fail/ufcs-explicit-self-bad.rs b/src/test/compile-fail/ufcs-explicit-self-bad.rs index e54a7623cb0c3..f14a3505cdeb4 100644 --- a/src/test/compile-fail/ufcs-explicit-self-bad.rs +++ b/src/test/compile-fail/ufcs-explicit-self-bad.rs @@ -45,12 +45,12 @@ impl<'a, T> SomeTrait for &'a Bar { //~^ ERROR mismatched types fn dummy3(self: &&Bar) {} //~^ ERROR mismatched types - //~| expected `&&'a Bar` - //~| found `&&Bar` + //~| expected type `&&'a Bar` + //~| found type `&&Bar` //~| lifetime mismatch //~| ERROR mismatched types - //~| expected `&&'a Bar` - //~| found `&&Bar` + //~| expected type `&&'a Bar` + //~| found type `&&Bar` //~| lifetime mismatch } diff --git a/src/test/compile-fail/variadic-ffi-3.rs b/src/test/compile-fail/variadic-ffi-3.rs index 6e60562da6749..b43159b0d96b0 100644 --- a/src/test/compile-fail/variadic-ffi-3.rs +++ b/src/test/compile-fail/variadic-ffi-3.rs @@ -21,17 +21,15 @@ fn main() { let x: unsafe extern "C" fn(f: isize, x: u8) = foo; //~^ ERROR: mismatched types - //~| expected `unsafe extern "C" fn(isize, u8)` - //~| found `unsafe extern "C" fn(isize, u8, ...) {foo}` - //~| expected non-variadic fn - //~| found variadic function + //~| expected type `unsafe extern "C" fn(isize, u8)` + //~| found type `unsafe extern "C" fn(isize, u8, ...) {foo}` + //~| NOTE: expected non-variadic fn, found variadic function let y: extern "C" fn(f: isize, x: u8, ...) = bar; //~^ ERROR: mismatched types - //~| expected `extern "C" fn(isize, u8, ...)` - //~| found `extern "C" fn(isize, u8) {bar}` - //~| expected variadic fn - //~| found non-variadic function + //~| expected type `extern "C" fn(isize, u8, ...)` + //~| found type `extern "C" fn(isize, u8) {bar}` + //~| NOTE: expected variadic fn, found non-variadic function foo(1, 2, 3f32); //~ ERROR: can't pass an `f32` to variadic function, cast to `c_double` foo(1, 2, true); //~ ERROR: can't pass `bool` to variadic function, cast to `c_int` diff --git a/src/test/run-make/json-errors/Makefile b/src/test/run-make/json-errors/Makefile deleted file mode 100644 index 30bcafd104945..0000000000000 --- a/src/test/run-make/json-errors/Makefile +++ /dev/null @@ -1,9 +0,0 @@ --include ../tools.mk - -LOG := $(TMPDIR)/foo.log - -all: - cp foo.rs $(TMPDIR) - cd $(TMPDIR) - -$(RUSTC) -Z unstable-options --error-format=json foo.rs 2>$(LOG) - diff foo.json $(LOG) diff --git a/src/test/run-make/json-errors/foo.json b/src/test/run-make/json-errors/foo.json deleted file mode 100644 index bde669ab0f7f9..0000000000000 --- a/src/test/run-make/json-errors/foo.json +++ /dev/null @@ -1,4 +0,0 @@ -{"message":"unresolved name `y`","code":{"code":"E0425","explanation":"\nAn unresolved name was used. Example of erroneous codes:\n\n```compile_fail\nsomething_that_doesnt_exist::foo;\n// error: unresolved name `something_that_doesnt_exist::foo`\n\n// or:\n\ntrait Foo {\n fn bar() {\n Self; // error: unresolved name `Self`\n }\n}\n\n// or:\n\nlet x = unknown_variable; // error: unresolved name `unknown_variable`\n```\n\nPlease verify that the name wasn't misspelled and ensure that the\nidentifier being referred to is valid for the given situation. Example:\n\n```\nenum something_that_does_exist {\n Foo,\n}\n```\n\nOr:\n\n```\nmod something_that_does_exist {\n pub static foo : i32 = 0i32;\n}\n\nsomething_that_does_exist::foo; // ok!\n```\n\nOr:\n\n```\nlet unknown_variable = 12u32;\nlet x = unknown_variable; // ok!\n```\n"},"level":"error","spans":[{"file_name":"foo.rs","byte_start":496,"byte_end":497,"line_start":12,"line_end":12,"column_start":18,"column_end":19,"text":[{"text":" let x = 42 + y;","highlight_start":18,"highlight_end":19}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null} -{"message":"mismatched types:\n expected `u8`,\n found `i32`","code":{"code":"E0308","explanation":"\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n\nAnother situation in which this occurs is when you attempt to use the `try!`\nmacro inside a function that does not return a `Result`:\n\n```compile_fail\nuse std::fs::File;\n\nfn main() {\n let mut f = try!(File::create(\"foo.txt\"));\n}\n```\n\nThis code gives an error like this:\n\n```text\n:5:8: 6:42 error: mismatched types:\n expected `()`,\n found `core::result::Result<_, _>`\n (expected (),\n found enum `core::result::Result`) [E0308]\n```\n\n`try!` returns a `Result`, and so the function must. But `main()` has\n`()` as its return type, hence the error.\n"},"level":"error","spans":[{"file_name":"foo.rs","byte_start":511,"byte_end":516,"line_start":14,"line_end":14,"column_start":12,"column_end":17,"text":[{"text":" 42u8 + 42i32;","highlight_start":12,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null} -{"message":"the trait bound `u8: std::ops::Add` is not satisfied","code":{"code":"E0277","explanation":"\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n```compile_fail\nfn some_func(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n```\n\n"},"level":"error","spans":[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":5,"column_end":17,"text":[{"text":" 42u8 + 42i32;","highlight_start":5,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[{"message":"the following implementations were found:","code":null,"level":"help","spans":[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":5,"column_end":17,"text":[{"text":" 42u8 + 42i32;","highlight_start":5,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null},{"message":" ","code":null,"level":"help","spans":[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":5,"column_end":17,"text":[{"text":" 42u8 + 42i32;","highlight_start":5,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null},{"message":" <&'a u8 as std::ops::Add>","code":null,"level":"help","spans":[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":5,"column_end":17,"text":[{"text":" 42u8 + 42i32;","highlight_start":5,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null},{"message":" >","code":null,"level":"help","spans":[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":5,"column_end":17,"text":[{"text":" 42u8 + 42i32;","highlight_start":5,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null},{"message":" <&'b u8 as std::ops::Add<&'a u8>>","code":null,"level":"help","spans":[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":5,"column_end":17,"text":[{"text":" 42u8 + 42i32;","highlight_start":5,"highlight_end":17}],"suggested_replacement":null,"expansion":null}],"children":[],"rendered":null}],"rendered":null} -{"message":"aborting due to 2 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":null} diff --git a/src/test/run-make/json-errors/foo.rs b/src/test/run-make/json-errors/foo.rs deleted file mode 100644 index 4db33940d8843..0000000000000 --- a/src/test/run-make/json-errors/foo.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn main() { - let x = 42 + y; - - 42u8 + 42i32; -} diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs index 073b5e57cc7c5..3501b335205ed 100644 --- a/src/tools/compiletest/src/json.rs +++ b/src/tools/compiletest/src/json.rs @@ -33,6 +33,8 @@ struct DiagnosticSpan { line_end: usize, column_start: usize, column_end: usize, + is_primary: bool, + label: Option, expansion: Option>, } @@ -66,7 +68,7 @@ fn parse_line(file_name: &str, line: &str) -> Vec { match json::decode::(line) { Ok(diagnostic) => { let mut expected_errors = vec![]; - push_expected_errors(&mut expected_errors, &diagnostic, file_name); + push_expected_errors(&mut expected_errors, &diagnostic, &[], file_name); expected_errors } Err(error) => { @@ -80,12 +82,24 @@ fn parse_line(file_name: &str, line: &str) -> Vec { fn push_expected_errors(expected_errors: &mut Vec, diagnostic: &Diagnostic, + default_spans: &[&DiagnosticSpan], file_name: &str) { - // We only consider messages pertaining to the current file. - let matching_spans = || { - diagnostic.spans.iter().filter(|span| { - Path::new(&span.file_name) == Path::new(&file_name) - }) + let spans_in_this_file: Vec<_> = + diagnostic.spans.iter() + .filter(|span| Path::new(&span.file_name) == Path::new(&file_name)) + .collect(); + + let primary_spans: Vec<_> = + spans_in_this_file.iter() + .cloned() + .filter(|span| span.is_primary) + .collect(); + let primary_spans = if primary_spans.is_empty() { + // subdiagnostics often don't have a span of their own; + // inherit the span from the parent in that case + default_spans + } else { + &primary_spans }; // We break the output into multiple lines, and then append the @@ -124,7 +138,7 @@ fn push_expected_errors(expected_errors: &mut Vec, // more structured shortly anyhow. let mut message_lines = diagnostic.message.lines(); if let Some(first_line) = message_lines.next() { - for span in matching_spans() { + for span in primary_spans { let msg = with_code(span, first_line); let kind = ErrorKind::from_str(&diagnostic.level).ok(); expected_errors.push( @@ -137,7 +151,7 @@ fn push_expected_errors(expected_errors: &mut Vec, } } for next_line in message_lines { - for span in matching_spans() { + for span in primary_spans { expected_errors.push( Error { line_num: span.line_start, @@ -150,7 +164,7 @@ fn push_expected_errors(expected_errors: &mut Vec, // If the message has a suggestion, register that. if let Some(ref rendered) = diagnostic.rendered { - let start_line = matching_spans().map(|s| s.line_start).min().expect("\ + let start_line = primary_spans.iter().map(|s| s.line_start).min().expect("\ every suggestion should have at least one span"); for (index, line) in rendered.lines().enumerate() { expected_errors.push( @@ -164,7 +178,7 @@ fn push_expected_errors(expected_errors: &mut Vec, } // Add notes for the backtrace - for span in matching_spans() { + for span in primary_spans { for frame in &span.expansion { push_backtrace(expected_errors, frame, @@ -172,9 +186,20 @@ fn push_expected_errors(expected_errors: &mut Vec, } } + // Add notes for any labels that appear in the message. + for span in spans_in_this_file.iter() + .filter(|span| span.label.is_some()) + { + expected_errors.push(Error { + line_num: span.line_start, + kind: Some(ErrorKind::Note), + msg: span.label.clone().unwrap() + }); + } + // Flatten out the children. for child in &diagnostic.children { - push_expected_errors(expected_errors, child, file_name); + push_expected_errors(expected_errors, child, primary_spans, file_name); } }