kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/indexer/indexer.go (about) 1 /* 2 * Copyright 2015 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Package indexer implements a Kythe indexer for the Go language. 18 // 19 // Usage example: Indexing a Kythe CompilationUnit message. 20 // 21 // // Obtain a compilation from some source, e.g., an kzip. 22 // var unit *apb.CompilationUnit = ... 23 // 24 // // Parse the sources and resolve types. 25 // pi, err := indexer.Resolve(unit, pack, &indexer.ResolveOptions{ 26 // Info: indexer.AllTypeInfo(), 27 // }) 28 // if err != nil { 29 // log.Fatal("Resolving failed: %v", err) 30 // } 31 // // Type information from http://godoc.org/go/types is now available 32 // // from pi.Info, which is a *types.Info record. 33 package indexer // import "kythe.io/kythe/go/indexer" 34 35 import ( 36 "bytes" 37 "encoding/base64" 38 "errors" 39 "fmt" 40 "go/ast" 41 "go/build" 42 "go/parser" 43 "go/token" 44 "go/types" 45 "io" 46 "io/ioutil" 47 "path/filepath" 48 "strconv" 49 "strings" 50 51 "kythe.io/kythe/go/extractors/govname" 52 "kythe.io/kythe/go/util/log" 53 "kythe.io/kythe/go/util/metadata" 54 "kythe.io/kythe/go/util/ptypes" 55 "kythe.io/kythe/go/util/schema/edges" 56 57 "bitbucket.org/creachadair/stringset" 58 "github.com/golang/protobuf/proto" 59 "golang.org/x/tools/go/gcexportdata" 60 61 apb "kythe.io/kythe/proto/analysis_go_proto" 62 gopb "kythe.io/kythe/proto/go_go_proto" 63 mpb "kythe.io/kythe/proto/metadata_go_proto" 64 spb "kythe.io/kythe/proto/storage_go_proto" 65 ) 66 67 // ErrNoSourceFiles is returned from Resolve if it is given a package without any sources files. 68 var ErrNoSourceFiles = errors.New("no source files in package") 69 70 // A Fetcher retrieves the contents of a file given its path and/or hex-encoded 71 // SHA256 digest, at least one of which must be set. 72 type Fetcher interface { 73 Fetch(path, digest string) ([]byte, error) 74 } 75 76 // PackageInfo records information about the Go packages defined by a 77 // compilation unit and its dependencies. 78 type PackageInfo struct { 79 Name string // The (short) name of the package 80 ImportPath string // The nominal import path of the package 81 Package *types.Package // The package for this compilation 82 Dependencies map[string]*types.Package // Packages imported from dependencies 83 VName *spb.VName // The base vname for this package 84 PackageVName map[*types.Package]*spb.VName // Resolved package to vname 85 FileSet *token.FileSet // Location info for the source files 86 Files []*ast.File // The parsed ASTs of the source files 87 SourceText map[*ast.File]string // The text of the source files 88 Rules map[*ast.File]metadata.Rules // Mapping metadata for each source file 89 Vendored map[string]string // Mapping from package to its vendor path 90 91 Info *types.Info // If non-nil, contains type-checker results 92 Errors []error // All errors reported by the type checker 93 94 // A lazily-initialized mapping from an object on the RHS of a selection 95 // (lhs.RHS) to the nearest enclosing named struct or interface type; or in 96 // the body of a function or method to the nearest enclosing named method. 97 owner map[types.Object]types.Object 98 99 // A lazily-initialized mapping from from AST nodes to their corresponding 100 // VNames. Only nodes that do not resolve directly to a type object are 101 // included in this map, e.g., function literals. 102 function map[ast.Node]*funcInfo 103 104 // A lazily-initialized set of standard library package import paths for 105 // which a node has been emitted. 106 standardLib stringset.Set 107 108 // A dummy function representing the undeclared package initilization 109 // functions, one per file in the package. 110 packageInit map[*ast.File]*funcInfo 111 112 // A cache of source file vnames. 113 fileVName map[*ast.File]*spb.VName 114 115 // A cache of type vnames. 116 typeVName map[types.Type]*spb.VName 117 118 // Cache storing whether a type's facts have been emitted (keyed by VName signature) 119 typeEmitted stringset.Set 120 121 // A cache of file location mappings. This lets us get back from the 122 // parser's location to the vname for the enclosing file, which is in turn 123 // affected by the build configuration. 124 fileLoc map[*token.File]*ast.File 125 126 // A cache of already-computed signatures. 127 sigs map[types.Object]string 128 129 // The number of package-level init declarations seen. 130 numInits int 131 132 // The Go-specific details from the compilation record. 133 details *gopb.GoDetails 134 } 135 136 type funcInfo struct { 137 vname *spb.VName 138 numAnons int // number of anonymous params/functions defined inside this one 139 } 140 141 // packageImporter implements the types.Importer interface by fetching files 142 // from the required inputs of a compilation unit. 143 type packageImporter struct { 144 deps map[string]*types.Package // packages already loaded 145 fileSet *token.FileSet // source location information 146 fileMap map[string]*apb.FileInfo // :: import path → required input location 147 fetcher Fetcher // access to required input contents 148 149 pkgPath string 150 vendored map[string]string // map of package paths to their vendor paths 151 } 152 153 // Import satisfies the types.Importer interface using the captured data from 154 // the compilation unit. 155 func (pi *packageImporter) Import(importPath string) (*types.Package, error) { 156 if pkg := pi.deps[importPath]; pkg != nil && pkg.Complete() { 157 return pkg, nil 158 } else if importPath == "unsafe" { 159 // The "unsafe" package is special, and isn't usually added by the 160 // resolver into the dependency map. 161 pi.deps[importPath] = types.Unsafe 162 return types.Unsafe, nil 163 } 164 165 // Fetch the required input holding the package for this import path, and 166 // load its export data for use by the type resolver. 167 fi := pi.fileMap[importPath] 168 169 // Check all possible vendor/ paths for missing package file 170 if fi == nil { 171 // Starting at the current package's path, look upwards for a vendor/ root 172 // with the imported package. 173 vendorRoot := pi.pkgPath 174 for { 175 vPath := filepath.Join(vendorRoot, "vendor", importPath) 176 fi = pi.fileMap[vPath] 177 if fi != nil { 178 pi.vendored[importPath] = vPath 179 importPath = vPath 180 break 181 } else if vendorRoot == "" { 182 return nil, fmt.Errorf("package %q not found", importPath) 183 } 184 vendorRoot, _ = filepath.Split(vendorRoot) 185 vendorRoot = strings.TrimRight(vendorRoot, "/") 186 } 187 } 188 189 data, err := pi.fetcher.Fetch(fi.Path, fi.Digest) 190 if err != nil { 191 return nil, fmt.Errorf("fetching %q (%s): %v", fi.Path, fi.Digest, err) 192 } 193 r, err := gcexportdata.NewReader(bytes.NewReader(data)) 194 if err != nil { 195 return nil, fmt.Errorf("reading export data in %q (%s): %v", fi.Path, fi.Digest, err) 196 } 197 return gcexportdata.Read(r, pi.fileSet, pi.deps, importPath) 198 } 199 200 // ResolveOptions control the behaviour of the Resolve function. A nil options 201 // pointer provides default values. 202 type ResolveOptions struct { 203 // Passes a value whose non-nil map fields will be filled in by the type 204 // checker during resolution. The value will also be copied to the Info 205 // field of the PackageInfo returned by Resolve. 206 Info *types.Info 207 208 // If set, this function is called for each required input to check whether 209 // it contains metadata rules. 210 // 211 // Valid return are: 212 // rs, nil -- a valid ruleset 213 // nil, nil -- no ruleset found 214 // _, err -- an error attempting to load a ruleset 215 // 216 CheckRules func(ri *apb.CompilationUnit_FileInput, f Fetcher) (*Ruleset, error) 217 } 218 219 func (r *ResolveOptions) info() *types.Info { 220 if r != nil { 221 return r.Info 222 } 223 return nil 224 } 225 226 func (r *ResolveOptions) checkRules(ri *apb.CompilationUnit_FileInput, f Fetcher) (*Ruleset, error) { 227 if r == nil || r.CheckRules == nil { 228 return nil, nil 229 } 230 return r.CheckRules(ri, f) 231 } 232 233 // A Ruleset represents a collection of mapping rules applicable to a source 234 // file in a compilation to be indexed. 235 type Ruleset struct { 236 Path string // the file path this rule set applies to 237 Rules metadata.Rules // the rules that apply to the path 238 } 239 240 // Resolve resolves the package information for unit and its dependencies. On 241 // success the package corresponding to unit is located via ImportPath in the 242 // Packages map of the returned value. 243 func Resolve(unit *apb.CompilationUnit, f Fetcher, opts *ResolveOptions) (*PackageInfo, error) { 244 sourceFiles := stringset.New(unit.SourceFile...) 245 246 imap := make(map[string]*spb.VName) // import path → vname 247 srcs := make(map[*ast.File]string) // file → text 248 fmap := make(map[string]*apb.FileInfo) // import path → file info 249 smap := make(map[string]*ast.File) // file path → file (sources) 250 filev := make(map[*ast.File]*spb.VName) // file → vname 251 floc := make(map[*token.File]*ast.File) // file → ast 252 fset := token.NewFileSet() // location info for the parser 253 details := goDetails(unit) 254 var files []*ast.File // parsed sources 255 var rules []*Ruleset // parsed linkage rules 256 257 // Classify the required inputs as either sources, which are to be parsed, 258 // or dependencies, which are to be "imported" via the type-checker's 259 // import mechanism. If successful, this populates fset and files with the 260 // lexical and syntactic content of the package's own sources. 261 // 262 // The build context is used to check build tags. 263 bc := &build.Context{ 264 GOOS: details.GetGoos(), 265 GOARCH: details.GetGoarch(), 266 BuildTags: details.GetBuildTags(), 267 ReleaseTags: build.Default.ReleaseTags, 268 ToolTags: build.Default.ToolTags, 269 } 270 for _, ri := range unit.RequiredInput { 271 if ri.Info == nil { 272 return nil, errors.New("required input file info missing") 273 } 274 275 // Source inputs need to be parsed, so we can give their ASTs to the 276 // type checker later on. 277 fpath := ri.Info.Path 278 if sourceFiles.Contains(fpath) { 279 data, err := f.Fetch(fpath, ri.Info.Digest) 280 if err != nil { 281 return nil, fmt.Errorf("fetching %q (%s): %v", fpath, ri.Info.Digest, err) 282 } 283 if !matchesBuildTags(fpath, data, bc) { 284 log.Infof("Skipped source file %q because build tags do not match", fpath) 285 continue 286 } 287 vpath := ri.VName.GetPath() 288 if vpath == "" { 289 vpath = fpath 290 } 291 parsed, err := parser.ParseFile(fset, vpath, data, parser.AllErrors|parser.ParseComments) 292 if err != nil { 293 return nil, fmt.Errorf("parsing %q: %v", fpath, err) 294 } 295 296 // Cache file VNames based on the required input. 297 files = append(files, parsed) 298 vname := proto.Clone(ri.VName).(*spb.VName) 299 if vname == nil { 300 vname = proto.Clone(unit.VName).(*spb.VName) 301 vname.Signature = "" 302 vname.Language = "" 303 } 304 vname.Path = vpath 305 filev[parsed] = vname 306 srcs[parsed] = string(data) 307 smap[fpath] = parsed 308 309 // If the file has inlined encoded metadata, add to rules. 310 var lastComment string 311 if len(parsed.Comments) > 0 { 312 lastComment = parsed.Comments[len(parsed.Comments)-1].Text() 313 } 314 const delimiter = "gokythe-inline-metadata:" 315 316 if strings.HasPrefix(lastComment, delimiter) { 317 encodedMetadata := strings.TrimPrefix(lastComment, delimiter) 318 newRule, err := loadInlineMetadata(ri, encodedMetadata) 319 if err != nil { 320 log.Errorf("loading metadata in %q: %v", ri.Info.GetPath(), err) 321 } else { 322 rules = append(rules, newRule) 323 } 324 } 325 continue 326 } 327 328 // Check for mapping metadata. 329 if rs, err := opts.checkRules(ri, f); err != nil { 330 log.Errorf("checking rules in %q: %v", fpath, err) 331 } else if rs != nil { 332 log.Infof("Found %d metadata rules for path %q", len(rs.Rules), rs.Path) 333 rules = append(rules, rs) 334 continue 335 } 336 337 // Files may be source or compiled archives with type information for 338 // other packages, or may be other ancillary files like C headers to 339 // support cgo. Use the vname to determine which import path for each 340 // and save that mapping for use by the importer. 341 if ri.VName == nil { 342 return nil, fmt.Errorf("missing vname for %q", fpath) 343 } 344 345 var ipath string 346 if info := goPackageInfo(ri.Details); info != nil { 347 ipath = info.ImportPath 348 } else { 349 ipath = govname.ImportPath(ri.VName, details.GetGoroot()) 350 } 351 imap[ipath] = ri.VName 352 fmap[ipath] = ri.Info 353 } 354 if len(files) == 0 { 355 return nil, ErrNoSourceFiles 356 } 357 358 // Populate the location mapping. This relies on the fact that Iterate 359 // reports its files in the order they were added to the set, which in turn 360 // is their order in the files list. 361 i := 0 362 fset.Iterate(func(f *token.File) bool { 363 floc[f] = files[i] 364 i++ 365 return true 366 }) 367 368 pi := &PackageInfo{ 369 Name: files[0].Name.Name, 370 FileSet: fset, 371 Files: files, 372 Info: opts.info(), 373 SourceText: srcs, 374 PackageVName: make(map[*types.Package]*spb.VName), 375 Dependencies: make(map[string]*types.Package), // :: import path → package 376 Vendored: make(map[string]string), 377 378 function: make(map[ast.Node]*funcInfo), 379 sigs: make(map[types.Object]string), 380 packageInit: make(map[*ast.File]*funcInfo), 381 fileVName: filev, 382 typeVName: make(map[types.Type]*spb.VName), 383 typeEmitted: stringset.New(), 384 fileLoc: floc, 385 details: details, 386 } 387 if info := goPackageInfo(unit.Details); info != nil { 388 pi.ImportPath = info.ImportPath 389 } else { 390 pi.ImportPath = govname.ImportPath(unit.VName, details.GetGoroot()) 391 } 392 393 // If mapping rules were found, populate the corresponding field. 394 if len(rules) != 0 { 395 pi.Rules = make(map[*ast.File]metadata.Rules) 396 for _, rs := range rules { 397 f, ok := smap[rs.Path] 398 if ok { 399 pi.Rules[f] = rs.Rules 400 } 401 } 402 } 403 404 // Run the type-checker and collect any errors it generates. Errors in the 405 // type checker are not returned directly; the caller can read them from 406 // the Errors field. 407 c := &types.Config{ 408 FakeImportC: true, // so we can handle cgo 409 DisableUnusedImportCheck: true, // this is not fatal to type-checking 410 Importer: &packageImporter{ 411 deps: pi.Dependencies, 412 fileSet: pi.FileSet, 413 fileMap: fmap, 414 fetcher: f, 415 416 pkgPath: pi.ImportPath, 417 vendored: pi.Vendored, 418 }, 419 Error: func(err error) { pi.Errors = append(pi.Errors, err) }, 420 } 421 pi.Package, _ = c.Check(pi.Name, pi.FileSet, pi.Files, pi.Info) 422 423 // Fill in the mapping from packages to vnames. 424 for ip, vname := range imap { 425 if pkg := pi.Dependencies[ip]; pkg != nil { 426 pi.PackageVName[pkg] = proto.Clone(vname).(*spb.VName) 427 pi.PackageVName[pkg].Signature = "package" 428 pi.PackageVName[pkg].Language = govname.Language 429 } 430 } 431 if _, ok := pi.Dependencies["unsafe"]; ok { 432 pi.PackageVName[types.Unsafe] = govname.ForStandardLibrary("unsafe") 433 } 434 435 // Set this package's own vname. 436 pi.VName = proto.Clone(unit.VName).(*spb.VName) 437 pi.VName.Language = govname.Language 438 pi.VName.Signature = "package" 439 pi.PackageVName[pi.Package] = pi.VName 440 441 return pi, nil 442 } 443 444 // String renders a human-readable synopsis of the package information. 445 func (pi *PackageInfo) String() string { 446 if pi == nil { 447 return "#<package-info nil>" 448 } 449 return fmt.Sprintf("#<package-info %q ip=%q pkg=%p #deps=%d #src=%d #errs=%d>", 450 pi.Name, pi.ImportPath, pi.Package, len(pi.Dependencies), len(pi.Files), len(pi.Errors)) 451 } 452 453 // Signature returns a signature for obj, suitable for use in a vname. 454 func (pi *PackageInfo) Signature(obj types.Object) string { 455 if obj == nil { 456 return "" 457 } else if pi.owner == nil { 458 pi.owner = make(map[types.Object]types.Object) 459 ownerByPos := make(map[token.Position]types.Object) 460 unownedByPos := make(map[token.Position][]types.Object) 461 pi.addOwners(pi.Package, ownerByPos, unownedByPos) 462 for _, pkg := range pi.Dependencies { 463 pi.addOwners(pkg, ownerByPos, unownedByPos) 464 } 465 } 466 if sig, ok := pi.sigs[obj]; ok { 467 return sig 468 } 469 tag, base := pi.newSignature(obj) 470 sig := base 471 if tag != "" { 472 sig = tag + " " + base 473 } 474 pi.sigs[obj] = sig 475 return sig 476 } 477 478 // ObjectVName returns a VName for obj relative to that of its package. 479 func (pi *PackageInfo) ObjectVName(obj types.Object) *spb.VName { 480 if pkg, ok := obj.(*types.PkgName); ok { 481 return pi.PackageVName[pkg.Imported()] 482 } 483 sig := pi.Signature(obj) 484 pkg := obj.Pkg() 485 var vname *spb.VName 486 if base := pi.PackageVName[pkg]; base != nil { 487 vname = proto.Clone(base).(*spb.VName) 488 } else if pkg == nil { 489 return govname.Builtin(obj.Name()) 490 } else { 491 // This is an indirect import, that is, a name imported but not 492 // mentioned explicitly by the package being indexed. 493 // TODO(#2383): This is a workaround, and may not be correct in all 494 // cases; work out a more comprehensive solution (possibly during 495 // extraction). 496 vname = proto.Clone(pi.VName).(*spb.VName) 497 vname.Path = strings.TrimPrefix(pkg.Path(), vname.Corpus+"/") 498 } 499 500 vname.Signature = sig 501 return vname 502 } 503 504 // FileVName returns a VName for path relative to the package base. 505 func (pi *PackageInfo) FileVName(file *ast.File) *spb.VName { 506 if v := pi.fileVName[file]; v != nil { 507 return v 508 } 509 v := proto.Clone(pi.VName).(*spb.VName) 510 v.Language = "" 511 v.Signature = "" 512 v.Path = pi.FileSet.Position(file.Pos()).Filename 513 return v 514 } 515 516 // AnchorVName returns a VName for the given file and offsets. 517 func (pi *PackageInfo) AnchorVName(file *ast.File, start, end int) *spb.VName { 518 vname := proto.Clone(pi.FileVName(file)).(*spb.VName) 519 vname.Signature = "#" + strconv.Itoa(start) + ":" + strconv.Itoa(end) 520 vname.Language = govname.Language 521 return vname 522 } 523 524 // Span returns the containing file and 0-based offset range of the given AST 525 // node. The range is half-open, including the start position but excluding 526 // the end. 527 // 528 // If node == nil or lacks a valid start position, Span returns nil -1, -1. If 529 // the end position of node is invalid, start == end. 530 func (pi *PackageInfo) Span(node ast.Node) (file *ast.File, start, end int) { 531 if node == nil { 532 return nil, -1, -1 533 } 534 pos := node.Pos() 535 if pos == token.NoPos { 536 return nil, -1, -1 537 } 538 sp := pi.FileSet.Position(pos) 539 file = pi.fileLoc[pi.FileSet.File(pos)] 540 start = sp.Offset 541 end = start 542 if pos := node.End(); pos != token.NoPos { 543 end = pi.FileSet.Position(pos).Offset 544 } 545 return 546 } 547 548 const ( 549 isBuiltin = "builtin-" 550 tagConst = "const" 551 tagField = "field" 552 tagFunc = "func" 553 tagLabel = "label" 554 tagMethod = "method" 555 tagParam = "param" 556 tagTVar = "tvar" 557 tagType = "type" 558 tagVar = "var" 559 ) 560 561 // newSignature constructs and returns a tag and base signature for obj. The 562 // tag represents the "kind" of signature, to disambiguate built-in types from 563 // user-defined names, fields from methods, and so on. The base is a unique 564 // name for obj within its package, modulo the tag. 565 func (pi *PackageInfo) newSignature(obj types.Object) (tag, base string) { 566 if obj.Name() == "" { 567 return tagVar, "_" 568 } 569 topLevelTag := tagVar 570 switch t := obj.(type) { 571 case *types.Builtin: 572 return isBuiltin + tagFunc, t.Name() 573 574 case *types.Nil: 575 return isBuiltin + tagConst, "nil" 576 577 case *types.PkgName: 578 return "", "package" // the vname corpus and path carry the package name 579 580 case *types.Const: 581 topLevelTag = tagConst 582 if t.Pkg() == nil { 583 return isBuiltin + tagConst, t.Name() 584 } 585 586 case *types.Var: 587 if t.IsField() { 588 if owner, ok := pi.owner[t]; ok { 589 _, base := pi.newSignature(owner) 590 return tagField, base + "." + t.Name() 591 } 592 return tagField, pi.anonSignature(t) 593 } else if owner, ok := pi.owner[t]; ok { 594 _, base := pi.newSignature(owner) 595 return tagParam, base + ":" + t.Name() 596 } 597 598 case *types.Func: 599 topLevelTag = tagFunc 600 if recv := t.Type().(*types.Signature).Recv(); recv != nil { // method 601 if owner, ok := pi.owner[t]; ok { 602 _, base := pi.newSignature(owner) 603 return tagMethod, base + "." + t.Name() 604 } 605 // If the receiver is defined in this package, fully qualify the 606 // name so references from elsewhere will work. Strictly speaking 607 // this is only necessary for exported methods, but it's simpler to 608 // do it for everything. 609 return tagMethod, fmt.Sprintf("(%s).%s", types.TypeString(recv.Type(), func(pkg *types.Package) string { 610 return pkg.Name() 611 }), t.Name()) 612 } 613 614 case *types.TypeName: 615 if param, ok := t.Type().(*types.TypeParam); ok { 616 return tagTVar, fmt.Sprintf("[%p]%s", t, param.String()) 617 } 618 topLevelTag = tagType 619 if t.Pkg() == nil { 620 return isBuiltin + tagType, t.Name() 621 } 622 623 case *types.Label: 624 return tagLabel, pi.anonSignature(t) 625 626 default: 627 panic(fmt.Sprintf("Unexpected object kind: %T", obj)) 628 } 629 630 // At this point, we have eliminated built-in objects; everything else must 631 // be defined in a package. 632 if obj.Pkg() == nil { 633 panic(fmt.Sprintf("Object without a package: %v", obj)) 634 } 635 636 // Objects at package scope (i.e., parent scope is package scope). 637 if obj.Parent() == obj.Pkg().Scope() { 638 return topLevelTag, obj.Name() 639 } 640 641 // Objects in interior (local) scopes, i.e., everything else. 642 return topLevelTag, pi.anonSignature(obj) 643 } 644 645 func (pi *PackageInfo) anonSignature(obj types.Object) string { 646 // Use the object's line number and file basename to differentiate the 647 // node while allowing for cross-package references (other parts of the 648 // Position may differ). This may collide if a source file isn't gofmt'd 649 // and defines multiple anonymous fields with the same name on the same 650 // line, but that's unlikely to happen in practice. 651 pos := pi.FileSet.Position(obj.Pos()) 652 return fmt.Sprintf("[%s#%d].%s", filepath.Base(pos.Filename), pos.Line, obj.Name()) 653 } 654 655 // addOwners updates pi.owner from the types in pkg, adding mapping from fields 656 // of package-level named struct types to the owning named struct type; from 657 // methods of package-level named interface types to the owning named interface 658 // type; and from parameters of package-level named function or method types to 659 // the owning named function or method. 660 // 661 // This relation is used to construct signatures for these fields/methods, 662 // since they may be referenced from another package and thus need 663 // deterministic names. An object does not expose its "owner"; indeed it may 664 // have several. 665 // 666 // Caveats: 667 // 668 // (1) This mapping is deterministic but not necessarily the best one according 669 // to the original syntax, to which, in general, we do not have access. In 670 // these two examples, the type checker considers field X as belonging equally 671 // to types T and U, even though according the syntax, it belongs primarily to 672 // T in the first example and U in the second: 673 // 674 // type T struct {X int} 675 // type U T 676 // 677 // type T U 678 // type U struct {X int} 679 // 680 // Similarly: 681 // 682 // type U struct {X int} 683 // type V struct {U} 684 // 685 // TODO(adonovan): sameer@ points out a useful heuristic: in a case of struct 686 // or interface embedding, if one struct/interface has fewer fields/methods, 687 // then it must be the primary one. 688 // 689 // (2) This pass is not exhaustive: there remain objects that may be referenced 690 // from outside the package but for which we can't easily come up with good 691 // names. Here are some examples: 692 // 693 // // package p 694 // var V1, V2 struct {X int} = ... 695 // func F() struct{X int} {...} 696 // type T struct { 697 // Y struct { X int } 698 // } 699 // 700 // // main 701 // p.V2.X = 1 702 // print(p.F().X) 703 // new(p.T).Y[0].X 704 // 705 // Also note that there may be arbitrary pointer, struct, chan, map, array, and 706 // slice type constructors between the type of the exported package member (V2, 707 // F or T) and the type of its X subelement. For now, we simply ignore such 708 // names. They should be rare in readable code. 709 func (pi *PackageInfo) addOwners(pkg *types.Package, ownerByPos map[token.Position]types.Object, unownedByPos map[token.Position][]types.Object) { 710 scope := pkg.Scope() 711 addTypeParams := func(obj types.Object, params *types.TypeParamList) { 712 mapTypeParams(params, func(i int, param *types.TypeParam) { 713 typeName := param.Obj() 714 if _, ok := pi.owner[typeName]; !ok { 715 pi.owner[typeName] = obj 716 } 717 }) 718 } 719 addFunc := func(obj *types.Func) { 720 // Inspect the receiver, parameters, and result values. 721 fsig := obj.Type().(*types.Signature) 722 if recv := fsig.Recv(); recv != nil { 723 pi.owner[recv] = obj 724 } 725 if params := fsig.Params(); params != nil { 726 for i := 0; i < params.Len(); i++ { 727 pi.owner[params.At(i)] = obj 728 } 729 } 730 if res := fsig.Results(); res != nil { 731 for i := 0; i < res.Len(); i++ { 732 pi.owner[res.At(i)] = obj 733 } 734 } 735 addTypeParams(obj, fsig.TypeParams()) 736 addTypeParams(obj, fsig.RecvTypeParams()) 737 } 738 addMethods := func(obj types.Object, n int, method func(i int) *types.Func) { 739 for i := 0; i < n; i++ { 740 m := method(i) 741 if _, ok := pi.owner[m]; !ok { 742 pos := pi.FileSet.Position(m.Pos()) 743 if m.Pkg() == pkg { 744 pi.owner[m] = obj 745 addFunc(m) 746 747 // Keep track of owners by position to facilitate ownership across 748 // package boundaries for new type names of existing interfaces: 749 // 750 // package pkg 751 // type A fmt.Stringer 752 // 753 // ----- 754 // 755 // package other 756 // import "pkg" 757 // \/ 758 // func f(a pkg.A) { a.String() } 759 // 760 // Understanding that the above reference is to 761 // (fmt.Stringer).String() is lost due to compiler unwrapping the 762 // `type A fmt.Stringer` definition for downstream compilations. 763 // However, position info remains behind for stack traces so we can 764 // use it as fallback heuristic to determine "sameness" of methods. 765 ownerByPos[pos] = obj 766 for _, m := range unownedByPos[pos] { 767 pi.owner[m] = obj 768 } 769 delete(unownedByPos, pos) 770 } else if owner, ok := ownerByPos[pos]; ok { 771 pi.owner[m] = owner 772 addFunc(m) 773 } else { 774 unownedByPos[pos] = append(unownedByPos[pos], m) 775 } 776 } 777 } 778 } 779 addNamed := func(obj types.Object, named *types.Named) { 780 addTypeParams(obj, named.TypeParams()) 781 switch t := named.Underlying().(type) { 782 case *types.Struct: 783 // Inspect the fields of a struct. 784 for i := 0; i < t.NumFields(); i++ { 785 f := t.Field(i) 786 if f.Pkg() != pkg && named.TypeArgs().Len() == 0 { 787 continue // wrong package (and not an instantiated type) 788 } 789 if _, ok := pi.owner[f]; !ok { 790 pi.owner[f] = obj 791 } 792 } 793 addMethods(obj, named.NumMethods(), named.Method) 794 795 case *types.Interface: 796 // Inspect the declared methods of an interface. 797 addMethods(obj, t.NumExplicitMethods(), t.ExplicitMethod) 798 799 default: 800 // Inspect declared methods of other named types. 801 addMethods(obj, named.NumMethods(), named.Method) 802 } 803 } 804 805 for _, name := range scope.Names() { 806 switch obj := scope.Lookup(name).(type) { 807 case *types.TypeName: 808 // TODO(schroederc): support for type aliases. For now, skip these so we 809 // don't wind up emitting redundant declaration sites for the aliased 810 // type. 811 named, ok := obj.Type().(*types.Named) 812 if !ok { 813 continue 814 } 815 addNamed(obj, named) 816 817 case *types.Func: 818 addFunc(obj) 819 } 820 } 821 822 if pkg == pi.Package { 823 // Add owners for members of known known instantiations 824 for _, t := range pi.Info.Types { 825 if n, ok := t.Type.(*types.Named); ok && n.TypeArgs().Len() > 0 { 826 addNamed(n.Obj(), n) 827 } 828 } 829 } 830 } 831 832 // mapTypeParams applies f to each type parameter declared in params. Each call 833 // to f is given the offset and the type parameter. 834 func mapTypeParams(params *types.TypeParamList, f func(i int, id *types.TypeParam)) { 835 if params == nil { 836 return 837 } 838 for i := 0; i < params.Len(); i++ { 839 f(i, params.At(i)) 840 } 841 } 842 843 // findFieldName tries to resolve the identifier that names an embedded 844 // anonymous field declaration at expr, and reports whether successful. 845 func (pi *PackageInfo) findFieldName(expr ast.Expr) (id *ast.Ident, ok bool) { 846 // There are three cases we care about here: 847 // 848 // A bare identifier (foo), which refers to a type defined in 849 // this package, or a builtin type, 850 // 851 // A selector expression (pkg.Foo) referring to an exported 852 // type defined in another package, or 853 // 854 // A pointer to either of these. 855 856 switch t := expr.(type) { 857 case *ast.StarExpr: // *T 858 return pi.findFieldName(t.X) 859 case *ast.Ident: // T declared locally 860 return t, true 861 case *ast.SelectorExpr: // pkg.T declared elsewhere 862 return t.Sel, true 863 default: 864 // No idea what this is; possibly malformed code. 865 return nil, false 866 } 867 } 868 869 // importPath returns the import path of pkg. 870 func (pi *PackageInfo) importPath(pkg *types.Package) string { 871 if v := pi.PackageVName[pkg]; v != nil { 872 return govname.ImportPath(v, pi.details.GetGoroot()) 873 } 874 return pkg.Name() 875 } 876 877 // isPackageInit reports whether fi belongs to a package-level init function. 878 func (pi *PackageInfo) isPackageInit(fi *funcInfo) bool { 879 for _, v := range pi.packageInit { 880 if fi == v { 881 return true 882 } 883 } 884 return false 885 } 886 887 // goDetails returns the GoDetails message attached to unit, if there is one; 888 // otherwise it returns nil. 889 func goDetails(unit *apb.CompilationUnit) *gopb.GoDetails { 890 for _, msg := range unit.Details { 891 var dets gopb.GoDetails 892 if err := ptypes.UnmarshalAny(msg, &dets); err == nil { 893 return &dets 894 } 895 } 896 return nil 897 } 898 899 // goPackageInfo returns the GoPackageInfo message within the given slice, if 900 // there is one; otherwise it returns nil. 901 func goPackageInfo(details []*ptypes.Any) *gopb.GoPackageInfo { 902 for _, msg := range details { 903 var info gopb.GoPackageInfo 904 if err := ptypes.UnmarshalAny(msg, &info); err == nil { 905 return &info 906 } 907 } 908 return nil 909 } 910 911 // loadInlineMetadata unmarshals the encoded metadata string and returns 912 // the corresponding metadata rules which link source definitions 913 // to the file fi. An error is returned if unmarshalling fails. 914 func loadInlineMetadata(fi *apb.CompilationUnit_FileInput, encodedMetadata string) (*Ruleset, error) { 915 var gci mpb.GeneratedCodeInfo 916 err := unmarshalProtoBase64(encodedMetadata, &gci) 917 if err != nil { 918 return nil, err 919 } 920 921 rules := make(metadata.Rules, 0, len(gci.GetMeta())) 922 for _, r := range gci.GetMeta() { 923 k, rev := strings.CutPrefix(r.Edge, "%") 924 rules = append(rules, metadata.Rule{ 925 EdgeIn: edges.DefinesBinding, 926 EdgeOut: k, 927 VName: r.GetVname(), 928 Reverse: rev, 929 Begin: int(r.GetBegin()), 930 End: int(r.GetEnd()), 931 }) 932 } 933 return &Ruleset{ 934 Path: fi.Info.GetPath(), 935 Rules: rules, 936 }, nil 937 } 938 939 // matchesBuildTags reports whether the file at fpath, whose content is in 940 // data, would be matched by the settings in bc. 941 func matchesBuildTags(fpath string, data []byte, bc *build.Context) bool { 942 dir, name := filepath.Split(fpath) 943 bc.OpenFile = func(path string) (io.ReadCloser, error) { 944 if path != fpath { 945 return nil, errors.New("file not found") 946 } 947 return ioutil.NopCloser(bytes.NewReader(data)), nil 948 } 949 match, err := bc.MatchFile(dir, name) 950 return err == nil && match 951 } 952 953 // unmarshalProtoBase64 parses a base64 encoded proto string into the supplied 954 // message, mutating msg. 955 func unmarshalProtoBase64(base64Str string, msg proto.Message) error { 956 b, _ := base64.StdEncoding.DecodeString(base64Str) 957 return proto.Unmarshal(b, msg) 958 } 959 960 // AllTypeInfo creates a new types.Info value with empty maps for each of the 961 // fields that can be filled in by the type-checker. 962 func AllTypeInfo() *types.Info { 963 return &types.Info{ 964 Types: make(map[ast.Expr]types.TypeAndValue), 965 Defs: make(map[*ast.Ident]types.Object), 966 Uses: make(map[*ast.Ident]types.Object), 967 Implicits: make(map[ast.Node]types.Object), 968 Selections: make(map[*ast.SelectorExpr]*types.Selection), 969 Scopes: make(map[ast.Node]*types.Scope), 970 } 971 } 972 973 // XRefTypeInfo creates a new types.Info value with empty maps for each of the 974 // fields needed for cross-reference indexing. 975 func XRefTypeInfo() *types.Info { 976 return &types.Info{ 977 Types: make(map[ast.Expr]types.TypeAndValue), 978 Defs: make(map[*ast.Ident]types.Object), 979 Uses: make(map[*ast.Ident]types.Object), 980 Implicits: make(map[ast.Node]types.Object), 981 } 982 }