github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/internal/gosmith/stmt.go (about)

     1  package main
     2  
     3  import (
     4  	_ "fmt"
     5  )
     6  
     7  func (smith *Smith) initStatements() {
     8  	smith.statements = []func(){
     9  		smith.stmtOas,
    10  		smith.stmtAs,
    11  		smith.stmtInc,
    12  		smith.stmtIf,
    13  		smith.stmtFor,
    14  		smith.stmtSend,
    15  		smith.stmtRecv,
    16  		smith.stmtSelect,
    17  		smith.stmtSwitchExpr,
    18  		smith.stmtSwitchType,
    19  		smith.stmtTypeDecl,
    20  		smith.stmtVarDecl,
    21  		smith.stmtCall,
    22  		smith.stmtReturn,
    23  		smith.stmtBreak,
    24  		smith.stmtContinue,
    25  		smith.stmtGoto,
    26  		smith.stmtSink,
    27  	}
    28  }
    29  
    30  func (smith *Smith) genStatement() {
    31  	if smith.stmtCount >= NStatements {
    32  		return
    33  	}
    34  	smith.exprCount = 0
    35  	smith.stmtCount++
    36  	smith.statements[smith.rnd(len(smith.statements))]()
    37  }
    38  
    39  func (smith *Smith) stmtOas() {
    40  	list := smith.atypeList(TraitAny)
    41  	str, vars := smith.fmtOasVarList(list)
    42  	smith.line("%v := %v", str, smith.fmtRvalueList(list))
    43  	for _, v := range vars {
    44  		smith.defineVar(v.id, v.typ)
    45  	}
    46  }
    47  
    48  func (smith *Smith) stmtReturn() {
    49  	smith.line("return %v", smith.fmtRvalueList(smith.curFunc.rets))
    50  }
    51  
    52  func (smith *Smith) stmtAs() {
    53  	types := smith.atypeList(TraitAny)
    54  	smith.line("%v = %v", smith.fmtLvalueList(types), smith.fmtRvalueList(types))
    55  }
    56  
    57  func (smith *Smith) stmtInc() {
    58  	smith.line("%v %v", smith.lvalueOrMapIndex(smith.atype(ClassNumeric)), smith.choice("--", "++"))
    59  }
    60  
    61  func (smith *Smith) stmtIf() {
    62  	smith.enterBlock(true)
    63  	smith.enterBlock(true)
    64  	if smith.rndBool() {
    65  		smith.line("if %v {", smith.rvalue(smith.atype(ClassBoolean)))
    66  	} else {
    67  		smith.line("if %v; %v {", smith.stmtSimple(true, nil), smith.rvalue(smith.atype(ClassBoolean)))
    68  	}
    69  	smith.genBlock()
    70  	if smith.rndBool() {
    71  		smith.line("} else {")
    72  		smith.genBlock()
    73  	}
    74  	smith.leaveBlock()
    75  	smith.line("}")
    76  	smith.leaveBlock()
    77  }
    78  
    79  func (smith *Smith) stmtFor() {
    80  	smith.enterBlock(true)
    81  	smith.enterBlock(true)
    82  	smith.curBlock.isBreakable = true
    83  	smith.curBlock.isContinuable = true
    84  	var vars []*Var
    85  	switch smith.choice("simple", "complex", "range") {
    86  	case "simple":
    87  		smith.line("for %v {", smith.rvalue(smith.atype(ClassBoolean)))
    88  	case "complex":
    89  		smith.line("for %v; %v; %v {", smith.stmtSimple(true, nil), smith.rvalue(smith.atype(ClassBoolean)), smith.stmtSimple(false, nil))
    90  	case "range":
    91  		switch smith.choice("slice", "string", "channel", "map") {
    92  		case "slice":
    93  			t := smith.atype(TraitAny)
    94  			s := smith.rvalue(smith.sliceOf(t))
    95  			switch smith.choice("one", "two", "oneDecl", "twoDecl") {
    96  			case "one":
    97  				smith.line("for %v = range %v {", smith.lvalueOrBlank(smith.intType), s)
    98  			case "two":
    99  				smith.line("for %v, %v = range %v {", smith.lvalueOrBlank(smith.intType), smith.lvalueOrBlank(t), s)
   100  			case "oneDecl":
   101  				id := smith.newId("Var")
   102  				smith.line("for %v := range %v {", id, s)
   103  				vars = append(vars, &Var{id: id, typ: smith.intType})
   104  			case "twoDecl":
   105  				types := []*Type{smith.intType, t}
   106  				str := ""
   107  				str, vars = smith.fmtOasVarList(types)
   108  				smith.line("for %v := range %v {", str, s)
   109  			default:
   110  				panic("bad")
   111  			}
   112  		case "string":
   113  			s := smith.rvalue(smith.stringType)
   114  			switch smith.choice("one", "two", "oneDecl", "twoDecl") {
   115  			case "one":
   116  				smith.line("for %v = range %v {", smith.lvalueOrBlank(smith.intType), s)
   117  			case "two":
   118  				smith.line("for %v, %v = range %v {", smith.lvalueOrBlank(smith.intType), smith.lvalueOrBlank(smith.runeType), s)
   119  			case "oneDecl":
   120  				id := smith.newId("Var")
   121  				smith.line("for %v := range %v {", id, s)
   122  				vars = append(vars, &Var{id: id, typ: smith.intType})
   123  			case "twoDecl":
   124  				types := []*Type{smith.intType, smith.runeType}
   125  				str := ""
   126  				str, vars = smith.fmtOasVarList(types)
   127  				smith.line("for %v := range %v {", str, s)
   128  			default:
   129  				panic("bad")
   130  			}
   131  		case "channel":
   132  			cht := smith.atype(ClassChan)
   133  			ch := smith.rvalue(cht)
   134  			switch smith.choice("one", "oneDecl") {
   135  			case "one":
   136  				smith.line("for %v = range %v {", smith.lvalueOrBlank(cht.ktyp), ch)
   137  			case "oneDecl":
   138  				id := smith.newId("Var")
   139  				smith.line("for %v := range %v {", id, ch)
   140  				vars = append(vars, &Var{id: id, typ: cht.ktyp})
   141  			default:
   142  				panic("bad")
   143  			}
   144  		case "map":
   145  			t := smith.atype(ClassMap)
   146  			m := smith.rvalue(t)
   147  			switch smith.choice("one", "two", "oneDecl", "twoDecl") {
   148  			case "one":
   149  				smith.line("for %v = range %v {", smith.lvalueOrBlank(t.ktyp), m)
   150  			case "two":
   151  				smith.line("for %v, %v = range %v {", smith.lvalueOrBlank(t.ktyp), smith.lvalueOrBlank(t.vtyp), m)
   152  			case "oneDecl":
   153  				id := smith.newId("Var")
   154  				smith.line("for %v := range %v {", id, m)
   155  				vars = append(vars, &Var{id: id, typ: t.ktyp})
   156  			case "twoDecl":
   157  				types := []*Type{t.ktyp, t.vtyp}
   158  				str := ""
   159  				str, vars = smith.fmtOasVarList(types)
   160  				smith.line("for %v := range %v {", str, m)
   161  			default:
   162  				panic("bad")
   163  			}
   164  		default:
   165  			panic("bad")
   166  		}
   167  	default:
   168  		panic("bad")
   169  	}
   170  	smith.enterBlock(true)
   171  	if len(vars) > 0 {
   172  		smith.line("")
   173  		for _, v := range vars {
   174  			smith.defineVar(v.id, v.typ)
   175  		}
   176  	}
   177  	smith.genBlock()
   178  	smith.leaveBlock()
   179  	smith.leaveBlock()
   180  	smith.line("}")
   181  	smith.leaveBlock()
   182  }
   183  
   184  func (smith *Smith) stmtSimple(oas bool, newVars *[]*Var) string {
   185  	// We emit a fake statement in "oas", so make sure that nothing can be inserted in between.
   186  	if smith.curBlock.extendable {
   187  		panic("bad")
   188  	}
   189  	// "send" crashes gccgo with random errors too frequently.
   190  	// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61273
   191  	switch smith.choice("empty", "inc", "assign", "oas", "send", "expr") {
   192  	case "empty":
   193  		return ""
   194  	case "inc":
   195  		return F("%v %v", smith.lvalueOrMapIndex(smith.atype(ClassNumeric)), smith.choice("--", "++"))
   196  	case "assign":
   197  		list := smith.atypeList(TraitAny)
   198  		return F("%v = %v", smith.fmtLvalueList(list), smith.fmtRvalueList(list))
   199  	case "oas":
   200  		if !oas {
   201  			return ""
   202  		}
   203  		list := smith.atypeList(TraitAny)
   204  		str, vars := smith.fmtOasVarList(list)
   205  		if newVars != nil {
   206  			*newVars = vars
   207  		}
   208  		res := F("%v := %v", str, smith.fmtRvalueList(list))
   209  		smith.line("")
   210  		for _, v := range vars {
   211  			smith.defineVar(v.id, v.typ)
   212  		}
   213  		return res
   214  	case "send":
   215  		t := smith.atype(TraitSendable)
   216  		return F("%v <- %v", smith.rvalue(t), smith.rvalue(t.ktyp))
   217  	case "expr":
   218  		return ""
   219  	default:
   220  		panic("bad")
   221  	}
   222  }
   223  
   224  func (smith *Smith) stmtSend() {
   225  	t := smith.atype(TraitSendable)
   226  	smith.line("%v <- %v", smith.rvalue(t), smith.rvalue(t.ktyp))
   227  }
   228  
   229  func (smith *Smith) stmtRecv() {
   230  	t := smith.atype(TraitReceivable)
   231  	ch := smith.rvalue(t)
   232  	switch smith.choice("normal", "decl") {
   233  	case "normal":
   234  		smith.line("%v, %v = <-%v", smith.lvalueOrBlank(t.ktyp), smith.lvalueOrBlank(smith.boolType), ch)
   235  	case "decl":
   236  		vv := smith.newId("Var")
   237  		ok := smith.newId("Var")
   238  		smith.line("%v, %v := <-%v", vv, ok, ch)
   239  		smith.defineVar(vv, t.ktyp)
   240  		smith.defineVar(ok, smith.boolType)
   241  	default:
   242  		panic("bad")
   243  	}
   244  }
   245  
   246  func (smith *Smith) stmtTypeDecl() {
   247  	id := smith.newId("Type")
   248  	t := smith.atype(TraitAny)
   249  	smith.line("type %v %v", id, t.id)
   250  
   251  	newTyp := new(Type)
   252  	*newTyp = *t
   253  	newTyp.id = id
   254  	newTyp.namedUserType = true
   255  	if t.class == ClassStruct {
   256  		newTyp.literal = func() string {
   257  			// replace struct name with new type id
   258  			l := t.literal()
   259  			l = l[len(t.id)+1:]
   260  			return "(" + id + l
   261  		}
   262  		newTyp.complexLiteral = func() string {
   263  			// replace struct name with new type id
   264  			l := t.complexLiteral()
   265  			l = l[len(t.id)+1:]
   266  			return "(" + id + l
   267  		}
   268  	} else {
   269  		newTyp.literal = func() string {
   270  			return F("%v(%v)", id, t.literal())
   271  		}
   272  		if t.complexLiteral != nil {
   273  			newTyp.complexLiteral = func() string {
   274  				return F("%v(%v)", id, t.complexLiteral())
   275  			}
   276  		}
   277  	}
   278  	smith.defineType(newTyp)
   279  }
   280  
   281  func (smith *Smith) stmtVarDecl() {
   282  	id := smith.newId("Var")
   283  	t := smith.atype(TraitAny)
   284  	smith.line("var %v %v = %v", id, t.id, smith.rvalue(t))
   285  	smith.defineVar(id, t)
   286  }
   287  
   288  func (smith *Smith) stmtSelect() {
   289  	smith.enterBlock(true)
   290  	smith.line("select {")
   291  	for smith.rnd(5) != 0 {
   292  		smith.enterBlock(true)
   293  		elem := smith.atype(TraitAny)
   294  		cht := smith.chanOf(elem)
   295  		ch := smith.rvalue(cht)
   296  		if smith.rndBool() {
   297  			smith.line("case %v <- %v:", ch, smith.rvalue(elem))
   298  		} else {
   299  			switch smith.choice("one", "two", "oneDecl", "twoDecl") {
   300  			case "one":
   301  				smith.line("case %v = <-%v:", smith.lvalueOrBlank(elem), ch)
   302  			case "two":
   303  				smith.line("case %v, %v = <-%v:", smith.lvalueOrBlank(elem), smith.lvalueOrBlank(smith.boolType), ch)
   304  			case "oneDecl":
   305  				vv := smith.newId("Var")
   306  				smith.line("case %v := <-%v:", vv, ch)
   307  				smith.defineVar(vv, elem)
   308  			case "twoDecl":
   309  				vv := smith.newId("Var")
   310  				ok := smith.newId("Var")
   311  				smith.line("case %v, %v := <-%v:", vv, ok, ch)
   312  				smith.defineVar(vv, elem)
   313  				smith.defineVar(ok, smith.boolType)
   314  			default:
   315  				panic("bad")
   316  			}
   317  		}
   318  		smith.genBlock()
   319  		smith.leaveBlock()
   320  	}
   321  	if smith.rndBool() {
   322  		smith.enterBlock(true)
   323  		smith.line("default:")
   324  		smith.genBlock()
   325  		smith.leaveBlock()
   326  	}
   327  	smith.line("}")
   328  	smith.leaveBlock()
   329  }
   330  
   331  func (smith *Smith) stmtSwitchExpr() {
   332  	var t *Type
   333  	cond := ""
   334  	if smith.rndBool() {
   335  		t = smith.atype(TraitComparable)
   336  		cond = smith.rvalue(t)
   337  	} else {
   338  		t = smith.boolType
   339  	}
   340  	smith.enterBlock(true)
   341  	smith.enterBlock(true)
   342  	smith.curBlock.isBreakable = true
   343  	var vars []*Var
   344  	if smith.rndBool() {
   345  		smith.line("switch %v {", cond)
   346  	} else {
   347  		smith.line("switch %v; %v {", smith.stmtSimple(true, &vars), cond)
   348  	}
   349  	// TODO: we generate at most one case, because if we generate more,
   350  	// we can generate two cases with equal constants.
   351  	fallthru := false
   352  	if smith.rndBool() {
   353  		smith.enterBlock(true)
   354  		smith.line("case %v:", smith.rvalue(t))
   355  		smith.genBlock()
   356  		smith.leaveBlock()
   357  		if smith.rndBool() {
   358  			fallthru = true
   359  			smith.line("fallthrough")
   360  		}
   361  	}
   362  	if fallthru || len(vars) > 0 || smith.rndBool() {
   363  		smith.enterBlock(true)
   364  		smith.line("default:")
   365  		smith.genBlock()
   366  		for _, v := range vars {
   367  			smith.line("_ = %v", v.id)
   368  			v.used = true
   369  		}
   370  		smith.leaveBlock()
   371  	}
   372  	smith.leaveBlock()
   373  	smith.line("}")
   374  	smith.leaveBlock()
   375  }
   376  
   377  func (smith *Smith) stmtSwitchType() {
   378  	cond := smith.lvalue(smith.atype(TraitAny))
   379  	smith.enterBlock(true)
   380  	smith.curBlock.isBreakable = true
   381  	smith.line("switch COND := (interface{})(%v); COND.(type) {", cond)
   382  	if smith.rndBool() {
   383  		smith.enterBlock(true)
   384  		smith.line("case %v:", smith.atype(TraitAny).id)
   385  		smith.genBlock()
   386  		smith.leaveBlock()
   387  	}
   388  	if smith.rndBool() {
   389  		smith.enterBlock(true)
   390  		smith.line("default:")
   391  		smith.genBlock()
   392  		smith.leaveBlock()
   393  	}
   394  	smith.line("}")
   395  	smith.leaveBlock()
   396  }
   397  
   398  func (smith *Smith) stmtCall() {
   399  	if smith.rndBool() {
   400  		smith.stmtCallBuiltin()
   401  	}
   402  	t := smith.atype(ClassFunction)
   403  	prefix := smith.choice("", "go", "defer")
   404  	smith.line("%v %v(%v)", prefix, smith.rvalue(t), smith.fmtRvalueList(t.styp))
   405  }
   406  
   407  func (smith *Smith) stmtCallBuiltin() {
   408  	prefix := smith.choice("", "go", "defer")
   409  	switch fn := smith.choice("close", "copy", "delete", "panic", "print", "println", "recover"); fn {
   410  	case "close":
   411  		smith.line("%v %v(%v)", prefix, fn, smith.rvalue(smith.atype(ClassChan)))
   412  	case "copy":
   413  		smith.line("%v %v", prefix, smith.exprCopySlice())
   414  	case "delete":
   415  		t := smith.atype(ClassMap)
   416  		smith.line("%v %v(%v, %v)", prefix, fn, smith.rvalue(t), smith.rvalue(t.ktyp))
   417  	case "panic":
   418  		smith.line("%v %v(%v)", prefix, fn, smith.rvalue(smith.atype(TraitAny)))
   419  	case "print":
   420  		fallthrough
   421  	case "println":
   422  		list := smith.atypeList(TraitPrintable)
   423  		smith.line("%v %v(%v)", prefix, fn, smith.fmtRvalueList(list))
   424  	case "recover":
   425  		smith.line("%v %v()", prefix, fn)
   426  	default:
   427  		panic("bad")
   428  	}
   429  }
   430  
   431  func (smith *Smith) stmtBreak() {
   432  	if !smith.curBlock.isBreakable {
   433  		return
   434  	}
   435  	smith.line("break")
   436  }
   437  
   438  func (smith *Smith) stmtContinue() {
   439  	if !smith.curBlock.isContinuable {
   440  		return
   441  	}
   442  	smith.line("continue")
   443  }
   444  
   445  func (smith *Smith) stmtGoto() {
   446  	// TODO: support goto down
   447  	id := smith.materializeGotoLabel()
   448  	smith.line("goto %v", id)
   449  }
   450  
   451  func (smith *Smith) stmtSink() {
   452  	// Makes var escape.
   453  	smith.line("SINK = %v", smith.exprVar(smith.atype(TraitAny)))
   454  }