source_env.go (3275B)
1 // Copyright 2024 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 imports 6 7 import ( 8 "context" 9 "path/filepath" 10 "strings" 11 "sync" 12 13 "golang.org/x/sync/errgroup" 14 "golang.org/x/tools/internal/gopathwalk" 15 ) 16 17 // ProcessEnvSource implements the [Source] interface using the legacy 18 // [ProcessEnv] abstraction. 19 type ProcessEnvSource struct { 20 env *ProcessEnv 21 srcDir string 22 filename string 23 pkgName string 24 } 25 26 // NewProcessEnvSource returns a [ProcessEnvSource] wrapping the given 27 // env, to be used for fixing imports in the file with name filename in package 28 // named pkgName. 29 func NewProcessEnvSource(env *ProcessEnv, filename, pkgName string) (*ProcessEnvSource, error) { 30 abs, err := filepath.Abs(filename) 31 if err != nil { 32 return nil, err 33 } 34 srcDir := filepath.Dir(abs) 35 return &ProcessEnvSource{ 36 env: env, 37 srcDir: srcDir, 38 filename: filename, 39 pkgName: pkgName, 40 }, nil 41 } 42 43 func (s *ProcessEnvSource) LoadPackageNames(ctx context.Context, srcDir string, unknown []string) (map[string]string, error) { 44 r, err := s.env.GetResolver() 45 if err != nil { 46 return nil, err 47 } 48 return r.loadPackageNames(unknown, srcDir) 49 } 50 51 func (s *ProcessEnvSource) ResolveReferences(ctx context.Context, filename string, refs map[string]map[string]bool) ([]*Result, error) { 52 var mu sync.Mutex 53 found := make(map[string][]pkgDistance) 54 callback := &scanCallback{ 55 rootFound: func(gopathwalk.Root) bool { 56 return true 57 }, 58 dirFound: func(pkg *pkg) bool { 59 return pkgIsCandidate(filename, refs, pkg) 60 }, 61 packageNameLoaded: func(pkg *pkg) bool { 62 if _, want := refs[pkg.packageName]; !want { 63 return false 64 } 65 if pkg.dir == s.srcDir && s.pkgName == pkg.packageName { 66 // The candidate is in the same directory and has the 67 // same package name. Don't try to import ourselves. 68 return false 69 } 70 if !CanUse(filename, pkg.dir) { 71 return false 72 } 73 mu.Lock() 74 defer mu.Unlock() 75 found[pkg.packageName] = append(found[pkg.packageName], pkgDistance{pkg, distance(s.srcDir, pkg.dir)}) 76 return false // We'll do our own loading after we sort. 77 }, 78 } 79 resolver, err := s.env.GetResolver() 80 if err != nil { 81 return nil, err 82 } 83 if err := resolver.scan(ctx, callback); err != nil { 84 return nil, err 85 } 86 87 g, ctx := errgroup.WithContext(ctx) 88 89 searcher := symbolSearcher{ 90 logf: s.env.logf, 91 srcDir: s.srcDir, 92 xtest: strings.HasSuffix(s.pkgName, "_test"), 93 loadExports: resolver.loadExports, 94 } 95 96 var resultMu sync.Mutex 97 results := make(map[string]*Result, len(refs)) 98 for pkgName, symbols := range refs { 99 g.Go(func() error { 100 found, err := searcher.search(ctx, found[pkgName], pkgName, symbols) 101 if err != nil { 102 return err 103 } 104 if found == nil { 105 return nil // No matching package. 106 } 107 108 imp := &ImportInfo{ 109 ImportPath: found.importPathShort, 110 } 111 pkg := &PackageInfo{ 112 Name: pkgName, 113 Exports: symbols, 114 } 115 resultMu.Lock() 116 results[pkgName] = &Result{Import: imp, Package: pkg} 117 resultMu.Unlock() 118 return nil 119 }) 120 } 121 if err := g.Wait(); err != nil { 122 return nil, err 123 } 124 var ans []*Result 125 for _, x := range results { 126 ans = append(ans, x) 127 } 128 return ans, nil 129 }