Source file src/cmd/vendor/golang.org/x/tools/go/ast/inspector/inspector.go

     1  // Copyright 2018 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  
     5  // Package inspector provides helper functions for traversal over the
     6  // syntax trees of a package, including node filtering by type, and
     7  // materialization of the traversal stack.
     8  //
     9  // During construction, the inspector does a complete traversal and
    10  // builds a list of push/pop events and their node type. Subsequent
    11  // method calls that request a traversal scan this list, rather than walk
    12  // the AST, and perform type filtering using efficient bit sets.
    13  // This representation is sometimes called a "balanced parenthesis tree."
    14  //
    15  // Experiments suggest the inspector's traversals are about 2.5x faster
    16  // than [ast.Inspect], but it may take around 5 traversals for this
    17  // benefit to amortize the inspector's construction cost.
    18  // If efficiency is the primary concern, do not use Inspector for
    19  // one-off traversals.
    20  //
    21  // The [Cursor] type provides a more flexible API for efficient
    22  // navigation of syntax trees in all four "cardinal directions". For
    23  // example, traversals may be nested, so you can find each node of
    24  // type A and then search within it for nodes of type B. Or you can
    25  // traverse from a node to its immediate neighbors: its parent, its
    26  // previous and next sibling, or its first and last child. We
    27  // recommend using methods of Cursor in preference to Inspector where
    28  // possible.
    29  package inspector
    30  
    31  // There are four orthogonal features in a traversal:
    32  //  1 type filtering
    33  //  2 pruning
    34  //  3 postorder calls to f
    35  //  4 stack
    36  // Rather than offer all of them in the API,
    37  // only a few combinations are exposed:
    38  // - Preorder is the fastest and has fewest features,
    39  //   but is the most commonly needed traversal.
    40  // - Nodes and WithStack both provide pruning and postorder calls,
    41  //   even though few clients need it, because supporting two versions
    42  //   is not justified.
    43  // More combinations could be supported by expressing them as
    44  // wrappers around a more generic traversal, but this was measured
    45  // and found to degrade performance significantly (30%).
    46  
    47  import (
    48  	"go/ast"
    49  
    50  	"golang.org/x/tools/go/ast/edge"
    51  )
    52  
    53  // An Inspector provides methods for inspecting
    54  // (traversing) the syntax trees of a package.
    55  type Inspector struct {
    56  	events []event
    57  }
    58  
    59  func packEdgeKindAndIndex(ek edge.Kind, index int) int32 {
    60  	return int32(uint32(index+1)<<7 | uint32(ek))
    61  }
    62  
    63  // unpackEdgeKindAndIndex unpacks the edge kind and edge index (within
    64  // an []ast.Node slice) from the parent field of a pop event.
    65  func unpackEdgeKindAndIndex(x int32) (edge.Kind, int) {
    66  	// The "parent" field of a pop node holds the
    67  	// edge Kind in the lower 7 bits and the index+1
    68  	// in the upper 25.
    69  	return edge.Kind(x & 0x7f), int(x>>7) - 1
    70  }
    71  
    72  // New returns an Inspector for the specified syntax trees.
    73  func New(files []*ast.File) *Inspector {
    74  	return &Inspector{traverse(files)}
    75  }
    76  
    77  // An event represents a push or a pop
    78  // of an ast.Node during a traversal.
    79  type event struct {
    80  	node   ast.Node
    81  	typ    uint64 // typeOf(node) on push event, or union of typ strictly between push and pop events on pop events
    82  	index  int32  // index of corresponding push or pop event
    83  	parent int32  // index of parent's push node (push nodes only), or packed edge kind/index (pop nodes only)
    84  }
    85  
    86  // TODO: Experiment with storing only the second word of event.node (unsafe.Pointer).
    87  // Type can be recovered from the sole bit in typ.
    88  // [Tried this, wasn't faster. --adonovan]
    89  
    90  // Preorder visits all the nodes of the files supplied to New in
    91  // depth-first order. It calls f(n) for each node n before it visits
    92  // n's children.
    93  //
    94  // The complete traversal sequence is determined by [ast.Inspect].
    95  // The types argument, if non-empty, enables type-based filtering of
    96  // events. The function f is called only for nodes whose type
    97  // matches an element of the types slice.
    98  //
    99  // The [Cursor.Preorder] method provides a richer alternative interface.
   100  // Example:
   101  //
   102  //	for c := range in.Root().Preorder(types) { ... }
   103  func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) {
   104  	// Because it avoids postorder calls to f, and the pruning
   105  	// check, Preorder is almost twice as fast as Nodes. The two
   106  	// features seem to contribute similar slowdowns (~1.4x each).
   107  
   108  	// This function is equivalent to the PreorderSeq call below,
   109  	// but to avoid the additional dynamic call (which adds 13-35%
   110  	// to the benchmarks), we expand it out.
   111  	//
   112  	// in.PreorderSeq(types...)(func(n ast.Node) bool {
   113  	// 	f(n)
   114  	// 	return true
   115  	// })
   116  
   117  	mask := maskOf(types)
   118  	for i := int32(0); i < int32(len(in.events)); {
   119  		ev := in.events[i]
   120  		if ev.index > i {
   121  			// push
   122  			if ev.typ&mask != 0 {
   123  				f(ev.node)
   124  			}
   125  			pop := ev.index
   126  			if in.events[pop].typ&mask == 0 {
   127  				// Subtrees do not contain types: skip them and pop.
   128  				i = pop + 1
   129  				continue
   130  			}
   131  		}
   132  		i++
   133  	}
   134  }
   135  
   136  // Nodes visits the nodes of the files supplied to New in depth-first
   137  // order. It calls f(n, true) for each node n before it visits n's
   138  // children. If f returns true, Nodes invokes f recursively for each
   139  // of the non-nil children of the node, followed by a call of
   140  // f(n, false).
   141  //
   142  // The complete traversal sequence is determined by [ast.Inspect].
   143  // The types argument, if non-empty, enables type-based filtering of
   144  // events. The function f if is called only for nodes whose type
   145  // matches an element of the types slice.
   146  //
   147  // The [Cursor.Inspect] method provides a richer alternative interface.
   148  // Example:
   149  //
   150  //	in.Root().Inspect(types, func(c Cursor) bool {
   151  //		...
   152  //		return true
   153  //	}
   154  func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proceed bool)) {
   155  	mask := maskOf(types)
   156  	for i := int32(0); i < int32(len(in.events)); {
   157  		ev := in.events[i]
   158  		if ev.index > i {
   159  			// push
   160  			pop := ev.index
   161  			if ev.typ&mask != 0 {
   162  				if !f(ev.node, true) {
   163  					i = pop + 1 // jump to corresponding pop + 1
   164  					continue
   165  				}
   166  			}
   167  			if in.events[pop].typ&mask == 0 {
   168  				// Subtrees do not contain types: skip them.
   169  				i = pop
   170  				continue
   171  			}
   172  		} else {
   173  			// pop
   174  			push := ev.index
   175  			if in.events[push].typ&mask != 0 {
   176  				f(ev.node, false)
   177  			}
   178  		}
   179  		i++
   180  	}
   181  }
   182  
   183  // WithStack visits nodes in a similar manner to Nodes, but it
   184  // supplies each call to f an additional argument, the current
   185  // traversal stack. The stack's first element is the outermost node,
   186  // an *ast.File; its last is the innermost, n.
   187  //
   188  // The [Cursor.Inspect] method provides a richer alternative interface.
   189  // Example:
   190  //
   191  //	in.Root().Inspect(types, func(c Cursor) bool {
   192  //		stack := slices.Collect(c.Enclosing())
   193  //		...
   194  //		return true
   195  //	})
   196  func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, stack []ast.Node) (proceed bool)) {
   197  	mask := maskOf(types)
   198  	var stack []ast.Node
   199  	for i := int32(0); i < int32(len(in.events)); {
   200  		ev := in.events[i]
   201  		if ev.index > i {
   202  			// push
   203  			pop := ev.index
   204  			stack = append(stack, ev.node)
   205  			if ev.typ&mask != 0 {
   206  				if !f(ev.node, true, stack) {
   207  					i = pop + 1
   208  					stack = stack[:len(stack)-1]
   209  					continue
   210  				}
   211  			}
   212  			if in.events[pop].typ&mask == 0 {
   213  				// Subtrees does not contain types: skip them.
   214  				i = pop
   215  				continue
   216  			}
   217  		} else {
   218  			// pop
   219  			push := ev.index
   220  			if in.events[push].typ&mask != 0 {
   221  				f(ev.node, false, stack)
   222  			}
   223  			stack = stack[:len(stack)-1]
   224  		}
   225  		i++
   226  	}
   227  }
   228  
   229  // traverse builds the table of events representing a traversal.
   230  func traverse(files []*ast.File) []event {
   231  	// Preallocate approximate number of events
   232  	// based on source file extent of the declarations.
   233  	// (We use End-Pos not FileStart-FileEnd to neglect
   234  	// the effect of long doc comments.)
   235  	// This makes traverse faster by 4x (!).
   236  	var extent int
   237  	for _, f := range files {
   238  		extent += int(f.End() - f.Pos())
   239  	}
   240  	// This estimate is based on the net/http package.
   241  	capacity := min(extent*33/100, 1e6) // impose some reasonable maximum (1M)
   242  
   243  	v := &visitor{
   244  		events: make([]event, 0, capacity),
   245  		stack:  []item{{index: -1}}, // include an extra event so file nodes have a parent
   246  	}
   247  	for _, file := range files {
   248  		walk(v, edge.Invalid, -1, file)
   249  	}
   250  	return v.events
   251  }
   252  
   253  type visitor struct {
   254  	events []event
   255  	stack  []item
   256  }
   257  
   258  type item struct {
   259  	index            int32  // index of current node's push event
   260  	parentIndex      int32  // index of parent node's push event
   261  	typAccum         uint64 // accumulated type bits of current node's descendants
   262  	edgeKindAndIndex int32  // edge.Kind and index, bit packed
   263  }
   264  
   265  func (v *visitor) push(ek edge.Kind, eindex int, node ast.Node) {
   266  	var (
   267  		index       = int32(len(v.events))
   268  		parentIndex = v.stack[len(v.stack)-1].index
   269  	)
   270  	v.events = append(v.events, event{
   271  		node:   node,
   272  		parent: parentIndex,
   273  		typ:    typeOf(node),
   274  		index:  0, // (pop index is set later by visitor.pop)
   275  	})
   276  	v.stack = append(v.stack, item{
   277  		index:            index,
   278  		parentIndex:      parentIndex,
   279  		edgeKindAndIndex: packEdgeKindAndIndex(ek, eindex),
   280  	})
   281  
   282  	// 2B nodes ought to be enough for anyone!
   283  	if int32(len(v.events)) < 0 {
   284  		panic("event index exceeded int32")
   285  	}
   286  
   287  	// 32M elements in an []ast.Node ought to be enough for anyone!
   288  	if ek2, eindex2 := unpackEdgeKindAndIndex(packEdgeKindAndIndex(ek, eindex)); ek2 != ek || eindex2 != eindex {
   289  		panic("Node slice index exceeded uint25")
   290  	}
   291  }
   292  
   293  func (v *visitor) pop(node ast.Node) {
   294  	top := len(v.stack) - 1
   295  	current := v.stack[top]
   296  
   297  	push := &v.events[current.index]
   298  	parent := &v.stack[top-1]
   299  
   300  	push.index = int32(len(v.events))              // make push event refer to pop
   301  	parent.typAccum |= current.typAccum | push.typ // accumulate type bits into parent
   302  
   303  	v.stack = v.stack[:top]
   304  
   305  	v.events = append(v.events, event{
   306  		node:   node,
   307  		typ:    current.typAccum,
   308  		index:  current.index,
   309  		parent: current.edgeKindAndIndex, // see [unpackEdgeKindAndIndex]
   310  	})
   311  }
   312  

View as plain text