Skip to content

Commit 1a4a627

Browse files
authored
Rollup merge of rust-lang#152422 - Zoxc:query-macro-tweaks, r=Zalathar
Change query proc macro to be more rust-analyzer friendly This changes the query proc macro to be more rust-analyzer friendly. - Types in the macro now have a proper span - Some functions have their span hidden so they don't show up when hovering over the query name - Added a hint on the provider field on how to find providers. That is shown when hovering over the query name - Linked query name to the provider field on all queries, not just ones with caching - Added tooltip for the query modifiers by linking to the new types in `rustc_middle:::query::modifiers`
2 parents 53f988e + 47b4e02 commit 1a4a627

File tree

4 files changed

+202
-37
lines changed

4 files changed

+202
-37
lines changed

compiler/rustc_macros/src/query.rs

Lines changed: 117 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use proc_macro::TokenStream;
2+
use proc_macro2::Span;
23
use quote::{quote, quote_spanned};
34
use syn::parse::{Parse, ParseStream, Result};
45
use syn::punctuated::Punctuated;
@@ -62,7 +63,7 @@ impl Parse for Query {
6263
// If there are no doc-comments, give at least some idea of what
6364
// it does by showing the query description.
6465
if doc_comments.is_empty() {
65-
doc_comments.push(doc_comment_from_desc(&modifiers.desc.1)?);
66+
doc_comments.push(doc_comment_from_desc(&modifiers.desc.expr_list)?);
6667
}
6768

6869
Ok(Query { doc_comments, modifiers, name, key, arg, result })
@@ -82,15 +83,27 @@ impl<T: Parse> Parse for List<T> {
8283
}
8384
}
8485

86+
struct Desc {
87+
modifier: Ident,
88+
tcx_binding: Option<Ident>,
89+
expr_list: Punctuated<Expr, Token![,]>,
90+
}
91+
92+
struct CacheOnDiskIf {
93+
modifier: Ident,
94+
tcx_binding: Option<Pat>,
95+
block: Block,
96+
}
97+
8598
struct QueryModifiers {
8699
/// The description of the query.
87-
desc: (Option<Ident>, Punctuated<Expr, Token![,]>),
100+
desc: Desc,
88101

89102
/// Use this type for the in-memory cache.
90103
arena_cache: Option<Ident>,
91104

92105
/// Cache the query to disk if the `Block` returns true.
93-
cache_on_disk_if: Option<(Option<Pat>, Block)>,
106+
cache_on_disk_if: Option<CacheOnDiskIf>,
94107

95108
/// A cycle error for this query aborting the compilation with a fatal error.
96109
cycle_fatal: Option<Ident>,
@@ -164,23 +177,23 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
164177
// `desc { |tcx| "foo {}", tcx.item_path(key) }`
165178
let attr_content;
166179
braced!(attr_content in input);
167-
let tcx = if attr_content.peek(Token![|]) {
180+
let tcx_binding = if attr_content.peek(Token![|]) {
168181
attr_content.parse::<Token![|]>()?;
169182
let tcx = attr_content.parse()?;
170183
attr_content.parse::<Token![|]>()?;
171184
Some(tcx)
172185
} else {
173186
None
174187
};
175-
let list = attr_content.parse_terminated(Expr::parse, Token![,])?;
176-
try_insert!(desc = (tcx, list));
188+
let expr_list = attr_content.parse_terminated(Expr::parse, Token![,])?;
189+
try_insert!(desc = Desc { modifier, tcx_binding, expr_list });
177190
} else if modifier == "cache_on_disk_if" {
178191
// Parse a cache-on-disk modifier like:
179192
//
180193
// `cache_on_disk_if { true }`
181194
// `cache_on_disk_if { key.is_local() }`
182195
// `cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) }`
183-
let args = if input.peek(token::Paren) {
196+
let tcx_binding = if input.peek(token::Paren) {
184197
let args;
185198
parenthesized!(args in input);
186199
let tcx = Pat::parse_single(&args)?;
@@ -189,7 +202,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
189202
None
190203
};
191204
let block = input.parse()?;
192-
try_insert!(cache_on_disk_if = (args, block));
205+
try_insert!(cache_on_disk_if = CacheOnDiskIf { modifier, tcx_binding, block });
193206
} else if modifier == "arena_cache" {
194207
try_insert!(arena_cache = modifier);
195208
} else if modifier == "cycle_fatal" {
@@ -275,44 +288,32 @@ struct HelperTokenStreams {
275288
}
276289

277290
fn make_helpers_for_query(query: &Query, streams: &mut HelperTokenStreams) {
278-
let Query { name, key, modifiers, .. } = &query;
291+
let Query { name, key, modifiers, arg, .. } = &query;
279292

280-
// This dead code exists to instruct rust-analyzer about the link between the `rustc_queries`
281-
// query names and the corresponding produced provider. The issue is that by nature of this
282-
// macro producing a higher order macro that has all its token in the macro declaration we lose
283-
// any meaningful spans, resulting in rust-analyzer being unable to make the connection between
284-
// the query name and the corresponding providers field. The trick to fix this is to have
285-
// `rustc_queries` emit a field access with the given name's span which allows it to successfully
286-
// show references / go to definition to the corresponding provider assignment which is usually
287-
// the more interesting place.
288-
let ra_hint = quote! {
289-
let crate::query::Providers { #name: _, .. };
290-
};
293+
// Replace span for `name` to make rust-analyzer ignore it.
294+
let mut erased_name = name.clone();
295+
erased_name.set_span(Span::call_site());
291296

292297
// Generate a function to check whether we should cache the query to disk, for some key.
293-
if let Some((args, expr)) = modifiers.cache_on_disk_if.as_ref() {
294-
let tcx = args.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ });
295-
// expr is a `Block`, meaning that `{ #expr }` gets expanded
296-
// to `{ { stmts... } }`, which triggers the `unused_braces` lint.
298+
if let Some(CacheOnDiskIf { tcx_binding, block, .. }) = modifiers.cache_on_disk_if.as_ref() {
299+
let tcx = tcx_binding.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ });
297300
// we're taking `key` by reference, but some rustc types usually prefer being passed by value
298301
streams.cache_on_disk_if_fns_stream.extend(quote! {
299-
#[allow(unused_variables, unused_braces, rustc::pass_by_value)]
302+
#[allow(unused_variables, rustc::pass_by_value)]
300303
#[inline]
301-
pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::queries::#name::Key<'tcx>) -> bool {
302-
#ra_hint
303-
#expr
304-
}
304+
pub fn #erased_name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::queries::#name::Key<'tcx>) -> bool
305+
#block
305306
});
306307
}
307308

308-
let (tcx, desc) = &modifiers.desc;
309-
let tcx = tcx.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t });
309+
let Desc { tcx_binding, expr_list, .. } = &modifiers.desc;
310+
let tcx = tcx_binding.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t });
310311

311312
let desc = quote! {
312313
#[allow(unused_variables)]
313-
pub fn #name<'tcx>(tcx: TyCtxt<'tcx>, key: crate::queries::#name::Key<'tcx>) -> String {
314+
pub fn #erased_name<'tcx>(tcx: TyCtxt<'tcx>, key: #arg) -> String {
314315
let (#tcx, #key) = (tcx, key);
315-
format!(#desc)
316+
format!(#expr_list)
316317
}
317318
};
318319

@@ -321,12 +322,88 @@ fn make_helpers_for_query(query: &Query, streams: &mut HelperTokenStreams) {
321322
});
322323
}
323324

325+
/// Add hints for rust-analyzer
326+
fn add_to_analyzer_stream(query: &Query, analyzer_stream: &mut proc_macro2::TokenStream) {
327+
// Add links to relevant modifiers
328+
329+
let modifiers = &query.modifiers;
330+
331+
let mut modifiers_stream = quote! {};
332+
333+
let name = &modifiers.desc.modifier;
334+
modifiers_stream.extend(quote! {
335+
crate::query::modifiers::#name;
336+
});
337+
338+
if let Some(CacheOnDiskIf { modifier, .. }) = &modifiers.cache_on_disk_if {
339+
modifiers_stream.extend(quote! {
340+
crate::query::modifiers::#modifier;
341+
});
342+
}
343+
344+
macro_rules! doc_link {
345+
( $( $modifier:ident ),+ $(,)? ) => {
346+
$(
347+
if let Some(name) = &modifiers.$modifier {
348+
modifiers_stream.extend(quote! {
349+
crate::query::modifiers::#name;
350+
});
351+
}
352+
)+
353+
}
354+
}
355+
356+
doc_link!(
357+
arena_cache,
358+
cycle_fatal,
359+
cycle_delay_bug,
360+
cycle_stash,
361+
no_hash,
362+
anon,
363+
eval_always,
364+
depth_limit,
365+
separate_provide_extern,
366+
feedable,
367+
return_result_from_ensure_ok,
368+
);
369+
370+
let name = &query.name;
371+
372+
// Replace span for `name` to make rust-analyzer ignore it.
373+
let mut erased_name = name.clone();
374+
erased_name.set_span(Span::call_site());
375+
376+
let result = &query.result;
377+
378+
// This dead code exists to instruct rust-analyzer about the link between the `rustc_queries`
379+
// query names and the corresponding produced provider. The issue is that by nature of this
380+
// macro producing a higher order macro that has all its token in the macro declaration we lose
381+
// any meaningful spans, resulting in rust-analyzer being unable to make the connection between
382+
// the query name and the corresponding providers field. The trick to fix this is to have
383+
// `rustc_queries` emit a field access with the given name's span which allows it to successfully
384+
// show references / go to definition to the corresponding provider assignment which is usually
385+
// the more interesting place.
386+
let ra_hint = quote! {
387+
let crate::query::Providers { #name: _, .. };
388+
};
389+
390+
analyzer_stream.extend(quote! {
391+
#[inline(always)]
392+
fn #erased_name<'tcx>() #result {
393+
#ra_hint
394+
#modifiers_stream
395+
loop {}
396+
}
397+
});
398+
}
399+
324400
pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
325401
let queries = parse_macro_input!(input as List<Query>);
326402

327403
let mut query_stream = quote! {};
328404
let mut helpers = HelperTokenStreams::default();
329405
let mut feedable_queries = quote! {};
406+
let mut analyzer_stream = quote! {};
330407
let mut errors = quote! {};
331408

332409
macro_rules! assert {
@@ -409,6 +486,7 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
409486
});
410487
}
411488

489+
add_to_analyzer_stream(&query, &mut analyzer_stream);
412490
make_helpers_for_query(&query, &mut helpers);
413491
}
414492

@@ -442,6 +520,12 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
442520
}
443521
}
444522

523+
// Add hints for rust-analyzer
524+
mod _analyzer_hints {
525+
use super::*;
526+
#analyzer_stream
527+
}
528+
445529
/// Functions that format a human-readable description of each query
446530
/// and its key, as specified by the `desc` query modifier.
447531
///

compiler/rustc_middle/src/query/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod keys;
1313
pub mod on_disk_cache;
1414
#[macro_use]
1515
pub mod plumbing;
16+
pub(crate) mod modifiers;
1617
pub mod values;
1718

1819
pub fn describe_as_module(def_id: impl Into<LocalDefId>, tcx: TyCtxt<'_>) -> String {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//! This contains documentation which is linked from query modifiers used in the `rustc_queries!` proc macro.
2+
#![allow(unused, non_camel_case_types)]
3+
4+
/// # `desc` query modifier
5+
///
6+
/// The description of the query. This modifier is required on every query.
7+
pub struct desc;
8+
9+
/// # `arena_cache` query modifier
10+
///
11+
/// Use this type for the in-memory cache.
12+
pub struct arena_cache;
13+
14+
/// # `cache_on_disk_if` query modifier
15+
///
16+
/// Cache the query to disk if the `Block` returns true.
17+
pub struct cache_on_disk_if;
18+
19+
/// # `cycle_fatal` query modifier
20+
///
21+
/// A cycle error for this query aborting the compilation with a fatal error.
22+
pub struct cycle_fatal;
23+
24+
/// # `cycle_delay_bug` query modifier
25+
///
26+
/// A cycle error results in a delay_bug call
27+
pub struct cycle_delay_bug;
28+
29+
/// # `cycle_stash` query modifier
30+
///
31+
/// A cycle error results in a stashed cycle error that can be unstashed and canceled later
32+
pub struct cycle_stash;
33+
34+
/// # `no_hash` query modifier
35+
///
36+
/// Don't hash the result, instead just mark a query red if it runs
37+
pub struct no_hash;
38+
39+
/// # `anon` query modifier
40+
///
41+
/// Generate a dep node based on the dependencies of the query
42+
pub struct anon;
43+
44+
/// # `eval_always` query modifier
45+
///
46+
/// Always evaluate the query, ignoring its dependencies
47+
pub struct eval_always;
48+
49+
/// # `depth_limit` query modifier
50+
///
51+
/// Whether the query has a call depth limit
52+
pub struct depth_limit;
53+
54+
/// # `separate_provide_extern` query modifier
55+
///
56+
/// Use a separate query provider for local and extern crates
57+
pub struct separate_provide_extern;
58+
59+
/// # `feedable` query modifier
60+
///
61+
/// Generate a `feed` method to set the query's value from another query.
62+
pub struct feedable;
63+
64+
/// # `return_result_from_ensure_ok` query modifier
65+
///
66+
/// When this query is called via `tcx.ensure_ok()`, it returns
67+
/// `Result<(), ErrorGuaranteed>` instead of `()`. If the query needs to
68+
/// be executed, and that execution returns an error, the error result is
69+
/// returned to the caller.
70+
///
71+
/// If execution is skipped, a synthetic `Ok(())` is returned, on the
72+
/// assumption that a query with all-green inputs must have succeeded.
73+
///
74+
/// Can only be applied to queries with a return value of
75+
/// `Result<_, ErrorGuaranteed>`.
76+
pub struct return_result_from_ensure_ok;

compiler/rustc_middle/src/query/plumbing.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -449,10 +449,14 @@ macro_rules! define_callbacks {
449449
}
450450

451451
pub struct Providers {
452-
$(pub $name: for<'tcx> fn(
453-
TyCtxt<'tcx>,
454-
$name::LocalKey<'tcx>,
455-
) -> $name::ProvidedValue<'tcx>,)*
452+
$(
453+
/// This is the provider for the query. Use `Find references` on this to
454+
/// navigate between the provider assignment and the query definition.
455+
pub $name: for<'tcx> fn(
456+
TyCtxt<'tcx>,
457+
$name::LocalKey<'tcx>,
458+
) -> $name::ProvidedValue<'tcx>,
459+
)*
456460
}
457461

458462
pub struct ExternProviders {

0 commit comments

Comments
 (0)