• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Post-nameres attribute resolution.
2 
3 use hir_expand::{attrs::Attr, MacroCallId};
4 use syntax::{ast, SmolStr};
5 
6 use crate::{
7     attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
8     attr_macro_as_call_id,
9     db::DefDatabase,
10     item_scope::BuiltinShadowMode,
11     macro_id_to_def_id,
12     nameres::path_resolution::ResolveMode,
13     path::{ModPath, PathKind},
14     AstIdWithPath, LocalModuleId, UnresolvedMacro,
15 };
16 
17 use super::{DefMap, MacroSubNs};
18 
19 pub enum ResolvedAttr {
20     /// Attribute resolved to an attribute macro.
21     Macro(MacroCallId),
22     /// Attribute resolved to something else that does not require expansion.
23     Other,
24 }
25 
26 impl DefMap {
resolve_attr_macro( &self, db: &dyn DefDatabase, original_module: LocalModuleId, ast_id: AstIdWithPath<ast::Item>, attr: &Attr, ) -> Result<ResolvedAttr, UnresolvedMacro>27     pub(crate) fn resolve_attr_macro(
28         &self,
29         db: &dyn DefDatabase,
30         original_module: LocalModuleId,
31         ast_id: AstIdWithPath<ast::Item>,
32         attr: &Attr,
33     ) -> Result<ResolvedAttr, UnresolvedMacro> {
34         // NB: does not currently work for derive helpers as they aren't recorded in the `DefMap`
35 
36         if self.is_builtin_or_registered_attr(&ast_id.path) {
37             return Ok(ResolvedAttr::Other);
38         }
39 
40         let resolved_res = self.resolve_path_fp_with_macro(
41             db,
42             ResolveMode::Other,
43             original_module,
44             &ast_id.path,
45             BuiltinShadowMode::Module,
46             Some(MacroSubNs::Attr),
47         );
48         let def = match resolved_res.resolved_def.take_macros() {
49             Some(def) => {
50                 // `MacroSubNs` is just a hint, so the path may still resolve to a custom derive
51                 // macro, or even function-like macro when the path is qualified.
52                 if def.is_attribute(db) {
53                     def
54                 } else {
55                     return Ok(ResolvedAttr::Other);
56                 }
57             }
58             None => return Err(UnresolvedMacro { path: ast_id.path }),
59         };
60 
61         Ok(ResolvedAttr::Macro(attr_macro_as_call_id(
62             db,
63             &ast_id,
64             attr,
65             self.krate,
66             macro_id_to_def_id(db, def),
67         )))
68     }
69 
is_builtin_or_registered_attr(&self, path: &ModPath) -> bool70     pub(crate) fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
71         if path.kind != PathKind::Plain {
72             return false;
73         }
74 
75         let segments = path.segments();
76 
77         if let Some(name) = segments.first() {
78             let name = name.to_smol_str();
79             let pred = |n: &_| *n == name;
80 
81             let registered = self.data.registered_tools.iter().map(SmolStr::as_str);
82             let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred);
83             // FIXME: tool modules can be shadowed by actual modules
84             if is_tool {
85                 return true;
86             }
87 
88             if segments.len() == 1 {
89                 let mut registered = self.data.registered_attrs.iter().map(SmolStr::as_str);
90                 let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred);
91                 return is_inert;
92             }
93         }
94         false
95     }
96 }
97