| /* |
| * Copyright 2015 The Kythe Authors. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Package indexer implements a Kythe indexer for the Go language. |
| // |
| // Usage example: Indexing a Kythe CompilationUnit message. |
| // |
| // // Obtain a compilation from some source, e.g., an index pack. |
| // var pack *indexpack.Archive = ... |
| // var unit *apb.CompilationUnit = ... |
| // |
| // // Parse the sources and resolve types. |
| // pi, err := indexer.Resolve(unit, pack, &indexer.ResolveOptions{ |
| // Info: indexer.AllTypeInfo(), |
| // }) |
| // if err != nil { |
| // log.Fatal("Resolving failed: %v", err) |
| // } |
| // // Type information from http://godoc.org/go/types is now available |
| // // from pi.Info, which is a *types.Info record. |
| // |
| package indexer |
| |
| import ( |
| "bytes" |
| "errors" |
| "fmt" |
| "go/ast" |
| "go/build" |
| "go/parser" |
| "go/token" |
| "go/types" |
| "io" |
| "io/ioutil" |
| "log" |
| "path/filepath" |
| "strconv" |
| "strings" |
| |
| "kythe.io/kythe/go/extractors/govname" |
| "kythe.io/kythe/go/util/metadata" |
| "kythe.io/kythe/go/util/ptypes" |
| |
| "bitbucket.org/creachadair/stringset" |
| "github.com/golang/protobuf/proto" |
| "golang.org/x/tools/go/gcexportdata" |
| |
| apb "kythe.io/kythe/proto/analysis_go_proto" |
| gopb "kythe.io/kythe/proto/go_go_proto" |
| spb "kythe.io/kythe/proto/storage_go_proto" |
| ) |
| |
| // A Fetcher retrieves the contents of a file given its path and/or hex-encoded |
| // SHA256 digest, at least one of which must be set. |
| type Fetcher interface { |
| Fetch(path, digest string) ([]byte, error) |
| } |
| |
| // PackageInfo records information about the Go packages defined by a |
| // compilation unit and its dependencies. |
| type PackageInfo struct { |
| Name string // The (short) name of the package |
| ImportPath string // The nominal import path of the package |
| Package *types.Package // The package for this compilation |
| Dependencies map[string]*types.Package // Packages imported from dependencies |
| VName *spb.VName // The base vname for this package |
| PackageVName map[*types.Package]*spb.VName // Resolved package to vname |
| FileSet *token.FileSet // Location info for the source files |
| Files []*ast.File // The parsed ASTs of the source files |
| SourceText map[*ast.File]string // The text of the source files |
| Rules map[*ast.File]metadata.Rules // Mapping metadata for each source file |
| Vendored map[string]string // Mapping from package to its vendor path |
| |
| Info *types.Info // If non-nil, contains type-checker results |
| Errors []error // All errors reported by the type checker |
| |
| // A lazily-initialized mapping from an object on the RHS of a selection |
| // (lhs.RHS) to the nearest enclosing named struct or interface type; or in |
| // the body of a function or method to the nearest enclosing named method. |
| owner map[types.Object]types.Object |
| |
| // A lazily-initialized mapping from from AST nodes to their corresponding |
| // VNames. Only nodes that do not resolve directly to a type object are |
| // included in this map, e.g., function literals. |
| function map[ast.Node]*funcInfo |
| |
| // A lazily-initialized set of standard library package import paths for |
| // which a node has been emitted. |
| standardLib stringset.Set |
| |
| // A dummy function representing the undeclared package initilization |
| // functions, one per file in the package. |
| packageInit map[*ast.File]*funcInfo |
| |
| // A cache of source file vnames. |
| fileVName map[*ast.File]*spb.VName |
| |
| // A cache of type vnames. |
| typeVName map[types.Type]*spb.VName |
| |
| // Cache storing whether a type's facts have been emitted (keyed by VName signature) |
| typeEmitted stringset.Set |
| |
| // A cache of file location mappings. This lets us get back from the |
| // parser's location to the vname for the enclosing file, which is in turn |
| // affected by the build configuration. |
| fileLoc map[*token.File]*ast.File |
| |
| // A cache of already-computed signatures. |
| sigs map[types.Object]string |
| |
| // The number of package-level init declarations seen. |
| numInits int |
| |
| // The Go-specific details from the compilation record. |
| details *gopb.GoDetails |
| } |
| |
| type funcInfo struct { |
| vname *spb.VName |
| numAnons int // number of anonymous functions defined inside this one |
| } |
| |
| // packageImporter implements the types.Importer interface by fetching files |
| // from the required inputs of a compilation unit. |
| type packageImporter struct { |
| deps map[string]*types.Package // packages already loaded |
| fileSet *token.FileSet // source location information |
| fileMap map[string]*apb.FileInfo // :: import path → required input location |
| fetcher Fetcher // access to required input contents |
| |
| pkgPath string |
| vendored map[string]string // map of package paths to their vendor paths |
| } |
| |
| // Import satisfies the types.Importer interface using the captured data from |
| // the compilation unit. |
| func (pi *packageImporter) Import(importPath string) (*types.Package, error) { |
| if pkg := pi.deps[importPath]; pkg != nil && pkg.Complete() { |
| return pkg, nil |
| } else if importPath == "unsafe" { |
| // The "unsafe" package is special, and isn't usually added by the |
| // resolver into the dependency map. |
| pi.deps[importPath] = types.Unsafe |
| return types.Unsafe, nil |
| } |
| |
| // Fetch the required input holding the package for this import path, and |
| // load its export data for use by the type resolver. |
| fi := pi.fileMap[importPath] |
| |
| // Check all possible vendor/ paths for missing package file |
| if fi == nil { |
| // Starting at the current package's path, look upwards for a vendor/ root |
| // with the imported package. |
| vendorRoot := pi.pkgPath |
| for { |
| vPath := filepath.Join(vendorRoot, "vendor", importPath) |
| fi = pi.fileMap[vPath] |
| if fi != nil { |
| pi.vendored[importPath] = vPath |
| importPath = vPath |
| break |
| } else if vendorRoot == "" { |
| return nil, fmt.Errorf("package %q not found", importPath) |
| } |
| vendorRoot, _ = filepath.Split(vendorRoot) |
| vendorRoot = strings.TrimRight(vendorRoot, "/") |
| } |
| } |
| |
| data, err := pi.fetcher.Fetch(fi.Path, fi.Digest) |
| if err != nil { |
| return nil, fmt.Errorf("fetching %q (%s): %v", fi.Path, fi.Digest, err) |
| } |
| r, err := gcexportdata.NewReader(bytes.NewReader(data)) |
| if err != nil { |
| return nil, fmt.Errorf("reading export data in %q (%s): %v", fi.Path, fi.Digest, err) |
| } |
| return gcexportdata.Read(r, pi.fileSet, pi.deps, importPath) |
| } |
| |
| // ResolveOptions control the behaviour of the Resolve function. A nil options |
| // pointer provides default values. |
| type ResolveOptions struct { |
| // Passes a value whose non-nil map fields will be filled in by the type |
| // checker during resolution. The value will also be copied to the Info |
| // field of the PackageInfo returned by Resolve. |
| Info *types.Info |
| |
| // If set, this function is called for each required input to check whether |
| // it contains metadata rules. |
| // |
| // Valid return are: |
| // rs, nil -- a valid ruleset |
| // nil, nil -- no ruleset found |
| // _, err -- an error attempting to load a ruleset |
| // |
| CheckRules func(ri *apb.CompilationUnit_FileInput, f Fetcher) (*Ruleset, error) |
| } |
| |
| func (r *ResolveOptions) info() *types.Info { |
| if r != nil { |
| return r.Info |
| } |
| return nil |
| } |
| |
| func (r *ResolveOptions) checkRules(ri *apb.CompilationUnit_FileInput, f Fetcher) (*Ruleset, error) { |
| if r == nil || r.CheckRules == nil { |
| return nil, nil |
| } |
| return r.CheckRules(ri, f) |
| } |
| |
| // A Ruleset represents a collection of mapping rules applicable to a source |
| // file in a compilation to be indexed. |
| type Ruleset struct { |
| Path string // the file path this rule set applies to |
| Rules metadata.Rules // the rules that apply to the path |
| } |
| |
| // Resolve resolves the package information for unit and its dependencies. On |
| // success the package corresponding to unit is located via ImportPath in the |
| // Packages map of the returned value. |
| func Resolve(unit *apb.CompilationUnit, f Fetcher, opts *ResolveOptions) (*PackageInfo, error) { |
| sourceFiles := stringset.New(unit.SourceFile...) |
| |
| imap := make(map[string]*spb.VName) // import path → vname |
| srcs := make(map[*ast.File]string) // file → text |
| fmap := make(map[string]*apb.FileInfo) // import path → file info |
| smap := make(map[string]*ast.File) // file path → file (sources) |
| filev := make(map[*ast.File]*spb.VName) // file → vname |
| floc := make(map[*token.File]*ast.File) // file → ast |
| fset := token.NewFileSet() // location info for the parser |
| details := goDetails(unit) |
| var files []*ast.File // parsed sources |
| var rules []*Ruleset // parsed linkage rules |
| |
| // Classify the required inputs as either sources, which are to be parsed, |
| // or dependencies, which are to be "imported" via the type-checker's |
| // import mechanism. If successful, this populates fset and files with the |
| // lexical and syntactic content of the package's own sources. |
| // |
| // The build context is used to check build tags. |
| bc := &build.Context{ |
| GOOS: details.GetGoos(), |
| GOARCH: details.GetGoarch(), |
| BuildTags: details.GetBuildTags(), |
| } |
| for _, ri := range unit.RequiredInput { |
| if ri.Info == nil { |
| return nil, errors.New("required input file info missing") |
| } |
| |
| // Source inputs need to be parsed, so we can give their ASTs to the |
| // type checker later on. |
| fpath := ri.Info.Path |
| if sourceFiles.Contains(fpath) { |
| data, err := f.Fetch(fpath, ri.Info.Digest) |
| if err != nil { |
| return nil, fmt.Errorf("fetching %q (%s): %v", fpath, ri.Info.Digest, err) |
| } |
| if !matchesBuildTags(fpath, data, bc) { |
| log.Printf("Skipped source file %q because build tags do not match", fpath) |
| continue |
| } |
| vpath := ri.VName.GetPath() |
| if vpath == "" { |
| vpath = fpath |
| } |
| parsed, err := parser.ParseFile(fset, vpath, data, parser.AllErrors|parser.ParseComments) |
| if err != nil { |
| return nil, fmt.Errorf("parsing %q: %v", fpath, err) |
| } |
| |
| // Cache file VNames based on the required input. |
| files = append(files, parsed) |
| vname := proto.Clone(ri.VName).(*spb.VName) |
| if vname == nil { |
| vname = proto.Clone(unit.VName).(*spb.VName) |
| vname.Signature = "" |
| vname.Language = "" |
| } |
| vname.Path = vpath |
| filev[parsed] = vname |
| srcs[parsed] = string(data) |
| smap[fpath] = parsed |
| continue |
| } |
| |
| // Check for mapping metadata. |
| if rs, err := opts.checkRules(ri, f); err != nil { |
| log.Printf("Error checking rules in %q: %v", fpath, err) |
| } else if rs != nil { |
| log.Printf("Found %d metadata rules for path %q", len(rs.Rules), rs.Path) |
| rules = append(rules, rs) |
| continue |
| } |
| |
| // Files may be source or compiled archives with type information for |
| // other packages, or may be other ancillary files like C headers to |
| // support cgo. Use the vname to determine which import path for each |
| // and save that mapping for use by the importer. |
| if ri.VName == nil { |
| return nil, fmt.Errorf("missing vname for %q", fpath) |
| } |
| |
| var ipath string |
| if info := goPackageInfo(ri.Details); info != nil { |
| ipath = info.ImportPath |
| } else { |
| ipath = govname.ImportPath(ri.VName, details.GetGoroot()) |
| } |
| imap[ipath] = ri.VName |
| fmap[ipath] = ri.Info |
| } |
| if len(files) == 0 { |
| return nil, errors.New("no source files in package") |
| } |
| |
| // Populate the location mapping. This relies on the fact that Iterate |
| // reports its files in the order they were added to the set, which in turn |
| // is their order in the files list. |
| i := 0 |
| fset.Iterate(func(f *token.File) bool { |
| floc[f] = files[i] |
| i++ |
| return true |
| }) |
| |
| pi := &PackageInfo{ |
| Name: files[0].Name.Name, |
| FileSet: fset, |
| Files: files, |
| Info: opts.info(), |
| SourceText: srcs, |
| PackageVName: make(map[*types.Package]*spb.VName), |
| Dependencies: make(map[string]*types.Package), // :: import path → package |
| Vendored: make(map[string]string), |
| |
| function: make(map[ast.Node]*funcInfo), |
| sigs: make(map[types.Object]string), |
| packageInit: make(map[*ast.File]*funcInfo), |
| fileVName: filev, |
| typeVName: make(map[types.Type]*spb.VName), |
| typeEmitted: stringset.New(), |
| fileLoc: floc, |
| details: details, |
| } |
| if info := goPackageInfo(unit.Details); info != nil { |
| pi.ImportPath = info.ImportPath |
| } else { |
| pi.ImportPath = govname.ImportPath(unit.VName, details.GetGoroot()) |
| } |
| |
| // If mapping rules were found, populate the corresponding field. |
| if len(rules) != 0 { |
| pi.Rules = make(map[*ast.File]metadata.Rules) |
| for _, rs := range rules { |
| f, ok := smap[rs.Path] |
| if ok { |
| pi.Rules[f] = rs.Rules |
| } |
| } |
| } |
| |
| // Run the type-checker and collect any errors it generates. Errors in the |
| // type checker are not returned directly; the caller can read them from |
| // the Errors field. |
| c := &types.Config{ |
| FakeImportC: true, // so we can handle cgo |
| DisableUnusedImportCheck: true, // this is not fatal to type-checking |
| Importer: &packageImporter{ |
| deps: pi.Dependencies, |
| fileSet: pi.FileSet, |
| fileMap: fmap, |
| fetcher: f, |
| |
| pkgPath: pi.ImportPath, |
| vendored: pi.Vendored, |
| }, |
| Error: func(err error) { pi.Errors = append(pi.Errors, err) }, |
| } |
| pi.Package, _ = c.Check(pi.Name, pi.FileSet, pi.Files, pi.Info) |
| pi.PackageVName[pi.Package] = unit.VName |
| |
| // Fill in the mapping from packages to vnames. |
| for ip, vname := range imap { |
| if pkg := pi.Dependencies[ip]; pkg != nil { |
| pi.PackageVName[pkg] = proto.Clone(vname).(*spb.VName) |
| pi.PackageVName[pkg].Signature = "package" |
| pi.PackageVName[pkg].Language = govname.Language |
| } |
| } |
| if _, ok := pi.Dependencies["unsafe"]; ok { |
| pi.PackageVName[types.Unsafe] = govname.ForStandardLibrary("unsafe") |
| } |
| |
| // Set this package's own vname. |
| pi.VName = proto.Clone(unit.VName).(*spb.VName) |
| pi.VName.Language = govname.Language |
| pi.VName.Signature = "package" |
| |
| return pi, nil |
| } |
| |
| // String renders a human-readable synopsis of the package information. |
| func (pi *PackageInfo) String() string { |
| if pi == nil { |
| return "#<package-info nil>" |
| } |
| return fmt.Sprintf("#<package-info %q ip=%q pkg=%p #deps=%d #src=%d #errs=%d>", |
| pi.Name, pi.ImportPath, pi.Package, len(pi.Dependencies), len(pi.Files), len(pi.Errors)) |
| } |
| |
| // Signature returns a signature for obj, suitable for use in a vname. |
| func (pi *PackageInfo) Signature(obj types.Object) string { |
| if obj == nil { |
| return "" |
| } else if pi.owner == nil { |
| pi.owner = make(map[types.Object]types.Object) |
| pi.addOwners(pi.Package) |
| for _, pkg := range pi.Dependencies { |
| pi.addOwners(pkg) |
| } |
| } |
| if sig, ok := pi.sigs[obj]; ok { |
| return sig |
| } |
| tag, base := pi.newSignature(obj) |
| sig := base |
| if tag != "" { |
| sig = tag + " " + base |
| } |
| pi.sigs[obj] = sig |
| return sig |
| } |
| |
| // ObjectVName returns a VName for obj relative to that of its package. |
| func (pi *PackageInfo) ObjectVName(obj types.Object) *spb.VName { |
| if pkg, ok := obj.(*types.PkgName); ok { |
| return pi.PackageVName[pkg.Imported()] |
| } |
| sig := pi.Signature(obj) |
| pkg := obj.Pkg() |
| var vname *spb.VName |
| if base := pi.PackageVName[pkg]; base != nil { |
| vname = proto.Clone(base).(*spb.VName) |
| } else if pkg == nil { |
| return govname.ForBuiltin(sig) |
| } else { |
| // This is an indirect import, that is, a name imported but not |
| // mentioned explicitly by the package being indexed. |
| // TODO(#2383): This is a workaround, and may not be correct in all |
| // cases; work out a more comprehensive solution (possibly during |
| // extraction). |
| vname = proto.Clone(pi.VName).(*spb.VName) |
| vname.Path = strings.TrimPrefix(pkg.Path(), vname.Corpus+"/") |
| } |
| |
| vname.Signature = sig |
| return vname |
| } |
| |
| // FileVName returns a VName for path relative to the package base. |
| func (pi *PackageInfo) FileVName(file *ast.File) *spb.VName { |
| if v := pi.fileVName[file]; v != nil { |
| return v |
| } |
| v := proto.Clone(pi.VName).(*spb.VName) |
| v.Language = "" |
| v.Signature = "" |
| v.Path = pi.FileSet.Position(file.Pos()).Filename |
| return v |
| } |
| |
| // AnchorVName returns a VName for the given file and offsets. |
| func (pi *PackageInfo) AnchorVName(file *ast.File, start, end int) *spb.VName { |
| vname := proto.Clone(pi.FileVName(file)).(*spb.VName) |
| vname.Signature = "#" + strconv.Itoa(start) + ":" + strconv.Itoa(end) |
| vname.Language = govname.Language |
| return vname |
| } |
| |
| // Span returns the containing file and 0-based offset range of the given AST |
| // node. The range is half-open, including the start position but excluding |
| // the end. |
| // |
| // If node == nil or lacks a valid start position, Span returns nil -1, -1. If |
| // the end position of node is invalid, start == end. |
| func (pi *PackageInfo) Span(node ast.Node) (file *ast.File, start, end int) { |
| if node == nil { |
| return nil, -1, -1 |
| } |
| pos := node.Pos() |
| if pos == token.NoPos { |
| return nil, -1, -1 |
| } |
| sp := pi.FileSet.Position(pos) |
| file = pi.fileLoc[pi.FileSet.File(pos)] |
| start = sp.Offset |
| end = start |
| if pos := node.End(); pos != token.NoPos { |
| end = pi.FileSet.Position(pos).Offset |
| } |
| return |
| } |
| |
| const ( |
| isBuiltin = "builtin-" |
| tagConst = "const" |
| tagField = "field" |
| tagFunc = "func" |
| tagLabel = "label" |
| tagMethod = "method" |
| tagParam = "param" |
| tagType = "type" |
| tagVar = "var" |
| ) |
| |
| // newSignature constructs and returns a tag and base signature for obj. The |
| // tag represents the "kind" of signature, to disambiguate built-in types from |
| // user-defined names, fields from methods, and so on. The base is a unique |
| // name for obj within its package, modulo the tag. |
| func (pi *PackageInfo) newSignature(obj types.Object) (tag, base string) { |
| if obj.Name() == "" { |
| return tagVar, "_" |
| } |
| topLevelTag := tagVar |
| switch t := obj.(type) { |
| case *types.Builtin: |
| return isBuiltin + tagFunc, t.Name() |
| |
| case *types.Nil: |
| return isBuiltin + tagConst, "nil" |
| |
| case *types.PkgName: |
| return "", "package" // the vname corpus and path carry the package name |
| |
| case *types.Const: |
| topLevelTag = tagConst |
| if t.Pkg() == nil { |
| return isBuiltin + tagConst, t.Name() |
| } |
| |
| case *types.Var: |
| if t.IsField() { |
| if owner, ok := pi.owner[t]; ok { |
| _, base := pi.newSignature(owner) |
| return tagField, base + "." + t.Name() |
| } |
| return tagField, fmt.Sprintf("[%p].%s", t, t.Name()) |
| } else if owner, ok := pi.owner[t]; ok { |
| _, base := pi.newSignature(owner) |
| return tagParam, base + ":" + t.Name() |
| } |
| |
| case *types.Func: |
| topLevelTag = tagFunc |
| if recv := t.Type().(*types.Signature).Recv(); recv != nil { // method |
| if owner, ok := pi.owner[t]; ok { |
| _, base := pi.newSignature(owner) |
| return tagMethod, base + "." + t.Name() |
| } |
| // If the receiver is defined in this package, fully qualify the |
| // name so references from elsewhere will work. Strictly speaking |
| // this is only necessary for exported methods, but it's simpler to |
| // do it for everything. |
| return tagMethod, fmt.Sprintf("(%s).%s", types.TypeString(recv.Type(), func(pkg *types.Package) string { |
| return pkg.Name() |
| }), t.Name()) |
| } |
| |
| case *types.TypeName: |
| topLevelTag = tagType |
| if t.Pkg() == nil { |
| return isBuiltin + tagType, t.Name() |
| } |
| |
| case *types.Label: |
| return tagLabel, fmt.Sprintf("[%p].%s", t, t.Name()) |
| |
| default: |
| log.Panicf("Unexpected object kind: %T", obj) |
| } |
| |
| // At this point, we have eliminated built-in objects; everything else must |
| // be defined in a package. |
| if obj.Pkg() == nil { |
| log.Panic("Object without a package: ", obj) |
| } |
| |
| // Objects at package scope (i.e., parent scope is package scope). |
| if obj.Parent() == obj.Pkg().Scope() { |
| return topLevelTag, obj.Name() |
| } |
| |
| // Objects in interior (local) scopes, i.e., everything else. |
| return topLevelTag, fmt.Sprintf("[%p].%s", obj, obj.Name()) |
| } |
| |
| // addOwners updates pi.owner from the types in pkg, adding mapping from fields |
| // of package-level named struct types to the owning named struct type; from |
| // methods of package-level named interface types to the owning named interface |
| // type; and from parameters of package-level named function or method types to |
| // the owning named function or method. |
| // |
| // This relation is used to construct signatures for these fields/methods, |
| // since they may be referenced from another package and thus need |
| // deterministic names. An object does not expose its "owner"; indeed it may |
| // have several. |
| // |
| // Caveats: |
| // |
| // (1) This mapping is deterministic but not necessarily the best one according |
| // to the original syntax, to which, in general, we do not have access. In |
| // these two examples, the type checker considers field X as belonging equally |
| // to types T and U, even though according the syntax, it belongs primarily to |
| // T in the first example and U in the second: |
| // |
| // type T struct {X int} |
| // type U T |
| // |
| // type T U |
| // type U struct {X int} |
| // |
| // Similarly: |
| // |
| // type U struct {X int} |
| // type V struct {U} |
| // |
| // TODO(adonovan): sameer@ points out a useful heuristic: in a case of struct |
| // or interface embedding, if one struct/interface has fewer fields/methods, |
| // then it must be the primary one. |
| // |
| // (2) This pass is not exhaustive: there remain objects that may be referenced |
| // from outside the package but for which we can't easily come up with good |
| // names. Here are some examples: |
| // |
| // // package p |
| // var V1, V2 struct {X int} = ... |
| // func F() struct{X int} {...} |
| // type T struct { |
| // Y struct { X int } |
| // } |
| // |
| // // main |
| // p.V2.X = 1 |
| // print(p.F().X) |
| // new(p.T).Y[0].X |
| // |
| // Also note that there may be arbitrary pointer, struct, chan, map, array, and |
| // slice type constructors between the type of the exported package member (V2, |
| // F or T) and the type of its X subelement. For now, we simply ignore such |
| // names. They should be rare in readable code. |
| func (pi *PackageInfo) addOwners(pkg *types.Package) { |
| scope := pkg.Scope() |
| addFunc := func(obj *types.Func) { |
| // Inspect the receiver, parameters, and result values. |
| fsig := obj.Type().(*types.Signature) |
| if recv := fsig.Recv(); recv != nil { |
| pi.owner[recv] = obj |
| } |
| if params := fsig.Params(); params != nil { |
| for i := 0; i < params.Len(); i++ { |
| pi.owner[params.At(i)] = obj |
| } |
| } |
| if res := fsig.Results(); res != nil { |
| for i := 0; i < res.Len(); i++ { |
| pi.owner[res.At(i)] = obj |
| } |
| } |
| } |
| addMethods := func(obj types.Object, n int, method func(i int) *types.Func) { |
| for i := 0; i < n; i++ { |
| if m := method(i); m.Pkg() == pkg { |
| if _, ok := pi.owner[m]; !ok { |
| pi.owner[m] = obj |
| addFunc(m) |
| } |
| } |
| } |
| } |
| |
| for _, name := range scope.Names() { |
| switch obj := scope.Lookup(name).(type) { |
| case *types.TypeName: |
| // TODO(schroederc): support for type aliases. For now, skip these so we |
| // don't wind up emitting redundant declaration sites for the aliased |
| // type. |
| named, ok := obj.Type().(*types.Named) |
| if !ok { |
| continue |
| } |
| switch t := named.Underlying().(type) { |
| case *types.Struct: |
| // Inspect the fields of a struct. |
| for i := 0; i < t.NumFields(); i++ { |
| f := t.Field(i) |
| if f.Pkg() != pkg { |
| continue // wrong package |
| } |
| if _, ok := pi.owner[f]; !ok { |
| pi.owner[f] = obj |
| } |
| } |
| addMethods(obj, named.NumMethods(), named.Method) |
| |
| case *types.Interface: |
| // Inspect the declared methods of an interface. |
| addMethods(obj, t.NumExplicitMethods(), t.ExplicitMethod) |
| |
| default: |
| // Inspect declared methods of other named types. |
| addMethods(obj, named.NumMethods(), named.Method) |
| } |
| |
| case *types.Func: |
| addFunc(obj) |
| } |
| } |
| } |
| |
| // findFieldName tries to resolve the identifier that names an embedded |
| // anonymous field declaration at expr, and reports whether successful. |
| func (pi *PackageInfo) findFieldName(expr ast.Expr) (id *ast.Ident, ok bool) { |
| // There are three cases we care about here: |
| // |
| // A bare identifier (foo), which refers to a type defined in |
| // this package, or a builtin type, |
| // |
| // A selector expression (pkg.Foo) referring to an exported |
| // type defined in another package, or |
| // |
| // A pointer to either of these. |
| |
| switch t := expr.(type) { |
| case *ast.StarExpr: // *T |
| return pi.findFieldName(t.X) |
| case *ast.Ident: // T declared locally |
| return t, true |
| case *ast.SelectorExpr: // pkg.T declared elsewhere |
| return t.Sel, true |
| default: |
| // No idea what this is; possibly malformed code. |
| return nil, false |
| } |
| } |
| |
| // importPath returns the import path of pkg. |
| func (pi *PackageInfo) importPath(pkg *types.Package) string { |
| if v := pi.PackageVName[pkg]; v != nil { |
| return govname.ImportPath(v, pi.details.GetGoroot()) |
| } |
| return pkg.Name() |
| } |
| |
| // isPackageInit reports whether fi belongs to a package-level init function. |
| func (pi *PackageInfo) isPackageInit(fi *funcInfo) bool { |
| for _, v := range pi.packageInit { |
| if fi == v { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // goDetails returns the GoDetails message attached to unit, if there is one; |
| // otherwise it returns nil. |
| func goDetails(unit *apb.CompilationUnit) *gopb.GoDetails { |
| for _, msg := range unit.Details { |
| var dets gopb.GoDetails |
| if err := ptypes.UnmarshalAny(msg, &dets); err == nil { |
| return &dets |
| } |
| } |
| return nil |
| } |
| |
| // goPackageInfo returns the GoPackageInfo message within the given slice, if |
| // there is one; otherwise it returns nil. |
| func goPackageInfo(details []*ptypes.Any) *gopb.GoPackageInfo { |
| for _, msg := range details { |
| var info gopb.GoPackageInfo |
| if err := ptypes.UnmarshalAny(msg, &info); err == nil { |
| return &info |
| } |
| } |
| return nil |
| } |
| |
| // matchesBuildTags reports whether the file at fpath, whose content is in |
| // data, would be matched by the settings in bc. |
| func matchesBuildTags(fpath string, data []byte, bc *build.Context) bool { |
| dir, name := filepath.Split(fpath) |
| bc.OpenFile = func(path string) (io.ReadCloser, error) { |
| if path != fpath { |
| return nil, errors.New("file not found") |
| } |
| return ioutil.NopCloser(bytes.NewReader(data)), nil |
| } |
| match, err := bc.MatchFile(dir, name) |
| return err == nil && match |
| } |
| |
| // AllTypeInfo creates a new types.Info value with empty maps for each of the |
| // fields that can be filled in by the type-checker. |
| func AllTypeInfo() *types.Info { |
| return &types.Info{ |
| Types: make(map[ast.Expr]types.TypeAndValue), |
| Defs: make(map[*ast.Ident]types.Object), |
| Uses: make(map[*ast.Ident]types.Object), |
| Implicits: make(map[ast.Node]types.Object), |
| Selections: make(map[*ast.SelectorExpr]*types.Selection), |
| Scopes: make(map[ast.Node]*types.Scope), |
| } |
| } |
| |
| // XRefTypeInfo creates a new types.Info value with empty maps for each of the |
| // fields needed for cross-reference indexing. |
| func XRefTypeInfo() *types.Info { |
| return &types.Info{ |
| Types: make(map[ast.Expr]types.TypeAndValue), |
| Defs: make(map[*ast.Ident]types.Object), |
| Uses: make(map[*ast.Ident]types.Object), |
| Implicits: make(map[ast.Node]types.Object), |
| } |
| } |