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