...
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 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/pkgfact",
42 Run: run,
43 FactTypes: []analysis.Fact{new(pairsFact)},
44 ResultType: reflect.TypeOf(map[string]string{}),
45 }
46
47
48
49
50
51 type pairsFact []string
52
53 func (f *pairsFact) AFact() {}
54 func (f *pairsFact) String() string { return "pairs(" + strings.Join(*f, ", ") + ")" }
55
56 func run(pass *analysis.Pass) (interface{}, error) {
57 result := make(map[string]string)
58
59
60
61
62 doImport := func(spec *ast.ImportSpec) {
63 pkg := imported(pass.TypesInfo, spec)
64 var fact pairsFact
65 if pass.ImportPackageFact(pkg, &fact) {
66 for _, pair := range fact {
67 eq := strings.IndexByte(pair, '=')
68 result[pair[:eq]] = pair[1+eq:]
69 }
70 pass.ReportRangef(spec, "%s", strings.Join(fact, " "))
71 }
72 }
73
74
75 doConst := func(spec *ast.ValueSpec) {
76 if len(spec.Names) == len(spec.Values) {
77 for i := range spec.Names {
78 name := spec.Names[i].Name
79 if strings.HasPrefix(name, "_") && strings.HasSuffix(name, "_") {
80
81 if key := strings.Trim(name, "_"); key != "" {
82 value := pass.TypesInfo.Types[spec.Values[i]].Value.String()
83 result[key] = value
84 }
85 }
86 }
87 }
88 }
89
90 for _, f := range pass.Files {
91 for _, decl := range f.Decls {
92 if decl, ok := decl.(*ast.GenDecl); ok {
93 for _, spec := range decl.Specs {
94 switch decl.Tok {
95 case token.IMPORT:
96 doImport(spec.(*ast.ImportSpec))
97 case token.CONST:
98 doConst(spec.(*ast.ValueSpec))
99 }
100 }
101 }
102 }
103 }
104
105
106 keys := make([]string, 0, len(result))
107 for key := range result {
108 keys = append(keys, key)
109 }
110 sort.Strings(keys)
111 var fact pairsFact
112 for _, key := range keys {
113 fact = append(fact, fmt.Sprintf("%s=%s", key, result[key]))
114 }
115 if len(fact) > 0 {
116 pass.ExportPackageFact(&fact)
117 }
118
119 return result, nil
120 }
121
122 func imported(info *types.Info, spec *ast.ImportSpec) *types.Package {
123 obj, ok := info.Implicits[spec]
124 if !ok {
125 obj = info.Defs[spec.Name]
126 }
127 return obj.(*types.PkgName).Imported()
128 }
129
View as plain text