Skip to content

Commit 194e0da

Browse files
author
Paolo Tranquilli
committed
Rust: add canonical_path and crate_origin to Item
1 parent 7338eaf commit 194e0da

File tree

82 files changed

+852
-144
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+852
-144
lines changed

rust/ast-generator/src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,8 @@ fn write_extractor(grammar: &AstSrc) -> std::io::Result<String> {
440440
"//! Generated by `ast-generator`, do not edit by hand.\n
441441
#![cfg_attr(any(), rustfmt::skip)]
442442
443-
use super::base::{{TextValue, Translator}};
443+
use super::base::Translator;
444+
use super::mappings::TextValue;
444445
use crate::emit_detached;
445446
use crate::generated;
446447
use crate::trap::{{Label, TrapId}};

rust/extractor/src/generated/.generated.list

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/extractor/src/generated/top.rs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/extractor/src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ fn extract(
3434
let line_index = LineIndex::new(text.as_ref());
3535
let display_path = file.to_string_lossy();
3636
let mut trap = traps.create("source", file);
37+
trap.writer.comment(format!(
38+
"semantics: {}",
39+
if semantics.is_some() { "yes" } else { "no" }
40+
));
3741
let label = trap.emit_file(file);
3842
let mut translator = translate::Translator::new(
3943
trap,

rust/extractor/src/translate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod base;
22
mod generated;
3+
mod mappings;
34

45
pub use base::Translator;

rust/extractor/src/translate/base.rs

Lines changed: 135 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1+
use super::mappings::{AddressableAst, AddressableHir};
12
use crate::generated::MacroCall;
23
use crate::generated::{self};
34
use crate::trap::{DiagnosticSeverity, TrapFile, TrapId};
45
use crate::trap::{Label, TrapClass};
56
use codeql_extractor::trap::{self};
67
use log::Level;
8+
use ra_ap_base_db::CrateOrigin;
79
use ra_ap_hir::db::ExpandDatabase;
8-
use ra_ap_hir::Semantics;
10+
use ra_ap_hir::{Adt, ItemContainer, Module, Semantics, Type};
11+
use ra_ap_hir_def::type_ref::Mutability;
912
use ra_ap_hir_expand::ExpandTo;
1013
use ra_ap_ide_db::line_index::{LineCol, LineIndex};
1114
use ra_ap_ide_db::RootDatabase;
1215
use ra_ap_parser::SyntaxKind;
1316
use ra_ap_span::{EditionedFileId, TextSize};
14-
use ra_ap_syntax::ast::RangeItem;
17+
use ra_ap_syntax::ast::HasName;
1518
use ra_ap_syntax::{
1619
ast, AstNode, NodeOrToken, SyntaxElementChildren, SyntaxError, SyntaxNode, SyntaxToken,
1720
TextRange,
@@ -20,62 +23,30 @@ use ra_ap_syntax::{
2023
#[macro_export]
2124
macro_rules! emit_detached {
2225
(MacroCall, $self:ident, $node:ident, $label:ident) => {
23-
$self.extract_macro_call_expanded(&$node, $label.into());
26+
$self.extract_macro_call_expanded(&$node, $label);
2427
};
28+
(Function, $self:ident, $node:ident, $label:ident) => {
29+
$self.extract_canonical_origin(&$node, $label.into());
30+
};
31+
(Trait, $self:ident, $node:ident, $label:ident) => {
32+
$self.extract_canonical_origin(&$node, $label.into());
33+
};
34+
(Struct, $self:ident, $node:ident, $label:ident) => {
35+
$self.extract_canonical_origin(&$node, $label.into());
36+
};
37+
(Enum, $self:ident, $node:ident, $label:ident) => {
38+
$self.extract_canonical_origin(&$node, $label.into());
39+
};
40+
(Union, $self:ident, $node:ident, $label:ident) => {
41+
$self.extract_canonical_origin(&$node, $label.into());
42+
};
43+
(Module, $self:ident, $node:ident, $label:ident) => {
44+
$self.extract_canonical_origin(&$node, $label.into());
45+
};
46+
// TODO canonical origin of other items
2547
($($_:tt)*) => {};
2648
}
2749

28-
pub trait TextValue {
29-
fn try_get_text(&self) -> Option<String>;
30-
}
31-
32-
impl TextValue for ast::Lifetime {
33-
fn try_get_text(&self) -> Option<String> {
34-
self.text().to_string().into()
35-
}
36-
}
37-
impl TextValue for ast::Name {
38-
fn try_get_text(&self) -> Option<String> {
39-
self.text().to_string().into()
40-
}
41-
}
42-
impl TextValue for ast::Literal {
43-
fn try_get_text(&self) -> Option<String> {
44-
self.token().text().to_string().into()
45-
}
46-
}
47-
impl TextValue for ast::NameRef {
48-
fn try_get_text(&self) -> Option<String> {
49-
self.text().to_string().into()
50-
}
51-
}
52-
impl TextValue for ast::Abi {
53-
fn try_get_text(&self) -> Option<String> {
54-
self.abi_string().map(|x| x.to_string())
55-
}
56-
}
57-
58-
impl TextValue for ast::BinExpr {
59-
fn try_get_text(&self) -> Option<String> {
60-
self.op_token().map(|x| x.text().to_string())
61-
}
62-
}
63-
impl TextValue for ast::PrefixExpr {
64-
fn try_get_text(&self) -> Option<String> {
65-
self.op_token().map(|x| x.text().to_string())
66-
}
67-
}
68-
impl TextValue for ast::RangeExpr {
69-
fn try_get_text(&self) -> Option<String> {
70-
self.op_token().map(|x| x.text().to_string())
71-
}
72-
}
73-
impl TextValue for ast::RangePat {
74-
fn try_get_text(&self) -> Option<String> {
75-
self.op_token().map(|x| x.text().to_string())
76-
}
77-
}
78-
7950
pub struct Translator<'a> {
8051
pub trap: TrapFile,
8152
path: &'a str,
@@ -325,4 +296,114 @@ impl<'a> Translator<'a> {
325296
);
326297
}
327298
}
299+
fn canonical_path_from_type(&self, ty: Type) -> Option<String> {
300+
let sema = self.semantics.as_ref().unwrap();
301+
// rust-analyzer doesn't provide a type enum directly
302+
if let Some(it) = ty.as_adt() {
303+
return match it {
304+
Adt::Struct(it) => self.canonical_path_from_hir(it),
305+
Adt::Union(it) => self.canonical_path_from_hir(it),
306+
Adt::Enum(it) => self.canonical_path_from_hir(it),
307+
};
308+
};
309+
if let Some((it, size)) = ty.as_array(sema.db) {
310+
return self
311+
.canonical_path_from_type(it)
312+
.map(|p| format!("[{p}; {size}]"));
313+
}
314+
if let Some(it) = ty.as_slice() {
315+
return self
316+
.canonical_path_from_type(it)
317+
.map(|p| format!("[{}]", p));
318+
}
319+
if let Some(it) = ty.as_builtin() {
320+
return Some(it.name().as_str().to_owned());
321+
}
322+
if let Some(it) = ty.as_dyn_trait() {
323+
return self.canonical_path_from_hir(it).map(|p| format!("dyn {p}"));
324+
}
325+
if let Some((it, mutability)) = ty.as_reference() {
326+
let mut_str = match mutability {
327+
Mutability::Shared => "",
328+
Mutability::Mut => "mut ",
329+
};
330+
return self
331+
.canonical_path_from_type(it)
332+
.map(|p| format!("&{mut_str}{p}"));
333+
}
334+
if let Some(it) = ty.as_impl_traits(sema.db) {
335+
let paths = it
336+
.map(|t| self.canonical_path_from_hir(t))
337+
.collect::<Option<Vec<_>>>()?;
338+
return Some(format!("impl {}", paths.join(" + ")));
339+
}
340+
if let Some(_) = ty.as_type_param(sema.db) {
341+
// from the canonical path perspective, we just want a special name
342+
// e.g. `crate::<_ as SomeTrait>::func`
343+
return Some("_".to_owned());
344+
}
345+
None
346+
}
347+
348+
fn canonical_path_from_hir_module(&self, item: Module) -> Option<String> {
349+
if item.is_crate_root() {
350+
return Some("crate".into());
351+
}
352+
self.canonical_path_from_hir::<ast::Module>(item)
353+
}
354+
355+
fn canonical_path_from_hir<T: AstNode>(&self, item: impl AddressableHir<T>) -> Option<String> {
356+
// if we have a Hir entity, it means we have semantics
357+
let sema = self.semantics.as_ref().unwrap();
358+
let name = item.name(sema)?;
359+
let container = item.container(sema.db);
360+
let prefix = match container {
361+
ItemContainer::Trait(it) => self.canonical_path_from_hir(it),
362+
ItemContainer::Impl(it) => {
363+
let ty = self.canonical_path_from_type(it.self_ty(sema.db))?;
364+
if let Some(trait_) = it.trait_(sema.db) {
365+
let tr = self.canonical_path_from_hir(trait_)?;
366+
Some(format!("<{ty} as {tr}>"))
367+
} else {
368+
Some(format!("<{ty}>"))
369+
}
370+
}
371+
ItemContainer::Module(it) => self.canonical_path_from_hir_module(it),
372+
ItemContainer::ExternBlock() | ItemContainer::Crate(_) => Some("".to_owned()),
373+
}?;
374+
Some(format!("{prefix}::{name}"))
375+
}
376+
377+
fn origin_from_hir<T: AstNode>(&self, item: impl AddressableHir<T>) -> String {
378+
// if we have a Hir entity, it means we have semantics
379+
let sema = self.semantics.as_ref().unwrap();
380+
match item.module(sema).krate().origin(sema.db) {
381+
CrateOrigin::Rustc { name } => format!("rustc:{}", name),
382+
CrateOrigin::Local { repo, name } => format!(
383+
"repo:{}:{}",
384+
repo.unwrap_or_default(),
385+
name.map(|s| s.as_str().to_owned()).unwrap_or_default()
386+
),
387+
CrateOrigin::Library { repo, name } => {
388+
format!("repo:{}:{}", repo.unwrap_or_default(), name)
389+
}
390+
CrateOrigin::Lang(it) => format!("lang:{}", it),
391+
}
392+
}
393+
394+
pub(crate) fn extract_canonical_origin<T: AddressableAst + HasName>(
395+
&mut self,
396+
item: &T,
397+
label: Label<generated::Item>,
398+
) {
399+
(|| {
400+
let sema = self.semantics.as_ref()?;
401+
let def = T::Hir::try_from_source(item, sema)?;
402+
let path = self.canonical_path_from_hir(def)?;
403+
let origin = self.origin_from_hir(def);
404+
generated::Item::emit_crate_origin(label, origin, &mut self.trap.writer);
405+
generated::Item::emit_canonical_path(label, path, &mut self.trap.writer);
406+
Some(())
407+
})();
408+
}
328409
}

rust/extractor/src/translate/generated.rs

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)