11use proc_macro:: TokenStream ;
2+ use proc_macro2:: Span ;
23use quote:: { quote, quote_spanned} ;
34use syn:: parse:: { Parse , ParseStream , Result } ;
45use 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+
8598struct 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
277290fn 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+
324400pub ( 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 ///
0 commit comments