1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
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
}
|