You've already forked postgres_exporter
mirror of
https://github.com/prometheus-community/postgres_exporter.git
synced 2025-09-10 22:51:54 +03:00
Revamp the build system to be more inline with other Prometheus exporters. Notably add Darwin and Windows build targets, and add support for releases using tar files.
214 lines
4.5 KiB
Go
214 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/build"
|
|
"go/parser"
|
|
"go/token"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
pwd = "./"
|
|
)
|
|
|
|
func init() {
|
|
//TODO allow build tags
|
|
build.Default.UseAllFiles = true
|
|
}
|
|
|
|
func usage() {
|
|
log.Printf("Usage of %s:\n", os.Args[0])
|
|
log.Printf("\nnakedret [flags] # runs on package in current directory\n")
|
|
log.Printf("\nnakedret [flags] [packages]\n")
|
|
log.Printf("Flags:\n")
|
|
flag.PrintDefaults()
|
|
}
|
|
|
|
type returnsVisitor struct {
|
|
f *token.FileSet
|
|
maxLength uint
|
|
}
|
|
|
|
func main() {
|
|
|
|
// Remove log timestamp
|
|
log.SetFlags(0)
|
|
|
|
maxLength := flag.Uint("l", 5, "maximum number of lines for a naked return function")
|
|
flag.Usage = usage
|
|
flag.Parse()
|
|
|
|
if err := checkNakedReturns(flag.Args(), maxLength); err != nil {
|
|
log.Println(err)
|
|
}
|
|
}
|
|
|
|
func checkNakedReturns(args []string, maxLength *uint) error {
|
|
|
|
fset := token.NewFileSet()
|
|
|
|
files, err := parseInput(args, fset)
|
|
if err != nil {
|
|
return fmt.Errorf("could not parse input %v", err)
|
|
}
|
|
|
|
if maxLength == nil {
|
|
return errors.New("max length nil")
|
|
}
|
|
|
|
retVis := &returnsVisitor{
|
|
f: fset,
|
|
maxLength: *maxLength,
|
|
}
|
|
|
|
for _, f := range files {
|
|
ast.Walk(retVis, f)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func parseInput(args []string, fset *token.FileSet) ([]*ast.File, error) {
|
|
var directoryList []string
|
|
var fileMode bool
|
|
files := make([]*ast.File, 0)
|
|
|
|
if len(args) == 0 {
|
|
directoryList = append(directoryList, pwd)
|
|
} else {
|
|
for _, arg := range args {
|
|
if strings.HasSuffix(arg, "/...") && isDir(arg[:len(arg)-len("/...")]) {
|
|
|
|
for _, dirname := range allPackagesInFS(arg) {
|
|
directoryList = append(directoryList, dirname)
|
|
}
|
|
|
|
} else if isDir(arg) {
|
|
directoryList = append(directoryList, arg)
|
|
|
|
} else if exists(arg) {
|
|
if strings.HasSuffix(arg, ".go") {
|
|
fileMode = true
|
|
f, err := parser.ParseFile(fset, arg, nil, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
files = append(files, f)
|
|
} else {
|
|
return nil, fmt.Errorf("invalid file %v specified", arg)
|
|
}
|
|
} else {
|
|
|
|
//TODO clean this up a bit
|
|
imPaths := importPaths([]string{arg})
|
|
for _, importPath := range imPaths {
|
|
pkg, err := build.Import(importPath, ".", 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var stringFiles []string
|
|
stringFiles = append(stringFiles, pkg.GoFiles...)
|
|
// files = append(files, pkg.CgoFiles...)
|
|
stringFiles = append(stringFiles, pkg.TestGoFiles...)
|
|
if pkg.Dir != "." {
|
|
for i, f := range stringFiles {
|
|
stringFiles[i] = filepath.Join(pkg.Dir, f)
|
|
}
|
|
}
|
|
|
|
fileMode = true
|
|
for _, stringFile := range stringFiles {
|
|
f, err := parser.ParseFile(fset, stringFile, nil, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
files = append(files, f)
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we're not in file mode, then we need to grab each and every package in each directory
|
|
// we can to grab all the files
|
|
if !fileMode {
|
|
for _, fpath := range directoryList {
|
|
pkgs, err := parser.ParseDir(fset, fpath, nil, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, pkg := range pkgs {
|
|
for _, f := range pkg.Files {
|
|
files = append(files, f)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return files, nil
|
|
}
|
|
|
|
func isDir(filename string) bool {
|
|
fi, err := os.Stat(filename)
|
|
return err == nil && fi.IsDir()
|
|
}
|
|
|
|
func exists(filename string) bool {
|
|
_, err := os.Stat(filename)
|
|
return err == nil
|
|
}
|
|
|
|
func (v *returnsVisitor) Visit(node ast.Node) ast.Visitor {
|
|
var namedReturns []*ast.Ident
|
|
|
|
funcDecl, ok := node.(*ast.FuncDecl)
|
|
if !ok {
|
|
return v
|
|
}
|
|
var functionLineLength int
|
|
// We've found a function
|
|
if funcDecl.Type != nil && funcDecl.Type.Results != nil {
|
|
for _, field := range funcDecl.Type.Results.List {
|
|
for _, ident := range field.Names {
|
|
if ident != nil {
|
|
namedReturns = append(namedReturns, ident)
|
|
}
|
|
}
|
|
}
|
|
file := v.f.File(funcDecl.Pos())
|
|
functionLineLength = file.Position(funcDecl.End()).Line - file.Position(funcDecl.Pos()).Line
|
|
}
|
|
|
|
if len(namedReturns) > 0 && funcDecl.Body != nil {
|
|
// Scan the body for usage of the named returns
|
|
for _, stmt := range funcDecl.Body.List {
|
|
|
|
switch s := stmt.(type) {
|
|
case *ast.ReturnStmt:
|
|
if len(s.Results) == 0 {
|
|
file := v.f.File(s.Pos())
|
|
if file != nil && uint(functionLineLength) > v.maxLength {
|
|
if funcDecl.Name != nil {
|
|
log.Printf("%v:%v %v naked returns on %v line function \n", file.Name(), file.Position(s.Pos()).Line, funcDecl.Name.Name, functionLineLength)
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
|
|
return v
|
|
}
|