1// Copyright 2019 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
5package cache
6
7import (
8	"context"
9	"fmt"
10	"go/ast"
11	"go/scanner"
12	"go/token"
13	"go/types"
14	"sync"
15
16	"golang.org/x/tools/go/analysis"
17	"golang.org/x/tools/go/packages"
18	"golang.org/x/tools/internal/lsp/source"
19	"golang.org/x/tools/internal/lsp/telemetry/trace"
20	"golang.org/x/tools/internal/span"
21)
22
23type importer struct {
24	view *view
25
26	// seen maintains the set of previously imported packages.
27	// If we have seen a package that is already in this map, we have a circular import.
28	seen map[packageID]struct{}
29
30	// topLevelPkgID is the ID of the package from which type-checking began.
31	topLevelPkgID packageID
32
33	ctx  context.Context
34	fset *token.FileSet
35}
36
37func (imp *importer) Import(pkgPath string) (*types.Package, error) {
38	ctx := imp.ctx
39	id, ok := imp.view.mcache.ids[packagePath(pkgPath)]
40	if !ok {
41		return nil, fmt.Errorf("no known ID for %s", pkgPath)
42	}
43	pkg, err := imp.getPkg(ctx, id)
44	if err != nil {
45		return nil, err
46	}
47	return pkg.types, nil
48}
49
50func (imp *importer) getPkg(ctx context.Context, id packageID) (*pkg, error) {
51	if _, ok := imp.seen[id]; ok {
52		return nil, fmt.Errorf("circular import detected")
53	}
54	imp.view.pcache.mu.Lock()
55	e, ok := imp.view.pcache.packages[id]
56
57	if ok {
58		// cache hit
59		imp.view.pcache.mu.Unlock()
60		// wait for entry to become ready
61		<-e.ready
62	} else {
63		// cache miss
64		e = &entry{ready: make(chan struct{})}
65		imp.view.pcache.packages[id] = e
66		imp.view.pcache.mu.Unlock()
67
68		// This goroutine becomes responsible for populating
69		// the entry and broadcasting its readiness.
70		e.pkg, e.err = imp.typeCheck(ctx, id)
71		close(e.ready)
72	}
73
74	if e.err != nil {
75		// If the import had been previously canceled, and that error cached, try again.
76		if e.err == context.Canceled && ctx.Err() == nil {
77			imp.view.pcache.mu.Lock()
78			// Clear out canceled cache entry if it is still there.
79			if imp.view.pcache.packages[id] == e {
80				delete(imp.view.pcache.packages, id)
81			}
82			imp.view.pcache.mu.Unlock()
83			return imp.getPkg(ctx, id)
84		}
85		return nil, e.err
86	}
87
88	return e.pkg, nil
89}
90
91func (imp *importer) typeCheck(ctx context.Context, id packageID) (*pkg, error) {
92	ctx, ts := trace.StartSpan(ctx, "cache.importer.typeCheck")
93	defer ts.End()
94	meta, ok := imp.view.mcache.packages[id]
95	if !ok {
96		return nil, fmt.Errorf("no metadata for %v", id)
97	}
98	pkg := &pkg{
99		id:         meta.id,
100		pkgPath:    meta.pkgPath,
101		imports:    make(map[packagePath]*pkg),
102		typesSizes: meta.typesSizes,
103		typesInfo: &types.Info{
104			Types:      make(map[ast.Expr]types.TypeAndValue),
105			Defs:       make(map[*ast.Ident]types.Object),
106			Uses:       make(map[*ast.Ident]types.Object),
107			Implicits:  make(map[ast.Node]types.Object),
108			Selections: make(map[*ast.SelectorExpr]*types.Selection),
109			Scopes:     make(map[ast.Node]*types.Scope),
110		},
111		analyses: make(map[*analysis.Analyzer]*analysisEntry),
112	}
113
114	// Ignore function bodies for any dependency packages.
115	mode := source.ParseFull
116	if imp.topLevelPkgID != pkg.id {
117		mode = source.ParseExported
118	}
119	var (
120		files []*astFile
121		phs   []source.ParseGoHandle
122		wg    sync.WaitGroup
123	)
124	for _, filename := range meta.files {
125		uri := span.FileURI(filename)
126		f, err := imp.view.getFile(ctx, uri)
127		if err != nil {
128			continue
129		}
130		ph := imp.view.session.cache.ParseGoHandle(f.Handle(ctx), mode)
131		phs = append(phs, ph)
132		files = append(files, &astFile{
133			uri:       ph.File().Identity().URI,
134			isTrimmed: mode == source.ParseExported,
135			ph:        ph,
136		})
137	}
138	for i, ph := range phs {
139		wg.Add(1)
140		go func(i int, ph source.ParseGoHandle) {
141			defer wg.Done()
142
143			files[i].file, files[i].err = ph.Parse(ctx)
144		}(i, ph)
145	}
146	wg.Wait()
147
148	for _, f := range files {
149		pkg.files = append(pkg.files, f)
150
151		if f.err != nil {
152			if f.err == context.Canceled {
153				return nil, f.err
154			}
155			imp.view.session.cache.appendPkgError(pkg, f.err)
156		}
157	}
158
159	// Use the default type information for the unsafe package.
160	if meta.pkgPath == "unsafe" {
161		pkg.types = types.Unsafe
162	} else if len(files) == 0 { // not the unsafe package, no parsed files
163		return nil, fmt.Errorf("no parsed files for package %s", pkg.pkgPath)
164	} else {
165		pkg.types = types.NewPackage(string(meta.pkgPath), meta.name)
166	}
167
168	// Handle circular imports by copying previously seen imports.
169	seen := make(map[packageID]struct{})
170	for k, v := range imp.seen {
171		seen[k] = v
172	}
173	seen[id] = struct{}{}
174
175	cfg := &types.Config{
176		Error: func(err error) {
177			imp.view.session.cache.appendPkgError(pkg, err)
178		},
179		IgnoreFuncBodies: mode == source.ParseExported,
180		Importer: &importer{
181			view:          imp.view,
182			ctx:           ctx,
183			fset:          imp.fset,
184			topLevelPkgID: imp.topLevelPkgID,
185			seen:          seen,
186		},
187	}
188	check := types.NewChecker(cfg, imp.fset, pkg.types, pkg.typesInfo)
189
190	// Ignore type-checking errors.
191	check.Files(pkg.GetSyntax())
192
193	// Add every file in this package to our cache.
194	if err := imp.cachePackage(ctx, pkg, meta, mode); err != nil {
195		return nil, err
196	}
197
198	return pkg, nil
199}
200
201func (imp *importer) cachePackage(ctx context.Context, pkg *pkg, meta *metadata, mode source.ParseMode) error {
202	for _, file := range pkg.files {
203		f, err := imp.view.getFile(ctx, file.uri)
204		if err != nil {
205			return fmt.Errorf("no such file %s: %v", file.uri, err)
206		}
207		gof, ok := f.(*goFile)
208		if !ok {
209			return fmt.Errorf("non Go file %s", file.uri)
210		}
211		if err := imp.cachePerFile(gof, file, pkg); err != nil {
212			return fmt.Errorf("failed to cache file %s: %v", gof.URI(), err)
213		}
214	}
215
216	// Set imports of package to correspond to cached packages.
217	// We lock the package cache, but we shouldn't get any inconsistencies
218	// because we are still holding the lock on the view.
219	for importPath := range meta.children {
220		importPkg, err := imp.getPkg(ctx, importPath)
221		if err != nil {
222			continue
223		}
224		pkg.imports[importPkg.pkgPath] = importPkg
225	}
226
227	return nil
228}
229
230func (imp *importer) cachePerFile(gof *goFile, file *astFile, p *pkg) error {
231	gof.mu.Lock()
232	defer gof.mu.Unlock()
233
234	// Set the package even if we failed to parse the file.
235	if gof.pkgs == nil {
236		gof.pkgs = make(map[packageID]*pkg)
237	}
238	gof.pkgs[p.id] = p
239
240	// Get the AST for the file.
241	gof.ast = file
242	if gof.ast == nil {
243		return fmt.Errorf("no AST information for %s", file.uri)
244	}
245	if gof.ast.file == nil {
246		return fmt.Errorf("no AST for %s", file.uri)
247	}
248	// Get the *token.File directly from the AST.
249	pos := gof.ast.file.Pos()
250	if !pos.IsValid() {
251		return fmt.Errorf("AST for %s has an invalid position", file.uri)
252	}
253	tok := imp.view.session.cache.FileSet().File(pos)
254	if tok == nil {
255		return fmt.Errorf("no *token.File for %s", file.uri)
256	}
257	gof.token = tok
258	gof.imports = gof.ast.file.Imports
259	return nil
260}
261
262func (c *cache) appendPkgError(pkg *pkg, err error) {
263	if err == nil {
264		return
265	}
266	var errs []packages.Error
267	switch err := err.(type) {
268	case *scanner.Error:
269		errs = append(errs, packages.Error{
270			Pos:  err.Pos.String(),
271			Msg:  err.Msg,
272			Kind: packages.ParseError,
273		})
274	case scanner.ErrorList:
275		// The first parser error is likely the root cause of the problem.
276		if err.Len() > 0 {
277			errs = append(errs, packages.Error{
278				Pos:  err[0].Pos.String(),
279				Msg:  err[0].Msg,
280				Kind: packages.ParseError,
281			})
282		}
283	case types.Error:
284		errs = append(errs, packages.Error{
285			Pos:  c.FileSet().Position(err.Pos).String(),
286			Msg:  err.Msg,
287			Kind: packages.TypeError,
288		})
289	}
290	pkg.errors = append(pkg.errors, errs...)
291}
292