blob: 33a2d32940da8bd778a3cc13d5db87c939970fc5 [file] [log] [blame]
// Copyright 2009 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 ast
import "fmt"
// 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 interface{}) (w Visitor)
}
func walkIdent(v Visitor, x *Ident) {
if x != nil {
Walk(v, x)
}
}
func walkCommentGroup(v Visitor, g *CommentGroup) {
if g != nil {
Walk(v, g)
}
}
func walkBlockStmt(v Visitor, b *BlockStmt) {
if b != nil {
Walk(v, b)
}
}
// Walk traverses an AST in depth-first order: If node != nil, it
// invokes v.Visit(node). If the visitor w returned by v.Visit(node) is
// not nil, Walk visits each of the children of node with the visitor w,
// followed by a call of w.Visit(nil).
//
// Walk may be called with any of the named ast node types. It also
// accepts arguments of type []*Field, []*Ident, []Expr, []Stmt and []Decl;
// the respective children are the slice elements.
//
func Walk(v Visitor, node interface{}) {
if node == nil {
return
}
if v = v.Visit(node); v == nil {
return
}
// walk children
// (the order of the cases matches the order
// of the corresponding declaration in ast.go)
switch n := node.(type) {
// Comments and fields
case *Comment:
// nothing to do
case *CommentGroup:
for _, c := range n.List {
Walk(v, c)
}
// TODO(gri): Keep comments in a list/vector instead
// of linking them via Next. Following next will lead
// to multiple visits and potentially n^2 behavior
// since Doc and Comments fields point into the global
// comments list.
case *Field:
walkCommentGroup(v, n.Doc)
Walk(v, n.Names)
Walk(v, n.Type)
for _, x := range n.Tag {
Walk(v, x)
}
walkCommentGroup(v, n.Comment)
// Expressions
case *BadExpr, *Ident, *Ellipsis, *BasicLit:
// nothing to do
case *StringList:
for _, x := range n.Strings {
Walk(v, x)
}
case *FuncLit:
if n != nil {
Walk(v, n.Type)
}
walkBlockStmt(v, n.Body)
case *CompositeLit:
Walk(v, n.Type)
Walk(v, n.Elts)
case *ParenExpr:
Walk(v, n.X)
case *SelectorExpr:
Walk(v, n.X)
walkIdent(v, n.Sel)
case *IndexExpr:
Walk(v, n.X)
Walk(v, n.Index)
case *SliceExpr:
Walk(v, n.X)
Walk(v, n.Index)
Walk(v, n.End)
case *TypeAssertExpr:
Walk(v, n.X)
Walk(v, n.Type)
case *CallExpr:
Walk(v, n.Fun)
Walk(v, n.Args)
case *StarExpr:
Walk(v, n.X)
case *UnaryExpr:
Walk(v, n.X)
case *BinaryExpr:
Walk(v, n.X)
Walk(v, n.Y)
case *KeyValueExpr:
Walk(v, n.Key)
Walk(v, n.Value)
// Types
case *ArrayType:
Walk(v, n.Len)
Walk(v, n.Elt)
case *StructType:
Walk(v, n.Fields)
case *FuncType:
Walk(v, n.Params)
Walk(v, n.Results)
case *InterfaceType:
Walk(v, n.Methods)
case *MapType:
Walk(v, n.Key)
Walk(v, n.Value)
case *ChanType:
Walk(v, n.Value)
// Statements
case *BadStmt:
// nothing to do
case *DeclStmt:
Walk(v, n.Decl)
case *EmptyStmt:
// nothing to do
case *LabeledStmt:
walkIdent(v, n.Label)
Walk(v, n.Stmt)
case *ExprStmt:
Walk(v, n.X)
case *IncDecStmt:
Walk(v, n.X)
case *AssignStmt:
Walk(v, n.Lhs)
Walk(v, n.Rhs)
case *GoStmt:
if n.Call != nil {
Walk(v, n.Call)
}
case *DeferStmt:
if n.Call != nil {
Walk(v, n.Call)
}
case *ReturnStmt:
Walk(v, n.Results)
case *BranchStmt:
walkIdent(v, n.Label)
case *BlockStmt:
Walk(v, n.List)
case *IfStmt:
Walk(v, n.Init)
Walk(v, n.Cond)
walkBlockStmt(v, n.Body)
Walk(v, n.Else)
case *CaseClause:
Walk(v, n.Values)
Walk(v, n.Body)
case *SwitchStmt:
Walk(v, n.Init)
Walk(v, n.Tag)
walkBlockStmt(v, n.Body)
case *TypeCaseClause:
Walk(v, n.Types)
Walk(v, n.Body)
case *TypeSwitchStmt:
Walk(v, n.Init)
Walk(v, n.Assign)
walkBlockStmt(v, n.Body)
case *CommClause:
Walk(v, n.Lhs)
Walk(v, n.Rhs)
Walk(v, n.Body)
case *SelectStmt:
walkBlockStmt(v, n.Body)
case *ForStmt:
Walk(v, n.Init)
Walk(v, n.Cond)
Walk(v, n.Post)
walkBlockStmt(v, n.Body)
case *RangeStmt:
Walk(v, n.Key)
Walk(v, n.Value)
Walk(v, n.X)
walkBlockStmt(v, n.Body)
// Declarations
case *ImportSpec:
walkCommentGroup(v, n.Doc)
walkIdent(v, n.Name)
for _, x := range n.Path {
Walk(v, x)
}
walkCommentGroup(v, n.Comment)
case *ValueSpec:
walkCommentGroup(v, n.Doc)
Walk(v, n.Names)
Walk(v, n.Type)
Walk(v, n.Values)
walkCommentGroup(v, n.Comment)
case *TypeSpec:
walkCommentGroup(v, n.Doc)
walkIdent(v, n.Name)
Walk(v, n.Type)
walkCommentGroup(v, n.Comment)
case *BadDecl:
// nothing to do
case *GenDecl:
walkCommentGroup(v, n.Doc)
for _, s := range n.Specs {
Walk(v, s)
}
case *FuncDecl:
walkCommentGroup(v, n.Doc)
if n.Recv != nil {
Walk(v, n.Recv)
}
walkIdent(v, n.Name)
if n.Type != nil {
Walk(v, n.Type)
}
walkBlockStmt(v, n.Body)
// Files and packages
case *File:
walkCommentGroup(v, n.Doc)
walkIdent(v, n.Name)
Walk(v, n.Decls)
walkCommentGroup(v, n.Comments)
case *Package:
for _, f := range n.Files {
Walk(v, f)
}
case []*Field:
for _, x := range n {
Walk(v, x)
}
case []*Ident:
for _, x := range n {
Walk(v, x)
}
case []Expr:
for _, x := range n {
Walk(v, x)
}
case []Stmt:
for _, x := range n {
Walk(v, x)
}
case []Decl:
for _, x := range n {
Walk(v, x)
}
default:
fmt.Printf("ast.Walk: unexpected type %T", n)
panic()
}
v.Visit(nil)
}