Unnamed repository; edit this file 'description' to name the repository.
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Machinery for hygienic macros.
//!
//! Inspired by Matthew Flatt et al., “Macros That Work Together: Compile-Time Bindings, Partial
//! Expansion, and Definition Contexts,” *Journal of Functional Programming* 22, no. 2
//! (March 1, 2012): 181–216, <https://doi.org/10.1017/S0956796812000093>.
//!
//! Also see https://rustc-dev-guide.rust-lang.org/macro-expansion.html#hygiene-and-hierarchies
//!
//! # The Expansion Order Hierarchy
//!
//! `ExpnData` in rustc, rust-analyzer's version is [`MacroCallLoc`]. Traversing the hierarchy
//! upwards can be achieved by walking up [`MacroCallLoc::kind`]'s contained file id, as
//! [`MacroFile`]s are interned [`MacroCallLoc`]s.
//!
//! # The Macro Definition Hierarchy
//!
//! `SyntaxContextData` in rustc and rust-analyzer. Basically the same in both.
//!
//! # The Call-site Hierarchy
//!
//! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer.
use std::fmt;

use salsa::{InternId, InternValue};

use crate::MacroCallId;

/// Interned [`SyntaxContextData`].
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SyntaxContextId(InternId);

impl salsa::InternKey for SyntaxContextId {
    fn from_intern_id(v: salsa::InternId) -> Self {
        SyntaxContextId(v)
    }
    fn as_intern_id(&self) -> salsa::InternId {
        self.0
    }
}

impl fmt::Display for SyntaxContextId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0.as_u32())
    }
}

impl SyntaxContextId {
    /// The root context, which is the parent of all other contexts. All [`FileId`]s have this context.
    pub const ROOT: Self = SyntaxContextId(unsafe { InternId::new_unchecked(0) });

    pub fn is_root(self) -> bool {
        self == Self::ROOT
    }

    /// Deconstruct a `SyntaxContextId` into a raw `u32`.
    /// This should only be used for deserialization purposes for the proc-macro server.
    pub fn into_u32(self) -> u32 {
        self.0.as_u32()
    }

    /// Constructs a `SyntaxContextId` from a raw `u32`.
    /// This should only be used for serialization purposes for the proc-macro server.
    pub fn from_u32(u32: u32) -> Self {
        Self(InternId::from(u32))
    }
}

/// A syntax context describes a hierarchy tracking order of macro definitions.
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct SyntaxContextData {
    pub outer_expn: Option<MacroCallId>,
    pub outer_transparency: Transparency,
    pub parent: SyntaxContextId,
    /// This context, but with all transparent and semi-transparent expansions filtered away.
    pub opaque: SyntaxContextId,
    /// This context, but with all transparent expansions filtered away.
    pub opaque_and_semitransparent: SyntaxContextId,
}

impl InternValue for SyntaxContextData {
    type Key = (SyntaxContextId, Option<MacroCallId>, Transparency);

    fn into_key(&self) -> Self::Key {
        (self.parent, self.outer_expn, self.outer_transparency)
    }
}

impl std::fmt::Debug for SyntaxContextData {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("SyntaxContextData")
            .field("outer_expn", &self.outer_expn)
            .field("outer_transparency", &self.outer_transparency)
            .field("parent", &self.parent)
            .field("opaque", &self.opaque)
            .field("opaque_and_semitransparent", &self.opaque_and_semitransparent)
            .finish()
    }
}

impl SyntaxContextData {
    pub fn root() -> Self {
        SyntaxContextData {
            outer_expn: None,
            outer_transparency: Transparency::Opaque,
            parent: SyntaxContextId::ROOT,
            opaque: SyntaxContextId::ROOT,
            opaque_and_semitransparent: SyntaxContextId::ROOT,
        }
    }
}

/// A property of a macro expansion that determines how identifiers
/// produced by that expansion are resolved.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug)]
pub enum Transparency {
    /// Identifier produced by a transparent expansion is always resolved at call-site.
    /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this.
    Transparent,
    /// Identifier produced by a semi-transparent expansion may be resolved
    /// either at call-site or at definition-site.
    /// If it's a local variable, label or `$crate` then it's resolved at def-site.
    /// Otherwise it's resolved at call-site.
    /// `macro_rules` macros behave like this, built-in macros currently behave like this too,
    /// but that's an implementation detail.
    SemiTransparent,
    /// Identifier produced by an opaque expansion is always resolved at definition-site.
    /// Def-site spans in procedural macros, identifiers from `macro` by default use this.
    Opaque,
}