Source File
inspector.go
Belonging Package
golang.org/x/tools/go/ast/inspector
// Copyright 2018 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.// Package inspector provides helper functions for traversal over the// syntax trees of a package, including node filtering by type, and// materialization of the traversal stack.//// During construction, the inspector does a complete traversal and// builds a list of push/pop events and their node type. Subsequent// method calls that request a traversal scan this list, rather than walk// the AST, and perform type filtering using efficient bit sets.// This representation is sometimes called a "balanced parenthesis tree."//// Experiments suggest the inspector's traversals are about 2.5x faster// than [ast.Inspect], but it may take around 5 traversals for this// benefit to amortize the inspector's construction cost.// If efficiency is the primary concern, do not use Inspector for// one-off traversals.//// The [Cursor] type provides a more flexible API for efficient// navigation of syntax trees in all four "cardinal directions". For// example, traversals may be nested, so you can find each node of// type A and then search within it for nodes of type B. Or you can// traverse from a node to its immediate neighbors: its parent, its// previous and next sibling, or its first and last child. We// recommend using methods of Cursor in preference to Inspector where// possible.package inspector// There are four orthogonal features in a traversal:// 1 type filtering// 2 pruning// 3 postorder calls to f// 4 stack// Rather than offer all of them in the API,// only a few combinations are exposed:// - Preorder is the fastest and has fewest features,// but is the most commonly needed traversal.// - Nodes and WithStack both provide pruning and postorder calls,// even though few clients need it, because supporting two versions// is not justified.// More combinations could be supported by expressing them as// wrappers around a more generic traversal, but this was measured// and found to degrade performance significantly (30%).import ()// An Inspector provides methods for inspecting// (traversing) the syntax trees of a package.type Inspector struct {events []event}func ( edge.Kind, int) int32 {return int32(uint32(+1)<<7 | uint32())}// unpackEdgeKindAndIndex unpacks the edge kind and edge index (within// an []ast.Node slice) from the parent field of a pop event.func ( int32) (edge.Kind, int) {// The "parent" field of a pop node holds the// edge Kind in the lower 7 bits and the index+1// in the upper 25.return edge.Kind( & 0x7f), int(>>7) - 1}// New returns an Inspector for the specified syntax trees.func ( []*ast.File) *Inspector {return &Inspector{traverse()}}// An event represents a push or a pop// of an ast.Node during a traversal.type event struct {node ast.Nodetyp uint64 // typeOf(node) on push event, or union of typ strictly between push and pop events on pop eventsindex int32 // index of corresponding push or pop eventparent int32 // index of parent's push node (push nodes only), or packed edge kind/index (pop nodes only)}// TODO: Experiment with storing only the second word of event.node (unsafe.Pointer).// Type can be recovered from the sole bit in typ.// [Tried this, wasn't faster. --adonovan]// Preorder visits all the nodes of the files supplied to [New] in// depth-first order. It calls f(n) for each node n before it visits// n's children.//// The complete traversal sequence is determined by [ast.Inspect].// The types argument, if non-empty, enables type-based filtering of// events. The function f is called only for nodes whose type// matches an element of the types slice.//// The [Cursor.Preorder] method provides a richer alternative interface.// Example://// for c := range in.Root().Preorder(types) { ... }func ( *Inspector) ( []ast.Node, func(ast.Node)) {// Because it avoids postorder calls to f, and the pruning// check, Preorder is almost twice as fast as Nodes. The two// features seem to contribute similar slowdowns (~1.4x each).// This function is equivalent to the PreorderSeq call below,// but to avoid the additional dynamic call (which adds 13-35%// to the benchmarks), we expand it out.//// in.PreorderSeq(types...)(func(n ast.Node) bool {// f(n)// return true// }):= maskOf()for := int32(0); < int32(len(.events)); {:= .events[]if .index > {// pushif .typ& != 0 {(.node)}:= .indexif .events[].typ& == 0 {// Subtrees do not contain types: skip them and pop.= + 1continue}}++}}// Nodes visits the nodes of the files supplied to [New] in depth-first// order. It calls f(n, true) for each node n before it visits n's// children. If f returns true, Nodes invokes f recursively for each// of the non-nil children of the node, followed by a call of// f(n, false).//// The complete traversal sequence is determined by [ast.Inspect].// The types argument, if non-empty, enables type-based filtering of// events. The function f if is called only for nodes whose type// matches an element of the types slice.//// The [Cursor.Inspect] method provides a richer alternative interface.// Example://// in.Root().Inspect(types, func(c Cursor) bool {// ...// return true// }func ( *Inspector) ( []ast.Node, func( ast.Node, bool) ( bool)) {:= maskOf()for := int32(0); < int32(len(.events)); {:= .events[]if .index > {// push:= .indexif .typ& != 0 {if !(.node, true) {= + 1 // jump to corresponding pop + 1continue}}if .events[].typ& == 0 {// Subtrees do not contain types: skip them.=continue}} else {// pop:= .indexif .events[].typ& != 0 {(.node, false)}}++}}// WithStack visits nodes in a similar manner to Nodes, but it// supplies each call to f an additional argument, the current// traversal stack. The stack's first element is the outermost node,// an *ast.File; its last is the innermost, n.//// The [Cursor.Inspect] method provides a richer alternative interface.// Example://// in.Root().Inspect(types, func(c Cursor) bool {// stack := slices.Collect(c.Enclosing())// ...// return true// })func ( *Inspector) ( []ast.Node, func( ast.Node, bool, []ast.Node) ( bool)) {:= maskOf()var []ast.Nodefor := int32(0); < int32(len(.events)); {:= .events[]if .index > {// push:= .index= append(, .node)if .typ& != 0 {if !(.node, true, ) {= + 1= [:len()-1]continue}}if .events[].typ& == 0 {// Subtrees does not contain types: skip them.=continue}} else {// pop:= .indexif .events[].typ& != 0 {(.node, false, )}= [:len()-1]}++}}// traverse builds the table of events representing a traversal.func ( []*ast.File) []event {// Preallocate approximate number of events// based on source file extent of the declarations.// (We use End-Pos not FileStart-FileEnd to neglect// the effect of long doc comments.)// This makes traverse faster by 4x (!).var intfor , := range {+= int(.End() - .Pos())}// This estimate is based on the net/http package.:= min(*33/100, 1e6) // impose some reasonable maximum (1M):= &visitor{events: make([]event, 0, ),stack: []item{{index: -1}}, // include an extra event so file nodes have a parent}for , := range {walk(, edge.Invalid, -1, )}return .events}type visitor struct {events []eventstack []item}type item struct {index int32 // index of current node's push eventparentIndex int32 // index of parent node's push eventtypAccum uint64 // accumulated type bits of current node's descendantsedgeKindAndIndex int32 // edge.Kind and index, bit packed}func ( *visitor) ( edge.Kind, int, ast.Node) {var (= int32(len(.events))= .stack[len(.stack)-1].index).events = append(.events, event{node: ,parent: ,typ: typeOf(),index: 0, // (pop index is set later by visitor.pop)}).stack = append(.stack, item{index: ,parentIndex: ,edgeKindAndIndex: packEdgeKindAndIndex(, ),})// 2B nodes ought to be enough for anyone!if int32(len(.events)) < 0 {panic("event index exceeded int32")}// 32M elements in an []ast.Node ought to be enough for anyone!if , := unpackEdgeKindAndIndex(packEdgeKindAndIndex(, )); != || != {panic("Node slice index exceeded uint25")}}func ( *visitor) ( ast.Node) {:= len(.stack) - 1:= .stack[]:= &.events[.index]:= &.stack[-1].index = int32(len(.events)) // make push event refer to pop.typAccum |= .typAccum | .typ // accumulate type bits into parent.stack = .stack[:].events = append(.events, event{node: ,typ: .typAccum,index: .index,parent: .edgeKindAndIndex, // see [unpackEdgeKindAndIndex]})}
The pages are generated with Golds v0.8.4. (GOOS=linux GOARCH=amd64)