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.
This commit is contained in:
parent
95545e7adc
commit
01d2b4ab6b
@ -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
|
||||
|
185
src/compiletest/json.rs
Normal file
185
src/compiletest/json.rs
Normal file
@ -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 <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;
|
||||
|
||||
// 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) => {
|
||||
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<Error>,
|
||||
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<Error>,
|
||||
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);
|
||||
}
|
||||
}
|
@ -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<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,
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
@ -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<DiagnosticSpan>,
|
||||
/// Assocaited diagnostic messages.
|
||||
/// Associated diagnostic messages.
|
||||
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)]
|
||||
@ -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<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)]
|
||||
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<DiagnosticSpan>,
|
||||
}
|
||||
|
||||
#[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<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> {
|
||||
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<DiagnosticSpan> {
|
||||
assert_eq!(suggestion.msp.spans.len(), suggestion.substitutes.len());
|
||||
suggestion.msp.spans.iter()
|
||||
.zip(&suggestion.substitutes)
|
||||
.map(|(&span, suggestion)| {
|
||||
DiagnosticSpan::from_span(span, Some(suggestion), je)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
|
||||
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<DiagnosticSpanLine> {
|
||||
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<DiagnosticSpanLine> {
|
||||
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<DiagnosticSpanLine> {
|
||||
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<DiagnosticSpanLine> {
|
||||
je.cm.span_to_lines(span)
|
||||
.map(|lines| {
|
||||
let fm = &*lines.file;
|
||||
lines.lines.iter()
|
||||
.enumerate()
|
||||
.map(|(i, line)| {
|
||||
// Invariant - CodeMap::span_to_lines
|
||||
// will not return extra context lines
|
||||
// - the last line returned is the last
|
||||
// line of `span`.
|
||||
let highlight = if i == lines.lines.len() - 1 {
|
||||
(line.end_col.0 + 1, line.end_col.0 + 1)
|
||||
} else {
|
||||
(0, 0)
|
||||
};
|
||||
DiagnosticSpanLine::line_from_filemap(fm,
|
||||
line.line_index,
|
||||
highlight.0,
|
||||
highlight.1)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,3 +387,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() {
|
||||
f1(2i32, 4i32);
|
||||
//~^ ERROR mismatched types
|
||||
//~| expected u32
|
||||
//~| found i32
|
||||
//~| expected `u32`
|
||||
//~| found `i32`
|
||||
}
|
||||
|
||||
pub fn f1_int_uint() {
|
||||
|
@ -11,9 +11,5 @@
|
||||
fn main() {
|
||||
loop {
|
||||
true //~ ERROR mismatched types
|
||||
//~| expected ()
|
||||
//~| found bool
|
||||
//~| expected ()
|
||||
//~| found bool
|
||||
}
|
||||
}
|
||||
|
@ -13,10 +13,6 @@ struct r;
|
||||
impl Drop for r {
|
||||
fn drop(&mut self) {
|
||||
true //~ ERROR mismatched types
|
||||
//~| expected ()
|
||||
//~| found bool
|
||||
//~| expected ()
|
||||
//~| found bool
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -20,7 +20,7 @@ impl<T: MyTrait> !Send for TestType<T> {}
|
||||
//~^ ERROR conflicting implementations of trait `std::marker::Send`
|
||||
|
||||
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> {}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -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`
|
||||
|
@ -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:
|
||||
|
@ -36,5 +36,4 @@ fn check<'r, I: Iterator<Item=usize>, 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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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<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 run `rustc --explain E0106` to see a detailed explanation
|
||||
|
||||
fn main() {}
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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!");
|
||||
}
|
||||
|
@ -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!");
|
||||
|
@ -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
|
||||
});
|
||||
}
|
||||
|
@ -36,5 +36,4 @@ fn main() {
|
||||
//~| help: the following implementations were found:
|
||||
//~| help: <Bar as Foo<i32>>
|
||||
//~| 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<u8>>
|
||||
//~| help: and 2 others
|
||||
//~| help: run `rustc --explain E0277`
|
||||
}
|
||||
|
@ -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() { }
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
|
@ -11,18 +11,15 @@
|
||||
fn parse_type(iter: Box<Iterator<Item=&str>+'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() {}
|
||||
|
@ -15,5 +15,5 @@ use issue_30123_aux::*;
|
||||
|
||||
fn main() {
|
||||
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,
|
||||
//~^ 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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() {}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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!()
|
||||
}
|
||||
|
@ -27,5 +27,5 @@ fn bar() {
|
||||
|
||||
#[forbid(warnings)]
|
||||
fn baz() {
|
||||
while true {} //~ ERROR: warnings
|
||||
while true {} //~ ERROR: infinite
|
||||
}
|
||||
|
@ -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() { }
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ fn foo<'z>() where &'z (): Sized {
|
||||
//[verbose]~| found `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::bar::<ReStatic, char>}`
|
||||
//[normal]~^^^^ ERROR mismatched types
|
||||
//[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>;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ trait Trait {
|
||||
|
||||
impl Trait for usize {
|
||||
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() {}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -16,13 +16,11 @@ fn check<T: Iterator, U: ?Sized>() {
|
||||
// suggest a where-clause, if needed
|
||||
mem::size_of::<U>();
|
||||
//~^ 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::<Misc<U>>();
|
||||
//~^ 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<U>`
|
||||
//~| NOTE required by `std::mem::size_of`
|
||||
@ -31,13 +29,11 @@ fn check<T: Iterator, U: ?Sized>() {
|
||||
|
||||
<u64 as From<T>>::from;
|
||||
//~^ ERROR `u64: std::convert::From<T>` is not satisfied
|
||||
//~| HELP E0277
|
||||
//~| HELP consider adding a `where u64: std::convert::From<T>` bound
|
||||
//~| NOTE required by `std::convert::From::from`
|
||||
|
||||
<u64 as From<<T as Iterator>::Item>>::from;
|
||||
//~^ ERROR `u64: std::convert::From<<T as std::iter::Iterator>::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<T: Iterator, U: ?Sized>() {
|
||||
|
||||
<Misc<_> as From<T>>::from;
|
||||
//~^ ERROR `Misc<_>: std::convert::From<T>` 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`
|
||||
}
|
||||
|
@ -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`
|
||||
}
|
||||
}
|
||||
|
@ -16,18 +16,15 @@
|
||||
struct SomeStruct<A> { x: u32 }
|
||||
//~^ ERROR parameter `A` is never used
|
||||
//~| HELP PhantomData
|
||||
//~| HELP run `rustc --explain E0392` to see a detailed explanation
|
||||
|
||||
enum SomeEnum<A> { 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<T> {
|
||||
//~^ ERROR parameter `T` is never used
|
||||
//~| HELP PhantomData
|
||||
//~| HELP run `rustc --explain E0392` to see a detailed explanation
|
||||
Cons(Box<ListCell<T>>),
|
||||
Nil
|
||||
}
|
||||
|
@ -10,4 +10,4 @@
|
||||
|
||||
// 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)
|
||||
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":" <u8 as std::ops::Add>","code":null,"level":"help",' $(LOG)
|
||||
diff foo.json $(LOG)
|
||||
|
4
src/test/run-make/json-errors/foo.json
Normal file
4
src/test/run-make/json-errors/foo.json
Normal file
@ -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}
|
@ -28,7 +28,9 @@ pub enum ErrorKind {
|
||||
impl FromStr for ErrorKind {
|
||||
type 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),
|
||||
"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<ExpectedError> {
|
||||
pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
|
||||
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<usize>,
|
||||
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<usize>,
|
||||
(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::<ErrorKind>()
|
||||
.ok();
|
||||
let letters = line[kind_start..].chars();
|
||||
let msg = letters.skip_while(|c| c.is_whitespace())
|
||||
.skip_while(|c| !c.is_whitespace())
|
||||
.collect::<String>().trim().to_owned();
|
||||
let (kind, msg);
|
||||
match
|
||||
line[kind_start..].split_whitespace()
|
||||
.next()
|
||||
.expect("Encountered unexpected empty comment")
|
||||
.parse::<ErrorKind>()
|
||||
{
|
||||
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::<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 {
|
||||
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={:?}",
|
||||
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, }))
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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<errors::ExpectedError>,
|
||||
expected_errors: Vec<errors::Error>,
|
||||
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::<Vec<String>>();
|
||||
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<F>(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());
|
||||
|
Loading…
Reference in New Issue
Block a user