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