1
1
use proc_macro:: TokenStream ;
2
- use proc_macro2:: { Delimiter , TokenTree } ;
3
2
use quote:: { quote, quote_spanned} ;
4
3
use syn:: parse:: { Parse , ParseStream , Result } ;
5
4
use syn:: punctuated:: Punctuated ;
6
5
use syn:: spanned:: Spanned ;
7
6
use syn:: {
8
- braced, parenthesized, parse_macro_input, parse_quote, AttrStyle , Attribute , Block , Error ,
9
- Expr , Ident , ReturnType , Token , Type ,
7
+ braced, parenthesized, parse_macro_input, parse_quote, token , AttrStyle , Attribute , Block ,
8
+ Error , Expr , Ident , Pat , ReturnType , Token , Type ,
10
9
} ;
11
10
12
11
mod kw {
13
12
syn:: custom_keyword!( query) ;
14
13
}
15
14
16
- /// Ident or a wildcard `_`.
17
- struct IdentOrWild ( Ident ) ;
18
-
19
- impl Parse for IdentOrWild {
20
- fn parse ( input : ParseStream < ' _ > ) -> Result < Self > {
21
- Ok ( if input. peek ( Token ! [ _] ) {
22
- let underscore = input. parse :: < Token ! [ _] > ( ) ?;
23
- IdentOrWild ( Ident :: new ( "_" , underscore. span ( ) ) )
24
- } else {
25
- IdentOrWild ( input. parse ( ) ?)
26
- } )
27
- }
28
- }
29
-
30
- /// A modifier for a query
31
- enum QueryModifier {
32
- /// The description of the query.
33
- Desc ( Option < Ident > , Punctuated < Expr , Token ! [ , ] > ) ,
34
-
35
- /// Use this type for the in-memory cache.
36
- Storage ( Type ) ,
37
-
38
- /// Cache the query to disk if the `Expr` returns true.
39
- Cache ( Option < IdentOrWild > , Block ) ,
40
-
41
- /// Custom code to load the query from disk.
42
- LoadCached ( Ident , Ident , Block ) ,
43
-
44
- /// A cycle error for this query aborting the compilation with a fatal error.
45
- FatalCycle ( Ident ) ,
46
-
47
- /// A cycle error results in a delay_bug call
48
- CycleDelayBug ( Ident ) ,
49
-
50
- /// Don't hash the result, instead just mark a query red if it runs
51
- NoHash ( Ident ) ,
52
-
53
- /// Generate a dep node based on the dependencies of the query
54
- Anon ( Ident ) ,
55
-
56
- /// Always evaluate the query, ignoring its dependencies
57
- EvalAlways ( Ident ) ,
58
-
59
- /// Use a separate query provider for local and extern crates
60
- SeparateProvideExtern ( Ident ) ,
61
-
62
- /// Always remap the ParamEnv's constness before hashing and passing to the query provider
63
- RemapEnvConstness ( Ident ) ,
64
- }
65
-
66
- impl Parse for QueryModifier {
67
- fn parse ( input : ParseStream < ' _ > ) -> Result < Self > {
68
- let modifier: Ident = input. parse ( ) ?;
69
- if modifier == "desc" {
70
- // Parse a description modifier like:
71
- // `desc { |tcx| "foo {}", tcx.item_path(key) }`
72
- let attr_content;
73
- braced ! ( attr_content in input) ;
74
- let tcx = if attr_content. peek ( Token ! [ |] ) {
75
- attr_content. parse :: < Token ! [ |] > ( ) ?;
76
- let tcx = attr_content. parse ( ) ?;
77
- attr_content. parse :: < Token ! [ |] > ( ) ?;
78
- Some ( tcx)
79
- } else {
80
- None
81
- } ;
82
- let desc = attr_content. parse_terminated ( Expr :: parse) ?;
83
- Ok ( QueryModifier :: Desc ( tcx, desc) )
84
- } else if modifier == "cache_on_disk_if" {
85
- // Parse a cache modifier like:
86
- // `cache(tcx, value) { |tcx| key.is_local() }`
87
- let has_args = if let TokenTree :: Group ( group) = input. fork ( ) . parse ( ) ? {
88
- group. delimiter ( ) == Delimiter :: Parenthesis
89
- } else {
90
- false
91
- } ;
92
- let args = if has_args {
93
- let args;
94
- parenthesized ! ( args in input) ;
95
- let tcx = args. parse ( ) ?;
96
- Some ( tcx)
97
- } else {
98
- None
99
- } ;
100
- let block = input. parse ( ) ?;
101
- Ok ( QueryModifier :: Cache ( args, block) )
102
- } else if modifier == "load_cached" {
103
- // Parse a load_cached modifier like:
104
- // `load_cached(tcx, id) { tcx.on_disk_cache.try_load_query_result(tcx, id) }`
105
- let args;
106
- parenthesized ! ( args in input) ;
107
- let tcx = args. parse ( ) ?;
108
- args. parse :: < Token ! [ , ] > ( ) ?;
109
- let id = args. parse ( ) ?;
110
- let block = input. parse ( ) ?;
111
- Ok ( QueryModifier :: LoadCached ( tcx, id, block) )
112
- } else if modifier == "storage" {
113
- let args;
114
- parenthesized ! ( args in input) ;
115
- let ty = args. parse ( ) ?;
116
- Ok ( QueryModifier :: Storage ( ty) )
117
- } else if modifier == "fatal_cycle" {
118
- Ok ( QueryModifier :: FatalCycle ( modifier) )
119
- } else if modifier == "cycle_delay_bug" {
120
- Ok ( QueryModifier :: CycleDelayBug ( modifier) )
121
- } else if modifier == "no_hash" {
122
- Ok ( QueryModifier :: NoHash ( modifier) )
123
- } else if modifier == "anon" {
124
- Ok ( QueryModifier :: Anon ( modifier) )
125
- } else if modifier == "eval_always" {
126
- Ok ( QueryModifier :: EvalAlways ( modifier) )
127
- } else if modifier == "separate_provide_extern" {
128
- Ok ( QueryModifier :: SeparateProvideExtern ( modifier) )
129
- } else if modifier == "remap_env_constness" {
130
- Ok ( QueryModifier :: RemapEnvConstness ( modifier) )
131
- } else {
132
- Err ( Error :: new ( modifier. span ( ) , "unknown query modifier" ) )
133
- }
134
- }
135
- }
136
-
137
15
/// Ensures only doc comment attributes are used
138
16
fn check_attributes ( attrs : Vec < Attribute > ) -> Result < Vec < Attribute > > {
139
17
let inner = |attr : Attribute | {
@@ -154,16 +32,16 @@ fn check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>> {
154
32
/// A compiler query. `query ... { ... }`
155
33
struct Query {
156
34
doc_comments : Vec < Attribute > ,
157
- modifiers : List < QueryModifier > ,
35
+ modifiers : QueryModifiers ,
158
36
name : Ident ,
159
- key : IdentOrWild ,
37
+ key : Pat ,
160
38
arg : Type ,
161
39
result : ReturnType ,
162
40
}
163
41
164
42
impl Parse for Query {
165
43
fn parse ( input : ParseStream < ' _ > ) -> Result < Self > {
166
- let doc_comments = check_attributes ( input. call ( Attribute :: parse_outer) ?) ?;
44
+ let mut doc_comments = check_attributes ( input. call ( Attribute :: parse_outer) ?) ?;
167
45
168
46
// Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
169
47
input. parse :: < kw:: query > ( ) ?;
@@ -178,7 +56,13 @@ impl Parse for Query {
178
56
// Parse the query modifiers
179
57
let content;
180
58
braced ! ( content in input) ;
181
- let modifiers = content. parse ( ) ?;
59
+ let modifiers = parse_query_modifiers ( & content) ?;
60
+
61
+ // If there are no doc-comments, give at least some idea of what
62
+ // it does by showing the query description.
63
+ if doc_comments. is_empty ( ) {
64
+ doc_comments. push ( doc_comment_from_desc ( & modifiers. desc . 1 ) ?) ;
65
+ }
182
66
183
67
Ok ( Query { doc_comments, modifiers, name, key, arg, result } )
184
68
}
@@ -205,7 +89,7 @@ struct QueryModifiers {
205
89
storage : Option < Type > ,
206
90
207
91
/// Cache the query to disk if the `Block` returns true.
208
- cache : Option < ( Option < IdentOrWild > , Block ) > ,
92
+ cache : Option < ( Option < Pat > , Block ) > ,
209
93
210
94
/// Custom code to load the query from disk.
211
95
load_cached : Option < ( Ident , Ident , Block ) > ,
@@ -232,8 +116,7 @@ struct QueryModifiers {
232
116
remap_env_constness : Option < Ident > ,
233
117
}
234
118
235
- /// Process query modifiers into a struct, erroring on duplicates
236
- fn process_modifiers ( query : & mut Query ) -> QueryModifiers {
119
+ fn parse_query_modifiers ( input : ParseStream < ' _ > ) -> Result < QueryModifiers > {
237
120
let mut load_cached = None ;
238
121
let mut storage = None ;
239
122
let mut cache = None ;
@@ -245,117 +128,84 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers {
245
128
let mut eval_always = None ;
246
129
let mut separate_provide_extern = None ;
247
130
let mut remap_env_constness = None ;
248
- for modifier in query. modifiers . 0 . drain ( ..) {
249
- match modifier {
250
- QueryModifier :: LoadCached ( tcx, id, block) => {
251
- if load_cached. is_some ( ) {
252
- panic ! ( "duplicate modifier `load_cached` for query `{}`" , query. name) ;
253
- }
254
- load_cached = Some ( ( tcx, id, block) ) ;
255
- }
256
- QueryModifier :: Storage ( ty) => {
257
- if storage. is_some ( ) {
258
- panic ! ( "duplicate modifier `storage` for query `{}`" , query. name) ;
259
- }
260
- storage = Some ( ty) ;
261
- }
262
- QueryModifier :: Cache ( args, expr) => {
263
- if cache. is_some ( ) {
264
- panic ! ( "duplicate modifier `cache` for query `{}`" , query. name) ;
265
- }
266
- cache = Some ( ( args, expr) ) ;
267
- }
268
- QueryModifier :: Desc ( tcx, list) => {
269
- if desc. is_some ( ) {
270
- panic ! ( "duplicate modifier `desc` for query `{}`" , query. name) ;
271
- }
272
- // If there are no doc-comments, give at least some idea of what
273
- // it does by showing the query description.
274
- if query. doc_comments . is_empty ( ) {
275
- use :: syn:: * ;
276
- let mut list = list. iter ( ) ;
277
- let format_str: String = match list. next ( ) {
278
- Some ( & Expr :: Lit ( ExprLit { lit : Lit :: Str ( ref lit_str) , .. } ) ) => {
279
- lit_str. value ( ) . replace ( "`{}`" , "{}" ) // We add them later anyways for consistency
280
- }
281
- _ => panic ! ( "Expected a string literal" ) ,
282
- } ;
283
- let mut fmt_fragments = format_str. split ( "{}" ) ;
284
- let mut doc_string = fmt_fragments. next ( ) . unwrap ( ) . to_string ( ) ;
285
- list. map ( :: quote:: ToTokens :: to_token_stream) . zip ( fmt_fragments) . for_each (
286
- |( tts, next_fmt_fragment) | {
287
- use :: core:: fmt:: Write ;
288
- write ! (
289
- & mut doc_string,
290
- " `{}` {}" ,
291
- tts. to_string( ) . replace( " . " , "." ) ,
292
- next_fmt_fragment,
293
- )
294
- . unwrap ( ) ;
295
- } ,
296
- ) ;
297
- let doc_string = format ! (
298
- "[query description - consider adding a doc-comment!] {}" ,
299
- doc_string
300
- ) ;
301
- let comment = parse_quote ! {
302
- #[ doc = #doc_string]
303
- } ;
304
- query. doc_comments . push ( comment) ;
305
- }
306
- desc = Some ( ( tcx, list) ) ;
307
- }
308
- QueryModifier :: FatalCycle ( ident) => {
309
- if fatal_cycle. is_some ( ) {
310
- panic ! ( "duplicate modifier `fatal_cycle` for query `{}`" , query. name) ;
311
- }
312
- fatal_cycle = Some ( ident) ;
313
- }
314
- QueryModifier :: CycleDelayBug ( ident) => {
315
- if cycle_delay_bug. is_some ( ) {
316
- panic ! ( "duplicate modifier `cycle_delay_bug` for query `{}`" , query. name) ;
317
- }
318
- cycle_delay_bug = Some ( ident) ;
319
- }
320
- QueryModifier :: NoHash ( ident) => {
321
- if no_hash. is_some ( ) {
322
- panic ! ( "duplicate modifier `no_hash` for query `{}`" , query. name) ;
323
- }
324
- no_hash = Some ( ident) ;
325
- }
326
- QueryModifier :: Anon ( ident) => {
327
- if anon. is_some ( ) {
328
- panic ! ( "duplicate modifier `anon` for query `{}`" , query. name) ;
329
- }
330
- anon = Some ( ident) ;
331
- }
332
- QueryModifier :: EvalAlways ( ident) => {
333
- if eval_always. is_some ( ) {
334
- panic ! ( "duplicate modifier `eval_always` for query `{}`" , query. name) ;
335
- }
336
- eval_always = Some ( ident) ;
337
- }
338
- QueryModifier :: SeparateProvideExtern ( ident) => {
339
- if separate_provide_extern. is_some ( ) {
340
- panic ! (
341
- "duplicate modifier `separate_provide_extern` for query `{}`" ,
342
- query. name
343
- ) ;
344
- }
345
- separate_provide_extern = Some ( ident) ;
346
- }
347
- QueryModifier :: RemapEnvConstness ( ident) => {
348
- if remap_env_constness. is_some ( ) {
349
- panic ! ( "duplicate modifier `remap_env_constness` for query `{}`" , query. name) ;
131
+
132
+ while !input. is_empty ( ) {
133
+ let modifier: Ident = input. parse ( ) ?;
134
+
135
+ macro_rules! try_insert {
136
+ ( $name: ident = $expr: expr) => {
137
+ if $name. is_some( ) {
138
+ return Err ( Error :: new( modifier. span( ) , "duplicate modifier" ) ) ;
350
139
}
351
- remap_env_constness = Some ( ident)
352
- }
140
+ $name = Some ( $expr) ;
141
+ } ;
142
+ }
143
+
144
+ if modifier == "desc" {
145
+ // Parse a description modifier like:
146
+ // `desc { |tcx| "foo {}", tcx.item_path(key) }`
147
+ let attr_content;
148
+ braced ! ( attr_content in input) ;
149
+ let tcx = if attr_content. peek ( Token ! [ |] ) {
150
+ attr_content. parse :: < Token ! [ |] > ( ) ?;
151
+ let tcx = attr_content. parse ( ) ?;
152
+ attr_content. parse :: < Token ! [ |] > ( ) ?;
153
+ Some ( tcx)
154
+ } else {
155
+ None
156
+ } ;
157
+ let list = attr_content. parse_terminated ( Expr :: parse) ?;
158
+ try_insert ! ( desc = ( tcx, list) ) ;
159
+ } else if modifier == "cache_on_disk_if" {
160
+ // Parse a cache modifier like:
161
+ // `cache(tcx) { |tcx| key.is_local() }`
162
+ let args = if input. peek ( token:: Paren ) {
163
+ let args;
164
+ parenthesized ! ( args in input) ;
165
+ let tcx = args. parse ( ) ?;
166
+ Some ( tcx)
167
+ } else {
168
+ None
169
+ } ;
170
+ let block = input. parse ( ) ?;
171
+ try_insert ! ( cache = ( args, block) ) ;
172
+ } else if modifier == "load_cached" {
173
+ // Parse a load_cached modifier like:
174
+ // `load_cached(tcx, id) { tcx.on_disk_cache.try_load_query_result(tcx, id) }`
175
+ let args;
176
+ parenthesized ! ( args in input) ;
177
+ let tcx = args. parse ( ) ?;
178
+ args. parse :: < Token ! [ , ] > ( ) ?;
179
+ let id = args. parse ( ) ?;
180
+ let block = input. parse ( ) ?;
181
+ try_insert ! ( load_cached = ( tcx, id, block) ) ;
182
+ } else if modifier == "storage" {
183
+ let args;
184
+ parenthesized ! ( args in input) ;
185
+ let ty = args. parse ( ) ?;
186
+ try_insert ! ( storage = ty) ;
187
+ } else if modifier == "fatal_cycle" {
188
+ try_insert ! ( fatal_cycle = modifier) ;
189
+ } else if modifier == "cycle_delay_bug" {
190
+ try_insert ! ( cycle_delay_bug = modifier) ;
191
+ } else if modifier == "no_hash" {
192
+ try_insert ! ( no_hash = modifier) ;
193
+ } else if modifier == "anon" {
194
+ try_insert ! ( anon = modifier) ;
195
+ } else if modifier == "eval_always" {
196
+ try_insert ! ( eval_always = modifier) ;
197
+ } else if modifier == "separate_provide_extern" {
198
+ try_insert ! ( separate_provide_extern = modifier) ;
199
+ } else if modifier == "remap_env_constness" {
200
+ try_insert ! ( remap_env_constness = modifier) ;
201
+ } else {
202
+ return Err ( Error :: new ( modifier. span ( ) , "unknown query modifier" ) ) ;
353
203
}
354
204
}
355
- let desc = desc. unwrap_or_else ( || {
356
- panic ! ( "no description provided for query `{}`" , query . name ) ;
357
- } ) ;
358
- QueryModifiers {
205
+ let Some ( desc) = desc else {
206
+ return Err ( input . error ( "no description provided" ) ) ;
207
+ } ;
208
+ Ok ( QueryModifiers {
359
209
load_cached,
360
210
storage,
361
211
cache,
@@ -367,17 +217,41 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers {
367
217
eval_always,
368
218
separate_provide_extern,
369
219
remap_env_constness,
370
- }
220
+ } )
221
+ }
222
+
223
+ fn doc_comment_from_desc ( list : & Punctuated < Expr , token:: Comma > ) -> Result < Attribute > {
224
+ use :: syn:: * ;
225
+ let mut iter = list. iter ( ) ;
226
+ let format_str: String = match iter. next ( ) {
227
+ Some ( & Expr :: Lit ( ExprLit { lit : Lit :: Str ( ref lit_str) , .. } ) ) => {
228
+ lit_str. value ( ) . replace ( "`{}`" , "{}" ) // We add them later anyways for consistency
229
+ }
230
+ _ => return Err ( Error :: new ( list. span ( ) , "Expected a string literal" ) ) ,
231
+ } ;
232
+ let mut fmt_fragments = format_str. split ( "{}" ) ;
233
+ let mut doc_string = fmt_fragments. next ( ) . unwrap ( ) . to_string ( ) ;
234
+ iter. map ( :: quote:: ToTokens :: to_token_stream) . zip ( fmt_fragments) . for_each (
235
+ |( tts, next_fmt_fragment) | {
236
+ use :: core:: fmt:: Write ;
237
+ write ! (
238
+ & mut doc_string,
239
+ " `{}` {}" ,
240
+ tts. to_string( ) . replace( " . " , "." ) ,
241
+ next_fmt_fragment,
242
+ )
243
+ . unwrap ( ) ;
244
+ } ,
245
+ ) ;
246
+ let doc_string = format ! ( "[query description - consider adding a doc-comment!] {}" , doc_string) ;
247
+ Ok ( parse_quote ! { #[ doc = #doc_string] } )
371
248
}
372
249
373
250
/// Add the impl of QueryDescription for the query to `impls` if one is requested
374
- fn add_query_description_impl (
375
- query : & Query ,
376
- modifiers : QueryModifiers ,
377
- impls : & mut proc_macro2:: TokenStream ,
378
- ) {
251
+ fn add_query_description_impl ( query : & Query , impls : & mut proc_macro2:: TokenStream ) {
379
252
let name = & query. name ;
380
- let key = & query. key . 0 ;
253
+ let key = & query. key ;
254
+ let modifiers = & query. modifiers ;
381
255
382
256
// Find out if we should cache the query on disk
383
257
let cache = if let Some ( ( args, expr) ) = modifiers. cache . as_ref ( ) {
@@ -395,13 +269,7 @@ fn add_query_description_impl(
395
269
}
396
270
} ;
397
271
398
- let tcx = args
399
- . as_ref ( )
400
- . map ( |t| {
401
- let t = & t. 0 ;
402
- quote ! { #t }
403
- } )
404
- . unwrap_or_else ( || quote ! { _ } ) ;
272
+ let tcx = args. as_ref ( ) . map ( |t| quote ! { #t } ) . unwrap_or_else ( || quote ! { _ } ) ;
405
273
// expr is a `Block`, meaning that `{ #expr }` gets expanded
406
274
// to `{ { stmts... } }`, which triggers the `unused_braces` lint.
407
275
quote ! {
@@ -427,7 +295,7 @@ fn add_query_description_impl(
427
295
}
428
296
} ;
429
297
430
- let ( tcx, desc) = modifiers. desc ;
298
+ let ( tcx, desc) = & modifiers. desc ;
431
299
let tcx = tcx. as_ref ( ) . map_or_else ( || quote ! { _ } , |t| quote ! { #t } ) ;
432
300
433
301
let desc = quote ! {
@@ -456,10 +324,8 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
456
324
let mut dep_node_def_stream = quote ! { } ;
457
325
let mut cached_queries = quote ! { } ;
458
326
459
- for mut query in queries. 0 {
460
- let modifiers = process_modifiers ( & mut query) ;
461
- let name = & query. name ;
462
- let arg = & query. arg ;
327
+ for query in queries. 0 {
328
+ let Query { name, arg, modifiers, .. } = & query;
463
329
let result_full = & query. result ;
464
330
let result = match query. result {
465
331
ReturnType :: Default => quote ! { -> ( ) } ,
@@ -528,7 +394,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
528
394
[ #attribute_stream] #name( #arg) ,
529
395
} ) ;
530
396
531
- add_query_description_impl ( & query, modifiers , & mut query_description_stream) ;
397
+ add_query_description_impl ( & query, & mut query_description_stream) ;
532
398
}
533
399
534
400
TokenStream :: from ( quote ! {
@@ -539,7 +405,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
539
405
$( $other) *
540
406
541
407
#query_stream
542
-
543
408
}
544
409
}
545
410
}
0 commit comments