1// The hugeparam command identifies by-value parameters that are larger than n bytes.
2//
3// Example:
4//	$ ./hugeparams encoding/xml
5package main
6
7import (
8	"flag"
9	"fmt"
10	"go/ast"
11	"go/token"
12	"go/types"
13	"log"
14
15	"golang.org/x/tools/go/loader"
16)
17
18//!+
19var bytesFlag = flag.Int("bytes", 48, "maximum parameter size in bytes")
20
21var sizeof = (&types.StdSizes{8, 8}).Sizeof // the sizeof function
22
23func PrintHugeParams(fset *token.FileSet, info *types.Info, files []*ast.File) {
24	checkTuple := func(descr string, tuple *types.Tuple) {
25		for i := 0; i < tuple.Len(); i++ {
26			v := tuple.At(i)
27			if sz := sizeof(v.Type()); sz > int64(*bytesFlag) {
28				fmt.Printf("%s: %q %s: %s = %d bytes\n",
29					fset.Position(v.Pos()),
30					v.Name(), descr, v.Type(), sz)
31			}
32		}
33	}
34	checkSig := func(sig *types.Signature) {
35		checkTuple("parameter", sig.Params())
36		checkTuple("result", sig.Results())
37	}
38	for _, file := range files {
39		ast.Inspect(file, func(n ast.Node) bool {
40			switch n := n.(type) {
41			case *ast.FuncDecl:
42				checkSig(info.Defs[n.Name].Type().(*types.Signature))
43			case *ast.FuncLit:
44				checkSig(info.Types[n.Type].Type.(*types.Signature))
45			}
46			return true
47		})
48	}
49}
50
51//!-
52
53func main() {
54	flag.Parse()
55
56	// The loader loads a complete Go program from source code.
57	var conf loader.Config
58	_, err := conf.FromArgs(flag.Args(), false)
59	if err != nil {
60		log.Fatal(err) // command syntax error
61	}
62	lprog, err := conf.Load()
63	if err != nil {
64		log.Fatal(err) // load error
65	}
66
67	for _, info := range lprog.InitialPackages() {
68		PrintHugeParams(lprog.Fset, &info.Info, info.Files)
69	}
70}
71
72/*
73//!+output
74% ./hugeparam encoding/xml
75/go/src/encoding/xml/marshal.go:167:50: "start" parameter: encoding/xml.StartElement = 56 bytes
76/go/src/encoding/xml/marshal.go:734:97: "" result: encoding/xml.StartElement = 56 bytes
77/go/src/encoding/xml/marshal.go:761:51: "start" parameter: encoding/xml.StartElement = 56 bytes
78/go/src/encoding/xml/marshal.go:781:68: "start" parameter: encoding/xml.StartElement = 56 bytes
79/go/src/encoding/xml/xml.go:72:30: "" result: encoding/xml.StartElement = 56 bytes
80//!-output
81*/
82