// 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/packagesinternal" "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 forTest packagePath goFiles []source.ParseGoHandle compiledGoFiles []source.ParseGoHandle errors []*source.Error imports map[packagePath]*pkg module *packagesinternal.Module typeErrors []types.Error 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 type viewLoadScope 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) ForTest() string { return string(p.forTest) } 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 (p *pkg) Module() *packagesinternal.Module { return p.module } func (s *snapshot) FindAnalysisError(ctx context.Context, pkgID, analyzerName, msg string, rng protocol.Range) (*source.Error, *source.Analyzer, error) { analyzer := findAnalyzer(s, analyzerName) if analyzer.Analyzer == nil { return nil, nil, errors.Errorf("unexpected analyzer: %s", analyzerName) } if !analyzer.Enabled { return nil, nil, errors.Errorf("disabled analyzer: %s", analyzerName) } act, err := s.actionHandle(ctx, packageID(pkgID), analyzer.Analyzer) if err != nil { return nil, nil, err } errs, _, err := act.analyze(ctx) if err != nil { return nil, nil, err } for _, err := range errs { if err.Category != analyzer.Analyzer.Name { continue } if err.Message != msg { continue } if protocol.CompareRange(err.Range, rng) != 0 { continue } return err, &analyzer, nil } return nil, nil, errors.Errorf("no matching diagnostic for %s:%v", pkgID, analyzerName) } func findAnalyzer(s *snapshot, analyzerName string) source.Analyzer { checked := s.View().Options().DefaultAnalyzers if a, ok := checked[analyzerName]; ok { return a } checked = s.View().Options().TypeErrorAnalyzers if a, ok := checked[analyzerName]; ok { return a } return source.Analyzer{} }