syn_solidity/yul/stmt/
var_decl.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use crate::{utils::DebugPunctuated, Spanned, WalrusToken, YulExpr, YulIdent};
use proc_macro2::Span;
use std::fmt;
use syn::{
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
    Result, Token,
};

/// Declares Yul variables, which may or may not have initial values. E.x.
/// `let x := 0`
/// `let x`
/// `let x, y := foo()`
/// `let x, y, z`
///
/// Multiple variables can only be initialized via a function call.
///
/// Solidity Reference:
/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.yulVariableDeclaration>
#[derive(Clone)]
pub struct YulVarDecl {
    pub let_token: Token![let],
    pub vars: Punctuated<YulIdent, Token![,]>,
    pub init_value: Option<(WalrusToken, YulExpr)>,
}

impl Parse for YulVarDecl {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        let let_token = input.parse()?;
        let vars = Punctuated::parse_separated_nonempty(input)?;
        let init_value = if input.peek(Token![:]) && input.peek2(Token![=]) {
            Some((input.parse()?, input.parse()?))
        } else {
            None
        };

        if vars.len() > 1
            && init_value.as_ref().map_or(false, |(_, expr)| !matches!(expr, YulExpr::Call(_)))
        {
            return Err(
                input.error("Multiple variables can only be initialized by a function call")
            );
        }

        Ok(Self { let_token, vars, init_value })
    }
}

impl Spanned for YulVarDecl {
    fn span(&self) -> Span {
        let span = self.let_token.span();
        if let Some((_, expr)) = &self.init_value {
            span.join(expr.span()).unwrap_or(span)
        } else {
            span.join(self.vars.span()).unwrap_or(span)
        }
    }

    fn set_span(&mut self, span: Span) {
        self.let_token.set_span(span);
        self.vars.set_span(span);
        if let Some((walrus_token, init_value)) = &mut self.init_value {
            walrus_token.set_span(span);
            init_value.set_span(span);
        }
    }
}

impl fmt::Debug for YulVarDecl {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("YulVarDecl")
            .field("vars", DebugPunctuated::new(&self.vars))
            .field("init_value", &self.init_value.as_ref().map(|(_, expr)| expr))
            .finish()
    }
}