@@ -109,14 +109,24 @@ impl<'a, 'b, A: 'a + Clone, P: PrettyAst<'a, 'b, A>, T: 'b + serde::Serialize> P
109
109
110
110
#[ macro_export]
111
111
/// Similar to [`std::todo`], but returns a document instead of panicking with a message.
112
+ /// Also, `todo_document!` accepts a `issue #123` prefix to point to a specific issue number.
112
113
macro_rules! todo_document {
114
+ ( $allocator: ident, issue $issue: literal) => {
115
+ { return $allocator. todo_document( & format!( "TODO_LINE_{}" , std:: line!( ) ) , Some ( $issue) ) ; }
116
+ } ;
117
+ ( $allocator: ident, issue $issue: literal, $( $tt: tt) * ) => {
118
+ {
119
+ let message = format!( $( $tt) * ) ;
120
+ return $allocator. todo_document( & message, Some ( $issue) ) ;
121
+ }
122
+ } ;
113
123
( $allocator: ident, ) => {
114
- { return $allocator. todo_document( & format!( "TODO_LINE_{}" , std:: line!( ) ) ) ; }
124
+ { return $allocator. todo_document( & format!( "TODO_LINE_{}" , std:: line!( ) ) , None ) ; }
115
125
} ;
116
126
( $allocator: ident, $( $tt: tt) * ) => {
117
127
{
118
128
let message = format!( $( $tt) * ) ;
119
- return $allocator. todo_document( & message) ;
129
+ return $allocator. todo_document( & message, None ) ;
120
130
}
121
131
} ;
122
132
}
@@ -157,6 +167,7 @@ macro_rules! install_pretty_helpers {
157
167
$crate:: printer:: pretty_ast:: install_pretty_helpers!(
158
168
@$allocator,
159
169
#[ doc = :: std:: concat!( "Proxy macro for [`" , stringify!( $crate) , "::printer::pretty_ast::todo_document`] that automatically uses `" , stringify!( $allocator) , "` as allocator." ) ]
170
+ #[ doc = :: std:: concat!( r#"Example: `disambiguated_todo!("Error message")` or `disambiguated_todo!(issue #123, "Error message with issue attached")`."# ) ]
160
171
disambiguated_todo{ $crate:: printer:: pretty_ast:: todo_document!} ,
161
172
#[ doc = :: std:: concat!( "Proxy macro for [`pretty::docs`] that automatically uses `" , stringify!( $allocator) , "` as allocator." ) ]
162
173
docs{ pretty:: docs!} ,
@@ -203,6 +214,28 @@ macro_rules! install_pretty_helpers {
203
214
}
204
215
pub use install_pretty_helpers;
205
216
217
+ // This module tracks a span information via a global mutex, because our
218
+ // printers cannot really carry information in a nice way. See issue
219
+ // https://github.com/cryspen/hax/issues/1667. Once addressed this can go away.
220
+ mod default_global_span_context {
221
+ use super :: Span ;
222
+
223
+ use std:: sync:: { LazyLock , Mutex } ;
224
+ static STATE : LazyLock < Mutex < Option < Span > > > = LazyLock :: new ( || Mutex :: new ( None ) ) ;
225
+
226
+ pub ( super ) fn with_span < T > ( span : Span , action : impl Fn ( ) -> T ) -> T {
227
+ let previous_span = STATE . lock ( ) . unwrap ( ) . clone ( ) ;
228
+ * STATE . lock ( ) . unwrap ( ) = Some ( span) ;
229
+ let result = action ( ) ;
230
+ * STATE . lock ( ) . unwrap ( ) = previous_span;
231
+ result
232
+ }
233
+
234
+ pub ( super ) fn get_ambiant_span ( ) -> Option < Span > {
235
+ STATE . lock ( ) . unwrap ( ) . clone ( )
236
+ }
237
+ }
238
+
206
239
macro_rules! mk {
207
240
( $( $ty: ident) ,* ) => {
208
241
pastey:: paste! {
@@ -237,10 +270,33 @@ macro_rules! mk {
237
270
/// that implicitely use `self` as allocator. Take a look at a
238
271
/// printer in the [`backends`] module for an example.
239
272
pub trait PrettyAst <' a, ' b, A : ' a + Clone >: DocAllocator <' a, A > + Sized {
273
+ /// A name for this instance of `PrettyAst`.
274
+ /// Useful for diagnostics and debugging.
275
+ const NAME : & ' static str ;
276
+
277
+ /// Emit a diagnostic with proper context and span.
278
+ fn emit_diagnostic( & ' a self , kind: hax_types:: diagnostics:: Kind ) {
279
+ let span = default_global_span_context:: get_ambiant_span( ) . unwrap_or_else( || Span :: dummy( ) ) ;
280
+ use crate :: printer:: pretty_ast:: diagnostics:: { DiagnosticInfo , Context } ;
281
+ ( DiagnosticInfo {
282
+ context: Context :: Printer ( Self :: NAME . to_string( ) ) ,
283
+ span,
284
+ kind
285
+ } ) . emit( )
286
+ }
287
+
240
288
/// Produce a non-panicking placeholder document. In general, prefer the use of the helper macro [`todo_document!`].
241
- fn todo_document( & ' a self , message: & str ) -> DocBuilder <' a, Self , A > {
289
+ fn todo_document( & ' a self , message: & str , issue_id: Option <u32 >) -> DocBuilder <' a, Self , A > {
290
+ self . emit_diagnostic( hax_types:: diagnostics:: Kind :: Unimplemented {
291
+ issue_id,
292
+ details: Some ( message. into( ) ) ,
293
+ } ) ;
242
294
self . as_string( message)
243
295
}
296
+ /// Execute an action with a span hint. Useful for errors.
297
+ fn with_span<T >( & self , span: Span , action: impl Fn ( & Self ) -> T ) -> T {
298
+ default_global_span_context:: with_span( span, || action( self ) )
299
+ }
244
300
/// Produce a structured error document for an unimplemented
245
301
/// method.
246
302
///
@@ -250,7 +306,12 @@ macro_rules! mk {
250
306
/// of text that includes the method name and a JSON handle for
251
307
/// the AST fragment (via [`DebugJSON`]).
252
308
fn unimplemented_method( & ' a self , method: & str , ast: ast:: fragment:: FragmentRef <' _>) -> DocBuilder <' a, Self , A > {
253
- self . text( format!( "`{method}` unimpl, {}" , DebugJSON ( ast) ) ) . parens( )
309
+ let debug_json = DebugJSON ( ast) . to_string( ) ;
310
+ self . emit_diagnostic( hax_types:: diagnostics:: Kind :: Unimplemented {
311
+ issue_id: None ,
312
+ 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 ) ) ,
313
+ } ) ;
314
+ self . text( format!( "`{method}` unimpl, {debug_json}" , ) ) . parens( )
254
315
}
255
316
$(
256
317
#[ doc = "Define how the printer formats a value of this AST type." ]
0 commit comments