diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2016-04-19 11:24:41 -0400 |
|---|---|---|
| committer | Niko Matsakis <niko@alum.mit.edu> | 2016-04-21 04:42:25 -0400 |
| commit | 6ce5e770d2b456fd44dce50703afd980ffe2ff33 (patch) | |
| tree | e8d2e2e4c376ba27a38dddba5590467d6c95156e /src/tools/compiletest | |
| parent | 28a3c881576ee09fe8ab9dfff4a15ffb89e1c149 (diff) | |
move json.rs file
Diffstat (limited to 'src/tools/compiletest')
| -rw-r--r-- | src/tools/compiletest/src/json.rs | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs new file mode 100644 index 00000000000..bbe038509e3 --- /dev/null +++ b/src/tools/compiletest/src/json.rs @@ -0,0 +1,194 @@ +// 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) => { + panic!("failed to decode compiler output as json: `{}`", error); + } + } + } else { + vec![] + } +} + +fn push_expected_errors(expected_errors: &mut Vec<Error>, + diagnostic: &Diagnostic, + file_name: &str) { + // We only consider messages pertaining to the current file. + let matching_spans = || { + diagnostic.spans.iter().filter(|span| span.file_name == file_name) + }; + + // We break the output into multiple lines, and then append the + // [E123] to every line in the output. This may be overkill. The + // intention was to match existing tests that do things like "//| + // found `i32` [E123]" and expect to match that somewhere, and yet + // also ensure that `//~ ERROR E123` *always* works. The + // assumption is that these multi-line error messages are on their + // way out anyhow. + let with_code = |span: &DiagnosticSpan, text: &str| { + match diagnostic.code { + Some(ref code) => + // FIXME(#33000) -- it'd be better to use a dedicated + // UI harness than to include the line/col number like + // this, but some current tests rely on it. + // + // Note: Do NOT include the filename. These can easily + // cause false matches where the expected message + // appears in the filename, and hence the message + // changes but the test still passes. + format!("{}:{}: {}:{}: {} [{}]", + span.line_start, span.column_start, + span.line_end, span.column_end, + text, code.code.clone()), + None => + // FIXME(#33000) -- it'd be better to use a dedicated UI harness + format!("{}:{}: {}:{}: {}", + span.line_start, span.column_start, + span.line_end, span.column_end, + text), + } + }; + + // Convert multi-line messages into multiple expected + // errors. We expect to replace these with something + // more structured shortly anyhow. + let mut message_lines = diagnostic.message.lines(); + if let Some(first_line) = message_lines.next() { + for span in matching_spans() { + let msg = with_code(span, first_line); + let kind = ErrorKind::from_str(&diagnostic.level).ok(); + expected_errors.push( + Error { + line_num: span.line_start, + kind: kind, + msg: msg, + } + ); + } + } + for next_line in message_lines { + for span in matching_spans() { + expected_errors.push( + Error { + line_num: span.line_start, + kind: None, + msg: with_code(span, next_line), + } + ); + } + } + + // If the message has a suggestion, register that. + if let Some(ref rendered) = diagnostic.rendered { + let start_line = matching_spans().map(|s| s.line_start).min().expect("\ + every suggestion should have at least one span"); + for (index, line) in rendered.lines().enumerate() { + expected_errors.push( + Error { + line_num: start_line + index, + kind: Some(ErrorKind::Suggestion), + msg: line.to_string() + } + ); + } + } + + // Add notes for the backtrace + for span in matching_spans() { + for frame in &span.expansion { + push_backtrace(expected_errors, + frame, + file_name); + } + } + + // Flatten out the children. + for child in &diagnostic.children { + push_expected_errors(expected_errors, child, file_name); + } +} + +fn push_backtrace(expected_errors: &mut Vec<Error>, + expansion: &DiagnosticSpanMacroExpansion, + file_name: &str) { + if 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); + } +} |
