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 "go/ast" 10 "go/types" 11 12 "golang.org/x/tools/internal/lsp/protocol" 13 "golang.org/x/tools/internal/lsp/source" 14 "golang.org/x/tools/internal/span" 15 errors "golang.org/x/xerrors" 16) 17 18// pkg contains the type information needed by the source package. 19type pkg struct { 20 // ID and package path have their own types to avoid being used interchangeably. 21 id packageID 22 pkgPath packagePath 23 mode source.ParseMode 24 forTest packagePath 25 goFiles []source.ParseGoHandle 26 compiledGoFiles []source.ParseGoHandle 27 errors []*source.Error 28 imports map[packagePath]*pkg 29 types *types.Package 30 typesInfo *types.Info 31 typesSizes types.Sizes 32} 33 34// Declare explicit types for package paths and IDs to ensure that we never use 35// an ID where a path belongs, and vice versa. If we confused the two, it would 36// result in confusing errors because package IDs often look like package paths. 37type packageID string 38type packagePath string 39 40// Declare explicit types for files and directories to distinguish between the two. 41type fileURI span.URI 42type directoryURI span.URI 43type viewLoadScope span.URI 44 45func (p *pkg) ID() string { 46 return string(p.id) 47} 48 49func (p *pkg) PkgPath() string { 50 return string(p.pkgPath) 51} 52 53func (p *pkg) CompiledGoFiles() []source.ParseGoHandle { 54 return p.compiledGoFiles 55} 56 57func (p *pkg) File(uri span.URI) (source.ParseGoHandle, error) { 58 for _, ph := range p.compiledGoFiles { 59 if ph.File().Identity().URI == uri { 60 return ph, nil 61 } 62 } 63 for _, ph := range p.goFiles { 64 if ph.File().Identity().URI == uri { 65 return ph, nil 66 } 67 } 68 return nil, errors.Errorf("no ParseGoHandle for %s", uri) 69} 70 71func (p *pkg) GetSyntax() []*ast.File { 72 var syntax []*ast.File 73 for _, ph := range p.compiledGoFiles { 74 file, _, _, _, err := ph.Cached() 75 if err == nil { 76 syntax = append(syntax, file) 77 } 78 } 79 return syntax 80} 81 82func (p *pkg) GetErrors() []*source.Error { 83 return p.errors 84} 85 86func (p *pkg) GetTypes() *types.Package { 87 return p.types 88} 89 90func (p *pkg) GetTypesInfo() *types.Info { 91 return p.typesInfo 92} 93 94func (p *pkg) GetTypesSizes() types.Sizes { 95 return p.typesSizes 96} 97 98func (p *pkg) IsIllTyped() bool { 99 return p.types == nil || p.typesInfo == nil || p.typesSizes == nil 100} 101 102func (p *pkg) ForTest() string { 103 return string(p.forTest) 104} 105 106func (p *pkg) GetImport(pkgPath string) (source.Package, error) { 107 if imp := p.imports[packagePath(pkgPath)]; imp != nil { 108 return imp, nil 109 } 110 // Don't return a nil pointer because that still satisfies the interface. 111 return nil, errors.Errorf("no imported package for %s", pkgPath) 112} 113 114func (p *pkg) Imports() []source.Package { 115 var result []source.Package 116 for _, imp := range p.imports { 117 result = append(result, imp) 118 } 119 return result 120} 121 122func (s *snapshot) FindAnalysisError(ctx context.Context, pkgID, analyzerName, msg string, rng protocol.Range) (*source.Error, error) { 123 analyzer, ok := s.View().Options().Analyzers[analyzerName] 124 if !ok { 125 return nil, errors.Errorf("unexpected analyzer: %s", analyzerName) 126 } 127 act, err := s.actionHandle(ctx, packageID(pkgID), analyzer) 128 if err != nil { 129 return nil, err 130 } 131 errs, _, err := act.analyze(ctx) 132 if err != nil { 133 return nil, err 134 } 135 for _, err := range errs { 136 if err.Category != analyzerName { 137 continue 138 } 139 if err.Message != msg { 140 continue 141 } 142 if protocol.CompareRange(err.Range, rng) != 0 { 143 continue 144 } 145 return err, nil 146 } 147 return nil, errors.Errorf("no matching diagnostic for %s:%v", pkgID, analyzerName) 148} 149