Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/nameres/attr_resolution.rs')
-rw-r--r--crates/hir-def/src/nameres/attr_resolution.rs98
1 files changed, 98 insertions, 0 deletions
diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs
new file mode 100644
index 0000000000..3650204ee9
--- /dev/null
+++ b/crates/hir-def/src/nameres/attr_resolution.rs
@@ -0,0 +1,98 @@
+//! Post-nameres attribute resolution.
+
+use hir_expand::MacroCallId;
+use syntax::{ast, SmolStr};
+
+use crate::{
+ attr::Attr,
+ attr_macro_as_call_id, builtin_attr,
+ db::DefDatabase,
+ item_scope::BuiltinShadowMode,
+ macro_id_to_def_id,
+ nameres::path_resolution::ResolveMode,
+ path::{ModPath, PathKind},
+ AstIdWithPath, LocalModuleId, UnresolvedMacro,
+};
+
+use super::DefMap;
+
+pub enum ResolvedAttr {
+ /// Attribute resolved to an attribute macro.
+ Macro(MacroCallId),
+ /// Attribute resolved to something else that does not require expansion.
+ Other,
+}
+
+impl DefMap {
+ pub(crate) fn resolve_attr_macro(
+ &self,
+ db: &dyn DefDatabase,
+ original_module: LocalModuleId,
+ ast_id: AstIdWithPath<ast::Item>,
+ attr: &Attr,
+ ) -> Result<ResolvedAttr, UnresolvedMacro> {
+ // NB: does not currently work for derive helpers as they aren't recorded in the `DefMap`
+
+ if self.is_builtin_or_registered_attr(&ast_id.path) {
+ return Ok(ResolvedAttr::Other);
+ }
+
+ let resolved_res = self.resolve_path_fp_with_macro(
+ db,
+ ResolveMode::Other,
+ original_module,
+ &ast_id.path,
+ BuiltinShadowMode::Module,
+ );
+ let def = match resolved_res.resolved_def.take_macros() {
+ Some(def) => {
+ if def.is_attribute(db) {
+ def
+ } else {
+ return Ok(ResolvedAttr::Other);
+ }
+ }
+ None => return Err(UnresolvedMacro { path: ast_id.path }),
+ };
+
+ Ok(ResolvedAttr::Macro(attr_macro_as_call_id(
+ db,
+ &ast_id,
+ attr,
+ self.krate,
+ macro_id_to_def_id(db, def),
+ false,
+ )))
+ }
+
+ pub(crate) fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
+ if path.kind != PathKind::Plain {
+ return false;
+ }
+
+ let segments = path.segments();
+
+ if let Some(name) = segments.first() {
+ let name = name.to_smol_str();
+ let pred = |n: &_| *n == name;
+
+ let registered = self.registered_tools.iter().map(SmolStr::as_str);
+ let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
+ // FIXME: tool modules can be shadowed by actual modules
+ if is_tool {
+ return true;
+ }
+
+ if segments.len() == 1 {
+ let registered = self.registered_attrs.iter().map(SmolStr::as_str);
+ let is_inert = builtin_attr::INERT_ATTRIBUTES
+ .iter()
+ .map(|it| it.name)
+ .chain(registered)
+ .any(pred);
+ return is_inert;
+ }
+ }
+ false
+ }
+}