// 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()
}