Skip to content

Commit cad0c79

Browse files
committed
Rust: Reintroduce extractor resolution
1 parent 2669780 commit cad0c79

File tree

4 files changed

+441
-3
lines changed

4 files changed

+441
-3
lines changed

rust/ast-generator/src/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ fn has_special_emission(type_name: &str) -> bool {
6464
| "Struct"
6565
| "Enum"
6666
| "Union"
67+
| "PathExpr"
68+
| "RecordExpr"
69+
| "PathPat"
70+
| "RecordPat"
71+
| "TupleStructPat"
72+
| "MethodCallExpr"
6773
| "PathSegment"
6874
| "Const"
6975
)

rust/extractor/src/translate/base.rs

Lines changed: 292 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
use super::mappings::Emission;
1+
use super::mappings::{AddressableHir, Emission, PathAst};
22
use crate::generated::{self};
33
use crate::rust_analyzer::FileSemanticInformation;
44
use crate::trap::{DiagnosticSeverity, TrapFile, TrapId};
55
use crate::trap::{Label, TrapClass};
6-
use ra_ap_base_db::EditionedFileId;
7-
use ra_ap_hir::Semantics;
6+
use itertools::Either;
7+
use ra_ap_base_db::{CrateOrigin, EditionedFileId};
88
use ra_ap_hir::db::ExpandDatabase;
9+
use ra_ap_hir::{
10+
Adt, Crate, ItemContainer, Module, ModuleDef, PathResolution, Semantics, Type, Variant,
11+
};
12+
use ra_ap_hir_def::ModuleId;
13+
use ra_ap_hir_def::type_ref::Mutability;
914
use ra_ap_hir_expand::{ExpandResult, ExpandTo, InFile};
1015
use ra_ap_ide_db::RootDatabase;
1116
use ra_ap_ide_db::line_index::{LineCol, LineIndex};
@@ -82,6 +87,42 @@ impl Emission<ast::Union> for Translator<'_> {
8287
}
8388
}
8489

90+
impl Emission<ast::PathExpr> for Translator<'_> {
91+
fn post_emit(&mut self, node: &ast::PathExpr, label: Label<generated::PathExpr>) {
92+
self.extract_path_canonical_destination(node, label.into());
93+
}
94+
}
95+
96+
impl Emission<ast::RecordExpr> for Translator<'_> {
97+
fn post_emit(&mut self, node: &ast::RecordExpr, label: Label<generated::StructExpr>) {
98+
self.extract_path_canonical_destination(node, label.into());
99+
}
100+
}
101+
102+
impl Emission<ast::PathPat> for Translator<'_> {
103+
fn post_emit(&mut self, node: &ast::PathPat, label: Label<generated::PathPat>) {
104+
self.extract_path_canonical_destination(node, label.into());
105+
}
106+
}
107+
108+
impl Emission<ast::RecordPat> for Translator<'_> {
109+
fn post_emit(&mut self, node: &ast::RecordPat, label: Label<generated::StructPat>) {
110+
self.extract_path_canonical_destination(node, label.into());
111+
}
112+
}
113+
114+
impl Emission<ast::TupleStructPat> for Translator<'_> {
115+
fn post_emit(&mut self, node: &ast::TupleStructPat, label: Label<generated::TupleStructPat>) {
116+
self.extract_path_canonical_destination(node, label.into());
117+
}
118+
}
119+
120+
impl Emission<ast::MethodCallExpr> for Translator<'_> {
121+
fn post_emit(&mut self, node: &ast::MethodCallExpr, label: Label<generated::MethodCallExpr>) {
122+
self.extract_method_canonical_destination(node, label);
123+
}
124+
}
125+
85126
impl Emission<ast::PathSegment> for Translator<'_> {
86127
fn post_emit(&mut self, node: &ast::PathSegment, label: Label<generated::PathSegment>) {
87128
self.extract_types_from_path_segment(node, label);
@@ -462,6 +503,254 @@ impl<'a> Translator<'a> {
462503
}
463504
}
464505

506+
fn canonical_path_from_type(&self, ty: Type) -> Option<String> {
507+
let sema = self.semantics.as_ref().unwrap();
508+
// rust-analyzer doesn't provide a type enum directly
509+
if let Some(it) = ty.as_adt() {
510+
return match it {
511+
Adt::Struct(it) => self.canonical_path_from_hir(it),
512+
Adt::Union(it) => self.canonical_path_from_hir(it),
513+
Adt::Enum(it) => self.canonical_path_from_hir(it),
514+
};
515+
};
516+
if let Some((it, size)) = ty.as_array(sema.db) {
517+
return self
518+
.canonical_path_from_type(it)
519+
.map(|p| format!("[{p}; {size}]"));
520+
}
521+
if let Some(it) = ty.as_slice() {
522+
return self.canonical_path_from_type(it).map(|p| format!("[{p}]"));
523+
}
524+
if let Some(it) = ty.as_builtin() {
525+
return Some(it.name().as_str().to_owned());
526+
}
527+
if let Some(it) = ty.as_dyn_trait() {
528+
return self.canonical_path_from_hir(it).map(|p| format!("dyn {p}"));
529+
}
530+
if let Some((it, mutability)) = ty.as_reference() {
531+
let mut_str = match mutability {
532+
Mutability::Shared => "",
533+
Mutability::Mut => "mut ",
534+
};
535+
return self
536+
.canonical_path_from_type(it)
537+
.map(|p| format!("&{mut_str}{p}"));
538+
}
539+
if let Some(it) = ty.as_impl_traits(sema.db) {
540+
let paths = it
541+
.map(|t| self.canonical_path_from_hir(t))
542+
.collect::<Option<Vec<_>>>()?;
543+
return Some(format!("impl {}", paths.join(" + ")));
544+
}
545+
if ty.as_type_param(sema.db).is_some() {
546+
// from the canonical path perspective, we just want a special name
547+
// e.g. `crate::<_ as SomeTrait>::func`
548+
return Some("_".to_owned());
549+
}
550+
None
551+
}
552+
553+
fn canonical_path_from_hir_module(&self, item: Module) -> Option<String> {
554+
if ModuleId::from(item).containing_block().is_some() {
555+
// this means this is a block module, i.e. a virtual module for an anonymous block scope
556+
return None;
557+
}
558+
if item.is_crate_root() {
559+
return Some("crate".into());
560+
}
561+
self.canonical_path_from_hir::<ast::Module>(item)
562+
}
563+
564+
fn canonical_path_from_hir<T: AstNode>(&self, item: impl AddressableHir<T>) -> Option<String> {
565+
// if we have a Hir entity, it means we have semantics
566+
let sema = self.semantics.as_ref().unwrap();
567+
let name = item.name(sema)?;
568+
let container = item.container(sema.db);
569+
let prefix = match container {
570+
ItemContainer::Trait(it) => self.canonical_path_from_hir(it),
571+
ItemContainer::Impl(it) => {
572+
let ty = self.canonical_path_from_type(it.self_ty(sema.db))?;
573+
if let Some(trait_) = it.trait_(sema.db) {
574+
let tr = self.canonical_path_from_hir(trait_)?;
575+
Some(format!("<{ty} as {tr}>"))
576+
} else {
577+
Some(format!("<{ty}>"))
578+
}
579+
}
580+
ItemContainer::Module(it) => self.canonical_path_from_hir_module(it),
581+
ItemContainer::ExternBlock(..) | ItemContainer::Crate(..) => Some("".to_owned()),
582+
}?;
583+
Some(format!("{prefix}::{name}"))
584+
}
585+
586+
fn canonical_path_from_module_def(&self, item: ModuleDef) -> Option<String> {
587+
match item {
588+
ModuleDef::Module(it) => self.canonical_path_from_hir(it),
589+
ModuleDef::Function(it) => self.canonical_path_from_hir(it),
590+
ModuleDef::Adt(Adt::Enum(it)) => self.canonical_path_from_hir(it),
591+
ModuleDef::Adt(Adt::Struct(it)) => self.canonical_path_from_hir(it),
592+
ModuleDef::Adt(Adt::Union(it)) => self.canonical_path_from_hir(it),
593+
ModuleDef::Trait(it) => self.canonical_path_from_hir(it),
594+
ModuleDef::Variant(it) => self.canonical_path_from_enum_variant(it),
595+
ModuleDef::Static(_) => None,
596+
ModuleDef::TraitAlias(_) => None,
597+
ModuleDef::TypeAlias(_) => None,
598+
ModuleDef::BuiltinType(_) => None,
599+
ModuleDef::Macro(_) => None,
600+
ModuleDef::Const(_) => None,
601+
}
602+
}
603+
604+
fn canonical_path_from_enum_variant(&self, item: Variant) -> Option<String> {
605+
// if we have a Hir entity, it means we have semantics
606+
let sema = self.semantics.as_ref().unwrap();
607+
let prefix = self.canonical_path_from_hir(item.parent_enum(sema.db))?;
608+
let name = item.name(sema.db);
609+
Some(format!("{prefix}::{}", name.as_str()))
610+
}
611+
612+
fn origin_from_hir<T: AstNode>(&self, item: impl AddressableHir<T>) -> String {
613+
// if we have a Hir entity, it means we have semantics
614+
let sema = self.semantics.as_ref().unwrap();
615+
self.origin_from_crate(item.module(sema).krate())
616+
}
617+
618+
fn origin_from_crate(&self, item: Crate) -> String {
619+
// if we have a Hir entity, it means we have semantics
620+
let sema = self.semantics.as_ref().unwrap();
621+
match item.origin(sema.db) {
622+
CrateOrigin::Rustc { name } => format!("rustc:{name}"),
623+
CrateOrigin::Local { repo, name } => format!(
624+
"repo:{}:{}",
625+
repo.unwrap_or_default(),
626+
name.map(|s| s.as_str().to_owned()).unwrap_or_default()
627+
),
628+
CrateOrigin::Library { repo, name } => {
629+
format!("repo:{}:{}", repo.unwrap_or_default(), name)
630+
}
631+
CrateOrigin::Lang(it) => format!("lang:{it}"),
632+
}
633+
}
634+
635+
fn origin_from_module_def(&self, item: ModuleDef) -> Option<String> {
636+
match item {
637+
ModuleDef::Module(it) => Some(self.origin_from_hir(it)),
638+
ModuleDef::Function(it) => Some(self.origin_from_hir(it)),
639+
ModuleDef::Adt(Adt::Enum(it)) => Some(self.origin_from_hir(it)),
640+
ModuleDef::Adt(Adt::Struct(it)) => Some(self.origin_from_hir(it)),
641+
ModuleDef::Adt(Adt::Union(it)) => Some(self.origin_from_hir(it)),
642+
ModuleDef::Trait(it) => Some(self.origin_from_hir(it)),
643+
ModuleDef::Variant(it) => Some(self.origin_from_enum_variant(it)),
644+
ModuleDef::Static(_) => None,
645+
ModuleDef::TraitAlias(_) => None,
646+
ModuleDef::TypeAlias(_) => None,
647+
ModuleDef::BuiltinType(_) => None,
648+
ModuleDef::Macro(_) => None,
649+
ModuleDef::Const(_) => None,
650+
}
651+
}
652+
653+
fn origin_from_enum_variant(&self, item: Variant) -> String {
654+
// if we have a Hir entity, it means we have semantics
655+
let sema = self.semantics.as_ref().unwrap();
656+
self.origin_from_hir(item.parent_enum(sema.db))
657+
}
658+
659+
// pub(crate) fn extract_canonical_origin<T: AddressableAst + HasName>(
660+
// &mut self,
661+
// item: &T,
662+
// label: Label<generated::Addressable>,
663+
// ) {
664+
// // if !self.resolve_paths {
665+
// // return;
666+
// // }
667+
// (|| {
668+
// let sema = self.semantics.as_ref()?;
669+
// let def = T::Hir::try_from_source(item, sema)?;
670+
// let path = self.canonical_path_from_hir(def)?;
671+
// let origin = self.origin_from_hir(def);
672+
// generated::Addressable::emit_crate_origin(label, origin, &mut self.trap.writer);
673+
// generated::Addressable::emit_extended_canonical_path(
674+
// label,
675+
// path,
676+
// &mut self.trap.writer,
677+
// );
678+
// Some(())
679+
// })();
680+
// }
681+
682+
// pub(crate) fn extract_canonical_origin_of_enum_variant(
683+
// &mut self,
684+
// item: &ast::Variant,
685+
// label: Label<generated::Variant>,
686+
// ) {
687+
// // if !self.resolve_paths {
688+
// // return;
689+
// // }
690+
// (|| {
691+
// let sema = self.semantics.as_ref()?;
692+
// let def = sema.to_enum_variant_def(item)?;
693+
// let path = self.canonical_path_from_enum_variant(def)?;
694+
// let origin = self.origin_from_enum_variant(def);
695+
// generated::Addressable::emit_crate_origin(label.into(), origin, &mut self.trap.writer);
696+
// generated::Addressable::emit_extended_canonical_path(
697+
// label.into(),
698+
// path,
699+
// &mut self.trap.writer,
700+
// );
701+
// Some(())
702+
// })();
703+
// }
704+
705+
pub(crate) fn extract_path_canonical_destination(
706+
&mut self,
707+
item: &impl PathAst,
708+
label: Label<generated::Resolvable>,
709+
) {
710+
// if !self.resolve_paths {
711+
// return;
712+
// }
713+
(|| {
714+
let path = item.path()?;
715+
let sema = self.semantics.as_ref()?;
716+
let resolution = sema.resolve_path(&path)?;
717+
let PathResolution::Def(def) = resolution else {
718+
return None;
719+
};
720+
let origin = self.origin_from_module_def(def)?;
721+
let path = self.canonical_path_from_module_def(def)?;
722+
generated::Resolvable::emit_resolved_crate_origin(label, origin, &mut self.trap.writer);
723+
generated::Resolvable::emit_resolved_path(label, path, &mut self.trap.writer);
724+
Some(())
725+
})();
726+
}
727+
728+
pub(crate) fn extract_method_canonical_destination(
729+
&mut self,
730+
item: &ast::MethodCallExpr,
731+
label: Label<generated::MethodCallExpr>,
732+
) {
733+
// if !self.resolve_paths {
734+
// return;
735+
// }
736+
(|| {
737+
let sema = self.semantics.as_ref()?;
738+
let resolved = sema.resolve_method_call_fallback(item)?;
739+
let (Either::Left(function), _) = resolved else {
740+
return None;
741+
};
742+
let origin = self.origin_from_hir(function);
743+
let path = self.canonical_path_from_hir(function)?;
744+
generated::Resolvable::emit_resolved_crate_origin(
745+
label.into(),
746+
origin,
747+
&mut self.trap.writer,
748+
);
749+
generated::Resolvable::emit_resolved_path(label.into(), path, &mut self.trap.writer);
750+
Some(())
751+
})();
752+
}
753+
465754
pub(crate) fn should_be_excluded(&self, item: &impl ast::HasAttrs) -> bool {
466755
self.semantics.is_some_and(|sema| {
467756
item.attrs().any(|attr| {

0 commit comments

Comments
 (0)