Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / go / analysis / passes / pkgfact / pkgfact.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // The pkgfact package is a demonstration and test of the package fact
6 // mechanism.
7 //
8 // The output of the pkgfact analysis is a set of key/values pairs
9 // gathered from the analyzed package and its imported dependencies.
10 // Each key/value pair comes from a top-level constant declaration
11 // whose name starts and ends with "_".  For example:
12 //
13 //      package p
14 //
15 //      const _greeting_  = "hello"
16 //      const _audience_  = "world"
17 //
18 // the pkgfact analysis output for package p would be:
19 //
20 //   {"greeting": "hello", "audience": "world"}.
21 //
22 // In addition, the analysis reports a diagnostic at each import
23 // showing which key/value pairs it contributes.
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 // A pairsFact is a package-level fact that records
47 // an set of key=value strings accumulated from constant
48 // declarations in this package and its dependencies.
49 // Elements are ordered by keys, which are unique.
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         // At each import, print the fact from the imported
59         // package and accumulate its information into the result.
60         // (Warning: accumulation leads to quadratic growth of work.)
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         // At each "const _name_ = value", add a fact into env.
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         // Sort/deduplicate the result and save it as a package fact.
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] // renaming import
125         }
126         return obj.(*types.PkgName).Imported()
127 }