1- use proc_macro:: TokenStream ;
1+ //! Proc macro which builds the Symbol table
2+ //!
3+ //! # Debugging
4+ //!
5+ //! Since this proc-macro does some non-trivial work, debugging it is important.
6+ //! This proc-macro can be invoked as an ordinary unit test, like so:
7+ //!
8+ //! ```bash
9+ //! cd compiler/rustc_macros
10+ //! cargo test symbols::test_symbols -- --nocapture
11+ //! ```
12+ //!
13+ //! This unit test finds the `symbols!` invocation in `compiler/rustc_span/src/symbol.rs`
14+ //! and runs it. It verifies that the output token stream can be parsed as valid module
15+ //! items and that no errors were produced.
16+ //!
17+ //! You can also view the generated code by using `cargo expand`:
18+ //!
19+ //! ```bash
20+ //! cargo install cargo-expand # this is necessary only once
21+ //! cd compiler/rustc_span
22+ //! cargo expand > /tmp/rustc_span.rs # it's a big file
23+ //! ```
24+
25+ use proc_macro2:: { Span , TokenStream } ;
226use quote:: quote;
3- use std:: collections:: HashSet ;
27+ use std:: collections:: HashMap ;
428use syn:: parse:: { Parse , ParseStream , Result } ;
5- use syn:: { braced, parse_macro_input, Ident , LitStr , Token } ;
29+ use syn:: { braced, punctuated:: Punctuated , Ident , LitStr , Token } ;
30+
31+ #[ cfg( test) ]
32+ mod tests;
633
734mod kw {
835 syn:: custom_keyword!( Keywords ) ;
@@ -19,7 +46,6 @@ impl Parse for Keyword {
1946 let name = input. parse ( ) ?;
2047 input. parse :: < Token ! [ : ] > ( ) ?;
2148 let value = input. parse ( ) ?;
22- input. parse :: < Token ! [ , ] > ( ) ?;
2349
2450 Ok ( Keyword { name, value } )
2551 }
@@ -37,78 +63,101 @@ impl Parse for Symbol {
3763 Ok ( _) => Some ( input. parse ( ) ?) ,
3864 Err ( _) => None ,
3965 } ;
40- input. parse :: < Token ! [ , ] > ( ) ?;
4166
4267 Ok ( Symbol { name, value } )
4368 }
4469}
4570
46- /// A type used to greedily parse another type until the input is empty.
47- struct List < T > ( Vec < T > ) ;
48-
49- impl < T : Parse > Parse for List < T > {
50- fn parse ( input : ParseStream < ' _ > ) -> Result < Self > {
51- let mut list = Vec :: new ( ) ;
52- while !input. is_empty ( ) {
53- list. push ( input. parse ( ) ?) ;
54- }
55- Ok ( List ( list) )
56- }
57- }
58-
5971struct Input {
60- keywords : List < Keyword > ,
61- symbols : List < Symbol > ,
72+ keywords : Punctuated < Keyword , Token ! [ , ] > ,
73+ symbols : Punctuated < Symbol , Token ! [ , ] > ,
6274}
6375
6476impl Parse for Input {
6577 fn parse ( input : ParseStream < ' _ > ) -> Result < Self > {
6678 input. parse :: < kw:: Keywords > ( ) ?;
6779 let content;
6880 braced ! ( content in input) ;
69- let keywords = content . parse ( ) ?;
81+ let keywords = Punctuated :: parse_terminated ( & content ) ?;
7082
7183 input. parse :: < kw:: Symbols > ( ) ?;
7284 let content;
7385 braced ! ( content in input) ;
74- let symbols = content . parse ( ) ?;
86+ let symbols = Punctuated :: parse_terminated ( & content ) ?;
7587
7688 Ok ( Input { keywords, symbols } )
7789 }
7890}
7991
92+ #[ derive( Default ) ]
93+ struct Errors {
94+ list : Vec < syn:: Error > ,
95+ }
96+
97+ impl Errors {
98+ fn error ( & mut self , span : Span , message : String ) {
99+ self . list . push ( syn:: Error :: new ( span, message) ) ;
100+ }
101+ }
102+
80103pub fn symbols ( input : TokenStream ) -> TokenStream {
81- let input = parse_macro_input ! ( input as Input ) ;
104+ let ( mut output, errors) = symbols_with_errors ( input) ;
105+
106+ // If we generated any errors, then report them as compiler_error!() macro calls.
107+ // This lets the errors point back to the most relevant span. It also allows us
108+ // to report as many errors as we can during a single run.
109+ output. extend ( errors. into_iter ( ) . map ( |e| e. to_compile_error ( ) ) ) ;
110+
111+ output
112+ }
113+
114+ fn symbols_with_errors ( input : TokenStream ) -> ( TokenStream , Vec < syn:: Error > ) {
115+ let mut errors = Errors :: default ( ) ;
116+
117+ let input: Input = match syn:: parse2 ( input) {
118+ Ok ( input) => input,
119+ Err ( e) => {
120+ // This allows us to display errors at the proper span, while minimizing
121+ // unrelated errors caused by bailing out (and not generating code).
122+ errors. list . push ( e) ;
123+ Input { keywords : Default :: default ( ) , symbols : Default :: default ( ) }
124+ }
125+ } ;
82126
83127 let mut keyword_stream = quote ! { } ;
84128 let mut symbols_stream = quote ! { } ;
85129 let mut digits_stream = quote ! { } ;
86130 let mut prefill_stream = quote ! { } ;
87131 let mut counter = 0u32 ;
88- let mut keys = HashSet :: < String > :: new ( ) ;
89- let mut prev_key: Option < String > = None ;
90- let mut errors = Vec :: < String > :: new ( ) ;
91-
92- let mut check_dup = |str : & str , errors : & mut Vec < String > | {
93- if !keys. insert ( str. to_string ( ) ) {
94- errors. push ( format ! ( "Symbol `{}` is duplicated" , str ) ) ;
132+ let mut keys =
133+ HashMap :: < String , Span > :: with_capacity ( input. keywords . len ( ) + input. symbols . len ( ) + 10 ) ;
134+ let mut prev_key: Option < ( Span , String ) > = None ;
135+
136+ let mut check_dup = |span : Span , str : & str , errors : & mut Errors | {
137+ if let Some ( prev_span) = keys. get ( str) {
138+ errors. error ( span, format ! ( "Symbol `{}` is duplicated" , str ) ) ;
139+ errors. error ( * prev_span, format ! ( "location of previous definition" ) ) ;
140+ } else {
141+ keys. insert ( str. to_string ( ) , span) ;
95142 }
96143 } ;
97144
98- let mut check_order = |str : & str , errors : & mut Vec < String > | {
99- if let Some ( ref prev_str) = prev_key {
145+ let mut check_order = |span : Span , str : & str , errors : & mut Errors | {
146+ if let Some ( ( prev_span , ref prev_str) ) = prev_key {
100147 if str < prev_str {
101- errors. push ( format ! ( "Symbol `{}` must precede `{}`" , str , prev_str) ) ;
148+ errors. error ( span, format ! ( "Symbol `{}` must precede `{}`" , str , prev_str) ) ;
149+ errors. error ( prev_span, format ! ( "location of previous symbol `{}`" , prev_str) ) ;
102150 }
103151 }
104- prev_key = Some ( str. to_string ( ) ) ;
152+ prev_key = Some ( ( span , str. to_string ( ) ) ) ;
105153 } ;
106154
107155 // Generate the listed keywords.
108- for keyword in & input. keywords . 0 {
156+ for keyword in input. keywords . iter ( ) {
109157 let name = & keyword. name ;
110158 let value = & keyword. value ;
111- check_dup ( & value. value ( ) , & mut errors) ;
159+ let value_string = value. value ( ) ;
160+ check_dup ( keyword. name . span ( ) , & value_string, & mut errors) ;
112161 prefill_stream. extend ( quote ! {
113162 #value,
114163 } ) ;
@@ -120,14 +169,15 @@ pub fn symbols(input: TokenStream) -> TokenStream {
120169 }
121170
122171 // Generate the listed symbols.
123- for symbol in & input. symbols . 0 {
172+ for symbol in input. symbols . iter ( ) {
124173 let name = & symbol. name ;
125174 let value = match & symbol. value {
126175 Some ( value) => value. value ( ) ,
127176 None => name. to_string ( ) ,
128177 } ;
129- check_dup ( & value, & mut errors) ;
130- check_order ( & name. to_string ( ) , & mut errors) ;
178+ check_dup ( symbol. name . span ( ) , & value, & mut errors) ;
179+ check_order ( symbol. name . span ( ) , & name. to_string ( ) , & mut errors) ;
180+
131181 prefill_stream. extend ( quote ! {
132182 #value,
133183 } ) ;
@@ -142,7 +192,7 @@ pub fn symbols(input: TokenStream) -> TokenStream {
142192 // Generate symbols for the strings "0", "1", ..., "9".
143193 for n in 0 ..10 {
144194 let n = n. to_string ( ) ;
145- check_dup ( & n, & mut errors) ;
195+ check_dup ( Span :: call_site ( ) , & n, & mut errors) ;
146196 prefill_stream. extend ( quote ! {
147197 #n,
148198 } ) ;
@@ -152,14 +202,7 @@ pub fn symbols(input: TokenStream) -> TokenStream {
152202 counter += 1 ;
153203 }
154204
155- if !errors. is_empty ( ) {
156- for error in errors. into_iter ( ) {
157- eprintln ! ( "error: {}" , error)
158- }
159- panic ! ( "errors in `Keywords` and/or `Symbols`" ) ;
160- }
161-
162- let tt = TokenStream :: from ( quote ! {
205+ let output = quote ! {
163206 macro_rules! keywords {
164207 ( ) => {
165208 #keyword_stream
@@ -184,11 +227,16 @@ pub fn symbols(input: TokenStream) -> TokenStream {
184227 ] )
185228 }
186229 }
187- } ) ;
230+ } ;
188231
189- // To see the generated code generated, uncomment this line, recompile, and
190- // run the resulting output through `rustfmt`.
191- //eprintln!("{}", tt);
232+ ( output, errors. list )
192233
193- tt
234+ // To see the generated code, use the "cargo expand" command.
235+ // Do this once to install:
236+ // cargo install cargo-expand
237+ //
238+ // Then, cd to rustc_span and run:
239+ // cargo expand > /tmp/rustc_span_expanded.rs
240+ //
241+ // and read that file.
194242}
0 commit comments