1use super::*;
19use derive_generic_visitor::*;
20use hax_lib_macros_types::AttrPayload;
21
22pub mod wrappers {
23 use std::ops::Deref;
30
31 use super::{infallible::AstVisitable as AstVisitableInfallible, *};
32 use diagnostics::*;
33
34 pub struct SpanWrapper<'a, V>(pub &'a mut V);
38
39 impl<'a, V: HasSpan> SpanWrapper<'a, V> {
40 fn spanned_action<T: Deref, U>(
44 &mut self,
45 ast_fragment: T,
46 action: impl Fn(&mut Self, T) -> U,
47 ) -> U
48 where
49 T::Target: HasSpan,
50 {
51 let span_before = self.0.span();
52 *self.0.span_mut() = ast_fragment.span();
53 let result = action(self, ast_fragment);
55 *self.0.span_mut() = span_before;
56 result
57 }
58 }
59
60 impl<'a, V: AstVisitorMut + HasSpan> AstVisitorMut for SpanWrapper<'a, V> {
61 fn visit_inner<T>(&mut self, x: &mut T)
62 where
63 T: AstVisitableInfallible,
64 T: for<'s> DriveMut<'s, AstVisitableInfallibleWrapper<Self>>,
65 {
66 x.drive_map(self.0)
67 }
68 fn visit_item(&mut self, x: &mut Item) {
69 self.spanned_action(x, Self::visit_inner)
70 }
71 fn visit_expr(&mut self, x: &mut Expr) {
72 self.spanned_action(x, Self::visit_inner)
73 }
74 fn visit_pat(&mut self, x: &mut Pat) {
75 self.spanned_action(x, Self::visit_inner)
76 }
77 fn visit_guard(&mut self, x: &mut Guard) {
78 self.spanned_action(x, Self::visit_inner)
79 }
80 fn visit_arm(&mut self, x: &mut Arm) {
81 self.spanned_action(x, Self::visit_inner)
82 }
83 fn visit_impl_item(&mut self, x: &mut ImplItem) {
84 self.spanned_action(x, Self::visit_inner)
85 }
86 fn visit_trait_item(&mut self, x: &mut TraitItem) {
87 self.spanned_action(x, Self::visit_inner)
88 }
89 fn visit_generic_param(&mut self, x: &mut GenericParam) {
90 self.spanned_action(x, Self::visit_inner)
91 }
92 fn visit_attribute(&mut self, x: &mut Attribute) {
93 self.spanned_action(x, Self::visit_inner)
94 }
95 fn visit_spanned_ty(&mut self, x: &mut SpannedTy) {
96 self.spanned_action(x, Self::visit_inner)
97 }
98 }
99
100 pub struct ErrorWrapper<'a, V>(pub &'a mut V);
105
106 #[derive(Default)]
109 pub struct ErrorVault(Vec<Diagnostic>);
110 impl ErrorVault {
111 fn add(&mut self, diagnostic: Diagnostic) {
112 self.0.push(diagnostic);
113 }
114 }
115
116 pub struct ErrorHandlingState(pub Span, pub ErrorVault);
119 impl Default for ErrorHandlingState {
120 fn default() -> Self {
121 Self(Span::dummy(), Default::default())
122 }
123 }
124
125 #[macro_export]
126 macro_rules! setup_error_handling_impl {
128 () => {
129 fn visit<T: $crate::ast::visitors::AstVisitableInfallible>(&mut self, x: &mut T) {
130 $crate::ast::visitors::wrappers::SpanWrapper(
131 &mut $crate::ast::visitors::wrappers::ErrorWrapper(self),
132 )
133 .visit(x)
134 }
135 };
136 }
137 pub use setup_error_handling_impl;
138
139 pub trait VisitorWithContext {
141 fn context(&self) -> Context;
143 }
144
145 impl<T: HasSpan> HasSpan for ErrorWrapper<'_, T> {
146 fn span(&self) -> Span {
147 self.0.span()
148 }
149
150 fn span_mut(&mut self) -> &mut Span {
151 self.0.span_mut()
152 }
153 }
154
155 pub trait VisitorWithErrors: HasSpan + VisitorWithContext {
161 fn error_vault(&mut self) -> &mut ErrorVault;
163 fn error(&mut self, node: impl Into<Fragment>, kind: DiagnosticInfoKind) {
165 let context = self.context();
166 let span = self.span();
167 self.error_vault().add(Diagnostic::new(
168 node,
169 DiagnosticInfo {
170 context,
171 span,
172 kind,
173 },
174 ));
175 }
176 }
177
178 impl<'a, V: VisitorWithErrors> ErrorWrapper<'a, V> {
179 fn error_handled_action<
180 T: FallibleAstNode + Clone + std::fmt::Debug + Into<Fragment>,
181 U,
182 >(
183 &mut self,
184 x: &mut T,
185 action: impl Fn(&mut Self, &mut T) -> U,
186 ) -> U {
187 let diagnostics_snapshot = self.0.error_vault().0.clone();
188 self.0.error_vault().0.clear();
189 let result = action(self, x);
190 let diagnostics: Vec<_> = self.0.error_vault().0.drain(..).collect();
191 if !diagnostics.is_empty() {
192 x.set_error(ErrorNode {
193 fragment: Box::new(x.clone().into()),
194 diagnostics,
195 });
196 }
197 self.0.error_vault().0 = diagnostics_snapshot;
198 result
199 }
200 }
201
202 impl<'a, V: AstVisitorMut + VisitorWithErrors> AstVisitorMut for ErrorWrapper<'a, V> {
203 fn visit_inner<T>(&mut self, x: &mut T)
204 where
205 T: AstVisitableInfallible,
206 T: for<'s> DriveMut<'s, AstVisitableInfallibleWrapper<Self>>,
207 {
208 x.drive_map(self.0)
209 }
210 fn visit_item(&mut self, x: &mut Item) {
211 self.error_handled_action(x, Self::visit_inner)
212 }
213 fn visit_pat(&mut self, x: &mut Pat) {
214 self.error_handled_action(x, Self::visit_inner)
215 }
216 fn visit_expr(&mut self, x: &mut Expr) {
217 self.error_handled_action(x, Self::visit_inner)
218 }
219 fn visit_ty(&mut self, x: &mut Ty) {
220 self.error_handled_action(x, Self::visit_inner)
221 }
222 }
223}
224
225#[hax_rust_engine_macros::replace(AstNodes => include(VisitableAstNodes))]
226mod replaced {
227 use super::*;
228 pub mod infallible {
229 use super::*;
230
231 #[visitable_group(
232 visitor(drive_map(
233 &mut AstVisitorMut
254 ), infallible),
255 visitor(drive(
256 &AstVisitor
258 ), infallible),
259 skip(
260 String, bool, char, hax_frontend_exporter::Span,
261 for<T> crate::interning::Interned<T>,
262 ),
263 drive(
264 for<T: AstVisitable> Box<T>, for<T: AstVisitable> Option<T>, for<T: AstVisitable> Vec<T>,
265 for<A: AstVisitable, B: AstVisitable> (A, B),
266 for<A: AstVisitable, B: AstVisitable, C: AstVisitable> (A, B, C),
267 usize
268 ),
269 override(AstNodes),
270 override_skip(
271 Span, Fragment, GlobalId, Diagnostic, AttrPayload,
272 ),
273 )]
274 pub trait AstVisitable {}
276 }
277
278 #[allow(missing_docs)]
279 pub mod fallible {
280 use super::*;
281
282 #[visitable_group(
283 visitor(drive(
284 &AstEarlyExitVisitor
286 )),
287 visitor(drive_mut(
288 &mut AstEarlyExitVisitorMut
290 )),
291 skip(
292 String, bool, char, hax_frontend_exporter::Span,
293 for<T> crate::interning::Interned<T>,
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, AttrPayload,
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}