hir_def/nameres/
attr_resolution.rs

1//! Post-nameres attribute resolution.
2
3use base_db::Crate;
4use hir_expand::{
5    AttrMacroAttrIds, MacroCallId, MacroCallKind, MacroDefId,
6    attrs::{Attr, AttrId, AttrInput},
7    inert_attr_macro::find_builtin_attr_idx,
8    mod_path::{ModPath, PathKind},
9};
10use span::SyntaxContext;
11use syntax::ast;
12use triomphe::Arc;
13
14use crate::{
15    AstIdWithPath, MacroId, ModuleId, UnresolvedMacro,
16    db::DefDatabase,
17    item_scope::BuiltinShadowMode,
18    nameres::{LocalDefMap, path_resolution::ResolveMode},
19};
20
21use super::{DefMap, MacroSubNs};
22
23pub enum ResolvedAttr {
24    /// Attribute resolved to an attribute macro.
25    Macro(MacroCallId),
26    /// Attribute resolved to something else that does not require expansion.
27    Other,
28}
29
30impl DefMap {
31    /// This cannot be used to resolve items that allow derives.
32    pub(crate) fn resolve_attr_macro(
33        &self,
34        local_def_map: &LocalDefMap,
35        db: &dyn DefDatabase,
36        original_module: ModuleId,
37        ast_id: AstIdWithPath<ast::Item>,
38        attr: &Attr,
39        attr_id: AttrId,
40    ) -> Result<ResolvedAttr, UnresolvedMacro> {
41        // NB: does not currently work for derive helpers as they aren't recorded in the `DefMap`
42
43        if self.is_builtin_or_registered_attr(&ast_id.path) {
44            return Ok(ResolvedAttr::Other);
45        }
46
47        let resolved_res = self.resolve_path_fp_with_macro(
48            local_def_map,
49            db,
50            ResolveMode::Other,
51            original_module,
52            &ast_id.path,
53            BuiltinShadowMode::Module,
54            Some(MacroSubNs::Attr),
55        );
56        let def = match resolved_res.resolved_def.take_macros() {
57            Some(def) => {
58                // `MacroSubNs` is just a hint, so the path may still resolve to a custom derive
59                // macro, or even function-like macro when the path is qualified.
60                if def.is_attribute(db) {
61                    def
62                } else {
63                    return Ok(ResolvedAttr::Other);
64                }
65            }
66            None => return Err(UnresolvedMacro { path: ast_id.path.as_ref().clone() }),
67        };
68
69        Ok(ResolvedAttr::Macro(attr_macro_as_call_id(
70            db,
71            &ast_id,
72            attr,
73            // There aren't any active attributes before this one, because attribute macros
74            // replace their input, and derive macros are not allowed in this function.
75            AttrMacroAttrIds::from_one(attr_id),
76            self.krate,
77            db.macro_def(def),
78        )))
79    }
80
81    pub(crate) fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
82        if path.kind != PathKind::Plain {
83            return false;
84        }
85
86        let segments = path.segments();
87
88        if let Some(name) = segments.first() {
89            let name = name.symbol();
90            let pred = |n: &_| *n == *name;
91
92            let is_tool = self.data.registered_tools.iter().any(pred);
93            // FIXME: tool modules can be shadowed by actual modules
94            if is_tool {
95                return true;
96            }
97
98            if segments.len() == 1 && find_builtin_attr_idx(name).is_some() {
99                return true;
100            }
101        }
102        false
103    }
104}
105
106pub(super) fn attr_macro_as_call_id(
107    db: &dyn DefDatabase,
108    item_attr: &AstIdWithPath<ast::Item>,
109    macro_attr: &Attr,
110    censored_attr_ids: AttrMacroAttrIds,
111    krate: Crate,
112    def: MacroDefId,
113) -> MacroCallId {
114    let arg = match macro_attr.input.as_deref() {
115        Some(AttrInput::TokenTree(tt)) => {
116            let mut tt = tt.clone();
117            tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible;
118            Some(tt)
119        }
120
121        _ => None,
122    };
123
124    def.make_call(
125        db,
126        krate,
127        MacroCallKind::Attr {
128            ast_id: item_attr.ast_id,
129            attr_args: arg.map(Arc::new),
130            censored_attr_ids,
131        },
132        macro_attr.ctxt,
133    )
134}
135
136pub(super) fn derive_macro_as_call_id(
137    db: &dyn DefDatabase,
138    item_attr: &AstIdWithPath<ast::Adt>,
139    derive_attr_index: AttrId,
140    derive_pos: u32,
141    call_site: SyntaxContext,
142    krate: Crate,
143    resolver: impl Fn(&ModPath) -> Option<(MacroId, MacroDefId)>,
144    derive_macro_id: MacroCallId,
145) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
146    let (macro_id, def_id) = resolver(&item_attr.path)
147        .filter(|(_, def_id)| def_id.is_derive())
148        .ok_or_else(|| UnresolvedMacro { path: item_attr.path.as_ref().clone() })?;
149    let call_id = def_id.make_call(
150        db,
151        krate,
152        MacroCallKind::Derive {
153            ast_id: item_attr.ast_id,
154            derive_index: derive_pos,
155            derive_attr_index,
156            derive_macro_id,
157        },
158        call_site,
159    );
160    Ok((macro_id, def_id, call_id))
161}