Skip to main content

rustc_ast_ir/
visit.rs

1use core::ops::ControlFlow;
2
3/// Similar to the `Try` trait, but also implemented for `()`.
4pub trait VisitorResult {
5    type Residual;
6    fn output() -> Self;
7    fn from_residual(residual: Self::Residual) -> Self;
8    fn from_branch(b: ControlFlow<Self::Residual>) -> Self;
9    fn branch(self) -> ControlFlow<Self::Residual>;
10}
11
12impl VisitorResult for () {
13    #[cfg(feature = "nightly")]
14    type Residual = !;
15
16    #[cfg(not(feature = "nightly"))]
17    type Residual = core::convert::Infallible;
18
19    fn output() -> Self {}
20    fn from_residual(_: Self::Residual) -> Self {}
21    fn from_branch(_: ControlFlow<Self::Residual>) -> Self {}
22    fn branch(self) -> ControlFlow<Self::Residual> {
23        ControlFlow::Continue(())
24    }
25}
26
27impl<T> VisitorResult for ControlFlow<T> {
28    type Residual = T;
29
30    fn output() -> Self {
31        ControlFlow::Continue(())
32    }
33    fn from_residual(residual: Self::Residual) -> Self {
34        ControlFlow::Break(residual)
35    }
36    fn from_branch(b: Self) -> Self {
37        b
38    }
39    fn branch(self) -> Self {
40        self
41    }
42}
43
44impl<E> VisitorResult for Result<(), E> {
45    type Residual = E;
46
47    fn output() -> Self {
48        Ok(())
49    }
50    fn from_residual(residual: Self::Residual) -> Self {
51        Err(residual)
52    }
53    fn from_branch(b: ControlFlow<Self::Residual>) -> Self {
54        match b {
55            ControlFlow::Continue(()) => Ok(()),
56            ControlFlow::Break(e) => Err(e),
57        }
58    }
59    fn branch(self) -> ControlFlow<Self::Residual> {
60        match self {
61            Ok(()) => ControlFlow::Continue(()),
62            Err(e) => ControlFlow::Break(e),
63        }
64    }
65}
66
67#[macro_export]
68macro_rules! try_visit {
69    ($e:expr) => {
70        match $crate::visit::VisitorResult::branch($e) {
71            core::ops::ControlFlow::Continue(()) => (),
72            #[allow(unreachable_code)]
73            core::ops::ControlFlow::Break(r) => {
74                return $crate::visit::VisitorResult::from_residual(r);
75            }
76        }
77    };
78}
79
80#[macro_export]
81macro_rules! visit_opt {
82    ($visitor: expr, $method: ident, $opt: expr $(, $($extra_args: expr),* )?) => {
83        if let Some(x) = $opt {
84            $crate::try_visit!($visitor.$method(x $(, $($extra_args,)* )?));
85        }
86    }
87}
88
89#[macro_export]
90macro_rules! walk_list {
91    ($visitor: expr, $method: ident, $list: expr $(, $($extra_args: expr),* )?) => {
92        for elem in $list {
93            $crate::try_visit!($visitor.$method(elem $(, $($extra_args,)* )?));
94        }
95    }
96}
97
98#[macro_export]
99macro_rules! walk_visitable_list {
100    ($visitor: expr, $list: expr $(, $($extra_args: expr),* )?) => {
101        for elem in $list {
102            $crate::try_visit!(elem.visit_with($visitor $(, $($extra_args,)* )?));
103        }
104    }
105}