Skip to content

Commit

Permalink
♻️ Centralize analysis data in Analyzer struct
Browse files Browse the repository at this point in the history
  • Loading branch information
ChmielewskiKamil committed Dec 14, 2024
1 parent ff8f464 commit b3fb931
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 54 deletions.
79 changes: 62 additions & 17 deletions analyzer/analyzer.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package analyzer

import (
"fmt"
"solbot/analyzer/screamingsnakeconst"
"solbot/ast"
"solbot/parser"
"solbot/reporter"
"solbot/symbols"
"solbot/token"
Expand All @@ -18,47 +20,90 @@ func GetAllDetectors() *[]Detector {
}
}

func AnalyzeFile(file *ast.File) []reporter.Finding {
globalEnv := symbols.NewEnvironment()
type Analyzer struct {
// All findings found during the analysis.
findings []reporter.Finding

currentFile *ast.File // The currently analysed file; returned from parser.ParseFile.
currentFileEnv *symbols.Environment // The environment of the currently analyred file.
parser *parser.Parser // Parser is used to parse newly encountered files.
}

func (a *Analyzer) Init(filePathToAnalyze string) error {
a.parser = &parser.Parser{}

sourceFile, err := token.NewSourceFile(filePathToAnalyze, "")
if err != nil {
return err
}

a.parser.Init(sourceFile)
file := a.parser.ParseFile()
if file == nil {
return fmt.Errorf("Could not parse file. Check parses errors.")
}

a.currentFile = file
return nil
}

func (a *Analyzer) AnalyzeCurrentFile() {
a.AnalyzeFile(a.currentFile)
}

func (a *Analyzer) AnalyzeFile(file *ast.File) {
fileEnv := symbols.NewEnvironment()
a.currentFileEnv = fileEnv

// Phase 1: Get all declarations first to avoid unknown symbol errors if
// the symbols are defined later in a file or somewhere else.
discoverSymbols(file, globalEnv, nil)
a.discoverSymbols(file, fileEnv)

// Phase 2: Populate all definitions and references. Resolve overrides and
// inheritance structure.
resolveDefinitions(file, globalEnv)
a.resolveDefinitions(file, fileEnv)

// Phase 3: The environment is populated with context at this point.
// Diagnose issues with the code. Run detectors.
findings := detectIssues(file, globalEnv)
findings := a.detectIssues(file, fileEnv)

return findings
for _, finding := range findings {
a.findings = append(a.findings, finding)
}
}

func (a *Analyzer) GetFindings() []reporter.Finding {
return a.findings
}

func (a *Analyzer) GetCurrentFileEnv() *symbols.Environment {
return a.currentFileEnv
}

func (a *Analyzer) GetParserErrors() parser.ErrorList {
return a.parser.Errors()
}

////////////////////////////////////////////////////////////////////
// PHASE 1 //
////////////////////////////////////////////////////////////////////

func discoverSymbols(node ast.Node,
env *symbols.Environment, src *token.SourceFile) {
func (a *Analyzer) discoverSymbols(node ast.Node, env *symbols.Environment) {
switch n := node.(type) {
case *ast.File:
for _, decl := range n.Declarations {
discoverSymbols(decl, env, n.SourceFile)
a.discoverSymbols(decl, env)
}
case *ast.FunctionDeclaration:
populateFunctionDeclaration(n, env, src)
a.populateFunctionDeclaration(n, env)
}
}

func populateFunctionDeclaration(
node *ast.FunctionDeclaration,
env *symbols.Environment,
src *token.SourceFile) {
func (a *Analyzer) populateFunctionDeclaration(
node *ast.FunctionDeclaration, env *symbols.Environment) {
baseSymbol := symbols.BaseSymbol{
Name: node.Name.Value,
SourceFile: src,
SourceFile: a.currentFile.SourceFile,
Offset: node.Pos,
AstNode: node,
}
Expand All @@ -74,9 +119,9 @@ func populateFunctionDeclaration(
// PHASE 2 //
////////////////////////////////////////////////////////////////////

func resolveDefinitions(node ast.Node, env *symbols.Environment) {}
func (a *Analyzer) resolveDefinitions(node ast.Node, env *symbols.Environment) {}

func detectIssues(node ast.Node, env *symbols.Environment) []reporter.Finding {
func (a *Analyzer) detectIssues(node ast.Node, env *symbols.Environment) []reporter.Finding {
var findings []reporter.Finding

// detectors := *GetAllDetectors()
Expand Down
31 changes: 10 additions & 21 deletions analyzer/analyzer_test.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,16 @@
package analyzer

import (
"solbot/parser"
"solbot/symbols"
"solbot/token"
"testing"
)

func Test_PopulateSymbols_GetSymbolsByName(t *testing.T) {
testContractPath := "testdata/foundry/src/002_SimpleCounter.sol"
p := parser.Parser{}
sourceFile, err := token.NewSourceFile(testContractPath, "")
if err != nil {
t.Fatalf("Could not create source file: %s", err)
}

p.Init(sourceFile)

file := p.ParseFile()
checkParserErrors(t, &p)
analyzer := Analyzer{}
analyzer.Init(testContractPath)
checkParserErrors(t, &analyzer)

if file == nil {
t.Fatalf("ParseFile() returned nil")
}

env := symbols.NewEnvironment()
discoverSymbols(file, env, nil)
analyzer.AnalyzeCurrentFile()

tests := []struct {
expectedSymbolName string
Expand All @@ -36,15 +21,19 @@ func Test_PopulateSymbols_GetSymbolsByName(t *testing.T) {
}

for _, tt := range tests {
env := analyzer.GetCurrentFileEnv()
if env == nil {
t.Fatalf("Currently analyzed file's env is nil.")
}
_, found := env.Get(tt.expectedSymbolName)
if !found {
t.Fatalf("Symbol: '%s' not found.", tt.expectedSymbolName)
}
}
}

func checkParserErrors(t *testing.T, p *parser.Parser) {
errors := p.Errors()
func checkParserErrors(t *testing.T, a *Analyzer) {
errors := a.GetParserErrors()
if len(errors) == 0 {
return
}
Expand Down
20 changes: 4 additions & 16 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ import (
"solbot/lsp"
"solbot/lsp/analysis"
"solbot/lsp/rpc"
"solbot/parser"
"solbot/reporter"
"solbot/token"
)

func main() {
Expand Down Expand Up @@ -64,24 +62,14 @@ func startLanguageServer() {

func startAnalyzer(filePath string) error {
println("Solbot starts")
p := parser.Parser{}
handle, err := token.NewSourceFile(filePath, "")
if err != nil {
return err
}

p.Init(handle)
p.ToggleTracing()

file := p.ParseFile()
a := analyzer.Analyzer{}
a.Init(filePath)
a.AnalyzeCurrentFile()

println("Solbot is analyzing your file...")
findings := analyzer.AnalyzeFile(file)
for _, finding := range findings {
finding.CalculatePositions(handle)
}

reporter.GenerateReport(findings, "solbot.md")
reporter.GenerateReport(a.GetFindings(), "solbot.md")

return nil
}
Expand Down

0 comments on commit b3fb931

Please sign in to comment.