summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
blob: 79cabeb0fb7a4ca123df00c1b9f3d610b4b21633 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! Post-nameres attribute resolution.

use hir_expand::{attrs::Attr, MacroCallId};
use syntax::{ast, SmolStr};

use crate::{
    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
    }
}