|  | //! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports) | 
|  | //! for LSIF and LSP. | 
|  |  | 
|  | use core::fmt; | 
|  |  | 
|  | use hir::{Adt, AsAssocItem, Crate, HirDisplay, MacroKind, Semantics}; | 
|  | use ide_db::{ | 
|  | FilePosition, RootDatabase, | 
|  | base_db::{CrateOrigin, LangCrateOrigin}, | 
|  | defs::{Definition, IdentClass}, | 
|  | helpers::pick_best_token, | 
|  | }; | 
|  | use itertools::Itertools; | 
|  | use syntax::{AstNode, SyntaxKind::*, T}; | 
|  |  | 
|  | use crate::{RangeInfo, doc_links::token_as_doc_comment, parent_module::crates_for}; | 
|  |  | 
|  | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | 
|  | pub enum MonikerDescriptorKind { | 
|  | Namespace, | 
|  | Type, | 
|  | Term, | 
|  | Method, | 
|  | TypeParameter, | 
|  | Parameter, | 
|  | Macro, | 
|  | Meta, | 
|  | } | 
|  |  | 
|  | // Subset of scip_types::SymbolInformation::Kind | 
|  | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 
|  | pub enum SymbolInformationKind { | 
|  | AssociatedType, | 
|  | Attribute, | 
|  | Constant, | 
|  | Enum, | 
|  | EnumMember, | 
|  | Field, | 
|  | Function, | 
|  | Macro, | 
|  | Method, | 
|  | Module, | 
|  | Parameter, | 
|  | SelfParameter, | 
|  | StaticMethod, | 
|  | StaticVariable, | 
|  | Struct, | 
|  | Trait, | 
|  | TraitMethod, | 
|  | Type, | 
|  | TypeAlias, | 
|  | TypeParameter, | 
|  | Union, | 
|  | Variable, | 
|  | } | 
|  |  | 
|  | impl From<SymbolInformationKind> for MonikerDescriptorKind { | 
|  | fn from(value: SymbolInformationKind) -> Self { | 
|  | match value { | 
|  | SymbolInformationKind::AssociatedType => Self::Type, | 
|  | SymbolInformationKind::Attribute => Self::Meta, | 
|  | SymbolInformationKind::Constant => Self::Term, | 
|  | SymbolInformationKind::Enum => Self::Type, | 
|  | SymbolInformationKind::EnumMember => Self::Type, | 
|  | SymbolInformationKind::Field => Self::Term, | 
|  | SymbolInformationKind::Function => Self::Method, | 
|  | SymbolInformationKind::Macro => Self::Macro, | 
|  | SymbolInformationKind::Method => Self::Method, | 
|  | SymbolInformationKind::Module => Self::Namespace, | 
|  | SymbolInformationKind::Parameter => Self::Parameter, | 
|  | SymbolInformationKind::SelfParameter => Self::Parameter, | 
|  | SymbolInformationKind::StaticMethod => Self::Method, | 
|  | SymbolInformationKind::StaticVariable => Self::Term, | 
|  | SymbolInformationKind::Struct => Self::Type, | 
|  | SymbolInformationKind::Trait => Self::Type, | 
|  | SymbolInformationKind::TraitMethod => Self::Method, | 
|  | SymbolInformationKind::Type => Self::Type, | 
|  | SymbolInformationKind::TypeAlias => Self::Type, | 
|  | SymbolInformationKind::TypeParameter => Self::TypeParameter, | 
|  | SymbolInformationKind::Union => Self::Type, | 
|  | SymbolInformationKind::Variable => Self::Term, | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | 
|  | pub struct MonikerDescriptor { | 
|  | pub name: String, | 
|  | pub desc: MonikerDescriptorKind, | 
|  | } | 
|  |  | 
|  | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | 
|  | pub struct MonikerIdentifier { | 
|  | pub crate_name: String, | 
|  | pub description: Vec<MonikerDescriptor>, | 
|  | } | 
|  |  | 
|  | impl fmt::Display for MonikerIdentifier { | 
|  | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 
|  | f.write_str(&self.crate_name)?; | 
|  | f.write_fmt(format_args!("::{}", self.description.iter().map(|x| &x.name).join("::"))) | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | 
|  | pub enum MonikerKind { | 
|  | Import, | 
|  | Export, | 
|  | } | 
|  |  | 
|  | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 
|  | pub enum MonikerResult { | 
|  | /// Uniquely identifies a definition. | 
|  | Moniker(Moniker), | 
|  | /// 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 { | 
|  | pub fn from_def(db: &RootDatabase, def: Definition, from_crate: Crate) -> Option<Self> { | 
|  | def_to_moniker(db, def, from_crate) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// 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)] | 
|  | pub struct PackageInformation { | 
|  | pub name: String, | 
|  | pub repo: Option<String>, | 
|  | pub version: Option<String>, | 
|  | } | 
|  |  | 
|  | pub(crate) fn moniker( | 
|  | db: &RootDatabase, | 
|  | FilePosition { file_id, offset }: FilePosition, | 
|  | ) -> Option<RangeInfo<Vec<MonikerResult>>> { | 
|  | let sema = &Semantics::new(db); | 
|  | let file = sema.parse_guess_edition(file_id).syntax().clone(); | 
|  | let current_crate: hir::Crate = crates_for(db, file_id).pop()?.into(); | 
|  | let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { | 
|  | IDENT | 
|  | | INT_NUMBER | 
|  | | LIFETIME_IDENT | 
|  | | T![self] | 
|  | | T![super] | 
|  | | T![crate] | 
|  | | T![Self] | 
|  | | COMMENT => 2, | 
|  | kind if kind.is_trivia() => 0, | 
|  | _ => 1, | 
|  | })?; | 
|  | if let Some(doc_comment) = token_as_doc_comment(&original_token) { | 
|  | return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, _| { | 
|  | let m = def_to_moniker(db, def, current_crate)?; | 
|  | Some(RangeInfo::new(original_token.text_range(), vec![m])) | 
|  | }); | 
|  | } | 
|  | let navs = sema | 
|  | .descend_into_macros_exact(original_token.clone()) | 
|  | .into_iter() | 
|  | .filter_map(|token| { | 
|  | IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| { | 
|  | it.into_iter().flat_map(|def| def_to_moniker(sema.db, def, current_crate)) | 
|  | }) | 
|  | }) | 
|  | .flatten() | 
|  | .unique() | 
|  | .collect::<Vec<_>>(); | 
|  | Some(RangeInfo::new(original_token.text_range(), navs)) | 
|  | } | 
|  |  | 
|  | pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformationKind { | 
|  | use SymbolInformationKind::*; | 
|  |  | 
|  | match def { | 
|  | Definition::Macro(it) => match it.kind(db) { | 
|  | MacroKind::Derive | 
|  | | MacroKind::DeriveBuiltIn | 
|  | | MacroKind::AttrBuiltIn | 
|  | | MacroKind::Attr => Attribute, | 
|  | MacroKind::Declarative | MacroKind::DeclarativeBuiltIn | MacroKind::ProcMacro => Macro, | 
|  | }, | 
|  | Definition::Field(..) | Definition::TupleField(..) => Field, | 
|  | Definition::Module(..) | Definition::Crate(..) => Module, | 
|  | Definition::Function(it) => { | 
|  | if it.as_assoc_item(db).is_some() { | 
|  | if it.has_self_param(db) { | 
|  | if it.has_body(db) { Method } else { TraitMethod } | 
|  | } else { | 
|  | StaticMethod | 
|  | } | 
|  | } else { | 
|  | Function | 
|  | } | 
|  | } | 
|  | Definition::Adt(Adt::Struct(..)) => Struct, | 
|  | Definition::Adt(Adt::Union(..)) => Union, | 
|  | Definition::Adt(Adt::Enum(..)) => Enum, | 
|  | Definition::Variant(..) => EnumMember, | 
|  | Definition::Const(..) => Constant, | 
|  | Definition::Static(..) => StaticVariable, | 
|  | Definition::Trait(..) => Trait, | 
|  | Definition::TypeAlias(it) => { | 
|  | if it.as_assoc_item(db).is_some() { | 
|  | AssociatedType | 
|  | } else { | 
|  | TypeAlias | 
|  | } | 
|  | } | 
|  | Definition::BuiltinType(..) => Type, | 
|  | Definition::BuiltinLifetime(_) => TypeParameter, | 
|  | Definition::SelfType(..) => TypeAlias, | 
|  | Definition::GenericParam(..) => TypeParameter, | 
|  | Definition::Local(it) => { | 
|  | if it.is_self(db) { | 
|  | SelfParameter | 
|  | } else if it.is_param(db) { | 
|  | Parameter | 
|  | } else { | 
|  | Variable | 
|  | } | 
|  | } | 
|  | Definition::Label(..) | Definition::InlineAsmOperand(_) => Variable, // For lack of a better variant | 
|  | Definition::DeriveHelper(..) => Attribute, | 
|  | Definition::BuiltinAttr(..) => Attribute, | 
|  | Definition::ToolModule(..) => Module, | 
|  | Definition::ExternCrateDecl(..) => Module, | 
|  | Definition::InlineAsmRegOrRegClass(..) => Module, | 
|  | } | 
|  | } | 
|  |  | 
|  | /// 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( | 
|  | db: &RootDatabase, | 
|  | definition: Definition, | 
|  | from_crate: Crate, | 
|  | ) -> Option<MonikerResult> { | 
|  | match definition { | 
|  | Definition::Local(_) | Definition::Label(_) | Definition::GenericParam(_) => { | 
|  | return Some(MonikerResult::Local { | 
|  | enclosing_moniker: enclosing_def_to_moniker(db, definition, from_crate), | 
|  | }); | 
|  | } | 
|  | _ => {} | 
|  | } | 
|  | Some(MonikerResult::Moniker(def_to_non_local_moniker(db, definition, from_crate)?)) | 
|  | } | 
|  |  | 
|  | 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 = match definition { | 
|  | Definition::Module(module) if module.is_crate_root() => module, | 
|  | _ => definition.module(db)?, | 
|  | }; | 
|  | let krate = module.krate(); | 
|  | let edition = krate.edition(db); | 
|  |  | 
|  | // Add descriptors for this definition and every enclosing definition. | 
|  | let mut reverse_description = vec![]; | 
|  | let mut def = definition; | 
|  | loop { | 
|  | match def { | 
|  | Definition::SelfType(impl_) => { | 
|  | if let Some(trait_ref) = impl_.trait_ref(db) { | 
|  | // Trait impls use the trait type for the 2nd parameter. | 
|  | reverse_description.push(MonikerDescriptor { | 
|  | name: display(db, module, trait_ref), | 
|  | desc: MonikerDescriptorKind::TypeParameter, | 
|  | }); | 
|  | } | 
|  | // Both inherent and trait impls use the self type for the first parameter. | 
|  | reverse_description.push(MonikerDescriptor { | 
|  | name: display(db, module, impl_.self_ty(db)), | 
|  | desc: MonikerDescriptorKind::TypeParameter, | 
|  | }); | 
|  | reverse_description.push(MonikerDescriptor { | 
|  | name: "impl".to_owned(), | 
|  | desc: MonikerDescriptorKind::Type, | 
|  | }); | 
|  | } | 
|  | _ => { | 
|  | if let Some(name) = def.name(db) { | 
|  | reverse_description.push(MonikerDescriptor { | 
|  | name: name.display(db, edition).to_string(), | 
|  | desc: def_to_kind(db, def).into(), | 
|  | }); | 
|  | } else { | 
|  | match def { | 
|  | Definition::Module(module) if module.is_crate_root() => { | 
|  | // only include `crate` namespace by itself because we prefer | 
|  | // `rust-analyzer cargo foo . bar/` over `rust-analyzer cargo foo . crate/bar/` | 
|  | if reverse_description.is_empty() { | 
|  | reverse_description.push(MonikerDescriptor { | 
|  | name: "crate".to_owned(), | 
|  | desc: MonikerDescriptorKind::Namespace, | 
|  | }); | 
|  | } | 
|  | } | 
|  | _ => { | 
|  | tracing::error!(?def, "Encountered enclosing definition with no name"); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | let Some(next_def) = def.enclosing_definition(db) else { | 
|  | break; | 
|  | }; | 
|  | def = next_def; | 
|  | } | 
|  | if reverse_description.is_empty() { | 
|  | return None; | 
|  | } | 
|  | reverse_description.reverse(); | 
|  | let description = reverse_description; | 
|  |  | 
|  | Some(Moniker { | 
|  | identifier: MonikerIdentifier { | 
|  | crate_name: krate.display_name(db)?.crate_name().to_string(), | 
|  | description, | 
|  | }, | 
|  | kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import }, | 
|  | package_information: { | 
|  | let (name, repo, version) = match krate.origin(db) { | 
|  | CrateOrigin::Library { repo, name } => (name, repo, krate.version(db)), | 
|  | CrateOrigin::Local { repo, name } => ( | 
|  | name.unwrap_or(krate.display_name(db)?.canonical_name().to_owned()), | 
|  | repo, | 
|  | krate.version(db), | 
|  | ), | 
|  | CrateOrigin::Rustc { name } => ( | 
|  | name.clone(), | 
|  | Some("https://github.com/rust-lang/rust/".to_owned()), | 
|  | Some(format!("https://github.com/rust-lang/rust/compiler/{name}",)), | 
|  | ), | 
|  | CrateOrigin::Lang(lang) => ( | 
|  | krate.display_name(db)?.canonical_name().to_owned(), | 
|  | Some("https://github.com/rust-lang/rust/".to_owned()), | 
|  | Some(match lang { | 
|  | LangCrateOrigin::Other => { | 
|  | "https://github.com/rust-lang/rust/library/".into() | 
|  | } | 
|  | lang => format!("https://github.com/rust-lang/rust/library/{lang}",), | 
|  | }), | 
|  | ), | 
|  | }; | 
|  | PackageInformation { name: name.as_str().to_owned(), repo, version } | 
|  | }, | 
|  | }) | 
|  | } | 
|  |  | 
|  | fn display<T: HirDisplay>(db: &RootDatabase, 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, module.krate().to_display_target(db)).to_string(); | 
|  | tracing::error!( | 
|  | display = %fallback_result, "`display_source_code` failed; falling back to using display" | 
|  | ); | 
|  | fallback_result | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[cfg(test)] | 
|  | mod tests { | 
|  | use crate::{MonikerResult, fixture}; | 
|  |  | 
|  | use super::MonikerKind; | 
|  |  | 
|  | #[allow(dead_code)] | 
|  | #[track_caller] | 
|  | fn no_moniker(#[rust_analyzer::rust_fixture] ra_fixture: &str) { | 
|  | let (analysis, position) = fixture::position(ra_fixture); | 
|  | if let Some(x) = analysis.moniker(position).unwrap() { | 
|  | assert_eq!(x.info.len(), 0, "Moniker found but no moniker expected: {x:?}"); | 
|  | } | 
|  | } | 
|  |  | 
|  | #[track_caller] | 
|  | fn check_local_moniker( | 
|  | #[rust_analyzer::rust_fixture] 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"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[track_caller] | 
|  | fn check_moniker( | 
|  | #[rust_analyzer::rust_fixture] 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 } => { | 
|  | panic!("Unexpected local enclosed in {enclosing_moniker:?}"); | 
|  | } | 
|  | MonikerResult::Moniker(x) => { | 
|  | assert_eq!(identifier, x.identifier.to_string()); | 
|  | assert_eq!(package, format!("{:?}", x.package_information)); | 
|  | assert_eq!(kind, x.kind); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn basic() { | 
|  | check_moniker( | 
|  | r#" | 
|  | //- /lib.rs crate:main deps:foo | 
|  | use foo::module::func; | 
|  | fn main() { | 
|  | func$0(); | 
|  | } | 
|  | //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library | 
|  | pub mod module { | 
|  | pub fn func() {} | 
|  | } | 
|  | "#, | 
|  | "foo::module::func", | 
|  | r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, | 
|  | MonikerKind::Import, | 
|  | ); | 
|  | check_moniker( | 
|  | r#" | 
|  | //- /lib.rs crate:main deps:foo | 
|  | use foo::module::func; | 
|  | fn main() { | 
|  | func(); | 
|  | } | 
|  | //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library | 
|  | pub mod module { | 
|  | pub fn func$0() {} | 
|  | } | 
|  | "#, | 
|  | "foo::module::func", | 
|  | r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, | 
|  | MonikerKind::Export, | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn moniker_for_trait() { | 
|  | check_moniker( | 
|  | r#" | 
|  | //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library | 
|  | pub mod module { | 
|  | pub trait MyTrait { | 
|  | pub fn func$0() {} | 
|  | } | 
|  | } | 
|  | "#, | 
|  | "foo::module::MyTrait::func", | 
|  | r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, | 
|  | MonikerKind::Export, | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn moniker_for_trait_constant() { | 
|  | check_moniker( | 
|  | r#" | 
|  | //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library | 
|  | pub mod module { | 
|  | pub trait MyTrait { | 
|  | const MY_CONST$0: u8; | 
|  | } | 
|  | } | 
|  | "#, | 
|  | "foo::module::MyTrait::MY_CONST", | 
|  | r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, | 
|  | MonikerKind::Export, | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn moniker_for_trait_type() { | 
|  | check_moniker( | 
|  | r#" | 
|  | //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library | 
|  | pub mod module { | 
|  | pub trait MyTrait { | 
|  | type MyType$0; | 
|  | } | 
|  | } | 
|  | "#, | 
|  | "foo::module::MyTrait::MyType", | 
|  | r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, | 
|  | MonikerKind::Export, | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn moniker_for_trait_impl_function() { | 
|  | check_moniker( | 
|  | r#" | 
|  | //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library | 
|  | pub mod module { | 
|  | pub trait MyTrait { | 
|  | pub fn func() {} | 
|  | } | 
|  | struct MyStruct {} | 
|  | impl MyTrait for MyStruct { | 
|  | pub fn func$0() {} | 
|  | } | 
|  | } | 
|  | "#, | 
|  | "foo::module::impl::MyStruct::MyTrait::func", | 
|  | r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, | 
|  | MonikerKind::Export, | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn moniker_for_field() { | 
|  | check_moniker( | 
|  | r#" | 
|  | //- /lib.rs crate:main deps:foo | 
|  | use foo::St; | 
|  | fn main() { | 
|  | let x = St { a$0: 2 }; | 
|  | } | 
|  | //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library | 
|  | pub struct St { | 
|  | pub a: i32, | 
|  | } | 
|  | "#, | 
|  | "foo::St::a", | 
|  | r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, | 
|  | MonikerKind::Import, | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn local() { | 
|  | check_local_moniker( | 
|  | r#" | 
|  | //- /lib.rs crate:main deps:foo | 
|  | use foo::module::func; | 
|  | fn main() { | 
|  | func(); | 
|  | } | 
|  | //- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library | 
|  | pub mod module { | 
|  | pub fn func() { | 
|  | let x$0 = 2; | 
|  | } | 
|  | } | 
|  | "#, | 
|  | "foo::module::func", | 
|  | r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, | 
|  | MonikerKind::Export, | 
|  | ); | 
|  | } | 
|  | } |