hir_def/nameres/
attr_resolution.rs

1//! Post-nameres attribute resolution.
2
3use base_db::Crate;
4use hir_expand::{
5    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, LocalModuleId, MacroId, 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    pub(crate) fn resolve_attr_macro(
32        &self,
33        local_def_map: &LocalDefMap,
34        db: &dyn DefDatabase,
35        original_module: LocalModuleId,
36        ast_id: AstIdWithPath<ast::Item>,
37        attr: &Attr,
38    ) -> Result<ResolvedAttr, UnresolvedMacro> {
39        // NB: does not currently work for derive helpers as they aren't recorded in the `DefMap`
40
41        if self.is_builtin_or_registered_attr(&ast_id.path) {
42            return Ok(ResolvedAttr::Other);
43        }
44
45        let resolved_res = self.resolve_path_fp_with_macro(
46            local_def_map,
47            db,
48            ResolveMode::Other,
49            original_module,
50            &ast_id.path,
51            BuiltinShadowMode::Module,
52            Some(MacroSubNs::Attr),
53        );
54        let def = match resolved_res.resolved_def.take_macros() {
55            Some(def) => {
56                // `MacroSubNs` is just a hint, so the path may still resolve to a custom derive
57                // macro, or even function-like macro when the path is qualified.
58                if def.is_attribute(db) {
59                    def
60                } else {
61                    return Ok(ResolvedAttr::Other);
62                }
63            }
64            None => return Err(UnresolvedMacro { path: ast_id.path.as_ref().clone() }),
65        };
66
67        Ok(ResolvedAttr::Macro(attr_macro_as_call_id(
68            db,
69            &ast_id,
70            attr,
71            self.krate,
72            db.macro_def(def),
73        )))
74    }
75
76    pub(crate) fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
77        if path.kind != PathKind::Plain {
78            return false;
79        }
80
81        let segments = path.segments();
82
83        if let Some(name) = segments.first() {
84            let name = name.symbol();
85            let pred = |n: &_| *n == *name;
86
87            let is_tool = self.data.registered_tools.iter().any(pred);
88            // FIXME: tool modules can be shadowed by actual modules
89            if is_tool {
90                return true;
91            }
92
93            if segments.len() == 1 {
94                if find_builtin_attr_idx(name).is_some() {
95                    return true;
96                }
97                if self.data.registered_attrs.iter().any(pred) {
98                    return true;
99                }
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    krate: Crate,
111    def: MacroDefId,
112) -> MacroCallId {
113    let arg = match macro_attr.input.as_deref() {
114        Some(AttrInput::TokenTree(tt)) => {
115            let mut tt = tt.clone();
116            tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Invisible;
117            Some(tt)
118        }
119
120        _ => None,
121    };
122
123    def.make_call(
124        db,
125        krate,
126        MacroCallKind::Attr {
127            ast_id: item_attr.ast_id,
128            attr_args: arg.map(Arc::new),
129            invoc_attr_index: macro_attr.id,
130        },
131        macro_attr.ctxt,
132    )
133}
134
135pub(super) fn derive_macro_as_call_id(
136    db: &dyn DefDatabase,
137    item_attr: &AstIdWithPath<ast::Adt>,
138    derive_attr_index: AttrId,
139    derive_pos: u32,
140    call_site: SyntaxContext,
141    krate: Crate,
142    resolver: impl Fn(&ModPath) -> Option<(MacroId, MacroDefId)>,
143    derive_macro_id: MacroCallId,
144) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
145    let (macro_id, def_id) = resolver(&item_attr.path)
146        .filter(|(_, def_id)| def_id.is_derive())
147        .ok_or_else(|| UnresolvedMacro { path: item_attr.path.as_ref().clone() })?;
148    let call_id = def_id.make_call(
149        db,
150        krate,
151        MacroCallKind::Derive {
152            ast_id: item_attr.ast_id,
153            derive_index: derive_pos,
154            derive_attr_index,
155            derive_macro_id,
156        },
157        call_site,
158    );
159    Ok((macro_id, def_id, call_id))
160}