syn_serde/
expr.rs

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