github.com/zachgersh/packr@v1.11.1/builder/visitor.go (about) 1 package builder 2 3 import ( 4 "go/ast" 5 "go/parser" 6 "go/token" 7 "io/ioutil" 8 "sort" 9 "strings" 10 11 "github.com/pkg/errors" 12 ) 13 14 type visitor struct { 15 Path string 16 Package string 17 Boxes []string 18 Errors []error 19 } 20 21 func newVisitor(path string) *visitor { 22 return &visitor{ 23 Path: path, 24 Boxes: []string{}, 25 Errors: []error{}, 26 } 27 } 28 29 func (v *visitor) Run() error { 30 b, err := ioutil.ReadFile(v.Path) 31 if err != nil { 32 return errors.WithStack(err) 33 } 34 35 fset := token.NewFileSet() 36 file, err := parser.ParseFile(fset, v.Path, string(b), parser.ParseComments) 37 if err != nil { 38 return errors.WithStack(err) 39 } 40 41 v.Package = file.Name.Name 42 ast.Walk(v, file) 43 44 m := map[string]string{} 45 for _, s := range v.Boxes { 46 m[s] = s 47 } 48 v.Boxes = []string{} 49 for k := range m { 50 v.Boxes = append(v.Boxes, k) 51 } 52 53 sort.Strings(v.Boxes) 54 55 if len(v.Errors) > 0 { 56 s := make([]string, len(v.Errors)) 57 for i, e := range v.Errors { 58 s[i] = e.Error() 59 } 60 return errors.New(strings.Join(s, "\n")) 61 } 62 return nil 63 } 64 65 func (v *visitor) Visit(node ast.Node) ast.Visitor { 66 if node == nil { 67 return v 68 } 69 if err := v.eval(node); err != nil { 70 v.Errors = append(v.Errors, err) 71 } 72 return v 73 } 74 75 func (v *visitor) eval(node ast.Node) error { 76 switch t := node.(type) { 77 case *ast.CallExpr: 78 return v.evalExpr(t) 79 case *ast.Ident: 80 return v.evalIdent(t) 81 case *ast.GenDecl: 82 for _, n := range t.Specs { 83 if err := v.eval(n); err != nil { 84 return errors.WithStack(err) 85 } 86 } 87 case *ast.FuncDecl: 88 if t.Body == nil { 89 return nil 90 } 91 for _, b := range t.Body.List { 92 if err := v.evalStmt(b); err != nil { 93 return errors.WithStack(err) 94 } 95 } 96 return nil 97 case *ast.ValueSpec: 98 for _, e := range t.Values { 99 if err := v.evalExpr(e); err != nil { 100 return errors.WithStack(err) 101 } 102 } 103 } 104 return nil 105 } 106 107 func (v *visitor) evalStmt(stmt ast.Stmt) error { 108 switch t := stmt.(type) { 109 case *ast.ExprStmt: 110 return v.evalExpr(t.X) 111 case *ast.AssignStmt: 112 for _, e := range t.Rhs { 113 if err := v.evalArgs(e); err != nil { 114 return errors.WithStack(err) 115 } 116 } 117 } 118 return nil 119 } 120 121 func (v *visitor) evalExpr(expr ast.Expr) error { 122 switch t := expr.(type) { 123 case *ast.CallExpr: 124 if t.Fun == nil { 125 return nil 126 } 127 for _, a := range t.Args { 128 switch at := a.(type) { 129 case *ast.CallExpr: 130 if sel, ok := t.Fun.(*ast.SelectorExpr); ok { 131 return v.evalSelector(at, sel) 132 } 133 134 if err := v.evalArgs(at); err != nil { 135 return errors.WithStack(err) 136 } 137 case *ast.CompositeLit: 138 for _, e := range at.Elts { 139 if err := v.evalExpr(e); err != nil { 140 return errors.WithStack(err) 141 } 142 } 143 } 144 } 145 if ft, ok := t.Fun.(*ast.SelectorExpr); ok { 146 return v.evalSelector(t, ft) 147 } 148 case *ast.KeyValueExpr: 149 return v.evalExpr(t.Value) 150 } 151 return nil 152 } 153 154 func (v *visitor) evalArgs(expr ast.Expr) error { 155 switch at := expr.(type) { 156 case *ast.CompositeLit: 157 for _, e := range at.Elts { 158 if err := v.evalExpr(e); err != nil { 159 return errors.WithStack(err) 160 } 161 } 162 // case *ast.BasicLit: 163 // fmt.Println("evalArgs", at.Value) 164 // v.addBox(at.Value) 165 case *ast.CallExpr: 166 if at.Fun == nil { 167 return nil 168 } 169 switch st := at.Fun.(type) { 170 case *ast.SelectorExpr: 171 if err := v.evalSelector(at, st); err != nil { 172 return errors.WithStack(err) 173 } 174 case *ast.Ident: 175 return v.evalIdent(st) 176 } 177 for _, a := range at.Args { 178 if err := v.evalArgs(a); err != nil { 179 return errors.WithStack(err) 180 } 181 } 182 } 183 return nil 184 } 185 186 func (v *visitor) evalSelector(expr *ast.CallExpr, sel *ast.SelectorExpr) error { 187 x, ok := sel.X.(*ast.Ident) 188 if !ok { 189 return nil 190 } 191 if x.Name == "packr" && sel.Sel.Name == "NewBox" { 192 for _, e := range expr.Args { 193 switch at := e.(type) { 194 case *ast.Ident: 195 switch at.Obj.Kind { 196 case ast.Var: 197 if as, ok := at.Obj.Decl.(*ast.AssignStmt); ok { 198 v.addVariable(as) 199 } 200 case ast.Con: 201 if vs, ok := at.Obj.Decl.(*ast.ValueSpec); ok { 202 v.addConstant(vs) 203 } 204 } 205 return v.evalIdent(at) 206 case *ast.BasicLit: 207 v.addBox(at.Value) 208 case *ast.CallExpr: 209 return v.evalExpr(at) 210 } 211 } 212 } 213 214 return nil 215 } 216 217 func (v *visitor) evalIdent(i *ast.Ident) error { 218 if i.Obj == nil { 219 return nil 220 } 221 if s, ok := i.Obj.Decl.(*ast.AssignStmt); ok { 222 return v.evalStmt(s) 223 } 224 return nil 225 } 226 227 func (v *visitor) addBox(b string) { 228 b = strings.Replace(b, "\"", "", -1) 229 v.Boxes = append(v.Boxes, b) 230 } 231 232 func (v *visitor) addVariable(as *ast.AssignStmt) error { 233 if len(as.Rhs) == 1 { 234 if bs, ok := as.Rhs[0].(*ast.BasicLit); ok { 235 v.addBox(bs.Value) 236 } 237 } 238 return nil 239 } 240 241 func (v *visitor) addConstant(vs *ast.ValueSpec) error { 242 if len(vs.Values) == 1 { 243 if bs, ok := vs.Values[0].(*ast.BasicLit); ok { 244 v.addBox(bs.Value) 245 } 246 } 247 return nil 248 }