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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
use crate::{utils::DebugPunctuated, Expr, Spanned, Stmt, VariableDeclaration};
use proc_macro2::Span;
use std::fmt;
use syn::{
    parenthesized,
    parse::{discouraged::Speculative, Parse, ParseStream},
    punctuated::Punctuated,
    token::Paren,
    Result, Token,
};

/// A variable declaration statement: `uint256 foo = 42;`.
///
/// Solidity Reference:
/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.variableDeclarationStatement>
#[derive(Clone)]
pub struct StmtVarDecl {
    pub declaration: VarDeclDecl,
    pub assignment: Option<(Token![=], Expr)>,
    pub semi_token: Token![;],
}

impl fmt::Debug for StmtVarDecl {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("StmtVarDecl")
            .field("declaration", &self.declaration)
            .field("assignment", &self.assignment)
            .finish()
    }
}

impl Parse for StmtVarDecl {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        let declaration: VarDeclDecl = input.parse()?;

        // tuple requires assignment
        let assignment = if matches!(declaration, VarDeclDecl::Tuple(_)) || input.peek(Token![=]) {
            Some((input.parse()?, input.parse()?))
        } else {
            None
        };

        let semi_token = input.parse()?;

        Ok(Self { declaration, assignment, semi_token })
    }
}

impl Spanned for StmtVarDecl {
    fn span(&self) -> Span {
        let span = self.declaration.span();
        self.assignment.as_ref().and_then(|(_, expr)| expr.span().join(span)).unwrap_or(span)
    }

    fn set_span(&mut self, span: Span) {
        self.declaration.set_span(span);
        if let Some((eq, expr)) = &mut self.assignment {
            eq.span = span;
            expr.set_span(span);
        }
        self.semi_token.span = span;
    }
}

impl StmtVarDecl {
    pub fn parse_or_expr(input: ParseStream<'_>) -> Result<Stmt> {
        // TODO: Figure if we can do this without forking
        let speculative_parse = || {
            let fork = input.fork();
            match fork.parse() {
                Ok(var) => {
                    input.advance_to(&fork);
                    Ok(Stmt::VarDecl(var))
                }
                Err(_) => input.parse().map(Stmt::Expr),
            }
        };

        if input.peek(Paren) {
            if input.peek2(Token![=]) {
                speculative_parse()
            } else {
                input.parse().map(Stmt::Expr)
            }
        } else {
            speculative_parse()
        }
    }
}

/// The declaration of the variable(s) in a variable declaration statement.
#[derive(Clone, Debug)]
pub enum VarDeclDecl {
    VarDecl(VariableDeclaration),
    Tuple(VarDeclTuple),
}

impl Parse for VarDeclDecl {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        if input.peek(Paren) {
            input.parse().map(Self::Tuple)
        } else {
            VariableDeclaration::parse_with_name(input).map(Self::VarDecl)
        }
    }
}

impl Spanned for VarDeclDecl {
    fn span(&self) -> Span {
        match self {
            Self::VarDecl(decl) => decl.span(),
            Self::Tuple(decl) => decl.span(),
        }
    }

    fn set_span(&mut self, span: Span) {
        match self {
            Self::VarDecl(decl) => decl.set_span(span),
            Self::Tuple(decl) => decl.set_span(span),
        }
    }
}

/// A declaration of variables in a tuple: `(,,uint256 foo,string memory bar)`.
///
/// Solidity Reference:
/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.variableDeclarationTuple>
#[derive(Clone)]
pub struct VarDeclTuple {
    pub paren_token: Paren,
    /// The list of variables being declared. The list can't be empty, but it
    /// can contain `None` elements, indicating the field is empty.
    pub vars: Punctuated<Option<VariableDeclaration>, Token![,]>,
}

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

impl Parse for VarDeclTuple {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        let content;
        Ok(Self {
            paren_token: parenthesized!(content in input),
            vars: content.parse_terminated(Self::parse_var_opt, Token![,])?,
        })
    }
}

impl Spanned for VarDeclTuple {
    fn span(&self) -> Span {
        self.paren_token.span.join()
    }

    fn set_span(&mut self, span: Span) {
        self.paren_token = Paren(span);
    }
}

impl VarDeclTuple {
    fn parse_var_opt(input: ParseStream<'_>) -> Result<Option<VariableDeclaration>> {
        if input.peek(Token![,]) {
            Ok(None)
        } else {
            input.parse().map(Some)
        }
    }
}