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