pkger/parser/visitor.go

344 lines
5.3 KiB
Go
Raw Normal View History

2019-08-01 19:03:12 +03:00
package parser
import (
"go/ast"
2019-09-23 16:53:30 +03:00
"go/token"
2019-10-21 17:27:04 +03:00
"path/filepath"
2019-08-01 19:03:12 +03:00
"strconv"
2019-10-21 17:27:04 +03:00
"github.com/markbates/pkger/here"
2019-08-01 19:03:12 +03:00
)
2019-09-23 16:53:30 +03:00
type visitor func(node ast.Node) (w ast.Visitor)
2019-08-01 19:03:12 +03:00
2019-09-23 16:53:30 +03:00
func (v visitor) Visit(node ast.Node) ast.Visitor {
return v(node)
2019-08-01 19:03:12 +03:00
}
2019-09-23 16:53:30 +03:00
// inspired by https://gist.github.com/cryptix/d1b129361cea51a59af2
type file struct {
fset *token.FileSet
astFile *ast.File
filename string
2019-10-21 17:27:04 +03:00
decls Decls
2019-09-23 16:53:30 +03:00
}
2019-08-01 19:03:12 +03:00
2019-09-23 16:53:30 +03:00
func (f *file) walk(fn func(ast.Node) bool) {
ast.Walk(walker(fn), f.astFile)
2019-08-01 19:03:12 +03:00
}
2019-10-21 17:27:04 +03:00
func (f *file) find() (Decls, error) {
2019-10-23 20:19:39 +03:00
// --- virtual calls first ---
if err := f.findMkdirAllCalls(); err != nil {
return nil, err
}
if err := f.findCreateCalls(); err != nil {
return nil, err
}
// -- physical calls second ---
if err := f.findStatCalls(); err != nil {
return nil, err
}
2019-09-23 16:53:30 +03:00
if err := f.findOpenCalls(); err != nil {
return nil, err
2019-08-30 05:30:00 +03:00
}
2019-08-01 19:03:12 +03:00
if err := f.findHTTPCalls(); err != nil {
return nil, err
}
2019-09-23 16:53:30 +03:00
if err := f.findWalkCalls(); err != nil {
return nil, err
2019-08-01 19:03:12 +03:00
}
2019-09-23 16:53:30 +03:00
2019-10-21 17:27:04 +03:00
return f.decls, nil
2019-08-01 19:03:12 +03:00
}
2019-10-21 17:27:04 +03:00
func (f *file) asValue(node ast.Node) (string, error) {
var s string
2019-08-01 19:03:12 +03:00
2019-10-18 19:01:48 +03:00
switch x := node.(type) {
case *ast.BasicLit:
2019-10-21 17:27:04 +03:00
s = x.Value
2019-10-18 19:01:48 +03:00
case *ast.Ident:
2019-10-21 17:27:04 +03:00
s = x.Name
2019-10-18 19:01:48 +03:00
}
2019-10-21 17:27:04 +03:00
return strconv.Unquote(s)
2019-10-18 19:01:48 +03:00
}
2019-10-23 20:19:39 +03:00
func (f *file) findMkdirAllCalls() error {
var err error
f.walk(func(node ast.Node) bool {
ce, ok := node.(*ast.CallExpr)
if !ok {
return true
}
exists := isPkgDot(ce.Fun, "pkger", "MkdirAll")
if !(exists) || len(ce.Args) != 2 {
return true
}
n := ce.Args[0]
s, err := f.asValue(n)
if err != nil {
return false
}
info, err := here.Dir(filepath.Dir(f.filename))
if err != nil {
return false
}
pf := &File{
Abs: f.filename,
Here: info,
}
decl := MkdirAllDecl{
file: pf,
pos: n.Pos(),
value: s,
}
f.decls = append(f.decls, decl)
return true
})
return err
}
func (f *file) findStatCalls() error {
var err error
f.walk(func(node ast.Node) bool {
ce, ok := node.(*ast.CallExpr)
if !ok {
return true
}
exists := isPkgDot(ce.Fun, "pkger", "Stat")
if !(exists) || len(ce.Args) != 1 {
return true
}
n := ce.Args[0]
s, err := f.asValue(n)
if err != nil {
return false
}
info, err := here.Dir(filepath.Dir(f.filename))
if err != nil {
return false
}
pf := &File{
Abs: f.filename,
Here: info,
}
decl := StatDecl{
file: pf,
pos: n.Pos(),
value: s,
}
f.decls = append(f.decls, decl)
return true
})
return err
}
func (f *file) findCreateCalls() error {
var err error
f.walk(func(node ast.Node) bool {
ce, ok := node.(*ast.CallExpr)
if !ok {
return true
}
exists := isPkgDot(ce.Fun, "pkger", "Create")
if !(exists) || len(ce.Args) != 1 {
return true
}
n := ce.Args[0]
s, err := f.asValue(n)
if err != nil {
return false
}
info, err := here.Dir(filepath.Dir(f.filename))
if err != nil {
return false
}
pf := &File{
Abs: f.filename,
Here: info,
}
decl := CreateDecl{
file: pf,
pos: n.Pos(),
value: s,
}
f.decls = append(f.decls, decl)
return true
})
return err
}
2019-09-23 16:53:30 +03:00
func (f *file) findOpenCalls() error {
var err error
f.walk(func(node ast.Node) bool {
ce, ok := node.(*ast.CallExpr)
if !ok {
return true
2019-08-01 19:03:12 +03:00
}
2019-09-23 16:53:30 +03:00
exists := isPkgDot(ce.Fun, "pkger", "Open")
if !(exists) || len(ce.Args) != 1 {
return true
2019-08-01 19:03:12 +03:00
}
2019-10-21 17:27:04 +03:00
n := ce.Args[0]
s, err := f.asValue(n)
if err != nil {
return false
}
info, err := here.Dir(filepath.Dir(f.filename))
if err != nil {
2019-10-18 19:01:48 +03:00
return false
2019-08-01 19:03:12 +03:00
}
2019-09-23 16:53:30 +03:00
2019-10-21 17:27:04 +03:00
pf := &File{
Abs: f.filename,
Here: info,
}
decl := OpenDecl{
file: pf,
pos: n.Pos(),
value: s,
}
f.decls = append(f.decls, decl)
2019-09-23 16:53:30 +03:00
return true
})
return err
2019-08-01 19:03:12 +03:00
}
2019-09-23 16:53:30 +03:00
func (f *file) findWalkCalls() error {
var err error
f.walk(func(node ast.Node) bool {
ce, ok := node.(*ast.CallExpr)
if !ok {
return true
}
2019-08-01 19:03:12 +03:00
2019-09-23 16:53:30 +03:00
exists := isPkgDot(ce.Fun, "pkger", "Walk")
if !(exists) || len(ce.Args) != 2 {
return true
}
2019-08-01 19:03:12 +03:00
2019-10-21 17:27:04 +03:00
n := ce.Args[0]
s, err := f.asValue(n)
if err != nil {
2019-10-18 19:01:48 +03:00
return false
2019-08-01 19:03:12 +03:00
}
2019-10-21 17:27:04 +03:00
info, err := here.Dir(filepath.Dir(f.filename))
if err != nil {
return false
}
2019-08-01 19:03:12 +03:00
2019-10-21 17:27:04 +03:00
pf := &File{
Abs: f.filename,
Here: info,
}
decl := WalkDecl{
file: pf,
pos: n.Pos(),
value: s,
}
f.decls = append(f.decls, decl)
2019-09-23 16:53:30 +03:00
return true
})
return err
2019-08-01 19:03:12 +03:00
}
func (f *file) findHTTPCalls() error {
var err error
f.walk(func(node ast.Node) bool {
ce, ok := node.(*ast.CallExpr)
if !ok {
return true
}
2019-10-25 16:30:31 +03:00
exists := isPkgDot(ce.Fun, "pkger", "Dir")
if !(exists) || len(ce.Args) != 1 {
return true
}
n := ce.Args[0]
s, err := f.asValue(n)
if err != nil {
return false
}
info, err := here.Dir(filepath.Dir(f.filename))
if err != nil {
return false
}
pf := &File{
Abs: f.filename,
Here: info,
}
decl := HTTPDecl{
file: pf,
pos: n.Pos(),
value: s,
}
f.decls = append(f.decls, decl)
return true
})
return err
}
2019-09-23 16:53:30 +03:00
// helpers
// =======
func isPkgDot(expr ast.Expr, pkg, name string) bool {
sel, ok := expr.(*ast.SelectorExpr)
return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name)
2019-08-01 19:03:12 +03:00
}
2019-09-23 16:53:30 +03:00
func isIdent(expr ast.Expr, ident string) bool {
id, ok := expr.(*ast.Ident)
return ok && id.Name == ident
2019-08-01 19:03:12 +03:00
}
2019-09-23 16:53:30 +03:00
// wrap a function to fulfill ast.Visitor interface
type walker func(ast.Node) bool
func (w walker) Visit(node ast.Node) ast.Visitor {
if w(node) {
return w
2019-08-01 19:03:12 +03:00
}
return nil
}