Skip to main content

hir_def/nameres/
attr_resolution.rs

1//! Post-nameres attribute resolution.
2
3use base_db::{Crate, SourceDatabase};
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;
12
13use crate::{
14    AstIdWithPath, MacroId, ModuleId, UnresolvedMacro,
15    item_scope::BuiltinShadowMode,
16    nameres::{LocalDefMap, path_resolution::ResolveMode},
17};
18
19use super::{DefMap, MacroSubNs};
20
21pub enum ResolvedAttr {
22    /// Attribute resolved to an attribute macro.
23    Macro(MacroCallId),
24    /// Attribute resolved to something else that does not require expansion.
25    Other,
26}
27
28impl DefMap {
29    /// This cannot be used to resolve items that allow derives.
30    pub(crate) fn resolve_attr_macro(
31        &self,
32        local_def_map: &LocalDefMap,
33        db: &dyn SourceDatabase,
34        original_module: ModuleId,
35        ast_id: AstIdWithPath<ast::Item>,
36        attr: &Attr,
37        attr_id: AttrId,
38        macro_depth: u32,
39    ) -> Result<ResolvedAttr, UnresolvedMacro> {
40        // NB: does not currently work for derive helpers as they aren't recorded in the `DefMap`
41
42        if self.is_builtin_or_registered_attr(&ast_id.path) {
43            return Ok(ResolvedAttr::Other);
44        }
45
46        let resolved_res = self.resolve_path_fp_with_macro(
47            local_def_map,
48            db,
49            ResolveMode::Other,
50            original_module,
51            &ast_id.path,
52            BuiltinShadowMode::Module,
53            Some(MacroSubNs::Attr),
54        );
55        let def = match resolved_res.resolved_def.take_macros() {
56            Some(def) => {
57                // `MacroSubNs` is just a hint, so the path may still resolve to a custom derive
58                // macro, or even function-like macro when the path is qualified.
59                if def.is_attribute(db) {
60                    def
61                } else {
62                    return Ok(ResolvedAttr::Other);
63                }
64            }
65            None => return Err(UnresolvedMacro { path: (*ast_id.path).clone() }),
66        };
67
68        Ok(ResolvedAttr::Macro(attr_macro_as_call_id(
69            db,
70            &ast_id,
71            attr,
72            // There aren't any active attributes before this one, because attribute macros
73            // replace their input, and derive macros are not allowed in this function.
74            AttrMacroAttrIds::from_one(attr_id),
75            self.krate,
76            def.definition(db),
77            macro_depth,
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 SourceDatabase,
108    item_attr: &AstIdWithPath<ast::Item>,
109    macro_attr: &Attr,
110    censored_attr_ids: AttrMacroAttrIds,
111    krate: Crate,
112    def: MacroDefId,
113    macro_depth: u32,
114) -> MacroCallId {
115    let arg = match macro_attr.input.as_deref() {
116        Some(AttrInput::TokenTree(tt)) => {
117            let mut tt = tt.clone();
118            tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
119            Some(tt)
120        }
121
122        _ => None,
123    };
124
125    def.make_call(
126        db,
127        krate,
128        MacroCallKind::Attr {
129            ast_id: item_attr.ast_id,
130            attr_args: arg.map(Box::new),
131            censored_attr_ids,
132        },
133        macro_attr.ctxt,
134        macro_depth,
135    )
136}
137
138pub(super) fn derive_macro_as_call_id(
139    db: &dyn SourceDatabase,
140    item_attr: &AstIdWithPath<ast::Adt>,
141    derive_attr_index: AttrId,
142    derive_pos: u32,
143    call_site: SyntaxContext,
144    krate: Crate,
145    resolver: impl Fn(&ModPath) -> Option<(MacroId, MacroDefId)>,
146    derive_macro_id: MacroCallId,
147    macro_depth: u32,
148) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
149    let (macro_id, def_id) = resolver(&item_attr.path)
150        .filter(|(_, def_id)| def_id.is_derive())
151        .ok_or_else(|| UnresolvedMacro { path: (*item_attr.path).clone() })?;
152    let call_id = def_id.make_call(
153        db,
154        krate,
155        MacroCallKind::Derive {
156            ast_id: item_attr.ast_id,
157            derive_index: derive_pos,
158            derive_attr_index,
159            derive_macro_id,
160        },
161        call_site,
162        macro_depth,
163    );
164    Ok((macro_id, def_id, call_id))
165}