| // 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) |
| } |