Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List all functions from source code

Tags:

go

godoc

I have folder with .go files and functions defined inside them.

Is it possible to list in command line all function declarations in current folder, probably with godoc?

godoc list functions /path/to/fileOrFolder

To have such output:

func Foo(a, b int) int
func Bar(c, d int) int
like image 315
Kenenbek Arzymatov Avatar asked Mar 30 '18 11:03

Kenenbek Arzymatov


2 Answers

Definitely Peter's answer is very much sufficient, but if you want to go down the rabbit hole... and for the fun of it. Using powers of golang std lib ast.

package main

import (
    "fmt"
    "go/ast"
    "go/parser"
    "go/token"
    "io/ioutil"
    "log"
    "os"
)

func main() {
    // read file
    // here you can filepath.Walk() for your go files
    gopath := os.ExpandEnv("$GOPATH")
    fname := gopath + "/src/github.com/golang/protobuf/proto/lib.go"

    // read file
    file, err := os.Open(fname)
    if err != nil {
        log.Println(err)
        return
    }
    defer file.Close()

    // read the whole file in
    srcbuf, err := ioutil.ReadAll(file)
    if err != nil {
        log.Println(err)
        return
    }
    src := string(srcbuf)

    // file set
    fset := token.NewFileSet()
    f, err := parser.ParseFile(fset, "lib.go", src, 0)
    if err != nil {
        log.Println(err)
        return
    }

    // main inspection
    ast.Inspect(f, func(n ast.Node) bool {

        switch fn := n.(type) {

        // catching all function declarations
        // other intersting things to catch FuncLit and FuncType
        case *ast.FuncDecl:
            fmt.Print("func ")

            // if a method, explore and print receiver
            if fn.Recv != nil {
                fmt.Printf("(%s)", fields(*fn.Recv))
            }

            // print actual function name
            fmt.Printf("%v", fn.Name)

            // print function parameters
            if fn.Type.Params != nil {
                fmt.Printf("(%s)", fields(*fn.Type.Params))
            }

            // print return params
            if fn.Type.Results != nil {
                fmt.Printf("(%s)", fields(*fn.Type.Results))
            }

            fmt.Println()

        }
        return true
    })
}

func expr(e ast.Expr) (ret string) {
    switch x := e.(type) {
    case *ast.StarExpr:
        return fmt.Sprintf("%s*%v", ret, x.X)
    case *ast.Ident:
        return fmt.Sprintf("%s%v", ret, x.Name)
    case *ast.ArrayType:
        if x.Len != nil {
            log.Println("OH OH looks like homework")
            return "TODO: HOMEWORK"
        }
        res := expr(x.Elt)
        return fmt.Sprintf("%s[]%v", ret, res)
    case *ast.MapType:
        return fmt.Sprintf("map[%s]%s", expr(x.Key), expr(x.Value))
    case *ast.SelectorExpr:
        return fmt.Sprintf("%s.%s", expr(x.X), expr(x.Sel))
    default:
        fmt.Printf("\nTODO HOMEWORK: %#v\n", x)
    }
    return
}

func fields(fl ast.FieldList) (ret string) {
    pcomma := ""
    for i, f := range fl.List {
        // get all the names if present
        var names string
        ncomma := ""
        for j, n := range f.Names {
            if j > 0 {
                ncomma = ", "
            }
            names = fmt.Sprintf("%s%s%s ", names, ncomma, n)
        }
        if i > 0 {
            pcomma = ", "
        }
        ret = fmt.Sprintf("%s%s%s%s", ret, pcomma, names, expr(f.Type))
    }
    return ret
}
like image 105
k1m190r Avatar answered Nov 18 '22 11:11

k1m190r


Building on @Peter's idea on an approach, you extract out all the exported functions using a simple grep + regex like this:

grep -rP '^func\s(?:\([^\)]+\)\s)?[A-Z].*' *.go
like image 2
0xInfection Avatar answered Nov 18 '22 10:11

0xInfection