1 use proc_macro::TokenStream;
2 use proc_macro2::{Delimiter, TokenTree};
3 use quote::quote;
4 use syn::parse::{Parse, ParseStream, Result};
5 use syn::punctuated::Punctuated;
6 use syn::spanned::Spanned;
7 use syn::{
8     braced, parenthesized, parse_macro_input, parse_quote, AttrStyle, Attribute, Block, Error,
9     Expr, Ident, ReturnType, Token, Type,
10 };
11 
12 mod kw {
13     syn::custom_keyword!(query);
14 }
15 
16 /// Ident or a wildcard `_`.
17 struct IdentOrWild(Ident);
18 
19 impl Parse for IdentOrWild {
parse(input: ParseStream<'_>) -> Result<Self>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, 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,
46 
47     /// A cycle error results in a delay_bug call
48     CycleDelayBug,
49 
50     /// Don't hash the result, instead just mark a query red if it runs
51     NoHash,
52 
53     /// Generate a dep node based on the dependencies of the query
54     Anon,
55 
56     /// Always evaluate the query, ignoring its dependencies
57     EvalAlways,
58 }
59 
60 impl Parse for QueryModifier {
parse(input: ParseStream<'_>) -> Result<Self>61     fn parse(input: ParseStream<'_>) -> Result<Self> {
62         let modifier: Ident = input.parse()?;
63         if modifier == "desc" {
64             // Parse a description modifier like:
65             // `desc { |tcx| "foo {}", tcx.item_path(key) }`
66             let attr_content;
67             braced!(attr_content in input);
68             let tcx = if attr_content.peek(Token![|]) {
69                 attr_content.parse::<Token![|]>()?;
70                 let tcx = attr_content.parse()?;
71                 attr_content.parse::<Token![|]>()?;
72                 Some(tcx)
73             } else {
74                 None
75             };
76             let desc = attr_content.parse_terminated(Expr::parse)?;
77             Ok(QueryModifier::Desc(tcx, desc))
78         } else if modifier == "cache_on_disk_if" {
79             // Parse a cache modifier like:
80             // `cache(tcx, value) { |tcx| key.is_local() }`
81             let has_args = if let TokenTree::Group(group) = input.fork().parse()? {
82                 group.delimiter() == Delimiter::Parenthesis
83             } else {
84                 false
85             };
86             let args = if has_args {
87                 let args;
88                 parenthesized!(args in input);
89                 let tcx = args.parse()?;
90                 args.parse::<Token![,]>()?;
91                 let value = args.parse()?;
92                 Some((tcx, value))
93             } else {
94                 None
95             };
96             let block = input.parse()?;
97             Ok(QueryModifier::Cache(args, block))
98         } else if modifier == "load_cached" {
99             // Parse a load_cached modifier like:
100             // `load_cached(tcx, id) { tcx.on_disk_cache.try_load_query_result(tcx, id) }`
101             let args;
102             parenthesized!(args in input);
103             let tcx = args.parse()?;
104             args.parse::<Token![,]>()?;
105             let id = args.parse()?;
106             let block = input.parse()?;
107             Ok(QueryModifier::LoadCached(tcx, id, block))
108         } else if modifier == "storage" {
109             let args;
110             parenthesized!(args in input);
111             let ty = args.parse()?;
112             Ok(QueryModifier::Storage(ty))
113         } else if modifier == "fatal_cycle" {
114             Ok(QueryModifier::FatalCycle)
115         } else if modifier == "cycle_delay_bug" {
116             Ok(QueryModifier::CycleDelayBug)
117         } else if modifier == "no_hash" {
118             Ok(QueryModifier::NoHash)
119         } else if modifier == "anon" {
120             Ok(QueryModifier::Anon)
121         } else if modifier == "eval_always" {
122             Ok(QueryModifier::EvalAlways)
123         } else {
124             Err(Error::new(modifier.span(), "unknown query modifier"))
125         }
126     }
127 }
128 
129 /// Ensures only doc comment attributes are used
check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>>130 fn check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>> {
131     let inner = |attr: Attribute| {
132         if !attr.path.is_ident("doc") {
133             Err(Error::new(attr.span(), "attributes not supported on queries"))
134         } else if attr.style != AttrStyle::Outer {
135             Err(Error::new(
136                 attr.span(),
137                 "attributes must be outer attributes (`///`), not inner attributes",
138             ))
139         } else {
140             Ok(attr)
141         }
142     };
143     attrs.into_iter().map(inner).collect()
144 }
145 
146 /// A compiler query. `query ... { ... }`
147 struct Query {
148     doc_comments: Vec<Attribute>,
149     modifiers: List<QueryModifier>,
150     name: Ident,
151     key: IdentOrWild,
152     arg: Type,
153     result: ReturnType,
154 }
155 
156 impl Parse for Query {
parse(input: ParseStream<'_>) -> Result<Self>157     fn parse(input: ParseStream<'_>) -> Result<Self> {
158         let doc_comments = check_attributes(input.call(Attribute::parse_outer)?)?;
159 
160         // Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
161         input.parse::<kw::query>()?;
162         let name: Ident = input.parse()?;
163         let arg_content;
164         parenthesized!(arg_content in input);
165         let key = arg_content.parse()?;
166         arg_content.parse::<Token![:]>()?;
167         let arg = arg_content.parse()?;
168         let result = input.parse()?;
169 
170         // Parse the query modifiers
171         let content;
172         braced!(content in input);
173         let modifiers = content.parse()?;
174 
175         Ok(Query { doc_comments, modifiers, name, key, arg, result })
176     }
177 }
178 
179 /// A type used to greedily parse another type until the input is empty.
180 struct List<T>(Vec<T>);
181 
182 impl<T: Parse> Parse for List<T> {
parse(input: ParseStream<'_>) -> Result<Self>183     fn parse(input: ParseStream<'_>) -> Result<Self> {
184         let mut list = Vec::new();
185         while !input.is_empty() {
186             list.push(input.parse()?);
187         }
188         Ok(List(list))
189     }
190 }
191 
192 struct QueryModifiers {
193     /// The description of the query.
194     desc: (Option<Ident>, Punctuated<Expr, Token![,]>),
195 
196     /// Use this type for the in-memory cache.
197     storage: Option<Type>,
198 
199     /// Cache the query to disk if the `Block` returns true.
200     cache: Option<(Option<(IdentOrWild, IdentOrWild)>, Block)>,
201 
202     /// Custom code to load the query from disk.
203     load_cached: Option<(Ident, Ident, Block)>,
204 
205     /// A cycle error for this query aborting the compilation with a fatal error.
206     fatal_cycle: bool,
207 
208     /// A cycle error results in a delay_bug call
209     cycle_delay_bug: bool,
210 
211     /// Don't hash the result, instead just mark a query red if it runs
212     no_hash: bool,
213 
214     /// Generate a dep node based on the dependencies of the query
215     anon: bool,
216 
217     // Always evaluate the query, ignoring its dependencies
218     eval_always: bool,
219 }
220 
221 /// Process query modifiers into a struct, erroring on duplicates
process_modifiers(query: &mut Query) -> QueryModifiers222 fn process_modifiers(query: &mut Query) -> QueryModifiers {
223     let mut load_cached = None;
224     let mut storage = None;
225     let mut cache = None;
226     let mut desc = None;
227     let mut fatal_cycle = false;
228     let mut cycle_delay_bug = false;
229     let mut no_hash = false;
230     let mut anon = false;
231     let mut eval_always = false;
232     for modifier in query.modifiers.0.drain(..) {
233         match modifier {
234             QueryModifier::LoadCached(tcx, id, block) => {
235                 if load_cached.is_some() {
236                     panic!("duplicate modifier `load_cached` for query `{}`", query.name);
237                 }
238                 load_cached = Some((tcx, id, block));
239             }
240             QueryModifier::Storage(ty) => {
241                 if storage.is_some() {
242                     panic!("duplicate modifier `storage` for query `{}`", query.name);
243                 }
244                 storage = Some(ty);
245             }
246             QueryModifier::Cache(args, expr) => {
247                 if cache.is_some() {
248                     panic!("duplicate modifier `cache` for query `{}`", query.name);
249                 }
250                 cache = Some((args, expr));
251             }
252             QueryModifier::Desc(tcx, list) => {
253                 if desc.is_some() {
254                     panic!("duplicate modifier `desc` for query `{}`", query.name);
255                 }
256                 // If there are no doc-comments, give at least some idea of what
257                 // it does by showing the query description.
258                 if query.doc_comments.is_empty() {
259                     use ::syn::*;
260                     let mut list = list.iter();
261                     let format_str: String = match list.next() {
262                         Some(&Expr::Lit(ExprLit { lit: Lit::Str(ref lit_str), .. })) => {
263                             lit_str.value().replace("`{}`", "{}") // We add them later anyways for consistency
264                         }
265                         _ => panic!("Expected a string literal"),
266                     };
267                     let mut fmt_fragments = format_str.split("{}");
268                     let mut doc_string = fmt_fragments.next().unwrap().to_string();
269                     list.map(::quote::ToTokens::to_token_stream).zip(fmt_fragments).for_each(
270                         |(tts, next_fmt_fragment)| {
271                             use ::core::fmt::Write;
272                             write!(
273                                 &mut doc_string,
274                                 " `{}` {}",
275                                 tts.to_string().replace(" . ", "."),
276                                 next_fmt_fragment,
277                             )
278                             .unwrap();
279                         },
280                     );
281                     let doc_string = format!(
282                         "[query description - consider adding a doc-comment!] {}",
283                         doc_string
284                     );
285                     let comment = parse_quote! {
286                         #[doc = #doc_string]
287                     };
288                     query.doc_comments.push(comment);
289                 }
290                 desc = Some((tcx, list));
291             }
292             QueryModifier::FatalCycle => {
293                 if fatal_cycle {
294                     panic!("duplicate modifier `fatal_cycle` for query `{}`", query.name);
295                 }
296                 fatal_cycle = true;
297             }
298             QueryModifier::CycleDelayBug => {
299                 if cycle_delay_bug {
300                     panic!("duplicate modifier `cycle_delay_bug` for query `{}`", query.name);
301                 }
302                 cycle_delay_bug = true;
303             }
304             QueryModifier::NoHash => {
305                 if no_hash {
306                     panic!("duplicate modifier `no_hash` for query `{}`", query.name);
307                 }
308                 no_hash = true;
309             }
310             QueryModifier::Anon => {
311                 if anon {
312                     panic!("duplicate modifier `anon` for query `{}`", query.name);
313                 }
314                 anon = true;
315             }
316             QueryModifier::EvalAlways => {
317                 if eval_always {
318                     panic!("duplicate modifier `eval_always` for query `{}`", query.name);
319                 }
320                 eval_always = true;
321             }
322         }
323     }
324     let desc = desc.unwrap_or_else(|| {
325         panic!("no description provided for query `{}`", query.name);
326     });
327     QueryModifiers {
328         load_cached,
329         storage,
330         cache,
331         desc,
332         fatal_cycle,
333         cycle_delay_bug,
334         no_hash,
335         anon,
336         eval_always,
337     }
338 }
339 
340 /// Add the impl of QueryDescription for the query to `impls` if one is requested
add_query_description_impl( query: &Query, modifiers: QueryModifiers, impls: &mut proc_macro2::TokenStream, )341 fn add_query_description_impl(
342     query: &Query,
343     modifiers: QueryModifiers,
344     impls: &mut proc_macro2::TokenStream,
345 ) {
346     let name = &query.name;
347     let key = &query.key.0;
348 
349     // Find out if we should cache the query on disk
350     let cache = if let Some((args, expr)) = modifiers.cache.as_ref() {
351         let try_load_from_disk = if let Some((tcx, id, block)) = modifiers.load_cached.as_ref() {
352             // Use custom code to load the query from disk
353             quote! {
354                 #[inline]
355                 fn try_load_from_disk(
356                     #tcx: QueryCtxt<'tcx>,
357                     #id: SerializedDepNodeIndex
358                 ) -> Option<Self::Value> {
359                     #block
360                 }
361             }
362         } else {
363             // Use the default code to load the query from disk
364             quote! {
365                 #[inline]
366                 fn try_load_from_disk(
367                     tcx: QueryCtxt<'tcx>,
368                     id: SerializedDepNodeIndex
369                 ) -> Option<Self::Value> {
370                     tcx.on_disk_cache.as_ref()?.try_load_query_result(*tcx, id)
371                 }
372             }
373         };
374 
375         let tcx = args
376             .as_ref()
377             .map(|t| {
378                 let t = &(t.0).0;
379                 quote! { #t }
380             })
381             .unwrap_or_else(|| quote! { _ });
382         let value = args
383             .as_ref()
384             .map(|t| {
385                 let t = &(t.1).0;
386                 quote! { #t }
387             })
388             .unwrap_or_else(|| quote! { _ });
389         // expr is a `Block`, meaning that `{ #expr }` gets expanded
390         // to `{ { stmts... } }`, which triggers the `unused_braces` lint.
391         quote! {
392             #[inline]
393             #[allow(unused_variables, unused_braces)]
394             fn cache_on_disk(
395                 #tcx: QueryCtxt<'tcx>,
396                 #key: &Self::Key,
397                 #value: Option<&Self::Value>
398             ) -> bool {
399                 #expr
400             }
401 
402             #try_load_from_disk
403         }
404     } else {
405         if modifiers.load_cached.is_some() {
406             panic!("load_cached modifier on query `{}` without a cache modifier", name);
407         }
408         quote! {}
409     };
410 
411     let (tcx, desc) = modifiers.desc;
412     let tcx = tcx.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t });
413 
414     let desc = quote! {
415         #[allow(unused_variables)]
416         fn describe(tcx: QueryCtxt<'tcx>, key: Self::Key) -> String {
417             let (#tcx, #key) = (*tcx, key);
418             ::rustc_middle::ty::print::with_no_trimmed_paths(|| format!(#desc).into())
419         }
420     };
421 
422     impls.extend(quote! {
423         impl<'tcx> QueryDescription<QueryCtxt<'tcx>> for queries::#name<'tcx> {
424             #desc
425             #cache
426         }
427     });
428 }
429 
rustc_queries(input: TokenStream) -> TokenStream430 pub fn rustc_queries(input: TokenStream) -> TokenStream {
431     let queries = parse_macro_input!(input as List<Query>);
432 
433     let mut query_stream = quote! {};
434     let mut query_description_stream = quote! {};
435     let mut dep_node_def_stream = quote! {};
436     let mut cached_queries = quote! {};
437 
438     for mut query in queries.0 {
439         let modifiers = process_modifiers(&mut query);
440         let name = &query.name;
441         let arg = &query.arg;
442         let result_full = &query.result;
443         let result = match query.result {
444             ReturnType::Default => quote! { -> () },
445             _ => quote! { #result_full },
446         };
447 
448         if modifiers.cache.is_some() {
449             cached_queries.extend(quote! {
450                 #name,
451             });
452         }
453 
454         let mut attributes = Vec::new();
455 
456         // Pass on the fatal_cycle modifier
457         if modifiers.fatal_cycle {
458             attributes.push(quote! { fatal_cycle });
459         };
460         // Pass on the storage modifier
461         if let Some(ref ty) = modifiers.storage {
462             attributes.push(quote! { storage(#ty) });
463         };
464         // Pass on the cycle_delay_bug modifier
465         if modifiers.cycle_delay_bug {
466             attributes.push(quote! { cycle_delay_bug });
467         };
468         // Pass on the no_hash modifier
469         if modifiers.no_hash {
470             attributes.push(quote! { no_hash });
471         };
472         // Pass on the anon modifier
473         if modifiers.anon {
474             attributes.push(quote! { anon });
475         };
476         // Pass on the eval_always modifier
477         if modifiers.eval_always {
478             attributes.push(quote! { eval_always });
479         };
480 
481         let attribute_stream = quote! {#(#attributes),*};
482         let doc_comments = query.doc_comments.iter();
483         // Add the query to the group
484         query_stream.extend(quote! {
485             #(#doc_comments)*
486             [#attribute_stream] fn #name(#arg) #result,
487         });
488 
489         // Create a dep node for the query
490         dep_node_def_stream.extend(quote! {
491             [#attribute_stream] #name(#arg),
492         });
493 
494         add_query_description_impl(&query, modifiers, &mut query_description_stream);
495     }
496 
497     TokenStream::from(quote! {
498         #[macro_export]
499         macro_rules! rustc_query_append {
500             ([$($macro:tt)*][$($other:tt)*]) => {
501                 $($macro)* {
502                     $($other)*
503 
504                     #query_stream
505 
506                 }
507             }
508         }
509         macro_rules! rustc_dep_node_append {
510             ([$($macro:tt)*][$($other:tt)*]) => {
511                 $($macro)*(
512                     $($other)*
513 
514                     #dep_node_def_stream
515                 );
516             }
517         }
518         #[macro_export]
519         macro_rules! rustc_cached_queries {
520             ($($macro:tt)*) => {
521                 $($macro)*(#cached_queries);
522             }
523         }
524         #[macro_export]
525         macro_rules! rustc_query_description {
526             () => { #query_description_stream }
527         }
528     })
529 }
530