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