blob: b02584420488442a3e284d5b611fa025001bd59e [file] [log] [blame]
// Copyright 2012 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.
// This file implements syntax tree walking.
package syntax
import "fmt"
// Inspect traverses an AST in pre-order: It starts by calling
// f(node); node must not be nil. If f returns true, Inspect invokes f
// recursively for each of the non-nil children of node, followed by a
// call of f(nil).
//
// See Walk for caveats about shared nodes.
func Inspect(root Node, f func(Node) bool) {
Walk(root, inspector(f))
}
type inspector func(Node) bool
func (v inspector) Visit(node Node) Visitor {
if v(node) {
return v
}
return nil
}
// Crawl traverses a syntax in pre-order: It starts by calling f(root);
// root must not be nil. If f returns false (== "continue"), Crawl calls
// f recursively for each of the non-nil children of that node; if f
// returns true (== "stop"), Crawl does not traverse the respective node's
// children.
//
// See Walk for caveats about shared nodes.
//
// Deprecated: Use Inspect instead.
func Crawl(root Node, f func(Node) bool) {
Inspect(root, func(node Node) bool {
return node != nil && !f(node)
})
}
// Walk traverses an AST in pre-order: It starts by calling
// v.Visit(node); node must not be nil. If the visitor w returned by
// v.Visit(node) is not nil, Walk is invoked recursively with visitor
// w for each of the non-nil children of node, followed by a call of
// w.Visit(nil).
//
// Some nodes may be shared among multiple parent nodes (e.g., types in
// field lists such as type T in "a, b, c T"). Such shared nodes are
// walked multiple times.
// TODO(gri) Revisit this design. It may make sense to walk those nodes
// only once. A place where this matters is types2.TestResolveIdents.
func Walk(root Node, v Visitor) {
walker{v}.node(root)
}
// A Visitor's Visit method is invoked for each node encountered by Walk.
// If the result visitor w is not nil, Walk visits each of the children
// of node with the visitor w, followed by a call of w.Visit(nil).
type Visitor interface {
Visit(node Node) (w Visitor)
}
type walker struct {
v Visitor
}
func (w walker) node(n Node) {
if n == nil {
panic("nil node")
}
w.v = w.v.Visit(n)
if w.v == nil {
return
}
switch n := n.(type) {
// packages
case *File:
w.node(n.PkgName)
w.declList(n.DeclList)
// declarations
case *ImportDecl:
if n.LocalPkgName != nil {
w.node(n.LocalPkgName)
}
w.node(n.Path)
case *ConstDecl:
w.nameList(n.NameList)
if n.Type != nil {
w.node(n.Type)
}
if n.Values != nil {
w.node(n.Values)
}
case *TypeDecl:
w.node(n.Name)
w.fieldList(n.TParamList)
w.node(n.Type)
case *VarDecl:
w.nameList(n.NameList)
if n.Type != nil {
w.node(n.Type)
}
if n.Values != nil {
w.node(n.Values)
}
case *FuncDecl:
if n.Recv != nil {
w.node(n.Recv)
}
w.node(n.Name)
w.fieldList(n.TParamList)
w.node(n.Type)
if n.Body != nil {
w.node(n.Body)
}
// expressions
case *BadExpr: // nothing to do
case *Name: // nothing to do
case *BasicLit: // nothing to do
case *CompositeLit:
if n.Type != nil {
w.node(n.Type)
}
w.exprList(n.ElemList)
case *KeyValueExpr:
w.node(n.Key)
w.node(n.Value)
case *FuncLit:
w.node(n.Type)
w.node(n.Body)
case *ParenExpr:
w.node(n.X)
case *SelectorExpr:
w.node(n.X)
w.node(n.Sel)
case *IndexExpr:
w.node(n.X)
w.node(n.Index)
case *SliceExpr:
w.node(n.X)
for _, x := range n.Index {
if x != nil {
w.node(x)
}
}
case *AssertExpr:
w.node(n.X)
w.node(n.Type)
case *TypeSwitchGuard:
if n.Lhs != nil {
w.node(n.Lhs)
}
w.node(n.X)
case *Operation:
w.node(n.X)
if n.Y != nil {
w.node(n.Y)
}
case *CallExpr:
w.node(n.Fun)
w.exprList(n.ArgList)
case *ListExpr:
w.exprList(n.ElemList)
// types
case *ArrayType:
if n.Len != nil {
w.node(n.Len)
}
w.node(n.Elem)
case *SliceType:
w.node(n.Elem)
case *DotsType:
w.node(n.Elem)
case *StructType:
w.fieldList(n.FieldList)
for _, t := range n.TagList {
if t != nil {
w.node(t)
}
}
case *Field:
if n.Name != nil {
w.node(n.Name)
}
w.node(n.Type)
case *InterfaceType:
w.fieldList(n.MethodList)
case *FuncType:
w.fieldList(n.ParamList)
w.fieldList(n.ResultList)
case *MapType:
w.node(n.Key)
w.node(n.Value)
case *ChanType:
w.node(n.Elem)
// statements
case *EmptyStmt: // nothing to do
case *LabeledStmt:
w.node(n.Label)
w.node(n.Stmt)
case *BlockStmt:
w.stmtList(n.List)
case *ExprStmt:
w.node(n.X)
case *SendStmt:
w.node(n.Chan)
w.node(n.Value)
case *DeclStmt:
w.declList(n.DeclList)
case *AssignStmt:
w.node(n.Lhs)
if n.Rhs != nil {
w.node(n.Rhs)
}
case *BranchStmt:
if n.Label != nil {
w.node(n.Label)
}
// Target points to nodes elsewhere in the syntax tree
case *CallStmt:
w.node(n.Call)
case *ReturnStmt:
if n.Results != nil {
w.node(n.Results)
}
case *IfStmt:
if n.Init != nil {
w.node(n.Init)
}
w.node(n.Cond)
w.node(n.Then)
if n.Else != nil {
w.node(n.Else)
}
case *ForStmt:
if n.Init != nil {
w.node(n.Init)
}
if n.Cond != nil {
w.node(n.Cond)
}
if n.Post != nil {
w.node(n.Post)
}
w.node(n.Body)
case *SwitchStmt:
if n.Init != nil {
w.node(n.Init)
}
if n.Tag != nil {
w.node(n.Tag)
}
for _, s := range n.Body {
w.node(s)
}
case *SelectStmt:
for _, s := range n.Body {
w.node(s)
}
// helper nodes
case *RangeClause:
if n.Lhs != nil {
w.node(n.Lhs)
}
w.node(n.X)
case *CaseClause:
if n.Cases != nil {
w.node(n.Cases)
}
w.stmtList(n.Body)
case *CommClause:
if n.Comm != nil {
w.node(n.Comm)
}
w.stmtList(n.Body)
default:
panic(fmt.Sprintf("internal error: unknown node type %T", n))
}
w.v.Visit(nil)
}
func (w walker) declList(list []Decl) {
for _, n := range list {
w.node(n)
}
}
func (w walker) exprList(list []Expr) {
for _, n := range list {
w.node(n)
}
}
func (w walker) stmtList(list []Stmt) {
for _, n := range list {
w.node(n)
}
}
func (w walker) nameList(list []*Name) {
for _, n := range list {
w.node(n)
}
}
func (w walker) fieldList(list []*Field) {
for _, n := range list {
w.node(n)
}
}