1use super::*;
19use derive_generic_visitor::*;
20
21pub mod wrappers {
22 use std::ops::Deref;
29
30 use super::{infallible::AstVisitable as AstVisitableInfallible, *};
31 use diagnostics::*;
32
33 pub struct SpanWrapper<'a, V>(pub &'a mut V);
37
38 impl<'a, V: HasSpan> SpanWrapper<'a, V> {
39 fn spanned_action<T: Deref, U>(
43 &mut self,
44 ast_fragment: T,
45 action: impl Fn(&mut Self, T) -> U,
46 ) -> U
47 where
48 T::Target: HasSpan,
49 {
50 let span_before = self.0.span();
51 *self.0.span_mut() = ast_fragment.span();
52 let result = action(self, ast_fragment);
54 *self.0.span_mut() = span_before;
55 result
56 }
57 }
58
59 impl<'a, V: AstVisitorMut + HasSpan> AstVisitorMut for SpanWrapper<'a, V> {
60 fn visit_inner<T>(&mut self, x: &mut T)
61 where
62 T: AstVisitableInfallible,
63 T: for<'s> DriveMut<'s, AstVisitableInfallibleWrapper<Self>>,
64 {
65 x.drive_map(self.0)
66 }
67 fn visit_item(&mut self, x: &mut Item) {
68 self.spanned_action(x, Self::visit_inner)
69 }
70 fn visit_expr(&mut self, x: &mut Expr) {
71 self.spanned_action(x, Self::visit_inner)
72 }
73 fn visit_pat(&mut self, x: &mut Pat) {
74 self.spanned_action(x, Self::visit_inner)
75 }
76 fn visit_guard(&mut self, x: &mut Guard) {
77 self.spanned_action(x, Self::visit_inner)
78 }
79 fn visit_arm(&mut self, x: &mut Arm) {
80 self.spanned_action(x, Self::visit_inner)
81 }
82 fn visit_impl_item(&mut self, x: &mut ImplItem) {
83 self.spanned_action(x, Self::visit_inner)
84 }
85 fn visit_trait_item(&mut self, x: &mut TraitItem) {
86 self.spanned_action(x, Self::visit_inner)
87 }
88 fn visit_generic_param(&mut self, x: &mut GenericParam) {
89 self.spanned_action(x, Self::visit_inner)
90 }
91 fn visit_attribute(&mut self, x: &mut Attribute) {
92 self.spanned_action(x, Self::visit_inner)
93 }
94 fn visit_spanned_ty(&mut self, x: &mut SpannedTy) {
95 self.spanned_action(x, Self::visit_inner)
96 }
97 }
98
99 pub struct ErrorWrapper<'a, V>(pub &'a mut V);
104
105 #[derive(Default)]
108 pub struct ErrorVault(Vec<Diagnostic>);
109 impl ErrorVault {
110 fn add(&mut self, diagnostic: Diagnostic) {
111 self.0.push(diagnostic);
112 }
113 }
114
115 pub struct ErrorHandlingState(pub Span, pub ErrorVault);
118 impl Default for ErrorHandlingState {
119 fn default() -> Self {
120 Self(Span::dummy(), Default::default())
121 }
122 }
123
124 #[macro_export]
125 macro_rules! setup_error_handling_impl {
127 () => {
128 fn visit<'a, T: $crate::ast::visitors::AstVisitableInfallible>(
129 &'a mut self,
130 x: &mut T,
131 ) {
132 $crate::ast::visitors::wrappers::SpanWrapper(
133 &mut $crate::ast::visitors::wrappers::ErrorWrapper(self),
134 )
135 .visit(x)
136 }
137 };
138 }
139 pub use setup_error_handling_impl;
140
141 pub trait VisitorWithContext {
143 fn context(&self) -> Context;
145 }
146
147 impl<T: HasSpan> HasSpan for ErrorWrapper<'_, T> {
148 fn span(&self) -> Span {
149 self.0.span()
150 }
151
152 fn span_mut(&mut self) -> &mut Span {
153 self.0.span_mut()
154 }
155 }
156
157 pub trait VisitorWithErrors: HasSpan + VisitorWithContext {
163 fn error_vault(&mut self) -> &mut ErrorVault;
165 fn error(&mut self, node: impl Into<Fragment>, kind: DiagnosticInfoKind) {
167 let context = self.context();
168 let span = self.span();
169 self.error_vault().add(Diagnostic::new(
170 node,
171 DiagnosticInfo {
172 context,
173 span,
174 kind,
175 },
176 ));
177 }
178 }
179
180 impl<'a, V: VisitorWithErrors> ErrorWrapper<'a, V> {
181 fn error_handled_action<
182 T: FallibleAstNode + Clone + std::fmt::Debug + Into<Fragment>,
183 U,
184 >(
185 &mut self,
186 x: &mut T,
187 action: impl Fn(&mut Self, &mut T) -> U,
188 ) -> U {
189 let diagnostics_snapshot = self.0.error_vault().0.clone();
190 self.0.error_vault().0.clear();
191 let result = action(self, x);
192 let diagnostics: Vec<_> = self.0.error_vault().0.drain(..).collect();
193 if !diagnostics.is_empty() {
194 x.set_error(ErrorNode {
195 fragment: Box::new(x.clone().into()),
196 diagnostics,
197 });
198 }
199 self.0.error_vault().0 = diagnostics_snapshot;
200 result
201 }
202 }
203
204 impl<'a, V: AstVisitorMut + VisitorWithErrors> AstVisitorMut for ErrorWrapper<'a, V> {
205 fn visit_inner<T>(&mut self, x: &mut T)
206 where
207 T: AstVisitableInfallible,
208 T: for<'s> DriveMut<'s, AstVisitableInfallibleWrapper<Self>>,
209 {
210 x.drive_map(self.0)
211 }
212 fn visit_item(&mut self, x: &mut Item) {
213 self.error_handled_action(x, Self::visit_inner)
214 }
215 fn visit_pat(&mut self, x: &mut Pat) {
216 self.error_handled_action(x, Self::visit_inner)
217 }
218 fn visit_expr(&mut self, x: &mut Expr) {
219 self.error_handled_action(x, Self::visit_inner)
220 }
221 fn visit_ty(&mut self, x: &mut Ty) {
222 self.error_handled_action(x, Self::visit_inner)
223 }
224 }
225}
226
227#[hax_rust_engine_macros::replace(AstNodes => include(VisitableAstNodes))]
228mod replaced {
229 use super::*;
230 pub mod infallible {
231 use super::*;
232
233 #[visitable_group(
234 visitor(drive_map(
235 &mut AstVisitorMut
256 ), infallible),
257 visitor(drive(
258 &AstVisitor
260 ), infallible),
261 skip(
262 String, bool, char, hax_frontend_exporter::Span,
263 ),
264 drive(
265 for<T: AstVisitable> Box<T>, for<T: AstVisitable> Option<T>, for<T: AstVisitable> Vec<T>,
266 for<A: AstVisitable, B: AstVisitable> (A, B),
267 for<A: AstVisitable, B: AstVisitable, C: AstVisitable> (A, B, C),
268 usize
269 ),
270 override(AstNodes),
271 override_skip(
272 Span, Fragment, GlobalId, Diagnostic,
273 ),
274 )]
275 pub trait AstVisitable {}
277 }
278
279 #[allow(missing_docs)]
280 pub mod fallible {
281 use super::*;
282
283 #[visitable_group(
284 visitor(drive(
285 &AstEarlyExitVisitor
287 )),
288 visitor(drive_mut(
289 &mut AstEarlyExitVisitorMut
291 )),
292 skip(
293 String, bool, char, hax_frontend_exporter::Span,
294 ),
295 drive(
296 for<T: AstVisitable> Box<T>, for<T: AstVisitable> Option<T>, for<T: AstVisitable> Vec<T>,
297 for<A: AstVisitable, B: AstVisitable> (A, B),
298 for<A: AstVisitable, B: AstVisitable, C: AstVisitable> (A, B, C),
299 usize
300 ),
301 override(AstNodes),
302 override_skip(
303 Span, Fragment, GlobalId, Diagnostic,
304 ),
305 )]
306 pub trait AstVisitable {}
308 }
309
310 pub mod dyn_compatible {
312 use super::*;
313
314 macro_rules! derive_erased_ast_visitors {
315 ({$($attrs:tt)*}, $name: ident, $helper: ident, $($ty:ty),*) => {
316 $($attrs)*
317 pub trait $name<'a>: $($helper<'a, $ty> + )* {}
318 };
319 }
320
321 macro_rules! render_path {
322 ($head:ident) => {stringify!($head)};
323 ($head:ident $(::$tail:ident)*) => {
324 concat!(stringify!($head), "::", render_path!($($tail)::*))
325 };
326 }
327
328 macro_rules! make_dyn_compatible {
329 ($($visitable_trait:ident)::*, $($visitor_trait:ident)::*, $helper_name: ident, $name: ident, mut:{$($mut:tt)?}, super:{$($super:ident)::*}, $ret:ty) => {
330 #[doc = concat!("A [dyn-compatible](https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility) trait similar to [`", render_path!($($visitor_trait)::*),"`].")]
331 #[doc = concat!("This trait provides one `visit` method to visit a given type `T` with a given visitor.")]
332 pub trait $helper_name<'a, T: ?Sized>: $($super)::* {
333 fn visit(&mut self, _: &'a $($mut)? T) -> $ret;
335 }
336
337 impl<'a, T: $($visitable_trait)::*, V: $($visitor_trait)::*> $helper_name<'a, T> for V {
338 fn visit(&mut self, e: &'a $($mut)? T) -> $ret {
339 <Self as $($visitor_trait)::*>::visit(self, e)
340 }
341 }
342 derive_erased_ast_visitors!({
343 #[doc = concat!("A [dyn-compatible](https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility) trait similar to [`", render_path!($($visitor_trait)::*),"`].")]
344 #[doc = concat!("This trait is empty, but it implies a super bound for every type in the AST, so that you can use [`", stringify!($helper_name), "::visit", "`] with the entire AST.")]
345 }, $name, $helper_name, AstNodes);
346
347 impl<'a, V: $($visitor_trait)::*> $name<'a> for V {}
348 };
349 }
350
351 make_dyn_compatible!(
352 infallible::AstVisitable,
353 infallible::AstVisitorMut,
354 AstVisitableMut,
355 AstVisitorMut,
356 mut:{mut},
357 super:{},
358 ()
359 );
360 make_dyn_compatible!(
361 infallible::AstVisitable,
362 infallible::AstVisitor,
363 AstVisitable,
364 AstVisitor,
365 mut:{},
366 super:{},
367 ()
368 );
369
370 make_dyn_compatible!(
371 fallible::AstVisitable,
372 fallible::AstEarlyExitVisitorMut,
373 AstEarlyExitVisitableMut,
374 AstEarlyExitVisitorMut,
375 mut:{mut},
376 super:{Visitor},
377 ControlFlow<<Self as Visitor>::Break>
378 );
379 make_dyn_compatible!(
380 fallible::AstVisitable,
381 fallible::AstEarlyExitVisitor,
382 AstEarlyExitVisitable,
383 AstEarlyExitVisitor,
384 mut:{},
385 super:{Visitor},
386 ControlFlow<<Self as Visitor>::Break>
387 );
388 }
389}
390
391pub use replaced::dyn_compatible;
392use replaced::{fallible, infallible};
393
394pub use fallible::{
395 AstEarlyExitVisitor, AstEarlyExitVisitorMut, AstVisitable as AstVisitableFallible,
396 AstVisitableWrapper,
397};
398pub use hax_rust_engine_macros::setup_error_handling_struct;
399pub use infallible::{
400 AstVisitable as AstVisitableInfallible, AstVisitableInfallibleWrapper, AstVisitor,
401 AstVisitorMut,
402};
403pub use wrappers::{VisitorWithContext, VisitorWithErrors, setup_error_handling_impl};
404
405#[test]
406fn double_literals_in_ast() {
407 use crate::ast::diagnostics::*;
408
409 #[setup_error_handling_struct]
410 #[derive(Default)]
411 struct DoubleU8Literals;
412
413 impl VisitorWithContext for DoubleU8Literals {
414 fn context(&self) -> Context {
415 Context::Import
416 }
417 }
418
419 impl AstVisitorMut for DoubleU8Literals {
420 setup_error_handling_impl!();
421
422 fn visit_literal(&mut self, x: &mut Literal) {
423 let Literal::Int { value, .. } = x else {
424 return;
425 };
426 let Ok(n): Result<u8, _> = str::parse(value) else {
427 return self.error(
428 x.clone(),
429 DiagnosticInfoKind::AssertionFailure {
430 details: "Bad literal".into(),
431 },
432 );
433 };
434 let n = (n as u16) * 2;
435 if n >= u8::MAX as u16 {
436 return self.error(
437 x.clone(),
438 DiagnosticInfoKind::AssertionFailure {
439 details: "Literal too big".into(),
440 },
441 );
442 }
443 *value = Symbol::new(&format!("{}", n));
444 }
445 }
446
447 let int_kind = IntKind {
449 size: IntSize::S8,
450 signedness: Signedness::Signed,
451 };
452 let mk_lit = |n: isize| Literal::Int {
453 value: Symbol::new(&format!("{}", n)),
454 negative: false,
455 kind: int_kind.clone(),
456 };
457 let meta = Metadata {
458 span: Span::dummy(),
459 attributes: vec![],
460 };
461 let mk_lit_expr = |n| Expr {
462 kind: Box::new(ExprKind::Literal(mk_lit(n))),
463 ty: Ty(Box::new(TyKind::Primitive(PrimitiveTy::Int(
464 int_kind.clone(),
465 )))),
466 meta: meta.clone(),
467 };
468 let mk_array = |exprs| Expr {
469 kind: Box::new(ExprKind::Array(exprs)),
470 ty: Ty(Box::new(TyKind::RawPointer)), meta: meta.clone(),
472 };
473 let mut lit_expr_200 = mk_lit_expr(200);
474
475 let mut e = mk_array(vec![
477 mk_lit_expr(50),
478 mk_lit_expr(100),
479 lit_expr_200.clone(),
480 ]);
481
482 DoubleU8Literals::default().visit(&mut e);
484
485 lit_expr_200.set_error(ErrorNode {
487 fragment: Box::new(lit_expr_200.clone().into()),
488 diagnostics: vec![Diagnostic::new(
489 mk_lit(200),
490 DiagnosticInfo {
491 span: lit_expr_200.span(),
492 context: Context::Import,
493 kind: DiagnosticInfoKind::AssertionFailure {
494 details: "Literal too big".into(),
495 },
496 },
497 )],
498 });
499
500 assert_eq!(
502 e,
503 mk_array(vec![mk_lit_expr(100), mk_lit_expr(200), lit_expr_200])
504 );
505}