bon_macros/parsing/
const_.rs

1use super::reject_attrs;
2use crate::util::prelude::*;
3
4/// At the time of this writing `Rust` doesn't support calling closures in `const`
5/// contexts. This doesn't compile:
6///
7/// ```compile_fail
8/// const { (|| {})() }
9/// ```
10///
11/// However, we need this to work because we need to wrap user-supplied expressions
12/// with closures. It gives us a guarantee that the user-supplied expression can't
13/// break out of the surrounding scope via a `return`, `break`, or `continue` inside
14/// of it.
15///
16/// Therefore, we use this function to allow very simple expressions to still be
17/// embedded in the surrounding context while recommending to delegate to a function
18/// call if more complex expression is required.
19pub(crate) fn require_embeddable_const_expr(expr: &syn::Expr) -> Result {
20    use require_embeddable_const_expr as recurse;
21
22    fn recurse_block(block: &syn::Block) -> Result {
23        let stmt = match block.stmts.as_slice() {
24            [stmt] => stmt,
25            [] => return Ok(()),
26            _ => bail!(
27                &block,
28                "only a single expression in a block is allowed in this position"
29            ),
30        };
31
32        match stmt {
33            syn::Stmt::Expr(expr, None) => recurse(expr),
34            _ => bail!(
35                &stmt,
36                "this kind of statement is not allowed in this position; \
37                only a single expression is allowed in a block is allowed here"
38            ),
39        }
40    }
41
42    use syn::Expr::*;
43
44    match expr {
45        Array(arr) => {
46            reject_attrs(&arr.attrs)?;
47            arr.elems.iter().try_for_each(recurse)?;
48        }
49        Binary(binary) => {
50            reject_attrs(&binary.attrs)?;
51            recurse(&binary.left)?;
52            recurse(&binary.right)?;
53        }
54        Block(block) => {
55            reject_attrs(&block.attrs)?;
56            recurse_block(&block.block)?;
57        }
58        Call(call) => {
59            reject_attrs(&call.attrs)?;
60            recurse(&call.func)?;
61            call.args.iter().try_for_each(recurse)?;
62        }
63        Cast(cast) => {
64            reject_attrs(&cast.attrs)?;
65            recurse(&cast.expr)?;
66        }
67        Field(field) => {
68            reject_attrs(&field.attrs)?;
69            recurse(&field.base)?;
70        }
71        Group(group) => {
72            reject_attrs(&group.attrs)?;
73            recurse(&group.expr)?;
74        }
75        If(if_expr) => {
76            reject_attrs(&if_expr.attrs)?;
77            recurse(&if_expr.cond)?;
78            recurse_block(&if_expr.then_branch)?;
79            if let Some((_else, else_branch)) = &if_expr.else_branch {
80                recurse(else_branch)?;
81            }
82        }
83        Const(const_block) => {
84            reject_attrs(&const_block.attrs)?;
85            recurse_block(&const_block.block)?;
86        }
87        Index(index) => {
88            reject_attrs(&index.attrs)?;
89            recurse(&index.expr)?;
90            recurse(&index.index)?;
91        }
92        Infer(infer) => reject_attrs(&infer.attrs)?,
93        Lit(lit) => reject_attrs(&lit.attrs)?,
94        Loop(loop_expr) => {
95            reject_attrs(&loop_expr.attrs)?;
96            recurse_block(&loop_expr.body)?;
97        }
98        Match(expr_match) => {
99            reject_attrs(&expr_match.attrs)?;
100            recurse(&expr_match.expr)?;
101            expr_match.arms.iter().try_for_each(|arm| {
102                reject_attrs(&arm.attrs)?;
103
104                if let Some((_if, guard)) = &arm.guard {
105                    recurse(guard)?;
106                }
107
108                recurse(&arm.body)
109            })?;
110        }
111        MethodCall(method_call) => {
112            reject_attrs(&method_call.attrs)?;
113            recurse(&method_call.receiver)?;
114            method_call.args.iter().try_for_each(recurse)?;
115        }
116        Paren(paren) => {
117            reject_attrs(&paren.attrs)?;
118            recurse(&paren.expr)?;
119        }
120        Path(path) => reject_attrs(&path.attrs)?,
121        Range(range) => {
122            reject_attrs(&range.attrs)?;
123            if let Some(start) = &range.start {
124                recurse(start)?;
125            }
126            if let Some(end) = &range.end {
127                recurse(end)?;
128            }
129        }
130
131        Reference(reference) => {
132            reject_attrs(&reference.attrs)?;
133            recurse(&reference.expr)?;
134        }
135        Repeat(repeat) => {
136            reject_attrs(&repeat.attrs)?;
137            recurse(&repeat.expr)?;
138            recurse(&repeat.len)?;
139        }
140        Struct(struct_expr) => {
141            reject_attrs(&struct_expr.attrs)?;
142            struct_expr.fields.iter().try_for_each(|field| {
143                reject_attrs(&field.attrs)?;
144                recurse(&field.expr)
145            })?;
146            if let Some(rest) = &struct_expr.rest {
147                recurse(rest)?;
148            }
149        }
150        Tuple(tuple) => {
151            reject_attrs(&tuple.attrs)?;
152            tuple.elems.iter().try_for_each(recurse)?;
153        }
154        Unary(unary) => {
155            reject_attrs(&unary.attrs)?;
156            recurse(&unary.expr)?;
157        }
158        Unsafe(unsafe_expr) => {
159            reject_attrs(&unsafe_expr.attrs)?;
160            recurse_block(&unsafe_expr.block)?;
161        }
162        While(while_expr) => {
163            reject_attrs(&while_expr.attrs)?;
164            recurse(&while_expr.cond)?;
165            recurse_block(&while_expr.body)?;
166        }
167        _ => {
168            bail!(
169                &expr,
170                "this kind of expression is not allowed in this position; \
171                if you need to use a complex expression such as this then \
172                move it into a separate `const fn` and call that function \
173                here instead"
174            )
175        }
176    }
177
178    Ok(())
179}