]> git.lizzy.rs Git - rust.git/blob - crates/ra_hir/src/lang_item.rs
Parse correct AttrInput
[rust.git] / crates / ra_hir / src / lang_item.rs
1 use rustc_hash::FxHashMap;
2 use std::sync::Arc;
3
4 use ra_syntax::{ast::AttrsOwner, SmolStr};
5
6 use crate::{
7     db::{AstDatabase, DefDatabase, HirDatabase},
8     Adt, Crate, Enum, Function, HasSource, ImplBlock, Module, ModuleDef, Static, Struct, Trait,
9 };
10
11 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12 pub enum LangItemTarget {
13     Enum(Enum),
14     Function(Function),
15     ImplBlock(ImplBlock),
16     Static(Static),
17     Struct(Struct),
18     Trait(Trait),
19 }
20
21 impl LangItemTarget {
22     pub(crate) fn krate(&self, db: &impl HirDatabase) -> Option<Crate> {
23         match self {
24             LangItemTarget::Enum(e) => e.module(db).krate(db),
25             LangItemTarget::Function(f) => f.module(db).krate(db),
26             LangItemTarget::ImplBlock(i) => i.module().krate(db),
27             LangItemTarget::Static(s) => s.module(db).krate(db),
28             LangItemTarget::Struct(s) => s.module(db).krate(db),
29             LangItemTarget::Trait(t) => t.module(db).krate(db),
30         }
31     }
32 }
33
34 #[derive(Default, Debug, Clone, PartialEq, Eq)]
35 pub struct LangItems {
36     items: FxHashMap<SmolStr, LangItemTarget>,
37 }
38
39 impl LangItems {
40     pub fn target<'a>(&'a self, item: &str) -> Option<&'a LangItemTarget> {
41         self.items.get(item)
42     }
43
44     /// Salsa query. This will look for lang items in a specific crate.
45     pub(crate) fn crate_lang_items_query(
46         db: &(impl DefDatabase + AstDatabase),
47         krate: Crate,
48     ) -> Arc<LangItems> {
49         let mut lang_items = LangItems::default();
50
51         if let Some(module) = krate.root_module(db) {
52             lang_items.collect_lang_items_recursive(db, module);
53         }
54
55         Arc::new(lang_items)
56     }
57
58     pub(crate) fn module_lang_items_query(
59         db: &(impl DefDatabase + AstDatabase),
60         module: Module,
61     ) -> Option<Arc<LangItems>> {
62         let mut lang_items = LangItems::default();
63         lang_items.collect_lang_items(db, module);
64         if lang_items.items.is_empty() {
65             None
66         } else {
67             Some(Arc::new(lang_items))
68         }
69     }
70
71     /// Salsa query. Look for a lang item, starting from the specified crate and recursively
72     /// traversing its dependencies.
73     pub(crate) fn lang_item_query(
74         db: &impl DefDatabase,
75         start_crate: Crate,
76         item: SmolStr,
77     ) -> Option<LangItemTarget> {
78         let lang_items = db.crate_lang_items(start_crate);
79         let start_crate_target = lang_items.items.get(&item);
80         if let Some(target) = start_crate_target {
81             Some(*target)
82         } else {
83             for dep in start_crate.dependencies(db) {
84                 let dep_crate = dep.krate;
85                 let dep_target = db.lang_item(dep_crate, item.clone());
86                 if dep_target.is_some() {
87                     return dep_target;
88                 }
89             }
90             None
91         }
92     }
93
94     fn collect_lang_items(&mut self, db: &(impl DefDatabase + AstDatabase), module: Module) {
95         // Look for impl targets
96         for impl_block in module.impl_blocks(db) {
97             let src = impl_block.source(db);
98             if let Some(lang_item_name) = lang_item_name(&src.ast) {
99                 self.items
100                     .entry(lang_item_name)
101                     .or_insert_with(|| LangItemTarget::ImplBlock(impl_block));
102             }
103         }
104
105         for def in module.declarations(db) {
106             match def {
107                 ModuleDef::Trait(trait_) => {
108                     self.collect_lang_item(db, trait_, LangItemTarget::Trait)
109                 }
110                 ModuleDef::Adt(Adt::Enum(e)) => self.collect_lang_item(db, e, LangItemTarget::Enum),
111                 ModuleDef::Adt(Adt::Struct(s)) => {
112                     self.collect_lang_item(db, s, LangItemTarget::Struct)
113                 }
114                 ModuleDef::Function(f) => self.collect_lang_item(db, f, LangItemTarget::Function),
115                 ModuleDef::Static(s) => self.collect_lang_item(db, s, LangItemTarget::Static),
116                 _ => {}
117             }
118         }
119     }
120
121     fn collect_lang_items_recursive(
122         &mut self,
123         db: &(impl DefDatabase + AstDatabase),
124         module: Module,
125     ) {
126         if let Some(module_lang_items) = db.module_lang_items(module) {
127             self.items.extend(module_lang_items.items.iter().map(|(k, v)| (k.clone(), *v)))
128         }
129
130         // Look for lang items in the children
131         for child in module.children(db) {
132             self.collect_lang_items_recursive(db, child);
133         }
134     }
135
136     fn collect_lang_item<T, N>(
137         &mut self,
138         db: &(impl DefDatabase + AstDatabase),
139         item: T,
140         constructor: fn(T) -> LangItemTarget,
141     ) where
142         T: Copy + HasSource<Ast = N>,
143         N: AttrsOwner,
144     {
145         let node = item.source(db).ast;
146         if let Some(lang_item_name) = lang_item_name(&node) {
147             self.items.entry(lang_item_name).or_insert_with(|| constructor(item));
148         }
149     }
150 }
151
152 fn lang_item_name<T: AttrsOwner>(node: &T) -> Option<SmolStr> {
153     node.attrs()
154         .filter_map(|a| a.as_key_value())
155         .filter(|(key, _)| key == "lang")
156         .map(|(_, val)| val)
157         .nth(0)
158 }