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