Skip to main content

syn_serde/
expr.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3use alloc::{boxed::Box, vec::Vec};
4
5use super::*;
6pub use crate::{
7    ast_enum::{Expr, Member, RangeLimits},
8    ast_struct::{
9        ExprArray, ExprAssign, ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCall,
10        ExprCast, ExprClosure, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprGroup, ExprIf,
11        ExprIndex, ExprInfer, ExprLet, ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall,
12        ExprParen, ExprPath, ExprRange, ExprReference, ExprRepeat, ExprReturn, ExprStruct, ExprTry,
13        ExprTryBlock, ExprTuple, ExprUnary, ExprUnsafe, ExprWhile, ExprYield, FieldValue, Index,
14        Label,
15    },
16};
17
18ast_struct! {
19    /// An adapter for [`struct@syn::Arm`].
20    pub struct Arm {
21        #[serde(default, skip_serializing_if = "Vec::is_empty")]
22        pub(crate) attrs: Vec<Attribute>,
23        pub(crate) pat: Pat,
24        #[serde(default, skip_serializing_if = "Option::is_none")]
25        pub(crate) guard: Option<Box<Expr>>,
26        pub(crate) body: Box<Expr>,
27        // #[serde(default, skip_serializing_if = "not")]
28        // pub(crate) comma: bool,
29    }
30}
31
32// https://github.com/dtolnay/syn/blob/2.0.15/src/expr.rs#L913
33pub(crate) fn requires_terminator(expr: &Expr) -> bool {
34    // see https://github.com/rust-lang/rust/blob/9a19e7604/compiler/rustc_ast/src/util/classify.rs#L7-L26
35    match expr {
36        Expr::If(_)
37        | Expr::Match(_)
38        | Expr::Block(_) | Expr::Unsafe(_) // both under ExprKind::Block in rustc
39        | Expr::While(_)
40        | Expr::Loop(_)
41        | Expr::ForLoop(_)
42        | Expr::TryBlock(_)
43        | Expr::Const(_) => false,
44        Expr::Array(_)
45        | Expr::Assign(_)
46        | Expr::Async(_)
47        | Expr::Await(_)
48        | Expr::Binary(_)
49        | Expr::Break(_)
50        | Expr::Call(_)
51        | Expr::Cast(_)
52        | Expr::Closure(_)
53        | Expr::Continue(_)
54        | Expr::Field(_)
55        | Expr::Group(_)
56        | Expr::Index(_)
57        | Expr::Infer(_)
58        | Expr::Let(_)
59        | Expr::Lit(_)
60        | Expr::Macro(_)
61        | Expr::MethodCall(_)
62        | Expr::Paren(_)
63        | Expr::Path(_)
64        | Expr::Range(_)
65        | Expr::Reference(_)
66        | Expr::Repeat(_)
67        | Expr::Return(_)
68        | Expr::Struct(_)
69        | Expr::Try(_)
70        | Expr::Tuple(_)
71        | Expr::Unary(_)
72        | Expr::Yield(_)
73        | Expr::Verbatim(_) => true
74    }
75}
76
77mod convert {
78    use super::*;
79
80    // ExprMatch
81    syn_trait_impl!(syn::ExprMatch);
82    fn from_syn_arms(other: &[syn::Arm]) -> Vec<Arm> {
83        let last = other.len().saturating_sub(1);
84        other
85            .iter()
86            .enumerate()
87            .map(|(i, other)| {
88                let body = other.body.map_into();
89                if i < last && requires_terminator(&body) {
90                    assert!(other.comma.is_some(), "expected `,`");
91                }
92
93                Arm {
94                    attrs: other.attrs.map_into(),
95                    pat: other.pat.ref_into(),
96                    guard: other.guard.ref_map(|(_, x)| x.map_into()),
97                    body,
98                }
99            })
100            .collect()
101    }
102    impl From<&syn::ExprMatch> for ExprMatch {
103        fn from(other: &syn::ExprMatch) -> Self {
104            Self {
105                attrs: other.attrs.map_into(),
106                expr: other.expr.map_into(),
107                arms: from_syn_arms(&other.arms),
108            }
109        }
110    }
111    impl From<&ExprMatch> for syn::ExprMatch {
112        fn from(other: &ExprMatch) -> Self {
113            Self {
114                attrs: other.attrs.map_into(),
115                match_token: default(),
116                expr: other.expr.map_into(),
117                brace_token: default(),
118                arms: other.arms.map_into(),
119            }
120        }
121    }
122
123    // Arm
124    syn_trait_impl!(syn::Arm);
125    impl From<&syn::Arm> for Arm {
126        fn from(other: &syn::Arm) -> Self {
127            let body = other.body.map_into();
128            if requires_terminator(&body) {
129                assert!(other.comma.is_some(), "expected `,`");
130            }
131
132            Self {
133                attrs: other.attrs.map_into(),
134                pat: other.pat.ref_into(),
135                guard: other.guard.ref_map(|(_, x)| x.map_into()),
136                body,
137            }
138        }
139    }
140    impl From<&Arm> for syn::Arm {
141        fn from(other: &Arm) -> Self {
142            Self {
143                attrs: other.attrs.map_into(),
144                pat: other.pat.ref_into(),
145                guard: other.guard.ref_map(|x| (default(), x.map_into())),
146                fat_arrow_token: default(),
147                body: other.body.map_into(),
148                comma: default_or_none(requires_terminator(&other.body)),
149            }
150        }
151    }
152}