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