go-jsonnet/linter/internal/traversal/traversal.go
Stanisław Barzowski 8fcbda5b50 Linter
2020-08-13 21:06:34 +02:00

74 lines
2.0 KiB
Go

package traversal
// Package traversal provides relatively lightweight checks
// which can all fit within one traversal of the AST.
// Currently available checks:
// * Loop detection
// TODO(sbarzowski) add more
import (
"github.com/google/go-jsonnet/ast"
"github.com/google/go-jsonnet/linter/internal/utils"
"github.com/google/go-jsonnet/internal/parser"
)
func findLoopingInChildren(node ast.Node, vars map[ast.Identifier]ast.Node, runOf map[ast.Identifier]int, currentRun int, ec *utils.ErrCollector) bool {
for _, c := range parser.DirectChildren(node) {
found := findLooping(c, vars, runOf, currentRun, ec)
if found {
return found
}
}
return false
}
func findLooping(node ast.Node, vars map[ast.Identifier]ast.Node, runOf map[ast.Identifier]int, currentRun int, ec *utils.ErrCollector) bool {
switch node := node.(type) {
case *ast.Var:
_, varFromThisLocal := vars[node.Id]
if !varFromThisLocal {
return false
}
firstRun, reachedBefore := runOf[node.Id]
if !reachedBefore {
runOf[node.Id] = currentRun
return findLooping(vars[node.Id], vars, runOf, currentRun, ec)
} else if firstRun == currentRun {
// TODO(sbarzowski) Maybe report the whole path of the looping, rather than just the last element
ec.StaticErr("Endless loop in local definition", node.Loc())
return true
}
}
return findLoopingInChildren(node, vars, runOf, currentRun, ec)
}
func findLoopingInLocal(node *ast.Local, ec *utils.ErrCollector) {
vars := make(map[ast.Identifier]ast.Node)
runOf := make(map[ast.Identifier]int)
for _, b := range node.Binds {
if b.Body == nil {
panic("Body cannot be nil")
}
vars[b.Variable] = b.Body
}
for i, b := range node.Binds {
found := findLooping(b.Body, vars, runOf, i, ec)
if found {
return
}
}
}
// Traverse visits all nodes in the AST and runs appropriate
// checks.
func Traverse(node ast.Node, ec *utils.ErrCollector) {
switch node := node.(type) {
case *ast.Local:
findLoopingInLocal(node, ec)
}
for _, c := range parser.Children(node) {
Traverse(c, ec)
}
}