golang.org/x/tools/gopls@v0.15.3/internal/golang/snapshot.go (about)

     1  // Copyright 2018 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 golang
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  
    11  	"golang.org/x/tools/gopls/internal/cache"
    12  	"golang.org/x/tools/gopls/internal/cache/metadata"
    13  	"golang.org/x/tools/gopls/internal/cache/parsego"
    14  	"golang.org/x/tools/gopls/internal/protocol"
    15  )
    16  
    17  // NarrowestMetadataForFile returns metadata for the narrowest package
    18  // (the one with the fewest files) that encloses the specified file.
    19  // The result may be a test variant, but never an intermediate test variant.
    20  func NarrowestMetadataForFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (*metadata.Package, error) {
    21  	mps, err := snapshot.MetadataForFile(ctx, uri)
    22  	if err != nil {
    23  		return nil, err
    24  	}
    25  	metadata.RemoveIntermediateTestVariants(&mps)
    26  	if len(mps) == 0 {
    27  		return nil, fmt.Errorf("no package metadata for file %s", uri)
    28  	}
    29  	return mps[0], nil
    30  }
    31  
    32  // NarrowestPackageForFile is a convenience function that selects the narrowest
    33  // non-ITV package to which this file belongs, type-checks it in the requested
    34  // mode (full or workspace), and returns it, along with the parse tree of that
    35  // file.
    36  //
    37  // The "narrowest" package is the one with the fewest number of files that
    38  // includes the given file. This solves the problem of test variants, as the
    39  // test will have more files than the non-test package.
    40  //
    41  // An intermediate test variant (ITV) package has identical source to a regular
    42  // package but resolves imports differently. gopls should never need to
    43  // type-check them.
    44  //
    45  // Type-checking is expensive. Call snapshot.ParseGo if all you need is a parse
    46  // tree, or snapshot.MetadataForFile if you only need metadata.
    47  func NarrowestPackageForFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (*cache.Package, *ParsedGoFile, error) {
    48  	return selectPackageForFile(ctx, snapshot, uri, func(metas []*metadata.Package) *metadata.Package { return metas[0] })
    49  }
    50  
    51  // WidestPackageForFile is a convenience function that selects the widest
    52  // non-ITV package to which this file belongs, type-checks it in the requested
    53  // mode (full or workspace), and returns it, along with the parse tree of that
    54  // file.
    55  //
    56  // The "widest" package is the one with the most number of files that includes
    57  // the given file. Which is the test variant if one exists.
    58  //
    59  // An intermediate test variant (ITV) package has identical source to a regular
    60  // package but resolves imports differently. gopls should never need to
    61  // type-check them.
    62  //
    63  // Type-checking is expensive. Call snapshot.ParseGo if all you need is a parse
    64  // tree, or snapshot.MetadataForFile if you only need metadata.
    65  func WidestPackageForFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) (*cache.Package, *ParsedGoFile, error) {
    66  	return selectPackageForFile(ctx, snapshot, uri, func(metas []*metadata.Package) *metadata.Package { return metas[len(metas)-1] })
    67  }
    68  
    69  func selectPackageForFile(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI, selector func([]*metadata.Package) *metadata.Package) (*cache.Package, *ParsedGoFile, error) {
    70  	mps, err := snapshot.MetadataForFile(ctx, uri)
    71  	if err != nil {
    72  		return nil, nil, err
    73  	}
    74  	metadata.RemoveIntermediateTestVariants(&mps)
    75  	if len(mps) == 0 {
    76  		return nil, nil, fmt.Errorf("no package metadata for file %s", uri)
    77  	}
    78  	mp := selector(mps)
    79  	pkgs, err := snapshot.TypeCheck(ctx, mp.ID)
    80  	if err != nil {
    81  		return nil, nil, err
    82  	}
    83  	pkg := pkgs[0]
    84  	pgf, err := pkg.File(uri)
    85  	if err != nil {
    86  		return nil, nil, err // "can't happen"
    87  	}
    88  	return pkg, pgf, err
    89  }
    90  
    91  type ParsedGoFile = parsego.File
    92  
    93  const (
    94  	ParseHeader = parsego.ParseHeader
    95  	ParseFull   = parsego.ParseFull
    96  )
    97  
    98  type (
    99  	PackageID   = metadata.PackageID
   100  	PackagePath = metadata.PackagePath
   101  	PackageName = metadata.PackageName
   102  	ImportPath  = metadata.ImportPath
   103  )
   104  
   105  type unit = struct{}