...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package pkgfact
25
26 import (
27 "fmt"
28 "go/ast"
29 "go/token"
30 "go/types"
31 "reflect"
32 "sort"
33 "strings"
34
35 "golang.org/x/tools/go/analysis"
36 )
37
38 var Analyzer = &analysis.Analyzer{
39 Name: "pkgfact",
40 Doc: "gather name/value pairs from constant declarations",
41 Run: run,
42 FactTypes: []analysis.Fact{new(pairsFact)},
43 ResultType: reflect.TypeOf(map[string]string{}),
44 }
45
46
47
48
49
50 type pairsFact []string
51
52 func (f *pairsFact) AFact() {}
53 func (f *pairsFact) String() string { return "pairs(" + strings.Join(*f, ", ") + ")" }
54
55 func run(pass *analysis.Pass) (interface{}, error) {
56 result := make(map[string]string)
57
58
59
60
61 doImport := func(spec *ast.ImportSpec) {
62 pkg := imported(pass.TypesInfo, spec)
63 var fact pairsFact
64 if pass.ImportPackageFact(pkg, &fact) {
65 for _, pair := range fact {
66 eq := strings.IndexByte(pair, '=')
67 result[pair[:eq]] = pair[1+eq:]
68 }
69 pass.ReportRangef(spec, "%s", strings.Join(fact, " "))
70 }
71 }
72
73
74 doConst := func(spec *ast.ValueSpec) {
75 if len(spec.Names) == len(spec.Values) {
76 for i := range spec.Names {
77 name := spec.Names[i].Name
78 if strings.HasPrefix(name, "_") && strings.HasSuffix(name, "_") {
79
80 if key := strings.Trim(name, "_"); key != "" {
81 value := pass.TypesInfo.Types[spec.Values[i]].Value.String()
82 result[key] = value
83 }
84 }
85 }
86 }
87 }
88
89 for _, f := range pass.Files {
90 for _, decl := range f.Decls {
91 if decl, ok := decl.(*ast.GenDecl); ok {
92 for _, spec := range decl.Specs {
93 switch decl.Tok {
94 case token.IMPORT:
95 doImport(spec.(*ast.ImportSpec))
96 case token.CONST:
97 doConst(spec.(*ast.ValueSpec))
98 }
99 }
100 }
101 }
102 }
103
104
105 keys := make([]string, 0, len(result))
106 for key := range result {
107 keys = append(keys, key)
108 }
109 sort.Strings(keys)
110 var fact pairsFact
111 for _, key := range keys {
112 fact = append(fact, fmt.Sprintf("%s=%s", key, result[key]))
113 }
114 if len(fact) > 0 {
115 pass.ExportPackageFact(&fact)
116 }
117
118 return result, nil
119 }
120
121 func imported(info *types.Info, spec *ast.ImportSpec) *types.Package {
122 obj, ok := info.Implicits[spec]
123 if !ok {
124 obj = info.Defs[spec.Name]
125 }
126 return obj.(*types.PkgName).Imported()
127 }
128
View as plain text