// Copyright 2017 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 gcexportdata command is a diagnostic tool that displays the
// contents of gc export data files.
package main

import (
	"flag"
	"fmt"
	"go/token"
	"go/types"
	"log"
	"os"

	"golang.org/x/tools/go/gcexportdata"
	"golang.org/x/tools/go/types/typeutil"
)

var packageFlag = flag.String("package", "", "alternative package to print")

func main() {
	log.SetPrefix("gcexportdata: ")
	log.SetFlags(0)
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "usage: gcexportdata [-package path] file.a")
	}
	flag.Parse()
	if flag.NArg() != 1 {
		flag.Usage()
		os.Exit(2)
	}
	filename := flag.Args()[0]

	f, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}

	r, err := gcexportdata.NewReader(f)
	if err != nil {
		log.Fatalf("%s: %s", filename, err)
	}

	// Decode the package.
	const primary = "<primary>"
	imports := make(map[string]*types.Package)
	fset := token.NewFileSet()
	pkg, err := gcexportdata.Read(r, fset, imports, primary)
	if err != nil {
		log.Fatalf("%s: %s", filename, err)
	}

	// Optionally select an indirectly mentioned package.
	if *packageFlag != "" {
		pkg = imports[*packageFlag]
		if pkg == nil {
			fmt.Fprintf(os.Stderr, "export data file %s does not mention %s; has:\n",
				filename, *packageFlag)
			for p := range imports {
				if p != primary {
					fmt.Fprintf(os.Stderr, "\t%s\n", p)
				}
			}
			os.Exit(1)
		}
	}

	// Print all package-level declarations, including non-exported ones.
	fmt.Printf("package %s\n", pkg.Name())
	for _, imp := range pkg.Imports() {
		fmt.Printf("import %q\n", imp.Path())
	}
	qual := func(p *types.Package) string {
		if pkg == p {
			return ""
		}
		return p.Name()
	}
	scope := pkg.Scope()
	for _, name := range scope.Names() {
		obj := scope.Lookup(name)
		fmt.Printf("%s: %s\n",
			fset.Position(obj.Pos()),
			types.ObjectString(obj, qual))

		// For types, print each method.
		if _, ok := obj.(*types.TypeName); ok {
			for _, method := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
				fmt.Printf("%s: %s\n",
					fset.Position(method.Obj().Pos()),
					types.SelectionString(method, qual))
			}
		}
	}
}
