]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/ide/src/moniker.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / ide / src / moniker.rs
1 //! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports)
2 //! for LSIF and LSP.
3
4 use hir::{AsAssocItem, AssocItemContainer, Crate, Name, Semantics};
5 use ide_db::{
6 base_db::{CrateOrigin, FilePosition, LangCrateOrigin},
7 defs::{Definition, IdentClass},
8 helpers::pick_best_token,
9 RootDatabase,
10 };
11 use itertools::Itertools;
12 use syntax::{AstNode, SyntaxKind::*, T};
13
14 use crate::{doc_links::token_as_doc_comment, parent_module::crates_for, RangeInfo};
15
16 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
17 pub enum MonikerDescriptorKind {
18 Namespace,
19 Type,
20 Term,
21 Method,
22 TypeParameter,
23 Parameter,
24 Macro,
25 Meta,
26 }
27
28 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
29 pub struct MonikerDescriptor {
30 pub name: Name,
31 pub desc: MonikerDescriptorKind,
32 }
33
34 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
35 pub struct MonikerIdentifier {
36 pub crate_name: String,
37 pub description: Vec<MonikerDescriptor>,
38 }
39
40 impl ToString for MonikerIdentifier {
41 fn to_string(&self) -> String {
42 match self {
43 MonikerIdentifier { description, crate_name } => {
44 format!(
45 "{}::{}",
46 crate_name,
47 description.iter().map(|x| x.name.to_string()).join("::")
48 )
49 }
50 }
51 }
52 }
53
54 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
55 pub enum MonikerKind {
56 Import,
57 Export,
58 }
59
60 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
61 pub struct MonikerResult {
62 pub identifier: MonikerIdentifier,
63 pub kind: MonikerKind,
64 pub package_information: PackageInformation,
65 }
66
67 impl MonikerResult {
68 pub fn from_def(db: &RootDatabase, def: Definition, from_crate: Crate) -> Option<Self> {
69 def_to_moniker(db, def, from_crate)
70 }
71 }
72
73 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
74 pub struct PackageInformation {
75 pub name: String,
76 pub repo: Option<String>,
77 pub version: Option<String>,
78 }
79
80 pub(crate) fn moniker(
81 db: &RootDatabase,
82 FilePosition { file_id, offset }: FilePosition,
83 ) -> Option<RangeInfo<Vec<MonikerResult>>> {
84 let sema = &Semantics::new(db);
85 let file = sema.parse(file_id).syntax().clone();
86 let current_crate: hir::Crate = crates_for(db, file_id).pop()?.into();
87 let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
88 IDENT
89 | INT_NUMBER
90 | LIFETIME_IDENT
91 | T![self]
92 | T![super]
93 | T![crate]
94 | T![Self]
95 | COMMENT => 2,
96 kind if kind.is_trivia() => 0,
97 _ => 1,
98 })?;
99 if let Some(doc_comment) = token_as_doc_comment(&original_token) {
100 return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, _| {
101 let m = def_to_moniker(db, def, current_crate)?;
102 Some(RangeInfo::new(original_token.text_range(), vec![m]))
103 });
104 }
105 let navs = sema
106 .descend_into_macros(original_token.clone())
107 .into_iter()
108 .filter_map(|token| {
109 IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| {
110 it.into_iter().flat_map(|def| def_to_moniker(sema.db, def, current_crate))
111 })
112 })
113 .flatten()
114 .unique()
115 .collect::<Vec<_>>();
116 Some(RangeInfo::new(original_token.text_range(), navs))
117 }
118
119 pub(crate) fn def_to_moniker(
120 db: &RootDatabase,
121 def: Definition,
122 from_crate: Crate,
123 ) -> Option<MonikerResult> {
124 if matches!(
125 def,
126 Definition::GenericParam(_)
127 | Definition::Label(_)
128 | Definition::DeriveHelper(_)
129 | Definition::BuiltinAttr(_)
130 | Definition::ToolModule(_)
131 ) {
132 return None;
133 }
134
135 let module = def.module(db)?;
136 let krate = module.krate();
137 let mut description = vec![];
138 description.extend(module.path_to_root(db).into_iter().filter_map(|x| {
139 Some(MonikerDescriptor { name: x.name(db)?, desc: MonikerDescriptorKind::Namespace })
140 }));
141
142 // Handle associated items within a trait
143 if let Some(assoc) = def.as_assoc_item(db) {
144 let container = assoc.container(db);
145 match container {
146 AssocItemContainer::Trait(trait_) => {
147 // Because different traits can have functions with the same name,
148 // we have to include the trait name as part of the moniker for uniqueness.
149 description.push(MonikerDescriptor {
150 name: trait_.name(db),
151 desc: MonikerDescriptorKind::Type,
152 });
153 }
154 AssocItemContainer::Impl(impl_) => {
155 // Because a struct can implement multiple traits, for implementations
156 // we add both the struct name and the trait name to the path
157 if let Some(adt) = impl_.self_ty(db).as_adt() {
158 description.push(MonikerDescriptor {
159 name: adt.name(db),
160 desc: MonikerDescriptorKind::Type,
161 });
162 }
163
164 if let Some(trait_) = impl_.trait_(db) {
165 description.push(MonikerDescriptor {
166 name: trait_.name(db),
167 desc: MonikerDescriptorKind::Type,
168 });
169 }
170 }
171 }
172 }
173
174 if let Definition::Field(it) = def {
175 description.push(MonikerDescriptor {
176 name: it.parent_def(db).name(db),
177 desc: MonikerDescriptorKind::Type,
178 });
179 }
180
181 let name_desc = match def {
182 // These are handled by top-level guard (for performance).
183 Definition::GenericParam(_)
184 | Definition::Label(_)
185 | Definition::DeriveHelper(_)
186 | Definition::BuiltinAttr(_)
187 | Definition::ToolModule(_) => return None,
188
189 Definition::Local(local) => {
190 if !local.is_param(db) {
191 return None;
192 }
193
194 MonikerDescriptor { name: local.name(db), desc: MonikerDescriptorKind::Parameter }
195 }
196 Definition::Macro(m) => {
197 MonikerDescriptor { name: m.name(db), desc: MonikerDescriptorKind::Macro }
198 }
199 Definition::Function(f) => {
200 MonikerDescriptor { name: f.name(db), desc: MonikerDescriptorKind::Method }
201 }
202 Definition::Variant(v) => {
203 MonikerDescriptor { name: v.name(db), desc: MonikerDescriptorKind::Type }
204 }
205 Definition::Const(c) => {
206 MonikerDescriptor { name: c.name(db)?, desc: MonikerDescriptorKind::Term }
207 }
208 Definition::Trait(trait_) => {
209 MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type }
210 }
211 Definition::TraitAlias(ta) => {
212 MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::Type }
213 }
214 Definition::TypeAlias(ta) => {
215 MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter }
216 }
217 Definition::Module(m) => {
218 MonikerDescriptor { name: m.name(db)?, desc: MonikerDescriptorKind::Namespace }
219 }
220 Definition::BuiltinType(b) => {
221 MonikerDescriptor { name: b.name(), desc: MonikerDescriptorKind::Type }
222 }
223 Definition::SelfType(imp) => MonikerDescriptor {
224 name: imp.self_ty(db).as_adt()?.name(db),
225 desc: MonikerDescriptorKind::Type,
226 },
227 Definition::Field(it) => {
228 MonikerDescriptor { name: it.name(db), desc: MonikerDescriptorKind::Term }
229 }
230 Definition::Adt(adt) => {
231 MonikerDescriptor { name: adt.name(db), desc: MonikerDescriptorKind::Type }
232 }
233 Definition::Static(s) => {
234 MonikerDescriptor { name: s.name(db), desc: MonikerDescriptorKind::Meta }
235 }
236 };
237
238 description.push(name_desc);
239
240 Some(MonikerResult {
241 identifier: MonikerIdentifier {
242 crate_name: krate.display_name(db)?.crate_name().to_string(),
243 description,
244 },
245 kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
246 package_information: {
247 let (name, repo, version) = match krate.origin(db) {
248 CrateOrigin::CratesIo { repo, name } => (
249 name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()),
250 repo,
251 krate.version(db),
252 ),
253 CrateOrigin::Lang(lang) => (
254 krate.display_name(db)?.canonical_name().to_string(),
255 Some("https://github.com/rust-lang/rust/".to_string()),
256 Some(match lang {
257 LangCrateOrigin::Other => {
258 "https://github.com/rust-lang/rust/library/".into()
259 }
260 lang => format!("https://github.com/rust-lang/rust/library/{lang}",),
261 }),
262 ),
263 };
264 PackageInformation { name, repo, version }
265 },
266 })
267 }
268
269 #[cfg(test)]
270 mod tests {
271 use crate::fixture;
272
273 use super::MonikerKind;
274
275 #[track_caller]
276 fn no_moniker(ra_fixture: &str) {
277 let (analysis, position) = fixture::position(ra_fixture);
278 if let Some(x) = analysis.moniker(position).unwrap() {
279 assert_eq!(x.info.len(), 0, "Moniker founded but no moniker expected: {x:?}");
280 }
281 }
282
283 #[track_caller]
284 fn check_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) {
285 let (analysis, position) = fixture::position(ra_fixture);
286 let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
287 assert_eq!(x.len(), 1);
288 let x = x.into_iter().next().unwrap();
289 assert_eq!(identifier, x.identifier.to_string());
290 assert_eq!(package, format!("{:?}", x.package_information));
291 assert_eq!(kind, x.kind);
292 }
293
294 #[test]
295 fn basic() {
296 check_moniker(
297 r#"
298 //- /lib.rs crate:main deps:foo
299 use foo::module::func;
300 fn main() {
301 func$0();
302 }
303 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
304 pub mod module {
305 pub fn func() {}
306 }
307 "#,
308 "foo::module::func",
309 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
310 MonikerKind::Import,
311 );
312 check_moniker(
313 r#"
314 //- /lib.rs crate:main deps:foo
315 use foo::module::func;
316 fn main() {
317 func();
318 }
319 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
320 pub mod module {
321 pub fn func$0() {}
322 }
323 "#,
324 "foo::module::func",
325 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
326 MonikerKind::Export,
327 );
328 }
329
330 #[test]
331 fn moniker_for_trait() {
332 check_moniker(
333 r#"
334 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
335 pub mod module {
336 pub trait MyTrait {
337 pub fn func$0() {}
338 }
339 }
340 "#,
341 "foo::module::MyTrait::func",
342 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
343 MonikerKind::Export,
344 );
345 }
346
347 #[test]
348 fn moniker_for_trait_constant() {
349 check_moniker(
350 r#"
351 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
352 pub mod module {
353 pub trait MyTrait {
354 const MY_CONST$0: u8;
355 }
356 }
357 "#,
358 "foo::module::MyTrait::MY_CONST",
359 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
360 MonikerKind::Export,
361 );
362 }
363
364 #[test]
365 fn moniker_for_trait_type() {
366 check_moniker(
367 r#"
368 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
369 pub mod module {
370 pub trait MyTrait {
371 type MyType$0;
372 }
373 }
374 "#,
375 "foo::module::MyTrait::MyType",
376 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
377 MonikerKind::Export,
378 );
379 }
380
381 #[test]
382 fn moniker_for_trait_impl_function() {
383 check_moniker(
384 r#"
385 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
386 pub mod module {
387 pub trait MyTrait {
388 pub fn func() {}
389 }
390
391 struct MyStruct {}
392
393 impl MyTrait for MyStruct {
394 pub fn func$0() {}
395 }
396 }
397 "#,
398 "foo::module::MyStruct::MyTrait::func",
399 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
400 MonikerKind::Export,
401 );
402 }
403
404 #[test]
405 fn moniker_for_field() {
406 check_moniker(
407 r#"
408 //- /lib.rs crate:main deps:foo
409 use foo::St;
410 fn main() {
411 let x = St { a$0: 2 };
412 }
413 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
414 pub struct St {
415 pub a: i32,
416 }
417 "#,
418 "foo::St::a",
419 r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#,
420 MonikerKind::Import,
421 );
422 }
423
424 #[test]
425 fn no_moniker_for_local() {
426 no_moniker(
427 r#"
428 //- /lib.rs crate:main deps:foo
429 use foo::module::func;
430 fn main() {
431 func();
432 }
433 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
434 pub mod module {
435 pub fn func() {
436 let x$0 = 2;
437 }
438 }
439 "#,
440 );
441 }
442 }