forked from go-chi/docgen
-
Notifications
You must be signed in to change notification settings - Fork 0
/
funcinfo.go
115 lines (97 loc) · 2.26 KB
/
funcinfo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package docgen
import (
"go/parser"
"go/token"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
)
//FuncInfo describes a function's metadata
type FuncInfo struct {
Pkg string `json:"pkg"`
Func string `json:"func"`
Comment string `json:"comment"`
File string `json:"file,omitempty"`
Line int `json:"line,omitempty"`
Anonymous bool `json:"anonymous,omitempty"`
Unresolvable bool `json:"unresolvable,omitempty"`
}
//GetFuncInfo returns a FuncInfo object for a given interface
func GetFuncInfo(i interface{}) FuncInfo {
fi := FuncInfo{}
frame := getCallerFrame(i)
goPathSrc := filepath.Join(os.Getenv("GOPATH"), "src")
if frame == nil {
fi.Unresolvable = true
return fi
}
pkgName := getPkgName(frame.File)
if pkgName == "chi" {
fi.Unresolvable = true
}
funcPath := frame.Func.Name()
idx := strings.Index(funcPath, "/"+pkgName)
if idx > 0 {
fi.Pkg = funcPath[:idx+1+len(pkgName)]
fi.Func = funcPath[idx+2+len(pkgName):]
} else {
fi.Func = funcPath
}
if strings.Index(fi.Func, ".func") > 0 {
fi.Anonymous = true
}
fi.File = frame.File
fi.Line = frame.Line
if filepath.HasPrefix(fi.File, goPathSrc) {
fi.File = fi.File[len(goPathSrc)+1:]
}
// Check if file info is unresolvable
if strings.Index(funcPath, pkgName) < 0 {
fi.Unresolvable = true
}
if !fi.Unresolvable {
fi.Comment = getFuncComment(frame.File, frame.Line)
}
return fi
}
func getCallerFrame(i interface{}) *runtime.Frame {
pc := reflect.ValueOf(i).Pointer()
frames := runtime.CallersFrames([]uintptr{pc})
if frames == nil {
return nil
}
frame, _ := frames.Next()
if frame.Entry == 0 {
return nil
}
return &frame
}
func getPkgName(file string) string {
fset := token.NewFileSet()
astFile, err := parser.ParseFile(fset, file, nil, parser.PackageClauseOnly)
if err != nil {
return ""
}
if astFile.Name == nil {
return ""
}
return astFile.Name.Name
}
func getFuncComment(file string, line int) string {
fset := token.NewFileSet()
astFile, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
if err != nil {
return ""
}
if len(astFile.Comments) == 0 {
return ""
}
for _, cmt := range astFile.Comments {
if fset.Position(cmt.End()).Line+1 == line {
return cmt.Text()
}
}
return ""
}