Skip to content

Latest commit

 

History

History
 
 

fmt

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

Formatter (fmt)

Solidity formatter that respects (some parts of) the Style Guide and is tested on the Prettier Solidity Plugin cases.

Features

Directives & Definitions

  • Pragma directive
  • Import directive
  • Contract definition
  • Enum definition
  • Struct definition
  • Event definition
  • Error definition
  • Function / Modifier / Constructor definitions
  • Variable definition
  • Type definition
  • Using

Statements

See Statement enum in Solang

  • Block
  • Assembly
  • Args
  • If
  • While
  • Expression
  • VariableDefinition
  • For
  • DoWhile
  • Continue
  • Break
  • Return
  • Revert
  • Emit
  • Try
  • DocComment

Expressions

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

Yul Statements

See YulStatement enum in Solang

  • Assign
  • VariableDeclaration
  • If
  • For
  • Switch
  • Leave
  • Break
  • Continue
  • Block
  • FunctionDefinition
  • FunctionCall

Yul Expressions

See YulExpression enum in Solang

  • BoolLiteral
  • NumberLiteral
  • HexNumberLiteral
  • HexStringLiteral
  • StringLiteral
  • Variable
  • FunctionCall
  • Member

Other

  • Comments

Architecture

The formatter works in two steps:

  1. Parse Solidity source code with solang into the PT (Parse Tree) (not the same as Abstract Syntax Tree, see difference).
  2. 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:

  1. Implement Formatter callback functions for each PT node type. Every callback function should write formatted output for the current node and call Visitable::visit function for child nodes delegating the output writing.
  2. Implement Visitable trait and its visit function for each PT node type. Every visit function should call corresponding Formatter's callback function.

Comments

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.

Example

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);