Skip to content

Commit fd93ab4

Browse files
authoredAug 23, 2022
Rollup merge of #100857 - camsteffen:query-parse-refactor, r=davidtwco
Refactor query modifier parsing Reduce redundancy and improve error spans.
·
1.88.01.65.0
2 parents 110d8d9 + c31f296 commit fd93ab4

File tree

1 file changed

+125
-260
lines changed

1 file changed

+125
-260
lines changed
 

‎compiler/rustc_macros/src/query.rs

Lines changed: 125 additions & 260 deletions
Original file line numberDiff line numberDiff line change
@@ -1,139 +1,17 @@
11
use proc_macro::TokenStream;
2-
use proc_macro2::{Delimiter, TokenTree};
32
use quote::{quote, quote_spanned};
43
use syn::parse::{Parse, ParseStream, Result};
54
use syn::punctuated::Punctuated;
65
use syn::spanned::Spanned;
76
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,
109
};
1110

1211
mod kw {
1312
syn::custom_keyword!(query);
1413
}
1514

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-
13715
/// Ensures only doc comment attributes are used
13816
fn check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>> {
13917
let inner = |attr: Attribute| {
@@ -154,16 +32,16 @@ fn check_attributes(attrs: Vec<Attribute>) -> Result<Vec<Attribute>> {
15432
/// A compiler query. `query ... { ... }`
15533
struct Query {
15634
doc_comments: Vec<Attribute>,
157-
modifiers: List<QueryModifier>,
35+
modifiers: QueryModifiers,
15836
name: Ident,
159-
key: IdentOrWild,
37+
key: Pat,
16038
arg: Type,
16139
result: ReturnType,
16240
}
16341

16442
impl Parse for Query {
16543
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)?)?;
16745

16846
// Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
16947
input.parse::<kw::query>()?;
@@ -178,7 +56,13 @@ impl Parse for Query {
17856
// Parse the query modifiers
17957
let content;
18058
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+
}
18266

18367
Ok(Query { doc_comments, modifiers, name, key, arg, result })
18468
}
@@ -205,7 +89,7 @@ struct QueryModifiers {
20589
storage: Option<Type>,
20690

20791
/// Cache the query to disk if the `Block` returns true.
208-
cache: Option<(Option<IdentOrWild>, Block)>,
92+
cache: Option<(Option<Pat>, Block)>,
20993

21094
/// Custom code to load the query from disk.
21195
load_cached: Option<(Ident, Ident, Block)>,
@@ -232,8 +116,7 @@ struct QueryModifiers {
232116
remap_env_constness: Option<Ident>,
233117
}
234118

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> {
237120
let mut load_cached = None;
238121
let mut storage = None;
239122
let mut cache = None;
@@ -245,117 +128,84 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers {
245128
let mut eval_always = None;
246129
let mut separate_provide_extern = None;
247130
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"));
350139
}
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"));
353203
}
354204
}
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 {
359209
load_cached,
360210
storage,
361211
cache,
@@ -367,17 +217,41 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers {
367217
eval_always,
368218
separate_provide_extern,
369219
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] })
371248
}
372249

373250
/// 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) {
379252
let name = &query.name;
380-
let key = &query.key.0;
253+
let key = &query.key;
254+
let modifiers = &query.modifiers;
381255

382256
// Find out if we should cache the query on disk
383257
let cache = if let Some((args, expr)) = modifiers.cache.as_ref() {
@@ -395,13 +269,7 @@ fn add_query_description_impl(
395269
}
396270
};
397271

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! { _ });
405273
// expr is a `Block`, meaning that `{ #expr }` gets expanded
406274
// to `{ { stmts... } }`, which triggers the `unused_braces` lint.
407275
quote! {
@@ -427,7 +295,7 @@ fn add_query_description_impl(
427295
}
428296
};
429297

430-
let (tcx, desc) = modifiers.desc;
298+
let (tcx, desc) = &modifiers.desc;
431299
let tcx = tcx.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t });
432300

433301
let desc = quote! {
@@ -456,10 +324,8 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
456324
let mut dep_node_def_stream = quote! {};
457325
let mut cached_queries = quote! {};
458326

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;
463329
let result_full = &query.result;
464330
let result = match query.result {
465331
ReturnType::Default => quote! { -> () },
@@ -528,7 +394,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
528394
[#attribute_stream] #name(#arg),
529395
});
530396

531-
add_query_description_impl(&query, modifiers, &mut query_description_stream);
397+
add_query_description_impl(&query, &mut query_description_stream);
532398
}
533399

534400
TokenStream::from(quote! {
@@ -539,7 +405,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
539405
$($other)*
540406

541407
#query_stream
542-
543408
}
544409
}
545410
}

0 commit comments

Comments
 (0)
Please sign in to comment.