syn_solidity/stmt/
var_decl.rs

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