godog/ast.go
2016-05-23 17:53:51 +03:00

109 строки
2,1 КиБ
Go

package godog
import (
"go/ast"
"go/build"
"go/token"
"path"
"strings"
)
func removeUnusedImports(f *ast.File) {
used := usedPackages(f)
isUsed := func(p string) bool {
for _, ref := range used {
if p == ref {
return true
}
}
return p == "_"
}
var decls []ast.Decl
for _, d := range f.Decls {
gen, ok := d.(*ast.GenDecl)
if ok && gen.Tok == token.IMPORT {
var specs []ast.Spec
for _, spec := range gen.Specs {
impspec := spec.(*ast.ImportSpec)
ipath := strings.Trim(impspec.Path.Value, `\"`)
check := importPathToName(ipath)
if impspec.Name != nil {
check = impspec.Name.Name
}
if isUsed(check) {
specs = append(specs, spec)
}
}
if len(specs) == 0 {
continue
}
gen.Specs = specs
}
decls = append(decls, d)
}
f.Decls = decls
}
func deleteTestMainFunc(f *ast.File) {
var decls []ast.Decl
var hadMain bool
for _, d := range f.Decls {
fun, ok := d.(*ast.FuncDecl)
if !ok {
decls = append(decls, d)
continue
}
if fun.Name.Name != "TestMain" {
decls = append(decls, fun)
} else {
hadMain = true
}
}
f.Decls = decls
if hadMain {
removeUnusedImports(f)
}
}
type visitFn func(node ast.Node) ast.Visitor
func (fn visitFn) Visit(node ast.Node) ast.Visitor {
return fn(node)
}
func usedPackages(f *ast.File) []string {
var refs []string
var visitor visitFn
visitor = visitFn(func(node ast.Node) ast.Visitor {
if node == nil {
return visitor
}
switch v := node.(type) {
case *ast.SelectorExpr:
xident, ok := v.X.(*ast.Ident)
if !ok {
break
}
if xident.Obj != nil {
// if the parser can resolve it, it's not a package ref
break
}
refs = append(refs, xident.Name)
}
return visitor
})
ast.Walk(visitor, f)
return refs
}
// importPathToName finds out the actual package name, as declared in its .go files.
// If there's a problem, it falls back to using importPathToNameBasic.
func importPathToName(importPath string) (packageName string) {
if buildPkg, err := build.Import(importPath, "", 0); err == nil {
return buildPkg.Name
}
return path.Base(importPath)
}