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 }