1use std::convert::identity;
26
27use span::{Edition, MacroCallId, Span, SyntaxContext};
28
29use crate::db::ExpandDatabase;
30
31pub use span::Transparency;
32
33pub fn span_with_def_site_ctxt(
34 db: &dyn ExpandDatabase,
35 span: Span,
36 expn_id: MacroCallId,
37 edition: Edition,
38) -> Span {
39 span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque, edition)
40}
41
42pub fn span_with_call_site_ctxt(
43 db: &dyn ExpandDatabase,
44 span: Span,
45 expn_id: MacroCallId,
46 edition: Edition,
47) -> Span {
48 span_with_ctxt_from_mark(db, span, expn_id, Transparency::Transparent, edition)
49}
50
51pub fn span_with_mixed_site_ctxt(
52 db: &dyn ExpandDatabase,
53 span: Span,
54 expn_id: MacroCallId,
55 edition: Edition,
56) -> Span {
57 span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiTransparent, edition)
58}
59
60fn span_with_ctxt_from_mark(
61 db: &dyn ExpandDatabase,
62 span: Span,
63 expn_id: MacroCallId,
64 transparency: Transparency,
65 edition: Edition,
66) -> Span {
67 Span {
68 ctx: apply_mark(db, SyntaxContext::root(edition), expn_id, transparency, edition),
69 ..span
70 }
71}
72
73pub(super) fn apply_mark(
74 db: &dyn ExpandDatabase,
75 ctxt: span::SyntaxContext,
76 call_id: span::MacroCallId,
77 transparency: Transparency,
78 edition: Edition,
79) -> SyntaxContext {
80 if transparency == Transparency::Opaque {
81 return apply_mark_internal(db, ctxt, call_id, transparency, edition);
82 }
83
84 let call_site_ctxt = db.lookup_intern_macro_call(call_id.into()).ctxt;
85 let mut call_site_ctxt = if transparency == Transparency::SemiTransparent {
86 call_site_ctxt.normalize_to_macros_2_0(db)
87 } else {
88 call_site_ctxt.normalize_to_macro_rules(db)
89 };
90
91 if call_site_ctxt.is_root() {
92 return apply_mark_internal(db, ctxt, call_id, transparency, edition);
93 }
94
95 for (call_id, transparency) in ctxt.marks(db) {
105 call_site_ctxt = apply_mark_internal(db, call_site_ctxt, call_id, transparency, edition);
106 }
107 apply_mark_internal(db, call_site_ctxt, call_id, transparency, edition)
108}
109
110fn apply_mark_internal(
111 db: &dyn ExpandDatabase,
112 ctxt: SyntaxContext,
113 call_id: MacroCallId,
114 transparency: Transparency,
115 edition: Edition,
116) -> SyntaxContext {
117 let call_id = Some(call_id);
118
119 let mut opaque = ctxt.opaque(db);
120 let mut opaque_and_semitransparent = ctxt.opaque_and_semitransparent(db);
121
122 if transparency >= Transparency::Opaque {
123 let parent = opaque;
124 opaque = SyntaxContext::new(db, call_id, transparency, edition, parent, identity, identity);
125 }
126
127 if transparency >= Transparency::SemiTransparent {
128 let parent = opaque_and_semitransparent;
129 opaque_and_semitransparent =
130 SyntaxContext::new(db, call_id, transparency, edition, parent, |_| opaque, identity);
131 }
132
133 let parent = ctxt;
134 SyntaxContext::new(
135 db,
136 call_id,
137 transparency,
138 edition,
139 parent,
140 |_| opaque,
141 |_| opaque_and_semitransparent,
142 )
143}