blob: 1a46c878663478c02673baaf07172f64577c15ef [file] [log] [blame]
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package parser
import (
"bytes"
"fmt"
"go/ast"
"go/token"
"io/fs"
"runtime"
"strings"
"testing"
)
var validFiles = []string{
"parser.go",
"parser_test.go",
"error_test.go",
"short_test.go",
}
func TestParse(t *testing.T) {
for _, filename := range validFiles {
_, err := ParseFile(token.NewFileSet(), filename, nil, DeclarationErrors)
if err != nil {
t.Fatalf("ParseFile(%s): %v", filename, err)
}
}
}
func nameFilter(filename string) bool {
switch filename {
case "parser.go", "interface.go", "parser_test.go":
return true
case "parser.go.orig":
return true // permit but should be ignored by ParseDir
}
return false
}
func dirFilter(f fs.FileInfo) bool { return nameFilter(f.Name()) }
func TestParseFile(t *testing.T) {
src := "package p\nvar _=s[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]"
_, err := ParseFile(token.NewFileSet(), "", src, 0)
if err == nil {
t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
}
}
func TestParseExprFrom(t *testing.T) {
src := "s[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]"
_, err := ParseExprFrom(token.NewFileSet(), "", src, 0)
if err == nil {
t.Errorf("ParseExprFrom(%s) succeeded unexpectedly", src)
}
}
func TestParseDir(t *testing.T) {
path := "."
pkgs, err := ParseDir(token.NewFileSet(), path, dirFilter, 0)
if err != nil {
t.Fatalf("ParseDir(%s): %v", path, err)
}
if n := len(pkgs); n != 1 {
t.Errorf("got %d packages; want 1", n)
}
pkg := pkgs["parser"]
if pkg == nil {
t.Errorf(`package "parser" not found`)
return
}
if n := len(pkg.Files); n != 3 {
t.Errorf("got %d package files; want 3", n)
}
for filename := range pkg.Files {
if !nameFilter(filename) {
t.Errorf("unexpected package file: %s", filename)
}
}
}
func TestIssue42951(t *testing.T) {
path := "./testdata/issue42951"
_, err := ParseDir(token.NewFileSet(), path, nil, 0)
if err != nil {
t.Errorf("ParseDir(%s): %v", path, err)
}
}
func TestParseExpr(t *testing.T) {
// just kicking the tires:
// a valid arithmetic expression
src := "a + b"
x, err := ParseExpr(src)
if err != nil {
t.Errorf("ParseExpr(%q): %v", src, err)
}
// sanity check
if _, ok := x.(*ast.BinaryExpr); !ok {
t.Errorf("ParseExpr(%q): got %T, want *ast.BinaryExpr", src, x)
}
// a valid type expression
src = "struct{x *int}"
x, err = ParseExpr(src)
if err != nil {
t.Errorf("ParseExpr(%q): %v", src, err)
}
// sanity check
if _, ok := x.(*ast.StructType); !ok {
t.Errorf("ParseExpr(%q): got %T, want *ast.StructType", src, x)
}
// an invalid expression
src = "a + *"
x, err = ParseExpr(src)
if err == nil {
t.Errorf("ParseExpr(%q): got no error", src)
}
if x == nil {
t.Errorf("ParseExpr(%q): got no (partial) result", src)
}
if _, ok := x.(*ast.BinaryExpr); !ok {
t.Errorf("ParseExpr(%q): got %T, want *ast.BinaryExpr", src, x)
}
// a valid expression followed by extra tokens is invalid
src = "a[i] := x"
if _, err := ParseExpr(src); err == nil {
t.Errorf("ParseExpr(%q): got no error", src)
}
// a semicolon is not permitted unless automatically inserted
src = "a + b\n"
if _, err := ParseExpr(src); err != nil {
t.Errorf("ParseExpr(%q): got error %s", src, err)
}
src = "a + b;"
if _, err := ParseExpr(src); err == nil {
t.Errorf("ParseExpr(%q): got no error", src)
}
// various other stuff following a valid expression
const validExpr = "a + b"
const anything = "dh3*#D)#_"
for _, c := range "!)]};," {
src := validExpr + string(c) + anything
if _, err := ParseExpr(src); err == nil {
t.Errorf("ParseExpr(%q): got no error", src)
}
}
// ParseExpr must not crash
for _, src := range valids {
ParseExpr(src)
}
}
func TestColonEqualsScope(t *testing.T) {
f, err := ParseFile(token.NewFileSet(), "", `package p; func f() { x, y, z := x, y, z }`, 0)
if err != nil {
t.Fatal(err)
}
// RHS refers to undefined globals; LHS does not.
as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.AssignStmt)
for _, v := range as.Rhs {
id := v.(*ast.Ident)
if id.Obj != nil {
t.Errorf("rhs %s has Obj, should not", id.Name)
}
}
for _, v := range as.Lhs {
id := v.(*ast.Ident)
if id.Obj == nil {
t.Errorf("lhs %s does not have Obj, should", id.Name)
}
}
}
func TestVarScope(t *testing.T) {
f, err := ParseFile(token.NewFileSet(), "", `package p; func f() { var x, y, z = x, y, z }`, 0)
if err != nil {
t.Fatal(err)
}
// RHS refers to undefined globals; LHS does not.
as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.DeclStmt).Decl.(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
for _, v := range as.Values {
id := v.(*ast.Ident)
if id.Obj != nil {
t.Errorf("rhs %s has Obj, should not", id.Name)
}
}
for _, id := range as.Names {
if id.Obj == nil {
t.Errorf("lhs %s does not have Obj, should", id.Name)
}
}
}
func TestObjects(t *testing.T) {
const src = `
package p
import fmt "fmt"
const pi = 3.14
type T struct{}
var x int
func f() { L: }
`
f, err := ParseFile(token.NewFileSet(), "", src, 0)
if err != nil {
t.Fatal(err)
}
objects := map[string]ast.ObjKind{
"p": ast.Bad, // not in a scope
"fmt": ast.Bad, // not resolved yet
"pi": ast.Con,
"T": ast.Typ,
"x": ast.Var,
"int": ast.Bad, // not resolved yet
"f": ast.Fun,
"L": ast.Lbl,
}
ast.Inspect(f, func(n ast.Node) bool {
if ident, ok := n.(*ast.Ident); ok {
obj := ident.Obj
if obj == nil {
if objects[ident.Name] != ast.Bad {
t.Errorf("no object for %s", ident.Name)
}
return true
}
if obj.Name != ident.Name {
t.Errorf("names don't match: obj.Name = %s, ident.Name = %s", obj.Name, ident.Name)
}
kind := objects[ident.Name]
if obj.Kind != kind {
t.Errorf("%s: obj.Kind = %s; want %s", ident.Name, obj.Kind, kind)
}
}
return true
})
}
func TestUnresolved(t *testing.T) {
f, err := ParseFile(token.NewFileSet(), "", `
package p
//
func f1a(int)
func f2a(byte, int, float)
func f3a(a, b int, c float)
func f4a(...complex)
func f5a(a s1a, b ...complex)
//
func f1b(*int)
func f2b([]byte, (int), *float)
func f3b(a, b *int, c []float)
func f4b(...*complex)
func f5b(a s1a, b ...[]complex)
//
type s1a struct { int }
type s2a struct { byte; int; s1a }
type s3a struct { a, b int; c float }
//
type s1b struct { *int }
type s2b struct { byte; int; *float }
type s3b struct { a, b *s3b; c []float }
`, 0)
if err != nil {
t.Fatal(err)
}
want := "int " + // f1a
"byte int float " + // f2a
"int float " + // f3a
"complex " + // f4a
"complex " + // f5a
//
"int " + // f1b
"byte int float " + // f2b
"int float " + // f3b
"complex " + // f4b
"complex " + // f5b
//
"int " + // s1a
"byte int " + // s2a
"int float " + // s3a
//
"int " + // s1a
"byte int float " + // s2a
"float " // s3a
// collect unresolved identifiers
var buf bytes.Buffer
for _, u := range f.Unresolved {
buf.WriteString(u.Name)
buf.WriteByte(' ')
}
got := buf.String()
if got != want {
t.Errorf("\ngot: %s\nwant: %s", got, want)
}
}
var imports = map[string]bool{
`"a"`: true,
"`a`": true,
`"a/b"`: true,
`"a.b"`: true,
`"m\x61th"`: true,
`"greek/αβ"`: true,
`""`: false,
// Each of these pairs tests both `` vs "" strings
// and also use of invalid characters spelled out as
// escape sequences and written directly.
// For example `"\x00"` tests import "\x00"
// while "`\x00`" tests import `<actual-NUL-byte>`.
`"\x00"`: false,
"`\x00`": false,
`"\x7f"`: false,
"`\x7f`": false,
`"a!"`: false,
"`a!`": false,
`"a b"`: false,
"`a b`": false,
`"a\\b"`: false,
"`a\\b`": false,
"\"`a`\"": false,
"`\"a\"`": false,
`"\x80\x80"`: false,
"`\x80\x80`": false,
`"\xFFFD"`: false,
"`\xFFFD`": false,
}
func TestImports(t *testing.T) {
for path, isValid := range imports {
src := fmt.Sprintf("package p; import %s", path)
_, err := ParseFile(token.NewFileSet(), "", src, 0)
switch {
case err != nil && isValid:
t.Errorf("ParseFile(%s): got %v; expected no error", src, err)
case err == nil && !isValid:
t.Errorf("ParseFile(%s): got no error; expected one", src)
}
}
}
func TestCommentGroups(t *testing.T) {
f, err := ParseFile(token.NewFileSet(), "", `
package p /* 1a */ /* 1b */ /* 1c */ // 1d
/* 2a
*/
// 2b
const pi = 3.1415
/* 3a */ // 3b
/* 3c */ const e = 2.7182
// Example from issue 3139
func ExampleCount() {
fmt.Println(strings.Count("cheese", "e"))
fmt.Println(strings.Count("five", "")) // before & after each rune
// Output:
// 3
// 5
}
`, ParseComments)
if err != nil {
t.Fatal(err)
}
expected := [][]string{
{"/* 1a */", "/* 1b */", "/* 1c */", "// 1d"},
{"/* 2a\n*/", "// 2b"},
{"/* 3a */", "// 3b", "/* 3c */"},
{"// Example from issue 3139"},
{"// before & after each rune"},
{"// Output:", "// 3", "// 5"},
}
if len(f.Comments) != len(expected) {
t.Fatalf("got %d comment groups; expected %d", len(f.Comments), len(expected))
}
for i, exp := range expected {
got := f.Comments[i].List
if len(got) != len(exp) {
t.Errorf("got %d comments in group %d; expected %d", len(got), i, len(exp))
continue
}
for j, exp := range exp {
got := got[j].Text
if got != exp {
t.Errorf("got %q in group %d; expected %q", got, i, exp)
}
}
}
}
func getField(file *ast.File, fieldname string) *ast.Field {
parts := strings.Split(fieldname, ".")
for _, d := range file.Decls {
if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.TYPE {
for _, s := range d.Specs {
if s, ok := s.(*ast.TypeSpec); ok && s.Name.Name == parts[0] {
if s, ok := s.Type.(*ast.StructType); ok {
for _, f := range s.Fields.List {
for _, name := range f.Names {
if name.Name == parts[1] {
return f
}
}
}
}
}
}
}
}
return nil
}
// Don't use ast.CommentGroup.Text() - we want to see exact comment text.
func commentText(c *ast.CommentGroup) string {
var buf bytes.Buffer
if c != nil {
for _, c := range c.List {
buf.WriteString(c.Text)
}
}
return buf.String()
}
func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line string) {
f := getField(file, fieldname)
if f == nil {
t.Fatalf("field not found: %s", fieldname)
}
if got := commentText(f.Doc); got != lead {
t.Errorf("got lead comment %q; expected %q", got, lead)
}
if got := commentText(f.Comment); got != line {
t.Errorf("got line comment %q; expected %q", got, line)
}
}
func TestLeadAndLineComments(t *testing.T) {
f, err := ParseFile(token.NewFileSet(), "", `
package p
type T struct {
/* F1 lead comment */
//
F1 int /* F1 */ // line comment
// F2 lead
// comment
F2 int // F2 line comment
// f3 lead comment
f3 int // f3 line comment
}
`, ParseComments)
if err != nil {
t.Fatal(err)
}
checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
checkFieldComments(t, f, "T.f3", "// f3 lead comment", "// f3 line comment")
ast.FileExports(f)
checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
if getField(f, "T.f3") != nil {
t.Error("not expected to find T.f3")
}
}
// TestIssue9979 verifies that empty statements are contained within their enclosing blocks.
func TestIssue9979(t *testing.T) {
for _, src := range []string{
"package p; func f() {;}",
"package p; func f() {L:}",
"package p; func f() {L:;}",
"package p; func f() {L:\n}",
"package p; func f() {L:\n;}",
"package p; func f() { ; }",
"package p; func f() { L: }",
"package p; func f() { L: ; }",
"package p; func f() { L: \n}",
"package p; func f() { L: \n; }",
} {
fset := token.NewFileSet()
f, err := ParseFile(fset, "", src, 0)
if err != nil {
t.Fatal(err)
}
var pos, end token.Pos
ast.Inspect(f, func(x ast.Node) bool {
switch s := x.(type) {
case *ast.BlockStmt:
pos, end = s.Pos()+1, s.End()-1 // exclude "{", "}"
case *ast.LabeledStmt:
pos, end = s.Pos()+2, s.End() // exclude "L:"
case *ast.EmptyStmt:
// check containment
if s.Pos() < pos || s.End() > end {
t.Errorf("%s: %T[%d, %d] not inside [%d, %d]", src, s, s.Pos(), s.End(), pos, end)
}
// check semicolon
offs := fset.Position(s.Pos()).Offset
if ch := src[offs]; ch != ';' != s.Implicit {
want := "want ';'"
if s.Implicit {
want = "but ';' is implicit"
}
t.Errorf("%s: found %q at offset %d; %s", src, ch, offs, want)
}
}
return true
})
}
}
// TestIncompleteSelection ensures that an incomplete selector
// expression is parsed as a (blank) *ast.SelectorExpr, not a
// *ast.BadExpr.
func TestIncompleteSelection(t *testing.T) {
for _, src := range []string{
"package p; var _ = fmt.", // at EOF
"package p; var _ = fmt.\ntype X int", // not at EOF
} {
fset := token.NewFileSet()
f, err := ParseFile(fset, "", src, 0)
if err == nil {
t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
continue
}
const wantErr = "expected selector or type assertion"
if !strings.Contains(err.Error(), wantErr) {
t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr)
}
var sel *ast.SelectorExpr
ast.Inspect(f, func(n ast.Node) bool {
if n, ok := n.(*ast.SelectorExpr); ok {
sel = n
}
return true
})
if sel == nil {
t.Error("found no *ast.SelectorExpr")
continue
}
const wantSel = "&{fmt _}"
if fmt.Sprint(sel) != wantSel {
t.Errorf("found selector %s, want %s", sel, wantSel)
continue
}
}
}
func TestLastLineComment(t *testing.T) {
const src = `package main
type x int // comment
`
fset := token.NewFileSet()
f, err := ParseFile(fset, "", src, ParseComments)
if err != nil {
t.Fatal(err)
}
comment := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Comment.List[0].Text
if comment != "// comment" {
t.Errorf("got %q, want %q", comment, "// comment")
}
}
var parseDepthTests = []struct {
name string
format string
// multipler is used when a single statement may result in more than one
// change in the depth level, for instance "1+(..." produces a BinaryExpr
// followed by a UnaryExpr, which increments the depth twice. The test
// case comment explains which nodes are triggering the multiple depth
// changes.
parseMultiplier int
// scope is true if we should also test the statement for the resolver scope
// depth limit.
scope bool
// scopeMultiplier does the same as parseMultiplier, but for the scope
// depths.
scopeMultiplier int
}{
// The format expands the part inside « » many times.
// A second set of brackets nested inside the first stops the repetition,
// so that for example «(«1»)» expands to (((...((((1))))...))).
{name: "array", format: "package main; var x «[1]»int"},
{name: "slice", format: "package main; var x «[]»int"},
{name: "struct", format: "package main; var x «struct { X «int» }»", scope: true},
{name: "pointer", format: "package main; var x «*»int"},
{name: "func", format: "package main; var x «func()»int", scope: true},
{name: "chan", format: "package main; var x «chan »int"},
{name: "chan2", format: "package main; var x «<-chan »int"},
{name: "interface", format: "package main; var x «interface { M() «int» }»", scope: true, scopeMultiplier: 2}, // Scopes: InterfaceType, FuncType
{name: "map", format: "package main; var x «map[int]»int"},
{name: "slicelit", format: "package main; var x = «[]any{«»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
{name: "arraylit", format: "package main; var x = «[1]any{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
{name: "structlit", format: "package main; var x = «struct{x any}{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
{name: "maplit", format: "package main; var x = «map[int]any{1:«nil»}»", parseMultiplier: 2}, // Parser nodes: CompositeLit, KeyValueExpr
{name: "dot", format: "package main; var x = «x.»x"},
{name: "index", format: "package main; var x = x«[1]»"},
{name: "slice", format: "package main; var x = x«[1:2]»"},
{name: "slice3", format: "package main; var x = x«[1:2:3]»"},
{name: "dottype", format: "package main; var x = x«.(any)»"},
{name: "callseq", format: "package main; var x = x«()»"},
{name: "methseq", format: "package main; var x = x«.m()»", parseMultiplier: 2}, // Parser nodes: SelectorExpr, CallExpr
{name: "binary", format: "package main; var x = «1+»1"},
{name: "binaryparen", format: "package main; var x = «1+(«1»)»", parseMultiplier: 2}, // Parser nodes: BinaryExpr, ParenExpr
{name: "unary", format: "package main; var x = «^»1"},
{name: "addr", format: "package main; var x = «& »x"},
{name: "star", format: "package main; var x = «*»x"},
{name: "recv", format: "package main; var x = «<-»x"},
{name: "call", format: "package main; var x = «f(«1»)»", parseMultiplier: 2}, // Parser nodes: Ident, CallExpr
{name: "conv", format: "package main; var x = «(*T)(«1»)»", parseMultiplier: 2}, // Parser nodes: ParenExpr, CallExpr
{name: "label", format: "package main; func main() { «Label:» }"},
{name: "if", format: "package main; func main() { «if true { «» }»}", parseMultiplier: 2, scope: true, scopeMultiplier: 2}, // Parser nodes: IfStmt, BlockStmt. Scopes: IfStmt, BlockStmt
{name: "ifelse", format: "package main; func main() { «if true {} else » {} }", scope: true},
{name: "switch", format: "package main; func main() { «switch { default: «» }»}", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause
{name: "typeswitch", format: "package main; func main() { «switch x.(type) { default: «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause
{name: "for0", format: "package main; func main() { «for { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt
{name: "for1", format: "package main; func main() { «for x { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt
{name: "for3", format: "package main; func main() { «for f(); g(); h() { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt
{name: "forrange0", format: "package main; func main() { «for range x { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt
{name: "forrange1", format: "package main; func main() { «for x = range z { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt
{name: "forrange2", format: "package main; func main() { «for x, y = range z { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt
{name: "go", format: "package main; func main() { «go func() { «» }()» }", parseMultiplier: 2, scope: true}, // Parser nodes: GoStmt, FuncLit
{name: "defer", format: "package main; func main() { «defer func() { «» }()» }", parseMultiplier: 2, scope: true}, // Parser nodes: DeferStmt, FuncLit
{name: "select", format: "package main; func main() { «select { default: «» }» }", scope: true},
}
// split splits pre«mid»post into pre, mid, post.
// If the string does not have that form, split returns x, "", "".
func split(x string) (pre, mid, post string) {
start, end := strings.Index(x, "«"), strings.LastIndex(x, "»")
if start < 0 || end < 0 {
return x, "", ""
}
return x[:start], x[start+len("«") : end], x[end+len("»"):]
}
func TestParseDepthLimit(t *testing.T) {
if runtime.GOARCH == "wasm" {
t.Skip("causes call stack exhaustion on js/wasm")
}
for _, tt := range parseDepthTests {
for _, size := range []string{"small", "big"} {
t.Run(tt.name+"/"+size, func(t *testing.T) {
n := maxNestLev + 1
if tt.parseMultiplier > 0 {
n /= tt.parseMultiplier
}
if size == "small" {
// Decrease the number of statements by 10, in order to check
// that we do not fail when under the limit. 10 is used to
// provide some wiggle room for cases where the surrounding
// scaffolding syntax adds some noise to the depth that changes
// on a per testcase basis.
n -= 10
}
pre, mid, post := split(tt.format)
if strings.Contains(mid, "«") {
left, base, right := split(mid)
mid = strings.Repeat(left, n) + base + strings.Repeat(right, n)
} else {
mid = strings.Repeat(mid, n)
}
input := pre + mid + post
fset := token.NewFileSet()
_, err := ParseFile(fset, "", input, ParseComments|SkipObjectResolution)
if size == "small" {
if err != nil {
t.Errorf("ParseFile(...): %v (want success)", err)
}
} else {
expected := "exceeded max nesting depth"
if err == nil || !strings.HasSuffix(err.Error(), expected) {
t.Errorf("ParseFile(...) = _, %v, want %q", err, expected)
}
}
})
}
}
}
func TestScopeDepthLimit(t *testing.T) {
if runtime.GOARCH == "wasm" {
t.Skip("causes call stack exhaustion on js/wasm")
}
for _, tt := range parseDepthTests {
if !tt.scope {
continue
}
for _, size := range []string{"small", "big"} {
t.Run(tt.name+"/"+size, func(t *testing.T) {
n := maxScopeDepth + 1
if tt.scopeMultiplier > 0 {
n /= tt.scopeMultiplier
}
if size == "small" {
// Decrease the number of statements by 10, in order to check
// that we do not fail when under the limit. 10 is used to
// provide some wiggle room for cases where the surrounding
// scaffolding syntax adds some noise to the depth that changes
// on a per testcase basis.
n -= 10
}
pre, mid, post := split(tt.format)
if strings.Contains(mid, "«") {
left, base, right := split(mid)
mid = strings.Repeat(left, n) + base + strings.Repeat(right, n)
} else {
mid = strings.Repeat(mid, n)
}
input := pre + mid + post
fset := token.NewFileSet()
_, err := ParseFile(fset, "", input, DeclarationErrors)
if size == "small" {
if err != nil {
t.Errorf("ParseFile(...): %v (want success)", err)
}
} else {
expected := "exceeded max scope depth during object resolution"
if err == nil || !strings.HasSuffix(err.Error(), expected) {
t.Errorf("ParseFile(...) = _, %v, want %q", err, expected)
}
}
})
}
}
}