]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile/internal/syntax: go/ast-style walk API
authorMatthew Dempsky <mdempsky@google.com>
Wed, 23 Jun 2021 19:08:42 +0000 (12:08 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Wed, 23 Jun 2021 22:23:16 +0000 (22:23 +0000)
This CL adds go/ast's Visitor, Walk, and Inspect functions to package
syntax. Having functions with the same API and semantics as their
go/ast counterparts reduces the mental load of context switching
between go/ast and syntax.

It also renames the existing Walk function into Crawl, and marks it as
a deprecated wrapper around Inspect. (I named it "Crawl" because it's
less functional than "Walk"... get it??)

There aren't that many callers to Crawl, so we can probably remove it
in the future. But it doesn't seem pressing, and I'm more concerned
about the risk of forgetting to invert a bool condition somewhere.

Change-Id: Ib2fb275873a1d1a730249c9cb584864cb6ec370e
Reviewed-on: https://go-review.googlesource.com/c/go/+/330429
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/cmd/compile/internal/noder/irgen.go
src/cmd/compile/internal/noder/quirks.go
src/cmd/compile/internal/noder/writer.go
src/cmd/compile/internal/syntax/walk.go
src/cmd/compile/internal/types2/errorcalls_test.go
src/cmd/compile/internal/types2/issues_test.go
src/cmd/compile/internal/types2/resolver_test.go

index aac8b5e6418f94a96fe88ff640dfeee4288c4510..9d14b06d3c865ed24025c00445bc7919788382ce 100644 (file)
@@ -191,7 +191,7 @@ Outer:
 
                // Double check for any type-checking inconsistencies. This can be
                // removed once we're confident in IR generation results.
-               syntax.Walk(p.file, func(n syntax.Node) bool {
+               syntax.Crawl(p.file, func(n syntax.Node) bool {
                        g.validate(n)
                        return false
                })
index 91b4c22025b8393e7557634a8c7212ef2912aeab..914c5d2bd7115663dd34df2833e12dd86fa7bdb0 100644 (file)
@@ -36,7 +36,7 @@ func posBasesOf(noders []*noder) []*syntax.PosBase {
        var bases []*syntax.PosBase
 
        for _, p := range noders {
-               syntax.Walk(p.file, func(n syntax.Node) bool {
+               syntax.Crawl(p.file, func(n syntax.Node) bool {
                        if b := n.Pos().Base(); !seen[b] {
                                bases = append(bases, b)
                                seen[b] = true
@@ -74,7 +74,7 @@ func importedObjsOf(curpkg *types2.Package, info *types2.Info, noders []*noder)
        }
 
        for _, p := range noders {
-               syntax.Walk(p.file, func(n syntax.Node) bool {
+               syntax.Crawl(p.file, func(n syntax.Node) bool {
                        switch n := n.(type) {
                        case *syntax.ConstDecl:
                                assoc(n, n.NameList...)
@@ -167,7 +167,7 @@ func importedObjsOf(curpkg *types2.Package, info *types2.Info, noders []*noder)
                if n == nil {
                        return
                }
-               syntax.Walk(n, func(n syntax.Node) bool {
+               syntax.Crawl(n, func(n syntax.Node) bool {
                        switch n := n.(type) {
                        case *syntax.Name:
                                checkdef(n)
@@ -237,7 +237,7 @@ func importedObjsOf(curpkg *types2.Package, info *types2.Info, noders []*noder)
                        }
 
                        if phase >= 5 {
-                               syntax.Walk(p.file, func(n syntax.Node) bool {
+                               syntax.Crawl(p.file, func(n syntax.Node) bool {
                                        if name, ok := n.(*syntax.Name); ok {
                                                if obj, ok := info.Uses[name]; ok {
                                                        resolveObj(name.Pos(), obj)
index cc749b0d1e52269f44b2e88cf4067102f69311a5..bc89e1a262ae34816bd25c5b5111e74ce3dfee50 100644 (file)
@@ -1318,7 +1318,7 @@ func (w *writer) captureVars(expr *syntax.FuncLit) (closureVars []posObj, locals
                        // function literal as the position of the intermediary capture.
                        if quirksMode() && rbracePos == (syntax.Pos{}) {
                                rbracePos = n.Body.Rbrace
-                               syntax.Walk(n.Body, visitor)
+                               syntax.Crawl(n.Body, visitor)
                                rbracePos = syntax.Pos{}
                                return true
                        }
@@ -1327,17 +1327,17 @@ func (w *writer) captureVars(expr *syntax.FuncLit) (closureVars []posObj, locals
                        // Quirk: typecheck visits (and thus captures) the RHS of
                        // assignment statements before the LHS.
                        if quirksMode() && (n.Op == 0 || n.Op == syntax.Def) {
-                               syntax.Walk(n.Rhs, visitor)
-                               syntax.Walk(n.Lhs, visitor)
+                               syntax.Crawl(n.Rhs, visitor)
+                               syntax.Crawl(n.Lhs, visitor)
                                return true
                        }
                case *syntax.RangeClause:
                        // Quirk: Similarly, it visits the expression to be iterated
                        // over before the iteration variables.
                        if quirksMode() {
-                               syntax.Walk(n.X, visitor)
+                               syntax.Crawl(n.X, visitor)
                                if n.Lhs != nil {
-                                       syntax.Walk(n.Lhs, visitor)
+                                       syntax.Crawl(n.Lhs, visitor)
                                }
                                return true
                        }
@@ -1345,7 +1345,7 @@ func (w *writer) captureVars(expr *syntax.FuncLit) (closureVars []posObj, locals
 
                return false
        }
-       syntax.Walk(expr.Body, visitor)
+       syntax.Crawl(expr.Body, visitor)
 
        return
 }
@@ -1392,7 +1392,7 @@ func (pw *pkgWriter) collectDecls(noders []*noder) {
        for _, p := range noders {
                var importedEmbed, importedUnsafe bool
 
-               syntax.Walk(p.file, func(n syntax.Node) bool {
+               syntax.Crawl(p.file, func(n syntax.Node) bool {
                        switch n := n.(type) {
                        case *syntax.File:
                                pw.checkPragmas(n.Pragma, ir.GoBuildPragma, false)
index c26e97a0d8f88771986be07a4a300ff98b993f4e..ef213daf7d124c77597bc7ddc43212bab6661880 100644 (file)
@@ -8,31 +8,73 @@ package syntax
 
 import "fmt"
 
-// Walk traverses a syntax in pre-order: It starts by calling f(root);
-// root must not be nil. If f returns false (== "continue"), Walk calls
+// 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"), Walk does not traverse the respective node's
+// 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, f func(Node) bool) {
-       w := walker{f}
-       w.node(root)
+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 {
-       f func(Node) bool
+       v Visitor
 }
 
-func (w *walker) node(n Node) {
+func (w walker) node(n Node) {
        if n == nil {
                panic("invalid syntax tree: nil node")
        }
 
-       if w.f(n) {
+       w.v = w.v.Visit(n)
+       if w.v == nil {
                return
        }
 
@@ -285,33 +327,35 @@ func (w *walker) node(n Node) {
        default:
                panic(fmt.Sprintf("internal error: unknown node type %T", n))
        }
+
+       w.v.Visit(nil)
 }
 
-func (w *walker) declList(list []Decl) {
+func (w walker) declList(list []Decl) {
        for _, n := range list {
                w.node(n)
        }
 }
 
-func (w *walker) exprList(list []Expr) {
+func (w walker) exprList(list []Expr) {
        for _, n := range list {
                w.node(n)
        }
 }
 
-func (w *walker) stmtList(list []Stmt) {
+func (w walker) stmtList(list []Stmt) {
        for _, n := range list {
                w.node(n)
        }
 }
 
-func (w *walker) nameList(list []*Name) {
+func (w walker) nameList(list []*Name) {
        for _, n := range list {
                w.node(n)
        }
 }
 
-func (w *walker) fieldList(list []*Field) {
+func (w walker) fieldList(list []*Field) {
        for _, n := range list {
                w.node(n)
        }
index 28bb33aaffd00443da31ca7086caa1e8cfba37f2..80b05f9f0f06883022a9333d84d93c233f4c7294 100644 (file)
@@ -18,7 +18,7 @@ func TestErrorCalls(t *testing.T) {
        }
 
        for _, file := range files {
-               syntax.Walk(file, func(n syntax.Node) bool {
+               syntax.Crawl(file, func(n syntax.Node) bool {
                        call, _ := n.(*syntax.CallExpr)
                        if call == nil {
                                return false
index e716a48038510a2320c02524c4375d0bdaa9ef6f..aafe8de3676efde8c56d81321865f915e33512af 100644 (file)
@@ -321,7 +321,7 @@ func TestIssue25627(t *testing.T) {
                        }
                }
 
-               syntax.Walk(f, func(n syntax.Node) bool {
+               syntax.Crawl(f, func(n syntax.Node) bool {
                        if decl, _ := n.(*syntax.TypeDecl); decl != nil {
                                if tv, ok := info.Types[decl.Type]; ok && decl.Name.Value == "T" {
                                        want := strings.Count(src, ";") + 1
index aee435ff5fb6cd7e0aa6d0d34028290197e71968..a02abce081b8334e3bb57bb0444ff6bced209e79 100644 (file)
@@ -143,7 +143,7 @@ func TestResolveIdents(t *testing.T) {
 
        // check that qualified identifiers are resolved
        for _, f := range files {
-               syntax.Walk(f, func(n syntax.Node) bool {
+               syntax.Crawl(f, func(n syntax.Node) bool {
                        if s, ok := n.(*syntax.SelectorExpr); ok {
                                if x, ok := s.X.(*syntax.Name); ok {
                                        obj := uses[x]
@@ -177,7 +177,7 @@ func TestResolveIdents(t *testing.T) {
        foundDefs := make(map[*syntax.Name]bool)
        var both []string
        for _, f := range files {
-               syntax.Walk(f, func(n syntax.Node) bool {
+               syntax.Crawl(f, func(n syntax.Node) bool {
                        if x, ok := n.(*syntax.Name); ok {
                                var objects int
                                if _, found := uses[x]; found {