mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-31 12:04:43 +00:00 
			
		
		
		
	 1fd9520c92
			
		
	
	
		1fd9520c92
		
	
	
	
	
		
			
			This refactors how we deal with items in hir-def lowering. - It now lowers all of them through an "ExpressionStore" (kind of a misnomer as this point) as their so called *Signatures. - We now uniformly lower type AST into TypeRefs before type inference. - Likewise, this moves macro expansion out of type inference, resulting in a single place where we do non-defmap macro expansion. - Finally, this PR removes a lot of information from ItemTree, making the DefMap a lot less likely to be recomputed and have it only depend on actual early name resolution related information (not 100% true, we still have ADT fields in there but thats a follow up removal).
		
			
				
	
	
		
			343 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			343 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! The IR of the `#[query_group]` macro.
 | |
| 
 | |
| use quote::{ToTokens, format_ident, quote, quote_spanned};
 | |
| use syn::{FnArg, Ident, PatType, Path, Receiver, ReturnType, Type, parse_quote, spanned::Spanned};
 | |
| 
 | |
| pub(crate) struct TrackedQuery {
 | |
|     pub(crate) trait_name: Ident,
 | |
|     pub(crate) signature: syn::Signature,
 | |
|     pub(crate) pat_and_tys: Vec<PatType>,
 | |
|     pub(crate) invoke: Option<Path>,
 | |
|     pub(crate) default: Option<syn::Block>,
 | |
|     pub(crate) cycle: Option<Path>,
 | |
|     pub(crate) lru: Option<u32>,
 | |
|     pub(crate) generated_struct: Option<GeneratedInputStruct>,
 | |
| }
 | |
| 
 | |
| pub(crate) struct GeneratedInputStruct {
 | |
|     pub(crate) input_struct_name: Ident,
 | |
|     pub(crate) create_data_ident: Ident,
 | |
| }
 | |
| 
 | |
| impl ToTokens for TrackedQuery {
 | |
|     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | |
|         let sig = &self.signature;
 | |
|         let trait_name = &self.trait_name;
 | |
| 
 | |
|         let ret = &sig.output;
 | |
| 
 | |
|         let invoke = match &self.invoke {
 | |
|             Some(path) => path.to_token_stream(),
 | |
|             None => sig.ident.to_token_stream(),
 | |
|         };
 | |
| 
 | |
|         let fn_ident = &sig.ident;
 | |
|         let shim: Ident = format_ident!("{}_shim", fn_ident);
 | |
| 
 | |
|         let annotation = match (self.cycle.clone(), self.lru) {
 | |
|             (Some(cycle), Some(lru)) => quote!(#[salsa::tracked(lru = #lru, recovery_fn = #cycle)]),
 | |
|             (Some(cycle), None) => quote!(#[salsa::tracked(recovery_fn = #cycle)]),
 | |
|             (None, Some(lru)) => quote!(#[salsa::tracked(lru = #lru)]),
 | |
|             (None, None) => quote!(#[salsa::tracked]),
 | |
|         };
 | |
| 
 | |
|         let pat_and_tys = &self.pat_and_tys;
 | |
|         let params = self
 | |
|             .pat_and_tys
 | |
|             .iter()
 | |
|             .map(|pat_type| pat_type.pat.clone())
 | |
|             .collect::<Vec<Box<syn::Pat>>>();
 | |
| 
 | |
|         let invoke_block = match &self.default {
 | |
|             Some(default) => quote! { #default },
 | |
|             None => {
 | |
|                 let invoke_params: proc_macro2::TokenStream = quote! {db, #(#params),*};
 | |
|                 quote_spanned! { invoke.span() =>  {#invoke(#invoke_params)}}
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         let method = match &self.generated_struct {
 | |
|             Some(generated_struct) => {
 | |
|                 let input_struct_name = &generated_struct.input_struct_name;
 | |
|                 let create_data_ident = &generated_struct.create_data_ident;
 | |
| 
 | |
|                 quote! {
 | |
|                     #sig {
 | |
|                         #annotation
 | |
|                         fn #shim(
 | |
|                             db: &dyn #trait_name,
 | |
|                             _input: #input_struct_name,
 | |
|                             #(#pat_and_tys),*
 | |
|                         ) #ret
 | |
|                             #invoke_block
 | |
|                         #shim(self, #create_data_ident(self), #(#params),*)
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             None => {
 | |
|                 quote! {
 | |
|                     #sig {
 | |
|                         #annotation
 | |
|                         fn #shim(
 | |
|                             db: &dyn #trait_name,
 | |
|                             #(#pat_and_tys),*
 | |
|                         ) #ret
 | |
|                             #invoke_block
 | |
| 
 | |
|                         #shim(self, #(#params),*)
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         method.to_tokens(tokens);
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(crate) struct InputQuery {
 | |
|     pub(crate) signature: syn::Signature,
 | |
|     pub(crate) create_data_ident: Ident,
 | |
| }
 | |
| 
 | |
| impl ToTokens for InputQuery {
 | |
|     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | |
|         let sig = &self.signature;
 | |
|         let fn_ident = &sig.ident;
 | |
|         let create_data_ident = &self.create_data_ident;
 | |
| 
 | |
|         let method = quote! {
 | |
|             #sig {
 | |
|                 let data = #create_data_ident(self);
 | |
|                 data.#fn_ident(self).unwrap()
 | |
|             }
 | |
|         };
 | |
|         method.to_tokens(tokens);
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(crate) struct InputSetter {
 | |
|     pub(crate) signature: syn::Signature,
 | |
|     pub(crate) return_type: syn::Type,
 | |
|     pub(crate) create_data_ident: Ident,
 | |
| }
 | |
| 
 | |
| impl ToTokens for InputSetter {
 | |
|     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | |
|         let sig = &mut self.signature.clone();
 | |
| 
 | |
|         let ty = &self.return_type;
 | |
|         let fn_ident = &sig.ident;
 | |
|         let create_data_ident = &self.create_data_ident;
 | |
| 
 | |
|         let setter_ident = format_ident!("set_{}", fn_ident);
 | |
|         sig.ident = setter_ident.clone();
 | |
| 
 | |
|         let value_argument: PatType = parse_quote!(__value: #ty);
 | |
|         sig.inputs.push(FnArg::Typed(value_argument.clone()));
 | |
| 
 | |
|         // make `&self` `&mut self` instead.
 | |
|         let mut_receiver: Receiver = parse_quote!(&mut self);
 | |
|         if let Some(og) = sig.inputs.first_mut() {
 | |
|             *og = FnArg::Receiver(mut_receiver)
 | |
|         }
 | |
| 
 | |
|         // remove the return value.
 | |
|         sig.output = ReturnType::Default;
 | |
| 
 | |
|         let value = &value_argument.pat;
 | |
|         let method = quote! {
 | |
|             #sig {
 | |
|                 use salsa::Setter;
 | |
|                 let data = #create_data_ident(self);
 | |
|                 data.#setter_ident(self).to(Some(#value));
 | |
|             }
 | |
|         };
 | |
|         method.to_tokens(tokens);
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(crate) struct InputSetterWithDurability {
 | |
|     pub(crate) signature: syn::Signature,
 | |
|     pub(crate) return_type: syn::Type,
 | |
|     pub(crate) create_data_ident: Ident,
 | |
| }
 | |
| 
 | |
| impl ToTokens for InputSetterWithDurability {
 | |
|     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | |
|         let sig = &mut self.signature.clone();
 | |
| 
 | |
|         let ty = &self.return_type;
 | |
|         let fn_ident = &sig.ident;
 | |
|         let setter_ident = format_ident!("set_{}", fn_ident);
 | |
| 
 | |
|         let create_data_ident = &self.create_data_ident;
 | |
| 
 | |
|         sig.ident = format_ident!("set_{}_with_durability", fn_ident);
 | |
| 
 | |
|         let value_argument: PatType = parse_quote!(__value: #ty);
 | |
|         sig.inputs.push(FnArg::Typed(value_argument.clone()));
 | |
| 
 | |
|         let durability_argument: PatType = parse_quote!(durability: salsa::Durability);
 | |
|         sig.inputs.push(FnArg::Typed(durability_argument.clone()));
 | |
| 
 | |
|         // make `&self` `&mut self` instead.
 | |
|         let mut_receiver: Receiver = parse_quote!(&mut self);
 | |
|         if let Some(og) = sig.inputs.first_mut() {
 | |
|             *og = FnArg::Receiver(mut_receiver)
 | |
|         }
 | |
| 
 | |
|         // remove the return value.
 | |
|         sig.output = ReturnType::Default;
 | |
| 
 | |
|         let value = &value_argument.pat;
 | |
|         let durability = &durability_argument.pat;
 | |
|         let method = quote! {
 | |
|             #sig {
 | |
|                 use salsa::Setter;
 | |
|                 let data = #create_data_ident(self);
 | |
|                 data.#setter_ident(self)
 | |
|                     .with_durability(#durability)
 | |
|                     .to(Some(#value));
 | |
|             }
 | |
|         };
 | |
|         method.to_tokens(tokens);
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(crate) enum SetterKind {
 | |
|     Plain(InputSetter),
 | |
|     WithDurability(InputSetterWithDurability),
 | |
| }
 | |
| 
 | |
| impl ToTokens for SetterKind {
 | |
|     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | |
|         match self {
 | |
|             SetterKind::Plain(input_setter) => input_setter.to_tokens(tokens),
 | |
|             SetterKind::WithDurability(input_setter_with_durability) => {
 | |
|                 input_setter_with_durability.to_tokens(tokens)
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(crate) struct Transparent {
 | |
|     pub(crate) signature: syn::Signature,
 | |
|     pub(crate) pat_and_tys: Vec<PatType>,
 | |
|     pub(crate) invoke: Option<Path>,
 | |
|     pub(crate) default: Option<syn::Block>,
 | |
| }
 | |
| 
 | |
| impl ToTokens for Transparent {
 | |
|     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | |
|         let sig = &self.signature;
 | |
| 
 | |
|         let ty = self
 | |
|             .pat_and_tys
 | |
|             .iter()
 | |
|             .map(|pat_type| pat_type.pat.clone())
 | |
|             .collect::<Vec<Box<syn::Pat>>>();
 | |
| 
 | |
|         let invoke = match &self.invoke {
 | |
|             Some(path) => path.to_token_stream(),
 | |
|             None => sig.ident.to_token_stream(),
 | |
|         };
 | |
| 
 | |
|         let method = match &self.default {
 | |
|             Some(default) => quote! {
 | |
|                 #sig { let db = self; #default }
 | |
|             },
 | |
|             None => quote! {
 | |
|                 #sig {
 | |
|                     #invoke(self, #(#ty),*)
 | |
|                 }
 | |
|             },
 | |
|         };
 | |
| 
 | |
|         method.to_tokens(tokens);
 | |
|     }
 | |
| }
 | |
| pub(crate) struct Intern {
 | |
|     pub(crate) signature: syn::Signature,
 | |
|     pub(crate) pat_and_tys: Vec<PatType>,
 | |
|     pub(crate) interned_struct_path: Path,
 | |
| }
 | |
| 
 | |
| impl ToTokens for Intern {
 | |
|     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | |
|         let sig = &self.signature;
 | |
| 
 | |
|         let ty = self.pat_and_tys.to_vec();
 | |
| 
 | |
|         let interned_pat = ty.first().expect("at least one pat; this is a bug");
 | |
|         let interned_pat = &interned_pat.pat;
 | |
| 
 | |
|         let wrapper_struct = self.interned_struct_path.to_token_stream();
 | |
| 
 | |
|         let method = quote! {
 | |
|             #sig {
 | |
|                 #wrapper_struct::new(self, #interned_pat)
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         method.to_tokens(tokens);
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(crate) struct Lookup {
 | |
|     pub(crate) signature: syn::Signature,
 | |
|     pub(crate) pat_and_tys: Vec<PatType>,
 | |
|     pub(crate) return_ty: Type,
 | |
|     pub(crate) interned_struct_path: Path,
 | |
| }
 | |
| 
 | |
| impl Lookup {
 | |
|     pub(crate) fn prepare_signature(&mut self) {
 | |
|         let sig = &self.signature;
 | |
| 
 | |
|         let ident = format_ident!("lookup_{}", sig.ident);
 | |
| 
 | |
|         let ty = self.pat_and_tys.to_vec();
 | |
| 
 | |
|         let interned_key = &self.return_ty;
 | |
| 
 | |
|         let interned_pat = ty.first().expect("at least one pat; this is a bug");
 | |
|         let interned_return_ty = &interned_pat.ty;
 | |
| 
 | |
|         self.signature = parse_quote!(
 | |
|             fn #ident(&self, id: #interned_key) -> #interned_return_ty
 | |
|         );
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl ToTokens for Lookup {
 | |
|     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | |
|         let sig = &self.signature;
 | |
| 
 | |
|         let wrapper_struct = self.interned_struct_path.to_token_stream();
 | |
|         let method = quote! {
 | |
|             #sig {
 | |
|                 #wrapper_struct::ingredient(self).data(self.as_dyn_database(), id.as_id()).0.clone()
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         method.to_tokens(tokens);
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(crate) enum Queries {
 | |
|     TrackedQuery(TrackedQuery),
 | |
|     InputQuery(InputQuery),
 | |
|     Intern(Intern),
 | |
|     Transparent(Transparent),
 | |
| }
 | |
| 
 | |
| impl ToTokens for Queries {
 | |
|     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | |
|         match self {
 | |
|             Queries::TrackedQuery(tracked_query) => tracked_query.to_tokens(tokens),
 | |
|             Queries::InputQuery(input_query) => input_query.to_tokens(tokens),
 | |
|             Queries::Transparent(transparent) => transparent.to_tokens(tokens),
 | |
|             Queries::Intern(intern) => intern.to_tokens(tokens),
 | |
|         }
 | |
|     }
 | |
| }
 |