github.com/hashicorp/hcl/v2@v2.20.0/hclwrite/parser_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package hclwrite
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"testing"
    10  
    11  	"github.com/davecgh/go-spew/spew"
    12  
    13  	"github.com/google/go-cmp/cmp"
    14  
    15  	"github.com/kylelemons/godebug/pretty"
    16  
    17  	"github.com/hashicorp/hcl/v2"
    18  	"github.com/hashicorp/hcl/v2/hclsyntax"
    19  )
    20  
    21  func TestParse(t *testing.T) {
    22  	tests := []struct {
    23  		src  string
    24  		want TestTreeNode
    25  	}{
    26  		{
    27  			"",
    28  			TestTreeNode{
    29  				Type: "Body",
    30  			},
    31  		},
    32  		{
    33  			"a = 1\n",
    34  			TestTreeNode{
    35  				Type: "Body",
    36  				Children: []TestTreeNode{
    37  					{
    38  						Type: "Attribute",
    39  						Children: []TestTreeNode{
    40  							{
    41  								Type: "comments",
    42  							},
    43  							{
    44  								Type: "identifier",
    45  								Val:  "a",
    46  							},
    47  							{
    48  								Type: "Tokens",
    49  								Val:  " =",
    50  							},
    51  							{
    52  								Type: "Expression",
    53  								Children: []TestTreeNode{
    54  									{
    55  										Type: "Tokens",
    56  										Val:  " 1",
    57  									},
    58  								},
    59  							},
    60  							{
    61  								Type: "comments",
    62  							},
    63  							{
    64  								Type: "Tokens",
    65  								Val:  "\n",
    66  							},
    67  						},
    68  					},
    69  				},
    70  			},
    71  		},
    72  		{
    73  			"# aye aye aye\na = 1\n",
    74  			TestTreeNode{
    75  				Type: "Body",
    76  				Children: []TestTreeNode{
    77  					{
    78  						Type: "Attribute",
    79  						Children: []TestTreeNode{
    80  							{
    81  								Type: "comments",
    82  								Val:  "# aye aye aye\n",
    83  							},
    84  							{
    85  								Type: "identifier",
    86  								Val:  "a",
    87  							},
    88  							{
    89  								Type: "Tokens",
    90  								Val:  " =",
    91  							},
    92  							{
    93  								Type: "Expression",
    94  								Children: []TestTreeNode{
    95  									{
    96  										Type: "Tokens",
    97  										Val:  " 1",
    98  									},
    99  								},
   100  							},
   101  							{
   102  								Type: "comments",
   103  							},
   104  							{
   105  								Type: "Tokens",
   106  								Val:  "\n",
   107  							},
   108  						},
   109  					},
   110  				},
   111  			},
   112  		},
   113  		{
   114  			"a = 1 # because it is\n",
   115  			TestTreeNode{
   116  				Type: "Body",
   117  				Children: []TestTreeNode{
   118  					{
   119  						Type: "Attribute",
   120  						Children: []TestTreeNode{
   121  							{
   122  								Type: "comments",
   123  							},
   124  							{
   125  								Type: "identifier",
   126  								Val:  "a",
   127  							},
   128  							{
   129  								Type: "Tokens",
   130  								Val:  " =",
   131  							},
   132  							{
   133  								Type: "Expression",
   134  								Children: []TestTreeNode{
   135  									{
   136  										Type: "Tokens",
   137  										Val:  " 1",
   138  									},
   139  								},
   140  							},
   141  							{
   142  								Type: "comments",
   143  								Val:  " # because it is\n",
   144  							},
   145  						},
   146  					},
   147  				},
   148  			},
   149  		},
   150  		{
   151  			"# bee bee bee\n\nb = 1\n", // two newlines separate the comment from the attribute
   152  			TestTreeNode{
   153  				Type: "Body",
   154  				Children: []TestTreeNode{
   155  					{
   156  						Type: "Tokens", // Only lead/line comments attached to an object have type "comments"
   157  						Val:  "# bee bee bee\n\n",
   158  					},
   159  					{
   160  						Type: "Attribute",
   161  						Children: []TestTreeNode{
   162  							{
   163  								Type: "comments",
   164  							},
   165  							{
   166  								Type: "identifier",
   167  								Val:  "b",
   168  							},
   169  							{
   170  								Type: "Tokens",
   171  								Val:  " =",
   172  							},
   173  							{
   174  								Type: "Expression",
   175  								Children: []TestTreeNode{
   176  									{
   177  										Type: "Tokens",
   178  										Val:  " 1",
   179  									},
   180  								},
   181  							},
   182  							{
   183  								Type: "comments",
   184  								Val:  "",
   185  							},
   186  							{
   187  								Type: "Tokens",
   188  								Val:  "\n",
   189  							},
   190  						},
   191  					},
   192  				},
   193  			},
   194  		},
   195  		{
   196  			"a = (\n  1 + 2\n)\nb = 3\n",
   197  			TestTreeNode{
   198  				Type: "Body",
   199  				Children: []TestTreeNode{
   200  					{
   201  						Type: "Attribute",
   202  						Children: []TestTreeNode{
   203  							{Type: "comments"},
   204  							{
   205  								Type: "identifier",
   206  								Val:  "a",
   207  							},
   208  							{
   209  								Type: "Tokens",
   210  								Val:  " =",
   211  							},
   212  							{
   213  								Type: "Expression",
   214  								Children: []TestTreeNode{
   215  									{
   216  										Type: "Tokens",
   217  										Val:  " (\n  1 + 2\n)",
   218  									},
   219  								},
   220  							},
   221  							{Type: "comments"},
   222  							{
   223  								Type: "Tokens",
   224  								Val:  "\n",
   225  							},
   226  						},
   227  					},
   228  					{
   229  						Type: "Attribute",
   230  						Children: []TestTreeNode{
   231  							{Type: "comments"},
   232  							{
   233  								Type: "identifier",
   234  								Val:  "b",
   235  							},
   236  							{
   237  								Type: "Tokens",
   238  								Val:  " =",
   239  							},
   240  							{
   241  								Type: "Expression",
   242  								Children: []TestTreeNode{
   243  									{
   244  										Type: "Tokens",
   245  										Val:  " 3",
   246  									},
   247  								},
   248  							},
   249  							{Type: "comments"},
   250  							{
   251  								Type: "Tokens",
   252  								Val:  "\n",
   253  							},
   254  						},
   255  					},
   256  				},
   257  			},
   258  		},
   259  		{
   260  			"b {}\n",
   261  			TestTreeNode{
   262  				Type: "Body",
   263  				Children: []TestTreeNode{
   264  					{
   265  						Type: "Block",
   266  						Children: []TestTreeNode{
   267  							{
   268  								Type: "comments",
   269  							},
   270  							{
   271  								Type: "identifier",
   272  								Val:  "b",
   273  							},
   274  							{
   275  								Type: "blockLabels",
   276  							},
   277  							{
   278  								Type: "Tokens",
   279  								Val:  " {",
   280  							},
   281  							{
   282  								Type: "Body",
   283  							},
   284  							{
   285  								Type: "Tokens",
   286  								Val:  "}",
   287  							},
   288  							{
   289  								Type: "Tokens",
   290  								Val:  "\n",
   291  							},
   292  						},
   293  					},
   294  				},
   295  			},
   296  		},
   297  		{
   298  			"b label {}\n",
   299  			TestTreeNode{
   300  				Type: "Body",
   301  				Children: []TestTreeNode{
   302  					{
   303  						Type: "Block",
   304  						Children: []TestTreeNode{
   305  							{
   306  								Type: "comments",
   307  							},
   308  							{
   309  								Type: "identifier",
   310  								Val:  "b",
   311  							},
   312  							{
   313  								Type: "blockLabels",
   314  								Children: []TestTreeNode{
   315  									{
   316  										Type: "identifier",
   317  										Val:  " label",
   318  									},
   319  								},
   320  							},
   321  							{
   322  								Type: "Tokens",
   323  								Val:  " {",
   324  							},
   325  							{
   326  								Type: "Body",
   327  							},
   328  							{
   329  								Type: "Tokens",
   330  								Val:  "}",
   331  							},
   332  							{
   333  								Type: "Tokens",
   334  								Val:  "\n",
   335  							},
   336  						},
   337  					},
   338  				},
   339  			},
   340  		},
   341  		{
   342  			"b \"label\" {}\n",
   343  			TestTreeNode{
   344  				Type: "Body",
   345  				Children: []TestTreeNode{
   346  					{
   347  						Type: "Block",
   348  						Children: []TestTreeNode{
   349  							{
   350  								Type: "comments",
   351  							},
   352  							{
   353  								Type: "identifier",
   354  								Val:  "b",
   355  							},
   356  							{
   357  								Type: "blockLabels",
   358  								Children: []TestTreeNode{
   359  									{
   360  										Type: "quoted",
   361  										Val:  ` "label"`,
   362  									},
   363  								},
   364  							},
   365  							{
   366  								Type: "Tokens",
   367  								Val:  " {",
   368  							},
   369  							{
   370  								Type: "Body",
   371  							},
   372  							{
   373  								Type: "Tokens",
   374  								Val:  "}",
   375  							},
   376  							{
   377  								Type: "Tokens",
   378  								Val:  "\n",
   379  							},
   380  						},
   381  					},
   382  				},
   383  			},
   384  		},
   385  		{
   386  			"b \"label1\" /* foo */ \"label2\" {}\n",
   387  			TestTreeNode{
   388  				Type: "Body",
   389  				Children: []TestTreeNode{
   390  					{
   391  						Type: "Block",
   392  						Children: []TestTreeNode{
   393  							{
   394  								Type: "comments",
   395  							},
   396  							{
   397  								Type: "identifier",
   398  								Val:  "b",
   399  							},
   400  							{
   401  								Type: "blockLabels",
   402  								Children: []TestTreeNode{
   403  									{
   404  										Type: "quoted",
   405  										Val:  ` "label1"`,
   406  									},
   407  									{
   408  										// The comment between the labels just
   409  										// becomes an "unstructured tokens"
   410  										// node, because this isn't a place
   411  										// where we expect comments to attach
   412  										// to a particular object as
   413  										// documentation.
   414  										Type: "Tokens",
   415  										Val:  ` /* foo */`,
   416  									},
   417  									{
   418  										Type: "quoted",
   419  										Val:  ` "label2"`,
   420  									},
   421  								},
   422  							},
   423  							{
   424  								Type: "Tokens",
   425  								Val:  " {",
   426  							},
   427  							{
   428  								Type: "Body",
   429  							},
   430  							{
   431  								Type: "Tokens",
   432  								Val:  "}",
   433  							},
   434  							{
   435  								Type: "Tokens",
   436  								Val:  "\n",
   437  							},
   438  						},
   439  					},
   440  				},
   441  			},
   442  		},
   443  		{
   444  			"b {\n  a = 1\n}\n",
   445  			TestTreeNode{
   446  				Type: "Body",
   447  				Children: []TestTreeNode{
   448  					{
   449  						Type: "Block",
   450  						Children: []TestTreeNode{
   451  							{
   452  								Type: "comments",
   453  							},
   454  							{
   455  								Type: "identifier",
   456  								Val:  "b",
   457  							},
   458  							{
   459  								Type: "blockLabels",
   460  							},
   461  							{
   462  								Type: "Tokens",
   463  								Val:  " {",
   464  							},
   465  							{
   466  								Type: "Body",
   467  								Children: []TestTreeNode{
   468  									{
   469  										Type: "Tokens",
   470  										Val:  "\n",
   471  									},
   472  									{
   473  										Type: "Attribute",
   474  										Children: []TestTreeNode{
   475  											{
   476  												Type: "comments",
   477  											},
   478  											{
   479  												Type: "identifier",
   480  												Val:  "  a",
   481  											},
   482  											{
   483  												Type: "Tokens",
   484  												Val:  " =",
   485  											},
   486  											{
   487  												Type: "Expression",
   488  												Children: []TestTreeNode{
   489  													{
   490  														Type: "Tokens",
   491  														Val:  " 1",
   492  													},
   493  												},
   494  											},
   495  											{
   496  												Type: "comments",
   497  											},
   498  											{
   499  												Type: "Tokens",
   500  												Val:  "\n",
   501  											},
   502  										},
   503  									},
   504  								},
   505  							},
   506  							{
   507  								Type: "Tokens",
   508  								Val:  "}",
   509  							},
   510  							{
   511  								Type: "Tokens",
   512  								Val:  "\n",
   513  							},
   514  						},
   515  					},
   516  				},
   517  			},
   518  		},
   519  		{
   520  			"a = foo\n",
   521  			TestTreeNode{
   522  				Type: "Body",
   523  				Children: []TestTreeNode{
   524  					{
   525  						Type: "Attribute",
   526  						Children: []TestTreeNode{
   527  							{
   528  								Type: "comments",
   529  							},
   530  							{
   531  								Type: "identifier",
   532  								Val:  "a",
   533  							},
   534  							{
   535  								Type: "Tokens",
   536  								Val:  " =",
   537  							},
   538  							{
   539  								Type: "Expression",
   540  								Children: []TestTreeNode{
   541  									{
   542  										Type: "Traversal",
   543  										Children: []TestTreeNode{
   544  											{
   545  												Type: "TraverseName",
   546  												Children: []TestTreeNode{
   547  													{
   548  														Type: "identifier",
   549  														Val:  " foo",
   550  													},
   551  												},
   552  											},
   553  										},
   554  									},
   555  								},
   556  							},
   557  							{
   558  								Type: "comments",
   559  							},
   560  							{
   561  								Type: "Tokens",
   562  								Val:  "\n",
   563  							},
   564  						},
   565  					},
   566  				},
   567  			},
   568  		},
   569  		{
   570  			"a = foo.bar\n",
   571  			TestTreeNode{
   572  				Type: "Body",
   573  				Children: []TestTreeNode{
   574  					{
   575  						Type: "Attribute",
   576  						Children: []TestTreeNode{
   577  							{
   578  								Type: "comments",
   579  							},
   580  							{
   581  								Type: "identifier",
   582  								Val:  "a",
   583  							},
   584  							{
   585  								Type: "Tokens",
   586  								Val:  " =",
   587  							},
   588  							{
   589  								Type: "Expression",
   590  								Children: []TestTreeNode{
   591  									{
   592  										Type: "Traversal",
   593  										Children: []TestTreeNode{
   594  											{
   595  												Type: "TraverseName",
   596  												Children: []TestTreeNode{
   597  													{
   598  														Type: "identifier",
   599  														Val:  " foo",
   600  													},
   601  												},
   602  											},
   603  											{
   604  												Type: "TraverseName",
   605  												Children: []TestTreeNode{
   606  													{
   607  														Type: "Tokens",
   608  														Val:  ".",
   609  													},
   610  													{
   611  														Type: "identifier",
   612  														Val:  "bar",
   613  													},
   614  												},
   615  											},
   616  										},
   617  									},
   618  								},
   619  							},
   620  							{
   621  								Type: "comments",
   622  							},
   623  							{
   624  								Type: "Tokens",
   625  								Val:  "\n",
   626  							},
   627  						},
   628  					},
   629  				},
   630  			},
   631  		},
   632  		{
   633  			"a = foo[0]\n",
   634  			TestTreeNode{
   635  				Type: "Body",
   636  				Children: []TestTreeNode{
   637  					{
   638  						Type: "Attribute",
   639  						Children: []TestTreeNode{
   640  							{
   641  								Type: "comments",
   642  							},
   643  							{
   644  								Type: "identifier",
   645  								Val:  "a",
   646  							},
   647  							{
   648  								Type: "Tokens",
   649  								Val:  " =",
   650  							},
   651  							{
   652  								Type: "Expression",
   653  								Children: []TestTreeNode{
   654  									{
   655  										Type: "Traversal",
   656  										Children: []TestTreeNode{
   657  											{
   658  												Type: "TraverseName",
   659  												Children: []TestTreeNode{
   660  													{
   661  														Type: "identifier",
   662  														Val:  " foo",
   663  													},
   664  												},
   665  											},
   666  											{
   667  												Type: "TraverseIndex",
   668  												Children: []TestTreeNode{
   669  													{
   670  														Type: "Tokens",
   671  														Val:  "[",
   672  													},
   673  													{
   674  														Type: "number",
   675  														Val:  "0",
   676  													},
   677  													{
   678  														Type: "Tokens",
   679  														Val:  "]",
   680  													},
   681  												},
   682  											},
   683  										},
   684  									},
   685  								},
   686  							},
   687  							{
   688  								Type: "comments",
   689  							},
   690  							{
   691  								Type: "Tokens",
   692  								Val:  "\n",
   693  							},
   694  						},
   695  					},
   696  				},
   697  			},
   698  		},
   699  		{
   700  			"a = foo.0\n",
   701  			TestTreeNode{
   702  				Type: "Body",
   703  				Children: []TestTreeNode{
   704  					{
   705  						Type: "Attribute",
   706  						Children: []TestTreeNode{
   707  							{
   708  								Type: "comments",
   709  							},
   710  							{
   711  								Type: "identifier",
   712  								Val:  "a",
   713  							},
   714  							{
   715  								Type: "Tokens",
   716  								Val:  " =",
   717  							},
   718  							{
   719  								Type: "Expression",
   720  								Children: []TestTreeNode{
   721  									{
   722  										Type: "Traversal",
   723  										Children: []TestTreeNode{
   724  											{
   725  												Type: "TraverseName",
   726  												Children: []TestTreeNode{
   727  													{
   728  														Type: "identifier",
   729  														Val:  " foo",
   730  													},
   731  												},
   732  											},
   733  											{
   734  												Type: "TraverseIndex",
   735  												Children: []TestTreeNode{
   736  													{
   737  														Type: "Tokens",
   738  														Val:  ".",
   739  													},
   740  													{
   741  														Type: "number",
   742  														Val:  "0",
   743  													},
   744  												},
   745  											},
   746  										},
   747  									},
   748  								},
   749  							},
   750  							{
   751  								Type: "comments",
   752  							},
   753  							{
   754  								Type: "Tokens",
   755  								Val:  "\n",
   756  							},
   757  						},
   758  					},
   759  				},
   760  			},
   761  		},
   762  		{
   763  			"a = foo.*\n",
   764  			TestTreeNode{
   765  				Type: "Body",
   766  				Children: []TestTreeNode{
   767  					{
   768  						Type: "Attribute",
   769  						Children: []TestTreeNode{
   770  							{
   771  								Type: "comments",
   772  							},
   773  							{
   774  								Type: "identifier",
   775  								Val:  "a",
   776  							},
   777  							{
   778  								Type: "Tokens",
   779  								Val:  " =",
   780  							},
   781  							{
   782  								Type: "Expression",
   783  								Children: []TestTreeNode{
   784  									{
   785  										Type: "Traversal",
   786  										Children: []TestTreeNode{
   787  											{
   788  												Type: "TraverseName",
   789  												Children: []TestTreeNode{
   790  													{
   791  														Type: "identifier",
   792  														Val:  " foo",
   793  													},
   794  												},
   795  											},
   796  										},
   797  									},
   798  									{
   799  										Type: "Tokens",
   800  										Val:  ".*",
   801  									},
   802  								},
   803  							},
   804  							{
   805  								Type: "comments",
   806  							},
   807  							{
   808  								Type: "Tokens",
   809  								Val:  "\n",
   810  							},
   811  						},
   812  					},
   813  				},
   814  			},
   815  		},
   816  		{
   817  			"a = foo.*.bar\n",
   818  			TestTreeNode{
   819  				Type: "Body",
   820  				Children: []TestTreeNode{
   821  					{
   822  						Type: "Attribute",
   823  						Children: []TestTreeNode{
   824  							{
   825  								Type: "comments",
   826  							},
   827  							{
   828  								Type: "identifier",
   829  								Val:  "a",
   830  							},
   831  							{
   832  								Type: "Tokens",
   833  								Val:  " =",
   834  							},
   835  							{
   836  								Type: "Expression",
   837  								Children: []TestTreeNode{
   838  									{
   839  										Type: "Traversal",
   840  										Children: []TestTreeNode{
   841  											{
   842  												Type: "TraverseName",
   843  												Children: []TestTreeNode{
   844  													{
   845  														Type: "identifier",
   846  														Val:  " foo",
   847  													},
   848  												},
   849  											},
   850  										},
   851  									},
   852  									{
   853  										Type: "Tokens",
   854  										Val:  ".*.bar",
   855  									},
   856  								},
   857  							},
   858  							{
   859  								Type: "comments",
   860  							},
   861  							{
   862  								Type: "Tokens",
   863  								Val:  "\n",
   864  							},
   865  						},
   866  					},
   867  				},
   868  			},
   869  		},
   870  		{
   871  			"a = foo[*]\n",
   872  			TestTreeNode{
   873  				Type: "Body",
   874  				Children: []TestTreeNode{
   875  					{
   876  						Type: "Attribute",
   877  						Children: []TestTreeNode{
   878  							{
   879  								Type: "comments",
   880  							},
   881  							{
   882  								Type: "identifier",
   883  								Val:  "a",
   884  							},
   885  							{
   886  								Type: "Tokens",
   887  								Val:  " =",
   888  							},
   889  							{
   890  								Type: "Expression",
   891  								Children: []TestTreeNode{
   892  									{
   893  										Type: "Traversal",
   894  										Children: []TestTreeNode{
   895  											{
   896  												Type: "TraverseName",
   897  												Children: []TestTreeNode{
   898  													{
   899  														Type: "identifier",
   900  														Val:  " foo",
   901  													},
   902  												},
   903  											},
   904  										},
   905  									},
   906  									{
   907  										Type: "Tokens",
   908  										Val:  "[*]",
   909  									},
   910  								},
   911  							},
   912  							{
   913  								Type: "comments",
   914  							},
   915  							{
   916  								Type: "Tokens",
   917  								Val:  "\n",
   918  							},
   919  						},
   920  					},
   921  				},
   922  			},
   923  		},
   924  		{
   925  			"a = foo[*].bar\n",
   926  			TestTreeNode{
   927  				Type: "Body",
   928  				Children: []TestTreeNode{
   929  					{
   930  						Type: "Attribute",
   931  						Children: []TestTreeNode{
   932  							{
   933  								Type: "comments",
   934  							},
   935  							{
   936  								Type: "identifier",
   937  								Val:  "a",
   938  							},
   939  							{
   940  								Type: "Tokens",
   941  								Val:  " =",
   942  							},
   943  							{
   944  								Type: "Expression",
   945  								Children: []TestTreeNode{
   946  									{
   947  										Type: "Traversal",
   948  										Children: []TestTreeNode{
   949  											{
   950  												Type: "TraverseName",
   951  												Children: []TestTreeNode{
   952  													{
   953  														Type: "identifier",
   954  														Val:  " foo",
   955  													},
   956  												},
   957  											},
   958  										},
   959  									},
   960  									{
   961  										Type: "Tokens",
   962  										Val:  "[*].bar",
   963  									},
   964  								},
   965  							},
   966  							{
   967  								Type: "comments",
   968  							},
   969  							{
   970  								Type: "Tokens",
   971  								Val:  "\n",
   972  							},
   973  						},
   974  					},
   975  				},
   976  			},
   977  		},
   978  		{
   979  			"a = foo[bar]\n",
   980  			TestTreeNode{
   981  				Type: "Body",
   982  				Children: []TestTreeNode{
   983  					{
   984  						Type: "Attribute",
   985  						Children: []TestTreeNode{
   986  							{
   987  								Type: "comments",
   988  							},
   989  							{
   990  								Type: "identifier",
   991  								Val:  "a",
   992  							},
   993  							{
   994  								Type: "Tokens",
   995  								Val:  " =",
   996  							},
   997  							{
   998  								Type: "Expression",
   999  								Children: []TestTreeNode{
  1000  									{
  1001  										Type: "Traversal",
  1002  										Children: []TestTreeNode{
  1003  											{
  1004  												Type: "TraverseName",
  1005  												Children: []TestTreeNode{
  1006  													{
  1007  														Type: "identifier",
  1008  														Val:  " foo",
  1009  													},
  1010  												},
  1011  											},
  1012  										},
  1013  									},
  1014  									{
  1015  										Type: "Tokens",
  1016  										Val:  "[",
  1017  									},
  1018  									{
  1019  										Type: "Traversal",
  1020  										Children: []TestTreeNode{
  1021  											{
  1022  												Type: "TraverseName",
  1023  												Children: []TestTreeNode{
  1024  													{
  1025  														Type: "identifier",
  1026  														Val:  "bar",
  1027  													},
  1028  												},
  1029  											},
  1030  										},
  1031  									},
  1032  									{
  1033  										Type: "Tokens",
  1034  										Val:  "]",
  1035  									},
  1036  								},
  1037  							},
  1038  							{
  1039  								Type: "comments",
  1040  							},
  1041  							{
  1042  								Type: "Tokens",
  1043  								Val:  "\n",
  1044  							},
  1045  						},
  1046  					},
  1047  				},
  1048  			},
  1049  		},
  1050  		{
  1051  			"a = foo[bar.baz]\n",
  1052  			TestTreeNode{
  1053  				Type: "Body",
  1054  				Children: []TestTreeNode{
  1055  					{
  1056  						Type: "Attribute",
  1057  						Children: []TestTreeNode{
  1058  							{
  1059  								Type: "comments",
  1060  							},
  1061  							{
  1062  								Type: "identifier",
  1063  								Val:  "a",
  1064  							},
  1065  							{
  1066  								Type: "Tokens",
  1067  								Val:  " =",
  1068  							},
  1069  							{
  1070  								Type: "Expression",
  1071  								Children: []TestTreeNode{
  1072  									{
  1073  										Type: "Traversal",
  1074  										Children: []TestTreeNode{
  1075  											{
  1076  												Type: "TraverseName",
  1077  												Children: []TestTreeNode{
  1078  													{
  1079  														Type: "identifier",
  1080  														Val:  " foo",
  1081  													},
  1082  												},
  1083  											},
  1084  										},
  1085  									},
  1086  									{
  1087  										Type: "Tokens",
  1088  										Val:  "[",
  1089  									},
  1090  									{
  1091  										Type: "Traversal",
  1092  										Children: []TestTreeNode{
  1093  											{
  1094  												Type: "TraverseName",
  1095  												Children: []TestTreeNode{
  1096  													{
  1097  														Type: "identifier",
  1098  														Val:  "bar",
  1099  													},
  1100  												},
  1101  											},
  1102  											{
  1103  												Type: "TraverseName",
  1104  												Children: []TestTreeNode{
  1105  													{
  1106  														Type: "Tokens",
  1107  														Val:  ".",
  1108  													},
  1109  													{
  1110  														Type: "identifier",
  1111  														Val:  "baz",
  1112  													},
  1113  												},
  1114  											},
  1115  										},
  1116  									},
  1117  									{
  1118  										Type: "Tokens",
  1119  										Val:  "]",
  1120  									},
  1121  								},
  1122  							},
  1123  							{
  1124  								Type: "comments",
  1125  							},
  1126  							{
  1127  								Type: "Tokens",
  1128  								Val:  "\n",
  1129  							},
  1130  						},
  1131  					},
  1132  				},
  1133  			},
  1134  		},
  1135  		{
  1136  			"a = foo[bar].baz\n",
  1137  			TestTreeNode{
  1138  				Type: "Body",
  1139  				Children: []TestTreeNode{
  1140  					{
  1141  						Type: "Attribute",
  1142  						Children: []TestTreeNode{
  1143  							{
  1144  								Type: "comments",
  1145  							},
  1146  							{
  1147  								Type: "identifier",
  1148  								Val:  "a",
  1149  							},
  1150  							{
  1151  								Type: "Tokens",
  1152  								Val:  " =",
  1153  							},
  1154  							{
  1155  								Type: "Expression",
  1156  								Children: []TestTreeNode{
  1157  									{
  1158  										Type: "Traversal",
  1159  										Children: []TestTreeNode{
  1160  											{
  1161  												Type: "TraverseName",
  1162  												Children: []TestTreeNode{
  1163  													{
  1164  														Type: "identifier",
  1165  														Val:  " foo",
  1166  													},
  1167  												},
  1168  											},
  1169  										},
  1170  									},
  1171  									{
  1172  										Type: "Tokens",
  1173  										Val:  "[",
  1174  									},
  1175  									{
  1176  										Type: "Traversal",
  1177  										Children: []TestTreeNode{
  1178  											{
  1179  												Type: "TraverseName",
  1180  												Children: []TestTreeNode{
  1181  													{
  1182  														Type: "identifier",
  1183  														Val:  "bar",
  1184  													},
  1185  												},
  1186  											},
  1187  										},
  1188  									},
  1189  									{
  1190  										Type: "Tokens",
  1191  										Val:  "].baz",
  1192  									},
  1193  								},
  1194  							},
  1195  							{
  1196  								Type: "comments",
  1197  							},
  1198  							{
  1199  								Type: "Tokens",
  1200  								Val:  "\n",
  1201  							},
  1202  						},
  1203  					},
  1204  				},
  1205  			},
  1206  		},
  1207  	}
  1208  
  1209  	for _, test := range tests {
  1210  		t.Run(test.src, func(t *testing.T) {
  1211  			file, diags := parse([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1})
  1212  			if len(diags) > 0 {
  1213  				for _, diag := range diags {
  1214  					t.Logf(" - %s", diag.Error())
  1215  				}
  1216  				t.Fatalf("unexpected diagnostics")
  1217  			}
  1218  
  1219  			got := makeTestTree(file.body)
  1220  
  1221  			if !cmp.Equal(got, test.want) {
  1222  				diff := cmp.Diff(got, test.want)
  1223  				t.Errorf(
  1224  					"wrong result\ninput:\n%s\n\ngot:\n%s\nwant:%s\n\ndiff:\n%s",
  1225  					test.src,
  1226  					spew.Sdump(got),
  1227  					spew.Sdump(test.want),
  1228  					diff,
  1229  				)
  1230  			}
  1231  		})
  1232  	}
  1233  }
  1234  
  1235  func TestPartitionTokens(t *testing.T) {
  1236  	tests := []struct {
  1237  		tokens    hclsyntax.Tokens
  1238  		rng       hcl.Range
  1239  		wantStart int
  1240  		wantEnd   int
  1241  	}{
  1242  		{
  1243  			hclsyntax.Tokens{},
  1244  			hcl.Range{
  1245  				Start: hcl.Pos{Byte: 0},
  1246  				End:   hcl.Pos{Byte: 0},
  1247  			},
  1248  			0,
  1249  			0,
  1250  		},
  1251  		{
  1252  			hclsyntax.Tokens{
  1253  				{
  1254  					Type: hclsyntax.TokenIdent,
  1255  					Range: hcl.Range{
  1256  						Start: hcl.Pos{Byte: 0},
  1257  						End:   hcl.Pos{Byte: 4},
  1258  					},
  1259  				},
  1260  			},
  1261  			hcl.Range{
  1262  				Start: hcl.Pos{Byte: 0},
  1263  				End:   hcl.Pos{Byte: 4},
  1264  			},
  1265  			0,
  1266  			1,
  1267  		},
  1268  		{
  1269  			hclsyntax.Tokens{
  1270  				{
  1271  					Type: hclsyntax.TokenIdent,
  1272  					Range: hcl.Range{
  1273  						Start: hcl.Pos{Byte: 0},
  1274  						End:   hcl.Pos{Byte: 4},
  1275  					},
  1276  				},
  1277  				{
  1278  					Type: hclsyntax.TokenIdent,
  1279  					Range: hcl.Range{
  1280  						Start: hcl.Pos{Byte: 4},
  1281  						End:   hcl.Pos{Byte: 8},
  1282  					},
  1283  				},
  1284  				{
  1285  					Type: hclsyntax.TokenIdent,
  1286  					Range: hcl.Range{
  1287  						Start: hcl.Pos{Byte: 8},
  1288  						End:   hcl.Pos{Byte: 12},
  1289  					},
  1290  				},
  1291  			},
  1292  			hcl.Range{
  1293  				Start: hcl.Pos{Byte: 4},
  1294  				End:   hcl.Pos{Byte: 8},
  1295  			},
  1296  			1,
  1297  			2,
  1298  		},
  1299  		{
  1300  			hclsyntax.Tokens{
  1301  				{
  1302  					Type: hclsyntax.TokenIdent,
  1303  					Range: hcl.Range{
  1304  						Start: hcl.Pos{Byte: 0},
  1305  						End:   hcl.Pos{Byte: 4},
  1306  					},
  1307  				},
  1308  				{
  1309  					Type: hclsyntax.TokenIdent,
  1310  					Range: hcl.Range{
  1311  						Start: hcl.Pos{Byte: 4},
  1312  						End:   hcl.Pos{Byte: 8},
  1313  					},
  1314  				},
  1315  				{
  1316  					Type: hclsyntax.TokenIdent,
  1317  					Range: hcl.Range{
  1318  						Start: hcl.Pos{Byte: 8},
  1319  						End:   hcl.Pos{Byte: 12},
  1320  					},
  1321  				},
  1322  			},
  1323  			hcl.Range{
  1324  				Start: hcl.Pos{Byte: 0},
  1325  				End:   hcl.Pos{Byte: 8},
  1326  			},
  1327  			0,
  1328  			2,
  1329  		},
  1330  		{
  1331  			hclsyntax.Tokens{
  1332  				{
  1333  					Type: hclsyntax.TokenIdent,
  1334  					Range: hcl.Range{
  1335  						Start: hcl.Pos{Byte: 0},
  1336  						End:   hcl.Pos{Byte: 4},
  1337  					},
  1338  				},
  1339  				{
  1340  					Type: hclsyntax.TokenIdent,
  1341  					Range: hcl.Range{
  1342  						Start: hcl.Pos{Byte: 4},
  1343  						End:   hcl.Pos{Byte: 8},
  1344  					},
  1345  				},
  1346  				{
  1347  					Type: hclsyntax.TokenIdent,
  1348  					Range: hcl.Range{
  1349  						Start: hcl.Pos{Byte: 8},
  1350  						End:   hcl.Pos{Byte: 12},
  1351  					},
  1352  				},
  1353  			},
  1354  			hcl.Range{
  1355  				Start: hcl.Pos{Byte: 4},
  1356  				End:   hcl.Pos{Byte: 12},
  1357  			},
  1358  			1,
  1359  			3,
  1360  		},
  1361  	}
  1362  
  1363  	prettyConfig := &pretty.Config{
  1364  		Diffable:          true,
  1365  		IncludeUnexported: true,
  1366  		PrintStringers:    true,
  1367  	}
  1368  
  1369  	for i, test := range tests {
  1370  		t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
  1371  			gotStart, gotEnd := partitionTokens(test.tokens, test.rng)
  1372  
  1373  			if gotStart != test.wantStart || gotEnd != test.wantEnd {
  1374  				t.Errorf(
  1375  					"wrong result\ntokens: %s\nrange: %#v\ngot:   %d, %d\nwant:  %d, %d",
  1376  					prettyConfig.Sprint(test.tokens), test.rng,
  1377  					gotStart, test.wantStart,
  1378  					gotEnd, test.wantEnd,
  1379  				)
  1380  			}
  1381  		})
  1382  	}
  1383  }
  1384  
  1385  func TestPartitionLeadCommentTokens(t *testing.T) {
  1386  	tests := []struct {
  1387  		tokens    hclsyntax.Tokens
  1388  		wantStart int
  1389  	}{
  1390  		{
  1391  			hclsyntax.Tokens{},
  1392  			0,
  1393  		},
  1394  		{
  1395  			hclsyntax.Tokens{
  1396  				{
  1397  					Type: hclsyntax.TokenComment,
  1398  				},
  1399  			},
  1400  			0,
  1401  		},
  1402  		{
  1403  			hclsyntax.Tokens{
  1404  				{
  1405  					Type: hclsyntax.TokenComment,
  1406  				},
  1407  				{
  1408  					Type: hclsyntax.TokenComment,
  1409  				},
  1410  			},
  1411  			0,
  1412  		},
  1413  		{
  1414  			hclsyntax.Tokens{
  1415  				{
  1416  					Type: hclsyntax.TokenComment,
  1417  				},
  1418  				{
  1419  					Type: hclsyntax.TokenNewline,
  1420  				},
  1421  			},
  1422  			2,
  1423  		},
  1424  		{
  1425  			hclsyntax.Tokens{
  1426  				{
  1427  					Type: hclsyntax.TokenComment,
  1428  				},
  1429  				{
  1430  					Type: hclsyntax.TokenNewline,
  1431  				},
  1432  				{
  1433  					Type: hclsyntax.TokenComment,
  1434  				},
  1435  			},
  1436  			2,
  1437  		},
  1438  	}
  1439  
  1440  	prettyConfig := &pretty.Config{
  1441  		Diffable:          true,
  1442  		IncludeUnexported: true,
  1443  		PrintStringers:    true,
  1444  	}
  1445  
  1446  	for i, test := range tests {
  1447  		t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
  1448  			gotStart := partitionLeadCommentTokens(test.tokens)
  1449  
  1450  			if gotStart != test.wantStart {
  1451  				t.Errorf(
  1452  					"wrong result\ntokens: %s\ngot:   %d\nwant:  %d",
  1453  					prettyConfig.Sprint(test.tokens),
  1454  					gotStart, test.wantStart,
  1455  				)
  1456  			}
  1457  		})
  1458  	}
  1459  }
  1460  
  1461  func TestLexConfig(t *testing.T) {
  1462  	tests := []struct {
  1463  		input string
  1464  		want  Tokens
  1465  	}{
  1466  		{
  1467  			`a  b `,
  1468  			Tokens{
  1469  				{
  1470  					Type:         hclsyntax.TokenIdent,
  1471  					Bytes:        []byte(`a`),
  1472  					SpacesBefore: 0,
  1473  				},
  1474  				{
  1475  					Type:         hclsyntax.TokenIdent,
  1476  					Bytes:        []byte(`b`),
  1477  					SpacesBefore: 2,
  1478  				},
  1479  				{
  1480  					Type:         hclsyntax.TokenEOF,
  1481  					Bytes:        []byte{},
  1482  					SpacesBefore: 1,
  1483  				},
  1484  			},
  1485  		},
  1486  		{
  1487  			`
  1488  foo "bar" "baz" {
  1489      pizza = " cheese "
  1490  }
  1491  `,
  1492  			Tokens{
  1493  				{
  1494  					Type:         hclsyntax.TokenNewline,
  1495  					Bytes:        []byte{'\n'},
  1496  					SpacesBefore: 0,
  1497  				},
  1498  				{
  1499  					Type:         hclsyntax.TokenIdent,
  1500  					Bytes:        []byte(`foo`),
  1501  					SpacesBefore: 0,
  1502  				},
  1503  				{
  1504  					Type:         hclsyntax.TokenOQuote,
  1505  					Bytes:        []byte(`"`),
  1506  					SpacesBefore: 1,
  1507  				},
  1508  				{
  1509  					Type:         hclsyntax.TokenQuotedLit,
  1510  					Bytes:        []byte(`bar`),
  1511  					SpacesBefore: 0,
  1512  				},
  1513  				{
  1514  					Type:         hclsyntax.TokenCQuote,
  1515  					Bytes:        []byte(`"`),
  1516  					SpacesBefore: 0,
  1517  				},
  1518  				{
  1519  					Type:         hclsyntax.TokenOQuote,
  1520  					Bytes:        []byte(`"`),
  1521  					SpacesBefore: 1,
  1522  				},
  1523  				{
  1524  					Type:         hclsyntax.TokenQuotedLit,
  1525  					Bytes:        []byte(`baz`),
  1526  					SpacesBefore: 0,
  1527  				},
  1528  				{
  1529  					Type:         hclsyntax.TokenCQuote,
  1530  					Bytes:        []byte(`"`),
  1531  					SpacesBefore: 0,
  1532  				},
  1533  				{
  1534  					Type:         hclsyntax.TokenOBrace,
  1535  					Bytes:        []byte(`{`),
  1536  					SpacesBefore: 1,
  1537  				},
  1538  				{
  1539  					Type:         hclsyntax.TokenNewline,
  1540  					Bytes:        []byte("\n"),
  1541  					SpacesBefore: 0,
  1542  				},
  1543  				{
  1544  					Type:         hclsyntax.TokenIdent,
  1545  					Bytes:        []byte(`pizza`),
  1546  					SpacesBefore: 4,
  1547  				},
  1548  				{
  1549  					Type:         hclsyntax.TokenEqual,
  1550  					Bytes:        []byte(`=`),
  1551  					SpacesBefore: 1,
  1552  				},
  1553  				{
  1554  					Type:         hclsyntax.TokenOQuote,
  1555  					Bytes:        []byte(`"`),
  1556  					SpacesBefore: 1,
  1557  				},
  1558  				{
  1559  					Type:         hclsyntax.TokenQuotedLit,
  1560  					Bytes:        []byte(` cheese `),
  1561  					SpacesBefore: 0,
  1562  				},
  1563  				{
  1564  					Type:         hclsyntax.TokenCQuote,
  1565  					Bytes:        []byte(`"`),
  1566  					SpacesBefore: 0,
  1567  				},
  1568  				{
  1569  					Type:         hclsyntax.TokenNewline,
  1570  					Bytes:        []byte("\n"),
  1571  					SpacesBefore: 0,
  1572  				},
  1573  				{
  1574  					Type:         hclsyntax.TokenCBrace,
  1575  					Bytes:        []byte(`}`),
  1576  					SpacesBefore: 0,
  1577  				},
  1578  				{
  1579  					Type:         hclsyntax.TokenNewline,
  1580  					Bytes:        []byte("\n"),
  1581  					SpacesBefore: 0,
  1582  				},
  1583  				{
  1584  					Type:         hclsyntax.TokenEOF,
  1585  					Bytes:        []byte{},
  1586  					SpacesBefore: 0,
  1587  				},
  1588  			},
  1589  		},
  1590  	}
  1591  
  1592  	prettyConfig := &pretty.Config{
  1593  		Diffable:          true,
  1594  		IncludeUnexported: true,
  1595  		PrintStringers:    true,
  1596  	}
  1597  
  1598  	for _, test := range tests {
  1599  		t.Run(test.input, func(t *testing.T) {
  1600  			got := lexConfig([]byte(test.input))
  1601  
  1602  			if !reflect.DeepEqual(got, test.want) {
  1603  				diff := prettyConfig.Compare(test.want, got)
  1604  				t.Errorf(
  1605  					"wrong result\ninput: %s\ndiff:  %s", test.input, diff,
  1606  				)
  1607  			}
  1608  		})
  1609  	}
  1610  }