1
2
3
4
5 package gcimporter_test
6
7 import (
8 "fmt"
9 "go/ast"
10 "go/parser"
11 "go/token"
12 "go/types"
13 "os"
14 "strings"
15 "testing"
16
17 "golang.org/x/sync/errgroup"
18 "golang.org/x/tools/go/packages"
19 "golang.org/x/tools/internal/gcimporter"
20 "golang.org/x/tools/internal/testenv"
21 )
22
23
24 func TestShallowStd(t *testing.T) {
25 if testing.Short() {
26 t.Skip("skipping in short mode; too slow (https://golang.org/issue/14113)")
27 }
28 testenv.NeedsTool(t, "go")
29
30
31
32 cfg := &packages.Config{
33 Mode: packages.NeedImports |
34 packages.NeedName |
35 packages.NeedFiles |
36 packages.NeedCompiledGoFiles,
37 Tests: false,
38 }
39 pkgs, err := packages.Load(cfg, "std")
40 if err != nil {
41 t.Fatalf("load: %v", err)
42 }
43 if len(pkgs) < 200 {
44 t.Fatalf("too few packages: %d", len(pkgs))
45 }
46
47
48 done := make(map[*packages.Package]chan struct{})
49 packages.Visit(pkgs, nil, func(p *packages.Package) {
50 done[p] = make(chan struct{})
51 })
52 packages.Visit(pkgs, nil,
53 func(pkg *packages.Package) {
54 go func() {
55
56 for _, imp := range pkg.Imports {
57 <-done[imp]
58 }
59 typecheck(t, pkg)
60 close(done[pkg])
61 }()
62 })
63 for _, root := range pkgs {
64 <-done[root]
65 }
66 }
67
68
69
70 func typecheck(t *testing.T, ppkg *packages.Package) {
71 if ppkg.PkgPath == "unsafe" {
72 return
73 }
74
75
76 fset := token.NewFileSet()
77
78
79 syntax := make([]*ast.File, len(ppkg.CompiledGoFiles))
80 var group errgroup.Group
81 for i, filename := range ppkg.CompiledGoFiles {
82 i, filename := i, filename
83 group.Go(func() error {
84 f, err := parser.ParseFile(fset, filename, nil, parser.SkipObjectResolution)
85 if err != nil {
86 return err
87 }
88 syntax[i] = f
89 return nil
90 })
91 }
92 if err := group.Wait(); err != nil {
93 t.Fatal(err)
94 }
95
96
97
98
99
100 depsByPkgPath := make(map[string]*packages.Package)
101 {
102 var visit func(*packages.Package)
103 visit = func(pkg *packages.Package) {
104 if depsByPkgPath[pkg.PkgPath] == nil {
105 depsByPkgPath[pkg.PkgPath] = pkg
106 for path := range pkg.Imports {
107 visit(pkg.Imports[path])
108 }
109 }
110 }
111 visit(ppkg)
112 }
113
114
115 var (
116 loadFromExportData func(*packages.Package) (*types.Package, error)
117 importMap = map[string]*types.Package{
118 ppkg.PkgPath: types.NewPackage(ppkg.PkgPath, ppkg.Name),
119 }
120 )
121 loadFromExportData = func(imp *packages.Package) (*types.Package, error) {
122 export := []byte(imp.ExportFile)
123 getPackages := func(items []gcimporter.GetPackagesItem) error {
124 for i, item := range items {
125 pkg, ok := importMap[item.Path]
126 if !ok {
127 dep, ok := depsByPkgPath[item.Path]
128 if !ok {
129 return fmt.Errorf("can't find dependency: %q", item.Path)
130 }
131 pkg = types.NewPackage(item.Path, dep.Name)
132 importMap[item.Path] = pkg
133 loadFromExportData(dep)
134 }
135 items[i].Pkg = pkg
136 }
137 return nil
138 }
139 return gcimporter.IImportShallow(fset, getPackages, export, imp.PkgPath, nil)
140 }
141
142
143 cfg := &types.Config{
144 Error: func(e error) {
145 t.Error(e)
146 },
147 Importer: importerFunc(func(importPath string) (*types.Package, error) {
148 if importPath == "unsafe" {
149 return types.Unsafe, nil
150 }
151 imp, ok := ppkg.Imports[importPath]
152 if !ok {
153 return nil, fmt.Errorf("missing import %q", importPath)
154 }
155 return loadFromExportData(imp)
156 }),
157 }
158
159
160 tpkg := types.NewPackage(ppkg.PkgPath, ppkg.Name)
161 _ = types.NewChecker(cfg, fset, tpkg, nil).Files(syntax)
162
163 postTypeCheck(t, fset, tpkg)
164
165
166 data, err := gcimporter.IExportShallow(fset, tpkg, nil)
167 if err != nil {
168 t.Fatalf("internal error marshalling export data: %v", err)
169 }
170 ppkg.ExportFile = string(data)
171 }
172
173
174
175
176
177
178 func postTypeCheck(t *testing.T, fset *token.FileSet, pkg *types.Package) {
179
180 var obj types.Object
181 switch pkg.Path() {
182 case "fmt":
183
184 obj = pkg.Scope().Lookup("Println")
185 case "net/http":
186
187 req := pkg.Scope().Lookup("Request")
188 obj, _, _ = types.LookupFieldOrMethod(req.Type(), true, pkg, "ParseForm")
189 default:
190 return
191 }
192 if obj == nil {
193 t.Errorf("object not found in package %s", pkg.Path())
194 return
195 }
196
197
198 posn := fset.Position(obj.Pos())
199 data, err := os.ReadFile(posn.Filename)
200 if err != nil {
201 t.Errorf("can't read source file declaring %v: %v", obj, err)
202 return
203 }
204
205
206 line := strings.Split(string(data), "\n")[posn.Line-1]
207
208 if id := line[posn.Column-1 : posn.Column-1+len(obj.Name())]; id != obj.Name() {
209 t.Errorf("%+v: expected declaration of %v at this line, column; got %q", posn, obj, line)
210 }
211
212
213 if id := string(data[posn.Offset : posn.Offset+len(obj.Name())]); id != obj.Name() {
214 t.Errorf("%+v: expected declaration of %v at this offset; got %q", posn, obj, id)
215 }
216
217
218
219
220
221 endPosn := fset.Position(obj.Pos() + token.Pos(len(obj.Name())))
222 wantEndPosn := token.Position{
223 Filename: posn.Filename,
224 Offset: posn.Offset + len(obj.Name()),
225 Line: posn.Line,
226 Column: posn.Column + len(obj.Name()),
227 }
228 if endPosn != wantEndPosn {
229 t.Errorf("%+v: expected end Position of %v here; was at %+v", wantEndPosn, obj, endPosn)
230 }
231 }
232
View as plain text