mirror of
https://github.com/github/codeql.git
synced 2026-05-24 16:17:07 +02:00
Rust: introduce canonical trap label caching
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
mod base;
|
||||
mod generated;
|
||||
mod label_cache;
|
||||
mod mappings;
|
||||
|
||||
pub use base::Translator;
|
||||
|
||||
@@ -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,
|
||||
|
||||
151
rust/extractor/src/translate/label_cache.rs
Normal file
151
rust/extractor/src/translate/label_cache.rs
Normal 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
|
||||
}
|
||||
}};
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user