github.com/april1989/origin-go-tools@v0.0.32/internal/lsp/cache/check.go (about)

     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  
     5  package cache
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"fmt"
    11  	"go/ast"
    12  	"go/types"
    13  	"path"
    14  	"sort"
    15  	"strings"
    16  	"sync"
    17  
    18  	"github.com/april1989/origin-go-tools/go/packages"
    19  	"github.com/april1989/origin-go-tools/internal/event"
    20  	"github.com/april1989/origin-go-tools/internal/lsp/debug/tag"
    21  	"github.com/april1989/origin-go-tools/internal/lsp/source"
    22  	"github.com/april1989/origin-go-tools/internal/memoize"
    23  	"github.com/april1989/origin-go-tools/internal/span"
    24  	"github.com/april1989/origin-go-tools/internal/typesinternal"
    25  	errors "golang.org/x/xerrors"
    26  )
    27  
    28  type packageHandleKey string
    29  
    30  type packageHandle struct {
    31  	handle *memoize.Handle
    32  
    33  	goFiles, compiledGoFiles []*parseGoHandle
    34  
    35  	// mode is the mode the the files were parsed in.
    36  	mode source.ParseMode
    37  
    38  	// m is the metadata associated with the package.
    39  	m *metadata
    40  
    41  	// key is the hashed key for the package.
    42  	key packageHandleKey
    43  }
    44  
    45  func (ph *packageHandle) packageKey() packageKey {
    46  	return packageKey{
    47  		id:   ph.m.id,
    48  		mode: ph.mode,
    49  	}
    50  }
    51  
    52  // packageData contains the data produced by type-checking a package.
    53  type packageData struct {
    54  	pkg *pkg
    55  	err error
    56  }
    57  
    58  // buildPackageHandle returns a packageHandle for a given package and mode.
    59  func (s *snapshot) buildPackageHandle(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, error) {
    60  	if ph := s.getPackage(id, mode); ph != nil {
    61  		return ph, nil
    62  	}
    63  
    64  	// Build the packageHandle for this ID and its dependencies.
    65  	ph, deps, err := s.buildKey(ctx, id, mode)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	// Do not close over the packageHandle or the snapshot in the Bind function.
    71  	// This creates a cycle, which causes the finalizers to never run on the handles.
    72  	// The possible cycles are:
    73  	//
    74  	//     packageHandle.h.function -> packageHandle
    75  	//     packageHandle.h.function -> snapshot -> packageHandle
    76  	//
    77  
    78  	m := ph.m
    79  	key := ph.key
    80  
    81  	h := s.generation.Bind(key, func(ctx context.Context, arg memoize.Arg) interface{} {
    82  		snapshot := arg.(*snapshot)
    83  
    84  		// Begin loading the direct dependencies, in parallel.
    85  		var wg sync.WaitGroup
    86  		for _, dep := range deps {
    87  			wg.Add(1)
    88  			go func(dep *packageHandle) {
    89  				dep.check(ctx, snapshot)
    90  				wg.Done()
    91  			}(dep)
    92  		}
    93  
    94  		data := &packageData{}
    95  		data.pkg, data.err = typeCheck(ctx, snapshot, m, mode, deps)
    96  		// Make sure that the workers above have finished before we return,
    97  		// especially in case of cancellation.
    98  		wg.Wait()
    99  
   100  		return data
   101  	})
   102  	ph.handle = h
   103  
   104  	// Cache the handle in the snapshot. If a package handle has already
   105  	// been cached, addPackage will return the cached value. This is fine,
   106  	// since the original package handle above will have no references and be
   107  	// garbage collected.
   108  	ph = s.addPackageHandle(ph)
   109  
   110  	return ph, nil
   111  }
   112  
   113  // buildKey computes the key for a given packageHandle.
   114  func (s *snapshot) buildKey(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, map[packagePath]*packageHandle, error) {
   115  	m := s.getMetadata(id)
   116  	if m == nil {
   117  		return nil, nil, errors.Errorf("no metadata for %s", id)
   118  	}
   119  	goFiles, err := s.parseGoHandles(ctx, m.goFiles, mode)
   120  	if err != nil {
   121  		return nil, nil, err
   122  	}
   123  	compiledGoFiles, err := s.parseGoHandles(ctx, m.compiledGoFiles, mode)
   124  	if err != nil {
   125  		return nil, nil, err
   126  	}
   127  	ph := &packageHandle{
   128  		m:               m,
   129  		goFiles:         goFiles,
   130  		compiledGoFiles: compiledGoFiles,
   131  		mode:            mode,
   132  	}
   133  	// Make sure all of the depList are sorted.
   134  	depList := append([]packageID{}, m.deps...)
   135  	sort.Slice(depList, func(i, j int) bool {
   136  		return depList[i] < depList[j]
   137  	})
   138  
   139  	deps := make(map[packagePath]*packageHandle)
   140  
   141  	// Begin computing the key by getting the depKeys for all dependencies.
   142  	var depKeys []packageHandleKey
   143  	for _, depID := range depList {
   144  		depHandle, err := s.buildPackageHandle(ctx, depID, s.workspaceParseMode(depID))
   145  		if err != nil {
   146  			event.Error(ctx, "no dep handle", err, tag.Package.Of(string(depID)))
   147  			if ctx.Err() != nil {
   148  				return nil, nil, ctx.Err()
   149  			}
   150  			// One bad dependency should not prevent us from checking the entire package.
   151  			// Add a special key to mark a bad dependency.
   152  			depKeys = append(depKeys, packageHandleKey(fmt.Sprintf("%s import not found", id)))
   153  			continue
   154  		}
   155  		deps[depHandle.m.pkgPath] = depHandle
   156  		depKeys = append(depKeys, depHandle.key)
   157  	}
   158  	ph.key = checkPackageKey(ctx, ph.m.id, compiledGoFiles, m.config, depKeys, mode)
   159  	return ph, deps, nil
   160  }
   161  
   162  func (s *snapshot) workspaceParseMode(id packageID) source.ParseMode {
   163  	if _, ws := s.isWorkspacePackage(id); ws {
   164  		return source.ParseFull
   165  	} else {
   166  		return source.ParseExported
   167  	}
   168  }
   169  
   170  func checkPackageKey(ctx context.Context, id packageID, pghs []*parseGoHandle, cfg *packages.Config, deps []packageHandleKey, mode source.ParseMode) packageHandleKey {
   171  	b := bytes.NewBuffer(nil)
   172  	b.WriteString(string(id))
   173  	b.WriteString(hashConfig(cfg))
   174  	b.WriteByte(byte(mode))
   175  	for _, dep := range deps {
   176  		b.WriteString(string(dep))
   177  	}
   178  	for _, cgf := range pghs {
   179  		b.WriteString(string(cgf.file.URI()))
   180  		b.WriteString(cgf.file.FileIdentity().Hash)
   181  	}
   182  	return packageHandleKey(hashContents(b.Bytes()))
   183  }
   184  
   185  // hashConfig returns the hash for the *packages.Config.
   186  func hashConfig(config *packages.Config) string {
   187  	b := bytes.NewBuffer(nil)
   188  
   189  	// Dir, Mode, Env, BuildFlags are the parts of the config that can change.
   190  	b.WriteString(config.Dir)
   191  	b.WriteString(string(rune(config.Mode)))
   192  
   193  	for _, e := range config.Env {
   194  		b.WriteString(e)
   195  	}
   196  	for _, f := range config.BuildFlags {
   197  		b.WriteString(f)
   198  	}
   199  	return hashContents(b.Bytes())
   200  }
   201  
   202  func (ph *packageHandle) Check(ctx context.Context, s source.Snapshot) (source.Package, error) {
   203  	return ph.check(ctx, s.(*snapshot))
   204  }
   205  
   206  func (ph *packageHandle) check(ctx context.Context, s *snapshot) (*pkg, error) {
   207  	v, err := ph.handle.Get(ctx, s.generation, s)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  	data := v.(*packageData)
   212  	return data.pkg, data.err
   213  }
   214  
   215  func (ph *packageHandle) CompiledGoFiles() []span.URI {
   216  	return ph.m.compiledGoFiles
   217  }
   218  
   219  func (ph *packageHandle) ID() string {
   220  	return string(ph.m.id)
   221  }
   222  
   223  func (ph *packageHandle) cached(g *memoize.Generation) (*pkg, error) {
   224  	v := ph.handle.Cached(g)
   225  	if v == nil {
   226  		return nil, errors.Errorf("no cached type information for %s", ph.m.pkgPath)
   227  	}
   228  	data := v.(*packageData)
   229  	return data.pkg, data.err
   230  }
   231  
   232  func (s *snapshot) parseGoHandles(ctx context.Context, files []span.URI, mode source.ParseMode) ([]*parseGoHandle, error) {
   233  	pghs := make([]*parseGoHandle, 0, len(files))
   234  	for _, uri := range files {
   235  		fh, err := s.GetFile(ctx, uri)
   236  		if err != nil {
   237  			return nil, err
   238  		}
   239  		pghs = append(pghs, s.parseGoHandle(ctx, fh, mode))
   240  	}
   241  	return pghs, nil
   242  }
   243  
   244  func typeCheck(ctx context.Context, snapshot *snapshot, m *metadata, mode source.ParseMode, deps map[packagePath]*packageHandle) (*pkg, error) {
   245  	ctx, done := event.Start(ctx, "cache.importer.typeCheck", tag.Package.Of(string(m.id)))
   246  	defer done()
   247  
   248  	var rawErrors []error
   249  	for _, err := range m.errors {
   250  		rawErrors = append(rawErrors, err)
   251  	}
   252  
   253  	fset := snapshot.view.session.cache.fset
   254  	pkg := &pkg{
   255  		m:               m,
   256  		mode:            mode,
   257  		goFiles:         make([]*source.ParsedGoFile, len(m.goFiles)),
   258  		compiledGoFiles: make([]*source.ParsedGoFile, len(m.compiledGoFiles)),
   259  		module:          m.module,
   260  		imports:         make(map[packagePath]*pkg),
   261  		typesSizes:      m.typesSizes,
   262  		typesInfo: &types.Info{
   263  			Types:      make(map[ast.Expr]types.TypeAndValue),
   264  			Defs:       make(map[*ast.Ident]types.Object),
   265  			Uses:       make(map[*ast.Ident]types.Object),
   266  			Implicits:  make(map[ast.Node]types.Object),
   267  			Selections: make(map[*ast.SelectorExpr]*types.Selection),
   268  			Scopes:     make(map[ast.Node]*types.Scope),
   269  		},
   270  	}
   271  	var (
   272  		files        = make([]*ast.File, len(m.compiledGoFiles))
   273  		parseErrors  = make([]error, len(m.compiledGoFiles))
   274  		actualErrors = make([]error, len(m.compiledGoFiles))
   275  		wg           sync.WaitGroup
   276  
   277  		mu             sync.Mutex
   278  		skipTypeErrors bool
   279  	)
   280  	for i, cgf := range m.compiledGoFiles {
   281  		wg.Add(1)
   282  		go func(i int, cgf span.URI) {
   283  			defer wg.Done()
   284  			fh, err := snapshot.GetFile(ctx, cgf)
   285  			if err != nil {
   286  				actualErrors[i] = err
   287  				return
   288  			}
   289  			pgh := snapshot.parseGoHandle(ctx, fh, mode)
   290  			pgf, fixed, err := snapshot.parseGo(ctx, pgh)
   291  			if err != nil {
   292  				actualErrors[i] = err
   293  				return
   294  			}
   295  			pkg.compiledGoFiles[i] = pgf
   296  			files[i], parseErrors[i], actualErrors[i] = pgf.File, pgf.ParseErr, err
   297  
   298  			mu.Lock()
   299  			skipTypeErrors = skipTypeErrors || fixed
   300  			mu.Unlock()
   301  		}(i, cgf)
   302  	}
   303  	for i, gf := range m.goFiles {
   304  		wg.Add(1)
   305  		// We need to parse the non-compiled go files, but we don't care about their errors.
   306  		go func(i int, gf span.URI) {
   307  			defer wg.Done()
   308  			fh, err := snapshot.GetFile(ctx, gf)
   309  			if err != nil {
   310  				return
   311  			}
   312  			pgf, _ := snapshot.ParseGo(ctx, fh, mode)
   313  			pkg.goFiles[i] = pgf
   314  		}(i, gf)
   315  	}
   316  	wg.Wait()
   317  	for _, err := range actualErrors {
   318  		if err != nil {
   319  			return nil, err
   320  		}
   321  	}
   322  
   323  	for _, e := range parseErrors {
   324  		if e != nil {
   325  			rawErrors = append(rawErrors, e)
   326  		}
   327  	}
   328  
   329  	var i int
   330  	for _, f := range files {
   331  		if f != nil {
   332  			files[i] = f
   333  			i++
   334  		}
   335  	}
   336  	files = files[:i]
   337  
   338  	// Use the default type information for the unsafe package.
   339  	if pkg.m.pkgPath == "unsafe" {
   340  		pkg.types = types.Unsafe
   341  		// Don't type check Unsafe: it's unnecessary, and doing so exposes a data
   342  		// race to Unsafe.completed.
   343  		return pkg, nil
   344  	} else if len(files) == 0 { // not the unsafe package, no parsed files
   345  		// Try to attach errors messages to the file as much as possible.
   346  		var found bool
   347  		for _, e := range rawErrors {
   348  			srcErr, err := sourceError(ctx, snapshot, pkg, e)
   349  			if err != nil {
   350  				continue
   351  			}
   352  			found = true
   353  			pkg.errors = append(pkg.errors, srcErr)
   354  		}
   355  		if found {
   356  			return pkg, nil
   357  		}
   358  		return nil, errors.Errorf("no parsed files for package %s, expected: %s, list errors: %v", pkg.m.pkgPath, pkg.compiledGoFiles, rawErrors)
   359  	} else {
   360  		pkg.types = types.NewPackage(string(m.pkgPath), string(m.name))
   361  	}
   362  
   363  	cfg := &types.Config{
   364  		Error: func(e error) {
   365  			// If we have fixed parse errors in any of the files,
   366  			// we should hide type errors, as they may be completely nonsensical.
   367  			if skipTypeErrors {
   368  				return
   369  			}
   370  			rawErrors = append(rawErrors, e)
   371  		},
   372  		Importer: importerFunc(func(pkgPath string) (*types.Package, error) {
   373  			// If the context was cancelled, we should abort.
   374  			if ctx.Err() != nil {
   375  				return nil, ctx.Err()
   376  			}
   377  			dep := resolveImportPath(pkgPath, pkg, deps)
   378  			if dep == nil {
   379  				return nil, errors.Errorf("no package for import %s", pkgPath)
   380  			}
   381  			if !isValidImport(m.pkgPath, dep.m.pkgPath) {
   382  				return nil, errors.Errorf("invalid use of internal package %s", pkgPath)
   383  			}
   384  			depPkg, err := dep.check(ctx, snapshot)
   385  			if err != nil {
   386  				return nil, err
   387  			}
   388  			pkg.imports[depPkg.m.pkgPath] = depPkg
   389  			return depPkg.types, nil
   390  		}),
   391  	}
   392  	// We want to type check cgo code if go/types supports it.
   393  	// We passed typecheckCgo to go/packages when we Loaded.
   394  	typesinternal.SetUsesCgo(cfg)
   395  
   396  	check := types.NewChecker(cfg, fset, pkg.types, pkg.typesInfo)
   397  
   398  	// Type checking errors are handled via the config, so ignore them here.
   399  	_ = check.Files(files)
   400  	// If the context was cancelled, we may have returned a ton of transient
   401  	// errors to the type checker. Swallow them.
   402  	if ctx.Err() != nil {
   403  		return nil, ctx.Err()
   404  	}
   405  
   406  	// We don't care about a package's errors unless we have parsed it in full.
   407  	if mode == source.ParseFull {
   408  		for _, e := range rawErrors {
   409  			srcErr, err := sourceError(ctx, snapshot, pkg, e)
   410  			if err != nil {
   411  				event.Error(ctx, "unable to compute error positions", err, tag.Package.Of(pkg.ID()))
   412  				continue
   413  			}
   414  			pkg.errors = append(pkg.errors, srcErr)
   415  			if err, ok := e.(types.Error); ok {
   416  				pkg.typeErrors = append(pkg.typeErrors, err)
   417  			}
   418  		}
   419  	}
   420  
   421  	return pkg, nil
   422  }
   423  
   424  // resolveImportPath resolves an import path in pkg to a package from deps.
   425  // It should produce the same results as resolveImportPath:
   426  // https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/load/pkg.go;drc=641918ee09cb44d282a30ee8b66f99a0b63eaef9;l=990.
   427  func resolveImportPath(importPath string, pkg *pkg, deps map[packagePath]*packageHandle) *packageHandle {
   428  	if dep := deps[packagePath(importPath)]; dep != nil {
   429  		return dep
   430  	}
   431  	// We may be in GOPATH mode, in which case we need to check vendor dirs.
   432  	searchDir := path.Dir(pkg.PkgPath())
   433  	for {
   434  		vdir := packagePath(path.Join(searchDir, "vendor", importPath))
   435  		if vdep := deps[vdir]; vdep != nil {
   436  			return vdep
   437  		}
   438  
   439  		// Search until Dir doesn't take us anywhere new, e.g. "." or "/".
   440  		next := path.Dir(searchDir)
   441  		if searchDir == next {
   442  			break
   443  		}
   444  		searchDir = next
   445  	}
   446  
   447  	// Vendor didn't work. Let's try minimal module compatibility mode.
   448  	// In MMC, the packagePath is the canonical (.../vN/...) path, which
   449  	// is hard to calculate. But the go command has already resolved the ID
   450  	// to the non-versioned path, and we can take advantage of that.
   451  	for _, dep := range deps {
   452  		if dep.ID() == importPath {
   453  			return dep
   454  		}
   455  	}
   456  	return nil
   457  }
   458  
   459  func isValidImport(pkgPath, importPkgPath packagePath) bool {
   460  	i := strings.LastIndex(string(importPkgPath), "/internal/")
   461  	if i == -1 {
   462  		return true
   463  	}
   464  	if pkgPath == "command-line-arguments" {
   465  		return true
   466  	}
   467  	return strings.HasPrefix(string(pkgPath), string(importPkgPath[:i]))
   468  }
   469  
   470  // An importFunc is an implementation of the single-method
   471  // types.Importer interface based on a function value.
   472  type importerFunc func(path string) (*types.Package, error)
   473  
   474  func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }