Rollup merge of #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`
This commit is contained in:
commit
bc3b9de404
4 changed files with 207 additions and 41 deletions
|
|
@ -1,4 +1,5 @@
|
|||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::parse::{Parse, ParseStream, Result};
|
||||
use syn::punctuated::Punctuated;
|
||||
|
|
@ -62,7 +63,7 @@ impl Parse for Query {
|
|||
// If there are no doc-comments, give at least some idea of what
|
||||
// it does by showing the query description.
|
||||
if doc_comments.is_empty() {
|
||||
doc_comments.push(doc_comment_from_desc(&modifiers.desc.1)?);
|
||||
doc_comments.push(doc_comment_from_desc(&modifiers.desc.expr_list)?);
|
||||
}
|
||||
|
||||
Ok(Query { doc_comments, modifiers, name, key, arg, result })
|
||||
|
|
@ -82,15 +83,27 @@ impl<T: Parse> Parse for List<T> {
|
|||
}
|
||||
}
|
||||
|
||||
struct Desc {
|
||||
modifier: Ident,
|
||||
tcx_binding: Option<Ident>,
|
||||
expr_list: Punctuated<Expr, Token![,]>,
|
||||
}
|
||||
|
||||
struct CacheOnDiskIf {
|
||||
modifier: Ident,
|
||||
tcx_binding: Option<Pat>,
|
||||
block: Block,
|
||||
}
|
||||
|
||||
struct QueryModifiers {
|
||||
/// The description of the query.
|
||||
desc: (Option<Ident>, Punctuated<Expr, Token![,]>),
|
||||
desc: Desc,
|
||||
|
||||
/// Use this type for the in-memory cache.
|
||||
arena_cache: Option<Ident>,
|
||||
|
||||
/// Cache the query to disk if the `Block` returns true.
|
||||
cache_on_disk_if: Option<(Option<Pat>, Block)>,
|
||||
cache_on_disk_if: Option<CacheOnDiskIf>,
|
||||
|
||||
/// A cycle error for this query aborting the compilation with a fatal error.
|
||||
cycle_fatal: Option<Ident>,
|
||||
|
|
@ -164,7 +177,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
|
|||
// `desc { |tcx| "foo {}", tcx.item_path(key) }`
|
||||
let attr_content;
|
||||
braced!(attr_content in input);
|
||||
let tcx = if attr_content.peek(Token![|]) {
|
||||
let tcx_binding = if attr_content.peek(Token![|]) {
|
||||
attr_content.parse::<Token![|]>()?;
|
||||
let tcx = attr_content.parse()?;
|
||||
attr_content.parse::<Token![|]>()?;
|
||||
|
|
@ -172,15 +185,15 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let list = attr_content.parse_terminated(Expr::parse, Token![,])?;
|
||||
try_insert!(desc = (tcx, list));
|
||||
let expr_list = attr_content.parse_terminated(Expr::parse, Token![,])?;
|
||||
try_insert!(desc = Desc { modifier, tcx_binding, expr_list });
|
||||
} else if modifier == "cache_on_disk_if" {
|
||||
// Parse a cache-on-disk modifier like:
|
||||
//
|
||||
// `cache_on_disk_if { true }`
|
||||
// `cache_on_disk_if { key.is_local() }`
|
||||
// `cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) }`
|
||||
let args = if input.peek(token::Paren) {
|
||||
let tcx_binding = if input.peek(token::Paren) {
|
||||
let args;
|
||||
parenthesized!(args in input);
|
||||
let tcx = Pat::parse_single(&args)?;
|
||||
|
|
@ -189,7 +202,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
|
|||
None
|
||||
};
|
||||
let block = input.parse()?;
|
||||
try_insert!(cache_on_disk_if = (args, block));
|
||||
try_insert!(cache_on_disk_if = CacheOnDiskIf { modifier, tcx_binding, block });
|
||||
} else if modifier == "arena_cache" {
|
||||
try_insert!(arena_cache = modifier);
|
||||
} else if modifier == "cycle_fatal" {
|
||||
|
|
@ -275,7 +288,92 @@ struct HelperTokenStreams {
|
|||
}
|
||||
|
||||
fn make_helpers_for_query(query: &Query, streams: &mut HelperTokenStreams) {
|
||||
let Query { name, key, modifiers, .. } = &query;
|
||||
let Query { name, key, modifiers, arg, .. } = &query;
|
||||
|
||||
// Replace span for `name` to make rust-analyzer ignore it.
|
||||
let mut erased_name = name.clone();
|
||||
erased_name.set_span(Span::call_site());
|
||||
|
||||
// Generate a function to check whether we should cache the query to disk, for some key.
|
||||
if let Some(CacheOnDiskIf { tcx_binding, block, .. }) = modifiers.cache_on_disk_if.as_ref() {
|
||||
let tcx = tcx_binding.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ });
|
||||
// we're taking `key` by reference, but some rustc types usually prefer being passed by value
|
||||
streams.cache_on_disk_if_fns_stream.extend(quote! {
|
||||
#[allow(unused_variables, rustc::pass_by_value)]
|
||||
#[inline]
|
||||
pub fn #erased_name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::queries::#name::Key<'tcx>) -> bool
|
||||
#block
|
||||
});
|
||||
}
|
||||
|
||||
let Desc { tcx_binding, expr_list, .. } = &modifiers.desc;
|
||||
let tcx = tcx_binding.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t });
|
||||
|
||||
let desc = quote! {
|
||||
#[allow(unused_variables)]
|
||||
pub fn #erased_name<'tcx>(tcx: TyCtxt<'tcx>, key: #arg) -> String {
|
||||
let (#tcx, #key) = (tcx, key);
|
||||
format!(#expr_list)
|
||||
}
|
||||
};
|
||||
|
||||
streams.description_fns_stream.extend(quote! {
|
||||
#desc
|
||||
});
|
||||
}
|
||||
|
||||
/// Add hints for rust-analyzer
|
||||
fn add_to_analyzer_stream(query: &Query, analyzer_stream: &mut proc_macro2::TokenStream) {
|
||||
// Add links to relevant modifiers
|
||||
|
||||
let modifiers = &query.modifiers;
|
||||
|
||||
let mut modifiers_stream = quote! {};
|
||||
|
||||
let name = &modifiers.desc.modifier;
|
||||
modifiers_stream.extend(quote! {
|
||||
crate::query::modifiers::#name;
|
||||
});
|
||||
|
||||
if let Some(CacheOnDiskIf { modifier, .. }) = &modifiers.cache_on_disk_if {
|
||||
modifiers_stream.extend(quote! {
|
||||
crate::query::modifiers::#modifier;
|
||||
});
|
||||
}
|
||||
|
||||
macro_rules! doc_link {
|
||||
( $( $modifier:ident ),+ $(,)? ) => {
|
||||
$(
|
||||
if let Some(name) = &modifiers.$modifier {
|
||||
modifiers_stream.extend(quote! {
|
||||
crate::query::modifiers::#name;
|
||||
});
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
doc_link!(
|
||||
arena_cache,
|
||||
cycle_fatal,
|
||||
cycle_delay_bug,
|
||||
cycle_stash,
|
||||
no_hash,
|
||||
anon,
|
||||
eval_always,
|
||||
depth_limit,
|
||||
separate_provide_extern,
|
||||
feedable,
|
||||
return_result_from_ensure_ok,
|
||||
);
|
||||
|
||||
let name = &query.name;
|
||||
|
||||
// Replace span for `name` to make rust-analyzer ignore it.
|
||||
let mut erased_name = name.clone();
|
||||
erased_name.set_span(Span::call_site());
|
||||
|
||||
let result = &query.result;
|
||||
|
||||
// This dead code exists to instruct rust-analyzer about the link between the `rustc_queries`
|
||||
// query names and the corresponding produced provider. The issue is that by nature of this
|
||||
|
|
@ -289,35 +387,13 @@ fn make_helpers_for_query(query: &Query, streams: &mut HelperTokenStreams) {
|
|||
let crate::query::Providers { #name: _, .. };
|
||||
};
|
||||
|
||||
// Generate a function to check whether we should cache the query to disk, for some key.
|
||||
if let Some((args, expr)) = modifiers.cache_on_disk_if.as_ref() {
|
||||
let tcx = args.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ });
|
||||
// expr is a `Block`, meaning that `{ #expr }` gets expanded
|
||||
// to `{ { stmts... } }`, which triggers the `unused_braces` lint.
|
||||
// we're taking `key` by reference, but some rustc types usually prefer being passed by value
|
||||
streams.cache_on_disk_if_fns_stream.extend(quote! {
|
||||
#[allow(unused_variables, unused_braces, rustc::pass_by_value)]
|
||||
#[inline]
|
||||
pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::queries::#name::Key<'tcx>) -> bool {
|
||||
#ra_hint
|
||||
#expr
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let (tcx, desc) = &modifiers.desc;
|
||||
let tcx = tcx.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t });
|
||||
|
||||
let desc = quote! {
|
||||
#[allow(unused_variables)]
|
||||
pub fn #name<'tcx>(tcx: TyCtxt<'tcx>, key: crate::queries::#name::Key<'tcx>) -> String {
|
||||
let (#tcx, #key) = (tcx, key);
|
||||
format!(#desc)
|
||||
analyzer_stream.extend(quote! {
|
||||
#[inline(always)]
|
||||
fn #erased_name<'tcx>() #result {
|
||||
#ra_hint
|
||||
#modifiers_stream
|
||||
loop {}
|
||||
}
|
||||
};
|
||||
|
||||
streams.description_fns_stream.extend(quote! {
|
||||
#desc
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -327,6 +403,7 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
let mut query_stream = quote! {};
|
||||
let mut helpers = HelperTokenStreams::default();
|
||||
let mut feedable_queries = quote! {};
|
||||
let mut analyzer_stream = quote! {};
|
||||
let mut errors = quote! {};
|
||||
|
||||
macro_rules! assert {
|
||||
|
|
@ -409,6 +486,7 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
});
|
||||
}
|
||||
|
||||
add_to_analyzer_stream(&query, &mut analyzer_stream);
|
||||
make_helpers_for_query(&query, &mut helpers);
|
||||
}
|
||||
|
||||
|
|
@ -442,6 +520,12 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
// Add hints for rust-analyzer
|
||||
mod _analyzer_hints {
|
||||
use super::*;
|
||||
#analyzer_stream
|
||||
}
|
||||
|
||||
/// Functions that format a human-readable description of each query
|
||||
/// and its key, as specified by the `desc` query modifier.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ mod keys;
|
|||
pub mod on_disk_cache;
|
||||
#[macro_use]
|
||||
pub mod plumbing;
|
||||
pub(crate) mod modifiers;
|
||||
|
||||
pub fn describe_as_module(def_id: impl Into<LocalDefId>, tcx: TyCtxt<'_>) -> String {
|
||||
let def_id = def_id.into();
|
||||
|
|
|
|||
77
compiler/rustc_middle/src/query/modifiers.rs
Normal file
77
compiler/rustc_middle/src/query/modifiers.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
//! This contains documentation which is linked from query modifiers used in the `rustc_queries!` proc macro.
|
||||
#![allow(unused, non_camel_case_types)]
|
||||
// FIXME: Update and clarify documentation for these modifiers.
|
||||
|
||||
/// # `desc` query modifier
|
||||
///
|
||||
/// The description of the query. This modifier is required on every query.
|
||||
pub struct desc;
|
||||
|
||||
/// # `arena_cache` query modifier
|
||||
///
|
||||
/// Use this type for the in-memory cache.
|
||||
pub struct arena_cache;
|
||||
|
||||
/// # `cache_on_disk_if` query modifier
|
||||
///
|
||||
/// Cache the query to disk if the `Block` returns true.
|
||||
pub struct cache_on_disk_if;
|
||||
|
||||
/// # `cycle_fatal` query modifier
|
||||
///
|
||||
/// A cycle error for this query aborting the compilation with a fatal error.
|
||||
pub struct cycle_fatal;
|
||||
|
||||
/// # `cycle_delay_bug` query modifier
|
||||
///
|
||||
/// A cycle error results in a delay_bug call
|
||||
pub struct cycle_delay_bug;
|
||||
|
||||
/// # `cycle_stash` query modifier
|
||||
///
|
||||
/// A cycle error results in a stashed cycle error that can be unstashed and canceled later
|
||||
pub struct cycle_stash;
|
||||
|
||||
/// # `no_hash` query modifier
|
||||
///
|
||||
/// Don't hash the result, instead just mark a query red if it runs
|
||||
pub struct no_hash;
|
||||
|
||||
/// # `anon` query modifier
|
||||
///
|
||||
/// Generate a dep node based on the dependencies of the query
|
||||
pub struct anon;
|
||||
|
||||
/// # `eval_always` query modifier
|
||||
///
|
||||
/// Always evaluate the query, ignoring its dependencies
|
||||
pub struct eval_always;
|
||||
|
||||
/// # `depth_limit` query modifier
|
||||
///
|
||||
/// Whether the query has a call depth limit
|
||||
pub struct depth_limit;
|
||||
|
||||
/// # `separate_provide_extern` query modifier
|
||||
///
|
||||
/// Use a separate query provider for local and extern crates
|
||||
pub struct separate_provide_extern;
|
||||
|
||||
/// # `feedable` query modifier
|
||||
///
|
||||
/// Generate a `feed` method to set the query's value from another query.
|
||||
pub struct feedable;
|
||||
|
||||
/// # `return_result_from_ensure_ok` query modifier
|
||||
///
|
||||
/// When this query is called via `tcx.ensure_ok()`, it returns
|
||||
/// `Result<(), ErrorGuaranteed>` instead of `()`. If the query needs to
|
||||
/// be executed, and that execution returns an error, the error result is
|
||||
/// returned to the caller.
|
||||
///
|
||||
/// If execution is skipped, a synthetic `Ok(())` is returned, on the
|
||||
/// assumption that a query with all-green inputs must have succeeded.
|
||||
///
|
||||
/// Can only be applied to queries with a return value of
|
||||
/// `Result<_, ErrorGuaranteed>`.
|
||||
pub struct return_result_from_ensure_ok;
|
||||
|
|
@ -462,10 +462,14 @@ macro_rules! define_callbacks {
|
|||
}
|
||||
|
||||
pub struct Providers {
|
||||
$(pub $name: for<'tcx> fn(
|
||||
TyCtxt<'tcx>,
|
||||
$name::LocalKey<'tcx>,
|
||||
) -> $name::ProvidedValue<'tcx>,)*
|
||||
$(
|
||||
/// This is the provider for the query. Use `Find references` on this to
|
||||
/// navigate between the provider assignment and the query definition.
|
||||
pub $name: for<'tcx> fn(
|
||||
TyCtxt<'tcx>,
|
||||
$name::LocalKey<'tcx>,
|
||||
) -> $name::ProvidedValue<'tcx>,
|
||||
)*
|
||||
}
|
||||
|
||||
pub struct ExternProviders {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue