1use std::{borrow::Cow, fmt::Display};
15
16use super::*;
17use crate::ast::*;
18use pretty::BoxAllocator;
19
20use crate::symbol::Symbol;
21use identifiers::*;
22use literals::*;
23use resugared::*;
24
25mod debug_json;
26mod to_document;
27pub use debug_json::*;
28pub use to_document::*;
29
30#[macro_export]
31macro_rules! todo_document {
40 ($allocator:ident, issue $issue:literal) => {
41 {return $allocator.todo_document(&format!("TODO_LINE_{}", std::line!()), Some($issue));}
42 };
43 ($allocator:ident, issue $issue:literal, $($tt:tt)*) => {
44 {
45 let message = format!($($tt)*);
46 return $allocator.todo_document(&message, Some($issue));
47 }
48 };
49 ($allocator:ident,) => {
50 {return $allocator.todo_document(&format!("TODO_LINE_{}", std::line!()), None);}
51 };
52 ($allocator:ident, $($tt:tt)*) => {
53 {
54 let message = format!($($tt)*);
55 return $allocator.todo_document(&message, None);
56 }
57 };
58}
59pub use todo_document;
60
61#[macro_export]
67macro_rules! pretty_ast_docs {
68 ($printer: expr, $docs:expr) => {{
69 use $crate::printer::pretty_ast::{ToDocumentOwned};
70 $docs.to_document_owned($printer)
71 }};
72 ($printer: expr, $($docs:expr),*$(,)?) => {{
73 use $crate::printer::pretty_ast::{ToDocumentOwned};
74 nil!()
75 $(.append($docs.to_document_owned($printer)))*
76 }};
77}
78pub use pretty_ast_docs;
79
80#[macro_export]
87macro_rules! pretty_ast_intersperse {
88 ($printer: expr, $docs:expr, $sep: expr$(,)?) => {{
89 let docs = $docs;
90 let sep = $sep;
91 $crate::printer::pretty_ast::PrettyAstExt::intersperse($printer, docs, sep)
92 }};
93}
94pub use pretty_ast_intersperse;
95
96#[macro_export]
97macro_rules! install_pretty_helpers {
127 ($allocator:ident : $allocator_type:ty) => {
128 $crate::printer::pretty_ast::install_pretty_helpers!(
129 @$allocator,
130 #[doc = ::std::concat!("Proxy macro for [`", stringify!($crate), "::printer::pretty_ast::todo_document`] that automatically uses `", stringify!($allocator),"` as allocator.")]
131 #[doc = ::std::concat!(r#"Example: `disambiguated_todo!("Error message")` or `disambiguated_todo!(issue #123, "Error message with issue attached")`."#)]
132 disambiguated_todo{$crate::printer::pretty_ast::todo_document!},
133 #[doc = ::std::concat!("Proxy macro for [`pretty::docs`] that automatically uses `", stringify!($allocator),"` as allocator.")]
134 docs{$crate::printer::pretty_ast::pretty_ast_docs!},
135 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::nil`] that automatically uses `", stringify!($allocator),"` as allocator.")]
136 nil{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::nil},
137 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::fail`] that automatically uses `", stringify!($allocator),"` as allocator.")]
138 fail{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::fail},
139 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::hardline`] that automatically uses `", stringify!($allocator),"` as allocator.")]
140 hardline{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::hardline},
141 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::space`] that automatically uses `", stringify!($allocator),"` as allocator.")]
142 space{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::space},
143 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::line`] that automatically uses `", stringify!($allocator),"` as allocator.")]
144 disambiguated_line{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::line},
145 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::line_`] that automatically uses `", stringify!($allocator),"` as allocator.")]
146 line_{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::line_},
147 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::softline`] that automatically uses `", stringify!($allocator),"` as allocator.")]
148 softline{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::softline},
149 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::softline_`] that automatically uses `", stringify!($allocator),"` as allocator.")]
150 softline_{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::softline_},
151 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::as_string`] that automatically uses `", stringify!($allocator),"` as allocator.")]
152 as_string{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::as_string},
153 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::text`] that automatically uses `", stringify!($allocator),"` as allocator.")]
154 text{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::text},
155 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::concat`] that automatically uses `", stringify!($allocator),"` as allocator.")]
156 disambiguated_concat{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::concat},
157 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::intersperse`] that automatically uses `", stringify!($allocator),"` as allocator.")]
158 intersperse{$crate::printer::pretty_ast::pretty_ast_intersperse!},
159 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::column`] that automatically uses `", stringify!($allocator),"` as allocator.")]
160 column{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::column},
161 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::nesting`] that automatically uses `", stringify!($allocator),"` as allocator.")]
162 nesting{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::nesting},
163 #[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::reflow`] that automatically uses `", stringify!($allocator),"` as allocator.")]
164 reflow{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::reflow}
165 );
166 };
167 (@$allocator:ident, $($(#[$($attrs:tt)*])*$name:ident{$($callable:tt)*}),*) => {
168 $(
169 #[hax_rust_engine_macros::partial_apply($($callable)*, $allocator,)]
170 #[allow(unused)]
171 $(#[$($attrs)*])*
172 macro_rules! $name {}
173 )*
174 };
175}
176pub use install_pretty_helpers;
177
178pub trait PrettyAstExt<A: 'static>: Sized {
183 fn nil(&self) -> DocBuilder<A> {
186 pretty::DocAllocator::nil(&BoxAllocator)
187 }
188
189 fn fail(&self) -> DocBuilder<A> {
194 pretty::DocAllocator::fail(&BoxAllocator)
195 }
196
197 fn hardline(&self) -> DocBuilder<A> {
200 pretty::DocAllocator::hardline(&BoxAllocator)
201 }
202
203 fn space(&self) -> DocBuilder<A> {
206 pretty::DocAllocator::space(&BoxAllocator)
207 }
208
209 fn line(&self) -> DocBuilder<A> {
212 pretty::DocAllocator::line(&BoxAllocator)
213 }
214
215 fn line_(&self) -> DocBuilder<A> {
218 pretty::DocAllocator::line_(&BoxAllocator)
219 }
220
221 fn softline(&self) -> DocBuilder<A> {
224 pretty::DocAllocator::softline(&BoxAllocator)
225 }
226
227 fn softline_(&self) -> DocBuilder<A> {
230 pretty::DocAllocator::softline_(&BoxAllocator)
231 }
232
233 fn as_string<U: Display>(&self, data: U) -> DocBuilder<A> {
238 pretty::DocAllocator::as_string(&BoxAllocator, data)
239 }
240
241 fn text<'a>(&self, data: impl Into<Cow<'a, str>>) -> DocBuilder<A> {
246 self.as_string(data.into())
247 }
248
249 fn concat<I>(&self, docs: I) -> DocBuilder<A>
252 where
253 I::Item: ToDocumentOwned<Self, A>,
254 I: IntoIterator,
255 {
256 pretty::DocAllocator::concat(
257 &BoxAllocator,
258 docs.into_iter().map(|doc| doc.to_document_owned(self)),
259 )
260 }
261
262 fn intersperse<I, S>(&self, docs: I, separator: S) -> DocBuilder<A>
267 where
268 I::Item: ToDocumentOwned<Self, A>,
269 I: IntoIterator,
270 S: ToDocumentOwned<Self, A> + Clone,
271 A: Clone,
272 {
273 let separator = separator.to_document_owned(self);
274 pretty::DocAllocator::intersperse(
275 &BoxAllocator,
276 docs.into_iter().map(|doc| doc.to_document_owned(self)),
277 separator,
278 )
279 }
280
281 fn reflow(&self, text: &'static str) -> DocBuilder<A>
284 where
285 A: Clone,
286 {
287 pretty::DocAllocator::reflow(&BoxAllocator, text)
288 }
289}
290
291impl<A: 'static + Clone, P: PrettyAst<A>> PrettyAstExt<A> for P {}
292
293macro_rules! make_cases_macro {
295 (
296 $macro_name:ident,
297 $(
298 $($idents:ident)|* => $target:ident,
299 )*
300 _ => $fallback:ident $(,)?
301 ) => {
302 macro_rules! $macro_name {
303 $(
304 $(
305 ($idents $tt:tt) => { $target!($tt); };
306 )*
307 )*
308 ($anything:ident $tt:tt) => { $fallback!($tt); };
309 }
310 };
311}
312
313macro_rules! skip {
315 ($tt:tt) => {};
316}
317macro_rules! keep {
320 ({$($tt:tt)*}) => { $($tt)* };
321}
322
323make_cases_macro!(method_deny_list,
324 ExprKind | PatKind | TyKind | GuardKind | ImplExprKind | ImplItemKind | TraitItemKind | AttributeKind | DocCommentKind => skip,
325 Signedness | IntSize => skip,
326 ItemQuoteOrigin | ItemQuoteOriginKind | ItemQuoteOriginPosition => skip,
327 ControlFlowKind | LoopState | LoopKind => skip,
328 _ => keep
329);
330
331make_cases_macro!(span_handling,
332 Item | Expr | Pat | Guard | Arm | ImplItem | TraitItem | GenericParam | Attribute | Attribute => keep,
333 _ => skip
334);
335
336pub trait HasContextualSpan: Clone {
343 fn with_span(&self, _span: Span) -> Self;
345
346 fn span(&self) -> Option<Span>;
348}
349
350macro_rules! mk {
353 ($($ty:ident),*) => {
354 pastey::paste! {
355 pub trait PrettyAst<A: 'static + Clone>: Sized + HasContextualSpan {
369 const NAME: &'static str;
372
373 fn emit_diagnostic(&self, kind: hax_types::diagnostics::Kind) {
375 let span = self.span().unwrap_or_else(|| Span::dummy());
376 use crate::ast::diagnostics::{DiagnosticInfo, Context};
377 (DiagnosticInfo {
378 context: Context::Printer(Self::NAME.to_string()),
379 span,
380 kind
381 }).emit()
382 }
383
384 fn todo_document(&self, message: &str, issue_id: Option<u32>) -> DocBuilder<A> {
386 self.emit_diagnostic(hax_types::diagnostics::Kind::Unimplemented {
387 issue_id,
388 details: Some(message.into()),
389 });
390 self.as_string(message)
391 }
392
393 fn unimplemented_method(&self, method: &str, ast: ast::fragment::FragmentRef<'_>) -> DocBuilder<A> {
402 let debug_json = DebugJSON(ast).to_string();
403 self.emit_diagnostic(hax_types::diagnostics::Kind::Unimplemented {
404 issue_id: None,
405 details: Some(format!("The method `{method}` is not implemented in the backend {}. To show the AST fragment that could not be printed, run {debug_json}.", Self::NAME)),
406 });
407 self.text(format!("`{method}` unimpl, {debug_json}", )).parens()
408 }
409
410 $(
411 method_deny_list!($ty{
412 #[doc = "Define how the printer formats a value of this AST type."]
413 #[doc = "Do not call this method directly. Use [`ToDocument::to_document`] instead, so annotations/spans are preserved correctly."]
414 #[deprecated = "Do not call this method directly. Use [`ToDocument::to_document`] instead, so annotations/spans are preserved correctly."]
415 fn [<$ty:snake>](&self, [<$ty:snake>]: &$ty) -> DocBuilder<A> {
416 mk!(@method_body $ty [<$ty:snake>] self [<$ty:snake>])
417 }
418 });
419 )*
420 }
421
422 $(
423 method_deny_list!($ty{
424 impl<A: 'static + Clone, P: PrettyAst<A>> ToDocument<P, A> for $ty {
425 fn to_document(&self, printer: &P) -> DocBuilder<A> {
426 span_handling!($ty{
427 let printer = &(printer.with_span(self.span()));
428 });
429 #[allow(deprecated)]
433 let print = <P as PrettyAst<A>>::[<$ty:snake>];
434 print(printer, self)
435 }
436 }
437 });
438 )*
439 }
440 };
441
442 (@method_body Symbol $meth:ident $self:ident $value:ident) => {
444 $self.as_string($value.to_string())
445 };
446 (@method_body LocalId $meth:ident $self:ident $value:ident) => {
447 $value.0.to_document($self)
448 };
449 (@method_body SpannedTy $meth:ident $self:ident $value:ident) => {
450 $value.ty.to_document($self)
451 };
452 (@method_body $ty:ident $meth:ident $self:ident $value:ident) => {
453 $self.unimplemented_method(stringify!($meth), ast::fragment::FragmentRef::from($meth))
454 };
455}
456
457#[hax_rust_engine_macros::replace(AstNodes => include(VisitableAstNodes))]
458mk!(GlobalId, AstNodes);