github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/transform/staticlark/analyze.go (about) 1 package staticlark 2 3 import ( 4 golog "github.com/ipfs/go-log" 5 "go.starlark.net/starlark" 6 "go.starlark.net/syntax" 7 ) 8 9 var log = golog.Logger("staticlark") 10 11 // AnalyzeFile performs static analysis and returns diagnostic results 12 func AnalyzeFile(filename string) ([]Diagnostic, error) { 13 // Parse the script to abstract syntax 14 f, err := syntax.Parse(filename, nil, 0) 15 if err != nil { 16 return nil, err 17 } 18 // Collect function definitions and top level function calls 19 funcs, topLevel, err := collectFuncDefsTopLevelCalls(f.Stmts) 20 if err != nil { 21 return nil, err 22 } 23 // Constuct pre-defined global symbols 24 globals := newSymtable(starlark.Universe) 25 // Build a graph of all calls, using top level calls and pre-defined globals 26 callGraph := buildCallGraph(funcs, topLevel, globals) 27 28 // Trace sensitive data using dataflow analysis 29 dataflowDiags, err := analyzeSensitiveDataflow(callGraph, nil) 30 if err != nil { 31 return nil, err 32 } 33 34 // Return any unused functions 35 // TODO(dustmop): As more analysis steps are introduced, refactor this 36 // into a generic interface that creates Diagnostics 37 unusedDiags := callGraph.findUnusedFuncs() 38 return append(dataflowDiags, unusedDiags...), nil 39 } 40 41 // Diagnostic represents a diagnostic message describing an issue with the code 42 type Diagnostic struct { 43 Pos syntax.Position 44 Category string 45 Message string 46 } 47 48 func newSymtable(symbols starlark.StringDict) map[string]*funcNode { 49 table := make(map[string]*funcNode) 50 for name := range symbols { 51 table[name] = &funcNode{name: name} 52 } 53 return table 54 }