diff --git a/rust/extractor/src/translate.rs b/rust/extractor/src/translate.rs index 4e9cda3e2aa..35e0712df0f 100644 --- a/rust/extractor/src/translate.rs +++ b/rust/extractor/src/translate.rs @@ -1,5 +1,6 @@ mod base; mod generated; +mod label_cache; mod mappings; pub use base::Translator; diff --git a/rust/extractor/src/translate/base.rs b/rust/extractor/src/translate/base.rs index a4716935f1a..1c4586b2a9a 100644 --- a/rust/extractor/src/translate/base.rs +++ b/rust/extractor/src/translate/base.rs @@ -1,18 +1,19 @@ -use super::mappings::{AddressableAst, AddressableHir, PathAst}; +use super::mappings::{ModuleItem, PathAst}; use crate::generated::MacroCall; use crate::generated::{self}; use crate::rust_analyzer::FileSemanticInformation; +use crate::translate::label_cache::CanonicalPathLabelCache; use crate::trap::AsTrapKeyPart; use crate::trap::{DiagnosticSeverity, TrapFile, TrapId}; use crate::trap::{Label, TrapClass}; -use crate::trap_key; +use crate::{cache_get_or_assign, trap_key}; use itertools::{Either, Itertools}; use log::{warn, Level}; use ra_ap_base_db::CrateOrigin; use ra_ap_hir::db::ExpandDatabase; use ra_ap_hir::{ - Adt, Crate, Enum, ItemContainer, Module, ModuleDef, PathResolution, Semantics, Trait, TraitRef, - Type, Variant, + Adt, Crate, Function, HasContainer, ItemContainer, Module, ModuleDef, PathResolution, + Semantics, TraitRef, Type, Variant, }; use ra_ap_hir_def::ModuleId; use ra_ap_hir_expand::ExpandTo; @@ -20,7 +21,6 @@ use ra_ap_ide_db::line_index::{LineCol, LineIndex}; use ra_ap_ide_db::RootDatabase; use ra_ap_parser::SyntaxKind; use ra_ap_span::{EditionedFileId, TextSize}; -use ra_ap_syntax::ast::HasName; use ra_ap_syntax::{ ast, AstNode, NodeOrToken, SyntaxElementChildren, SyntaxError, SyntaxNode, SyntaxToken, TextRange, @@ -34,22 +34,22 @@ macro_rules! emit_detached { $self.extract_macro_call_expanded(&$node, $label); }; (Function, $self:ident, $node:ident, $label:ident) => { - $self.extract_canonical_origin(&$node, $label.into()); + $self.extract_canonical_origin_of_function(&$node, $label); }; (Trait, $self:ident, $node:ident, $label:ident) => { - $self.extract_canonical_origin(&$node, $label.into()); + $self.extract_canonical_origin_of_trait(&$node, $label); }; (Struct, $self:ident, $node:ident, $label:ident) => { - $self.extract_canonical_origin(&$node, $label.into()); + $self.extract_canonical_origin_of_struct(&$node, $label); }; (Enum, $self:ident, $node:ident, $label:ident) => { - $self.extract_canonical_origin(&$node, $label.into()); + $self.extract_canonical_origin_of_enum(&$node, $label); }; (Union, $self:ident, $node:ident, $label:ident) => { - $self.extract_canonical_origin(&$node, $label.into()); + $self.extract_canonical_origin_of_union(&$node, $label); }; (Module, $self:ident, $node:ident, $label:ident) => { - $self.extract_canonical_origin(&$node, $label.into()); + $self.extract_canonical_origin_of_module(&$node, $label); }; (Variant, $self:ident, $node:ident, $label:ident) => { $self.extract_canonical_origin_of_enum_variant(&$node, $label); @@ -84,6 +84,7 @@ pub struct Translator<'a> { file_id: Option, pub semantics: Option<&'a Semantics<'a, RootDatabase>>, vfs: Option<&'a Vfs>, + canonical_path_cache: CanonicalPathLabelCache, } impl<'a> Translator<'a> { @@ -102,6 +103,7 @@ impl<'a> Translator<'a> { file_id: semantic_info.map(|i| i.file_id), semantics: semantic_info.map(|i| i.semantics), vfs: semantic_info.map(|i| i.vfs), + canonical_path_cache: CanonicalPathLabelCache::new(), } } fn location(&self, range: TextRange) -> (LineCol, LineCol) { @@ -356,13 +358,190 @@ impl<'a> Translator<'a> { &mut self, ty: Type, ) -> Option> { - let sema = self.semantics.as_ref().unwrap(); - // rust-analyzer doesn't provide a type enum directly - if let Some(it) = ty.as_adt() { - let name = it.name(sema.db).as_str().to_owned(); - let module = it.module(sema.db); + cache_get_or_assign!(self.canonical_path_cache, ty, { + let sema = self.semantics.as_ref().unwrap(); + // rust-analyzer doesn't provide a type enum directly + if let Some(it) = ty.as_adt() { + let name = it.name(sema.db).as_str().to_owned(); + let module = it.module(sema.db); + let mut generic_args = Vec::new(); + for arg in ty.type_arguments() { + let path = self.canonical_path_from_type(arg)?; + generic_args.push( + self.trap + .emit(generated::TypeGenericTypeArg { + id: trap_key!(path), + path, + }) + .into(), + ); + } + let namespace = self.canonical_path_from_module(module)?; + let base = self.trap.emit(generated::ModuleItemCanonicalPath { + id: trap_key!(namespace, "::", name), + namespace, + name, + }); + let path = self.trap.emit(generated::ParametrizedCanonicalPath { + id: trap_key!(base, generic_args), + base, + generic_args, + }); + Some( + self.trap + .emit(generated::ConcreteTypeCanonicalPath { + id: trap_key!(path), + path, + }) + .into(), + ) + } else if let Some((it, size)) = ty.as_array(sema.db) { + let modifiers = ["[".to_owned(), format!("; {size}]")]; + let bases = vec![self.canonical_path_from_type(it)?]; + Some(self.emit_derived_type_canonical_path(&modifiers, bases)) + } else if let Some(it) = ty.as_slice() { + let modifiers = ["[", "]"]; + let bases = vec![self.canonical_path_from_type(it)?]; + Some(self.emit_derived_type_canonical_path(&modifiers, bases)) + } else if ty.is_unit() { + let modifiers = ["()"]; + let bases = vec![]; + Some(self.emit_derived_type_canonical_path(&modifiers, bases)) + } else if let Some(it) = ty.as_builtin() { + let name = it.name().as_str().to_owned(); + Some( + self.trap + .emit(generated::BuiltinTypeCanonicalPath { + id: trap_key!(name), + name, + }) + .into(), + ) + } else if let Some((it, mutability)) = ty.as_reference() { + let modifiers = [format!("&{}", mutability.as_keyword_for_ref())]; + let bases = vec![self.canonical_path_from_type(it)?]; + Some(self.emit_derived_type_canonical_path(&modifiers, bases)) + } else if ty.as_type_param(sema.db).is_some() { + // from the canonical path perspective, we just want a special name + // e.g. `crate::<_ as SomeTrait>::func` + // TODO: This will not work for assigning trap keys to types themselves! + Some( + self.trap + .emit(generated::PlaceholderTypeCanonicalPath { id: trap_key!("_") }) + .into(), + ) + } else { + let tuple_args = ty.tuple_fields(sema.db); + if tuple_args.is_empty() { + None + } else { + let modifiers = std::iter::once("(") + .chain(std::iter::repeat(", ").take(tuple_args.len() - 1)) + .chain(std::iter::once(")")) + .collect::>(); + let bases = tuple_args + .into_iter() + .map(|arg| self.canonical_path_from_type(arg)) + .collect::>>()?; + Some(self.emit_derived_type_canonical_path(&modifiers, bases)) + } + } + }) + } + + fn emit_crate_root(&mut self, item: Crate) -> Option> { + cache_get_or_assign!(self.canonical_path_cache, item, { + let db = self.semantics.unwrap().db; + let (repo, name) = match item.origin(db) { + CrateOrigin::Rustc { name } => (None, Some(name)), + CrateOrigin::Local { repo, name } => (repo, name), + CrateOrigin::Library { repo, name } => (repo, Some(name)), + CrateOrigin::Lang(it) => { + let name = it.to_string(); + return Some( + self.trap + .emit(generated::LangCrateRoot { + id: trap_key!(name), + name, + }) + .into(), + ); + } + }; + let name = name.map(|s| s.to_string()); + let vfs_path = self.vfs.unwrap().file_path(item.root_file(db)); + let Some(file) = vfs_path.as_path() else { + warn!("Crate root file not found: {}", vfs_path); + return None; + }; + let source = self.trap.emit_file(&PathBuf::from(file.as_os_str())); + Some( + self.trap + .emit(generated::RepoCrateRoot { + id: trap_key!(source, name, repo), + name, + repo, + source, + }) + .into(), + ) + }) + } + + fn canonical_path_from_module(&mut self, item: Module) -> Option> { + cache_get_or_assign!(self.canonical_path_cache, item, { + // if we have a Hir entity, it means we have semantics + let sema = self.semantics.as_ref().unwrap(); + // do not assign namespaces to unnamed modules, i.e. a virtual modules for block scopes + if ModuleId::from(item).is_block_module() { + return None; + } + let path = item + .path_to_root(sema.db) + .iter() + .filter_map(|m| m.name(sema.db)) + .map(|n| n.as_str().to_owned()) + .join("::"); + let root = self.emit_crate_root(item.krate())?; + Some(self.trap.emit(generated::Namespace { + id: trap_key!(root, "::", path), + root, + path, + })) + }) + } + + fn canonical_path_from_module_item( + &mut self, + item: impl ModuleItem, + ) -> Option> { + cache_get_or_assign!(self.canonical_path_cache, item, { + // if we have a Hir entity, it means we have semantics + let sema = self.semantics.as_ref().unwrap(); + let module = item.module(sema); + let name = item.name(sema).as_str().to_owned(); + let namespace = self.canonical_path_from_module(module)?; + Some(self.trap.emit(generated::ModuleItemCanonicalPath { + id: trap_key!(namespace, "::", name), + namespace, + name, + })) + }) + } + + fn canonical_path_from_trait_ref( + &mut self, + item: TraitRef, + ) -> Option> { + cache_get_or_assign!(self.canonical_path_cache, item, { + let db = self.semantics.unwrap().db; + let trait_ = item.trait_(); + let base = self.canonical_path_from_module_item(trait_)?; + let param_count = trait_.type_or_const_param_count(db, false); let mut generic_args = Vec::new(); - for arg in ty.type_arguments() { + for i in 1..=param_count { + // TODO seems like you can't get const arguments currently... + let arg = item.get_type_argument(i)?; let path = self.canonical_path_from_type(arg)?; generic_args.push( self.trap @@ -373,210 +552,24 @@ impl<'a> Translator<'a> { .into(), ); } - let namespace = self.canonical_path_from_hir_module(module)?; - let base = self.trap.emit(generated::ModuleItemCanonicalPath { - id: trap_key!(namespace, "::", name), - namespace, - name, - }); - let path = self.trap.emit(generated::ParametrizedCanonicalPath { + Some(self.trap.emit(generated::ParametrizedCanonicalPath { id: trap_key!(base, generic_args), base, generic_args, - }); - Some( - self.trap - .emit(generated::ConcreteTypeCanonicalPath { - id: trap_key!(path), - path, - }) - .into(), - ) - } else if let Some((it, size)) = ty.as_array(sema.db) { - let modifiers = ["[".to_owned(), format!("; {size}]")]; - let bases = vec![self.canonical_path_from_type(it)?]; - Some(self.emit_derived_type_canonical_path(&modifiers, bases)) - } else if let Some(it) = ty.as_slice() { - let modifiers = ["[", "]"]; - let bases = vec![self.canonical_path_from_type(it)?]; - Some(self.emit_derived_type_canonical_path(&modifiers, bases)) - } else if ty.is_unit() { - let modifiers = ["()"]; - let bases = vec![]; - Some(self.emit_derived_type_canonical_path(&modifiers, bases)) - } else if let Some(it) = ty.as_builtin() { - let name = it.name().as_str().to_owned(); - Some( - self.trap - .emit(generated::BuiltinTypeCanonicalPath { - id: trap_key!(name), - name, - }) - .into(), - ) - } else if let Some((it, mutability)) = ty.as_reference() { - let modifiers = [format!("&{}", mutability.as_keyword_for_ref())]; - let bases = vec![self.canonical_path_from_type(it)?]; - Some(self.emit_derived_type_canonical_path(&modifiers, bases)) - } else if ty.as_type_param(sema.db).is_some() { - // from the canonical path perspective, we just want a special name - // e.g. `crate::<_ as SomeTrait>::func` - // TODO: This will not work for assigning trap keys to types themselves! - Some( - self.trap - .emit(generated::PlaceholderTypeCanonicalPath { id: trap_key!("_") }) - .into(), - ) - } else { - let tuple_args = ty.tuple_fields(sema.db); - if tuple_args.is_empty() { - None - } else { - let modifiers = std::iter::once("(") - .chain(std::iter::repeat(", ").take(tuple_args.len() - 1)) - .chain(std::iter::once(")")) - .collect::>(); - let bases = tuple_args - .into_iter() - .map(|arg| self.canonical_path_from_type(arg)) - .collect::>>()?; - Some(self.emit_derived_type_canonical_path(&modifiers, bases)) - } - } + })) + }) } - fn emit_crate_root(&mut self, item: Crate) -> Option> { - let db = self.semantics.unwrap().db; - let (repo, name) = match item.origin(db) { - CrateOrigin::Rustc { name } => (None, Some(name)), - CrateOrigin::Local { repo, name } => (repo, name), - CrateOrigin::Library { repo, name } => (repo, Some(name)), - CrateOrigin::Lang(it) => { - let name = it.to_string(); - return Some( - self.trap - .emit(generated::LangCrateRoot { - id: trap_key!(name), - name, - }) - .into(), - ); - } - }; - let name = name.map(|s| s.to_string()); - let vfs_path = self.vfs.unwrap().file_path(item.root_file(db)); - let Some(file) = vfs_path.as_path() else { - warn!("Crate root file not found: {}", vfs_path); - return None; - }; - let source = self.trap.emit_file(&PathBuf::from(file.as_os_str())); - Some( - self.trap - .emit(generated::RepoCrateRoot { - id: trap_key!(source, name, repo), - name, - repo, - source, - }) - .into(), - ) - } - - fn canonical_path_from_hir_module( + fn canonical_path_from_function( &mut self, - item: Module, - ) -> Option> { - // if we have a Hir entity, it means we have semantics - let sema = self.semantics.as_ref().unwrap(); - // do not assign namespaces to unnamed modules, i.e. a virtual modules for block scopes - if ModuleId::from(item).is_block_module() { - return None; - } - let path = item - .path_to_root(sema.db) - .iter() - .filter_map(|m| m.name(sema.db)) - .map(|n| n.as_str().to_owned()) - .join("::"); - let root = self.emit_crate_root(item.krate())?; - Some(self.trap.emit(generated::Namespace { - id: trap_key!(root, "::", path), - root, - path, - })) - } - - fn canonical_path_from_trait( - &mut self, - item: Trait, - ) -> Option> { - // if we have a Hir entity, it means we have semantics - let sema = self.semantics.as_ref().unwrap(); - let module = item.module(sema.db); - let name = item.name(sema.db).as_str().to_owned(); - let namespace = self.canonical_path_from_hir_module(module)?; - Some(self.trap.emit(generated::ModuleItemCanonicalPath { - id: trap_key!(namespace, "::", name), - namespace, - name, - })) - } - - fn canonical_path_from_enum( - &mut self, - item: Enum, - ) -> Option> { - // if we have a Hir entity, it means we have semantics - let sema = self.semantics.as_ref().unwrap(); - let module = item.module(sema.db); - let name = item.name(sema.db).as_str().to_owned(); - let namespace = self.canonical_path_from_hir_module(module)?; - Some(self.trap.emit(generated::ModuleItemCanonicalPath { - id: trap_key!(namespace, "::", name), - namespace, - name, - })) - } - - fn canonical_path_from_trait_ref( - &mut self, - item: TraitRef, - ) -> Option> { - let db = self.semantics.unwrap().db; - let trait_ = item.trait_(); - let base = self.canonical_path_from_trait(trait_)?; - let param_count = trait_.type_or_const_param_count(db, false); - let mut generic_args = Vec::new(); - for i in 1..=param_count { - // TODO seems like you can't get const arguments currently... - let arg = item.get_type_argument(i)?; - let path = self.canonical_path_from_type(arg)?; - generic_args.push( - self.trap - .emit(generated::TypeGenericTypeArg { - id: trap_key!(path), - path, - }) - .into(), - ); - } - Some(self.trap.emit(generated::ParametrizedCanonicalPath { - id: trap_key!(base, generic_args), - base, - generic_args, - })) - } - - fn canonical_path_from_hir( - &mut self, - item: impl AddressableHir, + item: Function, ) -> Option> { // if we have a Hir entity, it means we have semantics let sema = self.semantics.as_ref().unwrap(); - let name = item.name(sema)?; + let name = item.name(sema.db).as_str().to_owned(); match item.container(sema.db) { ItemContainer::Trait(it) => { - let parent = self.canonical_path_from_trait(it)?; + let parent = self.canonical_path_from_module_item(it)?; Some( self.trap .emit(generated::TypeItemCanonicalPath { @@ -603,7 +596,7 @@ impl<'a> Translator<'a> { ) } ItemContainer::Module(it) => { - let namespace = self.canonical_path_from_hir_module(it)?; + let namespace = self.canonical_path_from_module(it)?; Some( self.trap .emit(generated::ModuleItemCanonicalPath { @@ -624,12 +617,18 @@ impl<'a> Translator<'a> { item: ModuleDef, ) -> Option> { match item { - ModuleDef::Module(it) => self.canonical_path_from_hir(it), - ModuleDef::Function(it) => self.canonical_path_from_hir(it), - ModuleDef::Adt(Adt::Enum(it)) => self.canonical_path_from_hir(it), - ModuleDef::Adt(Adt::Struct(it)) => self.canonical_path_from_hir(it), - ModuleDef::Adt(Adt::Union(it)) => self.canonical_path_from_hir(it), - ModuleDef::Trait(it) => self.canonical_path_from_hir(it), + ModuleDef::Module(it) => self.canonical_path_from_module(it).map(|l| l.into()), + ModuleDef::Function(it) => self.canonical_path_from_function(it), + ModuleDef::Adt(Adt::Enum(it)) => { + self.canonical_path_from_module_item(it).map(|l| l.into()) + } + ModuleDef::Adt(Adt::Struct(it)) => { + self.canonical_path_from_module_item(it).map(|l| l.into()) + } + ModuleDef::Adt(Adt::Union(it)) => { + self.canonical_path_from_module_item(it).map(|l| l.into()) + } + ModuleDef::Trait(it) => self.canonical_path_from_module_item(it).map(|l| l.into()), ModuleDef::Variant(it) => self.canonical_path_from_enum_variant(it), ModuleDef::Static(_) => None, ModuleDef::TraitAlias(_) => None, @@ -644,32 +643,124 @@ impl<'a> Translator<'a> { &mut self, item: Variant, ) -> Option> { - // if we have a Hir entity, it means we have semantics - let sema = self.semantics.as_ref().unwrap(); - let enum_ = item.parent_enum(sema.db); - let name = item.name(sema.db).as_str().to_owned(); - let parent = self.canonical_path_from_enum(enum_)?; - Some( - self.trap - .emit(generated::TypeItemCanonicalPath { - id: trap_key!(parent, "::", name), - parent, - name, - }) - .into(), - ) + cache_get_or_assign!(self.canonical_path_cache, item, { + // if we have a Hir entity, it means we have semantics + let sema = self.semantics.as_ref().unwrap(); + let enum_ = item.parent_enum(sema.db); + let name = item.name(sema.db).as_str().to_owned(); + let parent = self.canonical_path_from_module_item(enum_)?; + Some( + self.trap + .emit(generated::TypeItemCanonicalPath { + id: trap_key!(parent, "::", name), + parent, + name, + }) + .into(), + ) + }) } - pub(crate) fn extract_canonical_origin( + pub(crate) fn extract_canonical_origin_of_module( &mut self, - item: &T, - label: Label, + item: &ast::Module, + label: Label, ) { (|| { let sema = self.semantics.as_ref()?; - let def = T::Hir::try_from_source(item, sema)?; - let path = self.canonical_path_from_hir(def)?; - generated::Addressable::emit_canonical_path(label, path, &mut self.trap.writer); + let def = sema.to_module_def(item)?; + let path = self.canonical_path_from_module(def)?; + generated::Addressable::emit_canonical_path( + label.into(), + path.into(), + &mut self.trap.writer, + ); + Some(()) + })(); + } + + pub(crate) fn extract_canonical_origin_of_function( + &mut self, + item: &ast::Fn, + label: Label, + ) { + (|| { + let sema = self.semantics.as_ref()?; + let def = sema.to_fn_def(item)?; + let path = self.canonical_path_from_function(def)?; + generated::Addressable::emit_canonical_path(label.into(), path, &mut self.trap.writer); + Some(()) + })(); + } + + pub(crate) fn extract_canonical_origin_of_enum( + &mut self, + item: &ast::Enum, + label: Label, + ) { + (|| { + let sema = self.semantics.as_ref()?; + let def = sema.to_enum_def(item)?; + let path = self.canonical_path_from_module_item(def)?; + generated::Addressable::emit_canonical_path( + label.into(), + path.into(), + &mut self.trap.writer, + ); + Some(()) + })(); + } + + pub(crate) fn extract_canonical_origin_of_struct( + &mut self, + item: &ast::Struct, + label: Label, + ) { + (|| { + let sema = self.semantics.as_ref()?; + let def = sema.to_struct_def(item)?; + let path = self.canonical_path_from_module_item(def)?; + generated::Addressable::emit_canonical_path( + label.into(), + path.into(), + &mut self.trap.writer, + ); + Some(()) + })(); + } + + pub(crate) fn extract_canonical_origin_of_union( + &mut self, + item: &ast::Union, + label: Label, + ) { + (|| { + let sema = self.semantics.as_ref()?; + let def = sema.to_union_def(item)?; + let path = self.canonical_path_from_module_item(def)?; + generated::Addressable::emit_canonical_path( + label.into(), + path.into(), + &mut self.trap.writer, + ); + Some(()) + })(); + } + + pub(crate) fn extract_canonical_origin_of_trait( + &mut self, + item: &ast::Trait, + label: Label, + ) { + (|| { + let sema = self.semantics.as_ref()?; + let def = sema.to_trait_def(item)?; + let path = self.canonical_path_from_module_item(def)?; + generated::Addressable::emit_canonical_path( + label.into(), + path.into(), + &mut self.trap.writer, + ); Some(()) })(); } @@ -717,7 +808,7 @@ impl<'a> Translator<'a> { let Either::Left(function) = resolved else { return None; }; - let path = self.canonical_path_from_hir(function)?; + let path = self.canonical_path_from_function(function)?; generated::Resolvable::emit_resolved_canonical_path( label.into(), path, diff --git a/rust/extractor/src/translate/label_cache.rs b/rust/extractor/src/translate/label_cache.rs new file mode 100644 index 00000000000..d39c9b653a3 --- /dev/null +++ b/rust/extractor/src/translate/label_cache.rs @@ -0,0 +1,151 @@ +use crate::generated::{self}; +use crate::trap::{Label, TrapClass, UntypedLabel}; +use ra_ap_hir::{Crate, Enum, Function, Module, Struct, Trait, TraitRef, Type, Union, Variant}; +use std::collections::HashMap; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Key { + Type(Type), + Function(Function), + Trait(Trait), + Enum(Enum), + Union(Union), + Struct(Struct), + Crate(Crate), + Module(Module), + TraitRef(TraitRef), + Variant(Variant), +} + +pub trait StorableAsCanonicalPath { + type TrapClass: TrapClass; + + fn to_key(&self) -> Key; +} + +pub trait StorableAsModuleItemCanonicalPath { + fn to_key(&self) -> Key; +} + +impl StorableAsCanonicalPath for T { + type TrapClass = generated::ModuleItemCanonicalPath; + + fn to_key(&self) -> Key { + ::to_key(self) + } +} + +impl StorableAsCanonicalPath for Type { + type TrapClass = generated::TypeCanonicalPath; + + fn to_key(&self) -> Key { + Key::Type(self.clone()) + } +} + +impl StorableAsCanonicalPath for Function { + type TrapClass = generated::CanonicalPath; + + fn to_key(&self) -> Key { + Key::Function(*self) + } +} + +impl StorableAsModuleItemCanonicalPath for Trait { + fn to_key(&self) -> Key { + Key::Trait(*self) + } +} + +impl StorableAsModuleItemCanonicalPath for Enum { + fn to_key(&self) -> Key { + Key::Enum(*self) + } +} + +impl StorableAsModuleItemCanonicalPath for Union { + fn to_key(&self) -> Key { + Key::Union(*self) + } +} + +impl StorableAsModuleItemCanonicalPath for Struct { + fn to_key(&self) -> Key { + Key::Struct(*self) + } +} + +impl StorableAsCanonicalPath for Crate { + type TrapClass = generated::CrateRoot; + + fn to_key(&self) -> Key { + Key::Crate(*self) + } +} + +impl StorableAsCanonicalPath for Module { + type TrapClass = generated::Namespace; + + fn to_key(&self) -> Key { + Key::Module(*self) + } +} + +impl StorableAsCanonicalPath for TraitRef { + type TrapClass = generated::ParametrizedCanonicalPath; + + fn to_key(&self) -> Key { + Key::TraitRef(self.clone()) + } +} + +impl StorableAsCanonicalPath for Variant { + type TrapClass = generated::CanonicalPath; + + fn to_key(&self) -> Key { + Key::Variant(*self) + } +} + +pub struct CanonicalPathLabelCache { + cache: HashMap>, +} + +impl CanonicalPathLabelCache { + pub fn new() -> Self { + Self { + cache: HashMap::new(), + } + } + + pub fn get(&self, key: &T) -> Option>> { + self.cache.get(&key.to_key()).map(|maybe| { + maybe.map(|l| { + // UNSAFE: The label can only have been inserted as Label + unsafe { Label::from_untyped(l) } + }) + }) + } + + pub fn insert( + &mut self, + key: &T, + label: Option>, + ) { + self.cache + .insert(key.to_key(), label.map(|l| l.as_untyped())); + } +} + +#[macro_export] +macro_rules! cache_get_or_assign { + ($cache:expr, $key:expr, $get:expr) => {{ + if let Some(label) = $cache.get(&$key) { + label + } else { + let label = $get; + $cache.insert(&$key, label.clone()); + label + } + }}; +} diff --git a/rust/extractor/src/translate/mappings.rs b/rust/extractor/src/translate/mappings.rs index 34d000fe3af..c3ca63a3b4f 100644 --- a/rust/extractor/src/translate/mappings.rs +++ b/rust/extractor/src/translate/mappings.rs @@ -1,4 +1,5 @@ -use ra_ap_hir::{Enum, Function, HasContainer, Module, Semantics, Struct, Trait, Union}; +use crate::translate::label_cache::StorableAsModuleItemCanonicalPath; +use ra_ap_hir::{Enum, Module, Semantics, Struct, Trait, Union}; use ra_ap_ide_db::RootDatabase; use ra_ap_syntax::{ast, ast::RangeItem, AstNode}; @@ -52,100 +53,6 @@ impl TextValue for ast::RangePat { self.op_token().map(|x| x.text().to_string()) } } - -pub(crate) trait AddressableHir: HasContainer + Copy { - fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option; - fn try_from_source(value: &Ast, sema: &Semantics<'_, RootDatabase>) -> Option; -} - -impl AddressableHir for Function { - fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option { - Some(self.name(sema.db).as_str().to_owned()) - } - - fn try_from_source(value: &ast::Fn, sema: &Semantics<'_, RootDatabase>) -> Option { - sema.to_fn_def(value) - } -} - -impl AddressableHir for Trait { - fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option { - Some(self.name(sema.db).as_str().to_owned()) - } - - fn try_from_source(value: &ast::Trait, sema: &Semantics<'_, RootDatabase>) -> Option { - sema.to_trait_def(value) - } -} - -impl AddressableHir for Module { - fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option { - self.name(sema.db).map(|s| s.as_str().to_owned()) - } - - fn try_from_source(value: &ast::Module, sema: &Semantics<'_, RootDatabase>) -> Option { - sema.to_module_def(value) - } -} - -impl AddressableHir for Struct { - fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option { - Some(self.name(sema.db).as_str().to_owned()) - } - - fn try_from_source(value: &ast::Struct, sema: &Semantics<'_, RootDatabase>) -> Option { - sema.to_struct_def(value) - } -} - -impl AddressableHir for Enum { - fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option { - Some(self.name(sema.db).as_str().to_owned()) - } - - fn try_from_source(value: &ast::Enum, sema: &Semantics<'_, RootDatabase>) -> Option { - sema.to_enum_def(value) - } -} - -impl AddressableHir for Union { - fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option { - Some(self.name(sema.db).as_str().to_owned()) - } - - fn try_from_source(value: &ast::Union, sema: &Semantics<'_, RootDatabase>) -> Option { - sema.to_union_def(value) - } -} - -pub(crate) trait AddressableAst: AstNode + Sized { - type Hir: AddressableHir; -} - -impl AddressableAst for ast::Fn { - type Hir = Function; -} - -impl AddressableAst for ast::Trait { - type Hir = Trait; -} - -impl AddressableAst for ast::Struct { - type Hir = Struct; -} - -impl AddressableAst for ast::Enum { - type Hir = Enum; -} - -impl AddressableAst for ast::Union { - type Hir = Union; -} - -impl AddressableAst for ast::Module { - type Hir = Module; -} - pub trait PathAst: AstNode { fn path(&self) -> Option; } @@ -179,3 +86,48 @@ impl PathAst for ast::TupleStructPat { self.path() } } + +pub trait ModuleItem: StorableAsModuleItemCanonicalPath { + fn module(&self, sema: &Semantics<'_, RootDatabase>) -> Module; + fn name(&self, sema: &Semantics<'_, RootDatabase>) -> String; +} + +impl ModuleItem for Enum { + fn module(&self, sema: &Semantics<'_, RootDatabase>) -> Module { + Enum::module(*self, sema.db) + } + + fn name(&self, sema: &Semantics<'_, RootDatabase>) -> String { + Enum::name(*self, sema.db).as_str().to_owned() + } +} + +impl ModuleItem for Struct { + fn module(&self, sema: &Semantics<'_, RootDatabase>) -> Module { + Struct::module(*self, sema.db) + } + + fn name(&self, sema: &Semantics<'_, RootDatabase>) -> String { + Struct::name(*self, sema.db).as_str().to_owned() + } +} + +impl ModuleItem for Union { + fn module(&self, sema: &Semantics<'_, RootDatabase>) -> Module { + Union::module(*self, sema.db) + } + + fn name(&self, sema: &Semantics<'_, RootDatabase>) -> String { + Union::name(*self, sema.db).as_str().to_owned() + } +} + +impl ModuleItem for Trait { + fn module(&self, sema: &Semantics<'_, RootDatabase>) -> Module { + Trait::module(*self, sema.db) + } + + fn name(&self, sema: &Semantics<'_, RootDatabase>) -> String { + Trait::name(*self, sema.db).as_str().to_owned() + } +}