]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/moniker.rs
Merge #11579
[rust.git] / 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::{db::DefDatabase, AsAssocItem, AssocItemContainer, Crate, Name, Semantics};
5 use ide_db::{
6     base_db::{CrateOrigin, FileId, FileLoader, FilePosition},
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, RangeInfo};
15
16 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
17 pub struct MonikerIdentifier {
18     crate_name: String,
19     path: Vec<Name>,
20 }
21
22 impl ToString for MonikerIdentifier {
23     fn to_string(&self) -> String {
24         match self {
25             MonikerIdentifier { path, crate_name } => {
26                 format!("{}::{}", crate_name, path.iter().map(|x| x.to_string()).join("::"))
27             }
28         }
29     }
30 }
31
32 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
33 pub enum MonikerKind {
34     Import,
35     Export,
36 }
37
38 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
39 pub struct MonikerResult {
40     pub identifier: MonikerIdentifier,
41     pub kind: MonikerKind,
42     pub package_information: PackageInformation,
43 }
44
45 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
46 pub struct PackageInformation {
47     pub name: String,
48     pub repo: String,
49     pub version: String,
50 }
51
52 pub(crate) fn crate_for_file(db: &RootDatabase, file_id: FileId) -> Option<Crate> {
53     for &krate in db.relevant_crates(file_id).iter() {
54         let crate_def_map = db.crate_def_map(krate);
55         for (_, data) in crate_def_map.modules() {
56             if data.origin.file_id() == Some(file_id) {
57                 return Some(krate.into());
58             }
59         }
60     }
61     None
62 }
63
64 pub(crate) fn moniker(
65     db: &RootDatabase,
66     FilePosition { file_id, offset }: FilePosition,
67 ) -> Option<RangeInfo<Vec<MonikerResult>>> {
68     let sema = &Semantics::new(db);
69     let file = sema.parse(file_id).syntax().clone();
70     let current_crate = crate_for_file(db, file_id)?;
71     let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
72         IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | COMMENT => 2,
73         kind if kind.is_trivia() => 0,
74         _ => 1,
75     })?;
76     if let Some(doc_comment) = token_as_doc_comment(&original_token) {
77         return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, _| {
78             let m = def_to_moniker(db, def, current_crate)?;
79             Some(RangeInfo::new(original_token.text_range(), vec![m]))
80         });
81     }
82     let navs = sema
83         .descend_into_macros(original_token.clone())
84         .into_iter()
85         .filter_map(|token| {
86             IdentClass::classify_token(sema, &token).map(IdentClass::definitions).map(|it| {
87                 it.into_iter().flat_map(|def| def_to_moniker(sema.db, def, current_crate))
88             })
89         })
90         .flatten()
91         .unique()
92         .collect::<Vec<_>>();
93     Some(RangeInfo::new(original_token.text_range(), navs))
94 }
95
96 pub(crate) fn def_to_moniker(
97     db: &RootDatabase,
98     def: Definition,
99     from_crate: Crate,
100 ) -> Option<MonikerResult> {
101     if matches!(def, Definition::GenericParam(_) | Definition::SelfType(_) | Definition::Local(_)) {
102         return None;
103     }
104     let module = def.module(db)?;
105     let krate = module.krate();
106     let mut path = vec![];
107     path.extend(module.path_to_root(db).into_iter().filter_map(|x| x.name(db)));
108
109     // Handle associated items within a trait
110     if let Some(assoc) = def.as_assoc_item(db) {
111         let container = assoc.container(db);
112         match container {
113             AssocItemContainer::Trait(trait_) => {
114                 // Because different traits can have functions with the same name,
115                 // we have to include the trait name as part of the moniker for uniqueness.
116                 path.push(trait_.name(db));
117             }
118             AssocItemContainer::Impl(impl_) => {
119                 // Because a struct can implement multiple traits, for implementations
120                 // we add both the struct name and the trait name to the path
121                 if let Some(adt) = impl_.self_ty(db).as_adt() {
122                     path.push(adt.name(db));
123                 }
124
125                 if let Some(trait_) = impl_.trait_(db) {
126                     path.push(trait_.name(db));
127                 }
128             }
129         }
130     }
131
132     if let Definition::Field(it) = def {
133         path.push(it.parent_def(db).name(db));
134     }
135
136     path.push(def.name(db)?);
137     Some(MonikerResult {
138         identifier: MonikerIdentifier {
139             crate_name: krate.display_name(db)?.crate_name().to_string(),
140             path,
141         },
142         kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
143         package_information: {
144             let name = krate.display_name(db)?.to_string();
145             let (repo, version) = match krate.origin(db) {
146                 CrateOrigin::CratesIo { repo } => (repo?, krate.version(db)?),
147                 CrateOrigin::Lang => (
148                     "https://github.com/rust-lang/rust/".to_string(),
149                     "compiler_version".to_string(),
150                 ),
151                 CrateOrigin::Unknown => return None,
152             };
153             PackageInformation { name, repo, version }
154         },
155     })
156 }
157
158 #[cfg(test)]
159 mod tests {
160     use crate::fixture;
161
162     use super::MonikerKind;
163
164     #[track_caller]
165     fn no_moniker(ra_fixture: &str) {
166         let (analysis, position) = fixture::position(ra_fixture);
167         if let Some(x) = analysis.moniker(position).unwrap() {
168             assert_eq!(x.info.len(), 0, "Moniker founded but no moniker expected: {:?}", x);
169         }
170     }
171
172     #[track_caller]
173     fn check_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) {
174         let (analysis, position) = fixture::position(ra_fixture);
175         let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
176         assert_eq!(x.len(), 1);
177         let x = x.into_iter().next().unwrap();
178         assert_eq!(identifier, x.identifier.to_string());
179         assert_eq!(package, format!("{:?}", x.package_information));
180         assert_eq!(kind, x.kind);
181     }
182
183     #[test]
184     fn basic() {
185         check_moniker(
186             r#"
187 //- /lib.rs crate:main deps:foo
188 use foo::module::func;
189 fn main() {
190     func$0();
191 }
192 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
193 pub mod module {
194     pub fn func() {}
195 }
196 "#,
197             "foo::module::func",
198             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
199             MonikerKind::Import,
200         );
201         check_moniker(
202             r#"
203 //- /lib.rs crate:main deps:foo
204 use foo::module::func;
205 fn main() {
206     func();
207 }
208 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
209 pub mod module {
210     pub fn func$0() {}
211 }
212 "#,
213             "foo::module::func",
214             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
215             MonikerKind::Export,
216         );
217     }
218
219     #[test]
220     fn moniker_for_trait() {
221         check_moniker(
222             r#"
223 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
224 pub mod module {
225     pub trait MyTrait {
226         pub fn func$0() {}
227     }
228 }
229 "#,
230             "foo::module::MyTrait::func",
231             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
232             MonikerKind::Export,
233         );
234     }
235
236     #[test]
237     fn moniker_for_trait_constant() {
238         check_moniker(
239             r#"
240 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
241 pub mod module {
242     pub trait MyTrait {
243         const MY_CONST$0: u8;
244     }
245 }
246 "#,
247             "foo::module::MyTrait::MY_CONST",
248             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
249             MonikerKind::Export,
250         );
251     }
252
253     #[test]
254     fn moniker_for_trait_type() {
255         check_moniker(
256             r#"
257 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
258 pub mod module {
259     pub trait MyTrait {
260         type MyType$0;
261     }
262 }
263 "#,
264             "foo::module::MyTrait::MyType",
265             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
266             MonikerKind::Export,
267         );
268     }
269
270     #[test]
271     fn moniker_for_trait_impl_function() {
272         check_moniker(
273             r#"
274 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
275 pub mod module {
276     pub trait MyTrait {
277         pub fn func() {}
278     }
279
280     struct MyStruct {}
281
282     impl MyTrait for MyStruct {
283         pub fn func$0() {}
284     }
285 }
286 "#,
287             "foo::module::MyStruct::MyTrait::func",
288             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
289             MonikerKind::Export,
290         );
291     }
292
293     #[test]
294     fn moniker_for_field() {
295         check_moniker(
296             r#"
297 //- /lib.rs crate:main deps:foo
298 use foo::St;
299 fn main() {
300     let x = St { a$0: 2 };
301 }
302 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
303 pub struct St {
304     pub a: i32,
305 }
306 "#,
307             "foo::St::a",
308             r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#,
309             MonikerKind::Import,
310         );
311     }
312
313     #[test]
314     fn no_moniker_for_local() {
315         no_moniker(
316             r#"
317 //- /lib.rs crate:main deps:foo
318 use foo::module::func;
319 fn main() {
320     func();
321 }
322 //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
323 pub mod module {
324     pub fn func() {
325         let x$0 = 2;
326     }
327 }
328 "#,
329         );
330     }
331 }