Solidity formatter that respects (some parts of) the Style Guide and is tested on the Prettier Solidity Plugin cases.
- Pragma directive
- Import directive
- Contract definition
- Enum definition
- Struct definition
- Event definition
- Error definition
- Function / Modifier / Constructor definitions
- Variable definition
- Type definition
- Using
See Statement enum in Solang
- Block
- Assembly
- Args
- If
- While
- Expression
- VariableDefinition
- For
- DoWhile
- Continue
- Break
- Return
- Revert
- Emit
- Try
- DocComment
See Expression enum in Solang
- PostIncrement, PostDecrement, PreIncrement, PreDecrement, UnaryPlus, UnaryMinus, Not, Complement
- Power, Multiply, Divide, Modulo, Add, Subtract
- ShiftLeft, ShiftRight, BitwiseAnd, BitwiseXor, BitwiseOr
- Assign, AssignOr, AssignAnd, AssignXor, AssignShiftLeft, AssignShiftRight, AssignAdd, AssignSubtract, AssignMultiply, AssignDivide, AssignModulo
- Less, More, LessEqual, MoreEqual, Equal, NotEqual, And, Or
- BoolLiteral, NumberLiteral, RationalNumberLiteral, HexNumberLiteral, StringLiteral, HexLiteral , AddressLiteral
- ArraySubscript, ArraySlice
- MemberAccess
- FunctionCall
- FunctionCallBlock
- NamedFunctionCall
- New
- Delete
- Ternary
- Type
- Address
- Address Payable
- Payable
- Bool
- String
- Int
- Uint
- Bytes
- Rational
- Dynamic Bytes
- Mapping
- Function
- Variable
- List
- ArrayLiteral
- Unit
- This
See YulStatement enum in Solang
- Assign
- VariableDeclaration
- If
- For
- Switch
- Leave
- Break
- Continue
- Block
- FunctionDefinition
- FunctionCall
See YulExpression enum in Solang
- BoolLiteral
- NumberLiteral
- HexNumberLiteral
- HexStringLiteral
- StringLiteral
- Variable
- FunctionCall
- Member
- Comments
The formatter works in two steps:
- Parse Solidity source code with solang into the PT (Parse Tree) (not the same as Abstract Syntax Tree, see difference).
- Walk the PT and output new source code that's compliant with provided config and rule set.
The technique for walking the tree is based on Visitor Pattern and works as following:
- Implement
Formatter
callback functions for each PT node type. Every callback function should write formatted output for the current node and callVisitable::visit
function for child nodes delegating the output writing. - Implement
Visitable
trait and itsvisit
function for each PT node type. Everyvisit
function should call correspondingFormatter
's callback function.
The solang parser does not output comments as a type of parse tree node, but rather in a list alongside the parse tree with location information. It is therefore necessary to infer where to insert the comments and how to format them while traversing the parse tree.
To handle this, the formatter pre-parses the comments and puts them into two categories: Prefix and Postfix comments. Prefix comments refer to the node directly after them, and postfix comments refer to the node before them. As an illustration:
// This is a prefix comment
/* This is also a prefix comment */
uint variable = 1 + 2; /* this is postfix */ // this is postfix too
// and this is a postfix comment on the next line
To insert the comments into the appropriate areas, strings get converted to chunks before being written to the buffer. A chunk is any string that cannot be split by whitespace. A chunk also carries with it the surrounding comment information. Thereby when writing the chunk the comments can be added before and after the chunk as well as any any whitespace surrounding.
To construct a chunk, the string and the location of the string is given to the Formatter and the pre-parsed comments before the start and end of the string are associated with that string. The source code can then further be chunked before the chunks are written to the buffer.
To write the chunk, first the comments associated with the start of the chunk get written to the buffer. Then the Formatter checks if any whitespace is needed between what's been written to the buffer and what's in the chunk and inserts it where appropriate. If the chunk content fits on the same line, it will be written directly to the buffer, otherwise it will be written on the next line. Finally, any associated postfix comments also get written.
Source code
pragma solidity ^0.8.10 ;
contract HelloWorld {
string public message;
constructor( string memory initMessage) { message = initMessage;}
}
event Greet( string indexed name) ;
Parse Tree (simplified)
SourceUnit
| PragmaDirective("solidity", "^0.8.10")
| ContractDefinition("HelloWorld")
| VariableDefinition("string", "message", null, ["public"])
| FunctionDefinition("constructor")
| Parameter("string", "initMessage", ["memory"])
| EventDefinition("string", "Greet", ["indexed"], ["name"])
Formatted source code that was reconstructed from the Parse Tree
pragma solidity ^0.8.10;
contract HelloWorld {
string public message;
constructor(string memory initMessage) {
message = initMessage;
}
}
event Greet(string indexed name);