forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of rust-lang#79944 - sivadeilra:syms_proc_macro_testing, r…
…=petrochenkov Improve error handling in `symbols` proc-macro This improves how the `symbols` proc-macro handles errors. If it finds an error in its input, the macro does not panic. Instead, it still produces an output token stream. That token stream will contain `compile_error!(...)` macro invocations. This will still cause compilation to fail (which is what we want), but it will prevent meaningless errors caused by the output not containing symbols that the macro normally generates. This solves a small (but annoying) problem. When you're editing rustc_span/src/symbol.rs, and you get something wrong (dup symbol name, misordered symbol), you want to get only the errors that are relevant, not a burst of errors that are irrelevant. This change also uses the correct Span when reporting errors, so you get errors that point to the correct place in rustc_span/src/symbol.rs where something is wrong. This also adds several unit tests which test the `symbols` proc-macro. This commit also makes it easy to run the `symbols` proc-macro as an ordinary Cargo test. Just run `cargo test`. This makes it easier to do development on the macro itself, such as running it under a debugger. This commit also uses the `Punctuated` type in `syn` for parsing comma-separated lists, rather than doing it manually. The output of the macro is not changed at all by this commit, so rustc should be completely unchanged. This just improves quality of life during development.
- Loading branch information
Showing
3 changed files
with
204 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
use super::*; | ||
|
||
// This test is mainly here for interactive development. Use this test while | ||
// you're working on the proc-macro defined in this file. | ||
#[test] | ||
fn test_symbols() { | ||
// We textually include the symbol.rs file, which contains the list of all | ||
// symbols, keywords, and common words. Then we search for the | ||
// `symbols! { ... }` call. | ||
|
||
static SYMBOL_RS_FILE: &str = include_str!("../../../rustc_span/src/symbol.rs"); | ||
|
||
let file = syn::parse_file(SYMBOL_RS_FILE).unwrap(); | ||
let symbols_path: syn::Path = syn::parse_quote!(symbols); | ||
|
||
let m: &syn::ItemMacro = file | ||
.items | ||
.iter() | ||
.filter_map(|i| { | ||
if let syn::Item::Macro(m) = i { | ||
if m.mac.path == symbols_path { Some(m) } else { None } | ||
} else { | ||
None | ||
} | ||
}) | ||
.next() | ||
.expect("did not find `symbols!` macro invocation."); | ||
|
||
let body_tokens = m.mac.tokens.clone(); | ||
|
||
test_symbols_macro(body_tokens, &[]); | ||
} | ||
|
||
fn test_symbols_macro(input: TokenStream, expected_errors: &[&str]) { | ||
let (output, found_errors) = symbols_with_errors(input); | ||
|
||
// It should always parse. | ||
let _parsed_file = syn::parse2::<syn::File>(output).unwrap(); | ||
|
||
assert_eq!( | ||
found_errors.len(), | ||
expected_errors.len(), | ||
"Macro generated a different number of errors than expected" | ||
); | ||
|
||
for (found_error, &expected_error) in found_errors.iter().zip(expected_errors.iter()) { | ||
let found_error_str = format!("{}", found_error); | ||
assert_eq!(found_error_str, expected_error); | ||
} | ||
} | ||
|
||
#[test] | ||
fn check_dup_keywords() { | ||
let input = quote! { | ||
Keywords { | ||
Crate: "crate", | ||
Crate: "crate", | ||
} | ||
Symbols {} | ||
}; | ||
test_symbols_macro(input, &["Symbol `crate` is duplicated", "location of previous definition"]); | ||
} | ||
|
||
#[test] | ||
fn check_dup_symbol() { | ||
let input = quote! { | ||
Keywords {} | ||
Symbols { | ||
splat, | ||
splat, | ||
} | ||
}; | ||
test_symbols_macro(input, &["Symbol `splat` is duplicated", "location of previous definition"]); | ||
} | ||
|
||
#[test] | ||
fn check_dup_symbol_and_keyword() { | ||
let input = quote! { | ||
Keywords { | ||
Splat: "splat", | ||
} | ||
Symbols { | ||
splat, | ||
} | ||
}; | ||
test_symbols_macro(input, &["Symbol `splat` is duplicated", "location of previous definition"]); | ||
} | ||
|
||
#[test] | ||
fn check_symbol_order() { | ||
let input = quote! { | ||
Keywords {} | ||
Symbols { | ||
zebra, | ||
aardvark, | ||
} | ||
}; | ||
test_symbols_macro( | ||
input, | ||
&["Symbol `aardvark` must precede `zebra`", "location of previous symbol `zebra`"], | ||
); | ||
} |