...

Source file src/cuelang.org/go/cue/parser/parser_test.go

Documentation: cuelang.org/go/cue/parser

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

View as plain text