// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cache import ( "context" "go/ast" "go/types" "golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/span" errors "golang.org/x/xerrors" ) // pkg contains the type information needed by the source package. type pkg struct { // ID and package path have their own types to avoid being used interchangeably. id packageID pkgPath packagePath mode source.ParseMode goFiles []source.ParseGoHandle compiledGoFiles []source.ParseGoHandle errors []*source.Error imports map[packagePath]*pkg types *types.Package typesInfo *types.Info typesSizes types.Sizes } // Declare explicit types for package paths and IDs to ensure that we never use // an ID where a path belongs, and vice versa. If we confused the two, it would // result in confusing errors because package IDs often look like package paths. type packageID string type packagePath string // Declare explicit types for files and directories to distinguish between the two. type fileURI span.URI type directoryURI span.URI func (p *pkg) ID() string { return string(p.id) } func (p *pkg) PkgPath() string { return string(p.pkgPath) } func (p *pkg) CompiledGoFiles() []source.ParseGoHandle { return p.compiledGoFiles } func (p *pkg) File(uri span.URI) (source.ParseGoHandle, error) { for _, ph := range p.compiledGoFiles { if ph.File().Identity().URI == uri { return ph, nil } } for _, ph := range p.goFiles { if ph.File().Identity().URI == uri { return ph, nil } } return nil, errors.Errorf("no ParseGoHandle for %s", uri) } func (p *pkg) GetSyntax() []*ast.File { var syntax []*ast.File for _, ph := range p.compiledGoFiles { file, _, _, err := ph.Cached() if err == nil { syntax = append(syntax, file) } } return syntax } func (p *pkg) GetErrors() []*source.Error { return p.errors } func (p *pkg) GetTypes() *types.Package { return p.types } func (p *pkg) GetTypesInfo() *types.Info { return p.typesInfo } func (p *pkg) GetTypesSizes() types.Sizes { return p.typesSizes } func (p *pkg) IsIllTyped() bool { return p.types == nil || p.typesInfo == nil || p.typesSizes == nil } func (p *pkg) GetImport(pkgPath string) (source.Package, error) { if imp := p.imports[packagePath(pkgPath)]; imp != nil { return imp, nil } // Don't return a nil pointer because that still satisfies the interface. return nil, errors.Errorf("no imported package for %s", pkgPath) } func (p *pkg) Imports() []source.Package { var result []source.Package for _, imp := range p.imports { result = append(result, imp) } return result } func (s *snapshot) FindAnalysisError(ctx context.Context, pkgID, analyzerName, msg string, rng protocol.Range) (*source.Error, error) { analyzer, ok := s.View().Options().Analyzers[analyzerName] if !ok { return nil, errors.Errorf("unexpected analyzer: %s", analyzerName) } act, err := s.actionHandle(ctx, packageID(pkgID), source.ParseFull, analyzer) if err != nil { return nil, err } errs, _, err := act.analyze(ctx) if err != nil { return nil, err } for _, err := range errs { if err.Category != analyzerName { continue } if err.Message != msg { continue } if protocol.CompareRange(err.Range, rng) != 0 { continue } return err, nil } return nil, errors.Errorf("no matching diagnostic for %s:%v", pkgID, analyzerName) }