internal/gcimporter: add tool to inspect export data

This CL adds an unexported command for inspecting "unified" export
data produced by the compiler, and checking that it can be
roundtripped through "indexed" format as used by gopls.

Change-Id: Idc8eea5c8d7287d7f10af7bd15c7a52e6987a4bf
Reviewed-on: https://go-review.googlesource.com/c/tools/+/572795
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Alan Donovan 2024-03-19 13:18:47 -04:00
Родитель 4fe2c29e13
Коммит 814f676cf4
1 изменённых файлов: 117 добавлений и 0 удалений

117
internal/gcimporter/main.go Normal file
Просмотреть файл

@ -0,0 +1,117 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
// The gcimporter command reads the compiler's export data for the
// named packages and prints the decoded type information.
//
// It is provided for debugging export data problems.
package main
import (
"bytes"
"flag"
"fmt"
"go/token"
"go/types"
"log"
"os"
"sort"
"golang.org/x/tools/go/gcexportdata"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/gcimporter"
)
func main() {
flag.Parse()
cfg := &packages.Config{
Fset: token.NewFileSet(),
// Don't request NeedTypes: we want to be certain that
// we loaded the types ourselves, from export data.
Mode: packages.NeedName | packages.NeedExportFile,
}
pkgs, err := packages.Load(cfg, flag.Args()...)
if err != nil {
log.Fatal(err)
}
if packages.PrintErrors(pkgs) > 0 {
os.Exit(1)
}
for _, pkg := range pkgs {
// Read types from compiler's unified export data file.
// This Package may included non-exported functions if they
// are called by inlinable exported functions.
var tpkg1 *types.Package
{
export, err := os.ReadFile(pkg.ExportFile)
if err != nil {
log.Fatalf("can't read %q export data: %v", pkg.PkgPath, err)
}
r, err := gcexportdata.NewReader(bytes.NewReader(export))
if err != nil {
log.Fatalf("reading export data %s: %v", pkg.ExportFile, err)
}
tpkg1, err = gcexportdata.Read(r, cfg.Fset, make(map[string]*types.Package), pkg.PkgPath)
if err != nil {
log.Fatalf("decoding export data: %v", err)
}
}
fmt.Println("# Read from compiler's unified export data:")
printPackage(tpkg1)
// Now reexport as indexed (deep) export data, and reimport.
// The Package will contain only exported symbols.
var tpkg2 *types.Package
{
var out bytes.Buffer
if err := gcimporter.IExportData(&out, cfg.Fset, tpkg1); err != nil {
log.Fatal(err)
}
var err error
_, tpkg2, err = gcimporter.IImportData(cfg.Fset, make(map[string]*types.Package), out.Bytes(), tpkg1.Path())
if err != nil {
log.Fatal(err)
}
}
fmt.Println("# After round-tripping through indexed export data:")
printPackage(tpkg2)
}
}
func printPackage(pkg *types.Package) {
fmt.Printf("package %s %q\n", pkg.Name(), pkg.Path())
if !pkg.Complete() {
fmt.Printf("\thas incomplete exported type info\n")
}
// imports
var lines []string
for _, imp := range pkg.Imports() {
lines = append(lines, fmt.Sprintf("\timport %q", imp.Path()))
}
sort.Strings(lines)
for _, line := range lines {
fmt.Println(line)
}
// types of package members
qual := types.RelativeTo(pkg)
scope := pkg.Scope()
for _, name := range scope.Names() {
obj := scope.Lookup(name)
fmt.Printf("\t%s\n", types.ObjectString(obj, qual))
if _, ok := obj.(*types.TypeName); ok {
for _, meth := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
fmt.Printf("\t%s\n", types.SelectionString(meth, qual))
}
}
}
fmt.Println()
}