package main import ( "flag" "fmt" "go/ast" "go/parser" "go/token" "log" "os" "path/filepath" "strings" ) var ( customLogs = flag.String("funcs", "", "Custom functions to export delimited by ','.") ) func main() { flag.Parse() if flag.NArg() != 1 { log.Fatalln("directory must be provided") } directories, err := getAllSubdirectories(flag.Arg(0)) if err != nil { log.Fatalf("failed to list subdirs: %v", err) } for _, dir := range directories { fset := token.NewFileSet() packages, err := parser.ParseDir(fset, dir, nil, 0) if err != nil { log.Fatalf("failed to parse directory %s: %v", dir, err) } for _, p := range packages { for _, f := range p.Files { ast.Inspect(f, func(n ast.Node) bool { expr, ok := n.(*ast.CallExpr) if !ok { return true } if isLogDot(expr.Fun) && len(expr.Args) > 0 && isStringValue(expr.Args[0]) { position := fset.Position(expr.Pos()) fmt.Printf("%v %s\n", position, stringValue(expr.Args[0])) return false } return true }) } } } } func getAllSubdirectories(dir string) ([]string, error) { result := make([]string, 0) stack := make([]string, 0) stack = append(stack, dir) for len(stack) > 0 { current := stack[len(stack)-1] stack = stack[:len(stack)-1] entities, err := os.ReadDir(current) if err != nil { return nil, err } for _, e := range entities { if e.IsDir() { path := filepath.Join(current, e.Name()) result = append(result, path) stack = append(stack, path) } } } return result, nil } func isLogDot(expr ast.Expr) bool { sel, ok := expr.(*ast.SelectorExpr) return ok && (isIdent(sel.Sel, "Debug") || isIdent(sel.Sel, "Info") || isIdent(sel.Sel, "Warn") || isIdent(sel.Sel, "Error") || isCustomLog(sel.Sel)) } func isCustomLog(expr ast.Expr) bool { id, ok := expr.(*ast.Ident) if !ok { return false } if len(*customLogs) == 0 { return false } ss := strings.Split(*customLogs, ",") for i := range ss { if id.Name == ss[i] { return true } } return false } func isIdent(expr ast.Expr, ident string) bool { id, ok := expr.(*ast.Ident) if !ok { return false } if id.Name == ident { return true } return false } func isStringValue(expr ast.Expr) bool { basicLit, ok := expr.(*ast.BasicLit) return ok && basicLit.Kind == token.STRING } func stringValue(expr ast.Expr) string { basicLit, _ := expr.(*ast.BasicLit) return basicLit.Value }