cuelang.org/go@v0.10.1/cue/parser/parser_test.go (about)

     1  // Copyright 2018 The CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package parser
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"testing"
    21  
    22  	"cuelang.org/go/cue/ast"
    23  	"cuelang.org/go/internal/astinternal"
    24  )
    25  
    26  func TestParse(t *testing.T) {
    27  	testCases := []struct{ desc, in, out string }{{
    28  
    29  		"ellipsis in structs",
    30  		`#Def: {
    31  			b: "2"
    32  			...
    33  		}
    34  		...
    35  
    36  		#Def2: {
    37  			...
    38  			b: "2"
    39  		}
    40  		#Def3: {...
    41  		_}
    42  		...
    43  		`,
    44  		`#Def: {b: "2", ...}, ..., #Def2: {..., b: "2"}, #Def3: {..., _}, ...`,
    45  	}, {
    46  
    47  		"empty file", "", "",
    48  	}, {
    49  		"empty struct", "{}", "{}",
    50  	}, {
    51  		"empty structs", "{},{},", "{}, {}",
    52  	}, {
    53  		"empty structs; elided comma", "{}\n{}", "{}, {}",
    54  	}, {
    55  		"basic lits", `"a","b", 3,3.4,5,2_3`, `"a", "b", 3, 3.4, 5, 2_3`,
    56  	}, {
    57  		"keyword basic lits", `true,false,null,for,in,if,let,if`, `true, false, null, for, in, if, let, if`,
    58  	}, {
    59  		"keyword basic newline", `
    60  		true
    61  		false
    62  		null
    63  		for
    64  		in
    65  		if
    66  		let
    67  		if
    68  		`, `true, false, null, for, in, if, let, if`,
    69  	}, {
    70  		"keywords as labels",
    71  		`if: 0, for: 1, in: 2, where: 3, div: 4, quo: 5, func: 6
    72  		for: if: func: let: 3
    73  		`,
    74  		`if: 0, for: 1, in: 2, where: 3, div: 4, quo: 5, func: 6, for: {if: {func: {let: 3}}}`,
    75  	}, {
    76  		"keywords as alias",
    77  		`if=foo: 0
    78  		for=bar: 2
    79  		let=bar: 3
    80  		func=baz: 4
    81  		`,
    82  		`if=foo: 0, for=bar: 2, let=bar: 3, func=baz: 4`,
    83  	}, {
    84  		"keywords as selector",
    85  		`a : {
    86  			if: 0
    87  			for: 1
    88  			in: 2
    89  			where: 3
    90  			div: 4
    91  			quo: 5
    92  			func: 6
    93  			float: 7
    94  			null: if: func: let: 3
    95  		}, b: [
    96  			a.if,
    97  			a.for,
    98  			a.in,
    99  			a.where,
   100  			a.div,
   101  			a.quo,
   102  			a.func,
   103  			a.float,
   104  			a.null.if.func.let,
   105  		]`,
   106  		`a: {if: 0, for: 1, in: 2, where: 3, div: 4, quo: 5, func: 6, float: 7, null: {if: {func: {let: 3}}}}, b: [a.if, a.for, a.in, a.where, a.div, a.quo, a.func, a.float, a.null.if.func.let]`,
   107  	}, {}, {
   108  		"json",
   109  		`{
   110  			"a": 1,
   111  			"b": "2",
   112  			"c": 3
   113  		}`,
   114  		`{"a": 1, "b": "2", "c": 3}`,
   115  	}, {
   116  		"json:extra comma",
   117  		`{
   118  			"a": 1,
   119  			"b": "2",
   120  			"c": 3,
   121  		}`,
   122  		`{"a": 1, "b": "2", "c": 3}`,
   123  	}, {
   124  		"json:simplified",
   125  		`{
   126  			a: 1
   127  			b: "2"
   128  			c: 3
   129  		}`,
   130  		`{a: 1, b: "2", c: 3}`,
   131  	}, {
   132  		"attributes",
   133  		`a: 1 @xml(,attr)
   134  		 b: 2 @foo(a,b=4) @go(Foo)
   135  		 c: {
   136  			 d: "x" @go(D) @json(,omitempty)
   137  			 e: "y" @ts(,type=string,"str")
   138  		 }`,
   139  		`a: 1 @xml(,attr), b: 2 @foo(a,b=4) @go(Foo), c: {d: "x" @go(D) @json(,omitempty), e: "y" @ts(,type=string,"str")}`,
   140  	}, {
   141  		"not emitted",
   142  		`a: true
   143  		 b?: "2"
   144  		 c?: 3
   145  
   146  		 d!: 2
   147  		 e: f!: 3
   148  
   149  		 "g\("en")"?: 4
   150  		 "h\("en")"!: 4
   151  		`,
   152  		`a: true, b?: "2", c?: 3, d!: 2, e: {f!: 3}, "g\("en")"?: 4, "h\("en")"!: 4`,
   153  	}, {
   154  		"definition",
   155  		`#Def: {
   156  			 b: "2"
   157  			 c: 3
   158  
   159  			 embedding
   160  		}
   161  		#Def: {}
   162  		`,
   163  		`#Def: {b: "2", c: 3, embedding}, #Def: {}`,
   164  	}, {
   165  		"one-line embedding",
   166  		`{ V1, V2 }`,
   167  		`{V1, V2}`,
   168  	}, {
   169  		"selectors",
   170  		`a.b. "str"`,
   171  		`a.b."str"`,
   172  	}, {
   173  		"selectors",
   174  		`a.b. "str"`,
   175  		`a.b."str"`,
   176  	}, {
   177  		"faulty bytes selector",
   178  		`a.b.'str'`,
   179  		"a.b._\nexpected selector, found 'STRING' 'str'",
   180  	}, {
   181  		"faulty multiline string selector",
   182  		`a.b."""
   183  			"""`,
   184  		"a.b._\nexpected selector, found 'STRING' \"\"\"\n\t\t\t\"\"\"",
   185  	}, {
   186  		"expression embedding",
   187  		`#Def: {
   188  			a.b.c
   189  			a > b < c
   190  			-1<2
   191  
   192  			foo: 2
   193  		}`,
   194  		`#Def: {a.b.c, a>b<c, -1<2, foo: 2}`,
   195  	}, {
   196  		"ellipsis in structs",
   197  		`#Def: {
   198  			b: "2"
   199  			...
   200  		}
   201  		...
   202  
   203  		#Def2: {
   204  			...
   205  			b: "2"
   206  		}
   207  		#Def3: {...
   208  		_}
   209  		...
   210  		`,
   211  		`#Def: {b: "2", ...}, ..., #Def2: {..., b: "2"}, #Def3: {..., _}, ...`,
   212  	}, {
   213  		"emitted referencing non-emitted",
   214  		`a: 1
   215  		 b: "2"
   216  		 c: 3
   217  		{ name: b, total: a + b }`,
   218  		`a: 1, b: "2", c: 3, {name: b, total: a+b}`,
   219  	}, {
   220  		"package file",
   221  		`package k8s
   222  		 {}
   223  		`,
   224  		`package k8s, {}`,
   225  	}, {
   226  		"imports group",
   227  		`package k8s
   228  
   229  		import (
   230  			a "foo"
   231  			"bar/baz"
   232  		)
   233  		`,
   234  		`package k8s, import ( a "foo", "bar/baz" )`,
   235  	}, {
   236  		"imports single",
   237  		`package k8s
   238  
   239  		import a "foo"
   240  		import "bar/baz"
   241  			`,
   242  		`package k8s, import a "foo", import "bar/baz"`,
   243  	}, {
   244  		"collapsed fields",
   245  		`a: #b: c?: [Name=_]: d: 1
   246  		"g\("en")"?: 4
   247  		 // job foo { bar: 1 } // TODO error after foo
   248  		 job: "foo": [_]: { bar: 1 }
   249  		`,
   250  		`a: {#b: {c?: {[Name=_]: {d: 1}}}}, "g\("en")"?: 4, job: {"foo": {[_]: {bar: 1}}}`,
   251  	}, {
   252  		"identifiers",
   253  		`// 	$_: 1,
   254  			a: {b: {c: d}}
   255  			c: a
   256  			d: a.b
   257  			// e: a."b" // TODO: is an error
   258  			e: a.b.c
   259  			"f": f,
   260  			[X=_]: X
   261  		`,
   262  		"a: {b: {c: d}}, c: a, d: a.b, e: a.b.c, \"f\": f, [X=_]: X",
   263  	}, {
   264  		"empty fields",
   265  		`
   266  		"": 3
   267  		`,
   268  		`"": 3`,
   269  	}, {
   270  		"expressions",
   271  		`	a: (2 + 3) * 5
   272  			b: (2 + 3) + 4
   273  			c: 2 + 3 + 4
   274  			d: -1
   275  			e: !foo
   276  			f: _|_
   277  		`,
   278  		"a: (2+3)*5, b: (2+3)+4, c: 2+3+4, d: -1, e: !foo, f: _|_",
   279  	}, {
   280  		"pseudo keyword expressions",
   281  		`	a: (2 div 3) mod 5
   282  			b: (2 quo 3) rem 4
   283  			c: 2 div 3 div 4
   284  		`,
   285  		"a: (2 div 3) mod 5, b: (2 quo 3) rem 4, c: 2 div 3 div 4",
   286  	}, {
   287  		"ranges",
   288  		`	a: >=1 & <=2
   289  			b: >2.0  & <= 40.0
   290  			c: >"a" & <="b"
   291  			v: (>=1 & <=2) & <=(>=5 & <=10)
   292  			w: >1 & <=2 & <=3
   293  			d: >=3T & <=5M
   294  		`,
   295  		"a: >=1&<=2, b: >2.0&<=40.0, c: >\"a\"&<=\"b\", v: (>=1&<=2)&<=(>=5&<=10), w: >1&<=2&<=3, d: >=3T&<=5M",
   296  	}, {
   297  		"indices",
   298  		`{
   299  			a: b[2]
   300  			b: c[1:2]
   301  			c: "asdf"
   302  			d: c ["a"]
   303  		}`,
   304  		`{a: b[2], b: c[1:2], c: "asdf", d: c["a"]}`,
   305  	}, {
   306  		"calls",
   307  		`{
   308  			a: b(a.b, c.d)
   309  			b: a.b(c)
   310  		}`,
   311  		`{a: b(a.b, c.d), b: a.b(c)}`,
   312  	}, {
   313  		"lists",
   314  		`{
   315  			a: [ 1, 2, 3, b, c, ... ]
   316  			b: [ 1, 2, 3, ],
   317  			c: [ 1,
   318  			 2,
   319  			 3
   320  			 ],
   321  			d: [ 1+2, 2, 4,]
   322  		}`,
   323  		`{a: [1, 2, 3, b, c, ...], b: [1, 2, 3], c: [1, 2, 3], d: [1+2, 2, 4]}`,
   324  	}, {
   325  		"list types",
   326  		`{
   327  			a: 4*[int]
   328  			b: <=5*[{a: 5}]
   329  			c1: [...int]
   330  			c2: [...]
   331  			c3: [1, 2, ...int,]
   332  		}`,
   333  		`{a: 4*[int], b: <=5*[{a: 5}], c1: [...int], c2: [...], c3: [1, 2, ...int]}`,
   334  	}, {
   335  		"list comprehensions",
   336  		`{
   337  				y: [1,2,3]
   338  				b: [for x in y if x == 1 { x }],
   339  			}`,
   340  		`{y: [1, 2, 3], b: [for x in y if x==1 {x}]}`,
   341  	}, {
   342  		"field comprehensions",
   343  		`{
   344  				y: { a: 1, b: 2}
   345  				a: {
   346  					for k, v in y if v > 2 {
   347  						"\(k)": v
   348  					}
   349  				}
   350  			 }`,
   351  		`{y: {a: 1, b: 2}, a: {for k: v in y if v>2 {"\(k)": v}}}`,
   352  	}, {
   353  		"nested comprehensions",
   354  		`{
   355  			y: { a: 1, b: 2}
   356  			a: {
   357  				for k, v in y let x = v+2 if x > 2 {
   358  					"\(k)": v
   359  				}
   360  			}
   361  		}`,
   362  		`{y: {a: 1, b: 2}, a: {for k: v in y let x=v+2 if x>2 {"\(k)": v}}}`,
   363  	}, {
   364  		"let declaration",
   365  		`{
   366  			let X = 42
   367  			let Y = "42",
   368  			let Z = 10 + 12
   369  		}`,
   370  		`{let X=42, let Y="42", let Z=10+12}`,
   371  	}, {
   372  		"duplicates allowed",
   373  		`{
   374  			a: b: 3
   375  			a: { b: 3 }
   376  		}`,
   377  		"{a: {b: 3}, a: {b: 3}}",
   378  	}, {
   379  		"templates", // TODO: remove
   380  		`{
   381  			[foo=_]: { a: int }
   382  			a:     { a: 1 }
   383  		}`,
   384  		"{[foo=_]: {a: int}, a: {a: 1}}",
   385  	}, {
   386  		"value alias",
   387  		`
   388  		{
   389  			a: X=foo
   390  			b: Y={foo}
   391  			c: d: e: X=5
   392  		}
   393  		`,
   394  		`{a: X=foo, b: Y={foo}, c: {d: {e: X=5}}}`,
   395  	}, {
   396  		"dynamic labels",
   397  		`{
   398  			(x): a: int
   399  			x:   "foo"
   400  			a: {
   401  				(a.b)
   402  			}
   403  
   404  			(x)?: 1
   405  			y: (x)!: 2
   406  		}`,
   407  		`{(x): {a: int}, x: "foo", a: {(a.b)}, (x)?: 1, y: {(x)!: 2}}`,
   408  	}, {
   409  		"foo",
   410  		`[
   411  			[1],
   412  			[1, 2],
   413  			[1, 2, 3],
   414  		]`,
   415  		"[[1], [1, 2], [1, 2, 3]]",
   416  	}, {
   417  		"interpolation",
   418  		`a: "foo \(ident)"
   419  		 b: "bar \(bar)  $$$ "
   420  		 c: "nest \(   { a: "\( nest ) "}.a ) \(5)"
   421  		 m1: """
   422  			 multi \(bar)
   423  			 """
   424  		 m2: '''
   425  			 \(bar) multi
   426  			 '''`,
   427  		`a: "foo \(ident)", b: "bar \(bar)  $$$ ", c: "nest \({a: "\(nest) "}.a) \(5)", ` + "m1: \"\"\"\n\t\t\t multi \\(bar)\n\t\t\t \"\"\", m2: '''\n\t\t\t \\(bar) multi\n\t\t\t '''",
   428  	}, {
   429  		"file comments",
   430  		`// foo
   431  
   432  		// uni
   433  		package foo // uniline
   434  
   435  		// file.1
   436  		// file.2
   437  
   438  		`,
   439  		"<[0// foo] <[d0// uni] [l3// uniline] [3// file.1 // file.2] package foo>>",
   440  	}, {
   441  		"line comments",
   442  		`// doc
   443  		 a: 5 // line
   444  		 b: 6 // lineb
   445  			  // next
   446  			`, // next is followed by EOF. Ensure it doesn't move to file.
   447  		"<[d0// doc] [l5// line] a: 5>, " +
   448  			"<[l5// lineb] [5// next] b: 6>",
   449  	}, {
   450  		"alt comments",
   451  		`// a ...
   452  		a: 5 // line a
   453  
   454  		// about a
   455  
   456  		// b ...
   457  		b: // lineb
   458  		  6
   459  
   460  		// about b
   461  
   462  		c: 7
   463  
   464  		// about c
   465  
   466  		// about d
   467  		d:
   468  			// about e
   469  			e: 3
   470  		`,
   471  		"<[d0// a ...] [l5// line a] [5// about a] a: 5>, " +
   472  			"<[d0// b ...] [l2// lineb] [5// about b] b: 6>, " +
   473  			"<[5// about c] c: 7>, " +
   474  			"<[d0// about d] d: {<[d0// about e] e>: 3}>",
   475  	}, {
   476  		"expr comments",
   477  		`
   478  		a: 2 +  // 2 +
   479  		   3 +  // 3 +
   480  		   4    // 4
   481  		   `,
   482  		"<[l5// 4] a: <[l2// 3 +] <[l2// 2 +] 2+3>+4>>",
   483  	}, {
   484  		"composit comments",
   485  		`a : {
   486  			a: 1, b: 2, c: 3, d: 4
   487  			// end
   488  		}
   489  		b: [
   490  			1, 2, 3, 4, 5,
   491  			// end
   492  		]
   493  		c: [ 1, 2, 3, 4, // here
   494  			{ a: 3 }, // here
   495  			5, 6, 7, 8 // and here
   496  		]
   497  		d: {
   498  			a: 1 // Hello
   499  			// Doc
   500  			b: 2
   501  		}
   502  		e1: [
   503  			// comment in list body
   504  		]
   505  		e2: {
   506  			// comment in struct body
   507  		}
   508  		`,
   509  		"a: {a: 1, b: 2, c: 3, <[d5// end] d: 4>}, " +
   510  			"b: [1, 2, 3, 4, <[d2// end] 5>], " +
   511  			"c: [1, 2, 3, <[l2// here] 4>, <[l4// here] {a: 3}>, 5, 6, 7, <[l2// and here] 8>], " +
   512  			"d: {<[l5// Hello] a: 1>, <[d0// Doc] b: 2>}, " +
   513  			"e1: <[d1// comment in list body] []>, " +
   514  			"e2: <[d1// comment in struct body] {}>",
   515  	}, {
   516  		"attribute comments",
   517  		`
   518  		a: 1 @a() @b() // d
   519  		`,
   520  		`<[l5// d] a: 1 @a() @b()>`,
   521  	}, {
   522  		"attribute declarations",
   523  		`
   524  		@foo()
   525  
   526  		package bar
   527  
   528  		@bar()
   529  
   530  		import "strings"
   531  
   532  		@baz()
   533  			`,
   534  		`@foo(), package bar, @bar(), import "strings", @baz()`,
   535  	}, {
   536  		"comprehension comments",
   537  		`
   538  		if X {
   539  			// Comment 1
   540  			Field: 2
   541  			// Comment 2
   542  		}
   543  		`,
   544  		`if X <[d2// Comment 2] {<[d0// Comment 1] Field: 2>}>`,
   545  	}, {
   546  		"let comments",
   547  		`let X = foo // Comment 1`,
   548  		`<[5// Comment 1] let X=foo>`,
   549  	}, {
   550  		"emit comments",
   551  		`// a comment at the beginning of the file
   552  
   553  		// a second comment
   554  
   555  		// comment
   556  		a: 5
   557  
   558  		{}
   559  
   560  		// a comment at the end of the file
   561  		`,
   562  		"<[0// a comment at the beginning of the file] [0// a second comment] <[d0// comment] a: 5>, <[2// a comment at the end of the file] {}>>",
   563  	}, {
   564  		"composite comments 2",
   565  		`
   566  	{
   567  // foo
   568  
   569  // fooo
   570  foo: 1
   571  
   572  bar: 2
   573  	}
   574  
   575  [
   576  	{"name": "value"}, // each element has a long
   577  	{"name": "next"}   // optional next element
   578  ]
   579  `,
   580  		`{<[0// foo] [d0// fooo] foo: 1>, bar: 2}, [<[l4// each element has a long] {"name": "value"}>, <[l4// optional next element] {"name": "next"}>]`,
   581  	}, {
   582  		desc: "field aliasing",
   583  		in: `
   584  		I="\(k)": v
   585  		S="foo-bar": w
   586  		L=foo: x
   587  		X=[0]: {
   588  			foo: X | null
   589  		}
   590  		[Y=string]: { name: Y }
   591  		X1=[X2=<"d"]: { name: X2 }
   592  		Y1=foo: Y2=bar: [Y1, Y2]
   593  		`,
   594  		out: `I="\(k)": v, ` +
   595  			`S="foo-bar": w, ` +
   596  			`L=foo: x, ` +
   597  			`X=[0]: {foo: X|null}, ` +
   598  			`[Y=string]: {name: Y}, ` +
   599  			`X1=[X2=<"d"]: {name: X2}, ` +
   600  			`Y1=foo: {Y2=bar: [Y1, Y2]}`,
   601  	}, {
   602  		desc: "allow keyword in expression",
   603  		in: `
   604  		foo: in & 2
   605  		`,
   606  		out: "foo: in&2",
   607  	}, {
   608  		desc: "dot import",
   609  		in: `
   610  		import . "foo"
   611  		`,
   612  		out: "import , \"foo\"\nexpected 'STRING', found '.'",
   613  	}, {
   614  		desc: "attributes",
   615  		in: `
   616  		package name
   617  
   618  		@t1(v1)
   619  
   620  		{
   621  			@t2(v2)
   622  		}
   623  		a: {
   624  			a: 1
   625  			@t3(v3)
   626  			@t4(v4)
   627  			c: 2
   628  		}
   629  		`,
   630  		out: "package name, @t1(v1), {@t2(v2)}, a: {a: 1, @t3(v3), @t4(v4), c: 2}",
   631  	}, {
   632  		desc: "Issue #276",
   633  		in: `
   634  		a: int=>2
   635  		`,
   636  		out: "a: int=>2",
   637  	}, {
   638  		desc: "struct comments",
   639  		in: `
   640  		struct: {
   641  			// This is a comment
   642  
   643  			// This is a comment
   644  
   645  			// Another comment
   646  			something: {
   647  			}
   648  
   649  			// extra comment
   650  		}`,
   651  		out: `struct: {<[0// This is a comment] [0// This is a comment] [d0// Another comment] [d5// extra comment] something: {}>}`,
   652  	}, {
   653  		desc: "list comments",
   654  		in: `
   655  		list: [
   656  			// Comment1
   657  
   658  			// Comment2
   659  
   660  			// Another comment
   661  			{
   662  			},
   663  
   664  			// Comment 3
   665  		]`,
   666  		out: "list: [<[0// Comment1] [0// Comment2] [d0// Another comment] [d3// Comment 3] {}>]",
   667  	}, {
   668  		desc: "call comments",
   669  		in: `
   670  		funcArg1: foo(
   671  			{},
   672  
   673  			// Comment1
   674  
   675  			// Comment2
   676  			{}
   677  
   678  			// Comment3
   679  		)`,
   680  		out: "funcArg1: foo(<[1// Comment1] {}>, <[d0// Comment2] [d1// Comment3] {}>)",
   681  	}, {
   682  		desc: "front-style commas",
   683  		in: `
   684  			frontStyle: { "key": "value"
   685  				, "key2": "value2"
   686  				, "foo" : bar
   687  			}
   688  			`,
   689  		out: "frontStyle: {\"key\": \"value\", \"key2\": \"value2\", \"foo\": bar}",
   690  	}, {
   691  		desc: "function types",
   692  		in: `
   693  			f0: func(): int
   694  			f1: func(int): int
   695  			f2: func(int, string): int
   696  			f3: func({a: int, b: string}): bool
   697  			f4: func(bool, func(int, string): int): string
   698  			f5: func(int, int): func(bool, bool): bool
   699  			f6: func(func(bool, bool): bool, func(string, string): string): func(int, func(int, string): int): func(int, string): int
   700  		`,
   701  		out: "f0: func(): int, f1: func(int): int, f2: func(int, string): int, f3: func({a: int, b: string}): bool, f4: func(bool, func(int, string): int): string, f5: func(int, int): func(bool, bool): bool, f6: func(func(bool, bool): bool, func(string, string): string): func(int, func(int, string): int): func(int, string): int",
   702  	}}
   703  	for _, tc := range testCases {
   704  		t.Run(tc.desc, func(t *testing.T) {
   705  			mode := []Option{AllErrors}
   706  			if strings.Contains(tc.desc, "comments") {
   707  				mode = append(mode, ParseComments)
   708  			}
   709  			if strings.Contains(tc.desc, "function") {
   710  				mode = append(mode, ParseFuncs)
   711  			}
   712  			f, err := ParseFile("input", tc.in, mode...)
   713  			got := astinternal.DebugStr(f)
   714  			if err != nil {
   715  				got += "\n" + err.Error()
   716  			}
   717  			if got != tc.out {
   718  				t.Errorf("\ngot  %q;\nwant %q", got, tc.out)
   719  			}
   720  		})
   721  	}
   722  }
   723  
   724  func TestStrict(t *testing.T) {
   725  	testCases := []struct{ desc, in string }{
   726  		{"block comments",
   727  			`a: 1 /* a */`},
   728  		{"space separator",
   729  			`a b c: 2`},
   730  		{"reserved identifiers",
   731  			`__foo: 3`},
   732  		{"old-style alias 1",
   733  			`X=3`},
   734  		{"old-style alias 2",
   735  			`X={}`},
   736  
   737  		// Not yet supported
   738  		{"additional typed not yet supported",
   739  			`{...int}`},
   740  	}
   741  	for _, tc := range testCases {
   742  		t.Run(tc.desc, func(t *testing.T) {
   743  			mode := []Option{AllErrors, ParseComments, FromVersion(Latest)}
   744  			_, err := ParseFile("input", tc.in, mode...)
   745  			if err == nil {
   746  				t.Errorf("unexpected success: %v", tc.in)
   747  			}
   748  		})
   749  	}
   750  }
   751  
   752  func TestParseExpr(t *testing.T) {
   753  	// just kicking the tires:
   754  	// a valid arithmetic expression
   755  	src := "a + b"
   756  	x, err := parseExprString(src)
   757  	if err != nil {
   758  		t.Errorf("ParseExpr(%q): %v", src, err)
   759  	}
   760  	// sanity check
   761  	if _, ok := x.(*ast.BinaryExpr); !ok {
   762  		t.Errorf("ParseExpr(%q): got %T, want *BinaryExpr", src, x)
   763  	}
   764  
   765  	// an invalid expression
   766  	src = "a + *"
   767  	if _, err := parseExprString(src); err == nil {
   768  		t.Errorf("ParseExpr(%q): got no error", src)
   769  	}
   770  
   771  	// a comma is not permitted unless automatically inserted
   772  	src = "a + b\n"
   773  	if _, err := parseExprString(src); err != nil {
   774  		t.Errorf("ParseExpr(%q): got error %s", src, err)
   775  	}
   776  	src = "a + b;"
   777  	if _, err := parseExprString(src); err == nil {
   778  		t.Errorf("ParseExpr(%q): got no error", src)
   779  	}
   780  
   781  	// check resolution
   782  	src = "{ foo: bar, bar: foo }"
   783  	x, err = parseExprString(src)
   784  	if err != nil {
   785  		t.Fatalf("ParseExpr(%q): %v", src, err)
   786  	}
   787  	for _, d := range x.(*ast.StructLit).Elts {
   788  		v := d.(*ast.Field).Value.(*ast.Ident)
   789  		if v.Scope == nil {
   790  			t.Errorf("ParseExpr(%q): scope of field %v not set", src, v.Name)
   791  		}
   792  		if v.Node == nil {
   793  			t.Errorf("ParseExpr(%q): scope of node %v not set", src, v.Name)
   794  		}
   795  	}
   796  
   797  	// various other stuff following a valid expression
   798  	const validExpr = "a + b"
   799  	const anything = "dh3*#D)#_"
   800  	for _, c := range "!)]};," {
   801  		src := validExpr + string(c) + anything
   802  		if _, err := parseExprString(src); err == nil {
   803  			t.Errorf("ParseExpr(%q): got no error", src)
   804  		}
   805  	}
   806  
   807  	// ParseExpr must not crash
   808  	for _, src := range valids {
   809  		_, _ = parseExprString(src)
   810  	}
   811  }
   812  
   813  func TestImports(t *testing.T) {
   814  	var imports = map[string]bool{
   815  		`"a"`:        true,
   816  		`"a/b"`:      true,
   817  		`"a.b"`:      true,
   818  		`'m\x61th'`:  true,
   819  		`"greek/αβ"`: true,
   820  		`""`:         false,
   821  
   822  		// Each of these pairs tests both #""# vs "" strings
   823  		// and also use of invalid characters spelled out as
   824  		// escape sequences and written directly.
   825  		// For example `"\x00"` tests import "\x00"
   826  		// while "`\x00`" tests import `<actual-NUL-byte>`.
   827  		`#"a"#`:        true,
   828  		`"\x00"`:       false,
   829  		"'\x00'":       false,
   830  		`"\x7f"`:       false,
   831  		"`\x7f`":       false,
   832  		`"a!"`:         false,
   833  		"#'a!'#":       false,
   834  		`"a b"`:        false,
   835  		`#"a b"#`:      false,
   836  		`"a\\b"`:       false,
   837  		"#\"a\\b\"#":   false,
   838  		"\"`a`\"":      false,
   839  		"#'\"a\"'#":    false,
   840  		`"\x80\x80"`:   false,
   841  		"#'\x80\x80'#": false,
   842  		`"\xFFFD"`:     false,
   843  		"#'\xFFFD'#":   false,
   844  	}
   845  	for path, isValid := range imports {
   846  		t.Run(path, func(t *testing.T) {
   847  			src := fmt.Sprintf("package p, import %s", path)
   848  			_, err := ParseFile("", src)
   849  			switch {
   850  			case err != nil && isValid:
   851  				t.Errorf("ParseFile(%s): got %v; expected no error", src, err)
   852  			case err == nil && !isValid:
   853  				t.Errorf("ParseFile(%s): got no error; expected one", src)
   854  			}
   855  		})
   856  	}
   857  }
   858  
   859  // TestIncompleteSelection ensures that an incomplete selector
   860  // expression is parsed as a (blank) *SelectorExpr, not a
   861  // *BadExpr.
   862  func TestIncompleteSelection(t *testing.T) {
   863  	for _, src := range []string{
   864  		"{ a: fmt. }",         // at end of object
   865  		"{ a: fmt.\n0.0: x }", // not at end of struct
   866  	} {
   867  		t.Run("", func(t *testing.T) {
   868  			f, err := ParseFile("", src)
   869  			if err == nil {
   870  				t.Fatalf("ParseFile(%s) succeeded unexpectedly", src)
   871  			}
   872  
   873  			const wantErr = "expected selector"
   874  			if !strings.Contains(err.Error(), wantErr) {
   875  				t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr)
   876  			}
   877  
   878  			var sel *ast.SelectorExpr
   879  			ast.Walk(f, func(n ast.Node) bool {
   880  				if n, ok := n.(*ast.SelectorExpr); ok {
   881  					sel = n
   882  				}
   883  				return true
   884  			}, nil)
   885  			if sel == nil {
   886  				t.Fatalf("found no *SelectorExpr: %#v %s", f.Decls[0], astinternal.DebugStr(f))
   887  			}
   888  			const wantSel = "&{fmt _ {<nil>} {{}}}"
   889  			if fmt.Sprint(sel) != wantSel {
   890  				t.Fatalf("found selector %v, want %s", sel, wantSel)
   891  			}
   892  		})
   893  	}
   894  }
   895  
   896  // For debugging, do not delete.
   897  func TestX(t *testing.T) {
   898  	t.Skip()
   899  
   900  	f, err := ParseFile("input", `
   901  	`, ParseComments)
   902  	if err != nil {
   903  		t.Errorf("unexpected error: %v", err)
   904  	}
   905  	t.Error(astinternal.DebugStr(f))
   906  }