25
25
//! ```
26
26
27
27
use std:: collections:: HashMap ;
28
+ use std:: collections:: hash_map:: Entry ;
28
29
29
30
use proc_macro2:: { Span , TokenStream } ;
30
31
use quote:: quote;
31
32
use syn:: parse:: { Parse , ParseStream , Result } ;
32
33
use syn:: punctuated:: Punctuated ;
33
- use syn:: { Expr , Ident , Lit , LitStr , Macro , Token , braced} ;
34
+ use syn:: { Expr , Ident , Lit , LitStr , Macro , Token , braced, bracketed } ;
34
35
35
36
#[ cfg( test) ]
36
37
mod tests;
@@ -147,25 +148,44 @@ struct Predefined {
147
148
span_of_name : Span ,
148
149
}
149
150
151
+ struct Duplicate {
152
+ name : String ,
153
+ span_of_name : Span ,
154
+ }
155
+
150
156
struct Entries {
151
157
map : HashMap < String , Predefined > ,
158
+ prefill_stream : TokenStream ,
152
159
}
153
160
154
161
impl Entries {
155
162
fn with_capacity ( capacity : usize ) -> Self {
156
- Entries { map : HashMap :: with_capacity ( capacity) }
163
+ Entries { map : HashMap :: with_capacity ( capacity) , prefill_stream : TokenStream :: new ( ) }
164
+ }
165
+
166
+ fn try_insert ( & mut self , span : Span , s : String ) -> ( u32 , Option < Duplicate > ) {
167
+ let len = self . len ( ) ;
168
+ match self . map . entry ( s) {
169
+ Entry :: Occupied ( entry) => {
170
+ let Predefined { idx, span_of_name } = * entry. get ( ) ;
171
+ ( idx, Some ( Duplicate { name : entry. key ( ) . clone ( ) , span_of_name } ) )
172
+ }
173
+ Entry :: Vacant ( entry) => {
174
+ let s = entry. key ( ) . as_str ( ) ;
175
+ self . prefill_stream . extend ( quote ! { #s, } ) ;
176
+ entry. insert ( Predefined { idx : len, span_of_name : span } ) ;
177
+ ( len, None )
178
+ }
179
+ }
157
180
}
158
181
159
- fn insert ( & mut self , span : Span , s : & str , errors : & mut Errors ) -> u32 {
160
- if let Some ( prev) = self . map . get ( s) {
161
- errors. error ( span, format ! ( "Symbol `{s}` is duplicated" ) ) ;
162
- errors. error ( prev. span_of_name , "location of previous definition" . to_string ( ) ) ;
163
- prev. idx
164
- } else {
165
- let idx = self . len ( ) ;
166
- self . map . insert ( s. to_string ( ) , Predefined { idx, span_of_name : span } ) ;
167
- idx
182
+ fn insert ( & mut self , span : Span , s : String , errors : & mut Errors ) -> u32 {
183
+ let ( idx, duplicate) = self . try_insert ( span, s) ;
184
+ if let Some ( Duplicate { name, span_of_name } ) = duplicate {
185
+ errors. error ( span, format ! ( "Symbol `{name}` is duplicated" ) ) ;
186
+ errors. error ( span_of_name, "location of previous definition" . to_string ( ) ) ;
168
187
}
188
+ idx
169
189
}
170
190
171
191
fn len ( & self ) -> u32 {
@@ -188,18 +208,13 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
188
208
189
209
let mut keyword_stream = quote ! { } ;
190
210
let mut symbols_stream = quote ! { } ;
191
- let mut prefill_stream = quote ! { } ;
192
211
let mut entries = Entries :: with_capacity ( input. keywords . len ( ) + input. symbols . len ( ) + 10 ) ;
193
212
194
213
// Generate the listed keywords.
195
214
for keyword in input. keywords . iter ( ) {
196
215
let name = & keyword. name ;
197
- let value = & keyword. value ;
198
- let value_string = value. value ( ) ;
199
- let idx = entries. insert ( keyword. name . span ( ) , & value_string, & mut errors) ;
200
- prefill_stream. extend ( quote ! {
201
- #value,
202
- } ) ;
216
+ let value_string = keyword. value . value ( ) ;
217
+ let idx = entries. insert ( keyword. name . span ( ) , value_string, & mut errors) ;
203
218
keyword_stream. extend ( quote ! {
204
219
pub const #name: Symbol = Symbol :: new( #idx) ;
205
220
} ) ;
@@ -224,23 +239,15 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
224
239
continue ;
225
240
}
226
241
} ;
227
- let idx = entries. insert ( symbol. name . span ( ) , & value, & mut errors) ;
228
-
229
- prefill_stream. extend ( quote ! {
230
- #value,
231
- } ) ;
242
+ let idx = entries. insert ( symbol. name . span ( ) , value, & mut errors) ;
232
243
symbols_stream. extend ( quote ! {
233
244
pub const #name: Symbol = Symbol :: new( #idx) ;
234
245
} ) ;
235
246
}
236
247
237
248
// Generate symbols for the strings "0", "1", ..., "9".
238
249
for n in 0 ..10 {
239
- let n = n. to_string ( ) ;
240
- entries. insert ( Span :: call_site ( ) , & n, & mut errors) ;
241
- prefill_stream. extend ( quote ! {
242
- #n,
243
- } ) ;
250
+ entries. insert ( Span :: call_site ( ) , n. to_string ( ) , & mut errors) ;
244
251
}
245
252
246
253
// Symbols whose value comes from an environment variable. It's allowed for
@@ -267,23 +274,16 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
267
274
}
268
275
} ;
269
276
270
- let idx = if let Some ( prev) = entries. map . get ( & value) {
271
- prev. idx
272
- } else {
273
- prefill_stream. extend ( quote ! {
274
- #value,
275
- } ) ;
276
- entries. insert ( symbol. name . span ( ) , & value, & mut errors)
277
- } ;
278
-
279
277
let name = & symbol. name ;
278
+ let ( idx, _) = entries. try_insert ( name. span ( ) , value) ;
280
279
symbols_stream. extend ( quote ! {
281
280
pub const #name: Symbol = Symbol :: new( #idx) ;
282
281
} ) ;
283
282
}
284
283
285
284
let symbol_digits_base = entries. map [ "0" ] . idx ;
286
285
let predefined_symbols_count = entries. len ( ) ;
286
+ let prefill_stream = entries. prefill_stream ;
287
287
let output = quote ! {
288
288
const SYMBOL_DIGITS_BASE : u32 = #symbol_digits_base;
289
289
@@ -309,14 +309,124 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
309
309
impl Interner {
310
310
/// Creates an `Interner` with the predefined symbols from the `symbols!` macro and
311
311
/// any extra symbols provided by external drivers such as Clippy
312
- pub ( crate ) fn with_extra_symbols ( extra_symbols : & [ & ' static str ] ) -> Self {
312
+ pub ( crate ) fn new ( driver_symbols : Option < & [ & ' static str ] > ) -> Self {
313
313
Interner :: prefill(
314
- & [ #prefill_stream] ,
315
- extra_symbols,
314
+ driver_symbols. unwrap_or( & [ #prefill_stream] )
316
315
)
317
316
}
318
317
}
318
+
319
+ /// Allows drivers to define extra preinterned symbols, the expanded `PREINTERNED_SYMBOLS`
320
+ /// is to be provided to `rustc_interface::Config`
321
+ #[ macro_export]
322
+ macro_rules! extra_symbols {
323
+ ( $( #[ macro_export] $further_symbols: ident; ) ? Symbols { $( $tt: tt) * } ) => {
324
+ rustc_macros:: extra_symbols_impl! {
325
+ $( $further_symbols) ? [ #prefill_stream] $( $tt) *
326
+ }
327
+ } ;
328
+ }
319
329
} ;
320
330
321
331
( output, errors. list )
322
332
}
333
+
334
+ #[ derive( Default ) ]
335
+ struct ExtraSymbols {
336
+ macro_ident : Option < Ident > ,
337
+ predefined : Punctuated < LitStr , Token ! [ , ] > ,
338
+ extra_symbols : Punctuated < Symbol , Token ! [ , ] > ,
339
+ }
340
+
341
+ impl Parse for ExtraSymbols {
342
+ fn parse ( input : ParseStream < ' _ > ) -> Result < Self > {
343
+ let macro_ident: Option < Ident > = input. parse ( ) ?;
344
+
345
+ let content;
346
+ bracketed ! ( content in input) ;
347
+ let predefined = Punctuated :: parse_terminated ( & content) ?;
348
+
349
+ let extra_symbols = Punctuated :: parse_terminated ( & input) ?;
350
+
351
+ Ok ( ExtraSymbols { macro_ident, predefined, extra_symbols } )
352
+ }
353
+ }
354
+
355
+ pub ( super ) fn extra_symbols ( input : TokenStream ) -> TokenStream {
356
+ let mut errors = Errors :: default ( ) ;
357
+
358
+ let input: ExtraSymbols = match syn:: parse2 ( input) {
359
+ Ok ( input) => input,
360
+ Err ( e) => {
361
+ errors. list . push ( e) ;
362
+ Default :: default ( )
363
+ }
364
+ } ;
365
+
366
+ let mut symbol_stream = TokenStream :: new ( ) ;
367
+ let mut duplicate_symbols = TokenStream :: new ( ) ;
368
+
369
+ let mut entries = Entries :: with_capacity ( input. predefined . len ( ) + input. extra_symbols . len ( ) ) ;
370
+ for lit in & input. predefined {
371
+ entries. insert ( lit. span ( ) , lit. value ( ) , & mut errors) ;
372
+ }
373
+
374
+ for symbol in input. extra_symbols {
375
+ let value = match symbol. value {
376
+ Value :: SameAsName => symbol. name . to_string ( ) ,
377
+ Value :: String ( lit) => lit. value ( ) ,
378
+ _ => {
379
+ errors. error (
380
+ symbol. name . span ( ) ,
381
+ "unsupported expression for extra symbol value" . to_string ( ) ,
382
+ ) ;
383
+ continue ;
384
+ }
385
+ } ;
386
+
387
+ let name = & symbol. name ;
388
+ let ( idx, duplicate) = entries. try_insert ( name. span ( ) , value) ;
389
+ if duplicate. is_some ( ) {
390
+ duplicate_symbols. extend ( quote ! { #name, } ) ;
391
+ }
392
+ symbol_stream. extend ( quote ! {
393
+ pub const #name: Symbol = Symbol :: new( #idx) ;
394
+ } ) ;
395
+ }
396
+
397
+ let prefill_stream = entries. prefill_stream ;
398
+
399
+ let further_symbols = if let Some ( macro_ident) = input. macro_ident {
400
+ quote ! {
401
+ #[ macro_export]
402
+ macro_rules! #macro_ident {
403
+ ( $( #[ macro_export] $further_symbols: ident; ) ? Symbols { $( $tt: tt) * } ) => {
404
+ rustc_macros:: extra_symbols_impl! {
405
+ $( $further_symbols) ? [ #prefill_stream] $( $tt) *
406
+ }
407
+ } ;
408
+ }
409
+ }
410
+ } else {
411
+ TokenStream :: new ( )
412
+ } ;
413
+
414
+ let mut output = quote ! {
415
+ /// To be supplied to `rustc_interface::Config`
416
+ pub const PREINTERNED_SYMBOLS : & [ & str ] = & [
417
+ #prefill_stream
418
+ ] ;
419
+
420
+ pub const DUPLICATE_SYMBOLS : & [ Symbol ] = & [
421
+ #duplicate_symbols
422
+ ] ;
423
+
424
+ #symbol_stream
425
+
426
+ #further_symbols
427
+ } ;
428
+
429
+ output. extend ( errors. list . into_iter ( ) . map ( |e| e. into_compile_error ( ) ) ) ;
430
+
431
+ output
432
+ }
0 commit comments