1 | // Copyright 2021 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 | //go:build go1.18 |
6 | // +build go1.18 |
7 | |
8 | package gcimporter_test |
9 | |
10 | import ( |
11 | "bytes" |
12 | "fmt" |
13 | "go/ast" |
14 | "go/importer" |
15 | "go/parser" |
16 | "go/token" |
17 | "go/types" |
18 | "os" |
19 | "path/filepath" |
20 | "runtime" |
21 | "strings" |
22 | "testing" |
23 | |
24 | "golang.org/x/tools/internal/gcimporter" |
25 | "golang.org/x/tools/internal/testenv" |
26 | ) |
27 | |
28 | // TODO(rfindley): migrate this to testdata, as has been done in the standard library. |
29 | func TestGenericExport(t *testing.T) { |
30 | const src = ` |
31 | package generic |
32 | |
33 | type Any any |
34 | |
35 | type T[A, B any] struct { Left A; Right B } |
36 | |
37 | func (T[P, Q]) m() {} |
38 | |
39 | var X T[int, string] = T[int, string]{1, "hi"} |
40 | |
41 | func ToInt[P interface{ ~int }](p P) int { return int(p) } |
42 | |
43 | var IntID = ToInt[int] |
44 | |
45 | type G[C comparable] int |
46 | |
47 | func ImplicitFunc[T ~int]() {} |
48 | |
49 | type ImplicitType[T ~int] int |
50 | |
51 | // Exercise constant import/export |
52 | const C1 = 42 |
53 | const C2 int = 42 |
54 | const C3 float64 = 42 |
55 | |
56 | type Constraint[T any] interface { |
57 | m(T) |
58 | } |
59 | |
60 | // TODO(rfindley): revert to multiple blanks once the restriction on multiple |
61 | // blanks is removed from the type checker. |
62 | // type Blanks[_ any, _ Constraint[int]] int |
63 | // func (Blanks[_, _]) m() {} |
64 | type Blanks[_ any] int |
65 | func (Blanks[_]) m() {} |
66 | ` |
67 | testExportSrc(t, []byte(src)) |
68 | } |
69 | |
70 | func testExportSrc(t *testing.T, src []byte) { |
71 | // This package only handles gc export data. |
72 | if runtime.Compiler != "gc" { |
73 | t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) |
74 | } |
75 | |
76 | fset := token.NewFileSet() |
77 | f, err := parser.ParseFile(fset, "g.go", src, 0) |
78 | if err != nil { |
79 | t.Fatal(err) |
80 | } |
81 | conf := types.Config{ |
82 | Importer: importer.Default(), |
83 | } |
84 | pkg, err := conf.Check("", fset, []*ast.File{f}, nil) |
85 | if err != nil { |
86 | t.Fatal(err) |
87 | } |
88 | |
89 | // export |
90 | version := gcimporter.IExportVersion |
91 | data, err := iexport(fset, version, pkg) |
92 | if err != nil { |
93 | t.Fatal(err) |
94 | } |
95 | |
96 | testPkgData(t, fset, version, pkg, data) |
97 | } |
98 | |
99 | func TestImportTypeparamTests(t *testing.T) { |
100 | testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache |
101 | |
102 | // Check go files in test/typeparam. |
103 | rootDir := filepath.Join(runtime.GOROOT(), "test", "typeparam") |
104 | list, err := os.ReadDir(rootDir) |
105 | if err != nil { |
106 | t.Fatal(err) |
107 | } |
108 | |
109 | if isUnifiedBuilder() { |
110 | t.Skip("unified export data format is currently unsupported") |
111 | } |
112 | |
113 | for _, entry := range list { |
114 | if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { |
115 | // For now, only consider standalone go files. |
116 | continue |
117 | } |
118 | |
119 | t.Run(entry.Name(), func(t *testing.T) { |
120 | filename := filepath.Join(rootDir, entry.Name()) |
121 | src, err := os.ReadFile(filename) |
122 | if err != nil { |
123 | t.Fatal(err) |
124 | } |
125 | |
126 | if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) { |
127 | // We're bypassing the logic of run.go here, so be conservative about |
128 | // the files we consider in an attempt to make this test more robust to |
129 | // changes in test/typeparams. |
130 | t.Skipf("not detected as a run test") |
131 | } |
132 | |
133 | testExportSrc(t, src) |
134 | }) |
135 | } |
136 | } |
137 | |
138 | func TestRecursiveExport_Issue51219(t *testing.T) { |
139 | const srca = ` |
140 | package a |
141 | |
142 | type Interaction[DataT InteractionDataConstraint] struct { |
143 | } |
144 | |
145 | type InteractionDataConstraint interface { |
146 | []byte | |
147 | UserCommandInteractionData |
148 | } |
149 | |
150 | type UserCommandInteractionData struct { |
151 | resolvedInteractionWithOptions |
152 | } |
153 | |
154 | type resolvedInteractionWithOptions struct { |
155 | Resolved Resolved |
156 | } |
157 | |
158 | type Resolved struct { |
159 | Users ResolvedData[User] |
160 | } |
161 | |
162 | type ResolvedData[T ResolvedDataConstraint] map[uint64]T |
163 | |
164 | type ResolvedDataConstraint interface { |
165 | User | Message |
166 | } |
167 | |
168 | type User struct{} |
169 | |
170 | type Message struct { |
171 | Interaction *Interaction[[]byte] |
172 | } |
173 | ` |
174 | |
175 | const srcb = ` |
176 | package b |
177 | |
178 | import ( |
179 | "a" |
180 | ) |
181 | |
182 | // InteractionRequest is an incoming request Interaction |
183 | type InteractionRequest[T a.InteractionDataConstraint] struct { |
184 | a.Interaction[T] |
185 | } |
186 | ` |
187 | |
188 | const srcp = ` |
189 | package p |
190 | |
191 | import ( |
192 | "b" |
193 | ) |
194 | |
195 | // ResponseWriterMock mocks corde's ResponseWriter interface |
196 | type ResponseWriterMock struct { |
197 | x b.InteractionRequest[[]byte] |
198 | } |
199 | ` |
200 | |
201 | importer := &testImporter{ |
202 | src: map[string][]byte{ |
203 | "a": []byte(srca), |
204 | "b": []byte(srcb), |
205 | "p": []byte(srcp), |
206 | }, |
207 | pkgs: make(map[string]*types.Package), |
208 | } |
209 | _, err := importer.Import("p") |
210 | if err != nil { |
211 | t.Fatal(err) |
212 | } |
213 | } |
214 | |
215 | // testImporter is a helper to test chains of imports using export data. |
216 | type testImporter struct { |
217 | src map[string][]byte // original source |
218 | pkgs map[string]*types.Package // memoized imported packages |
219 | } |
220 | |
221 | func (t *testImporter) Import(path string) (*types.Package, error) { |
222 | if pkg, ok := t.pkgs[path]; ok { |
223 | return pkg, nil |
224 | } |
225 | src, ok := t.src[path] |
226 | if !ok { |
227 | return nil, fmt.Errorf("unknown path %v", path) |
228 | } |
229 | |
230 | // Type-check, but don't return this package directly. |
231 | fset := token.NewFileSet() |
232 | f, err := parser.ParseFile(fset, path+".go", src, 0) |
233 | if err != nil { |
234 | return nil, err |
235 | } |
236 | conf := types.Config{ |
237 | Importer: t, |
238 | } |
239 | pkg, err := conf.Check(path, fset, []*ast.File{f}, nil) |
240 | if err != nil { |
241 | return nil, err |
242 | } |
243 | |
244 | // Export and import to get the package imported from export data. |
245 | exportdata, err := iexport(fset, gcimporter.IExportVersion, pkg) |
246 | if err != nil { |
247 | return nil, err |
248 | } |
249 | imports := make(map[string]*types.Package) |
250 | fset2 := token.NewFileSet() |
251 | _, pkg2, err := gcimporter.IImportData(fset2, imports, exportdata, pkg.Path()) |
252 | if err != nil { |
253 | return nil, err |
254 | } |
255 | t.pkgs[path] = pkg2 |
256 | return pkg2, nil |
257 | } |
258 |
Members