github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/astutil/enclosing_test.go (about)

     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/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  //
    43  func findInterval(t *testing.T, fset *token.FileSet, input, substr string) (f *ast.File, start, end token.Pos) {
    44  	f, err := parser.ParseFile(fset, "<input>", input, 0)
    45  	if err != nil {
    46  		t.Errorf("parse error: %s", err)
    47  		return
    48  	}
    49  
    50  	i := strings.Index(input, substr)
    51  	if i < 0 {
    52  		t.Errorf("%q is not a substring of input", substr)
    53  		f = nil
    54  		return
    55  	}
    56  
    57  	filePos := fset.File(f.Package)
    58  	return f, filePos.Pos(i), filePos.Pos(i + len(substr))
    59  }
    60  
    61  // Common input for following tests.
    62  const input = `
    63  // Hello.
    64  package main
    65  import "fmt"
    66  func f() {}
    67  func main() {
    68  	z := (x + y) // add them
    69          f() // NB: ExprStmt and its CallExpr have same Pos/End
    70  }
    71  `
    72  
    73  func TestPathEnclosingInterval_Exact(t *testing.T) {
    74  	// For the exact tests, we check that a substring is mapped to
    75  	// the canonical string for the node it denotes.
    76  	tests := []struct {
    77  		substr string // first occurrence of this string indicates interval
    78  		node   string // complete text of expected containing node
    79  	}{
    80  		{"package",
    81  			input[11 : len(input)-1]},
    82  		{"\npack",
    83  			input[11 : len(input)-1]},
    84  		{"main",
    85  			"main"},
    86  		{"import",
    87  			"import \"fmt\""},
    88  		{"\"fmt\"",
    89  			"\"fmt\""},
    90  		{"\nfunc f() {}\n",
    91  			"func f() {}"},
    92  		{"x ",
    93  			"x"},
    94  		{" y",
    95  			"y"},
    96  		{"z",
    97  			"z"},
    98  		{" + ",
    99  			"x + y"},
   100  		{" :=",
   101  			"z := (x + y)"},
   102  		{"x + y",
   103  			"x + y"},
   104  		{"(x + y)",
   105  			"(x + y)"},
   106  		{" (x + y) ",
   107  			"(x + y)"},
   108  		{" (x + y) // add",
   109  			"(x + y)"},
   110  		{"func",
   111  			"func f() {}"},
   112  		{"func f() {}",
   113  			"func f() {}"},
   114  		{"\nfun",
   115  			"func f() {}"},
   116  		{" f",
   117  			"f"},
   118  	}
   119  	for _, test := range tests {
   120  		f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
   121  		if f == nil {
   122  			continue
   123  		}
   124  
   125  		path, exact := astutil.PathEnclosingInterval(f, start, end)
   126  		if !exact {
   127  			t.Errorf("PathEnclosingInterval(%q) not exact", test.substr)
   128  			continue
   129  		}
   130  
   131  		if len(path) == 0 {
   132  			if test.node != "" {
   133  				t.Errorf("PathEnclosingInterval(%q).path: got [], want %q",
   134  					test.substr, test.node)
   135  			}
   136  			continue
   137  		}
   138  
   139  		if got := input[path[0].Pos():path[0].End()]; got != test.node {
   140  			t.Errorf("PathEnclosingInterval(%q): got %q, want %q (path was %s)",
   141  				test.substr, got, test.node, pathToString(path))
   142  			continue
   143  		}
   144  	}
   145  }
   146  
   147  func TestPathEnclosingInterval_Paths(t *testing.T) {
   148  	// For these tests, we check only the path of the enclosing
   149  	// node, but not its complete text because it's often quite
   150  	// large when !exact.
   151  	tests := []struct {
   152  		substr string // first occurrence of this string indicates interval
   153  		path   string // the pathToString(),exact of the expected path
   154  	}{
   155  		{"// add",
   156  			"[BlockStmt FuncDecl File],false"},
   157  		{"(x + y",
   158  			"[ParenExpr AssignStmt BlockStmt FuncDecl File],false"},
   159  		{"x +",
   160  			"[BinaryExpr ParenExpr AssignStmt BlockStmt FuncDecl File],false"},
   161  		{"z := (x",
   162  			"[AssignStmt BlockStmt FuncDecl File],false"},
   163  		{"func f",
   164  			"[FuncDecl File],false"},
   165  		{"func f()",
   166  			"[FuncDecl File],false"},
   167  		{" f()",
   168  			"[FuncDecl File],false"},
   169  		{"() {}",
   170  			"[FuncDecl File],false"},
   171  		{"// Hello",
   172  			"[File],false"},
   173  		{" f",
   174  			"[Ident FuncDecl File],true"},
   175  		{"func ",
   176  			"[FuncDecl File],true"},
   177  		{"mai",
   178  			"[Ident File],true"},
   179  		{"f() // NB",
   180  			"[CallExpr ExprStmt BlockStmt FuncDecl File],true"},
   181  	}
   182  	for _, test := range tests {
   183  		f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
   184  		if f == nil {
   185  			continue
   186  		}
   187  
   188  		path, exact := astutil.PathEnclosingInterval(f, start, end)
   189  		if got := fmt.Sprintf("%s,%v", pathToString(path), exact); got != test.path {
   190  			t.Errorf("PathEnclosingInterval(%q): got %q, want %q",
   191  				test.substr, got, test.path)
   192  			continue
   193  		}
   194  	}
   195  }