golang.org/x/tools/gopls@v0.15.3/internal/util/astutil/purge_test.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package astutil_test
     6  
     7  import (
     8  	"go/ast"
     9  	"go/parser"
    10  	"go/token"
    11  	"os"
    12  	"reflect"
    13  	"testing"
    14  
    15  	"golang.org/x/tools/go/packages"
    16  	"golang.org/x/tools/gopls/internal/util/astutil"
    17  	"golang.org/x/tools/internal/testenv"
    18  )
    19  
    20  // TestPurgeFuncBodies tests PurgeFuncBodies by comparing it against a
    21  // (less efficient) reference implementation that purges after parsing.
    22  func TestPurgeFuncBodies(t *testing.T) {
    23  	testenv.NeedsGoBuild(t) // we need the source code for std
    24  
    25  	// Load a few standard packages.
    26  	config := packages.Config{Mode: packages.NeedCompiledGoFiles}
    27  	pkgs, err := packages.Load(&config, "encoding/...")
    28  	if err != nil {
    29  		t.Fatal(err)
    30  	}
    31  
    32  	// preorder returns the nodes of tree f in preorder.
    33  	preorder := func(f *ast.File) (nodes []ast.Node) {
    34  		ast.Inspect(f, func(n ast.Node) bool {
    35  			if n != nil {
    36  				nodes = append(nodes, n)
    37  			}
    38  			return true
    39  		})
    40  		return nodes
    41  	}
    42  
    43  	packages.Visit(pkgs, nil, func(p *packages.Package) {
    44  		for _, filename := range p.CompiledGoFiles {
    45  			content, err := os.ReadFile(filename)
    46  			if err != nil {
    47  				t.Fatal(err)
    48  			}
    49  
    50  			fset := token.NewFileSet()
    51  
    52  			// Parse then purge (reference implementation).
    53  			f1, _ := parser.ParseFile(fset, filename, content, 0)
    54  			ast.Inspect(f1, func(n ast.Node) bool {
    55  				switch n := n.(type) {
    56  				case *ast.FuncDecl:
    57  					if n.Body != nil {
    58  						n.Body.List = nil
    59  					}
    60  				case *ast.FuncLit:
    61  					n.Body.List = nil
    62  				case *ast.CompositeLit:
    63  					n.Elts = nil
    64  				}
    65  				return true
    66  			})
    67  
    68  			// Purge before parse (logic under test).
    69  			f2, _ := parser.ParseFile(fset, filename, astutil.PurgeFuncBodies(content), 0)
    70  
    71  			// Compare sequence of node types.
    72  			nodes1 := preorder(f1)
    73  			nodes2 := preorder(f2)
    74  			if len(nodes2) < len(nodes1) {
    75  				t.Errorf("purged file has fewer nodes: %d vs  %d",
    76  					len(nodes2), len(nodes1))
    77  				nodes1 = nodes1[:len(nodes2)] // truncate
    78  			}
    79  			for i := range nodes1 {
    80  				x, y := nodes1[i], nodes2[i]
    81  				if reflect.TypeOf(x) != reflect.TypeOf(y) {
    82  					t.Errorf("%s: got %T, want %T",
    83  						fset.Position(x.Pos()), y, x)
    84  					break
    85  				}
    86  			}
    87  		}
    88  	})
    89  }