|
1 | | -use super::mappings::Emission; |
| 1 | +use super::mappings::{AddressableHir, Emission, PathAst}; |
2 | 2 | use crate::generated::{self}; |
3 | 3 | use crate::rust_analyzer::FileSemanticInformation; |
4 | 4 | use crate::trap::{DiagnosticSeverity, TrapFile, TrapId}; |
5 | 5 | 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}; |
8 | 8 | 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; |
9 | 14 | use ra_ap_hir_expand::{ExpandResult, ExpandTo, InFile}; |
10 | 15 | use ra_ap_ide_db::RootDatabase; |
11 | 16 | use ra_ap_ide_db::line_index::{LineCol, LineIndex}; |
@@ -82,6 +87,42 @@ impl Emission<ast::Union> for Translator<'_> { |
82 | 87 | } |
83 | 88 | } |
84 | 89 |
|
| 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 | + |
85 | 126 | impl Emission<ast::PathSegment> for Translator<'_> { |
86 | 127 | fn post_emit(&mut self, node: &ast::PathSegment, label: Label<generated::PathSegment>) { |
87 | 128 | self.extract_types_from_path_segment(node, label); |
@@ -462,6 +503,254 @@ impl<'a> Translator<'a> { |
462 | 503 | } |
463 | 504 | } |
464 | 505 |
|
| 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 | + |
465 | 754 | pub(crate) fn should_be_excluded(&self, item: &impl ast::HasAttrs) -> bool { |
466 | 755 | self.semantics.is_some_and(|sema| { |
467 | 756 | item.attrs().any(|attr| { |
|
0 commit comments