syn_solidity/stmt/
var_decl.rs

1use crate::{utils::DebugPunctuated, Expr, Spanned, Stmt, VariableDeclaration};
2use proc_macro2::Span;
3use std::fmt;
4use syn::{
5    parenthesized,
6    parse::{discouraged::Speculative, Parse, ParseStream},
7    punctuated::Punctuated,
8    token::Paren,
9    Result, Token,
10};
11
12/// A variable declaration statement: `uint256 foo = 42;`.
13///
14/// Solidity Reference:
15/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.variableDeclarationStatement>
16#[derive(Clone)]
17pub struct StmtVarDecl {
18    pub declaration: VarDeclDecl,
19    pub assignment: Option<(Token![=], Expr)>,
20    pub semi_token: Token![;],
21}
22
23impl fmt::Debug for StmtVarDecl {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        f.debug_struct("StmtVarDecl")
26            .field("declaration", &self.declaration)
27            .field("assignment", &self.assignment)
28            .finish()
29    }
30}
31
32impl Parse for StmtVarDecl {
33    fn parse(input: ParseStream<'_>) -> Result<Self> {
34        let declaration: VarDeclDecl = input.parse()?;
35
36        // tuple requires assignment
37        let assignment = if matches!(declaration, VarDeclDecl::Tuple(_)) || input.peek(Token![=]) {
38            Some((input.parse()?, input.parse()?))
39        } else {
40            None
41        };
42
43        let semi_token = input.parse()?;
44
45        Ok(Self { declaration, assignment, semi_token })
46    }
47}
48
49impl Spanned for StmtVarDecl {
50    fn span(&self) -> Span {
51        let span = self.declaration.span();
52        self.assignment.as_ref().and_then(|(_, expr)| expr.span().join(span)).unwrap_or(span)
53    }
54
55    fn set_span(&mut self, span: Span) {
56        self.declaration.set_span(span);
57        if let Some((eq, expr)) = &mut self.assignment {
58            eq.span = span;
59            expr.set_span(span);
60        }
61        self.semi_token.span = span;
62    }
63}
64
65impl StmtVarDecl {
66    pub fn parse_or_expr(input: ParseStream<'_>) -> Result<Stmt> {
67        // TODO: Figure if we can do this without forking
68        let speculative_parse = || {
69            let fork = input.fork();
70            match fork.parse() {
71                Ok(var) => {
72                    input.advance_to(&fork);
73                    Ok(Stmt::VarDecl(var))
74                }
75                Err(_) => input.parse().map(Stmt::Expr),
76            }
77        };
78
79        if input.peek(Paren) {
80            if input.peek2(Token![=]) {
81                speculative_parse()
82            } else {
83                input.parse().map(Stmt::Expr)
84            }
85        } else {
86            speculative_parse()
87        }
88    }
89}
90
91/// The declaration of the variable(s) in a variable declaration statement.
92#[derive(Clone, Debug)]
93pub enum VarDeclDecl {
94    VarDecl(VariableDeclaration),
95    Tuple(VarDeclTuple),
96}
97
98impl Parse for VarDeclDecl {
99    fn parse(input: ParseStream<'_>) -> Result<Self> {
100        if input.peek(Paren) {
101            input.parse().map(Self::Tuple)
102        } else {
103            VariableDeclaration::parse_with_name(input).map(Self::VarDecl)
104        }
105    }
106}
107
108impl Spanned for VarDeclDecl {
109    fn span(&self) -> Span {
110        match self {
111            Self::VarDecl(decl) => decl.span(),
112            Self::Tuple(decl) => decl.span(),
113        }
114    }
115
116    fn set_span(&mut self, span: Span) {
117        match self {
118            Self::VarDecl(decl) => decl.set_span(span),
119            Self::Tuple(decl) => decl.set_span(span),
120        }
121    }
122}
123
124/// A declaration of variables in a tuple: `(,,uint256 foo,string memory bar)`.
125///
126/// Solidity Reference:
127/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.variableDeclarationTuple>
128#[derive(Clone)]
129pub struct VarDeclTuple {
130    pub paren_token: Paren,
131    /// The list of variables being declared. The list can't be empty, but it
132    /// can contain `None` elements, indicating the field is empty.
133    pub vars: Punctuated<Option<VariableDeclaration>, Token![,]>,
134}
135
136impl fmt::Debug for VarDeclTuple {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        f.debug_struct("VarDeclTuple").field("vars", DebugPunctuated::new(&self.vars)).finish()
139    }
140}
141
142impl Parse for VarDeclTuple {
143    fn parse(input: ParseStream<'_>) -> Result<Self> {
144        let content;
145        Ok(Self {
146            paren_token: parenthesized!(content in input),
147            vars: content.parse_terminated(Self::parse_var_opt, Token![,])?,
148        })
149    }
150}
151
152impl Spanned for VarDeclTuple {
153    fn span(&self) -> Span {
154        self.paren_token.span.join()
155    }
156
157    fn set_span(&mut self, span: Span) {
158        self.paren_token = Paren(span);
159    }
160}
161
162impl VarDeclTuple {
163    fn parse_var_opt(input: ParseStream<'_>) -> Result<Option<VariableDeclaration>> {
164        if input.peek(Token![,]) {
165            Ok(None)
166        } else {
167            input.parse().map(Some)
168        }
169    }
170}