Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syntax Errors #22

Merged
merged 4 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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