1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use errors
::{Error, ErrorKind}
;
15 use std
::str::FromStr
;
17 // These structs are a subset of the ones found in
20 #[derive(Deserialize)]
23 code
: Option
<DiagnosticCode
>,
25 spans
: Vec
<DiagnosticSpan
>,
26 children
: Vec
<Diagnostic
>,
27 rendered
: Option
<String
>,
30 #[derive(Deserialize, Clone)]
31 struct DiagnosticSpan
{
38 label
: Option
<String
>,
39 suggested_replacement
: Option
<String
>,
40 expansion
: Option
<Box
<DiagnosticSpanMacroExpansion
>>,
43 #[derive(Deserialize, Clone)]
44 struct DiagnosticSpanMacroExpansion
{
45 /// span where macro was applied to generate this code
48 /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
49 macro_decl_name
: String
,
52 #[derive(Deserialize, Clone)]
53 struct DiagnosticCode
{
56 /// An explanation for the code.
57 explanation
: Option
<String
>,
60 pub fn extract_rendered(output
: &str, proc_res
: &ProcRes
) -> String
{
64 if line
.starts_with('
{'
) {
65 match serde_json
::from_str
::<Diagnostic
>(line
) {
66 Ok(diagnostic
) => diagnostic
.rendered
,
68 proc_res
.fatal(Some(&format
!(
69 "failed to decode compiler output as json: \
70 `{}`\noutput: {}\nline: {}",
82 pub fn parse_output(file_name
: &str, output
: &str, proc_res
: &ProcRes
) -> Vec
<Error
> {
85 .flat_map(|line
| parse_line(file_name
, line
, output
, proc_res
))
89 fn parse_line(file_name
: &str, line
: &str, output
: &str, proc_res
: &ProcRes
) -> Vec
<Error
> {
90 // The compiler sometimes intermingles non-JSON stuff into the
91 // output. This hack just skips over such lines. Yuck.
92 if line
.starts_with('
{'
) {
93 match serde_json
::from_str
::<Diagnostic
>(line
) {
95 let mut expected_errors
= vec
![];
96 push_expected_errors(&mut expected_errors
, &diagnostic
, &[], file_name
);
100 proc_res
.fatal(Some(&format
!(
101 "failed to decode compiler output as json: \
102 `{}`\noutput: {}\nline: {}",
112 fn push_expected_errors(
113 expected_errors
: &mut Vec
<Error
>,
114 diagnostic
: &Diagnostic
,
115 default_spans
: &[&DiagnosticSpan
],
118 let spans_in_this_file
: Vec
<_
> = diagnostic
121 .filter(|span
| Path
::new(&span
.file_name
) == Path
::new(&file_name
))
124 let primary_spans
: Vec
<_
> = spans_in_this_file
.iter()
126 .filter(|span
| span
.is_primary
)
127 .take(1) // sometimes we have more than one showing up in the json; pick first
129 let primary_spans
= if primary_spans
.is_empty() {
130 // subdiagnostics often don't have a span of their own;
131 // inherit the span from the parent in that case
137 // We break the output into multiple lines, and then append the
138 // [E123] to every line in the output. This may be overkill. The
139 // intention was to match existing tests that do things like "//|
140 // found `i32` [E123]" and expect to match that somewhere, and yet
141 // also ensure that `//~ ERROR E123` *always* works. The
142 // assumption is that these multi-line error messages are on their
144 let with_code
= |span
: &DiagnosticSpan
, text
: &str| {
145 match diagnostic
.code
{
147 // FIXME(#33000) -- it'd be better to use a dedicated
148 // UI harness than to include the line/col number like
149 // this, but some current tests rely on it.
151 // Note: Do NOT include the filename. These can easily
152 // cause false matches where the expected message
153 // appears in the filename, and hence the message
154 // changes but the test still passes.
155 format
!("{}:{}: {}:{}: {} [{}]",
156 span
.line_start
, span
.column_start
,
157 span
.line_end
, span
.column_end
,
158 text
, code
.code
.clone()),
160 // FIXME(#33000) -- it'd be better to use a dedicated UI harness
161 format
!("{}:{}: {}:{}: {}",
162 span
.line_start
, span
.column_start
,
163 span
.line_end
, span
.column_end
,
168 // Convert multi-line messages into multiple expected
169 // errors. We expect to replace these with something
170 // more structured shortly anyhow.
171 let mut message_lines
= diagnostic
.message
.lines();
172 if let Some(first_line
) = message_lines
.next() {
173 for span
in primary_spans
{
174 let msg
= with_code(span
, first_line
);
175 let kind
= ErrorKind
::from_str(&diagnostic
.level
).ok();
176 expected_errors
.push(Error
{
177 line_num
: span
.line_start
,
183 for next_line
in message_lines
{
184 for span
in primary_spans
{
185 expected_errors
.push(Error
{
186 line_num
: span
.line_start
,
188 msg
: with_code(span
, next_line
),
193 // If the message has a suggestion, register that.
194 for span
in primary_spans
{
195 if let Some(ref suggested_replacement
) = span
.suggested_replacement
{
196 for (index
, line
) in suggested_replacement
.lines().enumerate() {
197 expected_errors
.push(Error
{
198 line_num
: span
.line_start
+ index
,
199 kind
: Some(ErrorKind
::Suggestion
),
200 msg
: line
.to_string(),
206 // Add notes for the backtrace
207 for span
in primary_spans
{
208 for frame
in &span
.expansion
{
209 push_backtrace(expected_errors
, frame
, file_name
);
213 // Add notes for any labels that appear in the message.
214 for span
in spans_in_this_file
216 .filter(|span
| span
.label
.is_some())
218 expected_errors
.push(Error
{
219 line_num
: span
.line_start
,
220 kind
: Some(ErrorKind
::Note
),
221 msg
: span
.label
.clone().unwrap(),
225 // Flatten out the children.
226 for child
in &diagnostic
.children
{
227 push_expected_errors(expected_errors
, child
, primary_spans
, file_name
);
232 expected_errors
: &mut Vec
<Error
>,
233 expansion
: &DiagnosticSpanMacroExpansion
,
236 if Path
::new(&expansion
.span
.file_name
) == Path
::new(&file_name
) {
237 expected_errors
.push(Error
{
238 line_num
: expansion
.span
.line_start
,
239 kind
: Some(ErrorKind
::Note
),
240 msg
: format
!("in this expansion of {}", expansion
.macro_decl_name
),
244 for previous_expansion
in &expansion
.span
.expansion
{
245 push_backtrace(expected_errors
, previous_expansion
, file_name
);