Skip to content

Commit

Permalink
Syntax Errors (#22)
Browse files Browse the repository at this point in the history
* First round of refreshed syntax errors. Not working yet
* Unit test now uses external file and not handcoded one
* Syntax package now works
* Basic version of syntax errors now works
  • Loading branch information
0x19 authored Jul 4, 2023
1 parent 50eb954 commit bb910d4
Show file tree
Hide file tree
Showing 16 changed files with 426 additions and 221 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ go.work

# Go binaries
solgo
examples/examples
prototype/*
39 changes: 0 additions & 39 deletions contextual_solidity_parser.go

This file was deleted.

48 changes: 0 additions & 48 deletions contextual_solidity_parser_test.go

This file was deleted.

25 changes: 25 additions & 0 deletions data/tests/BuggyContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
pragma solidity ^0.8.0;

contract TestContract {
uint256 public count;

// Missing semicolon
function increment() public {
count += 1
}

// Mismatched parentheses
function decrement() public {
count -= 1;
}

// Missing function keyword
setCount(uint256 _count) public {
count = _count;
}

// Extraneous input 'returns'
function getCount() public returns (uint256) {
return count
}
}
5 changes: 5 additions & 0 deletions listeners.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ const (
ListenerAbi ListenerName = "abi"
ListenerContractInfo ListenerName = "contract_info"
ListenerAst ListenerName = "ast"
ListenerSyntaxErrors ListenerName = "syntax_errors"
)

func (l ListenerName) String() string {
return string(l)
}

type listeners map[ListenerName]antlr.ParseTreeListener

func (s *SolGo) RegisterListener(name ListenerName, listener antlr.ParseTreeListener) error {
Expand Down
33 changes: 19 additions & 14 deletions solgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (

"github.com/antlr4-go/antlr/v4"
"github.com/txpull/solgo/parser"
"github.com/txpull/solgo/syntaxerrors"
"go.uber.org/zap"
)

// SolGo is a struct that encapsulates the functionality for parsing and analyzing Solidity contracts.
Expand All @@ -22,12 +24,12 @@ type SolGo struct {
// tokenStream is the stream of tokens produced by the lexer.
tokenStream *antlr.CommonTokenStream
// solidityParser is the Solidity parser which parses the token stream.
solidityParser *parser.SolidityParser
solidityParser *syntaxerrors.ContextualParser
// listeners is a map of listener names to ParseTreeListener instances.
// These listeners are invoked as the parser walks the parse tree.
listeners listeners
// errListener is a SyntaxErrorListener which collects syntax errors encountered during parsing.
errListener *SyntaxErrorListener
errListener *syntaxerrors.SyntaxErrorListener
}

// New creates a new instance of SolGo.
Expand All @@ -43,7 +45,7 @@ func New(ctx context.Context, input io.Reader) (*SolGo, error) {
inputStream := antlr.NewInputStream(string(ib))

// Create a new SyntaxErrorListener
errListener := NewSyntaxErrorListener()
errListener := syntaxerrors.NewSyntaxErrorListener()

// Create a new Solidity lexer with the input stream
lexer := parser.NewSolidityLexer(inputStream)
Expand All @@ -57,22 +59,16 @@ func New(ctx context.Context, input io.Reader) (*SolGo, error) {
// Create a new token stream from the lexer
stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)

// Create a new Solidity parser with the token stream
solidityParser := parser.NewSolidityParser(stream)

// Remove the default error listeners
solidityParser.RemoveErrorListeners()

// Add our SyntaxErrorListener
solidityParser.AddErrorListener(errListener)
// Create a new ContextualParser with the token stream and listener
contextualParser := syntaxerrors.NewContextualParser(stream, errListener)

return &SolGo{
ctx: ctx,
inputRaw: input,
inputStream: inputStream,
lexer: lexer,
tokenStream: stream,
solidityParser: solidityParser,
solidityParser: contextualParser,
errListener: errListener,
listeners: make(listeners),
}, nil
Expand Down Expand Up @@ -100,6 +96,11 @@ func (s *SolGo) GetTokenStream() *antlr.CommonTokenStream {

// GetParser returns the Solidity parser which parses the token stream.
func (s *SolGo) GetParser() *parser.SolidityParser {
return s.solidityParser.SolidityParser
}

// GetContextualParser returns the ContextualParser which wraps the Solidity parser.
func (s *SolGo) GetContextualParser() *syntaxerrors.ContextualParser {
return s.solidityParser
}

Expand All @@ -110,11 +111,15 @@ func (s *SolGo) GetTree() antlr.ParseTree {

// Parse initiates the parsing process. It walks the parse tree with all registered listeners
// and returns any syntax errors that were encountered during parsing.
func (s *SolGo) Parse() []SyntaxError {
func (s *SolGo) Parse() []syntaxerrors.SyntaxError {
tree := s.GetTree()

// Walk the parse tree with all registered listeners
for _, listener := range s.GetAllListeners() {
for name, listener := range s.GetAllListeners() {
zap.L().Debug(
"walking parse tree",
zap.String("listener", name.String()),
)
antlr.ParseTreeWalkerDefault.Walk(listener, tree)
}

Expand Down
73 changes: 73 additions & 0 deletions solgo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/txpull/solgo/syntaxerrors"
"github.com/txpull/solgo/tests"
)

func TestNew(t *testing.T) {
Expand Down Expand Up @@ -94,3 +96,74 @@ func TestGetInput(t *testing.T) {

assert.Equal(t, input, solgo.GetInput(), "input reader is not returned correctly")
}

func TestNew_SyntaxErrors(t *testing.T) {
testCases := []struct {
name string
contract string
expected []syntaxerrors.SyntaxError
}{
{
name: "Randomly Corrupted Contract",
contract: tests.ReadContractFileForTestFromRootPath(t, "BuggyContract").Content,
expected: []syntaxerrors.SyntaxError{
{
Line: 9,
Column: 4,
Message: "missing ';' at '}'",
Severity: syntaxerrors.SeverityError,
Context: "SourceUnit",
},
{
Line: 17,
Column: 12,
Message: "mismatched input '(' expecting {'constant', 'error', 'from', 'global', 'immutable', 'internal', 'override', 'private', 'public', 'revert', Identifier}",
Severity: syntaxerrors.SeverityError,
Context: "SourceUnit",
},
{
Line: 17,
Column: 27,
Message: "mismatched input ')' expecting {';', '='}",
Severity: syntaxerrors.SeverityError,
Context: "SourceUnit",
},
{
Line: 18,
Column: 14,
Message: "extraneous input '=' expecting {'constant', 'error', 'from', 'global', 'immutable', 'internal', 'override', 'private', 'public', 'revert', Identifier}",
Severity: syntaxerrors.SeverityError,
Context: "SourceUnit",
},
{
Line: 24,
Column: 4,
Message: "missing ';' at '}'",
Severity: syntaxerrors.SeverityError,
Context: "SourceUnit",
},
{
Line: 25,
Column: 0,
Message: "extraneous input '}' expecting {<EOF>, 'abstract', 'address', 'bool', 'bytes', 'contract', 'enum', 'error', Fixed, FixedBytes, 'from', Function, 'global', 'import', 'interface', 'library', 'mapping', 'pragma', 'revert', SignedIntegerType, 'string', 'struct', 'type', Ufixed, UnsignedIntegerType, 'using', Identifier}",
Severity: syntaxerrors.SeverityError,
Context: "SourceUnit",
},
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Create a new SolGo instance
solGo, err := New(context.Background(), strings.NewReader(tc.contract))
assert.NoError(t, err)
assert.NotNil(t, solGo)

syntaxErrors := solGo.Parse()

// Check that the syntax errors match the expected syntax errors
assert.Equal(t, tc.expected, syntaxErrors)
})
}
}
85 changes: 0 additions & 85 deletions syntax_errors.go

This file was deleted.

Loading

0 comments on commit bb910d4

Please sign in to comment.