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 / internal / facts / facts_test.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 package facts_test
6
7 import (
8         "encoding/gob"
9         "fmt"
10         "go/token"
11         "go/types"
12         "os"
13         "reflect"
14         "testing"
15
16         "golang.org/x/tools/go/analysis/analysistest"
17         "golang.org/x/tools/go/analysis/internal/facts"
18         "golang.org/x/tools/go/packages"
19         "golang.org/x/tools/internal/testenv"
20 )
21
22 type myFact struct {
23         S string
24 }
25
26 func (f *myFact) String() string { return fmt.Sprintf("myFact(%s)", f.S) }
27 func (f *myFact) AFact()         {}
28
29 func TestEncodeDecode(t *testing.T) {
30         gob.Register(new(myFact))
31
32         // c -> b -> a, a2
33         // c does not directly depend on a, but it indirectly uses a.T.
34         //
35         // Package a2 is never loaded directly so it is incomplete.
36         //
37         // We use only types in this example because we rely on
38         // types.Eval to resolve the lookup expressions, and it only
39         // works for types. This is a definite gap in the typechecker API.
40         files := map[string]string{
41                 "a/a.go":  `package a; type A int; type T int`,
42                 "a2/a.go": `package a2; type A2 int; type Unneeded int`,
43                 "b/b.go":  `package b; import ("a"; "a2"); type B chan a2.A2; type F func() a.T`,
44                 "c/c.go":  `package c; import "b"; type C []b.B`,
45         }
46         dir, cleanup, err := analysistest.WriteFiles(files)
47         if err != nil {
48                 t.Fatal(err)
49         }
50         defer cleanup()
51
52         // factmap represents the passing of encoded facts from one
53         // package to another. In practice one would use the file system.
54         factmap := make(map[string][]byte)
55         read := func(path string) ([]byte, error) { return factmap[path], nil }
56
57         // In the following table, we analyze packages (a, b, c) in order,
58         // look up various objects accessible within each package,
59         // and see if they have a fact.  The "analysis" exports a fact
60         // for every object at package level.
61         //
62         // Note: Loop iterations are not independent test cases;
63         // order matters, as we populate factmap.
64         type lookups []struct {
65                 objexpr string
66                 want    string
67         }
68         for _, test := range []struct {
69                 path    string
70                 lookups lookups
71         }{
72                 {"a", lookups{
73                         {"A", "myFact(a.A)"},
74                 }},
75                 {"b", lookups{
76                         {"a.A", "myFact(a.A)"},
77                         {"a.T", "myFact(a.T)"},
78                         {"B", "myFact(b.B)"},
79                         {"F", "myFact(b.F)"},
80                         {"F(nil)()", "myFact(a.T)"}, // (result type of b.F)
81                 }},
82                 {"c", lookups{
83                         {"b.B", "myFact(b.B)"},
84                         {"b.F", "myFact(b.F)"},
85                         //{"b.F(nil)()", "myFact(a.T)"}, // no fact; TODO(adonovan): investigate
86                         {"C", "myFact(c.C)"},
87                         {"C{}[0]", "myFact(b.B)"},
88                         {"<-(C{}[0])", "no fact"}, // object but no fact (we never "analyze" a2)
89                 }},
90         } {
91                 // load package
92                 pkg, err := load(t, dir, test.path)
93                 if err != nil {
94                         t.Fatal(err)
95                 }
96
97                 // decode
98                 facts, err := facts.Decode(pkg, read)
99                 if err != nil {
100                         t.Fatalf("Decode failed: %v", err)
101                 }
102                 if true {
103                         t.Logf("decode %s facts = %v", pkg.Path(), facts) // show all facts
104                 }
105
106                 // export
107                 // (one fact for each package-level object)
108                 scope := pkg.Scope()
109                 for _, name := range scope.Names() {
110                         obj := scope.Lookup(name)
111                         fact := &myFact{obj.Pkg().Name() + "." + obj.Name()}
112                         facts.ExportObjectFact(obj, fact)
113                 }
114
115                 // import
116                 // (after export, because an analyzer may import its own facts)
117                 for _, lookup := range test.lookups {
118                         fact := new(myFact)
119                         var got string
120                         if obj := find(pkg, lookup.objexpr); obj == nil {
121                                 got = "no object"
122                         } else if facts.ImportObjectFact(obj, fact) {
123                                 got = fact.String()
124                         } else {
125                                 got = "no fact"
126                         }
127                         if got != lookup.want {
128                                 t.Errorf("in %s, ImportObjectFact(%s, %T) = %s, want %s",
129                                         pkg.Path(), lookup.objexpr, fact, got, lookup.want)
130                         }
131                 }
132
133                 // encode
134                 factmap[pkg.Path()] = facts.Encode()
135         }
136 }
137
138 func find(p *types.Package, expr string) types.Object {
139         // types.Eval only allows us to compute a TypeName object for an expression.
140         // TODO(adonovan): support other expressions that denote an object:
141         // - an identifier (or qualified ident) for a func, const, or var
142         // - new(T).f for a field or method
143         // I've added CheckExpr in https://go-review.googlesource.com/c/go/+/144677.
144         // If that becomes available, use it.
145
146         // Choose an arbitrary position within the (single-file) package
147         // so that we are within the scope of its import declarations.
148         somepos := p.Scope().Lookup(p.Scope().Names()[0]).Pos()
149         tv, err := types.Eval(token.NewFileSet(), p, somepos, expr)
150         if err != nil {
151                 return nil
152         }
153         if n, ok := tv.Type.(*types.Named); ok {
154                 return n.Obj()
155         }
156         return nil
157 }
158
159 func load(t *testing.T, dir string, path string) (*types.Package, error) {
160         cfg := &packages.Config{
161                 Mode: packages.LoadSyntax,
162                 Dir:  dir,
163                 Env:  append(os.Environ(), "GOPATH="+dir, "GO111MODULE=off", "GOPROXY=off"),
164         }
165         testenv.NeedsGoPackagesEnv(t, cfg.Env)
166         pkgs, err := packages.Load(cfg, path)
167         if err != nil {
168                 return nil, err
169         }
170         if packages.PrintErrors(pkgs) > 0 {
171                 return nil, fmt.Errorf("packages had errors")
172         }
173         if len(pkgs) == 0 {
174                 return nil, fmt.Errorf("no package matched %s", path)
175         }
176         return pkgs[0].Types, nil
177 }
178
179 type otherFact struct {
180         S string
181 }
182
183 func (f *otherFact) String() string { return fmt.Sprintf("otherFact(%s)", f.S) }
184 func (f *otherFact) AFact()         {}
185
186 func TestFactFilter(t *testing.T) {
187         files := map[string]string{
188                 "a/a.go": `package a; type A int`,
189         }
190         dir, cleanup, err := analysistest.WriteFiles(files)
191         if err != nil {
192                 t.Fatal(err)
193         }
194         defer cleanup()
195
196         pkg, err := load(t, dir, "a")
197         if err != nil {
198                 t.Fatal(err)
199         }
200
201         obj := pkg.Scope().Lookup("A")
202         s, err := facts.Decode(pkg, func(string) ([]byte, error) { return nil, nil })
203         if err != nil {
204                 t.Fatal(err)
205         }
206         s.ExportObjectFact(obj, &myFact{"good object fact"})
207         s.ExportPackageFact(&myFact{"good package fact"})
208         s.ExportObjectFact(obj, &otherFact{"bad object fact"})
209         s.ExportPackageFact(&otherFact{"bad package fact"})
210
211         filter := map[reflect.Type]bool{
212                 reflect.TypeOf(&myFact{}): true,
213         }
214
215         pkgFacts := s.AllPackageFacts(filter)
216         wantPkgFacts := `[{package a ("a") myFact(good package fact)}]`
217         if got := fmt.Sprintf("%v", pkgFacts); got != wantPkgFacts {
218                 t.Errorf("AllPackageFacts: got %v, want %v", got, wantPkgFacts)
219         }
220
221         objFacts := s.AllObjectFacts(filter)
222         wantObjFacts := "[{type a.A int myFact(good object fact)}]"
223         if got := fmt.Sprintf("%v", objFacts); got != wantObjFacts {
224                 t.Errorf("AllObjectFacts: got %v, want %v", got, wantObjFacts)
225         }
226 }