Auto merge of - nikomatsakis:compiletest-json, r=alexcrichton

port compiletest to use JSON output

This uncovered a lot of bugs in compiletest and also some shortcomings
of our existing JSON output. We had to add information to the JSON
output, such as suggested text and macro backtraces. We also had to fix
various bugs in the existing tests.

Joint work with @jonathandturner.

r? @alexcrichton
This commit is contained in:
bors 2016-04-22 21:47:54 -07:00
commit bb4b0d8943
71 changed files with 645 additions and 483 deletions
mk
src
libsyntax
test
tools

@ -128,7 +128,7 @@ DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \
test rustc_lint rustc_const_eval 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_rustdoc := rustdoc
TOOL_DEPS_rustc := rustc_driver TOOL_DEPS_rustc := rustc_driver
TOOL_DEPS_rustbook := std rustdoc TOOL_DEPS_rustbook := std rustdoc

@ -1394,6 +1394,56 @@ impl CodeMap {
pub fn count_lines(&self) -> usize { pub fn count_lines(&self) -> usize {
self.files.borrow().iter().fold(0, |a, f| a + f.count_lines()) self.files.borrow().iter().fold(0, |a, f| a + f.count_lines())
} }
pub fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace> {
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<Span>,
} }
// _____________________________________________________________________________ // _____________________________________________________________________________

@ -577,46 +577,17 @@ impl EmitterWriter {
fn print_macro_backtrace(&mut self, fn print_macro_backtrace(&mut self,
sp: Span) sp: Span)
-> io::Result<()> { -> io::Result<()> {
let mut last_span = codemap::DUMMY_SP; for trace in self.cm.macro_backtrace(sp) {
let mut span = sp; let mut diag_string =
format!("in this expansion of {}", trace.macro_decl_name);
loop { if let Some(def_site_span) = trace.def_site_span {
let span_name_span = self.cm.with_expn_info(span.expn_id, |expn_info| { diag_string.push_str(
expn_info.map(|ei| { &format!(" (defined in {})",
let (pre, post) = match ei.callee.format { self.cm.span_to_filename(def_site_span)));
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)?;
} }
last_span = span; let snippet = self.cm.span_to_string(sp);
print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?;
} }
Ok(()) Ok(())
} }
} }

@ -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 // file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT. // http://rust-lang.org/COPYRIGHT.
// //
@ -20,13 +20,14 @@
// FIXME spec the JSON output properly. // FIXME spec the JSON output properly.
use codemap::{self, Span, MultiSpan, CodeMap}; use codemap::{self, Span, MacroBacktrace, MultiSpan, CodeMap};
use diagnostics::registry::Registry; use diagnostics::registry::Registry;
use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion}; use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion};
use errors::emitter::Emitter; use errors::emitter::Emitter;
use std::rc::Rc; use std::rc::Rc;
use std::io::{self, Write}; use std::io::{self, Write};
use std::vec;
use rustc_serialize::json::as_json; use rustc_serialize::json::as_json;
@ -84,8 +85,12 @@ struct Diagnostic<'a> {
/// "error: internal compiler error", "error", "warning", "note", "help". /// "error: internal compiler error", "error", "warning", "note", "help".
level: &'static str, level: &'static str,
spans: Vec<DiagnosticSpan>, spans: Vec<DiagnosticSpan>,
/// Assocaited diagnostic messages. /// Associated diagnostic messages.
children: Vec<Diagnostic<'a>>, children: Vec<Diagnostic<'a>>,
/// The message as rustc would render it. Currently this is only
/// `Some` for "suggestions", but eventually it will include all
/// snippets.
rendered: Option<String>,
} }
#[derive(RustcEncodable)] #[derive(RustcEncodable)]
@ -101,16 +106,39 @@ struct DiagnosticSpan {
column_end: usize, column_end: usize,
/// Source text from the start of line_start to the end of line_end. /// Source text from the start of line_start to the end of line_end.
text: Vec<DiagnosticSpanLine>, text: Vec<DiagnosticSpanLine>,
/// 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<String>,
/// Macro invocations that created the code at this span, if any.
expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
} }
#[derive(RustcEncodable)] #[derive(RustcEncodable)]
struct DiagnosticSpanLine { struct DiagnosticSpanLine {
text: String, text: String,
/// 1-based, character offset in self.text. /// 1-based, character offset in self.text.
highlight_start: usize, highlight_start: usize,
highlight_end: 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<DiagnosticSpan>,
}
#[derive(RustcEncodable)] #[derive(RustcEncodable)]
struct DiagnosticCode { struct DiagnosticCode {
/// The code itself. /// The code itself.
@ -132,6 +160,7 @@ impl<'a> Diagnostic<'a> {
level: level.to_str(), level: level.to_str(),
spans: msp.map_or(vec![], |msp| DiagnosticSpan::from_multispan(msp, je)), spans: msp.map_or(vec![], |msp| DiagnosticSpan::from_multispan(msp, je)),
children: vec![], children: vec![],
rendered: None,
} }
} }
@ -146,6 +175,7 @@ impl<'a> Diagnostic<'a> {
level: level.to_str(), level: level.to_str(),
spans: DiagnosticSpan::from_render_span(span, je), spans: DiagnosticSpan::from_render_span(span, je),
children: vec![], children: vec![],
rendered: je.render(span),
} }
} }
@ -160,6 +190,7 @@ impl<'a> Diagnostic<'a> {
children: db.children.iter().map(|c| { children: db.children.iter().map(|c| {
Diagnostic::from_sub_diagnostic(c, je) Diagnostic::from_sub_diagnostic(c, je)
}).collect(), }).collect(),
rendered: None,
} }
} }
@ -173,37 +204,91 @@ impl<'a> Diagnostic<'a> {
.or_else(|| db.span.as_ref().map(|s| DiagnosticSpan::from_multispan(s, je))) .or_else(|| db.span.as_ref().map(|s| DiagnosticSpan::from_multispan(s, je)))
.unwrap_or(vec![]), .unwrap_or(vec![]),
children: vec![], children: vec![],
rendered: db.render_span.as_ref()
.and_then(|rsp| je.render(rsp)),
} }
} }
} }
impl DiagnosticSpan { 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<MacroBacktrace>,
je: &JsonEmitter)
-> DiagnosticSpan {
let start = je.cm.lookup_char_pos(span.lo);
let end = je.cm.lookup_char_pos(span.hi);
let backtrace_step =
backtrace.next()
.map(|bt| {
let call_site =
Self::from_span_and_backtrace(bt.call_site,
None,
backtrace,
je);
let def_site_span = bt.def_site_span.map(|sp| {
Self::from_span_and_backtrace(sp,
None,
vec![].into_iter(),
je)
});
Box::new(DiagnosticSpanMacroExpansion {
span: call_site,
macro_decl_name: bt.macro_decl_name,
def_site_span: def_site_span,
})
});
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<DiagnosticSpan> { fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
msp.spans.iter().map(|span| { msp.spans.iter().map(|&span| Self::from_span(span, None, je)).collect()
let start = je.cm.lookup_char_pos(span.lo); }
let end = je.cm.lookup_char_pos(span.hi);
DiagnosticSpan { fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter)
file_name: start.file.name.clone(), -> Vec<DiagnosticSpan> {
byte_start: span.lo.0, assert_eq!(suggestion.msp.spans.len(), suggestion.substitutes.len());
byte_end: span.hi.0, suggestion.msp.spans.iter()
line_start: start.line, .zip(&suggestion.substitutes)
line_end: end.line, .map(|(&span, suggestion)| {
column_start: start.col.0 + 1, DiagnosticSpan::from_span(span, Some(suggestion), je)
column_end: end.col.0 + 1, })
text: DiagnosticSpanLine::from_span(span, je), .collect()
}
}).collect()
} }
fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> { fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
match *rsp { match *rsp {
RenderSpan::FullSpan(ref msp) | RenderSpan::FileLine(ref msp) |
// FIXME(#30701) handle Suggestion properly RenderSpan::FullSpan(ref msp) => {
RenderSpan::Suggestion(CodeSuggestion { ref msp, .. }) => {
DiagnosticSpan::from_multispan(msp, je) DiagnosticSpan::from_multispan(msp, je)
} }
RenderSpan::Suggestion(ref suggestion) => {
DiagnosticSpan::from_suggestion(suggestion, je)
}
RenderSpan::EndSpan(ref msp) => { RenderSpan::EndSpan(ref msp) => {
msp.spans.iter().map(|span| { msp.spans.iter().map(|&span| {
let end = je.cm.lookup_char_pos(span.hi); let end = je.cm.lookup_char_pos(span.hi);
DiagnosticSpan { DiagnosticSpan {
file_name: end.file.name.clone(), file_name: end.file.name.clone(),
@ -214,37 +299,11 @@ impl DiagnosticSpan {
column_start: end.col.0 + 1, column_start: end.col.0 + 1,
column_end: end.col.0 + 1, column_end: end.col.0 + 1,
text: DiagnosticSpanLine::from_span_end(span, je), text: DiagnosticSpanLine::from_span_end(span, je),
suggested_replacement: None,
expansion: None,
} }
}).collect() }).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 /// Create a list of DiagnosticSpanLines from span - each line with any part
/// of `span` gets a DiagnosticSpanLine, with the highlight indicating the /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the
/// `span` within the line. /// `span` within the line.
fn from_span(span: &Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> { fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
let lines = get_lines_for_span!(span, je); je.cm.span_to_lines(span)
.map(|lines| {
let mut result = Vec::new(); let fm = &*lines.file;
let fm = &*lines.file; lines.lines
.iter()
for line in &lines.lines { .map(|line| {
result.push(DiagnosticSpanLine::line_from_filemap(fm, DiagnosticSpanLine::line_from_filemap(fm,
line.line_index, line.line_index,
line.start_col.0 + 1, line.start_col.0 + 1,
line.end_col.0 + 1)); line.end_col.0 + 1)
} })
.collect()
result })
.unwrap_or(vec![])
} }
/// Create a list of DiagnosticSpanLines from span - the result covers all /// 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`. /// of `span`, but the highlight is zero-length and at the end of `span`.
fn from_span_end(span: &Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> { fn from_span_end(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
let lines = get_lines_for_span!(span, je); je.cm.span_to_lines(span)
.map(|lines| {
let mut result = Vec::new(); let fm = &*lines.file;
let fm = &*lines.file; lines.lines.iter()
.enumerate()
for (i, line) in lines.lines.iter().enumerate() { .map(|(i, line)| {
// Invariant - CodeMap::span_to_lines will not return extra context // Invariant - CodeMap::span_to_lines
// lines - the last line returned is the last line of `span`. // will not return extra context lines
let highlight = if i == lines.lines.len() - 1 { // - the last line returned is the last
(line.end_col.0 + 1, line.end_col.0 + 1) // line of `span`.
} else { let highlight = if i == lines.lines.len() - 1 {
(0, 0) (line.end_col.0 + 1, line.end_col.0 + 1)
}; } else {
result.push(DiagnosticSpanLine::line_from_filemap(fm, (0, 0)
line.line_index, };
highlight.0, DiagnosticSpanLine::line_from_filemap(fm,
highlight.1)); line.line_index,
} highlight.0,
highlight.1)
result })
.collect()
})
.unwrap_or(vec![])
} }
} }
@ -322,3 +385,21 @@ impl DiagnosticCode {
}) })
} }
} }
impl JsonEmitter {
fn render(&self, render_span: &RenderSpan) -> Option<String> {
match *render_span {
RenderSpan::FileLine(_) |
RenderSpan::FullSpan(_) => {
None
}
RenderSpan::Suggestion(ref suggestion) => {
Some(suggestion.splice_lines(&self.cm))
}
RenderSpan::EndSpan(_) => {
None
}
}
}
}

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

@ -11,9 +11,5 @@
fn main() { fn main() {
loop { loop {
true //~ ERROR mismatched types true //~ ERROR mismatched types
//~| expected ()
//~| found bool
//~| expected ()
//~| found bool
} }
} }

@ -13,10 +13,6 @@ struct r;
impl Drop for r { impl Drop for r {
fn drop(&mut self) { fn drop(&mut self) {
true //~ ERROR mismatched types true //~ ERROR mismatched types
//~| expected ()
//~| found bool
//~| expected ()
//~| found bool
} }
} }

@ -11,6 +11,5 @@
fn main() { fn main() {
let u = 5 as bool; let u = 5 as bool;
//~^ ERROR cannot cast as `bool` //~^ ERROR cannot cast as `bool`
//~^^ HELP compare with zero instead //~| HELP compare with zero instead
//~^^^ HELP run `rustc --explain E0054` to see a detailed explanation
} }

@ -60,12 +60,10 @@ fn main()
//~^^ HELP through a usize first //~^^ HELP through a usize first
let _ = 3 as bool; let _ = 3 as bool;
//~^ ERROR cannot cast as `bool` //~^ ERROR cannot cast as `bool`
//~^^ HELP compare with zero //~| HELP compare with zero
//~^^^ HELP run `rustc --explain E0054` to see a detailed explanation
let _ = E::A as bool; let _ = E::A as bool;
//~^ ERROR cannot cast as `bool` //~^ ERROR cannot cast as `bool`
//~^^ HELP compare with zero //~| HELP compare with zero
//~^^^ HELP run `rustc --explain E0054` to see a detailed explanation
let _ = 0x61u32 as char; //~ ERROR only `u8` can be cast let _ = 0x61u32 as char; //~ ERROR only `u8` can be cast
let _ = false as f32; let _ = false as f32;
@ -92,9 +90,8 @@ fn main()
let _ = v as *const [u8]; //~ ERROR cannot cast let _ = v as *const [u8]; //~ ERROR cannot cast
let _ = fat_v as *const Foo; let _ = fat_v as *const Foo;
//~^ ERROR the trait bound `[u8]: std::marker::Sized` is not satisfied //~^ 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 `[u8]` does not have a constant size known at compile-time //~| NOTE required for the cast to the object type `Foo`
//~^^^^ NOTE required for the cast to the object type `Foo`
let _ = foo as *const str; //~ ERROR casting let _ = foo as *const str; //~ ERROR casting
let _ = foo as *mut str; //~ ERROR casting let _ = foo as *mut str; //~ ERROR casting
let _ = main 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 : *const str = "hello";
let _ = a as *const Foo; let _ = a as *const Foo;
//~^ ERROR the trait bound `str: std::marker::Sized` is not satisfied //~^ 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 `str` does not have a constant size known at compile-time //~| NOTE required for the cast to the object type `Foo`
//~^^^^ NOTE required for the cast to the object type `Foo`
// check no error cascade // check no error cascade
let _ = main.f as *const u32; //~ ERROR attempted access of field let _ = main.f as *const u32; //~ ERROR attempted access of field

@ -20,7 +20,7 @@ impl<T: MyTrait> !Send for TestType<T> {}
//~^ ERROR conflicting implementations of trait `std::marker::Send` //~^ ERROR conflicting implementations of trait `std::marker::Send`
unsafe impl<T:'static> Send for TestType<T> {} unsafe impl<T:'static> Send for TestType<T> {}
//~^ ERROR error: conflicting implementations of trait `std::marker::Send` //~^ ERROR conflicting implementations of trait `std::marker::Send`
impl !Send for TestType<i32> {} impl !Send for TestType<i32> {}

@ -9,13 +9,11 @@
// except according to those terms. // except according to those terms.
fn f() -> String { //~ ERROR E0269 fn f() -> String { //~ ERROR E0269
//~^ HELP detailed explanation
0u8; 0u8;
"bla".to_string(); //~ HELP consider removing this semicolon "bla".to_string(); //~ HELP consider removing this semicolon
} }
fn g() -> String { //~ ERROR E0269 fn g() -> String { //~ ERROR E0269
//~^ HELP detailed explanation
"this won't work".to_string(); "this won't work".to_string();
"removeme".to_string(); //~ HELP consider removing this semicolon "removeme".to_string(); //~ HELP consider removing this semicolon
} }

@ -37,11 +37,11 @@ fn main() {
// } // }
match e2 { match e2 {
Empty2(..) => () //~ ERROR `Empty2` does not name a tuple variant or a tuple struct Empty2(..) => () //~ ERROR `Empty2` does not name a tuple variant or a tuple struct
//~^ ERROR hard error //~^ WARNING hard error
} }
match xe2 { match xe2 {
XEmpty2(..) => () //~ ERROR `XEmpty2` does not name a tuple variant or a tuple struct XEmpty2(..) => () //~ ERROR `XEmpty2` does not name a tuple variant or a tuple struct
//~^ ERROR hard error //~^ WARNING hard error
} }
// Rejected by parser as yet // Rejected by parser as yet
// match e4 { // match e4 {
@ -53,11 +53,11 @@ fn main() {
// } // }
match e4 { match e4 {
E::Empty4(..) => () //~ ERROR `E::Empty4` does not name a tuple variant or a tuple struct E::Empty4(..) => () //~ ERROR `E::Empty4` does not name a tuple variant or a tuple struct
//~^ ERROR hard error //~^ WARNING hard error
} }
match xe4 { match xe4 {
XE::XEmpty4(..) => (), //~ ERROR `XE::XEmpty4` does not name a tuple variant or a tuple XE::XEmpty4(..) => (), //~ ERROR `XE::XEmpty4` does not name a tuple variant or a tuple
//~^ ERROR hard error //~^ WARNING hard error
_ => {}, _ => {},
} }
} }

@ -143,7 +143,7 @@ fn main() {
id_i64(a16); id_i64(a16);
//~^ ERROR mismatched types //~^ ERROR mismatched types
//~| expected `i64` //~| expected `i64`
//~| found i16 //~| found `i16`
id_i64(a32); id_i64(a32);
//~^ ERROR mismatched types //~^ ERROR mismatched types
//~| expected `i64` //~| expected `i64`

@ -9,7 +9,6 @@
// except according to those terms. // except according to those terms.
fn blah() -> i32 { //~ ERROR not all control paths return a value fn blah() -> i32 { //~ ERROR not all control paths return a value
//~^ HELP run `rustc --explain E0269` to see a detailed explanation
1 1
; //~ HELP consider removing this semicolon: ; //~ HELP consider removing this semicolon:

@ -36,5 +36,4 @@ fn check<'r, I: Iterator<Item=usize>, T: Itble<'r, usize, I>>(cont: &T) -> bool
fn main() { fn main() {
check((3, 5)); check((3, 5));
//~^ ERROR mismatched types //~^ ERROR mismatched types
//~| HELP run `rustc --explain E0308` to see a detailed explanation
} }

@ -11,7 +11,6 @@
// Regression test for #13428 // Regression test for #13428
fn foo() -> String { //~ ERROR not all control paths return a value fn foo() -> String { //~ ERROR not all control paths return a value
//~^ HELP run `rustc --explain E0269` to see a detailed explanation
format!("Hello {}", format!("Hello {}",
"world") "world")
// Put the trailing semicolon on its own line to test that the // 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 fn bar() -> String { //~ ERROR not all control paths return a value
//~^ HELP run `rustc --explain E0269` to see a detailed explanation
"foobar".to_string() "foobar".to_string()
; //~ HELP consider removing this semicolon ; //~ HELP consider removing this semicolon
} }

@ -17,7 +17,7 @@ fn main() {
//~^ ERROR mismatched types //~^ ERROR mismatched types
//~| expected `[_; 2]` //~| expected `[_; 2]`
//~| found `[_; 0]` //~| found `[_; 0]`
//~| expected array with a fixed size of 2 elements //~| expected an array with a fixed size of 2 elements
[a,_] => Some(a) [a,_] => Some(a)
}; };
} }

@ -19,7 +19,6 @@ struct List<'a, T: ListItem<'a>> {
//~^ ERROR the parameter type `T` may not live long enough //~^ ERROR the parameter type `T` may not live long enough
//~| HELP consider adding an explicit lifetime bound //~| HELP consider adding an explicit lifetime bound
//~| NOTE ...so that the reference type `&'a [T]` does not outlive the data it points at //~| 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> { impl<'a, T: ListItem<'a>> Collection for List<'a, T> {
fn len(&self) -> usize { fn len(&self) -> usize {

@ -13,10 +13,8 @@
type foo = fn(&u8, &u8) -> &u8; //~ ERROR missing lifetime specifier 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 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<F: Fn(&u8, &u8) -> &u8>(f: &F) {} //~ ERROR missing lifetime specifier fn bar<F: Fn(&u8, &u8) -> &u8>(f: &F) {} //~ ERROR missing lifetime specifier
//~^ HELP the signature does not say whether it is borrowed from argument 1 or argument 2 //~^ 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() {} fn main() {}

@ -10,11 +10,7 @@
fn foo(x: i32) { fn foo(x: i32) {
|y| x + y |y| x + y
//~^ ERROR: mismatched types: //~^ ERROR: mismatched types
//~| expected `()`,
//~| found closure
//~| (expected (),
//~| found closure) [E0308]
} }
fn main() { fn main() {

@ -55,7 +55,6 @@ impl Mul for Foo {
//~| HELP `mul1::Mul` //~| HELP `mul1::Mul`
//~| HELP `mul2::Mul` //~| HELP `mul2::Mul`
//~| HELP `std::ops::Mul` //~| HELP `std::ops::Mul`
//~| HELP run `rustc --explain E0405` to see a detailed explanation
//~| HELP you can import several candidates into scope (`use ...;`): //~| HELP you can import several candidates into scope (`use ...;`):
} }
@ -77,22 +76,19 @@ fn getMul() -> Mul {
//~| HELP `mul3::Mul` //~| HELP `mul3::Mul`
//~| HELP `mul4::Mul` //~| HELP `mul4::Mul`
//~| HELP and 2 other candidates //~| HELP and 2 other candidates
//~| HELP run `rustc --explain E0412` to see a detailed explanation
//~| HELP you can import several candidates into scope (`use ...;`): //~| HELP you can import several candidates into scope (`use ...;`):
} }
// Let's also test what happens if the trait doesn't exist: // Let's also test what happens if the trait doesn't exist:
impl ThisTraitReallyDoesntExistInAnyModuleReally for Foo { impl ThisTraitReallyDoesntExistInAnyModuleReally for Foo {
//~^ ERROR trait `ThisTraitReallyDoesntExistInAnyModuleReally` is not in scope //~^ 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: // Let's also test what happens if there's just one alternative:
impl Div for Foo { impl Div for Foo {
//~^ ERROR trait `Div` is not in scope //~^ ERROR trait `Div` is not in scope
//~| HELP `use std::ops::Div;` //~| HELP `use std::ops::Div;`
//~| HELP run `rustc --explain E0405` to see a detailed explanation
} }
fn main() { fn main() {

@ -28,4 +28,3 @@ struct Foo;
impl T for Foo { } impl T for Foo { }
//~^ ERROR trait `T` is not in scope //~^ ERROR trait `T` is not in scope
//~| HELP you can import it into scope: `use foo::bar::T;`. //~| HELP you can import it into scope: `use foo::bar::T;`.
//~| HELP run `rustc --explain E0405` to see a detailed explanation

@ -25,7 +25,6 @@ struct Foo;
impl OuterTrait for Foo {} impl OuterTrait for Foo {}
//~^ ERROR trait `OuterTrait` is not in scope //~^ ERROR trait `OuterTrait` is not in scope
//~| HELP you can import it into scope: `use issue_21221_3::outer::OuterTrait;`. //~| 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() { fn main() {
println!("Hello, world!"); println!("Hello, world!");
} }

@ -20,7 +20,6 @@ struct Foo;
impl T for Foo {} impl T for Foo {}
//~^ ERROR trait `T` is not in scope //~^ ERROR trait `T` is not in scope
//~| HELP you can import it into scope: `use issue_21221_4::T;`. //~| HELP you can import it into scope: `use issue_21221_4::T;`.
//~| HELP run `rustc --explain E0405` to see a detailed explanation
fn main() { fn main() {
println!("Hello, world!"); println!("Hello, world!");

@ -23,8 +23,6 @@ fn main() {
call_it(|| x.gen()); call_it(|| x.gen());
call_it(|| x.gen_mut()); //~ ERROR cannot borrow data mutably in a captured outer call_it(|| x.gen_mut()); //~ ERROR cannot borrow data mutably in a captured outer
//~^ 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 consider changing this closure to take self by mutable reference
//~^^^ HELP run `rustc --explain E0387` to see a detailed explanation
//~^^^^ HELP consider changing this closure to take self by mutable reference
}); });
} }

@ -36,5 +36,4 @@ fn main() {
//~| help: the following implementations were found: //~| help: the following implementations were found:
//~| help: <Bar as Foo<i32>> //~| help: <Bar as Foo<i32>>
//~| help: <Bar as Foo<u8>> //~| help: <Bar as Foo<u8>>
//~| help: run `rustc --explain E0277`
} }

@ -43,5 +43,4 @@ fn main() {
//~| help: <Bar as Foo<i32>> //~| help: <Bar as Foo<i32>>
//~| help: <Bar as Foo<u8>> //~| help: <Bar as Foo<u8>>
//~| help: and 2 others //~| help: and 2 others
//~| help: run `rustc --explain E0277`
} }

@ -14,7 +14,6 @@ fn closure_to_loc() {
//~^ ERROR mismatched types //~^ ERROR mismatched types
//~| NOTE no two closures, even if identical, have the same type //~| 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 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() { fn closure_from_match() {
@ -27,7 +26,6 @@ fn closure_from_match() {
//~^^^^^^ ERROR match arms have incompatible types //~^^^^^^ ERROR match arms have incompatible types
//~| NOTE no two closures, even if identical, have the same type //~| 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 consider boxing your closure and/or using it as a trait object
//~| HELP run `rustc --explain E0308` to see a detailed explanation
} }
fn main() { } fn main() { }

@ -11,10 +11,6 @@
fn main() { fn main() {
static foo: Fn() -> u32 = || -> u32 { static foo: Fn() -> u32 = || -> u32 {
//~^ ERROR: mismatched types: //~^ ERROR: mismatched types:
//~| expected `std::ops::Fn() -> u32 + 'static`,
//~| found closure
//~| (expected trait std::ops::Fn,
//~| found closure)
0 0
}; };
} }

@ -11,18 +11,15 @@
fn parse_type(iter: Box<Iterator<Item=&str>+'static>) -> &str { iter.next() } fn parse_type(iter: Box<Iterator<Item=&str>+'static>) -> &str { iter.next() }
//~^ ERROR missing lifetime specifier [E0106] //~^ ERROR missing lifetime specifier [E0106]
//~^^ HELP 2 elided lifetimes //~^^ HELP 2 elided lifetimes
//~^^^ HELP run `rustc --explain E0106` to see a detailed explanation
fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() }
//~^ ERROR missing lifetime specifier [E0106] //~^ ERROR missing lifetime specifier [E0106]
//~^^ HELP lifetime cannot be derived //~^^ 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!() } fn parse_type_3() -> &str { unimplemented!() }
//~^ ERROR missing lifetime specifier [E0106] //~^ ERROR missing lifetime specifier [E0106]
//~^^ HELP no value for it to be borrowed from //~^^ 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() {} fn main() {}

@ -15,5 +15,5 @@ use issue_30123_aux::*;
fn main() { fn main() {
let ug = Graph::<i32, i32>::new_undirected(); let ug = Graph::<i32, i32>::new_undirected();
//~^ ERR no associated item named `new_undirected` found for type //~^ ERROR no associated item named `new_undirected` found for type
} }

@ -18,10 +18,8 @@ fn is_empty<T>(s: Stack<T>) -> bool {
Nil => true, Nil => true,
//~^ WARN pattern binding `Nil` is named the same as one of the variants of the type `Stack` //~^ 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 consider making the path in the pattern qualified: `Stack::Nil`
//~| HELP run `rustc --explain E0170` to see a detailed explanation
_ => false _ => false
//~^ ERROR unreachable pattern //~^ ERROR unreachable pattern
//~| HELP run `rustc --explain E0001` to see a detailed explanation
} }
} }

@ -13,10 +13,6 @@ trait A {
|| self.b() || self.b()
//~^ ERROR no method named `b` found for type `&Self` in the current scope //~^ ERROR no method named `b` found for type `&Self` in the current scope
//~| ERROR mismatched types //~| ERROR mismatched types
//~| expected `()`
//~| found closure
//~| expected ()
//~| found closure
} }
} }
fn main() {} fn main() {}

@ -16,5 +16,4 @@ struct Monster {
fn main() { fn main() {
let _m = Monster(); //~ ERROR `Monster` is the name of a struct or let _m = Monster(); //~ ERROR `Monster` is the name of a struct or
//~^ HELP did you mean to write: `Monster { /* fields */ }`? //~^ HELP did you mean to write: `Monster { /* fields */ }`?
//~| HELP run `rustc --explain E0423` to see a detailed explanation
} }

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// ignore-test -- FIXME #33010
// This file was auto-generated using 'src/etc/generate-keyword-tests.py false' // This file was auto-generated using 'src/etc/generate-keyword-tests.py false'
fn main() { fn main() {

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// ignore-test -- FIXME #33010
// This file was auto-generated using 'src/etc/generate-keyword-tests.py true' // This file was auto-generated using 'src/etc/generate-keyword-tests.py true'
fn main() { fn main() {

@ -11,7 +11,6 @@
// Lifetime annotation needed because we have no arguments. // Lifetime annotation needed because we have no arguments.
fn f() -> &isize { //~ ERROR missing lifetime specifier fn f() -> &isize { //~ ERROR missing lifetime specifier
//~^ HELP there is no value for it to be borrowed from //~^ 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 //~| HELP consider giving it a 'static lifetime
panic!() panic!()
} }
@ -19,7 +18,6 @@ fn f() -> &isize { //~ ERROR missing lifetime specifier
// Lifetime annotation needed because we have two by-reference parameters. // Lifetime annotation needed because we have two by-reference parameters.
fn g(_x: &isize, _y: &isize) -> &isize { //~ ERROR missing lifetime specifier 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 the signature does not say whether it is borrowed from `_x` or `_y`
//~| HELP run `rustc --explain E0106` to see a detailed explanation
panic!() panic!()
} }
@ -31,13 +29,11 @@ struct Foo<'a> {
// and one on the reference. // and one on the reference.
fn h(_x: &Foo) -> &isize { //~ ERROR missing lifetime specifier 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 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!() panic!()
} }
fn i(_x: isize) -> &isize { //~ ERROR missing lifetime specifier fn i(_x: isize) -> &isize { //~ ERROR missing lifetime specifier
//~^ HELP this function's return type contains a borrowed value //~^ 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 //~| HELP consider giving it an explicit bounded or 'static lifetime
panic!() panic!()
} }

@ -27,5 +27,5 @@ fn bar() {
#[forbid(warnings)] #[forbid(warnings)]
fn baz() { fn baz() {
while true {} //~ ERROR: warnings while true {} //~ ERROR: infinite
} }

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![deny = "foo"] //~ ERR malformed lint attribute #![deny = "foo"] //~ ERROR malformed lint attribute
#![allow(bar = "baz")] //~ ERR malformed lint attribute #![allow(bar = "baz")] //~ ERROR malformed lint attribute
fn main() { } fn main() { }

@ -14,4 +14,4 @@
#[deny(raw_pointer_derive)] #[deny(raw_pointer_derive)]
#[allow(renamed_and_removed_lints)] #[allow(renamed_and_removed_lints)]
#[deny(unused_variables)] #[deny(unused_variables)]
fn main() { let unused = (); } //~ ERR unused fn main() { let unused = (); } //~ ERROR unused

@ -15,4 +15,4 @@
#[deny(raw_pointer_derive)] //~ WARN raw_pointer_derive has been removed #[deny(raw_pointer_derive)] //~ WARN raw_pointer_derive has been removed
#[deny(unused_variables)] #[deny(unused_variables)]
fn main() { let unused = (); } //~ ERR unused fn main() { let unused = (); } //~ ERROR unused

@ -14,4 +14,4 @@
#[deny(unknown_features)] #[deny(unknown_features)]
#[allow(renamed_and_removed_lints)] #[allow(renamed_and_removed_lints)]
#[deny(unused)] #[deny(unused)]
fn main() { let unused = (); } //~ ERR unused fn main() { let unused = (); } //~ ERROR unused

@ -10,4 +10,4 @@
#[deny(unknown_features)] //~ WARN lint unknown_features has been renamed to unused_features #[deny(unknown_features)] //~ WARN lint unknown_features has been renamed to unused_features
#[deny(unused)] #[deny(unused)]
fn main() { let unused = (); } //~ ERR unused fn main() { let unused = (); } //~ ERROR unused

@ -10,4 +10,4 @@
#![allow(not_a_real_lint)] //~ WARN unknown lint #![allow(not_a_real_lint)] //~ WARN unknown lint
#![deny(unused)] #![deny(unused)]
fn main() { let unused = (); } //~ ERR unused variable fn main() { let unused = (); } //~ ERROR unused variable

@ -12,19 +12,15 @@
macro_rules! test { () => { fn foo() -> i32 { 1; } } } macro_rules! test { () => { fn foo() -> i32 { 1; } } }
//~^ ERROR not all control paths return a value //~^ ERROR not all control paths return a value
//~^^ HELP consider removing this semicolon //~| HELP consider removing this semicolon
//~^^^ HELP run `rustc --explain E0269` to see a
fn no_return() -> i32 {} //~ ERROR not all control paths return a value 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 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 x * 2; //~ HELP consider removing this semicolon
} }
fn baz(x: u64) -> u32 { //~ ERROR not all control paths return a value 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; x * 2;
} }

@ -15,6 +15,8 @@ mod foo {
mod bar { mod bar {
use foo::Priv; use foo::Priv;
pub(super) fn f(_: Priv) {} pub(super) fn f(_: Priv) {}
pub(crate) fn f(_: Priv) {} //~ ERROR private pub(crate) fn g(_: Priv) {} //~ ERROR E0446
} }
} }
fn main() { }

@ -14,14 +14,12 @@ fn main() {
//~^ HELP use a `ref` binding as shown //~^ HELP use a `ref` binding as shown
//~| SUGGESTION let ref y = x; //~| SUGGESTION let ref y = x;
x; //~ ERROR use of moved value x; //~ ERROR use of moved value
//~^ HELP run `rustc --explain E0382` to see a detailed explanation
let x = vec![1]; let x = vec![1];
let mut y = x; let mut y = x;
//~^ HELP use a `ref` binding as shown //~^ HELP use a `ref` binding as shown
//~| SUGGESTION let ref mut y = x; //~| SUGGESTION let ref mut y = x;
x; //~ ERROR use of moved value x; //~ ERROR use of moved value
//~^ HELP run `rustc --explain E0382` to see a detailed explanation
let x = (Some(vec![1]), ()); let x = (Some(vec![1]), ());
@ -32,5 +30,4 @@ fn main() {
_ => {}, _ => {},
} }
x; //~ ERROR use of partially moved value x; //~ ERROR use of partially moved value
//~^ HELP run `rustc --explain E0382` to see a detailed explanation
} }

@ -28,7 +28,7 @@ fn foo<'z>() where &'z (): Sized {
//[verbose]~| found `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::bar::<ReStatic, char>}` //[verbose]~| found `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::bar::<ReStatic, char>}`
//[normal]~^^^^ ERROR mismatched types //[normal]~^^^^ ERROR mismatched types
//[normal]~| expected `()` //[normal]~| expected `()`
//[normal]~| found `fn() {<i8 as Foo<'static, 'static, u8>>::bar::<'static, char>}` //[normal]~| found `fn() {<i8 as Foo<'static, 'static, u8>>::bar::<'static, char>}`
let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>; let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>;

@ -27,54 +27,46 @@ fn h1() -> i32 {
a.I a.I
//~^ ERROR E0425 //~^ ERROR E0425
//~| HELP To reference an item from the `a` module, use `a::I` //~| 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 { fn h2() -> i32 {
a.g() a.g()
//~^ ERROR E0425 //~^ ERROR E0425
//~| HELP To call a function from the `a` module, use `a::g(..)` //~| 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 { fn h3() -> i32 {
a.b.J a.b.J
//~^ ERROR E0425 //~^ ERROR E0425
//~| HELP To reference an item from the `a` module, use `a::b` //~| 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 { fn h4() -> i32 {
a::b.J a::b.J
//~^ ERROR E0425 //~^ ERROR E0425
//~| HELP To reference an item from the `a::b` module, use `a::b::J` //~| 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 { fn h5() -> i32 {
a.b.f() a.b.f()
//~^ ERROR E0425 //~^ ERROR E0425
//~| HELP To reference an item from the `a` module, use `a::b` //~| 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 { fn h6() -> i32 {
a::b.f() a::b.f()
//~^ ERROR E0425 //~^ ERROR E0425
//~| HELP To call a function from the `a::b` module, use `a::b::f(..)` //~| 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() { fn h7() {
a::b a::b
//~^ ERROR E0425 //~^ ERROR E0425
//~| HELP Module `a::b` cannot be the value of an expression //~| HELP Module `a::b` cannot be the value of an expression
//~| HELP run `rustc --explain E0425` to see a detailed explanation
} }
fn h8() -> i32 { fn h8() -> i32 {
a::b() a::b()
//~^ ERROR E0425 //~^ ERROR E0425
//~| HELP No function corresponds to `a::b(..)` //~| HELP No function corresponds to `a::b(..)`
//~| HELP run `rustc --explain E0425` to see a detailed explanation
} }

@ -74,7 +74,7 @@ trait Trait {
impl Trait for usize { impl Trait for usize {
fn method<G: Getter<usize>>(&self) {} fn method<G: Getter<usize>>(&self) {}
//~^ G : Getter<usize>` appears on the impl method but not on the corresponding trait method //~^ ERROR `G: Getter<usize>` appears on the impl method
} }
fn main() {} fn main() {}

@ -13,10 +13,8 @@ fn main() {
//~^ ERROR expected a path //~^ ERROR expected a path
//~| HELP try adding parentheses //~| HELP try adding parentheses
//~| SUGGESTION let _: &(Copy + 'static); //~| SUGGESTION let _: &(Copy + 'static);
//~| HELP run `rustc --explain E0178` to see a detailed explanation
let _: &'static Copy + 'static; let _: &'static Copy + 'static;
//~^ ERROR expected a path //~^ ERROR expected a path
//~| HELP try adding parentheses //~| HELP try adding parentheses
//~| SUGGESTION let _: &'static (Copy + 'static); //~| SUGGESTION let _: &'static (Copy + 'static);
//~| HELP run `rustc --explain E0178` to see a detailed explanation
} }

@ -16,13 +16,11 @@ fn check<T: Iterator, U: ?Sized>() {
// suggest a where-clause, if needed // suggest a where-clause, if needed
mem::size_of::<U>(); mem::size_of::<U>();
//~^ ERROR `U: std::marker::Sized` is not satisfied //~^ ERROR `U: std::marker::Sized` is not satisfied
//~| HELP E0277
//~| HELP consider adding a `where U: std::marker::Sized` bound //~| HELP consider adding a `where U: std::marker::Sized` bound
//~| NOTE required by `std::mem::size_of` //~| NOTE required by `std::mem::size_of`
mem::size_of::<Misc<U>>(); mem::size_of::<Misc<U>>();
//~^ ERROR `U: std::marker::Sized` is not satisfied //~^ ERROR `U: std::marker::Sized` is not satisfied
//~| HELP E0277
//~| HELP consider adding a `where U: std::marker::Sized` bound //~| HELP consider adding a `where U: std::marker::Sized` bound
//~| NOTE required because it appears within the type `Misc<U>` //~| NOTE required because it appears within the type `Misc<U>`
//~| NOTE required by `std::mem::size_of` //~| NOTE required by `std::mem::size_of`
@ -31,13 +29,11 @@ fn check<T: Iterator, U: ?Sized>() {
<u64 as From<T>>::from; <u64 as From<T>>::from;
//~^ ERROR `u64: std::convert::From<T>` is not satisfied //~^ ERROR `u64: std::convert::From<T>` is not satisfied
//~| HELP E0277
//~| HELP consider adding a `where u64: std::convert::From<T>` bound //~| HELP consider adding a `where u64: std::convert::From<T>` bound
//~| NOTE required by `std::convert::From::from` //~| NOTE required by `std::convert::From::from`
<u64 as From<<T as Iterator>::Item>>::from; <u64 as From<<T as Iterator>::Item>>::from;
//~^ ERROR `u64: std::convert::From<<T as std::iter::Iterator>::Item>` is not satisfied //~^ ERROR `u64: std::convert::From<<T as std::iter::Iterator>::Item>` is not satisfied
//~| HELP E0277
//~| HELP consider adding a `where u64: //~| HELP consider adding a `where u64:
//~| NOTE required by `std::convert::From::from` //~| NOTE required by `std::convert::From::from`
@ -45,20 +41,17 @@ fn check<T: Iterator, U: ?Sized>() {
<Misc<_> as From<T>>::from; <Misc<_> as From<T>>::from;
//~^ ERROR `Misc<_>: std::convert::From<T>` is not satisfied //~^ ERROR `Misc<_>: std::convert::From<T>` is not satisfied
//~| HELP E0277
//~| NOTE required by `std::convert::From::from` //~| NOTE required by `std::convert::From::from`
// ... and also not if the error is not related to the type // ... and also not if the error is not related to the type
mem::size_of::<[T]>(); mem::size_of::<[T]>();
//~^ ERROR `[T]: std::marker::Sized` is not satisfied //~^ ERROR `[T]: std::marker::Sized` is not satisfied
//~| HELP E0277
//~| NOTE `[T]` does not have a constant size //~| NOTE `[T]` does not have a constant size
//~| NOTE required by `std::mem::size_of` //~| NOTE required by `std::mem::size_of`
mem::size_of::<[&U]>(); mem::size_of::<[&U]>();
//~^ ERROR `[&U]: std::marker::Sized` is not satisfied //~^ ERROR `[&U]: std::marker::Sized` is not satisfied
//~| HELP E0277
//~| NOTE `[&U]` does not have a constant size //~| NOTE `[&U]` does not have a constant size
//~| NOTE required by `std::mem::size_of` //~| NOTE required by `std::mem::size_of`
} }

@ -15,15 +15,15 @@ use std::mem;
unsafe fn foo() -> (isize, *const (), Option<fn()>) { unsafe fn foo() -> (isize, *const (), Option<fn()>) {
let i = mem::transmute(bar); let i = mem::transmute(bar);
//~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting //~^ 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); let p = mem::transmute(foo);
//~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting //~^ 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); let of = mem::transmute(main);
//~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting //~^ 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) (i, p, of)
} }
@ -31,11 +31,11 @@ unsafe fn foo() -> (isize, *const (), Option<fn()>) {
unsafe fn bar() { unsafe fn bar() {
mem::transmute::<_, *mut ()>(foo); mem::transmute::<_, *mut ()>(foo);
//~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting //~^ 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); mem::transmute::<_, fn()>(bar);
//~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting //~^ 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. // No error if a coercion would otherwise occur.
mem::transmute::<fn(), usize>(main); mem::transmute::<fn(), usize>(main);

@ -24,10 +24,8 @@ fn main() {
{ {
extern crate crate_a1 as a; extern crate crate_a1 as a;
a::try_foo(foo2); //~ ERROR mismatched types 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 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`
} }
} }

@ -16,18 +16,15 @@
struct SomeStruct<A> { x: u32 } struct SomeStruct<A> { x: u32 }
//~^ ERROR parameter `A` is never used //~^ ERROR parameter `A` is never used
//~| HELP PhantomData //~| HELP PhantomData
//~| HELP run `rustc --explain E0392` to see a detailed explanation
enum SomeEnum<A> { Nothing } enum SomeEnum<A> { Nothing }
//~^ ERROR parameter `A` is never used //~^ ERROR parameter `A` is never used
//~| HELP PhantomData //~| HELP PhantomData
//~| HELP run `rustc --explain E0392` to see a detailed explanation
// Here T might *appear* used, but in fact it isn't. // Here T might *appear* used, but in fact it isn't.
enum ListCell<T> { enum ListCell<T> {
//~^ ERROR parameter `T` is never used //~^ ERROR parameter `T` is never used
//~| HELP PhantomData //~| HELP PhantomData
//~| HELP run `rustc --explain E0392` to see a detailed explanation
Cons(Box<ListCell<T>>), Cons(Box<ListCell<T>>),
Nil Nil
} }

@ -10,4 +10,4 @@
// compile-flags: -Z parse-only // compile-flags: -Z parse-only
# //~ ERROR 13:1: 13:2 error: expected `[`, found `<eof>` # //~ ERROR 13:1: 13:2: expected `[`, found `<eof>`

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
fn main() {
let do = "bar"; //~ error: ident
}

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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
}

@ -6,5 +6,4 @@ all:
cp foo.rs $(TMPDIR) cp foo.rs $(TMPDIR)
cd $(TMPDIR) cd $(TMPDIR)
-$(RUSTC) -Z unstable-options --error-format=json foo.rs 2>$(LOG) -$(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) diff foo.json $(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":" <u8 as std::ops::Add>","code":null,"level":"help",' $(LOG)

@ -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<T, E>`:\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<std macros>: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<T, E>`, 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<i32>` 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<T: Foo>(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<T: Foo>(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<T>(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<T: fmt::Debug>(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":" <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":" <&'a u8 as std::ops::Add<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},{"message":" <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},{"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}

@ -4,6 +4,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serialize 0.0.0",
] ]
[[package]] [[package]]
@ -28,6 +29,10 @@ name = "libc"
version = "0.2.9" version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.0.0"
[[package]] [[package]]
name = "log" name = "log"
version = "0.3.6" version = "0.3.6"
@ -63,6 +68,13 @@ name = "regex-syntax"
version = "0.3.1" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serialize"
version = "0.0.0"
dependencies = [
"log 0.0.0",
]
[[package]] [[package]]
name = "utf8-ranges" name = "utf8-ranges"
version = "0.1.3" version = "0.1.3"

@ -13,3 +13,4 @@ opt-level = 2
[dependencies] [dependencies]
log = "0.3" log = "0.3"
env_logger = "0.3" env_logger = "0.3"
serialize = { path = "../../libserialize" }

@ -28,7 +28,9 @@ pub enum ErrorKind {
impl FromStr for ErrorKind { impl FromStr for ErrorKind {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
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), "HELP" => Ok(ErrorKind::Help),
"ERROR" => Ok(ErrorKind::Error), "ERROR" => Ok(ErrorKind::Error),
"NOTE" => Ok(ErrorKind::Note), "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, pub line_num: usize,
/// What kind of message we expect (e.g. warning, error, suggestion). /// What kind of message we expect (e.g. warning, error, suggestion).
/// `None` if not specified or unknown message kind. /// `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 /// If cfg is not None (i.e., in an incremental test), then we look
/// for `//[X]~` instead, where `X` is the current `cfg`. /// for `//[X]~` instead, where `X` is the current `cfg`.
pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<ExpectedError> { pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
let rdr = BufReader::new(File::open(testfile).unwrap()); let rdr = BufReader::new(File::open(testfile).unwrap());
// `last_nonfollow_error` tracks the most recently seen // `last_nonfollow_error` tracks the most recently seen
@ -113,7 +116,7 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
line_num: usize, line_num: usize,
line: &str, line: &str,
tag: &str) tag: &str)
-> Option<(WhichLine, ExpectedError)> { -> Option<(WhichLine, Error)> {
let start = match line.find(tag) { Some(i) => i, None => return None }; let start = match line.find(tag) { Some(i) => i, None => return None };
let (follow, adjusts) = if line[start + tag.len()..].chars().next().unwrap() == '|' { let (follow, adjusts) = if line[start + tag.len()..].chars().next().unwrap() == '|' {
(true, 0) (true, 0)
@ -121,15 +124,30 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
(false, line[start + tag.len()..].chars().take_while(|c| *c == '^').count()) (false, line[start + tag.len()..].chars().take_while(|c| *c == '^').count())
}; };
let kind_start = start + tag.len() + adjusts + (follow as usize); let kind_start = start + tag.len() + adjusts + (follow as usize);
let kind = line[kind_start..].split_whitespace() let (kind, msg);
.next() match
.expect("Encountered unexpected empty comment") line[kind_start..].split_whitespace()
.parse::<ErrorKind>() .next()
.ok(); .expect("Encountered unexpected empty comment")
let letters = line[kind_start..].chars(); .parse::<ErrorKind>()
let msg = letters.skip_while(|c| c.is_whitespace()) {
.skip_while(|c| !c.is_whitespace()) Ok(k) => {
.collect::<String>().trim().to_owned(); // 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::<String>();
}
Err(_) => {
// Otherwise we found `//~ foo`:
kind = None;
let letters = line[kind_start..].chars();
msg = letters.skip_while(|c| c.is_whitespace())
.collect::<String>();
}
}
let msg = msg.trim().to_owned();
let (which, line_num) = if follow { let (which, line_num) = if follow {
assert!(adjusts == 0, "use either //~| or //~^, not both."); assert!(adjusts == 0, "use either //~| or //~^, not both.");
@ -145,7 +163,7 @@ fn parse_expected(last_nonfollow_error: Option<usize>,
debug!("line={} tag={:?} which={:?} kind={:?} msg={:?}", debug!("line={} tag={:?} which={:?} kind={:?} msg={:?}",
line_num, tag, which, kind, msg); line_num, tag, which, kind, msg);
Some((which, ExpectedError { line_num: line_num, Some((which, Error { line_num: line_num,
kind: kind, kind: kind,
msg: msg, })) msg: msg, }))
} }

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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<DiagnosticCode>,
level: String,
spans: Vec<DiagnosticSpan>,
children: Vec<Diagnostic>,
rendered: Option<String>,
}
#[derive(RustcEncodable, RustcDecodable, Clone)]
struct DiagnosticSpan {
file_name: String,
line_start: usize,
line_end: usize,
column_start: usize,
column_end: usize,
expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
}
#[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<String>,
}
pub fn parse_output(file_name: &str, output: &str) -> Vec<Error> {
output.lines()
.flat_map(|line| parse_line(file_name, line))
.collect()
}
fn parse_line(file_name: &str, line: &str) -> Vec<Error> {
// 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::<Diagnostic>(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<Error>,
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<Error>,
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);
}
}

@ -21,6 +21,7 @@
extern crate libc; extern crate libc;
extern crate test; extern crate test;
extern crate getopts; extern crate getopts;
extern crate serialize as rustc_serialize;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
@ -40,6 +41,7 @@ use util::logv;
pub mod procsrv; pub mod procsrv;
pub mod util; pub mod util;
mod json;
pub mod header; pub mod header;
pub mod runtest; pub mod runtest;
pub mod common; pub mod common;

@ -12,7 +12,8 @@ use common::Config;
use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind}; use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits}; use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits};
use common::{Incremental}; use common::{Incremental};
use errors::{self, ErrorKind}; use errors::{self, ErrorKind, Error};
use json;
use header::TestProps; use header::TestProps;
use header; use header;
use procsrv; use procsrv;
@ -26,7 +27,7 @@ use std::fs::{self, File};
use std::io::BufReader; use std::io::BufReader;
use std::io::prelude::*; use std::io::prelude::*;
use std::net::TcpStream; use std::net::TcpStream;
use std::path::{Path, PathBuf, Component}; use std::path::{Path, PathBuf};
use std::process::{Command, Output, ExitStatus}; use std::process::{Command, Output, ExitStatus};
pub fn run(config: Config, testpaths: &TestPaths) { pub fn run(config: Config, testpaths: &TestPaths) {
@ -944,7 +945,7 @@ fn check_error_patterns(revision: Option<&str>,
testpaths.file.display())); testpaths.file.display()));
} }
let mut next_err_idx = 0; 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; let mut done = false;
for line in output_to_check.lines() { for line in output_to_check.lines() {
if line.contains(next_err_pat) { if line.contains(next_err_pat) {
@ -955,7 +956,7 @@ fn check_error_patterns(revision: Option<&str>,
done = true; done = true;
break; break;
} }
next_err_pat = &props.error_patterns[next_err_idx]; next_err_pat = props.error_patterns[next_err_idx].trim();
} }
} }
if done { return; } if done { return; }
@ -998,208 +999,110 @@ fn check_forbid_output(revision: Option<&str>,
} }
fn check_expected_errors(revision: Option<&str>, fn check_expected_errors(revision: Option<&str>,
expected_errors: Vec<errors::ExpectedError>, expected_errors: Vec<errors::Error>,
testpaths: &TestPaths, testpaths: &TestPaths,
proc_res: &ProcRes) { 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() { if proc_res.status.success() {
fatal_proc_rec(revision, "process did not return an error status", proc_res); fatal_proc_rec(revision, "process did not return an error status", proc_res);
} }
let prefixes = expected_errors.iter().map(|ee| { let file_name =
let expected = format!("{}:{}:", testpaths.file.display(), ee.line_num); format!("{}", testpaths.file.display())
// On windows just translate all '\' path separators to '/' .replace(r"\", "/"); // on windows, translate all '\' path separators to '/'
expected.replace(r"\", "/")
}).collect::<Vec<String>>();
// If the testcase being checked contains at least one expected "help" // If the testcase being checked contains at least one expected "help"
// message, then we'll ensure that all "help" messages are expected. // message, then we'll ensure that all "help" messages are expected.
// Otherwise, all "help" messages reported by the compiler will be ignored. // Otherwise, all "help" messages reported by the compiler will be ignored.
// This logic also applies to "note" messages. // This logic also applies to "note" messages.
let (expect_help, expect_note) = let expect_help = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Help));
expected_errors.iter() let expect_note = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Note));
.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, // Parse the JSON output from the compiler and extract out the messages.
// which look like: let actual_errors = json::parse_output(&file_name, &proc_res.stderr);
// 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 mut unexpected = 0; let mut unexpected = 0;
let mut not_found = 0; let mut not_found = 0;
for line in proc_res.stderr.lines() { let mut found = vec![false; expected_errors.len()];
let mut was_expected = false; for actual_error in &actual_errors {
let mut prev = 0; let opt_index =
for (i, ee) in expected_errors.iter().enumerate() { expected_errors
if !found_flags[i] { .iter()
debug!("prefix={} ee.kind={:?} ee.msg={} line={}", .enumerate()
prefixes[i], .position(|(index, expected_error)| {
ee.kind, !found[index] &&
ee.msg, actual_error.line_num == expected_error.line_num &&
line); (expected_error.kind.is_none() ||
// Suggestions have no line number in their output, so take on the line number of actual_error.kind == expected_error.kind) &&
// the previous expected error actual_error.msg.contains(&expected_error.msg)
if ee.kind == Some(ErrorKind::Suggestion) { });
assert!(expected_errors[prev].kind == Some(ErrorKind::Help),
"SUGGESTIONs must be preceded by a HELP"); match opt_index {
if line.contains(&ee.msg) { Some(index) => {
found_flags[i] = true; // found a match, everybody is happy
was_expected = true; assert!(!found[index]);
break; found[index] = true;
} }
}
if None => {
(prefix_matches(line, &prefixes[i]) || continuation(line)) && if is_unexpected_compiler_message(actual_error,
(ee.kind.is_none() || line.contains(&ee.kind.as_ref().unwrap().to_string())) && expect_help,
line.contains(&ee.msg) expect_note) {
{ error(revision,
found_flags[i] = true; &format!("{}:{}: unexpected {:?}: '{}'",
was_expected = true; file_name,
break; actual_error.line_num,
actual_error.kind.as_ref()
.map_or(String::from("message"),
|k| k.to_string()),
actual_error.msg));
unexpected += 1;
} }
} }
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;
} }
} }
for (i, &flag) in found_flags.iter().enumerate() { // anything not yet found is a problem
if !flag { for (index, expected_error) in expected_errors.iter().enumerate() {
let ee = &expected_errors[i]; if !found[index] {
error(revision, &format!("expected {} on line {} not found: {}", error(revision,
ee.kind.as_ref() &format!("{}:{}: expected {} not found: {}",
.map_or("message".into(), file_name,
|k| k.to_string()), expected_error.line_num,
ee.line_num, ee.msg)); expected_error.kind.as_ref()
.map_or("message".into(),
|k| k.to_string()),
expected_error.msg));
not_found += 1; not_found += 1;
} }
} }
if unexpected > 0 || not_found > 0 { if unexpected > 0 || not_found > 0 {
fatal_proc_rec( error(revision,
revision, &format!("{} unexpected errors found, {} expected errors not found",
&format!("{} unexpected errors found, {} expected errors not found", unexpected, not_found));
unexpected, not_found), print!("status: {}\ncommand: {}\n",
proc_res); proc_res.status, proc_res.cmdline);
} println!("actual errors (from JSON output): {:#?}\n", actual_errors);
println!("expected errors (from test file): {:#?}\n", expected_errors);
fn prefix_matches(line: &str, prefix: &str) -> bool { panic!();
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 { /// Returns true if we should report an error about `actual_error`,
let mut c = Path::new(line).components(); /// which did not match any of the expected error. We always require
let line = match c.next() { /// errors/warnings to be explicitly listed, but only require
Some(Component::Prefix(_)) => c.as_path().to_str().unwrap(), /// helps/notes if there are explicit helps/notes given.
_ => line, fn is_unexpected_compiler_message(actual_error: &Error,
}; expect_help: bool,
expect_note: bool)
let mut i = 0; -> bool {
return scan_until_char(line, ':', &mut i) && match actual_error.kind {
scan_char(line, ':', &mut i) && Some(ErrorKind::Help) => expect_help,
scan_integer(line, &mut i) && Some(ErrorKind::Note) => expect_note,
scan_char(line, ':', &mut i) && Some(ErrorKind::Error) => true,
scan_integer(line, &mut i) && Some(ErrorKind::Warning) => true,
scan_char(line, ':', &mut i) && Some(ErrorKind::Suggestion) => false,
scan_char(line, ' ', &mut i) && None => false
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;
}
*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;
}
}
*idx = haystack_i;
return true;
} }
struct ProcArgs { struct ProcArgs {
@ -1444,6 +1347,37 @@ fn make_compile_args<F>(config: &Config,
"-L".to_owned(), "-L".to_owned(),
config.build_base.to_str().unwrap().to_owned(), config.build_base.to_str().unwrap().to_owned(),
format!("--target={}", target)); 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); args.extend_from_slice(&extras);
if !props.no_prefer_dynamic { if !props.no_prefer_dynamic {
args.push("-C".to_owned()); args.push("-C".to_owned());

@ -35,7 +35,7 @@ pub fn check(path: &Path, bad: &mut bool) {
return return
} }
let metadata = t!(fs::metadata(&file)); let metadata = t!(fs::metadata(&file), &file);
if metadata.mode() & 0o111 != 0 { if metadata.mode() & 0o111 != 0 {
println!("binary checked into source: {}", file.display()); println!("binary checked into source: {}", file.display());
*bad = true; *bad = true;

@ -20,7 +20,7 @@ use std::fs::File;
use std::path::Path; use std::path::Path;
pub fn check(path: &Path, bad: &mut bool) { 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` // Look for `Cargo.toml` with a sibling `src/lib.rs` or `lib.rs`
if entry.file_name().to_str() == Some("Cargo.toml") { if entry.file_name().to_str() == Some("Cargo.toml") {
if path.join("src/lib.rs").is_file() { if path.join("src/lib.rs").is_file() {

@ -19,6 +19,11 @@ use std::path::{PathBuf, Path};
use std::env; use std::env;
macro_rules! t { 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 { ($e:expr) => (match $e {
Ok(e) => e, Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($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)) { 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 entry = t!(entry);
let kind = t!(entry.file_type()); let kind = t!(entry.file_type());
let path = entry.path(); let path = entry.path();

@ -52,7 +52,7 @@ pub fn check(path: &Path, bad: &mut bool) {
} }
contents.truncate(0); 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_cr = contents.contains("ignore-tidy-cr");
let skip_tab = contents.contains("ignore-tidy-tab"); let skip_tab = contents.contains("ignore-tidy-tab");
let skip_length = contents.contains("ignore-tidy-linelength"); let skip_length = contents.contains("ignore-tidy-linelength");