Expand description
§syn-solidity
syn
-powered parser for Solidity-like TokenStream
s.
The parsed root element is the File
, which contains a list of Item
s.
Item
s also support outer attributes, as shown below.
§Design
This parser is specifically designed for Rust procedural macros. It aims to
mimic the behavior of the official Solidity compiler (Solc) when it comes to
parsing valid Solidity code. This means that all valid Solidity code, as
recognized by Solc v0.5.*1 and above, will also be recognized and parsed
correctly by syn-solidity
.
However, syn-solidity
is more permissive and lenient compared to the official
Solidity compiler and grammar specifications. Some examples of code patterns
that are valid in syn-solidity
but not in the official compiler include:
- identifiers are Rust identifiers (
syn::Ident
), and as such cannot contain the dollar sign ($
), but can contain unicode characters - trailing punctuation, like commas (
,
) in function arguments or enums definitions - certain variable and function attributes in certain contexts, like
internal
functions or functions with implementations ({ ... }
) in interfaces - parameter storage locations in item definitions, like
uint256[] memory
in a struct or error definition - the tuple type
(T, U, ..)
is allowed wherever a type is expected, and can optionally be preceded by thetuple
keyword. This is the same asethers.js
’s Human-Readable ABI
This lenient behavior is intentionally designed to facilitate usage within procedural macros, and to reduce general code complexity in the parser and AST.
§Known limitations
This parser is limited to only valid Rust tokens, meaning that certain Solidity constructs are not supported. Some examples include, but are not limited to:
- dollar signs (
$
) inside of identifiers - single quote strings
hex
andunicode
string literal prefixes. Literal prefixes are reserved in Rust edition 2021 and above."\uXXXX"
unicode escapes. Rust uses"\u{XXXX}"
for unicode codepoints- invalid nested block comments. For example,
/*/*/
does not parse.
For the most part, you can copy-paste Solidity code and expect it to parse correctly most of the time. You can see a few examples of Solidity code that parses correctly (after some very light patching) in the tests directory.
§Examples
Basic usage:
use quote::quote;
use syn_solidity::{Expr, File, Item, Lit, Stmt};
// Create a Solidity `TokenStream`
let tokens = quote! {
/// @name HelloWorld
/// @notice A hello world example in Solidity.
contract HelloWorld {
/// @notice Returns the string "Hello, World!".
function helloWorld() external pure returns (string memory) {
return "Hello, World!";
}
}
};
// Parse the tokens into a `File`
let ast: File = syn_solidity::parse2(tokens)?;
let items: &[Item] = &ast.items;
let Some(Item::Contract(contract)) = items.first() else {
unreachable!()
};
assert_eq!(contract.name, "HelloWorld");
assert_eq!(contract.attrs.len(), 2); // doc comments
let body: &[Item] = &contract.body;
let Some(Item::Function(function)) = body.first() else {
unreachable!()
};
assert_eq!(function.attrs.len(), 1); // doc comment
assert_eq!(function.name.as_ref().unwrap(), "helloWorld");
assert!(function.parameters.is_empty()); // ()
assert_eq!(function.attributes.len(), 2); // external pure
assert!(function.returns.is_some());
let Some([Stmt::Return(ret)]) = function.body() else {
unreachable!()
};
let Some(Expr::Lit(Lit::Str(s))) = &ret.expr else {
unreachable!()
};
assert_eq!(s.value(), "Hello, World!");
Older versions may still parse successfully, but this is not guaranteed. ↩
Re-exports§
Modules§
- Solidity keywords.
- Helper trait and methods to manipulate syntax tree nodes’ spans.
- visit
visit
Syntax tree traversal to walk a shared borrow of a syntax tree. - visit_
mut visit-mut
Syntax tree traversal to mutate an exclusive borrow of a syntax tree in place.
Macros§
- Create a
SolPath
from a list of identifiers.
Structs§
- A list of named or unnamed arguments:
{ foo: 42, bar: 64 }
or(42, 64)
. - A list of flags of an assembly statement.
- A curly-braced block of statements:
{ ... }
. - A catch clause of a
StmtTry
:catch { ... }
. - An event parameter.
- An array literal expression:
[a, b, c, d]
. - A binary operation:
a + b
,a += b
. - A function call expression:
foo(42)
orfoo({ bar: 42 })
. - Function call options:
foo.bar{ value: 1, gas: 2 }
. - A unary
delete
expression:delete vector
. - A square bracketed indexing expression:
vector[2]
. - Access of a named member:
obj.k
. - A
new
expression:new Contract
. - A
payable
expression:payable(address(0x...))
. - A postfix unary expression:
foo++
. - A ternary (AKA conditional) expression:
foo ? bar : baz
. - A tuple expression:
(a, b, c, d)
. - A
type()
expression:type(uint256)
- A unary operation:
!x
,-x
. - A Solidity file. The root of the AST.
- A list of unique function attributes. Used in ItemFunction.
- A hex string.
- An import alias.
- A list of import aliases:
{ Foo as Bar, Baz } from "foo.sol"
. - An import directive:
import "foo.sol";
. - A glob import directive:
* as Foo from "foo.sol"
. - A plain import directive:
import "foo.sol" as Foo;
. - A list of inheritance specifiers of an
ItemContract
:is ERC20("Token", "TKN"), Ownable
. - A contract, abstract contract, interface, or library definition:
contract Foo is Bar("foo"), Baz { ... }
. - An enum definition:
enum Foo { A, B, C }
. - An error definition:
error Foo(uint256 a, uint256 b);
. - A function, constructor, fallback, receive, or modifier definition:
function helloWorld() external pure returns(string memory);
. - A struct definition:
struct Foo { uint256 bar; }
. - A user-defined value type definition:
type Foo is uint256;
. - A modifier invocation, or an inheritance specifier.
- A named argument in an argument list:
foo: uint256(42)
. - A named argument list:
{ foo: uint256(42), bar: true }
. - The
override
attribute. - A list of VariableDeclarations, separated by
P
. - A pragma directive:
pragma solidity ^0.8.0;
- The
returns
attribute of a function. - A Solidity identifier.
- A list of identifiers, separated by dots.
- An assembly block, with optional flags:
assembly "evmasm" { ... }
. - A break statement:
break;
. - A continue statement:
continue;
. - A do-while statement:
do { ... } while (condition);
. - An emit statement:
emit FooBar(42);
. - An expression with a trailing semicolon.
- A for statement:
for (uint256 i; i < 42; ++i) { ... }
. - An
if
statement with an optionalelse
block:if (expr) { ... } else { ... }
. - A return statement:
return 42;
. - A revert statement:
revert("error");
. - A try statement:
try fooBar(42) catch { ... }
. - A variable declaration statement:
uint256 foo = 42;
. - A while statement:
while (i < 42) { ... }
. - An array type.
- A function type:
function() returns (string memory)
. - A mapping type:
mapping(uint key => string value)
- A tuple type.
- An unchecked block:
unchecked { ... }
. - A unicode string.
- A
using
directive:using { A, B.mul as * } for uint256 global;
. - A declaration of variables in a tuple:
(,,uint256 foo,string memory bar)
. - A list of unique variable attributes.
- A variable declaration:
string memory hello
. - An enum variant.
- Represents the walrus operator
:=
. - A Yul block contains
YulStmt
between curly braces. - Represents a non-default case of a Yul switch statement.
- Yul function call.
- Yul for loop e.g
for {let i := 0} lt(i,10) {i := add(i,1)} {mstore(i,7)}
. - Yul function definition:
function f() -> a, b { ... }
. - A Yul identifier.
- A Yul if statement:
if lt(a, b) { sstore(0, 1) }
. - In inline assembly, only dot-less identifiers can be declared, but dotted paths can reference declarations made outside the assembly block.
- The return attribute of a Yul function definition.
- A Yul switch statement can consist of only a default-case or one or more non-default cases optionally followed by a default-case.
- Represents the default case of a Yul switch statement.
- Yul variable assignment.
x := 0
orx, y := foo()
. Assigning values to multiple variables requires a function call. - Declares Yul variables, which may or may not have initial values. E.x.
let x := 0
let x
let x, y := foo()
let x, y, z
Enums§
- A list of either unnamed or named arguments.
- A binary operator:
+
,+=
,&
. - The kind of contract.
- An expression.
- A for statement initializer.
- A function attribute.
- The body of a function.
- The kind of function.
- The path of an import directive.
- An AST item. A more expanded version of a Solidity source unit.
- A Solidity literal such as a string or integer or boolean.
- An integer or fixed-point number literal:
1
or1.0
. - A mutability attribute.
- Postfix unary operators.
- A statement, usually ending in a semicolon.
- A storage location.
- A sub-denomination suffix for a number literal.
- A type name.
- Unary operators.
- A user-definable operator:
+
,*
,|
, etc. - The declaration of the variable(s) in a variable declaration statement.
- A variable attribute.
- A visibility attribute.
- Representation of an EVM builtin opcode.
- A Yul expression.
- What type of function is called.
- A Yul statement.
Functions§
- Parse a Solidity
proc_macro::TokenStream
into aFile
. - Parse a Solidity
proc_macro2::TokenStream
into aFile
.
Type Aliases§
- A list of semicolon-separated VariableDeclarations.
- A list of comma-separated VariableDeclarations.