]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
793c22630bf8959039856f2ecd9ddd126565cc1a
[rust.git] / src / tools / rust-analyzer / crates / ide-completion / src / completions / attribute / derive.rs
1 //! Completion for derives
2 use hir::{HasAttrs, ScopeDef};
3 use ide_db::SymbolKind;
4 use itertools::Itertools;
5 use syntax::SmolStr;
6
7 use crate::{
8     context::{CompletionContext, ExistingDerives, PathCompletionCtx, Qualified},
9     item::CompletionItem,
10     Completions,
11 };
12
13 pub(crate) fn complete_derive_path(
14     acc: &mut Completions,
15     ctx: &CompletionContext<'_>,
16     path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
17     existing_derives: &ExistingDerives,
18 ) {
19     let core = ctx.famous_defs().core();
20
21     match qualified {
22         Qualified::With {
23             resolution: Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))),
24             super_chain_len,
25             ..
26         } => {
27             acc.add_super_keyword(ctx, *super_chain_len);
28
29             for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
30                 match def {
31                     ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac))
32                         if !existing_derives.contains(&mac) && mac.is_derive(ctx.db) =>
33                     {
34                         acc.add_macro(ctx, path_ctx, mac, name)
35                     }
36                     ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
37                         acc.add_module(ctx, path_ctx, m, name)
38                     }
39                     _ => (),
40                 }
41             }
42         }
43         Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
44         // only show modules in a fresh UseTree
45         Qualified::No => {
46             ctx.process_all_names(&mut |name, def| {
47                 let mac = match def {
48                     ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac))
49                         if !existing_derives.contains(&mac) && mac.is_derive(ctx.db) =>
50                     {
51                         mac
52                     }
53                     ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
54                         return acc.add_module(ctx, path_ctx, m, name);
55                     }
56                     _ => return,
57                 };
58
59                 match (core, mac.module(ctx.db).krate()) {
60                     // show derive dependencies for `core`/`std` derives
61                     (Some(core), mac_krate) if core == mac_krate => {}
62                     _ => return acc.add_macro(ctx, path_ctx, mac, name),
63                 };
64
65                 let name_ = name.to_smol_str();
66                 let find = DEFAULT_DERIVE_DEPENDENCIES
67                     .iter()
68                     .find(|derive_completion| derive_completion.label == name_);
69
70                 match find {
71                     Some(derive_completion) => {
72                         let mut components = vec![derive_completion.label];
73                         components.extend(derive_completion.dependencies.iter().filter(
74                             |&&dependency| {
75                                 !existing_derives
76                                     .iter()
77                                     .map(|it| it.name(ctx.db))
78                                     .any(|it| it.to_smol_str() == dependency)
79                             },
80                         ));
81                         let lookup = components.join(", ");
82                         let label = Itertools::intersperse(components.into_iter().rev(), ", ");
83
84                         let mut item = CompletionItem::new(
85                             SymbolKind::Derive,
86                             ctx.source_range(),
87                             SmolStr::from_iter(label),
88                         );
89                         if let Some(docs) = mac.docs(ctx.db) {
90                             item.documentation(docs);
91                         }
92                         item.lookup_by(lookup);
93                         item.add_to(acc);
94                     }
95                     None => acc.add_macro(ctx, path_ctx, mac, name),
96                 }
97             });
98             acc.add_nameref_keywords_with_colon(ctx);
99         }
100         Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
101     }
102 }
103
104 struct DeriveDependencies {
105     label: &'static str,
106     dependencies: &'static [&'static str],
107 }
108
109 /// Standard Rust derives that have dependencies
110 /// (the dependencies are needed so that the main derive don't break the compilation when added)
111 const DEFAULT_DERIVE_DEPENDENCIES: &[DeriveDependencies] = &[
112     DeriveDependencies { label: "Copy", dependencies: &["Clone"] },
113     DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] },
114     DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
115     DeriveDependencies { label: "PartialOrd", dependencies: &["PartialEq"] },
116 ];