Skip to content

Commit

Permalink
go/packages/internal/nodecount: count ast.Node frequency
Browse files Browse the repository at this point in the history
This internal tool counts node frequencies, which may be useful
when choosing type switch case order. It is also a nice
illustration of packages.Load and ast.Inspect.

Change-Id: I88c08987b6fe7b10e15f00844cd71fc850fc70c3
Reviewed-on: https://go-review.googlesource.com/c/tools/+/480915
Reviewed-by: Robert Findley <[email protected]>
Run-TryBot: Alan Donovan <[email protected]>
Auto-Submit: Alan Donovan <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
  • Loading branch information
adonovan authored and gopherbot committed Mar 31, 2023
1 parent 0a3e5f0 commit 58c9a63
Showing 1 changed file with 71 additions and 0 deletions.
71 changes: 71 additions & 0 deletions go/packages/internal/nodecount/nodecount.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2023 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.

// The nodecount program illustrates the use of packages.Load to print
// the frequency of occurrence of each type of syntax node among the
// selected packages.
//
// Example usage:
//
// $ nodecount golang.org/x/tools/... std
//
// A typical distribution is 40% identifiers, 10% literals, 8%
// selectors, and 6% calls; around 3% each of BinaryExpr, BlockStmt,
// AssignStmt, Field, and Comment; and the rest accounting for 20%.
package main

import (
"flag"
"fmt"
"go/ast"
"log"
"reflect"
"sort"

"golang.org/x/tools/go/packages"
)

func main() {
flag.Parse()

// Parse specified packages.
config := packages.Config{
Mode: packages.NeedSyntax | packages.NeedFiles,
Tests: true,
}
pkgs, err := packages.Load(&config, flag.Args()...)
if err != nil {
log.Fatal(err)
}

// Count each type of syntax node.
var (
byType = make(map[reflect.Type]int)
total int
)
packages.Visit(pkgs, nil, func(p *packages.Package) {
for _, f := range p.Syntax {
ast.Inspect(f, func(n ast.Node) bool {
if n != nil {
byType[reflect.TypeOf(n)]++
total++
}
return true
})
}
})

// Print results (percent, count, type) in descending order.
var types []reflect.Type
for t := range byType {
types = append(types, t)
}
sort.Slice(types, func(i, j int) bool {
return byType[types[i]] > byType[types[j]]
})
for _, t := range types {
percent := 100 * float64(byType[t]) / float64(total)
fmt.Printf("%6.2f%%\t%8d\t%s\n", percent, byType[t], t)
}
}

0 comments on commit 58c9a63

Please sign in to comment.