1use core::fmt;
5
6use hir::{Adt, AsAssocItem, Crate, HirDisplay, MacroKind, Semantics};
7use ide_db::{
8 FilePosition, RootDatabase,
9 base_db::{CrateOrigin, LangCrateOrigin},
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 => 2,
158 kind if kind.is_trivia() => 0,
159 _ => 1,
160 })?;
161 if let Some(doc_comment) = token_as_doc_comment(&original_token) {
162 return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, _| {
163 let m = def_to_moniker(db, def, current_crate)?;
164 Some(RangeInfo::new(original_token.text_range(), vec![m]))
165 });
166 }
167 let navs = sema
168 .descend_into_macros_exact(original_token.clone())
169 .into_iter()
170 .filter_map(|token| {
171 IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| {
172 it.into_iter().flat_map(|def| def_to_moniker(sema.db, def, current_crate))
173 })
174 })
175 .flatten()
176 .unique()
177 .collect::<Vec<_>>();
178 Some(RangeInfo::new(original_token.text_range(), navs))
179}
180
181pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformationKind {
182 use SymbolInformationKind::*;
183
184 match def {
185 Definition::Macro(it) => match it.kind(db) {
186 MacroKind::Derive
187 | MacroKind::DeriveBuiltIn
188 | MacroKind::AttrBuiltIn
189 | MacroKind::Attr => Attribute,
190 MacroKind::Declarative | MacroKind::DeclarativeBuiltIn | MacroKind::ProcMacro => Macro,
191 },
192 Definition::Field(..) | Definition::TupleField(..) => Field,
193 Definition::Module(..) | Definition::Crate(..) => Module,
194 Definition::Function(it) => {
195 if it.as_assoc_item(db).is_some() {
196 if it.has_self_param(db) {
197 if it.has_body(db) { Method } else { TraitMethod }
198 } else {
199 StaticMethod
200 }
201 } else {
202 Function
203 }
204 }
205 Definition::Adt(Adt::Struct(..)) => Struct,
206 Definition::Adt(Adt::Union(..)) => Union,
207 Definition::Adt(Adt::Enum(..)) => Enum,
208 Definition::Variant(..) => EnumMember,
209 Definition::Const(..) => Constant,
210 Definition::Static(..) => StaticVariable,
211 Definition::Trait(..) => Trait,
212 Definition::TraitAlias(..) => Trait,
213 Definition::TypeAlias(it) => {
214 if it.as_assoc_item(db).is_some() {
215 AssociatedType
216 } else {
217 TypeAlias
218 }
219 }
220 Definition::BuiltinType(..) => Type,
221 Definition::BuiltinLifetime(_) => TypeParameter,
222 Definition::SelfType(..) => TypeAlias,
223 Definition::GenericParam(..) => TypeParameter,
224 Definition::Local(it) => {
225 if it.is_self(db) {
226 SelfParameter
227 } else if it.is_param(db) {
228 Parameter
229 } else {
230 Variable
231 }
232 }
233 Definition::Label(..) | Definition::InlineAsmOperand(_) => Variable, Definition::DeriveHelper(..) => Attribute,
235 Definition::BuiltinAttr(..) => Attribute,
236 Definition::ToolModule(..) => Module,
237 Definition::ExternCrateDecl(..) => Module,
238 Definition::InlineAsmRegOrRegClass(..) => Module,
239 }
240}
241
242pub(crate) fn def_to_moniker(
253 db: &RootDatabase,
254 definition: Definition,
255 from_crate: Crate,
256) -> Option<MonikerResult> {
257 match definition {
258 Definition::Local(_) | Definition::Label(_) | Definition::GenericParam(_) => {
259 return Some(MonikerResult::Local {
260 enclosing_moniker: enclosing_def_to_moniker(db, definition, from_crate),
261 });
262 }
263 _ => {}
264 }
265 Some(MonikerResult::Moniker(def_to_non_local_moniker(db, definition, from_crate)?))
266}
267
268fn enclosing_def_to_moniker(
269 db: &RootDatabase,
270 mut def: Definition,
271 from_crate: Crate,
272) -> Option<Moniker> {
273 loop {
274 let enclosing_def = def.enclosing_definition(db)?;
275 if let Some(enclosing_moniker) = def_to_non_local_moniker(db, enclosing_def, from_crate) {
276 return Some(enclosing_moniker);
277 }
278 def = enclosing_def;
279 }
280}
281
282fn def_to_non_local_moniker(
283 db: &RootDatabase,
284 definition: Definition,
285 from_crate: Crate,
286) -> Option<Moniker> {
287 let module = match definition {
288 Definition::Module(module) if module.is_crate_root() => module,
289 _ => definition.module(db)?,
290 };
291 let krate = module.krate();
292 let edition = krate.edition(db);
293
294 let mut reverse_description = vec![];
296 let mut def = definition;
297 loop {
298 match def {
299 Definition::SelfType(impl_) => {
300 if let Some(trait_ref) = impl_.trait_ref(db) {
301 reverse_description.push(MonikerDescriptor {
303 name: display(db, module, trait_ref),
304 desc: MonikerDescriptorKind::TypeParameter,
305 });
306 }
307 reverse_description.push(MonikerDescriptor {
309 name: display(db, module, impl_.self_ty(db)),
310 desc: MonikerDescriptorKind::TypeParameter,
311 });
312 reverse_description.push(MonikerDescriptor {
313 name: "impl".to_owned(),
314 desc: MonikerDescriptorKind::Type,
315 });
316 }
317 _ => {
318 if let Some(name) = def.name(db) {
319 reverse_description.push(MonikerDescriptor {
320 name: name.display(db, edition).to_string(),
321 desc: def_to_kind(db, def).into(),
322 });
323 } else {
324 match def {
325 Definition::Module(module) if module.is_crate_root() => {
326 if reverse_description.is_empty() {
329 reverse_description.push(MonikerDescriptor {
330 name: "crate".to_owned(),
331 desc: MonikerDescriptorKind::Namespace,
332 });
333 }
334 }
335 _ => {
336 tracing::error!(?def, "Encountered enclosing definition with no name");
337 }
338 }
339 }
340 }
341 }
342 let Some(next_def) = def.enclosing_definition(db) else {
343 break;
344 };
345 def = next_def;
346 }
347 if reverse_description.is_empty() {
348 return None;
349 }
350 reverse_description.reverse();
351 let description = reverse_description;
352
353 Some(Moniker {
354 identifier: MonikerIdentifier {
355 crate_name: krate.display_name(db)?.crate_name().to_string(),
356 description,
357 },
358 kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
359 package_information: {
360 let (name, repo, version) = match krate.origin(db) {
361 CrateOrigin::Library { repo, name } => (name, repo, krate.version(db)),
362 CrateOrigin::Local { repo, name } => (
363 name.unwrap_or(krate.display_name(db)?.canonical_name().to_owned()),
364 repo,
365 krate.version(db),
366 ),
367 CrateOrigin::Rustc { name } => (
368 name.clone(),
369 Some("https://github.com/rust-lang/rust/".to_owned()),
370 Some(format!("https://github.com/rust-lang/rust/compiler/{name}",)),
371 ),
372 CrateOrigin::Lang(lang) => (
373 krate.display_name(db)?.canonical_name().to_owned(),
374 Some("https://github.com/rust-lang/rust/".to_owned()),
375 Some(match lang {
376 LangCrateOrigin::Other => {
377 "https://github.com/rust-lang/rust/library/".into()
378 }
379 lang => format!("https://github.com/rust-lang/rust/library/{lang}",),
380 }),
381 ),
382 };
383 PackageInformation { name: name.as_str().to_owned(), repo, version }
384 },
385 })
386}
387
388fn display<T: HirDisplay>(db: &RootDatabase, module: hir::Module, it: T) -> String {
389 match it.display_source_code(db, module.into(), true) {
390 Ok(result) => result,
391 Err(_) => {
393 let fallback_result = it.display(db, module.krate().to_display_target(db)).to_string();
394 tracing::error!(
395 display = %fallback_result, "`display_source_code` failed; falling back to using display"
396 );
397 fallback_result
398 }
399 }
400}
401
402#[cfg(test)]
403mod tests {
404 use crate::{MonikerResult, fixture};
405
406 use super::MonikerKind;
407
408 #[allow(dead_code)]
409 #[track_caller]
410 fn no_moniker(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
411 let (analysis, position) = fixture::position(ra_fixture);
412 if let Some(x) = analysis.moniker(position).unwrap() {
413 assert_eq!(x.info.len(), 0, "Moniker found but no moniker expected: {x:?}");
414 }
415 }
416
417 #[track_caller]
418 fn check_local_moniker(
419 #[rust_analyzer::rust_fixture] ra_fixture: &str,
420 identifier: &str,
421 package: &str,
422 kind: MonikerKind,
423 ) {
424 let (analysis, position) = fixture::position(ra_fixture);
425 let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
426 assert_eq!(x.len(), 1);
427 match x.into_iter().next().unwrap() {
428 MonikerResult::Local { enclosing_moniker: Some(x) } => {
429 assert_eq!(identifier, x.identifier.to_string());
430 assert_eq!(package, format!("{:?}", x.package_information));
431 assert_eq!(kind, x.kind);
432 }
433 MonikerResult::Local { enclosing_moniker: None } => {
434 panic!("Unexpected local with no enclosing moniker");
435 }
436 MonikerResult::Moniker(_) => {
437 panic!("Unexpected non-local moniker");
438 }
439 }
440 }
441
442 #[track_caller]
443 fn check_moniker(
444 #[rust_analyzer::rust_fixture] ra_fixture: &str,
445 identifier: &str,
446 package: &str,
447 kind: MonikerKind,
448 ) {
449 let (analysis, position) = fixture::position(ra_fixture);
450 let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
451 assert_eq!(x.len(), 1);
452 match x.into_iter().next().unwrap() {
453 MonikerResult::Local { enclosing_moniker } => {
454 panic!("Unexpected local enclosed in {enclosing_moniker:?}");
455 }
456 MonikerResult::Moniker(x) => {
457 assert_eq!(identifier, x.identifier.to_string());
458 assert_eq!(package, format!("{:?}", x.package_information));
459 assert_eq!(kind, x.kind);
460 }
461 }
462 }
463
464 #[test]
465 fn basic() {
466 check_moniker(
467 r#"
468//- /lib.rs crate:main deps:foo
469use foo::module::func;
470fn main() {
471 func$0();
472}
473//- /foo/lib.rs crate:[email protected],https://a.b/foo.git library
474pub mod module {
475 pub fn func() {}
476}
477"#,
478 "foo::module::func",
479 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
480 MonikerKind::Import,
481 );
482 check_moniker(
483 r#"
484//- /lib.rs crate:main deps:foo
485use foo::module::func;
486fn main() {
487 func();
488}
489//- /foo/lib.rs crate:[email protected],https://a.b/foo.git library
490pub mod module {
491 pub fn func$0() {}
492}
493"#,
494 "foo::module::func",
495 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
496 MonikerKind::Export,
497 );
498 }
499
500 #[test]
501 fn moniker_for_trait() {
502 check_moniker(
503 r#"
504//- /foo/lib.rs crate:[email protected],https://a.b/foo.git library
505pub mod module {
506 pub trait MyTrait {
507 pub fn func$0() {}
508 }
509}
510"#,
511 "foo::module::MyTrait::func",
512 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
513 MonikerKind::Export,
514 );
515 }
516
517 #[test]
518 fn moniker_for_trait_constant() {
519 check_moniker(
520 r#"
521//- /foo/lib.rs crate:[email protected],https://a.b/foo.git library
522pub mod module {
523 pub trait MyTrait {
524 const MY_CONST$0: u8;
525 }
526}
527"#,
528 "foo::module::MyTrait::MY_CONST",
529 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
530 MonikerKind::Export,
531 );
532 }
533
534 #[test]
535 fn moniker_for_trait_type() {
536 check_moniker(
537 r#"
538//- /foo/lib.rs crate:[email protected],https://a.b/foo.git library
539pub mod module {
540 pub trait MyTrait {
541 type MyType$0;
542 }
543}
544"#,
545 "foo::module::MyTrait::MyType",
546 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
547 MonikerKind::Export,
548 );
549 }
550
551 #[test]
552 fn moniker_for_trait_impl_function() {
553 check_moniker(
554 r#"
555//- /foo/lib.rs crate:[email protected],https://a.b/foo.git library
556pub mod module {
557 pub trait MyTrait {
558 pub fn func() {}
559 }
560 struct MyStruct {}
561 impl MyTrait for MyStruct {
562 pub fn func$0() {}
563 }
564}
565"#,
566 "foo::module::impl::MyStruct::MyTrait::func",
567 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
568 MonikerKind::Export,
569 );
570 }
571
572 #[test]
573 fn moniker_for_field() {
574 check_moniker(
575 r#"
576//- /lib.rs crate:main deps:foo
577use foo::St;
578fn main() {
579 let x = St { a$0: 2 };
580}
581//- /foo/lib.rs crate:[email protected],https://a.b/foo.git library
582pub struct St {
583 pub a: i32,
584}
585"#,
586 "foo::St::a",
587 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
588 MonikerKind::Import,
589 );
590 }
591
592 #[test]
593 fn local() {
594 check_local_moniker(
595 r#"
596//- /lib.rs crate:main deps:foo
597use foo::module::func;
598fn main() {
599 func();
600}
601//- /foo/lib.rs crate:[email protected],https://a.b/foo.git library
602pub mod module {
603 pub fn func() {
604 let x$0 = 2;
605 }
606}
607"#,
608 "foo::module::func",
609 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
610 MonikerKind::Export,
611 );
612 }
613}