...

Source file src/golang.org/x/tools/go/ast/astutil/enclosing_test.go

Documentation: golang.org/x/tools/go/ast/astutil

     1  // Copyright 2013 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  // This file defines tests of PathEnclosingInterval.
     8  
     9  // TODO(adonovan): exhaustive tests that run over the whole input
    10  // tree, not just handcrafted examples.
    11  
    12  import (
    13  	"bytes"
    14  	"fmt"
    15  	"go/ast"
    16  	"go/parser"
    17  	"go/token"
    18  	"strings"
    19  	"testing"
    20  
    21  	"golang.org/x/tools/go/ast/astutil"
    22  )
    23  
    24  // pathToString returns a string containing the concrete types of the
    25  // nodes in path.
    26  func pathToString(path []ast.Node) string {
    27  	var buf bytes.Buffer
    28  	fmt.Fprint(&buf, "[")
    29  	for i, n := range path {
    30  		if i > 0 {
    31  			fmt.Fprint(&buf, " ")
    32  		}
    33  		fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast."))
    34  	}
    35  	fmt.Fprint(&buf, "]")
    36  	return buf.String()
    37  }
    38  
    39  // findInterval parses input and returns the [start, end) positions of
    40  // the first occurrence of substr in input.  f==nil indicates failure;
    41  // an error has already been reported in that case.
    42  func findInterval(t *testing.T, fset *token.FileSet, input, substr string) (f *ast.File, start, end token.Pos) {
    43  	f, err := parser.ParseFile(fset, "<input>", input, 0)
    44  	if err != nil {
    45  		t.Errorf("parse error: %s", err)
    46  		return
    47  	}
    48  
    49  	i := strings.Index(input, substr)
    50  	if i < 0 {
    51  		t.Errorf("%q is not a substring of input", substr)
    52  		f = nil
    53  		return
    54  	}
    55  
    56  	filePos := fset.File(f.Package)
    57  	return f, filePos.Pos(i), filePos.Pos(i + len(substr))
    58  }
    59  
    60  // Common input for following tests.
    61  const input = `
    62  // Hello.
    63  package main
    64  import "fmt"
    65  func f() {}
    66  func main() {
    67  	z := (x + y) // add them
    68          f() // NB: ExprStmt and its CallExpr have same Pos/End
    69  }
    70  
    71  func g[A any, P interface{ctype1| ~ctype2}](a1 A, p1 P) {}
    72  
    73  type PT[T constraint] struct{ t T }
    74  
    75  var v GT[targ1]
    76  
    77  var h = g[ targ2, targ3]
    78  `
    79  
    80  func TestPathEnclosingInterval_Exact(t *testing.T) {
    81  	type testCase struct {
    82  		substr string // first occurrence of this string indicates interval
    83  		node   string // complete text of expected containing node
    84  	}
    85  
    86  	dup := func(s string) testCase { return testCase{s, s} }
    87  	// For the exact tests, we check that a substring is mapped to
    88  	// the canonical string for the node it denotes.
    89  	tests := []testCase{
    90  		{"package",
    91  			input[11 : len(input)-1]},
    92  		{"\npack",
    93  			input[11 : len(input)-1]},
    94  		dup("main"),
    95  		{"import",
    96  			"import \"fmt\""},
    97  		dup("\"fmt\""),
    98  		{"\nfunc f() {}\n",
    99  			"func f() {}"},
   100  		{"x ",
   101  			"x"},
   102  		{" y",
   103  			"y"},
   104  		dup("z"),
   105  		{" + ",
   106  			"x + y"},
   107  		{" :=",
   108  			"z := (x + y)"},
   109  		dup("x + y"),
   110  		dup("(x + y)"),
   111  		{" (x + y) ",
   112  			"(x + y)"},
   113  		{" (x + y) // add",
   114  			"(x + y)"},
   115  		{"func",
   116  			"func f() {}"},
   117  		dup("func f() {}"),
   118  		{"\nfun",
   119  			"func f() {}"},
   120  		{" f",
   121  			"f"},
   122  		dup("[A any, P interface{ctype1| ~ctype2}]"),
   123  		{"[", "[A any, P interface{ctype1| ~ctype2}]"},
   124  		dup("A"),
   125  		{" any", "any"},
   126  		dup("ctype1"),
   127  		{"|", "ctype1| ~ctype2"},
   128  		dup("ctype2"),
   129  		{"~", "~ctype2"},
   130  		dup("~ctype2"),
   131  		{" ~ctype2", "~ctype2"},
   132  		{"]", "[A any, P interface{ctype1| ~ctype2}]"},
   133  		dup("a1"),
   134  		dup("a1 A"),
   135  		dup("(a1 A, p1 P)"),
   136  		dup("type PT[T constraint] struct{ t T }"),
   137  		dup("PT"),
   138  		dup("[T constraint]"),
   139  		dup("constraint"),
   140  		dup("targ1"),
   141  		{" targ2", "targ2"},
   142  		dup("g[ targ2, targ3]"),
   143  	}
   144  	for _, test := range tests {
   145  		f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
   146  		if f == nil {
   147  			continue
   148  		}
   149  
   150  		path, exact := astutil.PathEnclosingInterval(f, start, end)
   151  		if !exact {
   152  			t.Errorf("PathEnclosingInterval(%q) not exact", test.substr)
   153  			continue
   154  		}
   155  
   156  		if len(path) == 0 {
   157  			if test.node != "" {
   158  				t.Errorf("PathEnclosingInterval(%q).path: got [], want %q",
   159  					test.substr, test.node)
   160  			}
   161  			continue
   162  		}
   163  
   164  		if got := input[path[0].Pos():path[0].End()]; got != test.node {
   165  			t.Errorf("PathEnclosingInterval(%q): got %q, want %q (path was %s)",
   166  				test.substr, got, test.node, pathToString(path))
   167  			continue
   168  		}
   169  	}
   170  }
   171  
   172  func TestPathEnclosingInterval_Paths(t *testing.T) {
   173  	type testCase struct {
   174  		substr string // first occurrence of this string indicates interval
   175  		path   string // the pathToString(),exact of the expected path
   176  	}
   177  	// For these tests, we check only the path of the enclosing
   178  	// node, but not its complete text because it's often quite
   179  	// large when !exact.
   180  	tests := []testCase{
   181  		{"// add",
   182  			"[BlockStmt FuncDecl File],false"},
   183  		{"(x + y",
   184  			"[ParenExpr AssignStmt BlockStmt FuncDecl File],false"},
   185  		{"x +",
   186  			"[BinaryExpr ParenExpr AssignStmt BlockStmt FuncDecl File],false"},
   187  		{"z := (x",
   188  			"[AssignStmt BlockStmt FuncDecl File],false"},
   189  		{"func f",
   190  			"[FuncDecl File],false"},
   191  		{"func f()",
   192  			"[FuncDecl File],false"},
   193  		{" f()",
   194  			"[FuncDecl File],false"},
   195  		{"() {}",
   196  			"[FuncDecl File],false"},
   197  		{"// Hello",
   198  			"[File],false"},
   199  		{" f",
   200  			"[Ident FuncDecl File],true"},
   201  		{"func ",
   202  			"[FuncDecl File],true"},
   203  		{"mai",
   204  			"[Ident File],true"},
   205  		{"f() // NB",
   206  			"[CallExpr ExprStmt BlockStmt FuncDecl File],true"},
   207  		{" any", "[Ident Field FieldList FuncDecl File],true"},
   208  		{"|", "[BinaryExpr Field FieldList InterfaceType Field FieldList FuncDecl File],true"},
   209  		{"ctype2",
   210  			"[Ident UnaryExpr BinaryExpr Field FieldList InterfaceType Field FieldList FuncDecl File],true"},
   211  		{"a1", "[Ident Field FieldList FuncDecl File],true"},
   212  		{"PT[T constraint]", "[TypeSpec GenDecl File],false"},
   213  		{"[T constraint]", "[FieldList TypeSpec GenDecl File],true"},
   214  		{"targ2", "[Ident IndexListExpr ValueSpec GenDecl File],true"},
   215  	}
   216  	for _, test := range tests {
   217  		f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
   218  		if f == nil {
   219  			continue
   220  		}
   221  
   222  		path, exact := astutil.PathEnclosingInterval(f, start, end)
   223  		if got := fmt.Sprintf("%s,%v", pathToString(path), exact); got != test.path {
   224  			t.Errorf("PathEnclosingInterval(%q): got %q, want %q",
   225  				test.substr, got, test.path)
   226  			continue
   227  		}
   228  	}
   229  }
   230  

View as plain text