Skip to content

Commit 7e8a09a

Browse files
authored
Merge pull request github#17825 from github/redsun82/rust-canonical-path
Rust: add `extended_canonical_path` and `crate_origin` to `Item`
2 parents f75615b + 9c95a17 commit 7e8a09a

File tree

127 files changed

+1124
-202
lines changed

Some content is hidden

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

127 files changed

+1124
-202
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: 141 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
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::salsa::InternKey;
9+
use ra_ap_base_db::CrateOrigin;
710
use ra_ap_hir::db::ExpandDatabase;
8-
use ra_ap_hir::Semantics;
11+
use ra_ap_hir::{Adt, ItemContainer, Module, Semantics, Type};
12+
use ra_ap_hir_def::type_ref::Mutability;
13+
use ra_ap_hir_def::ModuleId;
914
use ra_ap_hir_expand::ExpandTo;
1015
use ra_ap_ide_db::line_index::{LineCol, LineIndex};
1116
use ra_ap_ide_db::RootDatabase;
1217
use ra_ap_parser::SyntaxKind;
1318
use ra_ap_span::{EditionedFileId, TextSize};
14-
use ra_ap_syntax::ast::RangeItem;
19+
use ra_ap_syntax::ast::HasName;
1520
use ra_ap_syntax::{
1621
ast, AstNode, NodeOrToken, SyntaxElementChildren, SyntaxError, SyntaxNode, SyntaxToken,
1722
TextRange,
@@ -20,62 +25,30 @@ use ra_ap_syntax::{
2025
#[macro_export]
2126
macro_rules! emit_detached {
2227
(MacroCall, $self:ident, $node:ident, $label:ident) => {
23-
$self.extract_macro_call_expanded(&$node, $label.into());
28+
$self.extract_macro_call_expanded(&$node, $label);
2429
};
30+
(Function, $self:ident, $node:ident, $label:ident) => {
31+
$self.extract_canonical_origin(&$node, $label.into());
32+
};
33+
(Trait, $self:ident, $node:ident, $label:ident) => {
34+
$self.extract_canonical_origin(&$node, $label.into());
35+
};
36+
(Struct, $self:ident, $node:ident, $label:ident) => {
37+
$self.extract_canonical_origin(&$node, $label.into());
38+
};
39+
(Enum, $self:ident, $node:ident, $label:ident) => {
40+
$self.extract_canonical_origin(&$node, $label.into());
41+
};
42+
(Union, $self:ident, $node:ident, $label:ident) => {
43+
$self.extract_canonical_origin(&$node, $label.into());
44+
};
45+
(Module, $self:ident, $node:ident, $label:ident) => {
46+
$self.extract_canonical_origin(&$node, $label.into());
47+
};
48+
// TODO canonical origin of other items
2549
($($_:tt)*) => {};
2650
}
2751

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-
7952
pub struct Translator<'a> {
8053
pub trap: TrapFile,
8154
path: &'a str,
@@ -325,4 +298,118 @@ impl<'a> Translator<'a> {
325298
);
326299
}
327300
}
301+
fn canonical_path_from_type(&self, ty: Type) -> Option<String> {
302+
let sema = self.semantics.as_ref().unwrap();
303+
// rust-analyzer doesn't provide a type enum directly
304+
if let Some(it) = ty.as_adt() {
305+
return match it {
306+
Adt::Struct(it) => self.canonical_path_from_hir(it),
307+
Adt::Union(it) => self.canonical_path_from_hir(it),
308+
Adt::Enum(it) => self.canonical_path_from_hir(it),
309+
};
310+
};
311+
if let Some((it, size)) = ty.as_array(sema.db) {
312+
return self
313+
.canonical_path_from_type(it)
314+
.map(|p| format!("[{p}; {size}]"));
315+
}
316+
if let Some(it) = ty.as_slice() {
317+
return self
318+
.canonical_path_from_type(it)
319+
.map(|p| format!("[{}]", p));
320+
}
321+
if let Some(it) = ty.as_builtin() {
322+
return Some(it.name().as_str().to_owned());
323+
}
324+
if let Some(it) = ty.as_dyn_trait() {
325+
return self.canonical_path_from_hir(it).map(|p| format!("dyn {p}"));
326+
}
327+
if let Some((it, mutability)) = ty.as_reference() {
328+
let mut_str = match mutability {
329+
Mutability::Shared => "",
330+
Mutability::Mut => "mut ",
331+
};
332+
return self
333+
.canonical_path_from_type(it)
334+
.map(|p| format!("&{mut_str}{p}"));
335+
}
336+
if let Some(it) = ty.as_impl_traits(sema.db) {
337+
let paths = it
338+
.map(|t| self.canonical_path_from_hir(t))
339+
.collect::<Option<Vec<_>>>()?;
340+
return Some(format!("impl {}", paths.join(" + ")));
341+
}
342+
if ty.as_type_param(sema.db).is_some() {
343+
// from the canonical path perspective, we just want a special name
344+
// e.g. `crate::<_ as SomeTrait>::func`
345+
return Some("_".to_owned());
346+
}
347+
None
348+
}
349+
350+
fn canonical_path_from_hir_module(&self, item: Module) -> Option<String> {
351+
if let Some(block_id) = ModuleId::from(item).containing_block() {
352+
// this means this is a block module, i.e. a virtual module for a block scope
353+
return Some(format!("{{{}}}", block_id.as_intern_id()));
354+
}
355+
if item.is_crate_root() {
356+
return Some("crate".into());
357+
}
358+
self.canonical_path_from_hir::<ast::Module>(item)
359+
}
360+
361+
fn canonical_path_from_hir<T: AstNode>(&self, item: impl AddressableHir<T>) -> Option<String> {
362+
// if we have a Hir entity, it means we have semantics
363+
let sema = self.semantics.as_ref().unwrap();
364+
let name = item.name(sema)?;
365+
let container = item.container(sema.db);
366+
let prefix = match container {
367+
ItemContainer::Trait(it) => self.canonical_path_from_hir(it),
368+
ItemContainer::Impl(it) => {
369+
let ty = self.canonical_path_from_type(it.self_ty(sema.db))?;
370+
if let Some(trait_) = it.trait_(sema.db) {
371+
let tr = self.canonical_path_from_hir(trait_)?;
372+
Some(format!("<{ty} as {tr}>"))
373+
} else {
374+
Some(format!("<{ty}>"))
375+
}
376+
}
377+
ItemContainer::Module(it) => self.canonical_path_from_hir_module(it),
378+
ItemContainer::ExternBlock() | ItemContainer::Crate(_) => Some("".to_owned()),
379+
}?;
380+
Some(format!("{prefix}::{name}"))
381+
}
382+
383+
fn origin_from_hir<T: AstNode>(&self, item: impl AddressableHir<T>) -> String {
384+
// if we have a Hir entity, it means we have semantics
385+
let sema = self.semantics.as_ref().unwrap();
386+
match item.module(sema).krate().origin(sema.db) {
387+
CrateOrigin::Rustc { name } => format!("rustc:{}", name),
388+
CrateOrigin::Local { repo, name } => format!(
389+
"repo:{}:{}",
390+
repo.unwrap_or_default(),
391+
name.map(|s| s.as_str().to_owned()).unwrap_or_default()
392+
),
393+
CrateOrigin::Library { repo, name } => {
394+
format!("repo:{}:{}", repo.unwrap_or_default(), name)
395+
}
396+
CrateOrigin::Lang(it) => format!("lang:{}", it),
397+
}
398+
}
399+
400+
pub(crate) fn extract_canonical_origin<T: AddressableAst + HasName>(
401+
&mut self,
402+
item: &T,
403+
label: Label<generated::Item>,
404+
) {
405+
(|| {
406+
let sema = self.semantics.as_ref()?;
407+
let def = T::Hir::try_from_source(item, sema)?;
408+
let path = self.canonical_path_from_hir(def)?;
409+
let origin = self.origin_from_hir(def);
410+
generated::Item::emit_crate_origin(label, origin, &mut self.trap.writer);
411+
generated::Item::emit_extended_canonical_path(label, path, &mut self.trap.writer);
412+
Some(())
413+
})();
414+
}
328415
}

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)