151 lines
3.8 KiB
Go
151 lines
3.8 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"go/ast"
|
||
|
"go/build"
|
||
|
"go/parser"
|
||
|
"go/token"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
func badArgument(fileset *token.FileSet, p token.Pos) {
|
||
|
pos := fileset.Position(p)
|
||
|
filename := pos.Filename
|
||
|
base, err := os.Getwd()
|
||
|
if err == nil {
|
||
|
rpath, perr := filepath.Rel(base, pos.Filename)
|
||
|
if perr == nil {
|
||
|
filename = rpath
|
||
|
}
|
||
|
}
|
||
|
msg := fmt.Sprintf("%s:%d: Error: found call to rice.FindBox, "+
|
||
|
"but argument must be a string literal.\n", filename, pos.Line)
|
||
|
fmt.Println(msg)
|
||
|
os.Exit(1)
|
||
|
}
|
||
|
|
||
|
func findBoxes(pkg *build.Package) map[string]bool {
|
||
|
// create map of boxes to embed
|
||
|
var boxMap = make(map[string]bool)
|
||
|
|
||
|
// create one list of files for this package
|
||
|
filenames := make([]string, 0, len(pkg.GoFiles)+len(pkg.CgoFiles))
|
||
|
filenames = append(filenames, pkg.GoFiles...)
|
||
|
filenames = append(filenames, pkg.CgoFiles...)
|
||
|
|
||
|
// loop over files, search for rice.FindBox(..) calls
|
||
|
for _, filename := range filenames {
|
||
|
// find full filepath
|
||
|
fullpath := filepath.Join(pkg.Dir, filename)
|
||
|
if strings.HasSuffix(filename, "rice-box.go") {
|
||
|
// Ignore *.rice-box.go files
|
||
|
verbosef("skipping file %q\n", fullpath)
|
||
|
continue
|
||
|
}
|
||
|
verbosef("scanning file %q\n", fullpath)
|
||
|
|
||
|
fset := token.NewFileSet()
|
||
|
f, err := parser.ParseFile(fset, fullpath, nil, 0)
|
||
|
if err != nil {
|
||
|
fmt.Println(err)
|
||
|
os.Exit(1)
|
||
|
}
|
||
|
|
||
|
var riceIsImported bool
|
||
|
ricePkgName := "rice"
|
||
|
for _, imp := range f.Imports {
|
||
|
if strings.HasSuffix(imp.Path.Value, "go.rice\"") {
|
||
|
if imp.Name != nil {
|
||
|
ricePkgName = imp.Name.Name
|
||
|
}
|
||
|
riceIsImported = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if !riceIsImported {
|
||
|
// Rice wasn't imported, so we won't find a box.
|
||
|
continue
|
||
|
}
|
||
|
if ricePkgName == "_" {
|
||
|
// Rice pkg is unnamed, so we won't find a box.
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// Inspect AST, looking for calls to (Must)?FindBox.
|
||
|
// First parameter of the func must be a basic literal.
|
||
|
// Identifiers won't be resolved.
|
||
|
var nextIdentIsBoxFunc bool
|
||
|
var nextBasicLitParamIsBoxName bool
|
||
|
var boxCall token.Pos
|
||
|
var variableToRemember string
|
||
|
var validVariablesForBoxes map[string]bool = make(map[string]bool)
|
||
|
|
||
|
ast.Inspect(f, func(node ast.Node) bool {
|
||
|
if node == nil {
|
||
|
return false
|
||
|
}
|
||
|
switch x := node.(type) {
|
||
|
// this case fixes the var := func() style assignments, not assignments to vars declared separately from the assignment.
|
||
|
case *ast.AssignStmt:
|
||
|
var assign = node.(*ast.AssignStmt)
|
||
|
name, found := assign.Lhs[0].(*ast.Ident)
|
||
|
if found {
|
||
|
variableToRemember = name.Name
|
||
|
composite, first := assign.Rhs[0].(*ast.CompositeLit)
|
||
|
if first {
|
||
|
riceSelector, second := composite.Type.(*ast.SelectorExpr)
|
||
|
|
||
|
if second {
|
||
|
callCorrect := riceSelector.Sel.Name == "Config"
|
||
|
packageName, third := riceSelector.X.(*ast.Ident)
|
||
|
|
||
|
if third && callCorrect && packageName.Name == ricePkgName {
|
||
|
validVariablesForBoxes[name.Name] = true
|
||
|
verbosef("\tfound variable, saving to scan for boxes: %q\n", name.Name)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
case *ast.Ident:
|
||
|
if nextIdentIsBoxFunc || ricePkgName == "." {
|
||
|
nextIdentIsBoxFunc = false
|
||
|
if x.Name == "FindBox" || x.Name == "MustFindBox" {
|
||
|
nextBasicLitParamIsBoxName = true
|
||
|
boxCall = x.Pos()
|
||
|
}
|
||
|
} else {
|
||
|
if x.Name == ricePkgName || validVariablesForBoxes[x.Name] {
|
||
|
nextIdentIsBoxFunc = true
|
||
|
}
|
||
|
}
|
||
|
case *ast.BasicLit:
|
||
|
if nextBasicLitParamIsBoxName {
|
||
|
if x.Kind == token.STRING {
|
||
|
nextBasicLitParamIsBoxName = false
|
||
|
// trim "" or ``
|
||
|
name := x.Value[1 : len(x.Value)-1]
|
||
|
boxMap[name] = true
|
||
|
verbosef("\tfound box %q\n", name)
|
||
|
} else {
|
||
|
badArgument(fset, boxCall)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
if nextIdentIsBoxFunc {
|
||
|
nextIdentIsBoxFunc = false
|
||
|
}
|
||
|
if nextBasicLitParamIsBoxName {
|
||
|
badArgument(fset, boxCall)
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return boxMap
|
||
|
}
|