]> git.lizzy.rs Git - rust.git/blob - crates/ra_hir_def/src/item_scope.rs
Move some code to scope
[rust.git] / crates / ra_hir_def / src / item_scope.rs
1 //! Describes items defined or visible (ie, imported) in a certain scope.
2 //! This is shared between modules and blocks.
3
4 use hir_expand::name::Name;
5 use once_cell::sync::Lazy;
6 use rustc_hash::FxHashMap;
7
8 use crate::{per_ns::PerNs, BuiltinType, ImplId, LocalImportId, MacroDefId, ModuleDefId, TraitId};
9
10 #[derive(Debug, Default, PartialEq, Eq)]
11 pub struct ItemScope {
12     pub(crate) items: FxHashMap<Name, Resolution>,
13     pub(crate) impls: Vec<ImplId>,
14     /// Macros visible in current module in legacy textual scope
15     ///
16     /// For macros invoked by an unqualified identifier like `bar!()`, `legacy_macros` will be searched in first.
17     /// If it yields no result, then it turns to module scoped `macros`.
18     /// It macros with name qualified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped,
19     /// and only normal scoped `macros` will be searched in.
20     ///
21     /// Note that this automatically inherit macros defined textually before the definition of module itself.
22     ///
23     /// Module scoped macros will be inserted into `items` instead of here.
24     // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
25     // be all resolved to the last one defined if shadowing happens.
26     pub(crate) legacy_macros: FxHashMap<Name, MacroDefId>,
27 }
28
29 static BUILTIN_SCOPE: Lazy<FxHashMap<Name, Resolution>> = Lazy::new(|| {
30     BuiltinType::ALL
31         .iter()
32         .map(|(name, ty)| {
33             (name.clone(), Resolution { def: PerNs::types(ty.clone().into()), import: None })
34         })
35         .collect()
36 });
37
38 /// Shadow mode for builtin type which can be shadowed by module.
39 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
40 pub(crate) enum BuiltinShadowMode {
41     // Prefer Module
42     Module,
43     // Prefer Other Types
44     Other,
45 }
46
47 /// Legacy macros can only be accessed through special methods like `get_legacy_macros`.
48 /// Other methods will only resolve values, types and module scoped macros only.
49 impl ItemScope {
50     pub fn push_res(
51         &mut self,
52         name: Name,
53         res: &Resolution,
54         import: Option<LocalImportId>,
55     ) -> bool {
56         let mut changed = false;
57         let existing = self.items.entry(name.clone()).or_default();
58
59         if existing.def.types.is_none() && res.def.types.is_some() {
60             existing.def.types = res.def.types;
61             existing.import = import.or(res.import);
62             changed = true;
63         }
64         if existing.def.values.is_none() && res.def.values.is_some() {
65             existing.def.values = res.def.values;
66             existing.import = import.or(res.import);
67             changed = true;
68         }
69         if existing.def.macros.is_none() && res.def.macros.is_some() {
70             existing.def.macros = res.def.macros;
71             existing.import = import.or(res.import);
72             changed = true;
73         }
74
75         if existing.def.is_none()
76             && res.def.is_none()
77             && existing.import.is_none()
78             && res.import.is_some()
79         {
80             existing.import = res.import;
81         }
82         changed
83     }
84
85     pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a {
86         //FIXME: shadowing
87         self.items.iter().chain(BUILTIN_SCOPE.iter())
88     }
89
90     pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
91         self.entries()
92             .filter_map(|(_name, res)| if res.import.is_none() { Some(res.def) } else { None })
93             .flat_map(|per_ns| {
94                 per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter())
95             })
96     }
97
98     pub fn impls(&self) -> impl Iterator<Item = ImplId> + ExactSizeIterator + '_ {
99         self.impls.iter().copied()
100     }
101
102     /// Iterate over all module scoped macros
103     pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
104         self.items
105             .iter()
106             .filter_map(|(name, res)| res.def.take_macros().map(|macro_| (name, macro_)))
107     }
108
109     /// Iterate over all legacy textual scoped macros visible at the end of the module
110     pub(crate) fn legacy_macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
111         self.legacy_macros.iter().map(|(name, def)| (name, *def))
112     }
113
114     /// Get a name from current module scope, legacy macros are not included
115     pub(crate) fn get(&self, name: &Name, shadow: BuiltinShadowMode) -> Option<&Resolution> {
116         match shadow {
117             BuiltinShadowMode::Module => self.items.get(name).or_else(|| BUILTIN_SCOPE.get(name)),
118             BuiltinShadowMode::Other => {
119                 let item = self.items.get(name);
120                 if let Some(res) = item {
121                     if let Some(ModuleDefId::ModuleId(_)) = res.def.take_types() {
122                         return BUILTIN_SCOPE.get(name).or(item);
123                     }
124                 }
125
126                 item.or_else(|| BUILTIN_SCOPE.get(name))
127             }
128         }
129     }
130
131     pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a {
132         self.items.values().filter_map(|r| match r.def.take_types() {
133             Some(ModuleDefId::TraitId(t)) => Some(t),
134             _ => None,
135         })
136     }
137
138     pub(crate) fn get_legacy_macro(&self, name: &Name) -> Option<MacroDefId> {
139         self.legacy_macros.get(name).copied()
140     }
141 }
142
143 #[derive(Debug, Clone, PartialEq, Eq, Default)]
144 pub struct Resolution {
145     /// None for unresolved
146     pub def: PerNs,
147     /// ident by which this is imported into local scope.
148     pub import: Option<LocalImportId>,
149 }