1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package ir_test
6
7import (
8	"fmt"
9	"go/ast"
10	"go/importer"
11	"go/parser"
12	"go/token"
13	"go/types"
14	"log"
15	"os"
16
17	"golang.org/x/tools/go/packages"
18	"honnef.co/go/tools/ir"
19	"honnef.co/go/tools/ir/irutil"
20)
21
22const hello = `
23package main
24
25import "fmt"
26
27const message = "Hello, World!"
28
29func main() {
30	fmt.Println(message)
31}
32`
33
34// This program demonstrates how to run the IR builder on a single
35// package of one or more already-parsed files.  Its dependencies are
36// loaded from compiler export data.  This is what you'd typically use
37// for a compiler; it does not depend on golang.org/x/tools/go/loader.
38//
39// It shows the printed representation of packages, functions, and
40// instructions.  Within the function listing, the name of each
41// BasicBlock such as ".0.entry" is printed left-aligned, followed by
42// the block's Instructions.
43//
44// For each instruction that defines an IR virtual register
45// (i.e. implements Value), the type of that value is shown in the
46// right column.
47//
48// Build and run the irdump.go program if you want a standalone tool
49// with similar functionality. It is located at
50// honnef.co/go/tools/internal/cmd/irdump.
51//
52func Example_buildPackage() {
53	// Parse the source files.
54	fset := token.NewFileSet()
55	f, err := parser.ParseFile(fset, "hello.go", hello, parser.ParseComments)
56	if err != nil {
57		fmt.Print(err) // parse error
58		return
59	}
60	files := []*ast.File{f}
61
62	// Create the type-checker's package.
63	pkg := types.NewPackage("hello", "")
64
65	// Type-check the package, load dependencies.
66	// Create and build the IR program.
67	hello, _, err := irutil.BuildPackage(
68		&types.Config{Importer: importer.Default()}, fset, pkg, files, ir.SanityCheckFunctions)
69	if err != nil {
70		fmt.Print(err) // type error in some package
71		return
72	}
73
74	// Print out the package.
75	hello.WriteTo(os.Stdout)
76
77	// Print out the package-level functions.
78	hello.Func("init").WriteTo(os.Stdout)
79	hello.Func("main").WriteTo(os.Stdout)
80
81	// Output:
82	// package hello:
83	//   func  init       func()
84	//   var   init$guard bool
85	//   func  main       func()
86	//   const message    message = Const <untyped string> {"Hello, World!"}
87	//
88	// # Name: hello.init
89	// # Package: hello
90	// # Synthetic: package initializer
91	// func init():
92	// b0: # entry
93	// 	t1 = Const <bool> {true}
94	// 	t2 = Load <bool> init$guard
95	// 	If t2 → b1 b2
96	//
97	// b1: ← b0 b2 # exit
98	// 	Return
99	//
100	// b2: ← b0 # init.start
101	// 	Store {bool} init$guard t1
102	// 	t6 = Call <()> fmt.init
103	// 	Jump → b1
104	//
105	// # Name: hello.main
106	// # Package: hello
107	// # Location: hello.go:8:1
108	// func main():
109	// b0: # entry
110	// 	t1 = Const <string> {"Hello, World!"}
111	// 	t2 = Const <int> {0}
112	// 	t3 = HeapAlloc <*[1]interface{}>
113	// 	t4 = IndexAddr <*interface{}> t3 t2
114	// 	t5 = MakeInterface <interface{}> t1
115	// 	Store {interface{}} t4 t5
116	// 	t7 = Slice <[]interface{}> t3 <nil> <nil> <nil>
117	// 	t8 = Call <(n int, err error)> fmt.Println t7
118	// 	Jump → b1
119	//
120	// b1: ← b0 # exit
121	// 	Return
122}
123
124// This example builds IR code for a set of packages using the
125// x/tools/go/packages API. This is what you would typically use for a
126// analysis capable of operating on a single package.
127func Example_loadPackages() {
128	// Load, parse, and type-check the initial packages.
129	cfg := &packages.Config{Mode: packages.LoadSyntax}
130	initial, err := packages.Load(cfg, "fmt", "net/http")
131	if err != nil {
132		log.Fatal(err)
133	}
134
135	// Stop if any package had errors.
136	// This step is optional; without it, the next step
137	// will create IR for only a subset of packages.
138	if packages.PrintErrors(initial) > 0 {
139		log.Fatalf("packages contain errors")
140	}
141
142	// Create IR packages for all well-typed packages.
143	prog, pkgs := irutil.Packages(initial, ir.PrintPackages, nil)
144	_ = prog
145
146	// Build IR code for the well-typed initial packages.
147	for _, p := range pkgs {
148		if p != nil {
149			p.Build()
150		}
151	}
152}
153
154// This example builds IR code for a set of packages plus all their dependencies,
155// using the x/tools/go/packages API.
156// This is what you'd typically use for a whole-program analysis.
157func Example_loadWholeProgram() {
158	// Load, parse, and type-check the whole program.
159	cfg := packages.Config{Mode: packages.LoadAllSyntax}
160	initial, err := packages.Load(&cfg, "fmt", "net/http")
161	if err != nil {
162		log.Fatal(err)
163	}
164
165	// Create IR packages for well-typed packages and their dependencies.
166	prog, pkgs := irutil.AllPackages(initial, ir.PrintPackages, nil)
167	_ = pkgs
168
169	// Build IR code for the whole program.
170	prog.Build()
171}
172