From 01d2b4ab6bdb33e8678c43612b81dbbbad32cc93 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 15 Apr 2016 21:23:50 -0400 Subject: [PATCH] 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 jntrnr. --- mk/crates.mk | 2 +- src/compiletest/json.rs | 185 +++++++++++ src/libsyntax/codemap.rs | 50 +++ src/libsyntax/errors/emitter.rs | 47 +-- src/libsyntax/errors/json.rs | 251 ++++++++++----- .../compile-fail/associated-types-path-2.rs | 4 +- .../block-must-not-have-result-do.rs | 4 - .../block-must-not-have-result-res.rs | 4 - src/test/compile-fail/cast-as-bool.rs | 3 +- src/test/compile-fail/cast-rfc0401.rs | 16 +- ...herence-conflicting-negative-trait-impl.rs | 2 +- .../consider-removing-last-semi.rs | 2 - .../compile-fail/empty-struct-unit-pat.rs | 8 +- .../integer-literal-suffix-inference.rs | 2 +- src/test/compile-fail/issue-11714.rs | 1 - src/test/compile-fail/issue-13058.rs | 1 - src/test/compile-fail/issue-13428.rs | 2 - src/test/compile-fail/issue-13482.rs | 2 +- src/test/compile-fail/issue-16747.rs | 1 - src/test/compile-fail/issue-19707.rs | 2 - src/test/compile-fail/issue-20862.rs | 6 +- src/test/compile-fail/issue-21221-1.rs | 6 +- src/test/compile-fail/issue-21221-2.rs | 1 - src/test/compile-fail/issue-21221-3.rs | 1 - src/test/compile-fail/issue-21221-4.rs | 1 - src/test/compile-fail/issue-21600.rs | 4 +- ...issue-21659-show-relevant-trait-impls-1.rs | 1 - ...issue-21659-show-relevant-trait-impls-2.rs | 1 - src/test/compile-fail/issue-24036.rs | 2 - src/test/compile-fail/issue-24446.rs | 4 - src/test/compile-fail/issue-26638.rs | 7 +- src/test/compile-fail/issue-30123.rs | 2 +- src/test/compile-fail/issue-30302.rs | 2 - .../{symbol-names => }/issue-32709.rs | 0 src/test/compile-fail/issue-3563.rs | 4 - src/test/compile-fail/issue-6702.rs | 1 - .../keyword-false-as-identifier.rs | 2 + .../keyword-true-as-identifier.rs | 2 + ...-return-type-requires-explicit-lifetime.rs | 4 - src/test/compile-fail/lint-change-warnings.rs | 2 +- src/test/compile-fail/lint-malformed.rs | 4 +- src/test/compile-fail/lint-removed-allow.rs | 2 +- src/test/compile-fail/lint-removed.rs | 2 +- src/test/compile-fail/lint-renamed-allow.rs | 2 +- src/test/compile-fail/lint-renamed.rs | 2 +- src/test/compile-fail/lint-unknown-lint.rs | 2 +- .../liveness-return-last-stmt-semi.rs | 6 +- src/test/compile-fail/ref-suggestion.rs | 3 - src/test/compile-fail/substs-ppaux.rs | 2 +- .../suggest-path-instead-of-mod-dot-item.rs | 8 - .../trait-bounds-impl-comparison-1.rs | 2 +- ...ect-reference-without-parens-suggestion.rs | 2 - .../trait-suggest-where-clause.rs | 7 - .../type-mismatch-same-crate-name.rs | 6 +- .../variance-unused-type-param.rs | 3 - src/test/parse-fail/column-offset-1-based.rs | 2 +- .../parse-fail/keyword-do-as-identifier.rs | 15 - .../parse-fail/keyword-priv-as-identifier.rs | 17 - src/test/run-make/json-errors/Makefile | 3 +- src/test/run-make/json-errors/foo.json | 4 + src/tools/compiletest/src/errors.rs | 50 ++- src/tools/compiletest/src/main.rs | 2 + src/tools/compiletest/src/runtest.rs | 290 +++++++----------- 63 files changed, 605 insertions(+), 473 deletions(-) create mode 100644 src/compiletest/json.rs rename src/test/compile-fail/{symbol-names => }/issue-32709.rs (100%) delete mode 100644 src/test/parse-fail/keyword-do-as-identifier.rs delete mode 100644 src/test/parse-fail/keyword-priv-as-identifier.rs create mode 100644 src/test/run-make/json-errors/foo.json diff --git a/mk/crates.mk b/mk/crates.mk index c55a6e791b6..02b95f5b1a9 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/compiletest/json.rs b/src/compiletest/json.rs new file mode 100644 index 00000000000..69274689e5c --- /dev/null +++ b/src/compiletest/json.rs @@ -0,0 +1,185 @@ +// 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; + +// 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) => { + println!("failed to decode compiler output as json: `{}`", error); + panic!("failed to decode compiler output as json"); + } + } + } 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| span.file_name == file_name); + 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 expansion.span.file_name == 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/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index f771ee95bd1..35aa827782d 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 61fdc8453d8..0b5234769b2 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 f369582bc5c..5a195e9f078 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,93 @@ 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 +301,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 +326,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 +387,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 0c077e37e43..68fba56427c 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 30039a1c54c..2a6c71dbe39 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 6161660ddf7..8728685fc8b 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 4764ae380ff..af42d5c275c 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 dcd49e34bb2..b81617abcf4 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 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 afc3b8d4ccd..7fd1b17f296 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 02148a138c9..2e110cb3d0b 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 a75290c9405..05733762d37 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 8f04b58b778..a01db18a34a 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 6dde59d4a2e..998576097a0 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 b552d7678d5..de578257e46 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 5b8ab08aefc..c771970650d 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 2fbfd6cc84e..8d98fe31334 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 0fdb5f74e82..dd7e8a869ec 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 814c1a4131d..9affb44b744 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 72931141638..9df63583998 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 c53d5a0922e..2bc9ec3289a 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 cf5c6e8a3b4..861acf62d0b 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 a1a712d1421..05786e69cef 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 1ef205bd8be..bcbee16cdcf 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 d9dcebfda6a..1d0473ec4b6 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 e880a8b212b..99035209e14 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 2c5b18a8113..2009c32c854 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 28eebea749c..06b058cbfe1 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 cafe6d1bb58..a2831fd2b5a 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 9cbb64c2311..f918f0aed7a 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 cfd3cd3af3e..ae1320c821f 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 56f0b31da0d..26508a47224 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 29c1c584eed..7928c04b9df 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 6cb825a9be7..66ed817ffa8 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 d875898f8bc..60caca3da57 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 048b640c0b3..716a0ebf21c 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 be4166e43b5..7355c70ff95 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 441a8410700..19e253e3b8e 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 592e2b11905..ad5e3aa3f06 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 159a3d74603..1498ed4d17e 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 90693566041..aa7f535aa64 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 a2426d80f71..ea26c3656e6 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 2e85a323a1c..9e10ddf89ac 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 8f20a2c8ab7..2de8d849d19 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 343622c5c1b..03733cc2eb5 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/ref-suggestion.rs b/src/test/compile-fail/ref-suggestion.rs index 4625669d5ec..815f7526632 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 851e31b942e..8dd9994b234 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 8877377a6ec..1d04679fd11 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 3fffb2e19f2..9cf65a9d00d 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 29360e58b5b..fc2ed83b272 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 6950bce7304..a8ff1bae7a7 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/type-mismatch-same-crate-name.rs b/src/test/compile-fail/type-mismatch-same-crate-name.rs index 014fa35c309..e81ae5d7439 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 f7fed32cb5a..862d842d62c 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 bcadd1f39fa..8caf2e0c0a4 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 5cc14dfef0e..00000000000 --- 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 e80feb66d60..00000000000 --- 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 bb73fda67bd..30bcafd1049 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 00000000000..bde669ab0f7 --- /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/src/errors.rs b/src/tools/compiletest/src/errors.rs index 418a0bc7121..c3da891933f 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/main.rs b/src/tools/compiletest/src/main.rs index e92b0c87280..8e999139a24 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 6358d19ff09..8770d4b1f42 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))); + 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)); - // 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. + // 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; + } + + 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; } } - 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() { - 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("(") + 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!(); } } -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; +/// 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 } - 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 { @@ -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());