diff --git a/mk/crates.mk b/mk/crates.mk index c55a6e791b68d..02b95f5b1a927 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -128,7 +128,7 @@ DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \ test rustc_lint rustc_const_eval -TOOL_DEPS_compiletest := test getopts log +TOOL_DEPS_compiletest := test getopts log serialize TOOL_DEPS_rustdoc := rustdoc TOOL_DEPS_rustc := rustc_driver TOOL_DEPS_rustbook := std rustdoc diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index f771ee95bd121..35aa827782ddf 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -1394,6 +1394,56 @@ impl CodeMap { pub fn count_lines(&self) -> usize { self.files.borrow().iter().fold(0, |a, f| a + f.count_lines()) } + + pub fn macro_backtrace(&self, span: Span) -> Vec { + let mut last_span = DUMMY_SP; + let mut span = span; + let mut result = vec![]; + loop { + let span_name_span = self.with_expn_info(span.expn_id, |expn_info| { + expn_info.map(|ei| { + let (pre, post) = match ei.callee.format { + MacroAttribute(..) => ("#[", "]"), + MacroBang(..) => ("", "!"), + }; + let macro_decl_name = format!("{}{}{}", + pre, + ei.callee.name(), + post); + let def_site_span = ei.callee.span; + (ei.call_site, macro_decl_name, def_site_span) + }) + }); + + match span_name_span { + None => break, + Some((call_site, macro_decl_name, def_site_span)) => { + // Don't print recursive invocations + if !call_site.source_equal(&last_span) { + result.push(MacroBacktrace { + call_site: call_site, + macro_decl_name: macro_decl_name, + def_site_span: def_site_span, + }); + } + last_span = span; + span = call_site; + } + } + } + result + } +} + +pub struct MacroBacktrace { + /// span where macro was applied to generate this code + pub call_site: Span, + + /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]") + pub macro_decl_name: String, + + /// span where macro was defined (if known) + pub def_site_span: Option, } // _____________________________________________________________________________ diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs index 61fdc8453d8fd..0b5234769b219 100644 --- a/src/libsyntax/errors/emitter.rs +++ b/src/libsyntax/errors/emitter.rs @@ -577,46 +577,17 @@ impl EmitterWriter { fn print_macro_backtrace(&mut self, sp: Span) -> io::Result<()> { - let mut last_span = codemap::DUMMY_SP; - let mut span = sp; - - loop { - let span_name_span = self.cm.with_expn_info(span.expn_id, |expn_info| { - expn_info.map(|ei| { - let (pre, post) = match ei.callee.format { - codemap::MacroAttribute(..) => ("#[", "]"), - codemap::MacroBang(..) => ("", "!"), - }; - let macro_decl_name = format!("in this expansion of {}{}{}", - pre, - ei.callee.name(), - post); - let def_site_span = ei.callee.span; - (ei.call_site, macro_decl_name, def_site_span) - }) - }); - let (macro_decl_name, def_site_span) = match span_name_span { - None => break, - Some((sp, macro_decl_name, def_site_span)) => { - span = sp; - (macro_decl_name, def_site_span) - } - }; - - // Don't print recursive invocations - if !span.source_equal(&last_span) { - let mut diag_string = macro_decl_name; - if let Some(def_site_span) = def_site_span { - diag_string.push_str(&format!(" (defined in {})", - self.cm.span_to_filename(def_site_span))); - } - - let snippet = self.cm.span_to_string(span); - print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?; + for trace in self.cm.macro_backtrace(sp) { + let mut diag_string = + format!("in this expansion of {}", trace.macro_decl_name); + if let Some(def_site_span) = trace.def_site_span { + diag_string.push_str( + &format!(" (defined in {})", + self.cm.span_to_filename(def_site_span))); } - last_span = span; + let snippet = self.cm.span_to_string(sp); + print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?; } - Ok(()) } } diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/errors/json.rs index f369582bc5c30..821617bfe89df 100644 --- a/src/libsyntax/errors/json.rs +++ b/src/libsyntax/errors/json.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -20,13 +20,14 @@ // FIXME spec the JSON output properly. -use codemap::{self, Span, MultiSpan, CodeMap}; +use codemap::{self, Span, MacroBacktrace, MultiSpan, CodeMap}; use diagnostics::registry::Registry; use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion}; use errors::emitter::Emitter; use std::rc::Rc; use std::io::{self, Write}; +use std::vec; use rustc_serialize::json::as_json; @@ -84,8 +85,12 @@ struct Diagnostic<'a> { /// "error: internal compiler error", "error", "warning", "note", "help". level: &'static str, spans: Vec, - /// Assocaited diagnostic messages. + /// Associated diagnostic messages. children: Vec>, + /// The message as rustc would render it. Currently this is only + /// `Some` for "suggestions", but eventually it will include all + /// snippets. + rendered: Option, } #[derive(RustcEncodable)] @@ -101,16 +106,39 @@ struct DiagnosticSpan { column_end: usize, /// Source text from the start of line_start to the end of line_end. text: Vec, + /// 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`, + /// however. + suggested_replacement: Option, + /// Macro invocations that created the code at this span, if any. + expansion: Option>, } #[derive(RustcEncodable)] struct DiagnosticSpanLine { text: String, + /// 1-based, character offset in self.text. highlight_start: usize, + highlight_end: usize, } +#[derive(RustcEncodable)] +struct DiagnosticSpanMacroExpansion { + /// span where macro was applied to generate this code; note that + /// this may itself derive from a macro (if + /// `span.expansion.is_some()`) + span: DiagnosticSpan, + + /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]") + macro_decl_name: String, + + /// span where macro was defined (if known) + def_site_span: Option, +} + #[derive(RustcEncodable)] struct DiagnosticCode { /// The code itself. @@ -132,6 +160,7 @@ impl<'a> Diagnostic<'a> { level: level.to_str(), spans: msp.map_or(vec![], |msp| DiagnosticSpan::from_multispan(msp, je)), children: vec![], + rendered: None, } } @@ -146,6 +175,7 @@ impl<'a> Diagnostic<'a> { level: level.to_str(), spans: DiagnosticSpan::from_render_span(span, je), children: vec![], + rendered: je.render(span), } } @@ -160,6 +190,7 @@ impl<'a> Diagnostic<'a> { children: db.children.iter().map(|c| { Diagnostic::from_sub_diagnostic(c, je) }).collect(), + rendered: None, } } @@ -173,37 +204,91 @@ impl<'a> Diagnostic<'a> { .or_else(|| db.span.as_ref().map(|s| DiagnosticSpan::from_multispan(s, je))) .unwrap_or(vec![]), children: vec![], + rendered: db.render_span.as_ref() + .and_then(|rsp| je.render(rsp)), } } } impl DiagnosticSpan { + fn from_span(span: Span, 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) + } + + fn from_span_and_backtrace(span: Span, + 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, + }) + }); + DiagnosticSpan { + file_name: start.file.name.clone(), + byte_start: span.lo.0, + byte_end: span.hi.0, + line_start: start.line, + line_end: end.line, + column_start: start.col.0 + 1, + column_end: end.col.0 + 1, + text: DiagnosticSpanLine::from_span(span, je), + suggested_replacement: suggestion.cloned(), + expansion: backtrace_step, + } + } + fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec { - msp.spans.iter().map(|span| { - let start = je.cm.lookup_char_pos(span.lo); - let end = je.cm.lookup_char_pos(span.hi); - DiagnosticSpan { - file_name: start.file.name.clone(), - byte_start: span.lo.0, - byte_end: span.hi.0, - line_start: start.line, - line_end: end.line, - column_start: start.col.0 + 1, - column_end: end.col.0 + 1, - text: DiagnosticSpanLine::from_span(span, je), - } - }).collect() + msp.spans.iter().map(|&span| Self::from_span(span, 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() } fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec { match *rsp { - RenderSpan::FullSpan(ref msp) | - // FIXME(#30701) handle Suggestion properly - RenderSpan::Suggestion(CodeSuggestion { ref msp, .. }) => { + 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| { + msp.spans.iter().map(|&span| { let end = je.cm.lookup_char_pos(span.hi); DiagnosticSpan { file_name: end.file.name.clone(), @@ -214,37 +299,11 @@ impl DiagnosticSpan { 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::FileLine(ref msp) => { - msp.spans.iter().map(|span| { - let start = je.cm.lookup_char_pos(span.lo); - let end = je.cm.lookup_char_pos(span.hi); - DiagnosticSpan { - file_name: start.file.name.clone(), - byte_start: span.lo.0, - byte_end: span.hi.0, - line_start: start.line, - line_end: end.line, - column_start: 0, - column_end: 0, - text: DiagnosticSpanLine::from_span(span, je), - } - }).collect() - } - } - } -} - -macro_rules! get_lines_for_span { - ($span: ident, $je: ident) => { - match $je.cm.span_to_lines(*$span) { - Ok(lines) => lines, - Err(_) => { - debug!("unprintable span"); - return Vec::new(); - } } } } @@ -265,45 +324,49 @@ impl DiagnosticSpanLine { /// Create a list of DiagnosticSpanLines from span - each line with any part /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the /// `span` within the line. - fn from_span(span: &Span, je: &JsonEmitter) -> Vec { - let lines = get_lines_for_span!(span, je); - - let mut result = Vec::new(); - let fm = &*lines.file; - - for line in &lines.lines { - result.push(DiagnosticSpanLine::line_from_filemap(fm, - line.line_index, - line.start_col.0 + 1, - line.end_col.0 + 1)); - } - - result + fn from_span(span: Span, je: &JsonEmitter) -> Vec { + je.cm.span_to_lines(span) + .map(|lines| { + let fm = &*lines.file; + lines.lines + .iter() + .map(|line| { + DiagnosticSpanLine::line_from_filemap(fm, + line.line_index, + line.start_col.0 + 1, + line.end_col.0 + 1) + }) + .collect() + }) + .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 { - let lines = get_lines_for_span!(span, je); - - let mut result = Vec::new(); - let fm = &*lines.file; - - for (i, line) in lines.lines.iter().enumerate() { - // 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) - }; - result.push(DiagnosticSpanLine::line_from_filemap(fm, - line.line_index, - highlight.0, - highlight.1)); - } - - result + 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![]) } } @@ -322,3 +385,21 @@ 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/test/compile-fail/associated-types-path-2.rs b/src/test/compile-fail/associated-types-path-2.rs index 0c077e37e43be..68fba56427cc5 100644 --- a/src/test/compile-fail/associated-types-path-2.rs +++ b/src/test/compile-fail/associated-types-path-2.rs @@ -28,8 +28,8 @@ 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() { diff --git a/src/test/compile-fail/block-must-not-have-result-do.rs b/src/test/compile-fail/block-must-not-have-result-do.rs index 30039a1c54c3a..2a6c71dbe3923 100644 --- a/src/test/compile-fail/block-must-not-have-result-do.rs +++ b/src/test/compile-fail/block-must-not-have-result-do.rs @@ -11,9 +11,5 @@ fn main() { loop { true //~ ERROR mismatched types - //~| expected () - //~| found bool - //~| expected () - //~| found bool } } diff --git a/src/test/compile-fail/block-must-not-have-result-res.rs b/src/test/compile-fail/block-must-not-have-result-res.rs index 6161660ddf7b3..8728685fc8b02 100644 --- a/src/test/compile-fail/block-must-not-have-result-res.rs +++ b/src/test/compile-fail/block-must-not-have-result-res.rs @@ -13,10 +13,6 @@ struct r; impl Drop for r { fn drop(&mut self) { true //~ ERROR mismatched types - //~| expected () - //~| found bool - //~| expected () - //~| found bool } } diff --git a/src/test/compile-fail/cast-as-bool.rs b/src/test/compile-fail/cast-as-bool.rs index 4764ae380ff44..af42d5c275c75 100644 --- a/src/test/compile-fail/cast-as-bool.rs +++ b/src/test/compile-fail/cast-as-bool.rs @@ -11,6 +11,5 @@ fn main() { let u = 5 as bool; //~^ ERROR cannot cast as `bool` - //~^^ HELP compare with zero instead - //~^^^ HELP run `rustc --explain E0054` to see a detailed explanation + //~| HELP compare with zero instead } diff --git a/src/test/compile-fail/cast-rfc0401.rs b/src/test/compile-fail/cast-rfc0401.rs index fcfb5706e5d1a..05c531e91f128 100644 --- a/src/test/compile-fail/cast-rfc0401.rs +++ b/src/test/compile-fail/cast-rfc0401.rs @@ -60,12 +60,10 @@ fn main() //~^^ HELP through a usize first let _ = 3_i32 as bool; //~^ ERROR cannot cast as `bool` - //~^^ HELP compare with zero - //~^^^ HELP run `rustc --explain E0054` to see a detailed explanation + //~| HELP compare with zero let _ = E::A as bool; //~^ ERROR cannot cast as `bool` - //~^^ HELP compare with zero - //~^^^ HELP run `rustc --explain E0054` to see a detailed explanation + //~| HELP compare with zero let _ = 0x61u32 as char; //~ ERROR only `u8` can be cast let _ = false as f32; @@ -92,9 +90,8 @@ fn main() let _ = v as *const [u8]; //~ ERROR cannot cast let _ = fat_v as *const Foo; //~^ ERROR the trait bound `[u8]: std::marker::Sized` is not satisfied - //~^^ HELP run `rustc --explain E0277` to see a detailed explanation - //~^^^ NOTE `[u8]` does not have a constant size known at compile-time - //~^^^^ NOTE required for the cast to the object type `Foo` + //~| NOTE `[u8]` does not have a constant size known at compile-time + //~| NOTE required for the cast to the object type `Foo` let _ = foo as *const str; //~ ERROR casting let _ = foo as *mut str; //~ ERROR casting let _ = main as *mut str; //~ ERROR casting @@ -107,9 +104,8 @@ fn main() let a : *const str = "hello"; let _ = a as *const Foo; //~^ ERROR the trait bound `str: std::marker::Sized` is not satisfied - //~^^ HELP run `rustc --explain E0277` to see a detailed explanation - //~^^^ NOTE `str` does not have a constant size known at compile-time - //~^^^^ NOTE required for the cast to the object type `Foo` + //~| NOTE `str` does not have a constant size known at compile-time + //~| NOTE required for the cast to the object type `Foo` // check no error cascade let _ = main.f as *const u32; //~ ERROR attempted access of field diff --git a/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs b/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs index afc3b8d4ccddc..7fd1b17f2966c 100644 --- a/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs +++ b/src/test/compile-fail/coherence-conflicting-negative-trait-impl.rs @@ -20,7 +20,7 @@ impl !Send for TestType {} //~^ ERROR conflicting implementations of trait `std::marker::Send` unsafe impl Send for TestType {} -//~^ ERROR error: conflicting implementations of trait `std::marker::Send` +//~^ ERROR conflicting implementations of trait `std::marker::Send` impl !Send for TestType {} diff --git a/src/test/compile-fail/consider-removing-last-semi.rs b/src/test/compile-fail/consider-removing-last-semi.rs index 02148a138c9d9..2e110cb3d0bc8 100644 --- a/src/test/compile-fail/consider-removing-last-semi.rs +++ b/src/test/compile-fail/consider-removing-last-semi.rs @@ -9,13 +9,11 @@ // except according to those terms. fn f() -> String { //~ ERROR E0269 - //~^ HELP detailed explanation 0u8; "bla".to_string(); //~ HELP consider removing this semicolon } fn g() -> String { //~ ERROR E0269 - //~^ HELP detailed explanation "this won't work".to_string(); "removeme".to_string(); //~ HELP consider removing this semicolon } diff --git a/src/test/compile-fail/empty-struct-unit-pat.rs b/src/test/compile-fail/empty-struct-unit-pat.rs index a75290c94053d..05733762d37ad 100644 --- a/src/test/compile-fail/empty-struct-unit-pat.rs +++ b/src/test/compile-fail/empty-struct-unit-pat.rs @@ -37,11 +37,11 @@ fn main() { // } match e2 { Empty2(..) => () //~ ERROR `Empty2` does not name a tuple variant or a tuple struct - //~^ ERROR hard error + //~^ WARNING hard error } match xe2 { XEmpty2(..) => () //~ ERROR `XEmpty2` does not name a tuple variant or a tuple struct - //~^ ERROR hard error + //~^ WARNING hard error } // Rejected by parser as yet // match e4 { @@ -53,11 +53,11 @@ fn main() { // } match e4 { E::Empty4(..) => () //~ ERROR `E::Empty4` does not name a tuple variant or a tuple struct - //~^ ERROR hard error + //~^ WARNING hard error } match xe4 { XE::XEmpty4(..) => (), //~ ERROR `XE::XEmpty4` does not name a tuple variant or a tuple - //~^ ERROR hard error + //~^ WARNING hard error _ => {}, } } diff --git a/src/test/compile-fail/integer-literal-suffix-inference.rs b/src/test/compile-fail/integer-literal-suffix-inference.rs index 8f04b58b77868..a01db18a34a7f 100644 --- a/src/test/compile-fail/integer-literal-suffix-inference.rs +++ b/src/test/compile-fail/integer-literal-suffix-inference.rs @@ -143,7 +143,7 @@ fn main() { id_i64(a16); //~^ ERROR mismatched types //~| expected `i64` - //~| found i16 + //~| found `i16` id_i64(a32); //~^ ERROR mismatched types //~| expected `i64` diff --git a/src/test/compile-fail/issue-11714.rs b/src/test/compile-fail/issue-11714.rs index 6dde59d4a2e60..998576097a0a0 100644 --- a/src/test/compile-fail/issue-11714.rs +++ b/src/test/compile-fail/issue-11714.rs @@ -9,7 +9,6 @@ // except according to those terms. fn blah() -> i32 { //~ ERROR not all control paths return a value - //~^ HELP run `rustc --explain E0269` to see a detailed explanation 1 ; //~ HELP consider removing this semicolon: diff --git a/src/test/compile-fail/issue-13058.rs b/src/test/compile-fail/issue-13058.rs index b552d7678d563..de578257e4684 100644 --- a/src/test/compile-fail/issue-13058.rs +++ b/src/test/compile-fail/issue-13058.rs @@ -36,5 +36,4 @@ fn check<'r, I: Iterator, T: Itble<'r, usize, I>>(cont: &T) -> bool fn main() { check((3, 5)); //~^ ERROR mismatched types -//~| HELP run `rustc --explain E0308` to see a detailed explanation } diff --git a/src/test/compile-fail/issue-13428.rs b/src/test/compile-fail/issue-13428.rs index 5b8ab08aefca1..c771970650d31 100644 --- a/src/test/compile-fail/issue-13428.rs +++ b/src/test/compile-fail/issue-13428.rs @@ -11,7 +11,6 @@ // Regression test for #13428 fn foo() -> String { //~ ERROR not all control paths return a value - //~^ HELP run `rustc --explain E0269` to see a detailed explanation format!("Hello {}", "world") // Put the trailing semicolon on its own line to test that the @@ -20,7 +19,6 @@ fn foo() -> String { //~ ERROR not all control paths return a value } fn bar() -> String { //~ ERROR not all control paths return a value - //~^ HELP run `rustc --explain E0269` to see a detailed explanation "foobar".to_string() ; //~ HELP consider removing this semicolon } diff --git a/src/test/compile-fail/issue-13482.rs b/src/test/compile-fail/issue-13482.rs index 2fbfd6cc84ead..8d98fe313349b 100644 --- a/src/test/compile-fail/issue-13482.rs +++ b/src/test/compile-fail/issue-13482.rs @@ -17,7 +17,7 @@ fn main() { //~^ ERROR mismatched types //~| expected `[_; 2]` //~| found `[_; 0]` - //~| expected array with a fixed size of 2 elements + //~| expected an array with a fixed size of 2 elements [a,_] => Some(a) }; } diff --git a/src/test/compile-fail/issue-16747.rs b/src/test/compile-fail/issue-16747.rs index 0fdb5f74e8299..dd7e8a869eca9 100644 --- a/src/test/compile-fail/issue-16747.rs +++ b/src/test/compile-fail/issue-16747.rs @@ -19,7 +19,6 @@ struct List<'a, T: ListItem<'a>> { //~^ ERROR the parameter type `T` may not live long enough //~| HELP consider adding an explicit lifetime bound //~| NOTE ...so that the reference type `&'a [T]` does not outlive the data it points at -//~| HELP run `rustc --explain E0309` to see a detailed explanation } impl<'a, T: ListItem<'a>> Collection for List<'a, T> { fn len(&self) -> usize { diff --git a/src/test/compile-fail/issue-19707.rs b/src/test/compile-fail/issue-19707.rs index 814c1a4131d38..9affb44b7445c 100644 --- a/src/test/compile-fail/issue-19707.rs +++ b/src/test/compile-fail/issue-19707.rs @@ -13,10 +13,8 @@ type foo = fn(&u8, &u8) -> &u8; //~ ERROR missing lifetime specifier //~^ HELP the signature does not say whether it is borrowed from argument 1 or argument 2 -//~^^ HELP run `rustc --explain E0106` to see a detailed explanation fn bar &u8>(f: &F) {} //~ ERROR missing lifetime specifier //~^ HELP the signature does not say whether it is borrowed from argument 1 or argument 2 -//~^^ HELP run `rustc --explain E0106` to see a detailed explanation fn main() {} diff --git a/src/test/compile-fail/issue-20862.rs b/src/test/compile-fail/issue-20862.rs index 729311416386d..9df6358399869 100644 --- a/src/test/compile-fail/issue-20862.rs +++ b/src/test/compile-fail/issue-20862.rs @@ -10,11 +10,7 @@ fn foo(x: i32) { |y| x + y -//~^ ERROR: mismatched types: -//~| expected `()`, -//~| found closure -//~| (expected (), -//~| found closure) [E0308] +//~^ ERROR: mismatched types } fn main() { diff --git a/src/test/compile-fail/issue-21221-1.rs b/src/test/compile-fail/issue-21221-1.rs index c53d5a0922e64..2bc9ec3289a5d 100644 --- a/src/test/compile-fail/issue-21221-1.rs +++ b/src/test/compile-fail/issue-21221-1.rs @@ -55,7 +55,6 @@ impl Mul for Foo { //~| HELP `mul1::Mul` //~| HELP `mul2::Mul` //~| HELP `std::ops::Mul` -//~| HELP run `rustc --explain E0405` to see a detailed explanation //~| HELP you can import several candidates into scope (`use ...;`): } @@ -77,22 +76,19 @@ fn getMul() -> Mul { //~| HELP `mul3::Mul` //~| HELP `mul4::Mul` //~| HELP and 2 other candidates -//~| HELP run `rustc --explain E0412` to see a detailed explanation //~| HELP you can import several candidates into scope (`use ...;`): } // Let's also test what happens if the trait doesn't exist: impl ThisTraitReallyDoesntExistInAnyModuleReally for Foo { //~^ ERROR trait `ThisTraitReallyDoesntExistInAnyModuleReally` is not in scope -//~^^ HELP run `rustc --explain E0405` to see a detailed explanation -//~^^^ HELP no candidates by the name of `ThisTraitReallyDoesntExistInAnyModuleReally` found +//~| HELP no candidates by the name of `ThisTraitReallyDoesntExistInAnyModuleReally` found } // Let's also test what happens if there's just one alternative: impl Div for Foo { //~^ ERROR trait `Div` is not in scope //~| HELP `use std::ops::Div;` -//~| HELP run `rustc --explain E0405` to see a detailed explanation } fn main() { diff --git a/src/test/compile-fail/issue-21221-2.rs b/src/test/compile-fail/issue-21221-2.rs index cf5c6e8a3b4f0..861acf62d0b58 100644 --- a/src/test/compile-fail/issue-21221-2.rs +++ b/src/test/compile-fail/issue-21221-2.rs @@ -28,4 +28,3 @@ struct Foo; impl T for Foo { } //~^ ERROR trait `T` is not in scope //~| HELP you can import it into scope: `use foo::bar::T;`. -//~| HELP run `rustc --explain E0405` to see a detailed explanation diff --git a/src/test/compile-fail/issue-21221-3.rs b/src/test/compile-fail/issue-21221-3.rs index a1a712d142197..05786e69cefd2 100644 --- a/src/test/compile-fail/issue-21221-3.rs +++ b/src/test/compile-fail/issue-21221-3.rs @@ -25,7 +25,6 @@ struct Foo; impl OuterTrait for Foo {} //~^ ERROR trait `OuterTrait` is not in scope //~| HELP you can import it into scope: `use issue_21221_3::outer::OuterTrait;`. -//~| HELP run `rustc --explain E0405` to see a detailed explanation fn main() { println!("Hello, world!"); } diff --git a/src/test/compile-fail/issue-21221-4.rs b/src/test/compile-fail/issue-21221-4.rs index 1ef205bd8be85..bcbee16cdcf32 100644 --- a/src/test/compile-fail/issue-21221-4.rs +++ b/src/test/compile-fail/issue-21221-4.rs @@ -20,7 +20,6 @@ struct Foo; impl T for Foo {} //~^ ERROR trait `T` is not in scope //~| HELP you can import it into scope: `use issue_21221_4::T;`. -//~| HELP run `rustc --explain E0405` to see a detailed explanation fn main() { println!("Hello, world!"); diff --git a/src/test/compile-fail/issue-21600.rs b/src/test/compile-fail/issue-21600.rs index d9dcebfda6a1d..1d0473ec4b651 100644 --- a/src/test/compile-fail/issue-21600.rs +++ b/src/test/compile-fail/issue-21600.rs @@ -23,8 +23,6 @@ fn main() { call_it(|| x.gen()); call_it(|| x.gen_mut()); //~ ERROR cannot borrow data mutably in a captured outer //~^ ERROR cannot borrow data mutably in a captured outer - //~^^ HELP run `rustc --explain E0387` to see a detailed explanation - //~^^^ HELP run `rustc --explain E0387` to see a detailed explanation - //~^^^^ HELP consider changing this closure to take self by mutable reference + //~| HELP consider changing this closure to take self by mutable reference }); } diff --git a/src/test/compile-fail/issue-21659-show-relevant-trait-impls-1.rs b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-1.rs index e880a8b212bbc..99035209e14bc 100644 --- a/src/test/compile-fail/issue-21659-show-relevant-trait-impls-1.rs +++ b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-1.rs @@ -36,5 +36,4 @@ fn main() { //~| help: the following implementations were found: //~| help: > //~| help: > - //~| help: run `rustc --explain E0277` } diff --git a/src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs index 2c5b18a8113f7..2009c32c85436 100644 --- a/src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs +++ b/src/test/compile-fail/issue-21659-show-relevant-trait-impls-2.rs @@ -43,5 +43,4 @@ fn main() { //~| help: > //~| help: > //~| help: and 2 others - //~| help: run `rustc --explain E0277` } diff --git a/src/test/compile-fail/issue-24036.rs b/src/test/compile-fail/issue-24036.rs index 28eebea749cce..06b058cbfe179 100644 --- a/src/test/compile-fail/issue-24036.rs +++ b/src/test/compile-fail/issue-24036.rs @@ -14,7 +14,6 @@ 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 - //~| HELP run `rustc --explain E0308` to see a detailed explanation } fn closure_from_match() { @@ -27,7 +26,6 @@ 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 - //~| HELP run `rustc --explain E0308` to see a detailed explanation } fn main() { } diff --git a/src/test/compile-fail/issue-24446.rs b/src/test/compile-fail/issue-24446.rs index cafe6d1bb5829..a2831fd2b5ac8 100644 --- a/src/test/compile-fail/issue-24446.rs +++ b/src/test/compile-fail/issue-24446.rs @@ -11,10 +11,6 @@ fn main() { static foo: Fn() -> u32 = || -> u32 { //~^ ERROR: mismatched types: - //~| expected `std::ops::Fn() -> u32 + 'static`, - //~| found closure - //~| (expected trait std::ops::Fn, - //~| found closure) 0 }; } diff --git a/src/test/compile-fail/issue-26638.rs b/src/test/compile-fail/issue-26638.rs index 9cbb64c2311bc..f918f0aed7a10 100644 --- a/src/test/compile-fail/issue-26638.rs +++ b/src/test/compile-fail/issue-26638.rs @@ -11,18 +11,15 @@ fn parse_type(iter: Box+'static>) -> &str { iter.next() } //~^ ERROR missing lifetime specifier [E0106] //~^^ HELP 2 elided lifetimes -//~^^^ HELP run `rustc --explain E0106` to see a detailed explanation fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } //~^ ERROR missing lifetime specifier [E0106] //~^^ HELP lifetime cannot be derived -//~^^^ HELP run `rustc --explain E0106` to see a detailed explanation -//~^^^^ HELP consider giving it an explicit bounded or 'static lifetime +//~^^^ HELP consider giving it an explicit bounded or 'static lifetime fn parse_type_3() -> &str { unimplemented!() } //~^ ERROR missing lifetime specifier [E0106] //~^^ HELP no value for it to be borrowed from -//~^^^ HELP run `rustc --explain E0106` to see a detailed explanation -//~^^^^ HELP consider giving it a 'static lifetime +//~^^^ HELP consider giving it a 'static lifetime fn main() {} diff --git a/src/test/compile-fail/issue-30123.rs b/src/test/compile-fail/issue-30123.rs index cfd3cd3af3ebc..ae1320c821f8c 100644 --- a/src/test/compile-fail/issue-30123.rs +++ b/src/test/compile-fail/issue-30123.rs @@ -15,5 +15,5 @@ use issue_30123_aux::*; fn main() { let ug = Graph::::new_undirected(); - //~^ ERR no associated item named `new_undirected` found for type + //~^ ERROR no associated item named `new_undirected` found for type } diff --git a/src/test/compile-fail/issue-30302.rs b/src/test/compile-fail/issue-30302.rs index 56f0b31da0d80..26508a4722425 100644 --- a/src/test/compile-fail/issue-30302.rs +++ b/src/test/compile-fail/issue-30302.rs @@ -18,10 +18,8 @@ fn is_empty(s: Stack) -> bool { Nil => true, //~^ WARN pattern binding `Nil` is named the same as one of the variants of the type `Stack` //~| HELP consider making the path in the pattern qualified: `Stack::Nil` -//~| HELP run `rustc --explain E0170` to see a detailed explanation _ => false //~^ ERROR unreachable pattern -//~| HELP run `rustc --explain E0001` to see a detailed explanation } } diff --git a/src/test/compile-fail/symbol-names/issue-32709.rs b/src/test/compile-fail/issue-32709.rs similarity index 100% rename from src/test/compile-fail/symbol-names/issue-32709.rs rename to src/test/compile-fail/issue-32709.rs diff --git a/src/test/compile-fail/issue-3563.rs b/src/test/compile-fail/issue-3563.rs index 29c1c584eed24..7928c04b9df87 100644 --- a/src/test/compile-fail/issue-3563.rs +++ b/src/test/compile-fail/issue-3563.rs @@ -13,10 +13,6 @@ trait A { || self.b() //~^ ERROR no method named `b` found for type `&Self` in the current scope //~| ERROR mismatched types - //~| expected `()` - //~| found closure - //~| expected () - //~| found closure } } fn main() {} diff --git a/src/test/compile-fail/issue-6702.rs b/src/test/compile-fail/issue-6702.rs index 6cb825a9be736..66ed817ffa826 100644 --- a/src/test/compile-fail/issue-6702.rs +++ b/src/test/compile-fail/issue-6702.rs @@ -16,5 +16,4 @@ struct Monster { fn main() { let _m = Monster(); //~ ERROR `Monster` is the name of a struct or //~^ HELP did you mean to write: `Monster { /* fields */ }`? - //~| HELP run `rustc --explain E0423` to see a detailed explanation } diff --git a/src/test/compile-fail/keyword-false-as-identifier.rs b/src/test/compile-fail/keyword-false-as-identifier.rs index d875898f8bcc4..60caca3da57ed 100644 --- a/src/test/compile-fail/keyword-false-as-identifier.rs +++ b/src/test/compile-fail/keyword-false-as-identifier.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-test -- FIXME #33010 + // This file was auto-generated using 'src/etc/generate-keyword-tests.py false' fn main() { diff --git a/src/test/compile-fail/keyword-true-as-identifier.rs b/src/test/compile-fail/keyword-true-as-identifier.rs index 048b640c0b311..716a0ebf21cec 100644 --- a/src/test/compile-fail/keyword-true-as-identifier.rs +++ b/src/test/compile-fail/keyword-true-as-identifier.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-test -- FIXME #33010 + // This file was auto-generated using 'src/etc/generate-keyword-tests.py true' fn main() { diff --git a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs index be4166e43b504..7355c70ff95e8 100644 --- a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs +++ b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs @@ -11,7 +11,6 @@ // Lifetime annotation needed because we have no arguments. fn f() -> &isize { //~ ERROR missing lifetime specifier //~^ HELP there is no value for it to be borrowed from -//~| HELP run `rustc --explain E0106` to see a detailed explanation //~| HELP consider giving it a 'static lifetime panic!() } @@ -19,7 +18,6 @@ fn f() -> &isize { //~ ERROR missing lifetime specifier // Lifetime annotation needed because we have two by-reference parameters. fn g(_x: &isize, _y: &isize) -> &isize { //~ ERROR missing lifetime specifier //~^ HELP the signature does not say whether it is borrowed from `_x` or `_y` -//~| HELP run `rustc --explain E0106` to see a detailed explanation panic!() } @@ -31,13 +29,11 @@ struct Foo<'a> { // and one on the reference. fn h(_x: &Foo) -> &isize { //~ ERROR missing lifetime specifier //~^ HELP the signature does not say which one of `_x`'s 2 elided lifetimes it is borrowed from -//~| HELP run `rustc --explain E0106` to see a detailed explanation panic!() } fn i(_x: isize) -> &isize { //~ ERROR missing lifetime specifier //~^ HELP this function's return type contains a borrowed value -//~| HELP run `rustc --explain E0106` to see a detailed explanation //~| HELP consider giving it an explicit bounded or 'static lifetime panic!() } diff --git a/src/test/compile-fail/lint-change-warnings.rs b/src/test/compile-fail/lint-change-warnings.rs index 441a8410700bf..19e253e3b8e53 100644 --- a/src/test/compile-fail/lint-change-warnings.rs +++ b/src/test/compile-fail/lint-change-warnings.rs @@ -27,5 +27,5 @@ fn bar() { #[forbid(warnings)] fn baz() { - while true {} //~ ERROR: warnings + while true {} //~ ERROR: infinite } diff --git a/src/test/compile-fail/lint-malformed.rs b/src/test/compile-fail/lint-malformed.rs index 592e2b11905b3..ad5e3aa3f0606 100644 --- a/src/test/compile-fail/lint-malformed.rs +++ b/src/test/compile-fail/lint-malformed.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny = "foo"] //~ ERR malformed lint attribute -#![allow(bar = "baz")] //~ ERR malformed lint attribute +#![deny = "foo"] //~ ERROR malformed lint attribute +#![allow(bar = "baz")] //~ ERROR malformed lint attribute fn main() { } diff --git a/src/test/compile-fail/lint-removed-allow.rs b/src/test/compile-fail/lint-removed-allow.rs index 159a3d746034e..1498ed4d17ed7 100644 --- a/src/test/compile-fail/lint-removed-allow.rs +++ b/src/test/compile-fail/lint-removed-allow.rs @@ -14,4 +14,4 @@ #[deny(raw_pointer_derive)] #[allow(renamed_and_removed_lints)] #[deny(unused_variables)] -fn main() { let unused = (); } //~ ERR unused +fn main() { let unused = (); } //~ ERROR unused diff --git a/src/test/compile-fail/lint-removed.rs b/src/test/compile-fail/lint-removed.rs index 9069356604131..aa7f535aa64b7 100644 --- a/src/test/compile-fail/lint-removed.rs +++ b/src/test/compile-fail/lint-removed.rs @@ -15,4 +15,4 @@ #[deny(raw_pointer_derive)] //~ WARN raw_pointer_derive has been removed #[deny(unused_variables)] -fn main() { let unused = (); } //~ ERR unused +fn main() { let unused = (); } //~ ERROR unused diff --git a/src/test/compile-fail/lint-renamed-allow.rs b/src/test/compile-fail/lint-renamed-allow.rs index a2426d80f714d..ea26c3656e691 100644 --- a/src/test/compile-fail/lint-renamed-allow.rs +++ b/src/test/compile-fail/lint-renamed-allow.rs @@ -14,4 +14,4 @@ #[deny(unknown_features)] #[allow(renamed_and_removed_lints)] #[deny(unused)] -fn main() { let unused = (); } //~ ERR unused +fn main() { let unused = (); } //~ ERROR unused diff --git a/src/test/compile-fail/lint-renamed.rs b/src/test/compile-fail/lint-renamed.rs index 2e85a323a1c41..9e10ddf89ac33 100644 --- a/src/test/compile-fail/lint-renamed.rs +++ b/src/test/compile-fail/lint-renamed.rs @@ -10,4 +10,4 @@ #[deny(unknown_features)] //~ WARN lint unknown_features has been renamed to unused_features #[deny(unused)] -fn main() { let unused = (); } //~ ERR unused +fn main() { let unused = (); } //~ ERROR unused diff --git a/src/test/compile-fail/lint-unknown-lint.rs b/src/test/compile-fail/lint-unknown-lint.rs index 8f20a2c8ab758..2de8d849d1915 100644 --- a/src/test/compile-fail/lint-unknown-lint.rs +++ b/src/test/compile-fail/lint-unknown-lint.rs @@ -10,4 +10,4 @@ #![allow(not_a_real_lint)] //~ WARN unknown lint #![deny(unused)] -fn main() { let unused = (); } //~ ERR unused variable +fn main() { let unused = (); } //~ ERROR unused variable diff --git a/src/test/compile-fail/liveness-return-last-stmt-semi.rs b/src/test/compile-fail/liveness-return-last-stmt-semi.rs index 343622c5c1b04..03733cc2eb596 100644 --- a/src/test/compile-fail/liveness-return-last-stmt-semi.rs +++ b/src/test/compile-fail/liveness-return-last-stmt-semi.rs @@ -12,19 +12,15 @@ macro_rules! test { () => { fn foo() -> i32 { 1; } } } //~^ ERROR not all control paths return a value - //~^^ HELP consider removing this semicolon - //~^^^ HELP run `rustc --explain E0269` to see a + //~| HELP consider removing this semicolon fn no_return() -> i32 {} //~ ERROR not all control paths return a value - //~^ HELP run `rustc --explain E0269` to see a detailed explanation fn bar(x: u32) -> u32 { //~ ERROR not all control paths return a value - //~^ HELP run `rustc --explain E0269` to see a detailed explanation x * 2; //~ HELP consider removing this semicolon } fn baz(x: u64) -> u32 { //~ ERROR not all control paths return a value - //~^ HELP run `rustc --explain E0269` to see a detailed explanation x * 2; } diff --git a/src/test/compile-fail/privacy/restricted/private-in-public.rs b/src/test/compile-fail/privacy/restricted/private-in-public.rs index f8e7e6283a0af..84328ca387d73 100644 --- a/src/test/compile-fail/privacy/restricted/private-in-public.rs +++ b/src/test/compile-fail/privacy/restricted/private-in-public.rs @@ -15,6 +15,8 @@ mod foo { mod bar { use foo::Priv; pub(super) fn f(_: Priv) {} - pub(crate) fn f(_: Priv) {} //~ ERROR private + pub(crate) fn g(_: Priv) {} //~ ERROR E0446 } } + +fn main() { } diff --git a/src/test/compile-fail/ref-suggestion.rs b/src/test/compile-fail/ref-suggestion.rs index 4625669d5ecfe..815f752663223 100644 --- a/src/test/compile-fail/ref-suggestion.rs +++ b/src/test/compile-fail/ref-suggestion.rs @@ -14,14 +14,12 @@ fn main() { //~^ HELP use a `ref` binding as shown //~| SUGGESTION let ref y = x; x; //~ ERROR use of moved value - //~^ HELP run `rustc --explain E0382` to see a detailed explanation 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 - //~^ HELP run `rustc --explain E0382` to see a detailed explanation let x = (Some(vec![1]), ()); @@ -32,5 +30,4 @@ fn main() { _ => {}, } x; //~ ERROR use of partially moved value - //~^ HELP run `rustc --explain E0382` to see a detailed explanation } diff --git a/src/test/compile-fail/substs-ppaux.rs b/src/test/compile-fail/substs-ppaux.rs index 851e31b942ed1..8dd9994b234ba 100644 --- a/src/test/compile-fail/substs-ppaux.rs +++ b/src/test/compile-fail/substs-ppaux.rs @@ -28,7 +28,7 @@ fn foo<'z>() where &'z (): Sized { //[verbose]~| found `fn() {>::bar::}` //[normal]~^^^^ ERROR mismatched types //[normal]~| expected `()` - //[normal]~| found `fn() {>::bar::<'static, char>}` + //[normal]~| found `fn() {>::bar::<'static, char>}` let x: () = >::bar::<'static, char>; diff --git a/src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs b/src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs index 8877377a6ec96..1d04679fd11e7 100644 --- a/src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs +++ b/src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs @@ -27,54 +27,46 @@ fn h1() -> i32 { a.I //~^ ERROR E0425 //~| HELP To reference an item from the `a` module, use `a::I` - //~| HELP run `rustc --explain E0425` to see a detailed explanation } fn h2() -> i32 { a.g() //~^ ERROR E0425 //~| HELP To call a function from the `a` module, use `a::g(..)` - //~| HELP run `rustc --explain E0425` to see a detailed explanation } fn h3() -> i32 { a.b.J //~^ ERROR E0425 //~| HELP To reference an item from the `a` module, use `a::b` - //~| HELP run `rustc --explain E0425` to see a detailed explanation } fn h4() -> i32 { a::b.J //~^ ERROR E0425 //~| HELP To reference an item from the `a::b` module, use `a::b::J` - //~| HELP run `rustc --explain E0425` to see a detailed explanation } fn h5() -> i32 { a.b.f() //~^ ERROR E0425 //~| HELP To reference an item from the `a` module, use `a::b` - //~| HELP run `rustc --explain E0425` to see a detailed explanation } fn h6() -> i32 { a::b.f() //~^ ERROR E0425 //~| HELP To call a function from the `a::b` module, use `a::b::f(..)` - //~| HELP run `rustc --explain E0425` to see a detailed explanation } fn h7() { a::b //~^ ERROR E0425 //~| HELP Module `a::b` cannot be the value of an expression - //~| HELP run `rustc --explain E0425` to see a detailed explanation } fn h8() -> i32 { a::b() //~^ ERROR E0425 //~| HELP No function corresponds to `a::b(..)` - //~| HELP run `rustc --explain E0425` to see a detailed explanation } diff --git a/src/test/compile-fail/trait-bounds-impl-comparison-1.rs b/src/test/compile-fail/trait-bounds-impl-comparison-1.rs index 3fffb2e19f289..9cf65a9d00d07 100644 --- a/src/test/compile-fail/trait-bounds-impl-comparison-1.rs +++ b/src/test/compile-fail/trait-bounds-impl-comparison-1.rs @@ -74,7 +74,7 @@ trait Trait { impl Trait for usize { fn method>(&self) {} - //~^ G : Getter` appears on the impl method but not on the corresponding trait method + //~^ ERROR `G: Getter` appears on the impl method } fn main() {} diff --git a/src/test/compile-fail/trait-object-reference-without-parens-suggestion.rs b/src/test/compile-fail/trait-object-reference-without-parens-suggestion.rs index 29360e58b5bd3..fc2ed83b2724d 100644 --- a/src/test/compile-fail/trait-object-reference-without-parens-suggestion.rs +++ b/src/test/compile-fail/trait-object-reference-without-parens-suggestion.rs @@ -13,10 +13,8 @@ fn main() { //~^ ERROR expected a path //~| HELP try adding parentheses //~| SUGGESTION let _: &(Copy + 'static); - //~| HELP run `rustc --explain E0178` to see a detailed explanation let _: &'static Copy + 'static; //~^ ERROR expected a path //~| HELP try adding parentheses //~| SUGGESTION let _: &'static (Copy + 'static); - //~| HELP run `rustc --explain E0178` to see a detailed explanation } diff --git a/src/test/compile-fail/trait-suggest-where-clause.rs b/src/test/compile-fail/trait-suggest-where-clause.rs index 6950bce7304c0..a8ff1bae7a71a 100644 --- a/src/test/compile-fail/trait-suggest-where-clause.rs +++ b/src/test/compile-fail/trait-suggest-where-clause.rs @@ -16,13 +16,11 @@ fn check() { // suggest a where-clause, if needed mem::size_of::(); //~^ ERROR `U: std::marker::Sized` is not satisfied - //~| HELP E0277 //~| HELP consider adding a `where U: std::marker::Sized` bound //~| NOTE required by `std::mem::size_of` mem::size_of::>(); //~^ ERROR `U: std::marker::Sized` is not satisfied - //~| HELP E0277 //~| HELP consider adding a `where U: std::marker::Sized` bound //~| NOTE required because it appears within the type `Misc` //~| NOTE required by `std::mem::size_of` @@ -31,13 +29,11 @@ fn check() { >::from; //~^ ERROR `u64: std::convert::From` is not satisfied - //~| HELP E0277 //~| HELP consider adding a `where u64: std::convert::From` bound //~| NOTE required by `std::convert::From::from` ::Item>>::from; //~^ ERROR `u64: std::convert::From<::Item>` is not satisfied - //~| HELP E0277 //~| HELP consider adding a `where u64: //~| NOTE required by `std::convert::From::from` @@ -45,20 +41,17 @@ fn check() { as From>::from; //~^ ERROR `Misc<_>: std::convert::From` is not satisfied - //~| HELP E0277 //~| NOTE required by `std::convert::From::from` // ... and also not if the error is not related to the type mem::size_of::<[T]>(); //~^ ERROR `[T]: std::marker::Sized` is not satisfied - //~| HELP E0277 //~| NOTE `[T]` does not have a constant size //~| NOTE required by `std::mem::size_of` mem::size_of::<[&U]>(); //~^ ERROR `[&U]: std::marker::Sized` is not satisfied - //~| HELP E0277 //~| NOTE `[&U]` does not have a constant size //~| NOTE required by `std::mem::size_of` } diff --git a/src/test/compile-fail/transmute-from-fn-item-types-lint.rs b/src/test/compile-fail/transmute-from-fn-item-types-lint.rs index 42c3cb7f18131..08e660e878c29 100644 --- a/src/test/compile-fail/transmute-from-fn-item-types-lint.rs +++ b/src/test/compile-fail/transmute-from-fn-item-types-lint.rs @@ -15,15 +15,15 @@ use std::mem; unsafe fn foo() -> (isize, *const (), Option) { let i = mem::transmute(bar); //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting - //~^^ ERROR was previously accepted + //~^^ WARNING was previously accepted let p = mem::transmute(foo); //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting - //~^^ ERROR was previously accepted + //~^^ WARNING was previously accepted let of = mem::transmute(main); //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting - //~^^ ERROR was previously accepted + //~^^ WARNING was previously accepted (i, p, of) } @@ -31,11 +31,11 @@ unsafe fn foo() -> (isize, *const (), Option) { unsafe fn bar() { mem::transmute::<_, *mut ()>(foo); //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting - //~^^ ERROR was previously accepted + //~^^ WARNING was previously accepted mem::transmute::<_, fn()>(bar); //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting - //~^^ ERROR was previously accepted + //~^^ WARNING was previously accepted // No error if a coercion would otherwise occur. mem::transmute::(main); 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 014fa35c309e6..e81ae5d743939 100644 --- a/src/test/compile-fail/type-mismatch-same-crate-name.rs +++ b/src/test/compile-fail/type-mismatch-same-crate-name.rs @@ -24,10 +24,8 @@ fn main() { { extern crate crate_a1 as a; a::try_foo(foo2); //~ ERROR mismatched types - //~^ HELP run - //~^^ NOTE Perhaps two different versions of crate `crate_a1` + //~^ NOTE Perhaps two different versions of crate `crate_a1` a::try_bar(bar2); //~ ERROR mismatched types - //~^ HELP run - //~^^ NOTE Perhaps two different versions of crate `crate_a1` + //~^ NOTE Perhaps two different versions of crate `crate_a1` } } diff --git a/src/test/compile-fail/variance-unused-type-param.rs b/src/test/compile-fail/variance-unused-type-param.rs index f7fed32cb5af2..862d842d62c23 100644 --- a/src/test/compile-fail/variance-unused-type-param.rs +++ b/src/test/compile-fail/variance-unused-type-param.rs @@ -16,18 +16,15 @@ struct SomeStruct { x: u32 } //~^ ERROR parameter `A` is never used //~| HELP PhantomData -//~| HELP run `rustc --explain E0392` to see a detailed explanation enum SomeEnum { Nothing } //~^ ERROR parameter `A` is never used //~| HELP PhantomData -//~| HELP run `rustc --explain E0392` to see a detailed explanation // Here T might *appear* used, but in fact it isn't. enum ListCell { //~^ ERROR parameter `T` is never used //~| HELP PhantomData -//~| HELP run `rustc --explain E0392` to see a detailed explanation Cons(Box>), Nil } diff --git a/src/test/parse-fail/column-offset-1-based.rs b/src/test/parse-fail/column-offset-1-based.rs index bcadd1f39fa98..8caf2e0c0a477 100644 --- a/src/test/parse-fail/column-offset-1-based.rs +++ b/src/test/parse-fail/column-offset-1-based.rs @@ -10,4 +10,4 @@ // compile-flags: -Z parse-only -# //~ ERROR 13:1: 13:2 error: expected `[`, found `` +# //~ ERROR 13:1: 13:2: expected `[`, found `` diff --git a/src/test/parse-fail/keyword-do-as-identifier.rs b/src/test/parse-fail/keyword-do-as-identifier.rs deleted file mode 100644 index 5cc14dfef0ec6..0000000000000 --- a/src/test/parse-fail/keyword-do-as-identifier.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2013 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. - -// compile-flags: -Z parse-only - -fn main() { - let do = "bar"; //~ error: ident -} diff --git a/src/test/parse-fail/keyword-priv-as-identifier.rs b/src/test/parse-fail/keyword-priv-as-identifier.rs deleted file mode 100644 index e80feb66d6071..0000000000000 --- a/src/test/parse-fail/keyword-priv-as-identifier.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 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. - -// compile-flags: -Z parse-only - -// This file was auto-generated using 'src/etc/generate-keyword-tests.py priv' - -fn main() { - let priv = "foo"; //~ error: ident -} diff --git a/src/test/run-make/json-errors/Makefile b/src/test/run-make/json-errors/Makefile index bb73fda67bddb..30bcafd104945 100644 --- a/src/test/run-make/json-errors/Makefile +++ b/src/test/run-make/json-errors/Makefile @@ -6,5 +6,4 @@ all: cp foo.rs $(TMPDIR) cd $(TMPDIR) -$(RUSTC) -Z unstable-options --error-format=json foo.rs 2>$(LOG) - grep -q '{"message":"unresolved name `y`","code":{"code":"E0425","explanation":"\\nAn unresolved name was used. Example of erroneous codes.*"},"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}\]}\],"children":\[\]}' $(LOG) - grep -q '{"message":".*","code":{"code":"E0277","explanation":"\\nYou tried.*"},"level":"error","spans":\[{.*}\],"children":\[{"message":"the .*","code":null,"level":"help","spans":\[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":0,"column_end":0,"text":\[{.*}\]}\],"children":\[\]},{"message":" ","code":null,"level":"help",' $(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 new file mode 100644 index 0000000000000..bde669ab0f7f9 --- /dev/null +++ b/src/test/run-make/json-errors/foo.json @@ -0,0 +1,4 @@ +{"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/tools/compiletest/Cargo.lock b/src/tools/compiletest/Cargo.lock index 3b79636a2db5b..24c8b20fdf8d7 100644 --- a/src/tools/compiletest/Cargo.lock +++ b/src/tools/compiletest/Cargo.lock @@ -4,6 +4,7 @@ version = "0.0.0" dependencies = [ "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serialize 0.0.0", ] [[package]] @@ -28,6 +29,10 @@ name = "libc" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "log" +version = "0.0.0" + [[package]] name = "log" version = "0.3.6" @@ -63,6 +68,13 @@ name = "regex-syntax" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "serialize" +version = "0.0.0" +dependencies = [ + "log 0.0.0", +] + [[package]] name = "utf8-ranges" version = "0.1.3" diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 359efe8af62e9..573ee38886693 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -13,3 +13,4 @@ opt-level = 2 [dependencies] log = "0.3" env_logger = "0.3" +serialize = { path = "../../libserialize" } \ No newline at end of file diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs index 418a0bc7121cb..c3da891933f6d 100644 --- a/src/tools/compiletest/src/errors.rs +++ b/src/tools/compiletest/src/errors.rs @@ -28,7 +28,9 @@ pub enum ErrorKind { impl FromStr for ErrorKind { type Err = (); fn from_str(s: &str) -> Result { - match &s.trim_right_matches(':') as &str { + let s = s.to_uppercase(); + let part0: &str = s.split(':').next().unwrap(); + match part0 { "HELP" => Ok(ErrorKind::Help), "ERROR" => Ok(ErrorKind::Error), "NOTE" => Ok(ErrorKind::Note), @@ -52,7 +54,8 @@ impl fmt::Display for ErrorKind { } } -pub struct ExpectedError { +#[derive(Debug)] +pub struct Error { pub line_num: usize, /// What kind of message we expect (e.g. warning, error, suggestion). /// `None` if not specified or unknown message kind. @@ -73,7 +76,7 @@ enum WhichLine { ThisLine, FollowPrevious(usize), AdjustBackward(usize) } /// /// If cfg is not None (i.e., in an incremental test), then we look /// for `//[X]~` instead, where `X` is the current `cfg`. -pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec { +pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec { let rdr = BufReader::new(File::open(testfile).unwrap()); // `last_nonfollow_error` tracks the most recently seen @@ -113,7 +116,7 @@ fn parse_expected(last_nonfollow_error: Option, line_num: usize, line: &str, tag: &str) - -> Option<(WhichLine, ExpectedError)> { + -> Option<(WhichLine, Error)> { let start = match line.find(tag) { Some(i) => i, None => return None }; let (follow, adjusts) = if line[start + tag.len()..].chars().next().unwrap() == '|' { (true, 0) @@ -121,15 +124,30 @@ fn parse_expected(last_nonfollow_error: Option, (false, line[start + tag.len()..].chars().take_while(|c| *c == '^').count()) }; let kind_start = start + tag.len() + adjusts + (follow as usize); - let kind = line[kind_start..].split_whitespace() - .next() - .expect("Encountered unexpected empty comment") - .parse::() - .ok(); - let letters = line[kind_start..].chars(); - let msg = letters.skip_while(|c| c.is_whitespace()) - .skip_while(|c| !c.is_whitespace()) - .collect::().trim().to_owned(); + let (kind, msg); + match + line[kind_start..].split_whitespace() + .next() + .expect("Encountered unexpected empty comment") + .parse::() + { + Ok(k) => { + // If we find `//~ ERROR foo` or something like that: + kind = Some(k); + let letters = line[kind_start..].chars(); + msg = letters.skip_while(|c| c.is_whitespace()) + .skip_while(|c| !c.is_whitespace()) + .collect::(); + } + Err(_) => { + // Otherwise we found `//~ foo`: + kind = None; + let letters = line[kind_start..].chars(); + msg = letters.skip_while(|c| c.is_whitespace()) + .collect::(); + } + } + let msg = msg.trim().to_owned(); let (which, line_num) = if follow { assert!(adjusts == 0, "use either //~| or //~^, not both."); @@ -145,7 +163,7 @@ fn parse_expected(last_nonfollow_error: Option, debug!("line={} tag={:?} which={:?} kind={:?} msg={:?}", line_num, tag, which, kind, msg); - Some((which, ExpectedError { line_num: line_num, - kind: kind, - msg: msg, })) + Some((which, Error { line_num: line_num, + kind: kind, + msg: msg, })) } diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs new file mode 100644 index 0000000000000..073b5e57cc7c5 --- /dev/null +++ b/src/tools/compiletest/src/json.rs @@ -0,0 +1,197 @@ +// 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. + +use errors::{Error, ErrorKind}; +use rustc_serialize::json; +use std::str::FromStr; +use std::path::Path; + +// These structs are a subset of the ones found in +// `syntax::errors::json`. + +#[derive(RustcEncodable, RustcDecodable)] +struct Diagnostic { + message: String, + code: Option, + level: String, + spans: Vec, + children: Vec, + rendered: Option, +} + +#[derive(RustcEncodable, RustcDecodable, Clone)] +struct DiagnosticSpan { + file_name: String, + line_start: usize, + line_end: usize, + column_start: usize, + column_end: usize, + expansion: Option>, +} + +#[derive(RustcEncodable, RustcDecodable, Clone)] +struct DiagnosticSpanMacroExpansion { + /// span where macro was applied to generate this code + span: DiagnosticSpan, + + /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]") + macro_decl_name: String, +} + +#[derive(RustcEncodable, RustcDecodable, Clone)] +struct DiagnosticCode { + /// The code itself. + code: String, + /// An explanation for the code. + explanation: Option, +} + +pub fn parse_output(file_name: &str, output: &str) -> Vec { + output.lines() + .flat_map(|line| parse_line(file_name, line)) + .collect() +} + +fn parse_line(file_name: &str, line: &str) -> Vec { + // The compiler sometimes intermingles non-JSON stuff into the + // output. This hack just skips over such lines. Yuck. + if line.chars().next() == Some('{') { + match json::decode::(line) { + Ok(diagnostic) => { + let mut expected_errors = vec![]; + push_expected_errors(&mut expected_errors, &diagnostic, file_name); + expected_errors + } + Err(error) => { + panic!("failed to decode compiler output as json: `{}`", error); + } + } + } else { + vec![] + } +} + +fn push_expected_errors(expected_errors: &mut Vec, + diagnostic: &Diagnostic, + 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) + }) + }; + + // We break the output into multiple lines, and then append the + // [E123] to every line in the output. This may be overkill. The + // intention was to match existing tests that do things like "//| + // found `i32` [E123]" and expect to match that somewhere, and yet + // also ensure that `//~ ERROR E123` *always* works. The + // assumption is that these multi-line error messages are on their + // way out anyhow. + let with_code = |span: &DiagnosticSpan, text: &str| { + match diagnostic.code { + Some(ref code) => + // FIXME(#33000) -- it'd be better to use a dedicated + // UI harness than to include the line/col number like + // this, but some current tests rely on it. + // + // Note: Do NOT include the filename. These can easily + // cause false matches where the expected message + // appears in the filename, and hence the message + // changes but the test still passes. + format!("{}:{}: {}:{}: {} [{}]", + span.line_start, span.column_start, + span.line_end, span.column_end, + text, code.code.clone()), + None => + // FIXME(#33000) -- it'd be better to use a dedicated UI harness + format!("{}:{}: {}:{}: {}", + span.line_start, span.column_start, + span.line_end, span.column_end, + text), + } + }; + + // Convert multi-line messages into multiple expected + // errors. We expect to replace these with something + // more structured shortly anyhow. + let mut message_lines = diagnostic.message.lines(); + if let Some(first_line) = message_lines.next() { + for span in matching_spans() { + let msg = with_code(span, first_line); + let kind = ErrorKind::from_str(&diagnostic.level).ok(); + expected_errors.push( + Error { + line_num: span.line_start, + kind: kind, + msg: msg, + } + ); + } + } + for next_line in message_lines { + for span in matching_spans() { + expected_errors.push( + Error { + line_num: span.line_start, + kind: None, + msg: with_code(span, next_line), + } + ); + } + } + + // 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("\ + every suggestion should have at least one span"); + for (index, line) in rendered.lines().enumerate() { + expected_errors.push( + Error { + line_num: start_line + index, + kind: Some(ErrorKind::Suggestion), + msg: line.to_string() + } + ); + } + } + + // Add notes for the backtrace + for span in matching_spans() { + for frame in &span.expansion { + push_backtrace(expected_errors, + frame, + file_name); + } + } + + // Flatten out the children. + for child in &diagnostic.children { + push_expected_errors(expected_errors, child, file_name); + } +} + +fn push_backtrace(expected_errors: &mut Vec, + expansion: &DiagnosticSpanMacroExpansion, + file_name: &str) { + if Path::new(&expansion.span.file_name) == Path::new(&file_name) { + expected_errors.push( + Error { + line_num: expansion.span.line_start, + kind: Some(ErrorKind::Note), + msg: format!("in this expansion of {}", expansion.macro_decl_name), + } + ); + } + + for previous_expansion in &expansion.span.expansion { + push_backtrace(expected_errors, previous_expansion, file_name); + } +} diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index e92b0c8728099..8e999139a249b 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -21,6 +21,7 @@ extern crate libc; extern crate test; extern crate getopts; +extern crate serialize as rustc_serialize; #[macro_use] extern crate log; @@ -40,6 +41,7 @@ use util::logv; pub mod procsrv; pub mod util; +mod json; pub mod header; pub mod runtest; pub mod common; diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 6358d19ff0906..8770d4b1f4255 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -12,7 +12,8 @@ use common::Config; use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind}; use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits}; use common::{Incremental}; -use errors::{self, ErrorKind}; +use errors::{self, ErrorKind, Error}; +use json; use header::TestProps; use header; use procsrv; @@ -26,7 +27,7 @@ use std::fs::{self, File}; use std::io::BufReader; use std::io::prelude::*; use std::net::TcpStream; -use std::path::{Path, PathBuf, Component}; +use std::path::{Path, PathBuf}; use std::process::{Command, Output, ExitStatus}; pub fn run(config: Config, testpaths: &TestPaths) { @@ -944,7 +945,7 @@ fn check_error_patterns(revision: Option<&str>, testpaths.file.display())); } let mut next_err_idx = 0; - let mut next_err_pat = &props.error_patterns[next_err_idx]; + let mut next_err_pat = props.error_patterns[next_err_idx].trim(); let mut done = false; for line in output_to_check.lines() { if line.contains(next_err_pat) { @@ -955,7 +956,7 @@ fn check_error_patterns(revision: Option<&str>, done = true; break; } - next_err_pat = &props.error_patterns[next_err_idx]; + next_err_pat = props.error_patterns[next_err_idx].trim(); } } if done { return; } @@ -998,208 +999,110 @@ fn check_forbid_output(revision: Option<&str>, } fn check_expected_errors(revision: Option<&str>, - expected_errors: Vec, + expected_errors: Vec, testpaths: &TestPaths, proc_res: &ProcRes) { - // true if we found the error in question - let mut found_flags = vec![false; expected_errors.len()]; - if proc_res.status.success() { fatal_proc_rec(revision, "process did not return an error status", proc_res); } - let prefixes = expected_errors.iter().map(|ee| { - let expected = format!("{}:{}:", testpaths.file.display(), ee.line_num); - // On windows just translate all '\' path separators to '/' - expected.replace(r"\", "/") - }).collect::>(); + let file_name = + format!("{}", testpaths.file.display()) + .replace(r"\", "/"); // on windows, translate all '\' path separators to '/' // If the testcase being checked contains at least one expected "help" // message, then we'll ensure that all "help" messages are expected. // Otherwise, all "help" messages reported by the compiler will be ignored. // This logic also applies to "note" messages. - let (expect_help, expect_note) = - expected_errors.iter() - .fold((false, false), - |(acc_help, acc_note), ee| - (acc_help || ee.kind == Some(ErrorKind::Help), - acc_note || ee.kind == Some(ErrorKind::Note))); - - // Scan and extract our error/warning messages, - // which look like: - // filename:line1:col1: line2:col2: *error:* msg - // filename:line1:col1: line2:col2: *warning:* msg - // where line1:col1: is the starting point, line2:col2: - // is the ending point, and * represents ANSI color codes. - // - // This pattern is ambiguous on windows, because filename may contain - // a colon, so any path prefix must be detected and removed first. + let expect_help = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Help)); + let expect_note = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Note)); + + // Parse the JSON output from the compiler and extract out the messages. + let actual_errors = json::parse_output(&file_name, &proc_res.stderr); let mut unexpected = 0; let mut not_found = 0; - for line in proc_res.stderr.lines() { - let mut was_expected = false; - let mut prev = 0; - for (i, ee) in expected_errors.iter().enumerate() { - if !found_flags[i] { - debug!("prefix={} ee.kind={:?} ee.msg={} line={}", - prefixes[i], - ee.kind, - ee.msg, - line); - // Suggestions have no line number in their output, so take on the line number of - // the previous expected error - if ee.kind == Some(ErrorKind::Suggestion) { - assert!(expected_errors[prev].kind == Some(ErrorKind::Help), - "SUGGESTIONs must be preceded by a HELP"); - if line.contains(&ee.msg) { - found_flags[i] = true; - was_expected = true; - break; - } - } - if - (prefix_matches(line, &prefixes[i]) || continuation(line)) && - (ee.kind.is_none() || line.contains(&ee.kind.as_ref().unwrap().to_string())) && - line.contains(&ee.msg) - { - found_flags[i] = true; - was_expected = true; - break; - } + let mut found = vec![false; expected_errors.len()]; + for actual_error in &actual_errors { + let opt_index = + expected_errors + .iter() + .enumerate() + .position(|(index, expected_error)| { + !found[index] && + actual_error.line_num == expected_error.line_num && + (expected_error.kind.is_none() || + actual_error.kind == expected_error.kind) && + actual_error.msg.contains(&expected_error.msg) + }); + + match opt_index { + Some(index) => { + // found a match, everybody is happy + assert!(!found[index]); + found[index] = true; } - prev = i; - } - - // ignore this msg which gets printed at the end - if line.contains("aborting due to") { - was_expected = true; - } - if !was_expected && is_unexpected_compiler_message(line, expect_help, expect_note) { - error(revision, &format!("unexpected compiler message: '{}'", line)); - unexpected += 1; + None => { + if is_unexpected_compiler_message(actual_error, + expect_help, + expect_note) { + error(revision, + &format!("{}:{}: unexpected {:?}: '{}'", + file_name, + actual_error.line_num, + actual_error.kind.as_ref() + .map_or(String::from("message"), + |k| k.to_string()), + actual_error.msg)); + unexpected += 1; + } + } } } - for (i, &flag) in found_flags.iter().enumerate() { - if !flag { - let ee = &expected_errors[i]; - error(revision, &format!("expected {} on line {} not found: {}", - ee.kind.as_ref() - .map_or("message".into(), - |k| k.to_string()), - ee.line_num, ee.msg)); + // anything not yet found is a problem + for (index, expected_error) in expected_errors.iter().enumerate() { + if !found[index] { + error(revision, + &format!("{}:{}: expected {} not found: {}", + file_name, + expected_error.line_num, + expected_error.kind.as_ref() + .map_or("message".into(), + |k| k.to_string()), + expected_error.msg)); not_found += 1; } } if unexpected > 0 || not_found > 0 { - fatal_proc_rec( - revision, - &format!("{} unexpected errors found, {} expected errors not found", - unexpected, not_found), - proc_res); - } - - fn prefix_matches(line: &str, prefix: &str) -> bool { - use std::ascii::AsciiExt; - // On windows just translate all '\' path separators to '/' - let line = line.replace(r"\", "/"); - if cfg!(windows) { - line.to_ascii_lowercase().starts_with(&prefix.to_ascii_lowercase()) - } else { - line.starts_with(prefix) - } - } - - // A multi-line error will have followup lines which start with a space - // or open paren. - fn continuation( line: &str) -> bool { - line.starts_with(" ") || line.starts_with("(") - } -} - -fn is_unexpected_compiler_message(line: &str, expect_help: bool, expect_note: bool) -> bool { - let mut c = Path::new(line).components(); - let line = match c.next() { - Some(Component::Prefix(_)) => c.as_path().to_str().unwrap(), - _ => line, - }; - - let mut i = 0; - return scan_until_char(line, ':', &mut i) && - scan_char(line, ':', &mut i) && - scan_integer(line, &mut i) && - scan_char(line, ':', &mut i) && - scan_integer(line, &mut i) && - scan_char(line, ':', &mut i) && - scan_char(line, ' ', &mut i) && - scan_integer(line, &mut i) && - scan_char(line, ':', &mut i) && - scan_integer(line, &mut i) && - scan_char(line, ' ', &mut i) && - (scan_string(line, "error", &mut i) || - scan_string(line, "warning", &mut i) || - (expect_help && scan_string(line, "help", &mut i)) || - (expect_note && scan_string(line, "note", &mut i)) - ); -} - -fn scan_until_char(haystack: &str, needle: char, idx: &mut usize) -> bool { - if *idx >= haystack.len() { - return false; - } - let opt = haystack[(*idx)..].find(needle); - if opt.is_none() { - return false; - } - *idx = opt.unwrap(); - return true; -} - -fn scan_char(haystack: &str, needle: char, idx: &mut usize) -> bool { - if *idx >= haystack.len() { - return false; - } - let ch = haystack[*idx..].chars().next().unwrap(); - if ch != needle { - return false; - } - *idx += ch.len_utf8(); - return true; -} - -fn scan_integer(haystack: &str, idx: &mut usize) -> bool { - let mut i = *idx; - while i < haystack.len() { - let ch = haystack[i..].chars().next().unwrap(); - if ch < '0' || '9' < ch { - break; - } - i += ch.len_utf8(); - } - if i == *idx { - return false; + error(revision, + &format!("{} unexpected errors found, {} expected errors not found", + unexpected, not_found)); + print!("status: {}\ncommand: {}\n", + proc_res.status, proc_res.cmdline); + println!("actual errors (from JSON output): {:#?}\n", actual_errors); + println!("expected errors (from test file): {:#?}\n", expected_errors); + panic!(); } - *idx = i; - return true; } -fn scan_string(haystack: &str, needle: &str, idx: &mut usize) -> bool { - let mut haystack_i = *idx; - let mut needle_i = 0; - while needle_i < needle.len() { - if haystack_i >= haystack.len() { - return false; - } - let ch = haystack[haystack_i..].chars().next().unwrap(); - haystack_i += ch.len_utf8(); - if !scan_char(needle, ch, &mut needle_i) { - return false; - } +/// Returns true if we should report an error about `actual_error`, +/// which did not match any of the expected error. We always require +/// errors/warnings to be explicitly listed, but only require +/// helps/notes if there are explicit helps/notes given. +fn is_unexpected_compiler_message(actual_error: &Error, + expect_help: bool, + expect_note: bool) + -> bool { + match actual_error.kind { + Some(ErrorKind::Help) => expect_help, + Some(ErrorKind::Note) => expect_note, + Some(ErrorKind::Error) => true, + Some(ErrorKind::Warning) => true, + Some(ErrorKind::Suggestion) => false, + None => false } - *idx = haystack_i; - return true; } struct ProcArgs { @@ -1444,6 +1347,37 @@ fn make_compile_args(config: &Config, "-L".to_owned(), config.build_base.to_str().unwrap().to_owned(), format!("--target={}", target)); + + match config.mode { + CompileFail | + ParseFail | + Incremental => { + // If we are extracting and matching errors in the new + // fashion, then you want JSON mode. Old-skool error + // patterns still match the raw compiler output. + if props.error_patterns.is_empty() { + args.extend(["--error-format", + "json", + "-Z", + "unstable-options"] + .iter() + .map(|s| s.to_string())); + } + } + + RunFail | + RunPass | + RunPassValgrind | + Pretty | + DebugInfoGdb | + DebugInfoLldb | + Codegen | + Rustdoc | + CodegenUnits => { + // do not use JSON output + } + } + args.extend_from_slice(&extras); if !props.no_prefer_dynamic { args.push("-C".to_owned()); diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs index 84d3ff2b238bb..43475f203d57c 100644 --- a/src/tools/tidy/src/bins.rs +++ b/src/tools/tidy/src/bins.rs @@ -35,7 +35,7 @@ pub fn check(path: &Path, bad: &mut bool) { return } - let metadata = t!(fs::metadata(&file)); + let metadata = t!(fs::metadata(&file), &file); if metadata.mode() & 0o111 != 0 { println!("binary checked into source: {}", file.display()); *bad = true; diff --git a/src/tools/tidy/src/cargo.rs b/src/tools/tidy/src/cargo.rs index a170ecfdce096..77dcf9c1bd81f 100644 --- a/src/tools/tidy/src/cargo.rs +++ b/src/tools/tidy/src/cargo.rs @@ -20,7 +20,7 @@ use std::fs::File; use std::path::Path; pub fn check(path: &Path, bad: &mut bool) { - for entry in t!(path.read_dir()).map(|e| t!(e)) { + for entry in t!(path.read_dir(), path).map(|e| t!(e)) { // Look for `Cargo.toml` with a sibling `src/lib.rs` or `lib.rs` if entry.file_name().to_str() == Some("Cargo.toml") { if path.join("src/lib.rs").is_file() { diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 85b9c345e199c..e9e2508aba9bd 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -19,6 +19,11 @@ use std::path::{PathBuf, Path}; use std::env; macro_rules! t { + ($e:expr, $p:expr) => (match $e { + Ok(e) => e, + Err(e) => panic!("{} failed on {} with {}", stringify!($e), ($p).display(), e), + }); + ($e:expr) => (match $e { Ok(e) => e, Err(e) => panic!("{} failed with {}", stringify!($e), e), @@ -63,7 +68,7 @@ fn filter_dirs(path: &Path) -> bool { fn walk(path: &Path, skip: &mut FnMut(&Path) -> bool, f: &mut FnMut(&Path)) { - for entry in t!(fs::read_dir(path)) { + for entry in t!(fs::read_dir(path), path) { let entry = t!(entry); let kind = t!(entry.file_type()); let path = entry.path(); diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 4c5f72c1e7960..61230b3b030e5 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -52,7 +52,7 @@ pub fn check(path: &Path, bad: &mut bool) { } contents.truncate(0); - t!(t!(File::open(file)).read_to_string(&mut contents)); + t!(t!(File::open(file), file).read_to_string(&mut contents)); let skip_cr = contents.contains("ignore-tidy-cr"); let skip_tab = contents.contains("ignore-tidy-tab"); let skip_length = contents.contains("ignore-tidy-linelength");