1use core::fmt;
5
6use hir::{Adt, AsAssocItem, Crate, HirDisplay, MacroKind, Semantics};
7use ide_db::{
8 FilePosition, RootDatabase,
9 base_db::{CrateOrigin, LangCrateOrigin, TargetKind},
10 defs::{Definition, IdentClass},
11 helpers::pick_best_token,
12};
13use itertools::Itertools;
14use syntax::{AstNode, SyntaxKind::*, T};
15
16use crate::{RangeInfo, doc_links::token_as_doc_comment, parent_module::crates_for};
17
18#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
19pub enum MonikerDescriptorKind {
20 Namespace,
21 Type,
22 Term,
23 Method,
24 TypeParameter,
25 Parameter,
26 Macro,
27 Meta,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
32pub enum SymbolInformationKind {
33 AssociatedType,
34 Attribute,
35 Constant,
36 Enum,
37 EnumMember,
38 Field,
39 Function,
40 Macro,
41 Method,
42 Module,
43 Parameter,
44 SelfParameter,
45 StaticMethod,
46 StaticVariable,
47 Struct,
48 Trait,
49 TraitMethod,
50 Type,
51 TypeAlias,
52 TypeParameter,
53 Union,
54 Variable,
55}
56
57impl From<SymbolInformationKind> for MonikerDescriptorKind {
58 fn from(value: SymbolInformationKind) -> Self {
59 match value {
60 SymbolInformationKind::AssociatedType => Self::Type,
61 SymbolInformationKind::Attribute => Self::Meta,
62 SymbolInformationKind::Constant => Self::Term,
63 SymbolInformationKind::Enum => Self::Type,
64 SymbolInformationKind::EnumMember => Self::Type,
65 SymbolInformationKind::Field => Self::Term,
66 SymbolInformationKind::Function => Self::Method,
67 SymbolInformationKind::Macro => Self::Macro,
68 SymbolInformationKind::Method => Self::Method,
69 SymbolInformationKind::Module => Self::Namespace,
70 SymbolInformationKind::Parameter => Self::Parameter,
71 SymbolInformationKind::SelfParameter => Self::Parameter,
72 SymbolInformationKind::StaticMethod => Self::Method,
73 SymbolInformationKind::StaticVariable => Self::Term,
74 SymbolInformationKind::Struct => Self::Type,
75 SymbolInformationKind::Trait => Self::Type,
76 SymbolInformationKind::TraitMethod => Self::Method,
77 SymbolInformationKind::Type => Self::Type,
78 SymbolInformationKind::TypeAlias => Self::Type,
79 SymbolInformationKind::TypeParameter => Self::TypeParameter,
80 SymbolInformationKind::Union => Self::Type,
81 SymbolInformationKind::Variable => Self::Term,
82 }
83 }
84}
85
86#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
87pub struct MonikerDescriptor {
88 pub name: String,
89 pub desc: MonikerDescriptorKind,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
93pub struct MonikerIdentifier {
94 pub crate_name: String,
95 pub description: Vec<MonikerDescriptor>,
96}
97
98impl fmt::Display for MonikerIdentifier {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.write_str(&self.crate_name)?;
101 f.write_fmt(format_args!("::{}", self.description.iter().map(|x| &x.name).join("::")))
102 }
103}
104
105#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
106pub enum MonikerKind {
107 Import,
108 Export,
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Hash)]
112pub enum MonikerResult {
113 Moniker(Moniker),
115 Local { enclosing_moniker: Option<Moniker> },
118}
119
120impl MonikerResult {
121 pub fn from_def(db: &RootDatabase, def: Definition<'_>, from_crate: Crate) -> Option<Self> {
122 def_to_moniker(db, def, from_crate)
123 }
124}
125
126#[derive(Debug, Clone, PartialEq, Eq, Hash)]
129pub struct Moniker {
130 pub identifier: MonikerIdentifier,
131 pub kind: MonikerKind,
132 pub package_information: PackageInformation,
133}
134
135#[derive(Debug, Clone, PartialEq, Eq, Hash)]
136pub struct PackageInformation {
137 pub name: String,
138 pub repo: Option<String>,
139 pub version: Option<String>,
140}
141
142pub(crate) fn moniker(
143 db: &RootDatabase,
144 FilePosition { file_id, offset }: FilePosition,
145) -> Option<RangeInfo<Vec<MonikerResult>>> {
146 let sema = &Semantics::new(db);
147 let file = sema.parse_guess_edition(file_id).syntax().clone();
148 let current_crate: hir::Crate = crates_for(db, file_id).pop()?.into();
149 let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
150 IDENT
151 | INT_NUMBER
152 | LIFETIME_IDENT
153 | T![self]
154 | T![super]
155 | T![crate]
156 | T![Self]
157 | COMMENT
158 | INNER_DOC_COMMENT
159 | OUTER_DOC_COMMENT => 2,
160 kind if kind.is_trivia() => 0,
161 _ => 1,
162 })?;
163 if let Some(doc_comment) = token_as_doc_comment(&original_token) {
164 return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, _| {
165 let m = def_to_moniker(db, def, current_crate)?;
166 Some(RangeInfo::new(original_token.text_range(), vec![m]))
167 });
168 }
169 let navs = sema
170 .descend_into_macros_exact(original_token.clone())
171 .into_iter()
172 .filter_map(|token| {
173 IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| {
174 it.into_iter().flat_map(|def| def_to_moniker(sema.db, def, current_crate))
175 })
176 })
177 .flatten()
178 .unique()
179 .collect::<Vec<_>>();
180 Some(RangeInfo::new(original_token.text_range(), navs))
181}
182
183pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition<'_>) -> SymbolInformationKind {
184 use SymbolInformationKind::*;
185
186 match def {
187 Definition::Macro(it) => match it.kind(db) {
188 MacroKind::Derive
189 | MacroKind::DeriveBuiltIn
190 | MacroKind::AttrBuiltIn
191 | MacroKind::Attr => Attribute,
192 MacroKind::Declarative | MacroKind::DeclarativeBuiltIn | MacroKind::ProcMacro => Macro,
193 },
194 Definition::Field(..) | Definition::TupleField(..) => Field,
195 Definition::Module(..) | Definition::Crate(..) => Module,
196 Definition::Function(it) => {
197 if it.as_assoc_item(db).is_some() {
198 if it.has_self_param(db) {
199 if it.has_body(db) { Method } else { TraitMethod }
200 } else {
201 StaticMethod
202 }
203 } else {
204 Function
205 }
206 }
207 Definition::Adt(Adt::Struct(..)) => Struct,
208 Definition::Adt(Adt::Union(..)) => Union,
209 Definition::Adt(Adt::Enum(..)) => Enum,
210 Definition::EnumVariant(..) => EnumMember,
211 Definition::Const(..) => Constant,
212 Definition::Static(..) => StaticVariable,
213 Definition::Trait(..) => Trait,
214 Definition::TypeAlias(it) => {
215 if it.as_assoc_item(db).is_some() {
216 AssociatedType
217 } else {
218 TypeAlias
219 }
220 }
221 Definition::BuiltinType(..) => Type,
222 Definition::BuiltinLifetime(_) => TypeParameter,
223 Definition::SelfType(..) => TypeAlias,
224 Definition::GenericParam(..) => TypeParameter,
225 Definition::Local(it) => {
226 if it.is_self(db) {
227 SelfParameter
228 } else if it.is_param(db) {
229 Parameter
230 } else {
231 Variable
232 }
233 }
234 Definition::Label(..) | Definition::InlineAsmOperand(_) => Variable, Definition::DeriveHelper(..) => Attribute,
236 Definition::BuiltinAttr(..) => Attribute,
237 Definition::ToolModule(..) => Module,
238 Definition::ExternCrateDecl(..) => Module,
239 Definition::InlineAsmRegOrRegClass(..) => Module,
240 }
241}
242
243pub(crate) fn def_to_moniker(
254 db: &RootDatabase,
255 definition: Definition<'_>,
256 from_crate: Crate,
257) -> Option<MonikerResult> {
258 match definition {
259 Definition::Local(_) | Definition::Label(_) | Definition::GenericParam(_) => {
260 return Some(MonikerResult::Local {
261 enclosing_moniker: enclosing_def_to_moniker(db, definition, from_crate),
262 });
263 }
264 _ => {}
265 }
266 Some(MonikerResult::Moniker(def_to_non_local_moniker(db, definition, from_crate)?))
267}
268
269fn enclosing_def_to_moniker(
270 db: &RootDatabase,
271 mut def: Definition<'_>,
272 from_crate: Crate,
273) -> Option<Moniker> {
274 loop {
275 let enclosing_def = def.enclosing_definition(db)?;
276 if let Some(enclosing_moniker) = def_to_non_local_moniker(db, enclosing_def, from_crate) {
277 return Some(enclosing_moniker);
278 }
279 def = enclosing_def;
280 }
281}
282
283fn def_to_non_local_moniker(
284 db: &RootDatabase,
285 definition: Definition<'_>,
286 from_crate: Crate,
287) -> Option<Moniker> {
288 let module = match definition {
289 Definition::Module(module) if module.is_crate_root(db) => module,
290 _ => definition.module(db)?,
291 };
292 let krate = module.krate(db);
293 let edition = krate.edition(db);
294
295 let mut reverse_description = vec![];
297 let mut def = definition;
298 loop {
299 match def {
300 Definition::SelfType(impl_) => {
301 if let Some(trait_ref) = impl_.trait_ref(db) {
302 reverse_description.push(MonikerDescriptor {
304 name: display(db, module, trait_ref),
305 desc: MonikerDescriptorKind::TypeParameter,
306 });
307 }
308 reverse_description.push(MonikerDescriptor {
310 name: display(db, module, impl_.self_ty(db)),
311 desc: MonikerDescriptorKind::TypeParameter,
312 });
313 reverse_description.push(MonikerDescriptor {
314 name: "impl".to_owned(),
315 desc: MonikerDescriptorKind::Type,
316 });
317 }
318 _ => {
319 if let Some(name) = def.name(db) {
320 reverse_description.push(MonikerDescriptor {
321 name: name.display(db, edition).to_string(),
322 desc: def_to_kind(db, def).into(),
323 });
324 } else {
325 match def {
326 Definition::Module(module) if module.is_crate_root(db) => {
327 let krate = module.krate(db);
328
329 if !matches!(
331 krate.target_kind(db),
332 TargetKind::Lib { .. } | TargetKind::BuildScript
333 ) && let Some(display_name) = krate.display_name(db)
334 {
335 reverse_description.push(MonikerDescriptor {
336 name: display_name.to_string(),
337 desc: MonikerDescriptorKind::Namespace,
338 });
339 }
340
341 if let Some(target_type) = krate.target_kind(db).as_cargo_target() {
342 reverse_description.push(MonikerDescriptor {
343 name: target_type.to_owned(),
344 desc: MonikerDescriptorKind::Namespace,
345 });
346 }
347
348 if reverse_description.is_empty() {
350 reverse_description.push(MonikerDescriptor {
351 name: "crate".to_owned(),
352 desc: MonikerDescriptorKind::Namespace,
353 });
354 }
355 }
356 _ => {
357 tracing::error!(?def, "Encountered enclosing definition with no name");
358 }
359 }
360 }
361 }
362 }
363 let Some(next_def) = def.enclosing_definition(db) else {
364 break;
365 };
366 def = next_def;
367 }
368 if reverse_description.is_empty() {
369 return None;
370 }
371 reverse_description.reverse();
372 let description = reverse_description;
373
374 Some(Moniker {
375 identifier: MonikerIdentifier {
376 crate_name: krate.display_name(db)?.crate_name().to_string(),
377 description,
378 },
379 kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
380 package_information: {
381 let (name, repo, version) = match krate.origin(db) {
382 CrateOrigin::Library { repo, name } => (name, repo, krate.version(db)),
383 CrateOrigin::Local { repo, name } => (
384 name.unwrap_or(krate.display_name(db)?.canonical_name().to_owned()),
385 repo,
386 krate.version(db),
387 ),
388 CrateOrigin::Rustc { name } => (
389 name.clone(),
390 Some("https://github.com/rust-lang/rust/".to_owned()),
391 Some(format!("https://github.com/rust-lang/rust/compiler/{name}",)),
392 ),
393 CrateOrigin::Lang(lang) => (
394 krate.display_name(db)?.canonical_name().to_owned(),
395 Some("https://github.com/rust-lang/rust/".to_owned()),
396 Some(match lang {
397 LangCrateOrigin::Other => {
398 "https://github.com/rust-lang/rust/library/".into()
399 }
400 lang => format!("https://github.com/rust-lang/rust/library/{lang}",),
401 }),
402 ),
403 };
404 PackageInformation { name: name.as_str().to_owned(), repo, version }
405 },
406 })
407}
408
409fn display<'db, T: HirDisplay<'db>>(db: &'db RootDatabase, module: hir::Module, it: T) -> String {
410 match it.display_source_code(db, module.into(), true) {
411 Ok(result) => result,
412 Err(_) => {
414 let fallback_result =
415 it.display(db, module.krate(db).to_display_target(db)).to_string();
416 tracing::error!(
417 display = %fallback_result, "`display_source_code` failed; falling back to using display"
418 );
419 fallback_result
420 }
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use crate::{MonikerResult, fixture};
427
428 use super::MonikerKind;
429
430 #[allow(dead_code)]
431 #[track_caller]
432 fn no_moniker(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
433 let (analysis, position) = fixture::position(ra_fixture);
434 if let Some(x) = analysis.moniker(position).unwrap() {
435 assert_eq!(x.info.len(), 0, "Moniker found but no moniker expected: {x:?}");
436 }
437 }
438
439 #[track_caller]
440 fn check_local_moniker(
441 #[rust_analyzer::rust_fixture] ra_fixture: &str,
442 identifier: &str,
443 package: &str,
444 kind: MonikerKind,
445 ) {
446 let (analysis, position) = fixture::position(ra_fixture);
447 let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
448 assert_eq!(x.len(), 1);
449 match x.into_iter().next().unwrap() {
450 MonikerResult::Local { enclosing_moniker: Some(x) } => {
451 assert_eq!(identifier, x.identifier.to_string());
452 assert_eq!(package, format!("{:?}", x.package_information));
453 assert_eq!(kind, x.kind);
454 }
455 MonikerResult::Local { enclosing_moniker: None } => {
456 panic!("Unexpected local with no enclosing moniker");
457 }
458 MonikerResult::Moniker(_) => {
459 panic!("Unexpected non-local moniker");
460 }
461 }
462 }
463
464 #[track_caller]
465 fn check_moniker(
466 #[rust_analyzer::rust_fixture] ra_fixture: &str,
467 identifier: &str,
468 package: &str,
469 kind: MonikerKind,
470 ) {
471 let (analysis, position) = fixture::position(ra_fixture);
472 let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
473 assert_eq!(x.len(), 1);
474 match x.into_iter().next().unwrap() {
475 MonikerResult::Local { enclosing_moniker } => {
476 panic!("Unexpected local enclosed in {enclosing_moniker:?}");
477 }
478 MonikerResult::Moniker(x) => {
479 assert_eq!(identifier, x.identifier.to_string());
480 assert_eq!(package, format!("{:?}", x.package_information));
481 assert_eq!(kind, x.kind);
482 }
483 }
484 }
485
486 #[test]
487 fn basic() {
488 check_moniker(
489 r#"
490//- /lib.rs crate:main deps:foo
491use foo::module::func;
492fn main() {
493 func$0();
494}
495//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
496pub mod module {
497 pub fn func() {}
498}
499"#,
500 "foo::lib::module::func",
501 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
502 MonikerKind::Import,
503 );
504 check_moniker(
505 r#"
506//- /lib.rs crate:main deps:foo
507use foo::module::func;
508fn main() {
509 func();
510}
511//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
512pub mod module {
513 pub fn func$0() {}
514}
515"#,
516 "foo::lib::module::func",
517 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
518 MonikerKind::Export,
519 );
520 }
521
522 #[test]
523 fn moniker_for_trait() {
524 check_moniker(
525 r#"
526//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
527pub mod module {
528 pub trait MyTrait {
529 pub fn func$0() {}
530 }
531}
532"#,
533 "foo::lib::module::MyTrait::func",
534 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
535 MonikerKind::Export,
536 );
537 }
538
539 #[test]
540 fn moniker_for_trait_constant() {
541 check_moniker(
542 r#"
543//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
544pub mod module {
545 pub trait MyTrait {
546 const MY_CONST$0: u8;
547 }
548}
549"#,
550 "foo::lib::module::MyTrait::MY_CONST",
551 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
552 MonikerKind::Export,
553 );
554 }
555
556 #[test]
557 fn moniker_for_trait_type() {
558 check_moniker(
559 r#"
560//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
561pub mod module {
562 pub trait MyTrait {
563 type MyType$0;
564 }
565}
566"#,
567 "foo::lib::module::MyTrait::MyType",
568 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
569 MonikerKind::Export,
570 );
571 }
572
573 #[test]
574 fn moniker_for_trait_impl_function() {
575 check_moniker(
576 r#"
577//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
578pub mod module {
579 pub trait MyTrait {
580 pub fn func() {}
581 }
582 struct MyStruct {}
583 impl MyTrait for MyStruct {
584 pub fn func$0() {}
585 }
586}
587"#,
588 "foo::lib::module::impl::MyStruct::MyTrait::func",
589 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
590 MonikerKind::Export,
591 );
592 }
593
594 #[test]
595 fn moniker_for_field() {
596 check_moniker(
597 r#"
598//- /lib.rs crate:main deps:foo
599use foo::St;
600fn main() {
601 let x = St { a$0: 2 };
602}
603//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
604pub struct St {
605 pub a: i32,
606}
607"#,
608 "foo::lib::St::a",
609 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
610 MonikerKind::Import,
611 );
612 }
613
614 #[test]
615 fn local() {
616 check_local_moniker(
617 r#"
618//- /lib.rs crate:main deps:foo
619use foo::module::func;
620fn main() {
621 func();
622}
623//- /foo/lib.rs crate:foo@0.1.0,https://a.b/foo.git library
624pub mod module {
625 pub fn func() {
626 let x$0 = 2;
627 }
628}
629"#,
630 "foo::lib::module::func",
631 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
632 MonikerKind::Export,
633 );
634 }
635}