1use core::fmt;
4
5use crate::alloc;
6use crate::ast;
7use crate::ast::Span;
8use crate::compile::{self, ErrorKind, ItemMeta};
9use crate::indexing::Indexer;
10use crate::internal_macros::resolve_context;
11use crate::macros::{IntoLit, ToTokens, TokenStream};
12use crate::parse::{Parse, Resolve};
13use crate::runtime::Value;
14use crate::{Source, SourceId};
15
16#[cfg(feature = "std")]
32#[cfg_attr(rune_docsrs, doc(cfg(feature = "std")))]
33pub fn test<F, O>(f: F) -> crate::support::Result<O>
34where
35 F: FnOnce(&mut MacroContext<'_, '_, '_>) -> crate::support::Result<O>,
36{
37 use rust_alloc::rc::Rc;
38
39 use crate::compile::{NoopCompileVisitor, NoopSourceLoader, Pool, Prelude, UnitBuilder};
40 use crate::hir;
41 use crate::indexing::{IndexItem, Items, Scopes};
42 use crate::macros::Storage;
43 use crate::query::Query;
44 use crate::shared::{Consts, Gen};
45 use crate::support::Context as _;
46 use crate::{Context, Diagnostics, Item, Options, Sources};
47
48 let mut unit = UnitBuilder::default();
49 let prelude = Prelude::default();
50 let gen = Gen::default();
51 let const_arena = hir::Arena::new();
52 let mut consts = Consts::default();
53 let mut storage = Storage::default();
54 let mut sources = Sources::default();
55 let mut pool = Pool::new().context("Failed to allocate pool")?;
56 let mut visitor = NoopCompileVisitor::new();
57 let mut diagnostics = Diagnostics::default();
58 let mut source_loader = NoopSourceLoader::default();
59 let options = Options::from_default_env()?;
60 let context = Context::default();
61 let mut inner = Default::default();
62
63 let mut query = Query::new(
64 &mut unit,
65 &prelude,
66 &const_arena,
67 &mut consts,
68 &mut storage,
69 &mut sources,
70 &mut pool,
71 &mut visitor,
72 &mut diagnostics,
73 &mut source_loader,
74 &options,
75 &[],
76 &gen,
77 &context,
78 &mut inner,
79 );
80
81 let source_id = SourceId::empty();
82
83 let (root_id, root_mod_id) = query
84 .insert_root_mod(source_id, Span::empty())
85 .context("Failed to inserted root module")?;
86
87 let item_meta = query
88 .item_for("root item", root_id)
89 .context("Just inserted item meta does not exist")?;
90
91 let tree = Rc::default();
92
93 let mut idx = Indexer {
94 q: query.borrow(),
95 source_id,
96 items: Items::new(Item::new()).context("Failed to construct items")?,
97 scopes: Scopes::new().context("Failed to build indexer scopes")?,
98 item: IndexItem::new(root_mod_id, root_id),
99 nested_item: None,
100 macro_depth: 0,
101 root: None,
102 queue: None,
103 loaded: None,
104 tree: &tree,
105 };
106
107 let mut cx = MacroContext {
108 macro_span: Span::empty(),
109 input_span: Span::empty(),
110 item_meta,
111 idx: &mut idx,
112 };
113
114 f(&mut cx)
115}
116
117pub struct MacroContext<'a, 'b, 'arena> {
119 pub(crate) macro_span: Span,
121 pub(crate) input_span: Span,
123 pub(crate) item_meta: ItemMeta,
125 pub(crate) idx: &'a mut Indexer<'b, 'arena>,
127}
128
129impl<'a, 'b, 'arena> MacroContext<'a, 'b, 'arena> {
130 pub fn eval(&mut self, target: &ast::Expr) -> compile::Result<Value> {
158 target.eval(self)
159 }
160
161 pub fn lit<T>(&mut self, lit: T) -> alloc::Result<ast::Lit>
177 where
178 T: IntoLit,
179 {
180 T::into_lit(lit, self)
181 }
182
183 pub fn ident(&mut self, ident: &str) -> alloc::Result<ast::Ident> {
200 let span = self.macro_span();
201 let id = self.idx.q.storage.insert_str(ident)?;
202 let source = ast::LitSource::Synthetic(id);
203 Ok(ast::Ident { span, source })
204 }
205
206 pub fn label(&mut self, label: &str) -> alloc::Result<ast::Label> {
226 let span = self.macro_span();
227 let id = self.idx.q.storage.insert_str(label)?;
228 let source = ast::LitSource::Synthetic(id);
229 Ok(ast::Label { span, source })
230 }
231
232 pub fn stringify<T>(&mut self, tokens: &T) -> alloc::Result<Stringify<'_, 'a, 'b, 'arena>>
234 where
235 T: ToTokens,
236 {
237 let mut stream = TokenStream::new();
238 tokens.to_tokens(self, &mut stream)?;
239 Ok(Stringify { cx: self, stream })
240 }
241
242 pub fn resolve<'r, T>(&'r self, item: T) -> compile::Result<T::Output>
244 where
245 T: Resolve<'r>,
246 {
247 item.resolve(resolve_context!(self.idx.q))
248 }
249
250 pub(crate) fn literal_source(&self, source: ast::LitSource, span: Span) -> Option<&str> {
252 match source {
253 ast::LitSource::Text(source_id) => self.idx.q.sources.source(source_id, span),
254 ast::LitSource::Synthetic(id) => self.idx.q.storage.get_string(id),
255 ast::LitSource::BuiltIn(builtin) => Some(builtin.as_str()),
256 }
257 }
258
259 pub fn insert_source(&mut self, name: &str, source: &str) -> alloc::Result<SourceId> {
263 self.idx.q.sources.insert(Source::new(name, source)?)
264 }
265
266 pub fn parse_source<T>(&self, id: SourceId) -> compile::Result<T>
269 where
270 T: Parse,
271 {
272 let source = self.idx.q.sources.get(id).ok_or_else(|| {
273 compile::Error::new(Span::empty(), ErrorKind::MissingSourceId { source_id: id })
274 })?;
275
276 crate::parse::parse_all(source.as_str(), id, false)
277 }
278
279 pub fn macro_span(&self) -> Span {
284 self.macro_span
285 }
286
287 pub fn input_span(&self) -> Span {
291 self.input_span
292 }
293}
294
295pub struct Stringify<'cx, 'a, 'b, 'arena> {
296 cx: &'cx MacroContext<'a, 'b, 'arena>,
297 stream: TokenStream,
298}
299
300impl fmt::Display for Stringify<'_, '_, '_, '_> {
301 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302 let mut it = self.stream.iter();
303 let last = it.next_back();
304
305 for token in it {
306 token.token_fmt(self.cx, f)?;
307 write!(f, " ")?;
308 }
309
310 if let Some(last) = last {
311 last.token_fmt(self.cx, f)?;
312 }
313
314 Ok(())
315 }
316}