Browse Source

Adds functions to analyse the content of functions and removes

unused functions
content-update
Augusto 4 years ago committed by xpetit
parent
commit
a407e3504e
  1. 884
      go/tests/rc/rc.go

884
go/tests/rc/rc.go

@ -2,22 +2,15 @@ package main
import (
"flag"
"fmt"
"go/ast"
"go/parser"
"go/token"
"log"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
)
const (
identation = " "
)
type strBoolMap map[string]bool
func (a *strBoolMap) String() string {
@ -75,30 +68,8 @@ func (r *regexpFlag) Set(s string) error {
}
var (
allowedImp map[string]map[string]bool // Map of the allowed imports
allowedFun map[string]bool // Map of the allowed built-in functions
// Is necessary an array to keep all the call instances.
callX []nodePos // Keeps the name of the called functions and the position in the file.
// A map is enough for function declarations because they are unique.
funcDeclPkg map[string]*funcBody // Keeps the name of the function associated to its body and its position in the file.
allArrayTypes = true
arraysInstances []nodePos
forStmts []nodePos
basicLits []nodePos
illegals []illegal
notAllowedArrayT []string
predeclaredTypes = []string{"bool", "byte", "complex64", "complex128",
"error", "float32", "float64", "int", "int8",
"int16", "int32", "int64", "rune", "string",
"uint", "uint8", "uint16", "uint32", "uint64",
"uintptr",
}
relativeImports []string
importPkg map[string]*pkgFunc
pkgName []string
allImports map[string]bool
openImports []string
funcOccurrences map[string]int
allowedFun = make(map[string]map[string]bool)
allowedRep = make(map[string]int)
// Flags
noArrays bool
noRelativeImports bool
@ -106,6 +77,7 @@ var (
casting bool
noFor bool
noLit regexpFlag
allowBuiltin bool
)
// pkgFunc for all the functions of a given package
@ -125,44 +97,11 @@ type callVisitor struct {
Fset *token.FileSet
}
type fileVisitor struct {
funcDecl []string
funcCalls []string
selectExpr []string
arrayType []nodePos
Fset *token.FileSet
}
type pkgVisitor struct {
Fset *token.FileSet
}
type impVisitor struct {
Fset *token.FileSet
relativeImports []string
}
// Get the position of the node in the file
type locate interface {
getPos(ast.Node) string
}
func (i *impVisitor) getPos(n ast.Node) string {
return i.Fset.Position(n.Pos()).String()
}
func (fv *fileVisitor) getPos(n ast.Node) string {
return fv.Fset.Position(n.Pos()).String()
}
func (p *pkgVisitor) getPos(n ast.Node) string {
return p.Fset.Position(n.Pos()).String()
}
func (c *callVisitor) getPos(n ast.Node) string {
return c.Fset.Position(n.Pos()).String()
}
type illegal struct {
T string
Name string
@ -173,34 +112,6 @@ func (i *illegal) String() string {
return i.T + " " + i.Name + " " + i.Pos
}
func getPkgFunc(path string, fsetPkg *token.FileSet) {
i := &impVisitor{Fset: fsetPkg}
p := &pkgVisitor{Fset: fsetPkg}
pkgs, err := parser.ParseDir(fsetPkg, path, nil, parser.AllErrors)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
for pkgname := range pkgs {
pkg, _ := ast.NewPackage(fsetPkg, pkgs[pkgname].Files, nil, nil)
pkgName = append(pkgName, pkgname)
ast.Walk(i, pkg)
ast.Walk(p, pkg)
for _, v := range i.relativeImports {
if isIn(v, openImports) {
break
}
openImports = append(openImports, v)
getPkgFunc(path+"/"+v, fsetPkg)
}
}
}
// Returns the smallest block containing the position pos. It can
// return nil if `pos` is not inside any ast.BlockStmt
func smallestBlock(pos token.Pos, blocks []*ast.BlockStmt) *ast.BlockStmt {
@ -362,6 +273,239 @@ func loadProgram(path string, load loadedSource) error {
return err
}
func smallestScopeContaining(pos token.Pos, path string, load loadedSource) *ast.Scope {
pack := load[path]
sm := smallestBlock(pos, pack.blocks)
if sm == nil {
return pack.pkgScope
}
return pack.scopes[sm]
}
func lookupDefinitionObj(el element, path string, load loadedSource) *ast.Object {
scope := smallestScopeContaining(el.pos, path, load)
for scope != nil {
obj := scope.Lookup(el.name)
if obj != nil {
return obj
}
scope = scope.Outer
}
return nil
}
type visitor struct {
fset *token.FileSet
uses []element
selections map[string][]*element
arrays []*occurrence
lits []*occurrence
fors []*occurrence
callRep map[string]int
oneTime bool
}
func (v *visitor) getPos(n ast.Node) string {
return v.fset.Position(n.Pos()).String()
}
func (v *visitor) Visit(n ast.Node) ast.Visitor {
switch t := n.(type) {
case *ast.FuncDecl, *ast.GenDecl, *ast.AssignStmt:
//Avoids analysing a declaration inside a declaration
//Since this is handle by the functions `isAllowed`
if v.oneTime {
return nil
}
v.oneTime = true
return v
case *ast.BasicLit:
if t.Kind != token.CHAR && t.Kind != token.STRING {
return nil
}
v.lits = append(v.lits, &occurrence{pos: v.getPos(n), name: t.Value})
case *ast.ArrayType:
if op, ok := t.Elt.(*ast.Ident); ok {
v.arrays = append(v.arrays, &occurrence{
name: op.Name,
pos: v.getPos(n),
})
}
case *ast.ForStmt:
v.fors = append(v.fors, &occurrence{
name: "for",
pos: v.getPos(n),
})
case *ast.CallExpr:
if fun, ok := t.Fun.(*ast.Ident); ok {
v.uses = append(v.uses, element{
name: fun.Name,
pos: fun.Pos(),
})
v.callRep[fun.Name]++
}
case *ast.SelectorExpr:
if x, ok := t.X.(*ast.Ident); ok {
v.selections[x.Name] = append(v.selections[x.Name], &element{
name: t.Sel.Name,
pos: n.Pos(),
})
v.callRep[x.Name+"."+t.Sel.Name]++
}
}
return v
}
// Returns the info structure with all the ocurrences of the element
// of the analised in the project
func isAllowed(function element, path string, load loadedSource, walked map[ast.Node]bool, info *info) bool {
if walked == nil {
walked = make(map[ast.Node]bool)
}
fdef := lookupDefinitionObj(function, path, load)
if fdef == nil && !allowedFun["builtin"]["*"] && !allowedFun["builtin"][function.name] {
info.illegals = append(info.illegals, &illegal{
T: "illegal-call",
Name: function.name,
Pos: load[path].fset.Position(function.pos).String(),
})
return false
}
if fdef == nil {
return true
}
if arg, ok := fdef.Data.(data); ok && arg.argument {
return true
}
funcNode := load[path].objFunc[fdef]
v := &visitor{
selections: make(map[string][]*element),
callRep: make(map[string]int),
fset: load[path].fset,
}
if !walked[funcNode] {
ast.Walk(v, funcNode)
info.fors = append(info.fors, v.fors...)
info.lits = append(info.lits, v.lits...)
info.arrays = append(info.arrays, v.arrays...)
for name, v := range v.callRep {
info.callRep[name] += v
}
walked[funcNode] = true
}
if v.uses == nil && v.selections == nil {
return true
}
allowed := true
for _, use := range v.uses {
allowedUse := isAllowed(use, path, load, walked, info)
if !allowedUse {
info.illegals = append(info.illegals, &illegal{
T: "illegal-call",
Name: use.name,
Pos: load[path].fset.Position(use.pos).String(),
})
}
allowed = allowedUse && allowed
}
for pck, funcNames := range v.selections {
importRelPath := load[path].relImports[pck]
for _, fun := range funcNames {
if importRelPath == nil {
absImp := load[path].absImports[pck]
if absImp != nil && !allowedFun[absImp.name][fun.name] && !allowedFun[absImp.name]["*"] {
// Add to the illegals array the import and selection
info.illegals = append(info.illegals, &illegal{
T: "illegal-access",
Name: pck + "." + fun.name,
Pos: load[path].fset.Position(fun.pos).String(),
})
allowed = false
}
continue
}
newPath, err := filepath.Abs(path + "/" + importRelPath.name)
if err != nil {
panic(err)
}
newEl := element{
name: fun.name,
pos: token.Pos(0),
}
allowedSel := isAllowed(newEl, newPath, load, walked, info)
if !allowedSel {
info.illegals = append(info.illegals, &illegal{
T: "illegal-access",
Name: pck + "." + fun.name,
Pos: load[path].fset.Position(fun.pos).String(),
})
}
allowed = allowedSel && allowed
}
}
if !allowed {
info.illegals = append(info.illegals, &illegal{
T: "illegal-definition",
Name: fdef.Name,
Pos: load[path].fset.Position(funcNode.Pos()).String(),
})
}
return allowed
}
func removeRepetitions(slc []*illegal) []*illegal {
var result []*illegal
in := make(map[string]bool)
for _, v := range slc {
if in[v.Pos] {
continue
}
result = append(result, v)
in[v.Pos] = true
}
return result
}
type occurrence struct {
name string
pos string
}
type info struct {
arrays []*occurrence
lits []*occurrence
fors []*occurrence
callRep map[string]int
illegals []*illegal // functions, selections that are not allowed
}
func analyseProgram(functions []*fDefInfo, path string, load loadedSource) *info {
info := &info{
callRep: make(map[string]int),
}
walked := make(map[ast.Node]bool)
for _, v := range functions {
f := element{
name: v.obj.Name,
pos: token.Pos(0),
}
isAllowed(f, path, load, walked, info)
}
info.illegals = removeRepetitions(info.illegals)
return info
}
//reformat from the data base
func splitArgs(args string) []string {
result := strings.Split(args, " ")
@ -379,18 +523,6 @@ func rightFile(args string) string {
return ""
}
func allowCastingAndImp(allowedImports []string) {
casted := false
for i, v := range allowedImports {
casted = allow(v, casted)
if v == "--no-array" {
allArrayTypes = false
notAllowedArrayT = append(notAllowedArrayT, allowedImports[i+1:]...)
break
}
}
}
type flags struct {
l struct { // flag for char or string literal
noLit bool // true -> unallows
@ -425,36 +557,6 @@ func removeAmount(s string) string {
return strRm
}
// compares if the function is used a certain amount of times allowed
func allowedAmount(occurrences map[string]int, allowedImports []string) {
function := ""
funcSelector := ""
for _, v := range allowedImports {
// pkg in case it's a build in function and slice in case it's a selector function
pkg, slice := trimRelativeImport(v)
if slice != nil {
function = strings.Join(slice, ".")
funcSelector = removeAmount(function)
} else {
function = pkg
funcSelector = removeAmount(pkg)
}
if strings.ContainsAny(function, "#") {
strNbr := strings.TrimPrefix(function, funcSelector+"#")
nbr, err := strconv.Atoi(strNbr)
if err != nil {
log.Panic(err)
}
if occurrences[funcSelector] > nbr {
illegals = append(illegals, illegal{
T: "illegal-amount",
Name: funcSelector + " allowed count " + strNbr + " your count " + strconv.Itoa(occurrences[funcSelector]),
})
}
}
}
}
func init() {
flag.Var(&noTheseArrays, "no-these-arrays", "unallowes the array types passed in the flag")
flag.Var(&noLit, "no-lit",
@ -465,68 +567,10 @@ func init() {
flag.BoolVar(&noFor, "no-for", false, `The "for" instruction is not allowed`)
flag.BoolVar(&casting, "cast", false, "allowes casting")
flag.BoolVar(&noArrays, "no-arrays", false, "unallowes the array types passed in the flag")
flag.BoolVar(&allowBuiltin, "allow-builtin", false, "Allowes all builtin functions and casting")
}
func main() {
if len(os.Args) < 2 {
fmt.Println("No file or directory")
return
}
var allowedImports []string
if len(os.Args) > 2 {
allowedImports = splitArgs(os.Args[2])
}
allowCastingAndImp(allowedImports)
flag := parseFlags(allowedImports)
filename := strings.TrimSpace(rightFile(os.Args[1]))
split := strings.Split(filename, "/")
path := strings.Join(split[:len(split)-1], "/")
if path == "" {
path = "."
}
fsetFile := token.NewFileSet()
fsetPkg := token.NewFileSet()
file, err := parser.ParseFile(fsetFile, filename, nil, parser.AllErrors)
if err != nil {
fmt.Println("Parsing")
fmt.Println(err.Error())
os.Exit(1)
}
// Get all the name of all functions declared in the file
w := &fileVisitor{Fset: fsetFile}
ast.Walk(w, file)
getPkgFunc(path, fsetPkg)
for _, v := range w.funcDecl {
isFuncAllowed(v, fsetPkg)
}
// TODO: Parsing the arguments for the --max-occurrences flag
allowedAmount(funcOccurrences, allowedImports)
if flag != nil {
flag.unallowLits()
}
analyzeArrayT()
analyzeForStmt(allowedImports)
if len(illegals) > 0 {
fmt.Println("Cheating")
for _, i := range illegals {
fmt.Println(identation + i.String())
}
os.Exit(1)
}
}
type element struct {
@ -635,20 +679,6 @@ func (l *loadVisitor) Visit(n ast.Node) ast.Visitor {
return l
}
func (f flags) unallowLits() {
if f.l.noLit {
for _, v := range basicLits {
if !f.isLitAllowed(v.name) {
illegals = append(illegals, illegal{
T: "illegal-literal",
Name: v.name,
Pos: v.position,
})
}
}
}
}
func (f flags) isLitAllowed(s string) bool {
matched, err := regexp.Match(f.l.pattern, []byte(s))
@ -659,448 +689,8 @@ func (f flags) isLitAllowed(s string) bool {
return !matched
}
func analyzeForStmt(args []string) {
if isIn("--no-for", args) {
for _, v := range forStmts {
illegals = append(illegals, illegal{
T: "illegal-loop",
Name: v.name,
Pos: v.position,
})
}
}
}
func analyzeArrayT() {
if !allArrayTypes {
l := len(notAllowedArrayT)
for _, v := range arraysInstances {
if l == 0 ||
isIn(v.name, notAllowedArrayT) {
illegals = append(illegals, illegal{
T: "illegal-array-type",
Name: v.name,
Pos: v.position,
})
}
}
}
}
func allow(s string, casted bool) bool {
if strings.ContainsRune(s, '.') {
allowImport(s)
} else {
if allowedFun == nil {
allowedFun = make(map[string]bool)
}
if strings.ContainsAny(s, "#") {
s = removeAmount(s)
}
allowedFun[s] = true
}
if s == "--cast" && !casted {
for _, v := range predeclaredTypes {
allow(v, false)
}
return true
}
return casted
}
// Returns true if the string matches the format of a relative import
func isRelativeImport(s string) bool {
reg := regexp.MustCompile(`^\.`)
return reg.Match([]byte(s))
}
// Returns true if the string represents an import package
// i.e and expresion like <lib>.<function>
func isImport(s string) bool {
matched, _ := regexp.MatchString(`.\..`, s)
return matched
}
func trimRelativeImport(str string) (string, []string) {
var pkg string
var slice []string
if isImport(str) && !isRelativeImport(str) {
splited := strings.Split(str, "/")
slice = strings.Split(splited[len(splited)-1], ".")
splited[len(splited)-1] = slice[0]
pkg = strings.Join(splited, "/")
if allowedImp[slice[0]] == nil {
allowedImp[slice[0]] = make(map[string]bool)
}
fn := slice[len(slice)-1]
allowedImp[slice[0]][fn] = true
} else {
pkg = str
}
return pkg, slice
}
func allowImport(s string) {
if allowedImp == nil {
allowedImp = make(map[string]map[string]bool)
}
if allImports == nil {
allImports = make(map[string]bool)
}
pkg, slice := trimRelativeImport(s)
allImports[pkg] = true
fn := "*"
if len(slice) > 1 {
fn = removeAmount(slice[1])
}
if allowedImp[pkg] == nil {
allowedImp[pkg] = make(map[string]bool)
}
allowedImp[pkg][fn] = true
}
func addToIllegals(funcname string) {
for _, v := range callX {
if v.name == funcname {
pos := v.position
setIllegal("illegal-call", v.name, pos)
}
}
if funcDeclPkg[funcname] != nil {
pos := funcDeclPkg[funcname].position
setIllegal("illegal-call", funcname, pos)
}
}
// First ignoring the imported functions
func isFuncAllowed(funcname string, fset *token.FileSet) bool {
if allowedFun[funcname] {
return true
}
if !isFuncDeclIn(funcname, funcDeclPkg) {
addToIllegals(funcname)
return false
}
bodyf := funcDeclPkg[funcname].body
if bodyf == nil {
addToIllegals(funcname)
fmt.Println("Body is nil")
return false
}
c := &callVisitor{Fset: fset}
ast.Walk(c, bodyf)
res := true
for _, v := range c.Calls {
if v == funcname {
continue
}
allowed := isFuncAllowed(v, fset)
if !allowed {
addToIllegals(funcname)
for _, v := range c.Calls {
if v == funcname {
continue
}
allowed := isFuncAllowed(v, fset)
if !allowed {
addToIllegals(funcname)
}
res = res && allowed
}
}
res = res && allowed
}
return res
}
func isFuncDeclIn(funcname string, fundecl map[string]*funcBody) bool {
return fundecl[funcname] != nil
}
func isFuncCallIn(funcname string, funP []nodePos) bool {
for _, v := range funP {
if v.name == funcname {
return true
}
}
return false
}
func isIn(s string, slc []string) bool {
for _, v := range slc {
if s == v {
return true
}
}
return false
}
// Keeps the positions of each function call and declaration
type funcBody struct {
position string
body *ast.BlockStmt
}
type nodePos struct {
position string
name string
}
func (fv *fileVisitor) Visit(n ast.Node) ast.Visitor {
fv.checkImport(n)
if ide, ok := n.(*ast.CallExpr); ok {
if opt, ok := ide.Fun.(*ast.Ident); ok {
fv.funcCalls = append(fv.funcCalls, opt.Name)
newFun := nodePos{position: fv.getPos(n), name: opt.Name}
callX = append(callX, newFun)
}
}
if expr, ok := n.(*ast.SelectorExpr); ok {
if x, ok := expr.X.(*ast.Ident); ok {
fv.selectExpr = append(fv.selectExpr, x.Name+"."+expr.Sel.Name)
// saves the function in to the map, from the package
if importPkg[x.Name] != nil {
importPkg[x.Name].functions = append(importPkg[x.Name].functions, x.Name+"."+expr.Sel.Name)
}
if funcOccurrences == nil {
funcOccurrences = make(map[string]int)
}
funcOccurrences[x.Name+"."+expr.Sel.Name]++
}
}
if ex, ok := n.(*ast.ArrayType); ok {
if op, ok := ex.Elt.(*ast.Ident); ok {
fv.arrayType = append(fv.arrayType, nodePos{
name: op.Name,
position: fv.getPos(n),
})
}
}
if exp, ok := n.(*ast.FuncDecl); ok {
fv.funcDecl = append(fv.funcDecl, exp.Name.Name)
for _, v := range exp.Type.Params.List {
if _, ok := v.Type.(*ast.FuncType); ok {
if allowedFun == nil {
allowedFun = make(map[string]bool)
}
for _, name := range v.Names {
allowedFun[name.Name] = true
}
}
}
}
if ex, ok := n.(*ast.AssignStmt); ok {
if exp, ok := ex.Rhs[0].(*ast.FuncLit); ok {
if ide, ok := ex.Lhs[0].(*ast.Ident); ok {
if funcDeclPkg == nil {
funcDeclPkg = make(map[string]*funcBody)
}
funcDeclPkg[ide.Name] = &funcBody{body: exp.Body, position: fv.getPos(n)}
}
}
}
return fv
}
func positionIsIn(illegals []illegal, pos string) bool {
for _, v := range illegals {
if v.Pos == pos {
return true
}
}
return false
}
func (p *pkgVisitor) Visit(n ast.Node) ast.Visitor {
if exp, ok := n.(*ast.FuncDecl); ok {
if funcDeclPkg == nil {
funcDeclPkg = make(map[string]*funcBody)
}
funcDeclPkg[exp.Name.Name] = &funcBody{body: exp.Body, position: p.getPos(n)}
for _, pkg := range pkgName {
if importPkg[pkg] != nil && isIn(pkg+"."+exp.Name.Name, importPkg[pkg].functions) {
funcDeclPkg[pkg+"."+exp.Name.Name] = &funcBody{body: exp.Body, position: p.getPos(n)}
}
}
}
if ex, ok := n.(*ast.AssignStmt); ok {
if exp, ok := ex.Rhs[0].(*ast.FuncLit); ok {
if ide, ok := ex.Lhs[0].(*ast.Ident); ok {
if funcDeclPkg == nil {
funcDeclPkg = make(map[string]*funcBody)
}
funcDeclPkg[ide.Name] = &funcBody{body: exp.Body, position: p.getPos(n)}
}
}
}
return p
}
func setIllegal(illegalType, funcName, pos string) {
if !positionIsIn(illegals, pos) {
illegals = append(illegals, illegal{
T: illegalType,
Name: funcName,
Pos: pos,
})
}
}
// Signals that exists at least one callExpr in the node
func (c *callVisitor) Visit(n ast.Node) ast.Visitor {
if id, ok := n.(*ast.BasicLit); ok {
if id.Kind != token.CHAR && id.Kind != token.STRING {
return nil
}
basicLits = append(basicLits, nodePos{position: c.getPos(n), name: id.Value})
}
if exp, ok := n.(*ast.CallExpr); ok {
if fun, ok := exp.Fun.(*ast.Ident); ok {
c.Calls = append(c.Calls, fun.Name)
newFun := nodePos{position: c.getPos(n), name: fun.Name}
callX = append(callX, newFun)
if funcOccurrences == nil {
funcOccurrences = make(map[string]int)
}
funcOccurrences[fun.Name]++
return c
}
}
// SelectorExpr is when we access a value (dot opperator)
// We need to check those for specific functions
if expr, ok := n.(*ast.SelectorExpr); ok {
x, ok := expr.X.(*ast.Ident)
if !ok {
// in this case we are deep in an access
// example, fmt is banned, but pouet isn't.
// we must allow pouet.fmt.x but not fmt.x
// this is the pouet.fmt.x case.
return c
}
pkg := allowedImp[x.Name]
f := x.Name + "." + expr.Sel.Name
if funcDeclPkg[f] != nil {
c.Calls = append(c.Calls, f)
return c
}
if pkg == nil {
if allImports[x.Name] {
pos := c.getPos(n)
setIllegal("illegal-access", f, pos)
}
return c
}
if !pkg["*"] && !pkg[expr.Sel.Name] {
// all the package is not whiteList and is not explicitly allowed
pos := c.getPos(n)
setIllegal("illegal-access", f, pos)
}
}
if ex, ok := n.(*ast.ArrayType); ok {
if op, ok := ex.Elt.(*ast.Ident); ok {
arraysInstances = append(arraysInstances, nodePos{
name: op.Name,
position: c.getPos(n),
})
}
}
if _, ok := n.(*ast.ForStmt); ok {
forStmts = append(forStmts, nodePos{
name: "for",
position: c.getPos(n),
})
}
return c
}
func (fv *fileVisitor) checkImport(n ast.Node) ast.Visitor {
if spec, ok := n.(*ast.ImportSpec); ok {
pkg := spec.Path.Value[1 : len(spec.Path.Value)-1]
if allowedImp[pkg] == nil {
pos := fv.getPos(n)
setIllegal("illegal-import", pkg, pos)
return fv
}
// if the import is named, we need to move it to the new name
name := ""
if spec.Name != nil {
name = spec.Name.Name
} else if strings.ContainsRune(pkg, '/') {
parts := strings.Split(pkg, "/")
name = parts[len(parts)-1]
}
if allowedImp[pkg] != nil {
if name != "" {
allowedImp[name] = allowedImp[pkg]
allowedImp[pkg] = nil
}
}
if isRelativeImport(pkg) {
if importPkg == nil {
importPkg = make(map[string]*pkgFunc)
}
if name != "" {
importPkg[name] = &pkgFunc{
path: pkg,
}
}
relativeImports = append(relativeImports, name)
}
}
return fv
}
func (i *impVisitor) Visit(n ast.Node) ast.Visitor {
if spec, ok := n.(*ast.ImportSpec); ok {
pkg := spec.Path.Value[1 : len(spec.Path.Value)-1]
if allImports == nil {
allImports = make(map[string]bool)
}
pkgSplit := strings.Split(pkg, "/")
allImports[pkg] = true
allImports[pkgSplit[len(pkgSplit)-1]] = true
if isRelativeImport(pkg) {
i.relativeImports = append(i.relativeImports, pkg)
}
}
return i
}
func funcs(s *ast.Scope) []string {
scopeStr := s.String()
scp := strings.Split(scopeStr, "\n")
var funcs []string
for _, v := range scp {
nv := strings.TrimSpace(v)
if strings.HasPrefix(nv, "func ") {
funcs = append(funcs, strings.TrimPrefix(nv, "func "))
}
}
return funcs
}

Loading…
Cancel
Save