GoPLS Viewer

Home|gopls/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.
24package pkgfact
25
26import (
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
38var 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    ResultTypereflect.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.
50type pairsFact []string
51
52func (f *pairsFactAFact()         {}
53func (f *pairsFactString() string { return "pairs(" + strings.Join(*f", ") + ")" }
54
55func 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.TypesInfospec)
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 declok := 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([]string0len(result))
106    for key := range result {
107        keys = append(keyskey)
108    }
109    sort.Strings(keys)
110    var fact pairsFact
111    for _key := range keys {
112        fact = append(factfmt.Sprintf("%s=%s"keyresult[key]))
113    }
114    if len(fact) > 0 {
115        pass.ExportPackageFact(&fact)
116    }
117
118    return resultnil
119}
120
121func imported(info *types.Infospec *ast.ImportSpec) *types.Package {
122    objok := info.Implicits[spec]
123    if !ok {
124        obj = info.Defs[spec.Name// renaming import
125    }
126    return obj.(*types.PkgName).Imported()
127}
128
MembersX
ast
pairsFact.AFact
run.BlockStmt.BlockStmt.RangeStmt_1969.BlockStmt.eq
run.RangeStmt_3107.key
imported.spec
fmt
run
run.BlockStmt.BlockStmt.RangeStmt_2286.BlockStmt.BlockStmt.key
imported.info
strings
run.result
run.BlockStmt.pkg
run.BlockStmt.fact
run.RangeStmt_2588.f
run.BlockStmt.BlockStmt.RangeStmt_2286.BlockStmt.BlockStmt.BlockStmt.value
run.RangeStmt_2588.BlockStmt.RangeStmt_2621.decl
run.RangeStmt_2588.BlockStmt.RangeStmt_2621.BlockStmt.BlockStmt.RangeStmt_2700.spec
sort
pairsFact
pairsFact.String
run.BlockStmt.BlockStmt.RangeStmt_2286.i
run.BlockStmt.BlockStmt.RangeStmt_2286.BlockStmt.name
run.keys
run.RangeStmt_3010.key
token
pairsFact.AFact.f
imported
types
reflect
pairsFact.String.f
run.BlockStmt.BlockStmt.RangeStmt_1969.pair
run.fact
analysis
run.pass
Members
X