// 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" "golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/lsp/telemetry" "golang.org/x/tools/internal/span" errors "golang.org/x/xerrors" ) func (v *view) CheckPackageHandles(ctx context.Context, f source.File) (source.Snapshot, []source.CheckPackageHandle, error) { // Get the snapshot that will be used for type-checking. s := v.getSnapshot() cphs, err := s.CheckPackageHandles(ctx, f) if err != nil { return nil, nil, err } if len(cphs) == 0 { return nil, nil, errors.Errorf("no CheckPackageHandles for %s", f.URI()) } return s, cphs, nil } func (s *snapshot) CheckPackageHandles(ctx context.Context, f source.File) ([]source.CheckPackageHandle, error) { ctx = telemetry.File.With(ctx, f.URI()) fh := s.Handle(ctx, f) // Determine if we need to type-check the package. m, cphs, load, check := s.shouldCheck(fh) // We may need to re-load package metadata. // We only need to this if it has been invalidated, and is therefore unvailable. if load { var err error m, err = s.load(ctx, f.URI()) if err != nil { return nil, err } // If load has explicitly returned nil metadata and no error, // it means that we should not re-type-check the packages. if m == nil { return cphs, nil } } if check { var results []source.CheckPackageHandle for _, m := range m { cph, err := s.checkPackageHandle(ctx, m.id, source.ParseFull) if err != nil { return nil, err } results = append(results, cph) } cphs = results } if len(cphs) == 0 { return nil, errors.Errorf("no CheckPackageHandles for %s", f.URI()) } return cphs, nil } func (s *snapshot) shouldCheck(fh source.FileHandle) (m []*metadata, cphs []source.CheckPackageHandle, load, check bool) { // Get the metadata for the given file. m = s.getMetadataForURI(fh.Identity().URI) // If there is no metadata for the package, we definitely need to type-check again. if len(m) == 0 { return nil, nil, true, true } // If the metadata for the package had missing dependencies, // we _may_ need to re-check. If the missing dependencies haven't changed // since previous load, we will not check again. for _, m := range m { if len(m.missingDeps) != 0 { load = true check = true } } // We expect to see a checked package for each package ID, // and it should be parsed in full mode. cphs = s.getPackages(fh.Identity().URI, source.ParseFull) if len(cphs) < len(m) { return m, nil, load, true } return m, cphs, load, check } func (v *view) GetActiveReverseDeps(ctx context.Context, f source.File) (results []source.CheckPackageHandle) { var ( s = v.getSnapshot() rdeps = transitiveReverseDependencies(ctx, f.URI(), s) files = v.openFiles(ctx, rdeps) seen = make(map[span.URI]struct{}) ) for _, f := range files { if _, ok := seen[f.URI()]; ok { continue } cphs, err := s.CheckPackageHandles(ctx, f) if err != nil { continue } cph, err := source.WidestCheckPackageHandle(cphs) if err != nil { continue } for _, ph := range cph.Files() { seen[ph.File().Identity().URI] = struct{}{} } results = append(results, cph) } return results } func transitiveReverseDependencies(ctx context.Context, uri span.URI, s *snapshot) (result []span.URI) { var ( seen = make(map[packageID]struct{}) uris = make(map[span.URI]struct{}) topLevelURIs = make(map[span.URI]struct{}) ) metadata := s.getMetadataForURI(uri) for _, m := range metadata { for _, uri := range m.files { topLevelURIs[uri] = struct{}{} } s.reverseDependencies(m.id, uris, seen) } // Filter out the URIs that belong to the original package. for uri := range uris { if _, ok := topLevelURIs[uri]; ok { continue } result = append(result, uri) } return result }