Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / go / types / objectpath / objectpath_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 objectpath_test
6
7 import (
8         "bytes"
9         "go/ast"
10         "go/importer"
11         "go/parser"
12         "go/token"
13         "go/types"
14         "strings"
15         "testing"
16
17         "golang.org/x/tools/go/buildutil"
18         "golang.org/x/tools/go/gcexportdata"
19         "golang.org/x/tools/go/loader"
20         "golang.org/x/tools/go/types/objectpath"
21 )
22
23 func TestPaths(t *testing.T) {
24         pkgs := map[string]map[string]string{
25                 "b": {"b.go": `
26 package b
27
28 import "a"
29
30 const C = a.Int(0)
31
32 func F(a, b, c int, d a.T)
33
34 type T struct{ A int; b int; a.T }
35
36 func (T) M() *interface{ f() }
37
38 type U T
39
40 type A = struct{ x int }
41
42 var V []*a.T
43
44 type M map[struct{x int}]struct{y int}
45
46 func unexportedFunc()
47 type unexportedType struct{}
48 `},
49                 "a": {"a.go": `
50 package a
51
52 type Int int
53
54 type T struct{x, y int}
55
56 `},
57         }
58         conf := loader.Config{Build: buildutil.FakeContext(pkgs)}
59         conf.Import("a")
60         conf.Import("b")
61         prog, err := conf.Load()
62         if err != nil {
63                 t.Fatal(err)
64         }
65         a := prog.Imported["a"].Pkg
66         b := prog.Imported["b"].Pkg
67
68         // We test objectpath by enumerating a set of paths
69         // and ensuring that Path(pkg, Object(pkg, path)) == path.
70         //
71         // It might seem more natural to invert the test:
72         // identify a set of objects and for each one,
73         // ensure that Object(pkg, Path(pkg, obj)) == obj.
74         // However, for most interesting test cases there is no
75         // easy way to identify the object short of applying
76         // a series of destructuring operations to pkg---which
77         // is essentially what objectpath.Object does.
78         // (We do a little of that when testing bad paths, below.)
79         //
80         // The downside is that the test depends on the path encoding.
81         // The upside is that the test exercises the encoding.
82
83         // good paths
84         for _, test := range []struct {
85                 pkg     *types.Package
86                 path    objectpath.Path
87                 wantobj string
88         }{
89                 {b, "C", "const b.C a.Int"},
90                 {b, "F", "func b.F(a int, b int, c int, d a.T)"},
91                 {b, "F.PA0", "var a int"},
92                 {b, "F.PA1", "var b int"},
93                 {b, "F.PA2", "var c int"},
94                 {b, "F.PA3", "var d a.T"},
95                 {b, "T", "type b.T struct{A int; b int; a.T}"},
96                 {b, "T.O", "type b.T struct{A int; b int; a.T}"},
97                 {b, "T.UF0", "field A int"},
98                 {b, "T.UF1", "field b int"},
99                 {b, "T.UF2", "field T a.T"},
100                 {b, "U.UF2", "field T a.T"}, // U.U... are aliases for T.U...
101                 {b, "A", "type b.A = struct{x int}"},
102                 {b, "A.F0", "field x int"},
103                 {b, "V", "var b.V []*a.T"},
104                 {b, "M", "type b.M map[struct{x int}]struct{y int}"},
105                 {b, "M.UKF0", "field x int"},
106                 {b, "M.UEF0", "field y int"},
107                 {b, "T.M0", "func (b.T).M() *interface{f()}"}, // concrete method
108                 {b, "T.M0.RA0", "var  *interface{f()}"},       // parameter
109                 {b, "T.M0.RA0.EM0", "func (interface).f()"},   // interface method
110                 {b, "unexportedType", "type b.unexportedType struct{}"},
111                 {a, "T", "type a.T struct{x int; y int}"},
112                 {a, "T.UF0", "field x int"},
113         } {
114                 // check path -> object
115                 obj, err := objectpath.Object(test.pkg, test.path)
116                 if err != nil {
117                         t.Errorf("Object(%s, %q) failed: %v",
118                                 test.pkg.Path(), test.path, err)
119                         continue
120                 }
121                 if obj.String() != test.wantobj {
122                         t.Errorf("Object(%s, %q) = %v, want %s",
123                                 test.pkg.Path(), test.path, obj, test.wantobj)
124                         continue
125                 }
126                 if obj.Pkg() != test.pkg {
127                         t.Errorf("Object(%s, %q) = %v, which belongs to package %s",
128                                 test.pkg.Path(), test.path, obj, obj.Pkg().Path())
129                         continue
130                 }
131
132                 // check object -> path
133                 path2, err := objectpath.For(obj)
134                 if err != nil {
135                         t.Errorf("For(%v) failed: %v, want %q", obj, err, test.path)
136                         continue
137                 }
138                 // We do not require that test.path == path2. Aliases are legal.
139                 // But we do require that Object(path2) finds the same object.
140                 obj2, err := objectpath.Object(test.pkg, path2)
141                 if err != nil {
142                         t.Errorf("Object(%s, %q) failed: %v (roundtrip from %q)",
143                                 test.pkg.Path(), path2, err, test.path)
144                         continue
145                 }
146                 if obj2 != obj {
147                         t.Errorf("Object(%s, For(obj)) != obj: got %s, obj is %s (path1=%q, path2=%q)",
148                                 test.pkg.Path(), obj2, obj, test.path, path2)
149                         continue
150                 }
151         }
152
153         // bad paths (all relative to package b)
154         for _, test := range []struct {
155                 pkg     *types.Package
156                 path    objectpath.Path
157                 wantErr string
158         }{
159                 {b, "", "empty path"},
160                 {b, "missing", `package b does not contain "missing"`},
161                 {b, "F.U", "invalid path: ends with 'U', want [AFMO]"},
162                 {b, "F.PA3.O", "path denotes type a.T struct{x int; y int}, which belongs to a different package"},
163                 {b, "F.PA!", `invalid path: bad numeric operand "" for code 'A'`},
164                 {b, "F.PA3.UF0", "path denotes field x int, which belongs to a different package"},
165                 {b, "F.PA3.UF5", "field index 5 out of range [0-2)"},
166                 {b, "V.EE", "invalid path: ends with 'E', want [AFMO]"},
167                 {b, "F..O", "invalid path: unexpected '.' in type context"},
168                 {b, "T.OO", "invalid path: code 'O' in object context"},
169                 {b, "T.EO", "cannot apply 'E' to b.T (got *types.Named, want pointer, slice, array, chan or map)"},
170                 {b, "A.O", "cannot apply 'O' to struct{x int} (got struct{x int}, want named)"},
171                 {b, "A.UF0", "cannot apply 'U' to struct{x int} (got struct{x int}, want named)"},
172                 {b, "M.UPO", "cannot apply 'P' to map[struct{x int}]struct{y int} (got *types.Map, want signature)"},
173                 {b, "C.O", "path denotes type a.Int int, which belongs to a different package"},
174         } {
175                 obj, err := objectpath.Object(test.pkg, test.path)
176                 if err == nil {
177                         t.Errorf("Object(%s, %q) = %s, want error",
178                                 test.pkg.Path(), test.path, obj)
179                         continue
180                 }
181                 if err.Error() != test.wantErr {
182                         t.Errorf("Object(%s, %q) error was %q, want %q",
183                                 test.pkg.Path(), test.path, err, test.wantErr)
184                         continue
185                 }
186         }
187
188         // bad objects
189         bInfo := prog.Imported["b"]
190         for _, test := range []struct {
191                 obj     types.Object
192                 wantErr string
193         }{
194                 {types.Universe.Lookup("nil"), "predeclared nil has no path"},
195                 {types.Universe.Lookup("len"), "predeclared builtin len has no path"},
196                 {types.Universe.Lookup("int"), "predeclared type int has no path"},
197                 {bInfo.Info.Implicits[bInfo.Files[0].Imports[0]], "no path for package a"}, // import "a"
198                 {b.Scope().Lookup("unexportedFunc"), "no path for non-exported func b.unexportedFunc()"},
199         } {
200                 path, err := objectpath.For(test.obj)
201                 if err == nil {
202                         t.Errorf("Object(%s) = %q, want error", test.obj, path)
203                         continue
204                 }
205                 if err.Error() != test.wantErr {
206                         t.Errorf("Object(%s) error was %q, want %q", test.obj, err, test.wantErr)
207                         continue
208                 }
209         }
210 }
211
212 // TestSourceAndExportData uses objectpath to compute a correspondence
213 // of objects between two versions of the same package, one loaded from
214 // source, the other from export data.
215 func TestSourceAndExportData(t *testing.T) {
216         const src = `
217 package p
218
219 type I int
220
221 func (I) F() *struct{ X, Y int } {
222         return nil
223 }
224
225 type Foo interface {
226         Method() (string, func(int) struct{ X int })
227 }
228
229 var X chan struct{ Z int }
230 var Z map[string]struct{ A int }
231 `
232
233         // Parse source file and type-check it as a package, "src".
234         fset := token.NewFileSet()
235         f, err := parser.ParseFile(fset, "src.go", src, 0)
236         if err != nil {
237                 t.Fatal(err)
238         }
239         conf := types.Config{Importer: importer.For("source", nil)}
240         info := &types.Info{
241                 Defs: make(map[*ast.Ident]types.Object),
242         }
243         srcpkg, err := conf.Check("src/p", fset, []*ast.File{f}, info)
244         if err != nil {
245                 t.Fatal(err)
246         }
247
248         // Export binary export data then reload it as a new package, "bin".
249         var buf bytes.Buffer
250         if err := gcexportdata.Write(&buf, fset, srcpkg); err != nil {
251                 t.Fatal(err)
252         }
253
254         imports := make(map[string]*types.Package)
255         binpkg, err := gcexportdata.Read(&buf, fset, imports, "bin/p")
256         if err != nil {
257                 t.Fatal(err)
258         }
259
260         // Now find the correspondences between them.
261         for _, srcobj := range info.Defs {
262                 if srcobj == nil {
263                         continue // e.g. package declaration
264                 }
265                 if _, ok := srcobj.(*types.PkgName); ok {
266                         continue // PkgName has no objectpath
267                 }
268
269                 path, err := objectpath.For(srcobj)
270                 if err != nil {
271                         t.Errorf("For(%v): %v", srcobj, err)
272                         continue
273                 }
274                 binobj, err := objectpath.Object(binpkg, path)
275                 if err != nil {
276                         t.Errorf("Object(%s, %q): %v", binpkg.Path(), path, err)
277                         continue
278                 }
279
280                 // Check the object strings match.
281                 // (We can't check that types are identical because the
282                 // objects belong to different type-checker realms.)
283                 srcstr := objectString(srcobj)
284                 binstr := objectString(binobj)
285                 if srcstr != binstr {
286                         t.Errorf("ObjectStrings do not match: Object(For(%q)) = %s, want %s",
287                                 path, srcstr, binstr)
288                         continue
289                 }
290         }
291 }
292
293 func objectString(obj types.Object) string {
294         s := types.ObjectString(obj, (*types.Package).Name)
295
296         // The printing of interface methods changed in go1.11.
297         // This work-around makes the specific test pass with earlier versions.
298         s = strings.Replace(s, "func (interface).Method", "func (p.Foo).Method", -1)
299
300         return s
301 }