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) }