github.com/gotranspile/cxgo@v0.3.7/flow_test.go (about)

     1  package cxgo
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/gotranspile/cxgo/libs"
    13  	"github.com/gotranspile/cxgo/types"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func numStmt(n int) CStmt {
    18  	return NewCExprStmt1(&CallExpr{
    19  		Fun:  FuncIdent{types.NewIdent("foo", types.UnkT(1))},
    20  		Args: []Expr{cIntLit(int64(n))},
    21  	})
    22  }
    23  
    24  func varDecl(n int) CStmt {
    25  	return &CDeclStmt{&CVarDecl{
    26  		CVarSpec: CVarSpec{
    27  			Type: types.NamedT("bar", types.UnkT(1)),
    28  			Names: []*types.Ident{
    29  				types.NewIdent(fmt.Sprintf("foo%d", n), types.UnkT(1)),
    30  			},
    31  			Inits: []Expr{
    32  				cIntLit(int64(n)),
    33  			},
    34  		},
    35  	}}
    36  }
    37  
    38  func numCond(n int) BoolExpr {
    39  	return BoolIdent{types.NewIdent(fmt.Sprintf("%d", n), types.BoolT())}
    40  }
    41  
    42  func ret(n int) CStmt {
    43  	return &CReturnStmt{Expr: cIntLit(int64(n))}
    44  }
    45  
    46  func newBlock(stmts ...CStmt) *BlockStmt {
    47  	return &BlockStmt{Stmts: stmts}
    48  }
    49  
    50  var casesControlFlow = []struct {
    51  	name string
    52  	tree []CStmt
    53  	exp  string
    54  	dom  string
    55  	flat string
    56  }{
    57  	{
    58  		name: "return",
    59  		tree: []CStmt{
    60  			ret(1),
    61  		},
    62  		exp: `
    63  	n2[label="return 1",shape="box"];
    64  	n1->n2;
    65  `,
    66  		dom: `
    67  	n2[label="return 1",shape="box"];
    68  	n1->n2;
    69  `,
    70  		flat: `
    71  return 1
    72  `,
    73  	},
    74  	{
    75  		name: "no return",
    76  		tree: []CStmt{
    77  			numStmt(1),
    78  		},
    79  		exp: `
    80  	n2[label="foo(1)",shape="box"];
    81  	n3[label="return",shape="box"];
    82  	n1->n2;
    83  	n2->n3;
    84  	n3->n2[color="#99999955"];
    85  `,
    86  		dom: `
    87  	n2[label="foo(1)",shape="box"];
    88  	n3[label="return",shape="box"];
    89  	n1->n2;
    90  	n2->n3;
    91  `,
    92  		flat: `
    93  foo(1)
    94  goto L_1
    95  L_1:
    96  return
    97  `,
    98  	},
    99  	{
   100  		name: "code and return",
   101  		tree: []CStmt{
   102  			numStmt(1),
   103  			ret(2),
   104  		},
   105  		exp: `
   106  	n2[label="foo(1)",shape="box"];
   107  	n3[label="return 2",shape="box"];
   108  	n1->n2;
   109  	n2->n3;
   110  	n3->n2[color="#99999955"];
   111  `,
   112  		dom: `
   113  	n2[label="foo(1)",shape="box"];
   114  	n3[label="return 2",shape="box"];
   115  	n1->n2;
   116  	n2->n3;
   117  `,
   118  		flat: `
   119  foo(1)
   120  goto L_1
   121  L_1:
   122  return 2
   123  `,
   124  	},
   125  	{
   126  		name: "code and return 2",
   127  		tree: []CStmt{
   128  			numStmt(1),
   129  			numStmt(2),
   130  			ret(3),
   131  		},
   132  		exp: `
   133  	n2[label="foo(1)\nfoo(2)",shape="box"];
   134  	n3[label="return 3",shape="box"];
   135  	n1->n2;
   136  	n2->n3;
   137  	n3->n2[color="#99999955"];
   138  `,
   139  		dom: `
   140  	n2[label="foo(1)\nfoo(2)",shape="box"];
   141  	n3[label="return 3",shape="box"];
   142  	n1->n2;
   143  	n2->n3;
   144  `,
   145  		flat: `
   146  foo(1)
   147  foo(2)
   148  goto L_1
   149  L_1:
   150  return 3
   151  `,
   152  	},
   153  	{
   154  		name: "code with decls",
   155  		tree: []CStmt{
   156  			&CIfStmt{Cond: numCond(1), Then: newBlock(varDecl(1))},
   157  			varDecl(2),
   158  			ret(1),
   159  		},
   160  		exp: `
   161  	n2[label="if 1",shape="hexagon"];
   162  	n3[label="var foo1 bar = 1",shape="box"];
   163  	n4[label="var foo2 bar = 2",shape="box"];
   164  	n5[label="return 1",shape="box"];
   165  	n1->n2;
   166  	n2->n3[color="#00aa00"];
   167  	n2->n4[color="#aa0000"];
   168  	n3->n2[color="#99999955"];
   169  	n3->n4;
   170  	n4->n3[color="#99999955"];
   171  	n4->n2[color="#99999955"];
   172  	n4->n5;
   173  	n5->n4[color="#99999955"];
   174  `,
   175  		dom: `
   176  	n2[label="if 1",shape="hexagon"];
   177  	n3[label="var foo1 bar = 1",shape="box"];
   178  	n4[label="var foo2 bar = 2",shape="box"];
   179  	n5[label="return 1",shape="box"];
   180  	n1->n2;
   181  	n2->n3;
   182  	n2->n4;
   183  	n4->n5;
   184  `,
   185  		flat: `
   186  var foo1 bar
   187  var foo2 bar
   188  if 1 {
   189  goto L_1
   190  } else {
   191  goto L_2
   192  }
   193  L_1:
   194  foo1 = 1
   195  goto L_2
   196  L_2:
   197  foo2 = 2
   198  goto L_3
   199  L_3:
   200  return 1
   201  `,
   202  	},
   203  	{
   204  		name: "if",
   205  		tree: []CStmt{
   206  			&CIfStmt{Cond: numCond(1), Then: newBlock(numStmt(2))},
   207  			numStmt(3),
   208  			numStmt(4),
   209  			ret(5),
   210  		},
   211  		exp: `
   212  	n2[label="if 1",shape="hexagon"];
   213  	n3[label="foo(2)",shape="box"];
   214  	n4[label="foo(3)\nfoo(4)",shape="box"];
   215  	n5[label="return 5",shape="box"];
   216  	n1->n2;
   217  	n2->n3[color="#00aa00"];
   218  	n2->n4[color="#aa0000"];
   219  	n3->n2[color="#99999955"];
   220  	n3->n4;
   221  	n4->n3[color="#99999955"];
   222  	n4->n2[color="#99999955"];
   223  	n4->n5;
   224  	n5->n4[color="#99999955"];
   225  `,
   226  		dom: `
   227  	n2[label="if 1",shape="hexagon"];
   228  	n3[label="foo(2)",shape="box"];
   229  	n4[label="foo(3)\nfoo(4)",shape="box"];
   230  	n5[label="return 5",shape="box"];
   231  	n1->n2;
   232  	n2->n3;
   233  	n2->n4;
   234  	n4->n5;
   235  `,
   236  		flat: `
   237  if 1 {
   238  goto L_1
   239  } else {
   240  goto L_2
   241  }
   242  L_1:
   243  foo(2)
   244  goto L_2
   245  L_2:
   246  foo(3)
   247  foo(4)
   248  goto L_3
   249  L_3:
   250  return 5
   251  `,
   252  	},
   253  	{
   254  		name: "if else",
   255  		tree: []CStmt{
   256  			&CIfStmt{Cond: numCond(1), Then: newBlock(numStmt(2)), Else: newBlock(numStmt(3))},
   257  			ret(4),
   258  		},
   259  		exp: `
   260  	n2[label="if 1",shape="hexagon"];
   261  	n3[label="foo(2)",shape="box"];
   262  	n4[label="return 4",shape="box"];
   263  	n5[label="foo(3)",shape="box"];
   264  	n1->n2;
   265  	n2->n3[color="#00aa00"];
   266  	n2->n5[color="#aa0000"];
   267  	n3->n2[color="#99999955"];
   268  	n3->n4;
   269  	n4->n3[color="#99999955"];
   270  	n4->n5[color="#99999955"];
   271  	n5->n2[color="#99999955"];
   272  	n5->n4;
   273  `,
   274  		dom: `
   275  	n2[label="if 1",shape="hexagon"];
   276  	n3[label="foo(2)",shape="box"];
   277  	n4[label="return 4",shape="box"];
   278  	n5[label="foo(3)",shape="box"];
   279  	n1->n2;
   280  	n2->n3;
   281  	n2->n4;
   282  	n2->n5;
   283  `,
   284  		flat: `
   285  if 1 {
   286  goto L_1
   287  } else {
   288  goto L_3
   289  }
   290  L_1:
   291  foo(2)
   292  goto L_2
   293  L_2:
   294  return 4
   295  L_3:
   296  foo(3)
   297  goto L_2
   298  `,
   299  	},
   300  	{
   301  		name: "if return else",
   302  		tree: []CStmt{
   303  			&CIfStmt{Cond: numCond(1), Then: newBlock(ret(2)), Else: newBlock(numStmt(3))},
   304  			ret(4),
   305  		},
   306  		exp: `
   307  	n2[label="if 1",shape="hexagon"];
   308  	n3[label="return 2",shape="box"];
   309  	n4[label="foo(3)",shape="box"];
   310  	n5[label="return 4",shape="box"];
   311  	n1->n2;
   312  	n2->n3[color="#00aa00"];
   313  	n2->n4[color="#aa0000"];
   314  	n3->n2[color="#99999955"];
   315  	n4->n2[color="#99999955"];
   316  	n4->n5;
   317  	n5->n4[color="#99999955"];
   318  `,
   319  		dom: `
   320  	n2[label="if 1",shape="hexagon"];
   321  	n3[label="return 2",shape="box"];
   322  	n4[label="foo(3)",shape="box"];
   323  	n5[label="return 4",shape="box"];
   324  	n1->n2;
   325  	n2->n3;
   326  	n2->n4;
   327  	n4->n5;
   328  `,
   329  		flat: `
   330  if 1 {
   331  goto L_1
   332  } else {
   333  goto L_2
   334  }
   335  L_1:
   336  return 2
   337  L_2:
   338  foo(3)
   339  goto L_3
   340  L_3:
   341  return 4
   342  `,
   343  	},
   344  	{
   345  		name: "if else return",
   346  		tree: []CStmt{
   347  			&CIfStmt{Cond: numCond(1), Then: newBlock(numStmt(2)), Else: newBlock(ret(3))},
   348  			ret(4),
   349  		},
   350  		exp: `
   351  	n2[label="if 1",shape="hexagon"];
   352  	n3[label="foo(2)",shape="box"];
   353  	n4[label="return 4",shape="box"];
   354  	n5[label="return 3",shape="box"];
   355  	n1->n2;
   356  	n2->n3[color="#00aa00"];
   357  	n2->n5[color="#aa0000"];
   358  	n3->n2[color="#99999955"];
   359  	n3->n4;
   360  	n4->n3[color="#99999955"];
   361  	n5->n2[color="#99999955"];
   362  `,
   363  		dom: `
   364  	n2[label="if 1",shape="hexagon"];
   365  	n3[label="foo(2)",shape="box"];
   366  	n4[label="return 4",shape="box"];
   367  	n5[label="return 3",shape="box"];
   368  	n1->n2;
   369  	n2->n3;
   370  	n2->n5;
   371  	n3->n4;
   372  `,
   373  		flat: `
   374  if 1 {
   375  goto L_1
   376  } else {
   377  goto L_3
   378  }
   379  L_1:
   380  foo(2)
   381  goto L_2
   382  L_2:
   383  return 4
   384  L_3:
   385  return 3
   386  `,
   387  	},
   388  	{
   389  		name: "if all return",
   390  		tree: []CStmt{
   391  			&CIfStmt{Cond: numCond(1), Then: newBlock(ret(2)), Else: newBlock(ret(3))},
   392  		},
   393  		exp: `
   394  	n2[label="if 1",shape="hexagon"];
   395  	n3[label="return 2",shape="box"];
   396  	n4[label="return 3",shape="box"];
   397  	n1->n2;
   398  	n2->n3[color="#00aa00"];
   399  	n2->n4[color="#aa0000"];
   400  	n3->n2[color="#99999955"];
   401  	n4->n2[color="#99999955"];
   402  `,
   403  		dom: `
   404  	n2[label="if 1",shape="hexagon"];
   405  	n3[label="return 2",shape="box"];
   406  	n4[label="return 3",shape="box"];
   407  	n1->n2;
   408  	n2->n3;
   409  	n2->n4;
   410  `,
   411  		flat: `
   412  if 1 {
   413  goto L_1
   414  } else {
   415  goto L_2
   416  }
   417  L_1:
   418  return 2
   419  L_2:
   420  return 3
   421  `,
   422  	},
   423  	{
   424  		name: "if all return unreachable",
   425  		tree: []CStmt{
   426  			&CIfStmt{Cond: numCond(1), Then: newBlock(ret(2)), Else: newBlock(ret(3))},
   427  			ret(4),
   428  		},
   429  		exp: `
   430  	n2[label="if 1",shape="hexagon"];
   431  	n3[label="return 2",shape="box"];
   432  	n4[label="return 3",shape="box"];
   433  	n1->n2;
   434  	n2->n3[color="#00aa00"];
   435  	n2->n4[color="#aa0000"];
   436  	n3->n2[color="#99999955"];
   437  	n4->n2[color="#99999955"];
   438  `,
   439  		dom: `
   440  	n2[label="if 1",shape="hexagon"];
   441  	n3[label="return 2",shape="box"];
   442  	n4[label="return 3",shape="box"];
   443  	n1->n2;
   444  	n2->n3;
   445  	n2->n4;
   446  `,
   447  		flat: `
   448  if 1 {
   449  goto L_1
   450  } else {
   451  goto L_2
   452  }
   453  L_1:
   454  return 2
   455  L_2:
   456  return 3
   457  `,
   458  	},
   459  	{
   460  		name: "switch",
   461  		tree: []CStmt{
   462  			&CSwitchStmt{
   463  				Cond: cIntLit(1),
   464  				Cases: []*CCaseStmt{
   465  					{Expr: cIntLit(1), Stmts: []CStmt{numStmt(1)}},
   466  					{Expr: cIntLit(2), Stmts: []CStmt{numStmt(2)}},
   467  					{Expr: cIntLit(3), Stmts: []CStmt{numStmt(3), &CBreakStmt{}}},
   468  					{Stmts: []CStmt{numStmt(4)}},
   469  				},
   470  			},
   471  			ret(1),
   472  		},
   473  		exp: `
   474  	n2[label="switch 1",shape="trapezium"];
   475  	n3[label="foo(1)",shape="box"];
   476  	n4[label="foo(2)",shape="box"];
   477  	n5[label="foo(3)",shape="box"];
   478  	n6[label="return 1",shape="box"];
   479  	n7[label="foo(4)",shape="box"];
   480  	n1->n2;
   481  	n2->n3[label="1"];
   482  	n2->n4[label="2"];
   483  	n2->n5[label="3"];
   484  	n2->n7[color="#aa0000"];
   485  	n3->n2[color="#99999955"];
   486  	n3->n4;
   487  	n4->n2[color="#99999955"];
   488  	n4->n3[color="#99999955"];
   489  	n4->n5;
   490  	n5->n2[color="#99999955"];
   491  	n5->n4[color="#99999955"];
   492  	n5->n6;
   493  	n6->n7[color="#99999955"];
   494  	n6->n5[color="#99999955"];
   495  	n7->n2[color="#99999955"];
   496  	n7->n6;
   497  `,
   498  		dom: `
   499  	n2[label="switch 1",shape="trapezium"];
   500  	n3[label="foo(1)",shape="box"];
   501  	n4[label="foo(2)",shape="box"];
   502  	n5[label="foo(3)",shape="box"];
   503  	n6[label="return 1",shape="box"];
   504  	n7[label="foo(4)",shape="box"];
   505  	n1->n2;
   506  	n2->n3;
   507  	n2->n4;
   508  	n2->n5;
   509  	n2->n6;
   510  	n2->n7;
   511  `,
   512  		flat: `
   513  switch 1 {
   514  case 1:
   515  goto L_1
   516  case 2:
   517  goto L_2
   518  case 3:
   519  goto L_3
   520  default:
   521  goto L_5
   522  }
   523  L_1:
   524  foo(1)
   525  goto L_2
   526  L_2:
   527  foo(2)
   528  goto L_3
   529  L_3:
   530  foo(3)
   531  goto L_4
   532  L_4:
   533  return 1
   534  L_5:
   535  foo(4)
   536  goto L_4
   537  `,
   538  	},
   539  	{
   540  		name: "switch no default",
   541  		tree: []CStmt{
   542  			&CSwitchStmt{
   543  				Cond: cIntLit(1),
   544  				Cases: []*CCaseStmt{
   545  					{Expr: cIntLit(1), Stmts: []CStmt{numStmt(1)}},
   546  					{Expr: cIntLit(2), Stmts: []CStmt{&CBreakStmt{}}},
   547  					{Expr: cIntLit(3), Stmts: []CStmt{numStmt(3), &CBreakStmt{}}},
   548  				},
   549  			},
   550  			ret(1),
   551  		},
   552  		exp: `
   553  	n2[label="switch 1",shape="trapezium"];
   554  	n3[label="foo(1)",shape="box"];
   555  	n4[label="return 1",shape="box"];
   556  	n5[label="foo(3)",shape="box"];
   557  	n1->n2;
   558  	n2->n3[label="1"];
   559  	n2->n4[label="2"];
   560  	n2->n5[label="3"];
   561  	n2->n4[color="#aa0000"];
   562  	n3->n2[color="#99999955"];
   563  	n3->n4;
   564  	n4->n5[color="#99999955"];
   565  	n4->n2[color="#99999955"];
   566  	n4->n3[color="#99999955"];
   567  	n5->n2[color="#99999955"];
   568  	n5->n4;
   569  `,
   570  		dom: `
   571  	n2[label="switch 1",shape="trapezium"];
   572  	n3[label="foo(1)",shape="box"];
   573  	n4[label="return 1",shape="box"];
   574  	n5[label="foo(3)",shape="box"];
   575  	n1->n2;
   576  	n2->n3;
   577  	n2->n4;
   578  	n2->n5;
   579  `,
   580  		flat: `
   581  switch 1 {
   582  case 1:
   583  goto L_1
   584  case 2:
   585  goto L_2
   586  case 3:
   587  goto L_3
   588  default:
   589  goto L_2
   590  }
   591  L_1:
   592  foo(1)
   593  goto L_2
   594  L_2:
   595  return 1
   596  L_3:
   597  foo(3)
   598  goto L_2
   599  `,
   600  	},
   601  	{
   602  		name: "goto",
   603  		tree: []CStmt{
   604  			numStmt(1),
   605  			&CIfStmt{
   606  				Cond: numCond(1),
   607  				Then: newBlock(
   608  					numStmt(2),
   609  					&CGotoStmt{Label: "L1"},
   610  				),
   611  			},
   612  			&CLabelStmt{Label: "L1"},
   613  			ret(3),
   614  		},
   615  		exp: `
   616  	n2[label="foo(1)",shape="box"];
   617  	n3[label="if 1",shape="hexagon"];
   618  	n4[label="foo(2)",shape="box"];
   619  	n5[label="return 3",shape="box"];
   620  	n1->n2;
   621  	n2->n3;
   622  	n3->n2[color="#99999955"];
   623  	n3->n4[color="#00aa00"];
   624  	n3->n5[color="#aa0000"];
   625  	n4->n3[color="#99999955"];
   626  	n4->n5;
   627  	n5->n4[color="#99999955"];
   628  	n5->n3[color="#99999955"];
   629  `,
   630  		dom: `
   631  	n2[label="foo(1)",shape="box"];
   632  	n3[label="if 1",shape="hexagon"];
   633  	n4[label="foo(2)",shape="box"];
   634  	n5[label="return 3",shape="box"];
   635  	n1->n2;
   636  	n2->n3;
   637  	n3->n4;
   638  	n3->n5;
   639  `,
   640  		flat: `
   641  foo(1)
   642  goto L_1
   643  L_1:
   644  if 1 {
   645  goto L_2
   646  } else {
   647  goto L_3
   648  }
   649  L_2:
   650  foo(2)
   651  goto L_3
   652  L_3:
   653  return 3
   654  `,
   655  	},
   656  	{
   657  		name: "goto loop",
   658  		tree: []CStmt{
   659  			numStmt(0),
   660  			&CLabelStmt{Label: "L1"},
   661  			&CIfStmt{
   662  				Cond: numCond(1),
   663  				Then: newBlock(
   664  					numStmt(1),
   665  					&CGotoStmt{Label: "L1"},
   666  				),
   667  			},
   668  			ret(3),
   669  		},
   670  		exp: `
   671  	n2[label="foo(0)",shape="box"];
   672  	n3[label="if 1",shape="hexagon"];
   673  	n4[label="foo(1)",shape="box"];
   674  	n5[label="return 3",shape="box"];
   675  	n1->n2;
   676  	n2->n3;
   677  	n3->n4[color="#99999955"];
   678  	n3->n2[color="#99999955"];
   679  	n3->n4[color="#00aa00"];
   680  	n3->n5[color="#aa0000"];
   681  	n4->n3[color="#99999955"];
   682  	n4->n3;
   683  	n5->n3[color="#99999955"];
   684  `,
   685  		dom: `
   686  	n2[label="foo(0)",shape="box"];
   687  	n3[label="if 1",shape="hexagon"];
   688  	n4[label="foo(1)",shape="box"];
   689  	n5[label="return 3",shape="box"];
   690  	n1->n2;
   691  	n2->n3;
   692  	n3->n4;
   693  	n3->n5;
   694  `,
   695  		flat: `
   696  foo(0)
   697  goto L_1
   698  L_1:
   699  if 1 {
   700  goto L_2
   701  } else {
   702  goto L_3
   703  }
   704  L_2:
   705  foo(1)
   706  goto L_1
   707  L_3:
   708  return 3
   709  `,
   710  	},
   711  	{
   712  		name: "for infinite empty",
   713  		tree: []CStmt{
   714  			&CForStmt{},
   715  			ret(2),
   716  		},
   717  		exp: `
   718  	n2[label="",shape="box"];
   719  	n1->n2;
   720  	n2->n2[color="#99999955"];
   721  	n2->n2;
   722  `,
   723  		dom: `
   724  	n2[label="",shape="box"];
   725  	n1->n2;
   726  `,
   727  		flat: `
   728  L_1:
   729  goto L_1
   730  `,
   731  	},
   732  	{
   733  		name: "for infinite",
   734  		tree: []CStmt{
   735  			&CForStmt{Body: *newBlock(numStmt(1))},
   736  			ret(2),
   737  		},
   738  		exp: `
   739  	n2[label="foo(1)",shape="box"];
   740  	n1->n2;
   741  	n2->n2[color="#99999955"];
   742  	n2->n2;
   743  `,
   744  		dom: `
   745  	n2[label="foo(1)",shape="box"];
   746  	n1->n2;
   747  `,
   748  		flat: `
   749  L_1:
   750  foo(1)
   751  goto L_1
   752  `,
   753  	},
   754  	{
   755  		name: "for infinite break",
   756  		tree: []CStmt{
   757  			&CForStmt{Body: *newBlock(
   758  				numStmt(1),
   759  				&CBreakStmt{},
   760  			)},
   761  			ret(2),
   762  		},
   763  		exp: `
   764  	n2[label="foo(1)",shape="box"];
   765  	n3[label="return 2",shape="box"];
   766  	n1->n2;
   767  	n2->n3;
   768  	n3->n2[color="#99999955"];
   769  `,
   770  		dom: `
   771  	n2[label="foo(1)",shape="box"];
   772  	n3[label="return 2",shape="box"];
   773  	n1->n2;
   774  	n2->n3;
   775  `,
   776  		flat: `
   777  foo(1)
   778  goto L_1
   779  L_1:
   780  return 2
   781  `,
   782  	},
   783  	{
   784  		name: "for infinite continue",
   785  		tree: []CStmt{
   786  			&CForStmt{Body: *newBlock(
   787  				numStmt(1),
   788  				&CContinueStmt{},
   789  			)},
   790  			ret(2),
   791  		},
   792  		exp: `
   793  	n2[label="foo(1)",shape="box"];
   794  	n1->n2;
   795  	n2->n2[color="#99999955"];
   796  	n2->n2;
   797  `,
   798  		dom: `
   799  	n2[label="foo(1)",shape="box"];
   800  	n1->n2;
   801  `,
   802  		flat: `
   803  L_1:
   804  foo(1)
   805  goto L_1
   806  `,
   807  	},
   808  	{
   809  		name: "for cond",
   810  		tree: []CStmt{
   811  			&CForStmt{Cond: cIntLit(1), Body: *newBlock(
   812  				numStmt(1),
   813  			)},
   814  			ret(2),
   815  		},
   816  		exp: `
   817  	n2[label="if false",shape="hexagon"];
   818  	n3[label="foo(1)",shape="box"];
   819  	n4[label="return 2",shape="box"];
   820  	n1->n2;
   821  	n2->n3[color="#99999955"];
   822  	n2->n4[color="#00aa00"];
   823  	n2->n3[color="#aa0000"];
   824  	n3->n2[color="#99999955"];
   825  	n3->n2;
   826  	n4->n2[color="#99999955"];
   827  `,
   828  		dom: `
   829  	n2[label="if false",shape="hexagon"];
   830  	n3[label="return 2",shape="box"];
   831  	n4[label="foo(1)",shape="box"];
   832  	n1->n2;
   833  	n2->n3;
   834  	n2->n4;
   835  `,
   836  		flat: `
   837  L_1:
   838  if false {
   839  goto L_2
   840  } else {
   841  goto L_3
   842  }
   843  L_2:
   844  return 2
   845  L_3:
   846  foo(1)
   847  goto L_1
   848  `,
   849  	},
   850  	{
   851  		name: "for cond break",
   852  		tree: []CStmt{
   853  			&CForStmt{
   854  				Cond: cIntLit(1),
   855  				Body: *newBlock(
   856  					&CIfStmt{
   857  						Cond: numCond(2),
   858  						Then: newBlock(
   859  							numStmt(3),
   860  							&CBreakStmt{},
   861  						),
   862  					},
   863  					numStmt(4),
   864  				),
   865  			},
   866  			ret(5),
   867  		},
   868  		exp: `
   869  	n2[label="if false",shape="hexagon"];
   870  	n3[label="foo(4)",shape="box"];
   871  	n4[label="if 2",shape="hexagon"];
   872  	n5[label="foo(3)",shape="box"];
   873  	n6[label="return 5",shape="box"];
   874  	n1->n2;
   875  	n2->n3[color="#99999955"];
   876  	n2->n6[color="#00aa00"];
   877  	n2->n4[color="#aa0000"];
   878  	n3->n4[color="#99999955"];
   879  	n3->n2;
   880  	n4->n2[color="#99999955"];
   881  	n4->n5[color="#00aa00"];
   882  	n4->n3[color="#aa0000"];
   883  	n5->n4[color="#99999955"];
   884  	n5->n6;
   885  	n6->n5[color="#99999955"];
   886  	n6->n2[color="#99999955"];
   887  `,
   888  		dom: `
   889  	n2[label="if false",shape="hexagon"];
   890  	n3[label="return 5",shape="box"];
   891  	n4[label="if 2",shape="hexagon"];
   892  	n5[label="foo(3)",shape="box"];
   893  	n6[label="foo(4)",shape="box"];
   894  	n1->n2;
   895  	n2->n3;
   896  	n2->n4;
   897  	n4->n5;
   898  	n4->n6;
   899  `,
   900  		flat: `
   901  L_1:
   902  if false {
   903  goto L_2
   904  } else {
   905  goto L_3
   906  }
   907  L_2:
   908  return 5
   909  L_3:
   910  if 2 {
   911  goto L_4
   912  } else {
   913  goto L_5
   914  }
   915  L_4:
   916  foo(3)
   917  goto L_2
   918  L_5:
   919  foo(4)
   920  goto L_1
   921  `,
   922  	},
   923  	{
   924  		name: "for cond nested",
   925  		tree: []CStmt{
   926  			&CForStmt{Cond: cIntLit(2), Body: *newBlock(
   927  				numStmt(2),
   928  				&CForStmt{Cond: cIntLit(3), Body: *newBlock(
   929  					numStmt(3),
   930  				)},
   931  			)},
   932  			ret(1),
   933  		},
   934  		exp: `
   935  	n2[label="if 2 == 0",shape="hexagon"];
   936  	n3[label="if 3 == 0",shape="hexagon"];
   937  	n4[label="foo(3)",shape="box"];
   938  	n5[label="foo(2)",shape="box"];
   939  	n6[label="return 1",shape="box"];
   940  	n1->n2;
   941  	n2->n3[color="#99999955"];
   942  	n2->n6[color="#00aa00"];
   943  	n2->n5[color="#aa0000"];
   944  	n3->n4[color="#99999955"];
   945  	n3->n5[color="#99999955"];
   946  	n3->n2[color="#00aa00"];
   947  	n3->n4[color="#aa0000"];
   948  	n4->n3[color="#99999955"];
   949  	n4->n3;
   950  	n5->n2[color="#99999955"];
   951  	n5->n3;
   952  	n6->n2[color="#99999955"];
   953  `,
   954  		dom: `
   955  	n2[label="if 2 == 0",shape="hexagon"];
   956  	n3[label="return 1",shape="box"];
   957  	n4[label="foo(2)",shape="box"];
   958  	n5[label="if 3 == 0",shape="hexagon"];
   959  	n6[label="foo(3)",shape="box"];
   960  	n1->n2;
   961  	n2->n3;
   962  	n2->n4;
   963  	n4->n5;
   964  	n5->n6;
   965  `,
   966  		flat: `
   967  L_1:
   968  if 2 == 0 {
   969  goto L_2
   970  } else {
   971  goto L_3
   972  }
   973  L_2:
   974  return 1
   975  L_3:
   976  foo(2)
   977  goto L_4
   978  L_4:
   979  if 3 == 0 {
   980  goto L_1
   981  } else {
   982  goto L_5
   983  }
   984  L_5:
   985  foo(3)
   986  goto L_4
   987  `,
   988  	},
   989  	{
   990  		name: "for cond nested 2",
   991  		tree: []CStmt{
   992  			&CForStmt{Cond: cIntLit(2), Body: *newBlock(
   993  				numStmt(2),
   994  				&CForStmt{Cond: cIntLit(3), Body: *newBlock(
   995  					numStmt(3),
   996  				)},
   997  				numStmt(4),
   998  			)},
   999  			ret(1),
  1000  		},
  1001  		exp: `
  1002  	n2[label="if 2 == 0",shape="hexagon"];
  1003  	n3[label="foo(4)",shape="box"];
  1004  	n4[label="if 3 == 0",shape="hexagon"];
  1005  	n5[label="foo(3)",shape="box"];
  1006  	n6[label="foo(2)",shape="box"];
  1007  	n7[label="return 1",shape="box"];
  1008  	n1->n2;
  1009  	n2->n3[color="#99999955"];
  1010  	n2->n7[color="#00aa00"];
  1011  	n2->n6[color="#aa0000"];
  1012  	n3->n4[color="#99999955"];
  1013  	n3->n2;
  1014  	n4->n5[color="#99999955"];
  1015  	n4->n6[color="#99999955"];
  1016  	n4->n3[color="#00aa00"];
  1017  	n4->n5[color="#aa0000"];
  1018  	n5->n4[color="#99999955"];
  1019  	n5->n4;
  1020  	n6->n2[color="#99999955"];
  1021  	n6->n4;
  1022  	n7->n2[color="#99999955"];
  1023  `,
  1024  		dom: `
  1025  	n2[label="if 2 == 0",shape="hexagon"];
  1026  	n3[label="return 1",shape="box"];
  1027  	n4[label="foo(2)",shape="box"];
  1028  	n5[label="if 3 == 0",shape="hexagon"];
  1029  	n6[label="foo(4)",shape="box"];
  1030  	n7[label="foo(3)",shape="box"];
  1031  	n1->n2;
  1032  	n2->n3;
  1033  	n2->n4;
  1034  	n4->n5;
  1035  	n5->n6;
  1036  	n5->n7;
  1037  `,
  1038  		flat: `
  1039  L_1:
  1040  if 2 == 0 {
  1041  goto L_2
  1042  } else {
  1043  goto L_3
  1044  }
  1045  L_2:
  1046  return 1
  1047  L_3:
  1048  foo(2)
  1049  goto L_4
  1050  L_4:
  1051  if 3 == 0 {
  1052  goto L_5
  1053  } else {
  1054  goto L_6
  1055  }
  1056  L_5:
  1057  foo(4)
  1058  goto L_1
  1059  L_6:
  1060  foo(3)
  1061  goto L_4
  1062  `,
  1063  	},
  1064  	{
  1065  		name: "for full",
  1066  		tree: []CStmt{
  1067  			&CForStmt{
  1068  				Init: numStmt(1), Cond: cIntLit(1), Iter: numStmt(2),
  1069  				Body: *newBlock(
  1070  					numStmt(3),
  1071  				),
  1072  			},
  1073  			ret(4),
  1074  		},
  1075  		exp: `
  1076  	n2[label="foo(1)",shape="box"];
  1077  	n3[label="if false",shape="hexagon"];
  1078  	n4[label="foo(2)",shape="box"];
  1079  	n5[label="foo(3)",shape="box"];
  1080  	n6[label="return 4",shape="box"];
  1081  	n1->n2;
  1082  	n2->n3;
  1083  	n3->n4[color="#99999955"];
  1084  	n3->n2[color="#99999955"];
  1085  	n3->n6[color="#00aa00"];
  1086  	n3->n5[color="#aa0000"];
  1087  	n4->n5[color="#99999955"];
  1088  	n4->n3;
  1089  	n5->n3[color="#99999955"];
  1090  	n5->n4;
  1091  	n6->n3[color="#99999955"];
  1092  `,
  1093  		dom: `
  1094  	n2[label="foo(1)",shape="box"];
  1095  	n3[label="if false",shape="hexagon"];
  1096  	n4[label="return 4",shape="box"];
  1097  	n5[label="foo(3)",shape="box"];
  1098  	n6[label="foo(2)",shape="box"];
  1099  	n1->n2;
  1100  	n2->n3;
  1101  	n3->n4;
  1102  	n3->n5;
  1103  	n5->n6;
  1104  `,
  1105  		flat: `
  1106  foo(1)
  1107  goto L_1
  1108  L_1:
  1109  if false {
  1110  goto L_2
  1111  } else {
  1112  goto L_3
  1113  }
  1114  L_2:
  1115  return 4
  1116  L_3:
  1117  foo(3)
  1118  goto L_4
  1119  L_4:
  1120  foo(2)
  1121  goto L_1
  1122  `,
  1123  	},
  1124  	{
  1125  		name: "for full break",
  1126  		tree: []CStmt{
  1127  			&CForStmt{
  1128  				Init: numStmt(1), Cond: cIntLit(1), Iter: numStmt(2),
  1129  				Body: *newBlock(
  1130  					&CIfStmt{
  1131  						Cond: numCond(2),
  1132  						Then: newBlock(
  1133  							numStmt(3),
  1134  							&CBreakStmt{},
  1135  						),
  1136  					},
  1137  					numStmt(4),
  1138  				),
  1139  			},
  1140  			ret(5),
  1141  		},
  1142  		exp: `
  1143  	n2[label="foo(1)",shape="box"];
  1144  	n3[label="if false",shape="hexagon"];
  1145  	n4[label="foo(2)",shape="box"];
  1146  	n5[label="foo(4)",shape="box"];
  1147  	n6[label="if 2",shape="hexagon"];
  1148  	n7[label="foo(3)",shape="box"];
  1149  	n8[label="return 5",shape="box"];
  1150  	n1->n2;
  1151  	n2->n3;
  1152  	n3->n4[color="#99999955"];
  1153  	n3->n2[color="#99999955"];
  1154  	n3->n8[color="#00aa00"];
  1155  	n3->n6[color="#aa0000"];
  1156  	n4->n5[color="#99999955"];
  1157  	n4->n3;
  1158  	n5->n6[color="#99999955"];
  1159  	n5->n4;
  1160  	n6->n3[color="#99999955"];
  1161  	n6->n7[color="#00aa00"];
  1162  	n6->n5[color="#aa0000"];
  1163  	n7->n6[color="#99999955"];
  1164  	n7->n8;
  1165  	n8->n7[color="#99999955"];
  1166  	n8->n3[color="#99999955"];
  1167  `,
  1168  		dom: `
  1169  	n2[label="foo(1)",shape="box"];
  1170  	n3[label="if false",shape="hexagon"];
  1171  	n4[label="return 5",shape="box"];
  1172  	n5[label="if 2",shape="hexagon"];
  1173  	n6[label="foo(3)",shape="box"];
  1174  	n7[label="foo(4)",shape="box"];
  1175  	n8[label="foo(2)",shape="box"];
  1176  	n1->n2;
  1177  	n2->n3;
  1178  	n3->n4;
  1179  	n3->n5;
  1180  	n5->n6;
  1181  	n5->n7;
  1182  	n7->n8;
  1183  `,
  1184  		flat: `
  1185  foo(1)
  1186  goto L_1
  1187  L_1:
  1188  if false {
  1189  goto L_2
  1190  } else {
  1191  goto L_3
  1192  }
  1193  L_2:
  1194  return 5
  1195  L_3:
  1196  if 2 {
  1197  goto L_4
  1198  } else {
  1199  goto L_5
  1200  }
  1201  L_4:
  1202  foo(3)
  1203  goto L_2
  1204  L_5:
  1205  foo(4)
  1206  goto L_6
  1207  L_6:
  1208  foo(2)
  1209  goto L_1
  1210  `,
  1211  	},
  1212  }
  1213  
  1214  func writeDotFile(name string, data []byte) {
  1215  	name = strings.ReplaceAll(name, " ", "_")
  1216  	fname := name + ".dot"
  1217  	_ = ioutil.WriteFile(fname, data, 0644)
  1218  	sdata, _ := exec.Command("dot", "-Tsvg", fname).Output()
  1219  	_ = ioutil.WriteFile(name+".svg", sdata, 0644)
  1220  	_ = os.Remove(fname)
  1221  }
  1222  
  1223  func cleanDot(s string) string {
  1224  	s = strings.TrimPrefix(s, `digraph  {`)
  1225  	s = strings.TrimSpace(s)
  1226  	s = strings.TrimPrefix(s, `n1[label="begin"];`)
  1227  	s = strings.TrimSuffix(s, `}`)
  1228  	s = strings.TrimSpace(s)
  1229  	return s
  1230  }
  1231  
  1232  func TestControlFlow(t *testing.T) {
  1233  	const dir = "testout"
  1234  	_ = os.MkdirAll(dir, 0755)
  1235  	for _, c := range casesControlFlow {
  1236  		t.Run(c.name, func(t *testing.T) {
  1237  			tr := newTranslator(libs.NewEnv(types.Config32()), Config{})
  1238  			var fixer Visitor
  1239  			fixer = func(n Node) {
  1240  				switch n := n.(type) {
  1241  				case nil:
  1242  					return
  1243  				case *CVarDecl:
  1244  					n.g = tr
  1245  				case *CVarSpec:
  1246  					n.g = tr
  1247  				}
  1248  				n.Visit(fixer)
  1249  			}
  1250  
  1251  			for _, st := range c.tree {
  1252  				st.Visit(fixer)
  1253  			}
  1254  
  1255  			cf := tr.NewControlFlow(c.tree)
  1256  
  1257  			got := cf.dumpDot()
  1258  			writeDotFile(filepath.Join(dir, c.name), []byte(got))
  1259  			got = cleanDot(got)
  1260  			require.Equal(t, strings.TrimSpace(c.exp), got)
  1261  
  1262  			got = cf.dumpDomDot()
  1263  			writeDotFile(filepath.Join(dir, c.name+"_dom"), []byte(got))
  1264  			got = cleanDot(got)
  1265  			require.Equal(t, strings.TrimSpace(c.dom), got)
  1266  
  1267  			stmts := cf.Flatten()
  1268  			got = printStmts(stmts)
  1269  			got = strings.ReplaceAll(got, "\t", "")
  1270  			require.Equal(t, strings.TrimSpace(c.flat), got)
  1271  		})
  1272  	}
  1273  }