Skip to main content

syn_solidity/variable/
mod.rs

1use crate::{Expr, SolIdent, Spanned, Storage, Type, VariableAttributes};
2use proc_macro2::Span;
3use std::fmt::{self, Write};
4use syn::{
5    Attribute, Ident, Result, Token,
6    ext::IdentExt,
7    parse::{Parse, ParseStream},
8};
9
10mod list;
11pub use list::{FieldList, ParameterList, Parameters};
12
13/// A variable declaration: `string memory hello`.
14#[derive(Clone, Debug, PartialEq, Eq, Hash)]
15pub struct VariableDeclaration {
16    /// The attributes of the variable.
17    pub attrs: Vec<Attribute>,
18    /// The type of the variable.
19    pub ty: Type,
20    /// The storage location of the variable, if any.
21    pub storage: Option<Storage>,
22    /// The name of the variable. This is always Some if parsed as part of
23    /// [`Parameters`] or a [`Stmt`][crate::Stmt].
24    pub name: Option<SolIdent>,
25}
26
27impl fmt::Display for VariableDeclaration {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        self.ty.fmt(f)?;
30        if let Some(storage) = &self.storage {
31            f.write_char(' ')?;
32            storage.fmt(f)?;
33        }
34        if let Some(name) = &self.name {
35            f.write_char(' ')?;
36            name.fmt(f)?;
37        }
38        Ok(())
39    }
40}
41
42impl Parse for VariableDeclaration {
43    fn parse(input: ParseStream<'_>) -> Result<Self> {
44        Self::_parse(input, false)
45    }
46}
47
48impl Spanned for VariableDeclaration {
49    fn span(&self) -> Span {
50        let span = self.ty.span();
51        match (&self.storage, &self.name) {
52            (Some(storage), None) => span.join(storage.span()),
53            (_, Some(name)) => span.join(name.span()),
54            (None, None) => Some(span),
55        }
56        .unwrap_or(span)
57    }
58
59    fn set_span(&mut self, span: Span) {
60        self.ty.set_span(span);
61        if let Some(storage) = &mut self.storage {
62            storage.set_span(span);
63        }
64        if let Some(name) = &mut self.name {
65            name.set_span(span);
66        }
67    }
68}
69
70impl VariableDeclaration {
71    pub const fn new(ty: Type) -> Self {
72        Self::new_with(ty, None, None)
73    }
74
75    pub const fn new_with(ty: Type, storage: Option<Storage>, name: Option<SolIdent>) -> Self {
76        Self { attrs: Vec::new(), ty, storage, name }
77    }
78
79    /// Formats `self` as an EIP-712 field: `<ty> <name>`
80    pub fn fmt_eip712(&self, f: &mut impl Write) -> fmt::Result {
81        // According to EIP-712, type strings should only contain struct name, not interface prefix
82        fn fmt_type_eip712(ty: &crate::Type, f: &mut impl Write) -> fmt::Result {
83            match ty {
84                crate::Type::Custom(path) => {
85                    write!(f, "{}", path.last())
86                }
87
88                crate::Type::Array(array) => {
89                    fmt_type_eip712(&array.ty, f)?;
90                    write!(f, "[")?;
91                    if let Some(s) = array.size_lit() {
92                        write!(f, "{}", s.base10_digits())?;
93                    }
94                    write!(f, "]")
95                }
96
97                _ => write!(f, "{ty}"),
98            }
99        }
100
101        fmt_type_eip712(&self.ty, f)?;
102        if let Some(name) = &self.name {
103            write!(f, " {name}")?;
104        }
105        Ok(())
106    }
107
108    pub fn parse_with_name(input: ParseStream<'_>) -> Result<Self> {
109        Self::_parse(input, true)
110    }
111
112    fn _parse(input: ParseStream<'_>, require_name: bool) -> Result<Self> {
113        Ok(Self {
114            attrs: input.call(Attribute::parse_outer)?,
115            ty: input.parse()?,
116            storage: input.call(Storage::parse_opt)?,
117            name: if require_name || input.peek(Ident::peek_any) {
118                Some(input.parse()?)
119            } else {
120                None
121            },
122        })
123    }
124}
125
126#[derive(Clone)]
127pub struct VariableDefinition {
128    pub attrs: Vec<Attribute>,
129    pub ty: Type,
130    pub attributes: VariableAttributes,
131    pub name: SolIdent,
132    pub initializer: Option<(Token![=], Expr)>,
133    pub semi_token: Token![;],
134}
135
136impl fmt::Display for VariableDefinition {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        write!(f, "{} {} {}", self.ty, self.attributes, self.name)?;
139        if let Some((_, _expr)) = &self.initializer {
140            // TODO: fmt::Display for Expr
141            write!(f, " = <expr>")?;
142        }
143        f.write_str(";")
144    }
145}
146
147impl fmt::Debug for VariableDefinition {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        f.debug_struct("VariableDefinition")
150            .field("ty", &self.ty)
151            .field("attributes", &self.attributes)
152            .field("name", &self.name)
153            .field("initializer", &self.initializer)
154            .finish()
155    }
156}
157
158impl Parse for VariableDefinition {
159    fn parse(input: ParseStream<'_>) -> Result<Self> {
160        Ok(Self {
161            attrs: Attribute::parse_outer(input)?,
162            ty: input.parse()?,
163            attributes: input.parse()?,
164            name: input.parse()?,
165            initializer: if input.peek(Token![=]) {
166                Some((input.parse()?, input.parse()?))
167            } else {
168                None
169            },
170            semi_token: input.parse()?,
171        })
172    }
173}
174
175impl Spanned for VariableDefinition {
176    fn span(&self) -> Span {
177        let span = self.ty.span();
178        span.join(self.semi_token.span).unwrap_or(span)
179    }
180
181    fn set_span(&mut self, span: Span) {
182        self.ty.set_span(span);
183        self.semi_token.span = span;
184    }
185}
186
187impl VariableDefinition {
188    pub fn as_declaration(&self) -> VariableDeclaration {
189        VariableDeclaration {
190            attrs: Vec::new(),
191            ty: self.ty.clone(),
192            storage: None,
193            name: Some(self.name.clone()),
194        }
195    }
196}