syn_solidity/item/
contract.rs

1use crate::{kw, utils::DebugPunctuated, Item, Modifier, SolIdent, Spanned, Type};
2use proc_macro2::Span;
3use std::{cmp::Ordering, fmt};
4use syn::{
5    braced,
6    parse::{Lookahead1, Parse, ParseStream},
7    punctuated::Punctuated,
8    token::Brace,
9    Attribute, Error, Result, Token,
10};
11
12/// A contract, abstract contract, interface, or library definition:
13/// `contract Foo is Bar("foo"), Baz { ... }`.
14///
15/// Solidity reference:
16/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.contractDefinition>
17#[derive(Clone)]
18pub struct ItemContract {
19    pub attrs: Vec<Attribute>,
20    pub kind: ContractKind,
21    pub name: SolIdent,
22    pub inheritance: Option<Inheritance>,
23    pub brace_token: Brace,
24    pub body: Vec<Item>,
25}
26
27impl fmt::Display for ItemContract {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        write!(f, "{} {}", self.kind, self.name)?;
30        if let Some(inheritance) = &self.inheritance {
31            write!(f, " {inheritance}")?;
32        }
33        let s = self
34            .body
35            .iter()
36            .map(|item| item.to_string())
37            .collect::<Vec<_>>()
38            .join("\n")
39            .replace('\n', "\n    ");
40        write!(f, " {{\n    {s}\n}}")
41    }
42}
43
44impl fmt::Debug for ItemContract {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        f.debug_struct("ItemContract")
47            .field("attrs", &self.attrs)
48            .field("kind", &self.kind)
49            .field("name", &self.name)
50            .field("inheritance", &self.inheritance)
51            .field("body", &self.body)
52            .finish()
53    }
54}
55
56impl Parse for ItemContract {
57    fn parse(input: ParseStream<'_>) -> Result<Self> {
58        let kind;
59        let content;
60        Ok(Self {
61            attrs: input.call(Attribute::parse_outer)?,
62            kind: {
63                kind = input.parse()?;
64                kind
65            },
66            name: input.parse()?,
67            inheritance: {
68                if input.peek(kw::is) {
69                    if kind.is_library() {
70                        return Err(input.error("libraries are not allowed to inherit"));
71                    }
72                    Some(input.parse()?)
73                } else {
74                    None
75                }
76            },
77            brace_token: braced!(content in input),
78            body: {
79                let mut body = Vec::new();
80                while !content.is_empty() {
81                    let item: Item = content.parse()?;
82                    if matches!(item, Item::Contract(_)) {
83                        return Err(Error::new(item.span(), "cannot declare nested contracts"));
84                    }
85                    body.push(item);
86                }
87                body
88            },
89        })
90    }
91}
92
93impl Spanned for ItemContract {
94    fn span(&self) -> Span {
95        self.name.span()
96    }
97
98    fn set_span(&mut self, span: Span) {
99        self.name.set_span(span);
100    }
101}
102
103impl ItemContract {
104    pub fn as_type(&self) -> Type {
105        Type::Address(self.span(), None)
106    }
107
108    /// Returns true if `self` is an abstract contract.
109    pub fn is_abstract_contract(&self) -> bool {
110        self.kind.is_abstract_contract()
111    }
112
113    /// Returns true if `self` is a contract.
114    pub fn is_contract(&self) -> bool {
115        self.kind.is_contract()
116    }
117
118    /// Returns true if `self` is an interface.
119    pub fn is_interface(&self) -> bool {
120        self.kind.is_interface()
121    }
122
123    /// Returns true if `self` is a library.
124    pub fn is_library(&self) -> bool {
125        self.kind.is_library()
126    }
127}
128
129/// The kind of contract.
130#[derive(Clone, Copy, PartialEq, Eq, Hash)]
131pub enum ContractKind {
132    /// `abstract contract`
133    AbstractContract(Token![abstract], kw::contract),
134    /// `contract`
135    Contract(kw::contract),
136    /// `interface`
137    Interface(kw::interface),
138    /// `library`
139    Library(kw::library),
140}
141
142impl fmt::Display for ContractKind {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        f.write_str(self.as_str())
145    }
146}
147
148impl fmt::Debug for ContractKind {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        f.write_str(self.as_debug_str())
151    }
152}
153
154impl PartialOrd for ContractKind {
155    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
156        Some(self.cmp(other))
157    }
158}
159
160impl Ord for ContractKind {
161    fn cmp(&self, other: &Self) -> Ordering {
162        self.idx().cmp(&other.idx())
163    }
164}
165
166impl Parse for ContractKind {
167    fn parse(input: ParseStream<'_>) -> Result<Self> {
168        let lookahead = input.lookahead1();
169        if lookahead.peek(Token![abstract]) {
170            Ok(Self::AbstractContract(input.parse()?, input.parse()?))
171        } else if lookahead.peek(kw::contract) {
172            input.parse().map(Self::Contract)
173        } else if lookahead.peek(kw::interface) {
174            input.parse().map(Self::Interface)
175        } else if lookahead.peek(kw::library) {
176            input.parse().map(Self::Library)
177        } else {
178            Err(lookahead.error())
179        }
180    }
181}
182
183impl Spanned for ContractKind {
184    fn span(&self) -> Span {
185        match self {
186            Self::AbstractContract(kw_abstract, kw_contract) => {
187                let span = kw_abstract.span;
188                span.join(kw_contract.span).unwrap_or(span)
189            }
190            Self::Contract(kw) => kw.span,
191            Self::Interface(kw) => kw.span,
192            Self::Library(kw) => kw.span,
193        }
194    }
195
196    fn set_span(&mut self, span: Span) {
197        match self {
198            Self::AbstractContract(kw_abstract, kw_contract) => {
199                kw_abstract.span = span;
200                kw_contract.span = span;
201            }
202            Self::Contract(kw) => kw.span = span,
203            Self::Interface(kw) => kw.span = span,
204            Self::Library(kw) => kw.span = span,
205        }
206    }
207}
208
209impl ContractKind {
210    pub fn peek(lookahead: &Lookahead1<'_>) -> bool {
211        lookahead.peek(Token![abstract])
212            || lookahead.peek(kw::contract)
213            || lookahead.peek(kw::interface)
214            || lookahead.peek(kw::library)
215    }
216
217    /// Returns true if `self` is an abstract contract.
218    pub fn is_abstract_contract(self) -> bool {
219        matches!(self, Self::AbstractContract(..))
220    }
221
222    /// Returns true if `self` is a contract.
223    pub fn is_contract(self) -> bool {
224        matches!(self, Self::Contract(_))
225    }
226
227    /// Returns true if `self` is an interface.
228    pub fn is_interface(self) -> bool {
229        matches!(self, Self::Interface(_))
230    }
231
232    /// Returns true if `self` is a library.
233    pub fn is_library(self) -> bool {
234        matches!(self, Self::Library(_))
235    }
236
237    pub const fn as_debug_str(self) -> &'static str {
238        match self {
239            Self::AbstractContract(..) => "AbstractContract",
240            Self::Contract(_) => "Contract",
241            Self::Interface(_) => "Interface",
242            Self::Library(_) => "Library",
243        }
244    }
245
246    pub const fn as_str(self) -> &'static str {
247        match self {
248            Self::AbstractContract(..) => "abstract contract",
249            Self::Contract(_) => "contract",
250            Self::Interface(_) => "interface",
251            Self::Library(_) => "library",
252        }
253    }
254
255    fn idx(&self) -> usize {
256        match self {
257            Self::AbstractContract(..) => 0,
258            Self::Contract(_) => 1,
259            Self::Interface(_) => 2,
260            Self::Library(_) => 3,
261        }
262    }
263}
264
265/// A list of inheritance specifiers of an [`ItemContract`]:
266/// `is ERC20("Token", "TKN"), Ownable`.
267///
268/// Solidity reference:
269/// <https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityParser.inheritanceSpecifier>
270#[derive(Clone, PartialEq, Eq, Hash)]
271pub struct Inheritance {
272    pub is_token: kw::is,
273    pub inheritance: Punctuated<Modifier, Token![,]>,
274}
275
276impl fmt::Display for Inheritance {
277    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278        f.write_str("is ")?;
279        for (i, modifier) in self.inheritance.iter().enumerate() {
280            if i > 0 {
281                f.write_str(", ")?;
282            }
283            modifier.fmt(f)?;
284        }
285        Ok(())
286    }
287}
288
289impl fmt::Debug for Inheritance {
290    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291        f.debug_tuple("Inheritance").field(DebugPunctuated::new(&self.inheritance)).finish()
292    }
293}
294
295impl Parse for Inheritance {
296    fn parse(input: ParseStream<'_>) -> Result<Self> {
297        let is_token = input.parse()?;
298        let mut inheritance = Punctuated::new();
299        loop {
300            if input.is_empty() || input.peek(Brace) {
301                break;
302            }
303            inheritance.push_value(input.parse()?);
304            if input.is_empty() || input.peek(Brace) {
305                break;
306            }
307            inheritance.push_punct(input.parse()?);
308        }
309        if inheritance.is_empty() {
310            Err(input.parse::<SolIdent>().unwrap_err())
311        } else {
312            Ok(Self { is_token, inheritance })
313        }
314    }
315}
316
317impl Spanned for Inheritance {
318    fn span(&self) -> Span {
319        let span = self.is_token.span;
320        self.inheritance.last().and_then(|last| span.join(last.span())).unwrap_or(span)
321    }
322
323    fn set_span(&mut self, span: Span) {
324        self.is_token.span = span;
325    }
326}