GoPLS Viewer

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