mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-26 03:45:04 +00:00
Merge pull request #18758 from mgsloan/scip-unique-symbols
Improve SCIP symbols
This commit is contained in:
commit
add0963033
8 changed files with 617 additions and 322 deletions
|
@ -474,7 +474,9 @@ impl HirDisplay for ProjectionTy {
|
||||||
|
|
||||||
let trait_ref = self.trait_ref(f.db);
|
let trait_ref = self.trait_ref(f.db);
|
||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
fmt_trait_ref(f, &trait_ref, true)?;
|
trait_ref.self_type_parameter(Interner).hir_fmt(f)?;
|
||||||
|
write!(f, " as ")?;
|
||||||
|
trait_ref.hir_fmt(f)?;
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
">::{}",
|
">::{}",
|
||||||
|
@ -1775,32 +1777,14 @@ fn write_bounds_like_dyn_trait(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_trait_ref(
|
impl HirDisplay for TraitRef {
|
||||||
f: &mut HirFormatter<'_>,
|
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||||
tr: &TraitRef,
|
let trait_ = self.hir_trait_id();
|
||||||
use_as: bool,
|
|
||||||
) -> Result<(), HirDisplayError> {
|
|
||||||
if f.should_truncate() {
|
|
||||||
return write!(f, "{TYPE_HINT_TRUNCATION}");
|
|
||||||
}
|
|
||||||
|
|
||||||
tr.self_type_parameter(Interner).hir_fmt(f)?;
|
|
||||||
if use_as {
|
|
||||||
write!(f, " as ")?;
|
|
||||||
} else {
|
|
||||||
write!(f, ": ")?;
|
|
||||||
}
|
|
||||||
let trait_ = tr.hir_trait_id();
|
|
||||||
f.start_location_link(trait_.into());
|
f.start_location_link(trait_.into());
|
||||||
write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast(), f.edition()))?;
|
write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast(), f.edition()))?;
|
||||||
f.end_location_link();
|
f.end_location_link();
|
||||||
let substs = tr.substitution.as_slice(Interner);
|
let substs = self.substitution.as_slice(Interner);
|
||||||
hir_fmt_generics(f, &substs[1..], None, substs[0].ty(Interner))
|
hir_fmt_generics(f, &substs[1..], None, substs[0].ty(Interner))
|
||||||
}
|
|
||||||
|
|
||||||
impl HirDisplay for TraitRef {
|
|
||||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
|
||||||
fmt_trait_ref(f, self, false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1811,10 +1795,17 @@ impl HirDisplay for WhereClause {
|
||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
WhereClause::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
|
WhereClause::Implemented(trait_ref) => {
|
||||||
|
trait_ref.self_type_parameter(Interner).hir_fmt(f)?;
|
||||||
|
write!(f, ": ")?;
|
||||||
|
trait_ref.hir_fmt(f)?;
|
||||||
|
}
|
||||||
WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
|
WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
|
||||||
write!(f, "<")?;
|
write!(f, "<")?;
|
||||||
fmt_trait_ref(f, &projection_ty.trait_ref(f.db), true)?;
|
let trait_ref = &projection_ty.trait_ref(f.db);
|
||||||
|
trait_ref.self_type_parameter(Interner).hir_fmt(f)?;
|
||||||
|
write!(f, " as ")?;
|
||||||
|
trait_ref.hir_fmt(f)?;
|
||||||
write!(f, ">::",)?;
|
write!(f, ">::",)?;
|
||||||
let type_alias = from_assoc_type_id(projection_ty.associated_ty_id);
|
let type_alias = from_assoc_type_id(projection_ty.associated_ty_id);
|
||||||
f.start_location_link(type_alias.into());
|
f.start_location_link(type_alias.into());
|
||||||
|
|
|
@ -22,7 +22,7 @@ use itertools::Itertools;
|
||||||
use crate::{
|
use crate::{
|
||||||
Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl,
|
Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl,
|
||||||
Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module,
|
Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module,
|
||||||
SelfParam, Static, Struct, Trait, TraitAlias, TupleField, TyBuilder, Type, TypeAlias,
|
SelfParam, Static, Struct, Trait, TraitAlias, TraitRef, TupleField, TyBuilder, Type, TypeAlias,
|
||||||
TypeOrConstParam, TypeParam, Union, Variant,
|
TypeOrConstParam, TypeParam, Union, Variant,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -743,6 +743,12 @@ impl HirDisplay for Static {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HirDisplay for TraitRef {
|
||||||
|
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||||
|
self.trait_ref.hir_fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HirDisplay for Trait {
|
impl HirDisplay for Trait {
|
||||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||||
write_trait_header(self, f)?;
|
write_trait_header(self, f)?;
|
||||||
|
|
|
@ -13,10 +13,10 @@ use either::Either;
|
||||||
use hir::{
|
use hir::{
|
||||||
Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
|
Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
|
||||||
Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
|
Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
|
||||||
Function, GenericParam, GenericSubstitution, HasVisibility, HirDisplay, Impl, InlineAsmOperand,
|
Function, GenericDef, GenericParam, GenericSubstitution, HasContainer, HasVisibility,
|
||||||
Label, Local, Macro, Module, ModuleDef, Name, PathResolution, Semantics, Static,
|
HirDisplay, Impl, InlineAsmOperand, ItemContainer, Label, Local, Macro, Module, ModuleDef,
|
||||||
StaticLifetime, Struct, ToolModule, Trait, TraitAlias, TupleField, TypeAlias, Variant,
|
Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule, Trait, TraitAlias,
|
||||||
VariantDef, Visibility,
|
TupleField, TypeAlias, Variant, VariantDef, Visibility,
|
||||||
};
|
};
|
||||||
use span::Edition;
|
use span::Edition;
|
||||||
use stdx::{format_to, impl_from};
|
use stdx::{format_to, impl_from};
|
||||||
|
@ -97,9 +97,39 @@ impl Definition {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enclosing_definition(&self, db: &RootDatabase) -> Option<Definition> {
|
pub fn enclosing_definition(&self, db: &RootDatabase) -> Option<Definition> {
|
||||||
|
fn container_to_definition(container: ItemContainer) -> Option<Definition> {
|
||||||
|
match container {
|
||||||
|
ItemContainer::Trait(it) => Some(it.into()),
|
||||||
|
ItemContainer::Impl(it) => Some(it.into()),
|
||||||
|
ItemContainer::Module(it) => Some(it.into()),
|
||||||
|
ItemContainer::ExternBlock() | ItemContainer::Crate(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
match self {
|
match self {
|
||||||
|
Definition::Macro(it) => Some(it.module(db).into()),
|
||||||
|
Definition::Module(it) => it.parent(db).map(Definition::Module),
|
||||||
|
Definition::Field(it) => Some(it.parent_def(db).into()),
|
||||||
|
Definition::Function(it) => container_to_definition(it.container(db)),
|
||||||
|
Definition::Adt(it) => Some(it.module(db).into()),
|
||||||
|
Definition::Const(it) => container_to_definition(it.container(db)),
|
||||||
|
Definition::Static(it) => container_to_definition(it.container(db)),
|
||||||
|
Definition::Trait(it) => container_to_definition(it.container(db)),
|
||||||
|
Definition::TraitAlias(it) => container_to_definition(it.container(db)),
|
||||||
|
Definition::TypeAlias(it) => container_to_definition(it.container(db)),
|
||||||
|
Definition::Variant(it) => Some(Adt::Enum(it.parent_enum(db)).into()),
|
||||||
|
Definition::SelfType(it) => Some(it.module(db).into()),
|
||||||
Definition::Local(it) => it.parent(db).try_into().ok(),
|
Definition::Local(it) => it.parent(db).try_into().ok(),
|
||||||
_ => None,
|
Definition::GenericParam(it) => Some(it.parent().into()),
|
||||||
|
Definition::Label(it) => it.parent(db).try_into().ok(),
|
||||||
|
Definition::ExternCrateDecl(it) => container_to_definition(it.container(db)),
|
||||||
|
Definition::DeriveHelper(it) => Some(it.derive().module(db).into()),
|
||||||
|
Definition::InlineAsmOperand(it) => it.parent(db).try_into().ok(),
|
||||||
|
Definition::BuiltinAttr(_)
|
||||||
|
| Definition::BuiltinType(_)
|
||||||
|
| Definition::BuiltinLifetime(_)
|
||||||
|
| Definition::TupleField(_)
|
||||||
|
| Definition::ToolModule(_)
|
||||||
|
| Definition::InlineAsmRegOrRegClass(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -932,3 +962,17 @@ impl TryFrom<DefWithBody> for Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<GenericDef> for Definition {
|
||||||
|
fn from(def: GenericDef) -> Self {
|
||||||
|
match def {
|
||||||
|
GenericDef::Function(it) => it.into(),
|
||||||
|
GenericDef::Adt(it) => it.into(),
|
||||||
|
GenericDef::Trait(it) => it.into(),
|
||||||
|
GenericDef::TraitAlias(it) => it.into(),
|
||||||
|
GenericDef::TypeAlias(it) => it.into(),
|
||||||
|
GenericDef::Impl(it) => it.into(),
|
||||||
|
GenericDef::Const(it) => it.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -96,8 +96,8 @@ pub use crate::{
|
||||||
join_lines::JoinLinesConfig,
|
join_lines::JoinLinesConfig,
|
||||||
markup::Markup,
|
markup::Markup,
|
||||||
moniker::{
|
moniker::{
|
||||||
MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation,
|
Moniker, MonikerDescriptorKind, MonikerIdentifier, MonikerKind, MonikerResult,
|
||||||
SymbolInformationKind,
|
PackageInformation, SymbolInformationKind,
|
||||||
},
|
},
|
||||||
move_item::Direction,
|
move_item::Direction,
|
||||||
navigation_target::{NavigationTarget, TryToNav, UpmappingResult},
|
navigation_target::{NavigationTarget, TryToNav, UpmappingResult},
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
use hir::{Adt, AsAssocItem, AssocItemContainer, Crate, MacroKind, Semantics};
|
use hir::{Adt, AsAssocItem, Crate, HirDisplay, MacroKind, Semantics};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::{CrateOrigin, LangCrateOrigin},
|
base_db::{CrateOrigin, LangCrateOrigin},
|
||||||
defs::{Definition, IdentClass},
|
defs::{Definition, IdentClass},
|
||||||
|
@ -11,6 +11,7 @@ use ide_db::{
|
||||||
FilePosition, RootDatabase,
|
FilePosition, RootDatabase,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use span::Edition;
|
||||||
use syntax::{AstNode, SyntaxKind::*, T};
|
use syntax::{AstNode, SyntaxKind::*, T};
|
||||||
|
|
||||||
use crate::{doc_links::token_as_doc_comment, parent_module::crates_for, RangeInfo};
|
use crate::{doc_links::token_as_doc_comment, parent_module::crates_for, RangeInfo};
|
||||||
|
@ -57,8 +58,8 @@ pub enum SymbolInformationKind {
|
||||||
impl From<SymbolInformationKind> for MonikerDescriptorKind {
|
impl From<SymbolInformationKind> for MonikerDescriptorKind {
|
||||||
fn from(value: SymbolInformationKind) -> Self {
|
fn from(value: SymbolInformationKind) -> Self {
|
||||||
match value {
|
match value {
|
||||||
SymbolInformationKind::AssociatedType => Self::TypeParameter,
|
SymbolInformationKind::AssociatedType => Self::Type,
|
||||||
SymbolInformationKind::Attribute => Self::Macro,
|
SymbolInformationKind::Attribute => Self::Meta,
|
||||||
SymbolInformationKind::Constant => Self::Term,
|
SymbolInformationKind::Constant => Self::Term,
|
||||||
SymbolInformationKind::Enum => Self::Type,
|
SymbolInformationKind::Enum => Self::Type,
|
||||||
SymbolInformationKind::EnumMember => Self::Type,
|
SymbolInformationKind::EnumMember => Self::Type,
|
||||||
|
@ -70,7 +71,7 @@ impl From<SymbolInformationKind> for MonikerDescriptorKind {
|
||||||
SymbolInformationKind::Parameter => Self::Parameter,
|
SymbolInformationKind::Parameter => Self::Parameter,
|
||||||
SymbolInformationKind::SelfParameter => Self::Parameter,
|
SymbolInformationKind::SelfParameter => Self::Parameter,
|
||||||
SymbolInformationKind::StaticMethod => Self::Method,
|
SymbolInformationKind::StaticMethod => Self::Method,
|
||||||
SymbolInformationKind::StaticVariable => Self::Meta,
|
SymbolInformationKind::StaticVariable => Self::Term,
|
||||||
SymbolInformationKind::Struct => Self::Type,
|
SymbolInformationKind::Struct => Self::Type,
|
||||||
SymbolInformationKind::Trait => Self::Type,
|
SymbolInformationKind::Trait => Self::Type,
|
||||||
SymbolInformationKind::TraitMethod => Self::Method,
|
SymbolInformationKind::TraitMethod => Self::Method,
|
||||||
|
@ -109,10 +110,12 @@ pub enum MonikerKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct MonikerResult {
|
pub enum MonikerResult {
|
||||||
pub identifier: MonikerIdentifier,
|
/// Uniquely identifies a definition.
|
||||||
pub kind: MonikerKind,
|
Moniker(Moniker),
|
||||||
pub package_information: PackageInformation,
|
/// Specifies that the definition is a local, and so does not have a unique identifier. Provides
|
||||||
|
/// a unique identifier for the container.
|
||||||
|
Local { enclosing_moniker: Option<Moniker> },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MonikerResult {
|
impl MonikerResult {
|
||||||
|
@ -121,6 +124,15 @@ impl MonikerResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information which uniquely identifies a definition which might be referenceable outside of the
|
||||||
|
/// source file. Visibility declarations do not affect presence.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Moniker {
|
||||||
|
pub identifier: MonikerIdentifier,
|
||||||
|
pub kind: MonikerKind,
|
||||||
|
pub package_information: PackageInformation,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct PackageInformation {
|
pub struct PackageInformation {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -232,157 +244,106 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes a `MonikerResult` for a definition. Result cases:
|
||||||
|
///
|
||||||
|
/// * `Some(MonikerResult::Moniker(_))` provides a unique `Moniker` which refers to a definition.
|
||||||
|
///
|
||||||
|
/// * `Some(MonikerResult::Local { .. })` provides a `Moniker` for the definition enclosing a local.
|
||||||
|
///
|
||||||
|
/// * `None` is returned for definitions which are not in a module: `BuiltinAttr`, `BuiltinType`,
|
||||||
|
/// `BuiltinLifetime`, `TupleField`, `ToolModule`, and `InlineAsmRegOrRegClass`. TODO: it might be
|
||||||
|
/// sensible to provide monikers that refer to some non-existent crate of compiler builtin
|
||||||
|
/// definitions.
|
||||||
pub(crate) fn def_to_moniker(
|
pub(crate) fn def_to_moniker(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
def: Definition,
|
definition: Definition,
|
||||||
from_crate: Crate,
|
from_crate: Crate,
|
||||||
) -> Option<MonikerResult> {
|
) -> Option<MonikerResult> {
|
||||||
if matches!(
|
match definition {
|
||||||
def,
|
Definition::Local(_) | Definition::Label(_) | Definition::GenericParam(_) => {
|
||||||
Definition::GenericParam(_)
|
return Some(MonikerResult::Local {
|
||||||
| Definition::Label(_)
|
enclosing_moniker: enclosing_def_to_moniker(db, definition, from_crate),
|
||||||
| Definition::DeriveHelper(_)
|
});
|
||||||
| Definition::BuiltinAttr(_)
|
|
||||||
| Definition::ToolModule(_)
|
|
||||||
) {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Some(MonikerResult::Moniker(def_to_non_local_moniker(db, definition, from_crate)?))
|
||||||
|
}
|
||||||
|
|
||||||
let module = def.module(db)?;
|
fn enclosing_def_to_moniker(
|
||||||
|
db: &RootDatabase,
|
||||||
|
mut def: Definition,
|
||||||
|
from_crate: Crate,
|
||||||
|
) -> Option<Moniker> {
|
||||||
|
loop {
|
||||||
|
let enclosing_def = def.enclosing_definition(db)?;
|
||||||
|
if let Some(enclosing_moniker) = def_to_non_local_moniker(db, enclosing_def, from_crate) {
|
||||||
|
return Some(enclosing_moniker);
|
||||||
|
}
|
||||||
|
def = enclosing_def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn def_to_non_local_moniker(
|
||||||
|
db: &RootDatabase,
|
||||||
|
definition: Definition,
|
||||||
|
from_crate: Crate,
|
||||||
|
) -> Option<Moniker> {
|
||||||
|
let module = definition.module(db)?;
|
||||||
let krate = module.krate();
|
let krate = module.krate();
|
||||||
let edition = krate.edition(db);
|
let edition = krate.edition(db);
|
||||||
let mut description = vec![];
|
|
||||||
description.extend(module.path_to_root(db).into_iter().filter_map(|x| {
|
|
||||||
Some(MonikerDescriptor {
|
|
||||||
name: x.name(db)?.display(db, edition).to_string(),
|
|
||||||
desc: def_to_kind(db, x.into()).into(),
|
|
||||||
})
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Handle associated items within a trait
|
// Add descriptors for this definition and every enclosing definition.
|
||||||
if let Some(assoc) = def.as_assoc_item(db) {
|
let mut reverse_description = vec![];
|
||||||
let container = assoc.container(db);
|
let mut def = definition;
|
||||||
match container {
|
loop {
|
||||||
AssocItemContainer::Trait(trait_) => {
|
match def {
|
||||||
// Because different traits can have functions with the same name,
|
Definition::SelfType(impl_) => {
|
||||||
// we have to include the trait name as part of the moniker for uniqueness.
|
if let Some(trait_ref) = impl_.trait_ref(db) {
|
||||||
description.push(MonikerDescriptor {
|
// Trait impls use the trait type for the 2nd parameter.
|
||||||
name: trait_.name(db).display(db, edition).to_string(),
|
reverse_description.push(MonikerDescriptor {
|
||||||
desc: def_to_kind(db, trait_.into()).into(),
|
name: display(db, edition, module, trait_ref),
|
||||||
|
desc: MonikerDescriptorKind::TypeParameter,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
AssocItemContainer::Impl(impl_) => {
|
// Both inherent and trait impls use the self type for the first parameter.
|
||||||
// Because a struct can implement multiple traits, for implementations
|
reverse_description.push(MonikerDescriptor {
|
||||||
// we add both the struct name and the trait name to the path
|
name: display(db, edition, module, impl_.self_ty(db)),
|
||||||
if let Some(adt) = impl_.self_ty(db).as_adt() {
|
desc: MonikerDescriptorKind::TypeParameter,
|
||||||
description.push(MonikerDescriptor {
|
});
|
||||||
name: adt.name(db).display(db, edition).to_string(),
|
reverse_description.push(MonikerDescriptor {
|
||||||
desc: def_to_kind(db, adt.into()).into(),
|
name: "impl".to_owned(),
|
||||||
|
desc: MonikerDescriptorKind::Type,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
if let Some(trait_) = impl_.trait_(db) {
|
if let Some(name) = def.name(db) {
|
||||||
description.push(MonikerDescriptor {
|
reverse_description.push(MonikerDescriptor {
|
||||||
name: trait_.name(db).display(db, edition).to_string(),
|
|
||||||
desc: def_to_kind(db, trait_.into()).into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Definition::Field(it) = def {
|
|
||||||
description.push(MonikerDescriptor {
|
|
||||||
name: it.parent_def(db).name(db).display(db, edition).to_string(),
|
|
||||||
desc: def_to_kind(db, it.parent_def(db).into()).into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Qualify locals/parameters by their parent definition name.
|
|
||||||
if let Definition::Local(it) = def {
|
|
||||||
let parent = Definition::try_from(it.parent(db)).ok();
|
|
||||||
if let Some(parent) = parent {
|
|
||||||
let parent_name = parent.name(db);
|
|
||||||
if let Some(name) = parent_name {
|
|
||||||
description.push(MonikerDescriptor {
|
|
||||||
name: name.display(db, edition).to_string(),
|
name: name.display(db, edition).to_string(),
|
||||||
desc: def_to_kind(db, parent).into(),
|
desc: def_to_kind(db, def).into(),
|
||||||
});
|
});
|
||||||
}
|
} else if reverse_description.is_empty() {
|
||||||
}
|
// Don't allow the last descriptor to be absent.
|
||||||
}
|
|
||||||
|
|
||||||
let desc = def_to_kind(db, def).into();
|
|
||||||
|
|
||||||
let name_desc = match def {
|
|
||||||
// These are handled by top-level guard (for performance).
|
|
||||||
Definition::GenericParam(_)
|
|
||||||
| Definition::Label(_)
|
|
||||||
| Definition::DeriveHelper(_)
|
|
||||||
| Definition::BuiltinLifetime(_)
|
|
||||||
| Definition::BuiltinAttr(_)
|
|
||||||
| Definition::ToolModule(_)
|
|
||||||
| Definition::InlineAsmRegOrRegClass(_)
|
|
||||||
| Definition::InlineAsmOperand(_) => return None,
|
|
||||||
|
|
||||||
Definition::Local(local) => {
|
|
||||||
if !local.is_param(db) {
|
|
||||||
return None;
|
return None;
|
||||||
|
} else {
|
||||||
|
match def {
|
||||||
|
Definition::Module(module) if module.is_crate_root() => {}
|
||||||
|
_ => {
|
||||||
|
tracing::error!(?def, "Encountered enclosing definition with no name");
|
||||||
}
|
}
|
||||||
|
|
||||||
MonikerDescriptor { name: local.name(db).display(db, edition).to_string(), desc }
|
|
||||||
}
|
}
|
||||||
Definition::Macro(m) => {
|
|
||||||
MonikerDescriptor { name: m.name(db).display(db, edition).to_string(), desc }
|
|
||||||
}
|
}
|
||||||
Definition::Function(f) => {
|
|
||||||
MonikerDescriptor { name: f.name(db).display(db, edition).to_string(), desc }
|
|
||||||
}
|
}
|
||||||
Definition::Variant(v) => {
|
|
||||||
MonikerDescriptor { name: v.name(db).display(db, edition).to_string(), desc }
|
|
||||||
}
|
|
||||||
Definition::Const(c) => {
|
|
||||||
MonikerDescriptor { name: c.name(db)?.display(db, edition).to_string(), desc }
|
|
||||||
}
|
|
||||||
Definition::Trait(trait_) => {
|
|
||||||
MonikerDescriptor { name: trait_.name(db).display(db, edition).to_string(), desc }
|
|
||||||
}
|
|
||||||
Definition::TraitAlias(ta) => {
|
|
||||||
MonikerDescriptor { name: ta.name(db).display(db, edition).to_string(), desc }
|
|
||||||
}
|
|
||||||
Definition::TypeAlias(ta) => {
|
|
||||||
MonikerDescriptor { name: ta.name(db).display(db, edition).to_string(), desc }
|
|
||||||
}
|
|
||||||
Definition::Module(m) => {
|
|
||||||
MonikerDescriptor { name: m.name(db)?.display(db, edition).to_string(), desc }
|
|
||||||
}
|
|
||||||
Definition::BuiltinType(b) => {
|
|
||||||
MonikerDescriptor { name: b.name().display(db, edition).to_string(), desc }
|
|
||||||
}
|
|
||||||
Definition::SelfType(imp) => MonikerDescriptor {
|
|
||||||
name: imp.self_ty(db).as_adt()?.name(db).display(db, edition).to_string(),
|
|
||||||
desc,
|
|
||||||
},
|
|
||||||
Definition::Field(it) => {
|
|
||||||
MonikerDescriptor { name: it.name(db).display(db, edition).to_string(), desc }
|
|
||||||
}
|
|
||||||
Definition::TupleField(it) => {
|
|
||||||
MonikerDescriptor { name: it.name().display(db, edition).to_string(), desc }
|
|
||||||
}
|
|
||||||
Definition::Adt(adt) => {
|
|
||||||
MonikerDescriptor { name: adt.name(db).display(db, edition).to_string(), desc }
|
|
||||||
}
|
|
||||||
Definition::Static(s) => {
|
|
||||||
MonikerDescriptor { name: s.name(db).display(db, edition).to_string(), desc }
|
|
||||||
}
|
|
||||||
Definition::ExternCrateDecl(m) => {
|
|
||||||
MonikerDescriptor { name: m.name(db).display(db, edition).to_string(), desc }
|
|
||||||
}
|
}
|
||||||
|
let Some(next_def) = def.enclosing_definition(db) else {
|
||||||
|
break;
|
||||||
};
|
};
|
||||||
|
def = next_def;
|
||||||
|
}
|
||||||
|
reverse_description.reverse();
|
||||||
|
let description = reverse_description;
|
||||||
|
|
||||||
description.push(name_desc);
|
Some(Moniker {
|
||||||
|
|
||||||
Some(MonikerResult {
|
|
||||||
identifier: MonikerIdentifier {
|
identifier: MonikerIdentifier {
|
||||||
crate_name: krate.display_name(db)?.crate_name().to_string(),
|
crate_name: krate.display_name(db)?.crate_name().to_string(),
|
||||||
description,
|
description,
|
||||||
|
@ -417,17 +378,57 @@ pub(crate) fn def_to_moniker(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn display<T: HirDisplay>(
|
||||||
|
db: &RootDatabase,
|
||||||
|
edition: Edition,
|
||||||
|
module: hir::Module,
|
||||||
|
it: T,
|
||||||
|
) -> String {
|
||||||
|
match it.display_source_code(db, module.into(), true) {
|
||||||
|
Ok(result) => result,
|
||||||
|
// Fallback on display variant that always succeeds
|
||||||
|
Err(_) => {
|
||||||
|
let fallback_result = it.display(db, edition).to_string();
|
||||||
|
tracing::error!(
|
||||||
|
display = %fallback_result, "`display_source_code` failed; falling back to using display"
|
||||||
|
);
|
||||||
|
fallback_result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::fixture;
|
use crate::{fixture, MonikerResult};
|
||||||
|
|
||||||
use super::MonikerKind;
|
use super::MonikerKind;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn no_moniker(ra_fixture: &str) {
|
fn no_moniker(ra_fixture: &str) {
|
||||||
let (analysis, position) = fixture::position(ra_fixture);
|
let (analysis, position) = fixture::position(ra_fixture);
|
||||||
if let Some(x) = analysis.moniker(position).unwrap() {
|
if let Some(x) = analysis.moniker(position).unwrap() {
|
||||||
assert_eq!(x.info.len(), 0, "Moniker founded but no moniker expected: {x:?}");
|
assert_eq!(x.info.len(), 0, "Moniker found but no moniker expected: {x:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn check_local_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) {
|
||||||
|
let (analysis, position) = fixture::position(ra_fixture);
|
||||||
|
let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
|
||||||
|
assert_eq!(x.len(), 1);
|
||||||
|
match x.into_iter().next().unwrap() {
|
||||||
|
MonikerResult::Local { enclosing_moniker: Some(x) } => {
|
||||||
|
assert_eq!(identifier, x.identifier.to_string());
|
||||||
|
assert_eq!(package, format!("{:?}", x.package_information));
|
||||||
|
assert_eq!(kind, x.kind);
|
||||||
|
}
|
||||||
|
MonikerResult::Local { enclosing_moniker: None } => {
|
||||||
|
panic!("Unexpected local with no enclosing moniker");
|
||||||
|
}
|
||||||
|
MonikerResult::Moniker(_) => {
|
||||||
|
panic!("Unexpected non-local moniker");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,11 +437,17 @@ mod tests {
|
||||||
let (analysis, position) = fixture::position(ra_fixture);
|
let (analysis, position) = fixture::position(ra_fixture);
|
||||||
let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
|
let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
|
||||||
assert_eq!(x.len(), 1);
|
assert_eq!(x.len(), 1);
|
||||||
let x = x.into_iter().next().unwrap();
|
match x.into_iter().next().unwrap() {
|
||||||
|
MonikerResult::Local { enclosing_moniker } => {
|
||||||
|
panic!("Unexpected local enclosed in {:?}", enclosing_moniker);
|
||||||
|
}
|
||||||
|
MonikerResult::Moniker(x) => {
|
||||||
assert_eq!(identifier, x.identifier.to_string());
|
assert_eq!(identifier, x.identifier.to_string());
|
||||||
assert_eq!(package, format!("{:?}", x.package_information));
|
assert_eq!(package, format!("{:?}", x.package_information));
|
||||||
assert_eq!(kind, x.kind);
|
assert_eq!(kind, x.kind);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic() {
|
fn basic() {
|
||||||
|
@ -538,15 +545,13 @@ pub mod module {
|
||||||
pub trait MyTrait {
|
pub trait MyTrait {
|
||||||
pub fn func() {}
|
pub fn func() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MyStruct {}
|
struct MyStruct {}
|
||||||
|
|
||||||
impl MyTrait for MyStruct {
|
impl MyTrait for MyStruct {
|
||||||
pub fn func$0() {}
|
pub fn func$0() {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
"foo::module::MyStruct::MyTrait::func",
|
"foo::module::impl::MyStruct::MyTrait::func",
|
||||||
r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
|
r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
|
||||||
MonikerKind::Export,
|
MonikerKind::Export,
|
||||||
);
|
);
|
||||||
|
@ -573,8 +578,8 @@ pub struct St {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_moniker_for_local() {
|
fn local() {
|
||||||
no_moniker(
|
check_local_moniker(
|
||||||
r#"
|
r#"
|
||||||
//- /lib.rs crate:main deps:foo
|
//- /lib.rs crate:main deps:foo
|
||||||
use foo::module::func;
|
use foo::module::func;
|
||||||
|
@ -588,6 +593,9 @@ pub mod module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
"foo::module::func",
|
||||||
|
r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
|
||||||
|
MonikerKind::Export,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,6 @@ pub struct TokenStaticData {
|
||||||
pub references: Vec<ReferenceData>,
|
pub references: Vec<ReferenceData>,
|
||||||
pub moniker: Option<MonikerResult>,
|
pub moniker: Option<MonikerResult>,
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub enclosing_moniker: Option<MonikerResult>,
|
|
||||||
pub signature: Option<String>,
|
pub signature: Option<String>,
|
||||||
pub kind: SymbolInformationKind,
|
pub kind: SymbolInformationKind,
|
||||||
}
|
}
|
||||||
|
@ -225,9 +224,6 @@ impl StaticIndex<'_> {
|
||||||
display_name: def
|
display_name: def
|
||||||
.name(self.db)
|
.name(self.db)
|
||||||
.map(|name| name.display(self.db, edition).to_string()),
|
.map(|name| name.display(self.db, edition).to_string()),
|
||||||
enclosing_moniker: current_crate
|
|
||||||
.zip(def.enclosing_definition(self.db))
|
|
||||||
.and_then(|(cc, enclosing_def)| def_to_moniker(self.db, enclosing_def, cc)),
|
|
||||||
signature: Some(def.label(self.db, edition)),
|
signature: Some(def.label(self.db, edition)),
|
||||||
kind: def_to_kind(self.db, def),
|
kind: def_to_kind(self.db, def),
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,8 +4,9 @@ use std::env;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use ide::{
|
use ide::{
|
||||||
Analysis, AnalysisHost, FileId, FileRange, MonikerKind, PackageInformation, RootDatabase,
|
Analysis, AnalysisHost, FileId, FileRange, MonikerKind, MonikerResult, PackageInformation,
|
||||||
StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, VendoredLibrariesConfig,
|
RootDatabase, StaticIndex, StaticIndexedFile, TokenId, TokenStaticData,
|
||||||
|
VendoredLibrariesConfig,
|
||||||
};
|
};
|
||||||
use ide_db::{line_index::WideEncoding, LineIndexDatabase};
|
use ide_db::{line_index::WideEncoding, LineIndexDatabase};
|
||||||
use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
|
use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
|
||||||
|
@ -167,7 +168,7 @@ impl LsifManager<'_, '_> {
|
||||||
out_v: result_set_id.into(),
|
out_v: result_set_id.into(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
if let Some(moniker) = token.moniker {
|
if let Some(MonikerResult::Moniker(moniker)) = token.moniker {
|
||||||
let package_id = self.get_package_id(moniker.package_information);
|
let package_id = self.get_package_id(moniker.package_information);
|
||||||
let moniker_id = self.add_vertex(lsif::Vertex::Moniker(lsp_types::Moniker {
|
let moniker_id = self.add_vertex(lsif::Vertex::Moniker(lsp_types::Moniker {
|
||||||
scheme: "rust-analyzer".to_owned(),
|
scheme: "rust-analyzer".to_owned(),
|
||||||
|
|
|
@ -3,14 +3,16 @@
|
||||||
use std::{path::PathBuf, time::Instant};
|
use std::{path::PathBuf, time::Instant};
|
||||||
|
|
||||||
use ide::{
|
use ide::{
|
||||||
AnalysisHost, LineCol, MonikerDescriptorKind, MonikerResult, StaticIndex, StaticIndexedFile,
|
AnalysisHost, LineCol, Moniker, MonikerDescriptorKind, MonikerIdentifier, MonikerResult,
|
||||||
SymbolInformationKind, TextRange, TokenId, VendoredLibrariesConfig,
|
RootDatabase, StaticIndex, StaticIndexedFile, SymbolInformationKind, TextRange, TokenId,
|
||||||
|
TokenStaticData, VendoredLibrariesConfig,
|
||||||
};
|
};
|
||||||
use ide_db::LineIndexDatabase;
|
use ide_db::LineIndexDatabase;
|
||||||
use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
|
use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use scip::types as scip_types;
|
use scip::types::{self as scip_types, SymbolInformation};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
use vfs::FileId;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cli::flags,
|
cli::flags,
|
||||||
|
@ -83,32 +85,56 @@ impl flags::Scip {
|
||||||
text_document_encoding: scip_types::TextEncoding::UTF8.into(),
|
text_document_encoding: scip_types::TextEncoding::UTF8.into(),
|
||||||
special_fields: Default::default(),
|
special_fields: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut documents = Vec::new();
|
let mut documents = Vec::new();
|
||||||
|
|
||||||
let mut symbols_emitted: FxHashSet<TokenId> = FxHashSet::default();
|
// All TokenIds where an Occurrence has been emitted that references a symbol.
|
||||||
let mut tokens_to_symbol: FxHashMap<TokenId, String> = FxHashMap::default();
|
let mut token_ids_referenced: FxHashSet<TokenId> = FxHashSet::default();
|
||||||
let mut tokens_to_enclosing_symbol: FxHashMap<TokenId, Option<String>> =
|
// All TokenIds where the SymbolInformation has been written to the document.
|
||||||
FxHashMap::default();
|
let mut token_ids_emitted: FxHashSet<TokenId> = FxHashSet::default();
|
||||||
|
// All FileIds emitted as documents.
|
||||||
|
let mut file_ids_emitted: FxHashSet<FileId> = FxHashSet::default();
|
||||||
|
|
||||||
|
// All non-local symbols encountered, for detecting duplicate symbol errors.
|
||||||
|
let mut nonlocal_symbols_emitted: FxHashSet<String> = FxHashSet::default();
|
||||||
|
// List of (source_location, symbol) for duplicate symbol errors to report.
|
||||||
|
let mut duplicate_symbol_errors: Vec<(String, String)> = Vec::new();
|
||||||
|
// This is called after definitions have been deduplicated by token_ids_emitted. The purpose
|
||||||
|
// is to detect reuse of symbol names because this causes ambiguity about their meaning.
|
||||||
|
let mut record_error_if_symbol_already_used =
|
||||||
|
|symbol: String,
|
||||||
|
is_inherent_impl: bool,
|
||||||
|
relative_path: &str,
|
||||||
|
line_index: &LineIndex,
|
||||||
|
text_range: TextRange| {
|
||||||
|
let is_local = symbol.starts_with("local ");
|
||||||
|
if !is_local && !nonlocal_symbols_emitted.insert(symbol.clone()) {
|
||||||
|
if is_inherent_impl {
|
||||||
|
// FIXME: See #18772. Duplicate SymbolInformation for inherent impls is
|
||||||
|
// omitted. It would be preferable to emit them with numbers with
|
||||||
|
// disambiguation, but this is more complex to implement.
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
let source_location =
|
||||||
|
text_range_to_string(relative_path, line_index, text_range);
|
||||||
|
duplicate_symbol_errors.push((source_location, symbol));
|
||||||
|
// Keep duplicate SymbolInformation. This behavior is preferred over
|
||||||
|
// omitting so that the issue might be visible within downstream tools.
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generates symbols from token monikers.
|
||||||
|
let mut symbol_generator = SymbolGenerator::new();
|
||||||
|
|
||||||
for StaticIndexedFile { file_id, tokens, .. } in si.files {
|
for StaticIndexedFile { file_id, tokens, .. } in si.files {
|
||||||
let mut local_count = 0;
|
symbol_generator.clear_document_local_state();
|
||||||
let mut new_local_symbol = || {
|
|
||||||
let new_symbol = scip::types::Symbol::new_local(local_count);
|
|
||||||
local_count += 1;
|
|
||||||
|
|
||||||
new_symbol
|
let Some(relative_path) = get_relative_filepath(&vfs, &root, file_id) else { continue };
|
||||||
};
|
let line_index = get_line_index(db, file_id);
|
||||||
|
|
||||||
let relative_path = match get_relative_filepath(&vfs, &root, file_id) {
|
|
||||||
Some(relative_path) => relative_path,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let line_index = LineIndex {
|
|
||||||
index: db.line_index(file_id),
|
|
||||||
encoding: PositionEncoding::Utf8,
|
|
||||||
endings: LineEndings::Unix,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut occurrences = Vec::new();
|
let mut occurrences = Vec::new();
|
||||||
let mut symbols = Vec::new();
|
let mut symbols = Vec::new();
|
||||||
|
@ -116,71 +142,58 @@ impl flags::Scip {
|
||||||
tokens.into_iter().for_each(|(text_range, id)| {
|
tokens.into_iter().for_each(|(text_range, id)| {
|
||||||
let token = si.tokens.get(id).unwrap();
|
let token = si.tokens.get(id).unwrap();
|
||||||
|
|
||||||
let range = text_range_to_scip_range(&line_index, text_range);
|
let (symbol, enclosing_symbol, is_inherent_impl) =
|
||||||
let symbol = tokens_to_symbol
|
if let Some(TokenSymbols { symbol, enclosing_symbol, is_inherent_impl }) =
|
||||||
.entry(id)
|
symbol_generator.token_symbols(id, token)
|
||||||
.or_insert_with(|| {
|
{
|
||||||
let symbol = token
|
(symbol, enclosing_symbol, is_inherent_impl)
|
||||||
.moniker
|
} else {
|
||||||
.as_ref()
|
("".to_owned(), None, false)
|
||||||
.map(moniker_to_symbol)
|
};
|
||||||
.unwrap_or_else(&mut new_local_symbol);
|
|
||||||
scip::symbol::format_symbol(symbol)
|
if !symbol.is_empty() {
|
||||||
})
|
let is_defined_in_this_document = match token.definition {
|
||||||
.clone();
|
Some(def) => def.file_id == file_id,
|
||||||
let enclosing_symbol = tokens_to_enclosing_symbol
|
_ => false,
|
||||||
.entry(id)
|
};
|
||||||
.or_insert_with(|| {
|
if is_defined_in_this_document {
|
||||||
token
|
if token_ids_emitted.insert(id) {
|
||||||
.enclosing_moniker
|
// token_ids_emitted does deduplication. This checks that this results
|
||||||
.as_ref()
|
// in unique emitted symbols, as otherwise references are ambiguous.
|
||||||
.map(moniker_to_symbol)
|
let should_emit = record_error_if_symbol_already_used(
|
||||||
.map(scip::symbol::format_symbol)
|
symbol.clone(),
|
||||||
})
|
is_inherent_impl,
|
||||||
.clone();
|
relative_path.as_str(),
|
||||||
|
&line_index,
|
||||||
|
text_range,
|
||||||
|
);
|
||||||
|
if should_emit {
|
||||||
|
symbols.push(compute_symbol_info(
|
||||||
|
symbol.clone(),
|
||||||
|
enclosing_symbol,
|
||||||
|
token,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
token_ids_referenced.insert(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the range of the def and the range of the token are the same, this must be the definition.
|
||||||
|
// they also must be in the same file. See https://github.com/rust-lang/rust-analyzer/pull/17988
|
||||||
|
let is_definition = match token.definition {
|
||||||
|
Some(def) => def.file_id == file_id && def.range == text_range,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
let mut symbol_roles = Default::default();
|
let mut symbol_roles = Default::default();
|
||||||
|
if is_definition {
|
||||||
if let Some(def) = token.definition {
|
|
||||||
// if the range of the def and the range of the token are the same, this must be the definition.
|
|
||||||
// they also must be in the same file. See https://github.com/rust-lang/rust-analyzer/pull/17988
|
|
||||||
if def.file_id == file_id && def.range == text_range {
|
|
||||||
symbol_roles |= scip_types::SymbolRole::Definition as i32;
|
symbol_roles |= scip_types::SymbolRole::Definition as i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
if symbols_emitted.insert(id) {
|
|
||||||
let documentation = match &token.documentation {
|
|
||||||
Some(doc) => vec![doc.as_str().to_owned()],
|
|
||||||
None => vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
let position_encoding =
|
|
||||||
scip_types::PositionEncoding::UTF8CodeUnitOffsetFromLineStart.into();
|
|
||||||
let signature_documentation =
|
|
||||||
token.signature.clone().map(|text| scip_types::Document {
|
|
||||||
relative_path: relative_path.clone(),
|
|
||||||
language: "rust".to_owned(),
|
|
||||||
text,
|
|
||||||
position_encoding,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
let symbol_info = scip_types::SymbolInformation {
|
|
||||||
symbol: symbol.clone(),
|
|
||||||
documentation,
|
|
||||||
relationships: Vec::new(),
|
|
||||||
special_fields: Default::default(),
|
|
||||||
kind: symbol_kind(token.kind).into(),
|
|
||||||
display_name: token.display_name.clone().unwrap_or_default(),
|
|
||||||
signature_documentation: signature_documentation.into(),
|
|
||||||
enclosing_symbol: enclosing_symbol.unwrap_or_default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
symbols.push(symbol_info)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
occurrences.push(scip_types::Occurrence {
|
occurrences.push(scip_types::Occurrence {
|
||||||
range,
|
range: text_range_to_scip_range(&line_index, text_range),
|
||||||
symbol,
|
symbol,
|
||||||
symbol_roles,
|
symbol_roles,
|
||||||
override_documentation: Vec::new(),
|
override_documentation: Vec::new(),
|
||||||
|
@ -206,15 +219,63 @@ impl flags::Scip {
|
||||||
position_encoding,
|
position_encoding,
|
||||||
special_fields: Default::default(),
|
special_fields: Default::default(),
|
||||||
});
|
});
|
||||||
|
if !file_ids_emitted.insert(file_id) {
|
||||||
|
panic!("Invariant violation: file emitted multiple times.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all symbols referenced by the files but not defined within them.
|
||||||
|
let mut external_symbols = Vec::new();
|
||||||
|
for id in token_ids_referenced.difference(&token_ids_emitted) {
|
||||||
|
let id = *id;
|
||||||
|
let token = si.tokens.get(id).unwrap();
|
||||||
|
|
||||||
|
let Some(definition) = token.definition else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
let file_id = definition.file_id;
|
||||||
|
let Some(relative_path) = get_relative_filepath(&vfs, &root, file_id) else { continue };
|
||||||
|
let line_index = get_line_index(db, file_id);
|
||||||
|
let text_range = definition.range;
|
||||||
|
if file_ids_emitted.contains(&file_id) {
|
||||||
|
tracing::error!(
|
||||||
|
"Bug: definition at {} should have been in an SCIP document but was not.",
|
||||||
|
text_range_to_string(relative_path.as_str(), &line_index, text_range)
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let TokenSymbols { symbol, enclosing_symbol, .. } = symbol_generator
|
||||||
|
.token_symbols(id, token)
|
||||||
|
.expect("To have been referenced, the symbol must be in the cache.");
|
||||||
|
|
||||||
|
record_error_if_symbol_already_used(
|
||||||
|
symbol.clone(),
|
||||||
|
false,
|
||||||
|
relative_path.as_str(),
|
||||||
|
&line_index,
|
||||||
|
text_range,
|
||||||
|
);
|
||||||
|
external_symbols.push(compute_symbol_info(symbol.clone(), enclosing_symbol, token));
|
||||||
}
|
}
|
||||||
|
|
||||||
let index = scip_types::Index {
|
let index = scip_types::Index {
|
||||||
metadata: Some(metadata).into(),
|
metadata: Some(metadata).into(),
|
||||||
documents,
|
documents,
|
||||||
external_symbols: Vec::new(),
|
external_symbols,
|
||||||
special_fields: Default::default(),
|
special_fields: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !duplicate_symbol_errors.is_empty() {
|
||||||
|
eprintln!("{}", DUPLICATE_SYMBOLS_MESSAGE);
|
||||||
|
for (source_location, symbol) in duplicate_symbol_errors {
|
||||||
|
eprintln!("{}", source_location);
|
||||||
|
eprintln!(" Duplicate symbol: {}", symbol);
|
||||||
|
eprintln!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let out_path = self.output.unwrap_or_else(|| PathBuf::from(r"index.scip"));
|
let out_path = self.output.unwrap_or_else(|| PathBuf::from(r"index.scip"));
|
||||||
scip::write_message_to_file(out_path, index)
|
scip::write_message_to_file(out_path, index)
|
||||||
.map_err(|err| anyhow::format_err!("Failed to write scip to file: {}", err))?;
|
.map_err(|err| anyhow::format_err!("Failed to write scip to file: {}", err))?;
|
||||||
|
@ -224,6 +285,53 @@ impl flags::Scip {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Known buggy cases are described here.
|
||||||
|
const DUPLICATE_SYMBOLS_MESSAGE: &str = "
|
||||||
|
Encountered duplicate scip symbols, indicating an internal rust-analyzer bug. These duplicates are
|
||||||
|
included in the output, but this causes information lookup to be ambiguous and so information about
|
||||||
|
these symbols presented by downstream tools may be incorrect.
|
||||||
|
|
||||||
|
Known rust-analyzer bugs that can cause this:
|
||||||
|
|
||||||
|
* Definitions in crate example binaries which have the same symbol as definitions in the library
|
||||||
|
or some other example.
|
||||||
|
|
||||||
|
* Struct/enum/const/static/impl definitions nested in a function do not mention the function name.
|
||||||
|
See #18771.
|
||||||
|
|
||||||
|
Duplicate symbols encountered:
|
||||||
|
";
|
||||||
|
|
||||||
|
fn compute_symbol_info(
|
||||||
|
symbol: String,
|
||||||
|
enclosing_symbol: Option<String>,
|
||||||
|
token: &TokenStaticData,
|
||||||
|
) -> SymbolInformation {
|
||||||
|
let documentation = match &token.documentation {
|
||||||
|
Some(doc) => vec![doc.as_str().to_owned()],
|
||||||
|
None => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let position_encoding = scip_types::PositionEncoding::UTF8CodeUnitOffsetFromLineStart.into();
|
||||||
|
let signature_documentation = token.signature.clone().map(|text| scip_types::Document {
|
||||||
|
relative_path: "".to_owned(),
|
||||||
|
language: "rust".to_owned(),
|
||||||
|
text,
|
||||||
|
position_encoding,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
scip_types::SymbolInformation {
|
||||||
|
symbol,
|
||||||
|
documentation,
|
||||||
|
relationships: Vec::new(),
|
||||||
|
special_fields: Default::default(),
|
||||||
|
kind: symbol_kind(token.kind).into(),
|
||||||
|
display_name: token.display_name.clone().unwrap_or_default(),
|
||||||
|
signature_documentation: signature_documentation.into(),
|
||||||
|
enclosing_symbol: enclosing_symbol.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_relative_filepath(
|
fn get_relative_filepath(
|
||||||
vfs: &vfs::Vfs,
|
vfs: &vfs::Vfs,
|
||||||
rootpath: &vfs::AbsPathBuf,
|
rootpath: &vfs::AbsPathBuf,
|
||||||
|
@ -232,6 +340,14 @@ fn get_relative_filepath(
|
||||||
Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_str().to_owned())
|
Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_str().to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_line_index(db: &RootDatabase, file_id: FileId) -> LineIndex {
|
||||||
|
LineIndex {
|
||||||
|
index: db.line_index(file_id),
|
||||||
|
encoding: PositionEncoding::Utf8,
|
||||||
|
endings: LineEndings::Unix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SCIP Ranges have a (very large) optimization that ranges if they are on the same line
|
// SCIP Ranges have a (very large) optimization that ranges if they are on the same line
|
||||||
// only encode as a vector of [start_line, start_col, end_col].
|
// only encode as a vector of [start_line, start_col, end_col].
|
||||||
//
|
//
|
||||||
|
@ -247,6 +363,13 @@ fn text_range_to_scip_range(line_index: &LineIndex, range: TextRange) -> Vec<i32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn text_range_to_string(relative_path: &str, line_index: &LineIndex, range: TextRange) -> String {
|
||||||
|
let LineCol { line: start_line, col: start_col } = line_index.index.line_col(range.start());
|
||||||
|
let LineCol { line: end_line, col: end_col } = line_index.index.line_col(range.end());
|
||||||
|
|
||||||
|
format!("{relative_path}:{start_line}:{start_col}-{end_line}:{end_col}")
|
||||||
|
}
|
||||||
|
|
||||||
fn new_descriptor_str(
|
fn new_descriptor_str(
|
||||||
name: &str,
|
name: &str,
|
||||||
suffix: scip_types::descriptor::Suffix,
|
suffix: scip_types::descriptor::Suffix,
|
||||||
|
@ -259,14 +382,6 @@ fn new_descriptor_str(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_descriptor(name: &str, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor {
|
|
||||||
if name.contains('\'') {
|
|
||||||
new_descriptor_str(&format!("`{name}`"), suffix)
|
|
||||||
} else {
|
|
||||||
new_descriptor_str(name, suffix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn symbol_kind(kind: SymbolInformationKind) -> scip_types::symbol_information::Kind {
|
fn symbol_kind(kind: SymbolInformationKind) -> scip_types::symbol_information::Kind {
|
||||||
use scip_types::symbol_information::Kind as ScipKind;
|
use scip_types::symbol_information::Kind as ScipKind;
|
||||||
match kind {
|
match kind {
|
||||||
|
@ -295,17 +410,91 @@ fn symbol_kind(kind: SymbolInformationKind) -> scip_types::symbol_information::K
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol {
|
#[derive(Clone)]
|
||||||
use scip_types::descriptor::Suffix::*;
|
struct TokenSymbols {
|
||||||
|
symbol: String,
|
||||||
|
/// Definition that contains this one. Only set when `symbol` is local.
|
||||||
|
enclosing_symbol: Option<String>,
|
||||||
|
/// True if this symbol is for an inherent impl. This is used to only emit `SymbolInformation`
|
||||||
|
/// for a struct's first inherent impl, since their symbol names are not disambiguated.
|
||||||
|
is_inherent_impl: bool,
|
||||||
|
}
|
||||||
|
|
||||||
let package_name = moniker.package_information.name.clone();
|
struct SymbolGenerator {
|
||||||
let version = moniker.package_information.version.clone();
|
token_to_symbols: FxHashMap<TokenId, Option<TokenSymbols>>,
|
||||||
let descriptors = moniker
|
local_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolGenerator {
|
||||||
|
fn new() -> Self {
|
||||||
|
SymbolGenerator { token_to_symbols: FxHashMap::default(), local_count: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_document_local_state(&mut self) {
|
||||||
|
self.local_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn token_symbols(&mut self, id: TokenId, token: &TokenStaticData) -> Option<TokenSymbols> {
|
||||||
|
let mut local_count = self.local_count;
|
||||||
|
let token_symbols = self
|
||||||
|
.token_to_symbols
|
||||||
|
.entry(id)
|
||||||
|
.or_insert_with(|| {
|
||||||
|
Some(match token.moniker.as_ref()? {
|
||||||
|
MonikerResult::Moniker(moniker) => TokenSymbols {
|
||||||
|
symbol: scip::symbol::format_symbol(moniker_to_symbol(moniker)),
|
||||||
|
enclosing_symbol: None,
|
||||||
|
is_inherent_impl: moniker
|
||||||
.identifier
|
.identifier
|
||||||
|
.description
|
||||||
|
.get(moniker.identifier.description.len() - 2)
|
||||||
|
.map_or(false, |descriptor| {
|
||||||
|
descriptor.desc == MonikerDescriptorKind::Type
|
||||||
|
&& descriptor.name == "impl"
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
MonikerResult::Local { enclosing_moniker } => {
|
||||||
|
let local_symbol = scip::types::Symbol::new_local(local_count);
|
||||||
|
local_count += 1;
|
||||||
|
TokenSymbols {
|
||||||
|
symbol: scip::symbol::format_symbol(local_symbol),
|
||||||
|
enclosing_symbol: enclosing_moniker
|
||||||
|
.as_ref()
|
||||||
|
.map(moniker_to_symbol)
|
||||||
|
.map(scip::symbol::format_symbol),
|
||||||
|
is_inherent_impl: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.clone();
|
||||||
|
self.local_count = local_count;
|
||||||
|
token_symbols
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn moniker_to_symbol(moniker: &Moniker) -> scip_types::Symbol {
|
||||||
|
scip_types::Symbol {
|
||||||
|
scheme: "rust-analyzer".into(),
|
||||||
|
package: Some(scip_types::Package {
|
||||||
|
manager: "cargo".to_owned(),
|
||||||
|
name: moniker.package_information.name.clone(),
|
||||||
|
version: moniker.package_information.version.clone().unwrap_or_else(|| ".".to_owned()),
|
||||||
|
special_fields: Default::default(),
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
descriptors: moniker_descriptors(&moniker.identifier),
|
||||||
|
special_fields: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn moniker_descriptors(identifier: &MonikerIdentifier) -> Vec<scip_types::Descriptor> {
|
||||||
|
use scip_types::descriptor::Suffix::*;
|
||||||
|
identifier
|
||||||
.description
|
.description
|
||||||
.iter()
|
.iter()
|
||||||
.map(|desc| {
|
.map(|desc| {
|
||||||
new_descriptor(
|
new_descriptor_str(
|
||||||
&desc.name,
|
&desc.name,
|
||||||
match desc.desc {
|
match desc.desc {
|
||||||
MonikerDescriptorKind::Namespace => Namespace,
|
MonikerDescriptorKind::Namespace => Namespace,
|
||||||
|
@ -319,27 +508,13 @@ fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect()
|
||||||
|
|
||||||
scip_types::Symbol {
|
|
||||||
scheme: "rust-analyzer".into(),
|
|
||||||
package: Some(scip_types::Package {
|
|
||||||
manager: "cargo".to_owned(),
|
|
||||||
name: package_name,
|
|
||||||
version: version.unwrap_or_else(|| ".".to_owned()),
|
|
||||||
special_fields: Default::default(),
|
|
||||||
})
|
|
||||||
.into(),
|
|
||||||
descriptors,
|
|
||||||
special_fields: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ide::{FilePosition, TextSize};
|
use ide::{FilePosition, TextSize};
|
||||||
use scip::symbol::format_symbol;
|
|
||||||
use test_fixture::ChangeFixture;
|
use test_fixture::ChangeFixture;
|
||||||
use vfs::VfsPath;
|
use vfs::VfsPath;
|
||||||
|
|
||||||
|
@ -376,7 +551,21 @@ mod test {
|
||||||
for &(range, id) in &file.tokens {
|
for &(range, id) in &file.tokens {
|
||||||
if range.contains(offset - TextSize::from(1)) {
|
if range.contains(offset - TextSize::from(1)) {
|
||||||
let token = si.tokens.get(id).unwrap();
|
let token = si.tokens.get(id).unwrap();
|
||||||
found_symbol = token.moniker.as_ref().map(moniker_to_symbol);
|
found_symbol = match token.moniker.as_ref() {
|
||||||
|
None => None,
|
||||||
|
Some(MonikerResult::Moniker(moniker)) => {
|
||||||
|
Some(scip::symbol::format_symbol(moniker_to_symbol(moniker)))
|
||||||
|
}
|
||||||
|
Some(MonikerResult::Local { enclosing_moniker: Some(moniker) }) => {
|
||||||
|
Some(format!(
|
||||||
|
"local enclosed by {}",
|
||||||
|
scip::symbol::format_symbol(moniker_to_symbol(moniker))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Some(MonikerResult::Local { enclosing_moniker: None }) => {
|
||||||
|
Some("unenclosed local".to_owned())
|
||||||
|
}
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -388,9 +577,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(found_symbol.is_some(), "must have one symbol {found_symbol:?}");
|
assert!(found_symbol.is_some(), "must have one symbol {found_symbol:?}");
|
||||||
let res = found_symbol.unwrap();
|
assert_eq!(found_symbol.unwrap(), expected);
|
||||||
let formatted = format_symbol(res);
|
|
||||||
assert_eq!(formatted, expected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -467,8 +654,7 @@ pub mod module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
// "foo::module::MyTrait::MyType",
|
"rust-analyzer cargo foo 0.1.0 module/MyTrait#MyType#",
|
||||||
"rust-analyzer cargo foo 0.1.0 module/MyTrait#[MyType]",
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,8 +675,7 @@ pub mod module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
// "foo::module::MyStruct::MyTrait::func",
|
"rust-analyzer cargo foo 0.1.0 module/impl#[MyStruct][MyTrait]func().",
|
||||||
"rust-analyzer cargo foo 0.1.0 module/MyStruct#MyTrait#func().",
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,7 +711,7 @@ pub mod example_mod {
|
||||||
pub fn func(x$0: usize) {}
|
pub fn func(x$0: usize) {}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
"rust-analyzer cargo foo 0.1.0 example_mod/func().(x)",
|
"local enclosed by rust-analyzer cargo foo 0.1.0 example_mod/func().",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,7 +731,7 @@ pub mod example_mod {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
"rust-analyzer cargo foo 0.1.0 example_mod/func().(x)",
|
"local enclosed by rust-analyzer cargo foo 0.1.0 example_mod/func().",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -566,7 +751,7 @@ pub mod example_mod {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
"",
|
"local enclosed by rust-analyzer cargo foo 0.1.0 module/func().",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,7 +794,7 @@ pub mod example_mod {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn symbol_for_for_type_alias() {
|
fn symbol_for_type_alias() {
|
||||||
check_symbol(
|
check_symbol(
|
||||||
r#"
|
r#"
|
||||||
//- /workspace/lib.rs crate:main
|
//- /workspace/lib.rs crate:main
|
||||||
|
@ -619,6 +804,70 @@ pub mod example_mod {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: This test represents current misbehavior.
|
||||||
|
#[test]
|
||||||
|
fn symbol_for_nested_function() {
|
||||||
|
check_symbol(
|
||||||
|
r#"
|
||||||
|
//- /workspace/lib.rs crate:main
|
||||||
|
pub fn func() {
|
||||||
|
pub fn inner_func$0() {}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"rust-analyzer cargo main . inner_func().",
|
||||||
|
// FIXME: This should be a local:
|
||||||
|
// "local enclosed by rust-analyzer cargo main . func().",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This test represents current misbehavior.
|
||||||
|
#[test]
|
||||||
|
fn symbol_for_struct_in_function() {
|
||||||
|
check_symbol(
|
||||||
|
r#"
|
||||||
|
//- /workspace/lib.rs crate:main
|
||||||
|
pub fn func() {
|
||||||
|
struct SomeStruct$0 {}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"rust-analyzer cargo main . SomeStruct#",
|
||||||
|
// FIXME: This should be a local:
|
||||||
|
// "local enclosed by rust-analyzer cargo main . func().",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This test represents current misbehavior.
|
||||||
|
#[test]
|
||||||
|
fn symbol_for_const_in_function() {
|
||||||
|
check_symbol(
|
||||||
|
r#"
|
||||||
|
//- /workspace/lib.rs crate:main
|
||||||
|
pub fn func() {
|
||||||
|
const SOME_CONST$0: u32 = 1;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"rust-analyzer cargo main . SOME_CONST.",
|
||||||
|
// FIXME: This should be a local:
|
||||||
|
// "local enclosed by rust-analyzer cargo main . func().",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This test represents current misbehavior.
|
||||||
|
#[test]
|
||||||
|
fn symbol_for_static_in_function() {
|
||||||
|
check_symbol(
|
||||||
|
r#"
|
||||||
|
//- /workspace/lib.rs crate:main
|
||||||
|
pub fn func() {
|
||||||
|
static SOME_STATIC$0: u32 = 1;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"rust-analyzer cargo main . SOME_STATIC.",
|
||||||
|
// FIXME: This should be a local:
|
||||||
|
// "local enclosed by rust-analyzer cargo main . func().",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn documentation_matches_doc_comment() {
|
fn documentation_matches_doc_comment() {
|
||||||
let s = "/// foo\nfn bar() {}";
|
let s = "/// foo\nfn bar() {}";
|
||||||
|
|
Loading…
Reference in a new issue