Rust: introduce canonical trap label caching

This commit is contained in:
Paolo Tranquilli
2024-12-09 17:57:01 +01:00
parent 9563d99ebc
commit aa129a78d9
4 changed files with 528 additions and 333 deletions

View File

@@ -1,5 +1,6 @@
mod base;
mod generated;
mod label_cache;
mod mappings;
pub use base::Translator;

View File

@@ -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<EditionedFileId>,
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<Label<generated::TypeCanonicalPath>> {
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::<Vec<_>>();
let bases = tuple_args
.into_iter()
.map(|arg| self.canonical_path_from_type(arg))
.collect::<Option<Vec<_>>>()?;
Some(self.emit_derived_type_canonical_path(&modifiers, bases))
}
}
})
}
fn emit_crate_root(&mut self, item: Crate) -> Option<Label<generated::CrateRoot>> {
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<Label<generated::Namespace>> {
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<Label<generated::ModuleItemCanonicalPath>> {
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<Label<generated::ParametrizedCanonicalPath>> {
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::<Vec<_>>();
let bases = tuple_args
.into_iter()
.map(|arg| self.canonical_path_from_type(arg))
.collect::<Option<Vec<_>>>()?;
Some(self.emit_derived_type_canonical_path(&modifiers, bases))
}
}
}))
})
}
fn emit_crate_root(&mut self, item: Crate) -> Option<Label<generated::CrateRoot>> {
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<Label<generated::Namespace>> {
// 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<Label<generated::ModuleItemCanonicalPath>> {
// 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<Label<generated::ModuleItemCanonicalPath>> {
// 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<Label<generated::ParametrizedCanonicalPath>> {
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<T: AstNode>(
&mut self,
item: impl AddressableHir<T>,
item: Function,
) -> Option<Label<generated::CanonicalPath>> {
// 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<Label<generated::CanonicalPath>> {
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<Label<generated::CanonicalPath>> {
// 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<T: AddressableAst + HasName>(
pub(crate) fn extract_canonical_origin_of_module(
&mut self,
item: &T,
label: Label<generated::Addressable>,
item: &ast::Module,
label: Label<generated::Module>,
) {
(|| {
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<generated::Function>,
) {
(|| {
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<generated::Enum>,
) {
(|| {
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<generated::Struct>,
) {
(|| {
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<generated::Union>,
) {
(|| {
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<generated::Trait>,
) {
(|| {
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,

View File

@@ -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<T: StorableAsModuleItemCanonicalPath> StorableAsCanonicalPath for T {
type TrapClass = generated::ModuleItemCanonicalPath;
fn to_key(&self) -> Key {
<Self as StorableAsModuleItemCanonicalPath>::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<Key, Option<UntypedLabel>>,
}
impl CanonicalPathLabelCache {
pub fn new() -> Self {
Self {
cache: HashMap::new(),
}
}
pub fn get<T: StorableAsCanonicalPath>(&self, key: &T) -> Option<Option<Label<T::TrapClass>>> {
self.cache.get(&key.to_key()).map(|maybe| {
maybe.map(|l| {
// UNSAFE: The label can only have been inserted as Label<T::TrapClass>
unsafe { Label::from_untyped(l) }
})
})
}
pub fn insert<T: StorableAsCanonicalPath>(
&mut self,
key: &T,
label: Option<Label<T::TrapClass>>,
) {
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
}
}};
}

View File

@@ -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<Ast: AstNode>: HasContainer + Copy {
fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option<String>;
fn try_from_source(value: &Ast, sema: &Semantics<'_, RootDatabase>) -> Option<Self>;
}
impl AddressableHir<ast::Fn> for Function {
fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
Some(self.name(sema.db).as_str().to_owned())
}
fn try_from_source(value: &ast::Fn, sema: &Semantics<'_, RootDatabase>) -> Option<Self> {
sema.to_fn_def(value)
}
}
impl AddressableHir<ast::Trait> for Trait {
fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
Some(self.name(sema.db).as_str().to_owned())
}
fn try_from_source(value: &ast::Trait, sema: &Semantics<'_, RootDatabase>) -> Option<Self> {
sema.to_trait_def(value)
}
}
impl AddressableHir<ast::Module> for Module {
fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
self.name(sema.db).map(|s| s.as_str().to_owned())
}
fn try_from_source(value: &ast::Module, sema: &Semantics<'_, RootDatabase>) -> Option<Self> {
sema.to_module_def(value)
}
}
impl AddressableHir<ast::Struct> for Struct {
fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
Some(self.name(sema.db).as_str().to_owned())
}
fn try_from_source(value: &ast::Struct, sema: &Semantics<'_, RootDatabase>) -> Option<Self> {
sema.to_struct_def(value)
}
}
impl AddressableHir<ast::Enum> for Enum {
fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
Some(self.name(sema.db).as_str().to_owned())
}
fn try_from_source(value: &ast::Enum, sema: &Semantics<'_, RootDatabase>) -> Option<Self> {
sema.to_enum_def(value)
}
}
impl AddressableHir<ast::Union> for Union {
fn name(self, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
Some(self.name(sema.db).as_str().to_owned())
}
fn try_from_source(value: &ast::Union, sema: &Semantics<'_, RootDatabase>) -> Option<Self> {
sema.to_union_def(value)
}
}
pub(crate) trait AddressableAst: AstNode + Sized {
type Hir: AddressableHir<Self>;
}
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<ast::Path>;
}
@@ -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()
}
}