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 "fmt" 10 "go/ast" 11 "go/scanner" 12 "go/token" 13 "go/types" 14 "sync" 15 16 "golang.org/x/tools/go/analysis" 17 "golang.org/x/tools/go/packages" 18 "golang.org/x/tools/internal/lsp/source" 19 "golang.org/x/tools/internal/lsp/telemetry/trace" 20 "golang.org/x/tools/internal/span" 21) 22 23type importer struct { 24 view *view 25 26 // seen maintains the set of previously imported packages. 27 // If we have seen a package that is already in this map, we have a circular import. 28 seen map[packageID]struct{} 29 30 // topLevelPkgID is the ID of the package from which type-checking began. 31 topLevelPkgID packageID 32 33 ctx context.Context 34 fset *token.FileSet 35} 36 37func (imp *importer) Import(pkgPath string) (*types.Package, error) { 38 ctx := imp.ctx 39 id, ok := imp.view.mcache.ids[packagePath(pkgPath)] 40 if !ok { 41 return nil, fmt.Errorf("no known ID for %s", pkgPath) 42 } 43 pkg, err := imp.getPkg(ctx, id) 44 if err != nil { 45 return nil, err 46 } 47 return pkg.types, nil 48} 49 50func (imp *importer) getPkg(ctx context.Context, id packageID) (*pkg, error) { 51 if _, ok := imp.seen[id]; ok { 52 return nil, fmt.Errorf("circular import detected") 53 } 54 imp.view.pcache.mu.Lock() 55 e, ok := imp.view.pcache.packages[id] 56 57 if ok { 58 // cache hit 59 imp.view.pcache.mu.Unlock() 60 // wait for entry to become ready 61 <-e.ready 62 } else { 63 // cache miss 64 e = &entry{ready: make(chan struct{})} 65 imp.view.pcache.packages[id] = e 66 imp.view.pcache.mu.Unlock() 67 68 // This goroutine becomes responsible for populating 69 // the entry and broadcasting its readiness. 70 e.pkg, e.err = imp.typeCheck(ctx, id) 71 close(e.ready) 72 } 73 74 if e.err != nil { 75 // If the import had been previously canceled, and that error cached, try again. 76 if e.err == context.Canceled && ctx.Err() == nil { 77 imp.view.pcache.mu.Lock() 78 // Clear out canceled cache entry if it is still there. 79 if imp.view.pcache.packages[id] == e { 80 delete(imp.view.pcache.packages, id) 81 } 82 imp.view.pcache.mu.Unlock() 83 return imp.getPkg(ctx, id) 84 } 85 return nil, e.err 86 } 87 88 return e.pkg, nil 89} 90 91func (imp *importer) typeCheck(ctx context.Context, id packageID) (*pkg, error) { 92 ctx, ts := trace.StartSpan(ctx, "cache.importer.typeCheck") 93 defer ts.End() 94 meta, ok := imp.view.mcache.packages[id] 95 if !ok { 96 return nil, fmt.Errorf("no metadata for %v", id) 97 } 98 pkg := &pkg{ 99 id: meta.id, 100 pkgPath: meta.pkgPath, 101 imports: make(map[packagePath]*pkg), 102 typesSizes: meta.typesSizes, 103 typesInfo: &types.Info{ 104 Types: make(map[ast.Expr]types.TypeAndValue), 105 Defs: make(map[*ast.Ident]types.Object), 106 Uses: make(map[*ast.Ident]types.Object), 107 Implicits: make(map[ast.Node]types.Object), 108 Selections: make(map[*ast.SelectorExpr]*types.Selection), 109 Scopes: make(map[ast.Node]*types.Scope), 110 }, 111 analyses: make(map[*analysis.Analyzer]*analysisEntry), 112 } 113 114 // Ignore function bodies for any dependency packages. 115 mode := source.ParseFull 116 if imp.topLevelPkgID != pkg.id { 117 mode = source.ParseExported 118 } 119 var ( 120 files []*astFile 121 phs []source.ParseGoHandle 122 wg sync.WaitGroup 123 ) 124 for _, filename := range meta.files { 125 uri := span.FileURI(filename) 126 f, err := imp.view.getFile(ctx, uri) 127 if err != nil { 128 continue 129 } 130 ph := imp.view.session.cache.ParseGoHandle(f.Handle(ctx), mode) 131 phs = append(phs, ph) 132 files = append(files, &astFile{ 133 uri: ph.File().Identity().URI, 134 isTrimmed: mode == source.ParseExported, 135 ph: ph, 136 }) 137 } 138 for i, ph := range phs { 139 wg.Add(1) 140 go func(i int, ph source.ParseGoHandle) { 141 defer wg.Done() 142 143 files[i].file, files[i].err = ph.Parse(ctx) 144 }(i, ph) 145 } 146 wg.Wait() 147 148 for _, f := range files { 149 pkg.files = append(pkg.files, f) 150 151 if f.err != nil { 152 if f.err == context.Canceled { 153 return nil, f.err 154 } 155 imp.view.session.cache.appendPkgError(pkg, f.err) 156 } 157 } 158 159 // Use the default type information for the unsafe package. 160 if meta.pkgPath == "unsafe" { 161 pkg.types = types.Unsafe 162 } else if len(files) == 0 { // not the unsafe package, no parsed files 163 return nil, fmt.Errorf("no parsed files for package %s", pkg.pkgPath) 164 } else { 165 pkg.types = types.NewPackage(string(meta.pkgPath), meta.name) 166 } 167 168 // Handle circular imports by copying previously seen imports. 169 seen := make(map[packageID]struct{}) 170 for k, v := range imp.seen { 171 seen[k] = v 172 } 173 seen[id] = struct{}{} 174 175 cfg := &types.Config{ 176 Error: func(err error) { 177 imp.view.session.cache.appendPkgError(pkg, err) 178 }, 179 IgnoreFuncBodies: mode == source.ParseExported, 180 Importer: &importer{ 181 view: imp.view, 182 ctx: ctx, 183 fset: imp.fset, 184 topLevelPkgID: imp.topLevelPkgID, 185 seen: seen, 186 }, 187 } 188 check := types.NewChecker(cfg, imp.fset, pkg.types, pkg.typesInfo) 189 190 // Ignore type-checking errors. 191 check.Files(pkg.GetSyntax()) 192 193 // Add every file in this package to our cache. 194 if err := imp.cachePackage(ctx, pkg, meta, mode); err != nil { 195 return nil, err 196 } 197 198 return pkg, nil 199} 200 201func (imp *importer) cachePackage(ctx context.Context, pkg *pkg, meta *metadata, mode source.ParseMode) error { 202 for _, file := range pkg.files { 203 f, err := imp.view.getFile(ctx, file.uri) 204 if err != nil { 205 return fmt.Errorf("no such file %s: %v", file.uri, err) 206 } 207 gof, ok := f.(*goFile) 208 if !ok { 209 return fmt.Errorf("non Go file %s", file.uri) 210 } 211 if err := imp.cachePerFile(gof, file, pkg); err != nil { 212 return fmt.Errorf("failed to cache file %s: %v", gof.URI(), err) 213 } 214 } 215 216 // Set imports of package to correspond to cached packages. 217 // We lock the package cache, but we shouldn't get any inconsistencies 218 // because we are still holding the lock on the view. 219 for importPath := range meta.children { 220 importPkg, err := imp.getPkg(ctx, importPath) 221 if err != nil { 222 continue 223 } 224 pkg.imports[importPkg.pkgPath] = importPkg 225 } 226 227 return nil 228} 229 230func (imp *importer) cachePerFile(gof *goFile, file *astFile, p *pkg) error { 231 gof.mu.Lock() 232 defer gof.mu.Unlock() 233 234 // Set the package even if we failed to parse the file. 235 if gof.pkgs == nil { 236 gof.pkgs = make(map[packageID]*pkg) 237 } 238 gof.pkgs[p.id] = p 239 240 // Get the AST for the file. 241 gof.ast = file 242 if gof.ast == nil { 243 return fmt.Errorf("no AST information for %s", file.uri) 244 } 245 if gof.ast.file == nil { 246 return fmt.Errorf("no AST for %s", file.uri) 247 } 248 // Get the *token.File directly from the AST. 249 pos := gof.ast.file.Pos() 250 if !pos.IsValid() { 251 return fmt.Errorf("AST for %s has an invalid position", file.uri) 252 } 253 tok := imp.view.session.cache.FileSet().File(pos) 254 if tok == nil { 255 return fmt.Errorf("no *token.File for %s", file.uri) 256 } 257 gof.token = tok 258 gof.imports = gof.ast.file.Imports 259 return nil 260} 261 262func (c *cache) appendPkgError(pkg *pkg, err error) { 263 if err == nil { 264 return 265 } 266 var errs []packages.Error 267 switch err := err.(type) { 268 case *scanner.Error: 269 errs = append(errs, packages.Error{ 270 Pos: err.Pos.String(), 271 Msg: err.Msg, 272 Kind: packages.ParseError, 273 }) 274 case scanner.ErrorList: 275 // The first parser error is likely the root cause of the problem. 276 if err.Len() > 0 { 277 errs = append(errs, packages.Error{ 278 Pos: err[0].Pos.String(), 279 Msg: err[0].Msg, 280 Kind: packages.ParseError, 281 }) 282 } 283 case types.Error: 284 errs = append(errs, packages.Error{ 285 Pos: c.FileSet().Position(err.Pos).String(), 286 Msg: err.Msg, 287 Kind: packages.TypeError, 288 }) 289 } 290 pkg.errors = append(pkg.errors, errs...) 291} 292