Skip to content

Commit dd89a98

Browse files
committed
Match errors using the callsite of macro expansions
Closes Manishearth#48 This commit corresponds to 6a1c0637ce44aeea6c60527f4c0e7fb33f2bcd0d on rust-lang/rust
1 parent db11f07 commit dd89a98

File tree

3 files changed

+60
-6
lines changed

3 files changed

+60
-6
lines changed

src/json.rs

+28-5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@ struct DiagnosticSpan {
3939
expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
4040
}
4141

42+
impl DiagnosticSpan {
43+
/// Returns the deepest source span in the macro call stack with a given file name.
44+
/// This is either the supplied span, or the span for some macro callsite that expanded to it.
45+
fn first_callsite_in_file(&self, file_name: &str) -> &DiagnosticSpan {
46+
if self.file_name == file_name {
47+
self
48+
} else {
49+
self.expansion
50+
.as_ref()
51+
.map(|origin| origin.span.first_callsite_in_file(file_name))
52+
.unwrap_or(self)
53+
}
54+
}
55+
}
56+
4257
#[derive(Deserialize, Clone)]
4358
struct DiagnosticSpanMacroExpansion {
4459
/// span where macro was applied to generate this code
@@ -89,15 +104,23 @@ fn push_expected_errors(expected_errors: &mut Vec<Error>,
89104
diagnostic: &Diagnostic,
90105
default_spans: &[&DiagnosticSpan],
91106
file_name: &str) {
92-
let spans_in_this_file: Vec<_> = diagnostic.spans
107+
// In case of macro expansions, we need to get the span of the callsite
108+
let spans_info_in_this_file: Vec<_> = diagnostic
109+
.spans
93110
.iter()
94-
.filter(|span| Path::new(&span.file_name) == Path::new(&file_name))
111+
.map(|span| (span.is_primary, span.first_callsite_in_file(file_name)))
112+
.filter(|(_, span)| Path::new(&span.file_name) == Path::new(&file_name))
95113
.collect();
96114

97-
let primary_spans: Vec<_> = spans_in_this_file.iter()
98-
.cloned()
99-
.filter(|span| span.is_primary)
115+
let spans_in_this_file: Vec<_> = spans_info_in_this_file.iter()
116+
.map(|(_, span)| span)
117+
.collect();
118+
119+
let primary_spans: Vec<_> = spans_info_in_this_file.iter()
120+
.filter(|(is_primary, _)| *is_primary)
121+
.map(|(_, span)| span)
100122
.take(1) // sometimes we have more than one showing up in the json; pick first
123+
.cloned()
101124
.collect();
102125
let primary_spans = if primary_spans.is_empty() {
103126
// subdiagnostics often don't have a span of their own;

src/runtest.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,10 @@ actual:\n\
10081008
self.fatal_proc_rec("process did not return an error status", proc_res);
10091009
}
10101010

1011+
// On Windows, keep all '\' path separators to match the paths reported in the JSON output
1012+
// from the compiler
1013+
let os_file_name = self.testpaths.file.display().to_string();
1014+
10111015
let file_name =
10121016
format!("{}", self.testpaths.file.display())
10131017
.replace(r"\", "/"); // on windows, translate all '\' path separators to '/'
@@ -1020,7 +1024,7 @@ actual:\n\
10201024
let expect_note = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Note));
10211025

10221026
// Parse the JSON output from the compiler and extract out the messages.
1023-
let actual_errors = json::parse_output(&file_name, &proc_res.stderr, proc_res);
1027+
let actual_errors = json::parse_output(&os_file_name, &proc_res.stderr, proc_res);
10241028
let mut unexpected = Vec::new();
10251029
let mut found = vec![false; expected_errors.len()];
10261030
for actual_error in &actual_errors {
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2018 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.
4+
//
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.
10+
11+
macro_rules! macro_with_error {
12+
( ) => {
13+
println!("{"); //~ ERROR invalid
14+
};
15+
}
16+
17+
fn foo() {
18+
19+
}
20+
21+
fn main() {
22+
macro_with_error!();
23+
//^ In case of a local macro we want the error to be matched in the macro definition, not here
24+
25+
println!("}"); //~ ERROR invalid
26+
//^ In case of an external macro we want the error to be matched here
27+
}

0 commit comments

Comments
 (0)