// stmt generates code for statements. func (c *compiler) stmt(s ast.Stmt) { pos := s.Span().Start switch s := s.(type) { case *ast.EmptyStmt, *ast.BadStmt: // skip case *ast.BlockStmt: for _, x := range s.Stmt { c.stmt(x) } case *ast.IfStmt: c.ifStmt(s) case *ast.ForStmt: c.forStmt(s) case *ast.DoStmt: c.doStmt(s) case *ast.WhileStmt: c.whileStmt(s) case *ast.ReturnStmt: c.returnStmt(s) case *ast.SwitchStmt: c.switchStmt(s) case *ast.GotoStmt: c.cg.Jump(c.labels[s.Label.Name]) case *ast.LabeledStmt: c.cg.Lab(c.labels[s.Label.Name]) c.stmt(s.Stmt) case *ast.BranchStmt: switch s.Type { case scan.Break: c.cg.Jump(c.breakStack[len(c.breakStack)-1]) case scan.Continue: c.cg.Jump(c.continueStack[len(c.continueStack)-1]) default: c.invalidAST(pos, "bad statement: %T", s) } case *ast.ExprStmt: c.expr(s.X) c.cg.Commit() default: c.invalidAST(pos, "bad statement: %T", s) } c.cg.Clear(true) }
// stmt type checks a statement. func (c *checker) stmt(ctx stmtContext, s ast.Stmt) { var x operand pos := s.Span().Start inner := ctx c.recordScope(s, c.scope) switch s := s.(type) { case *ast.BadStmt, *ast.EmptyStmt: // ignore case *ast.BranchStmt: switch s.Type { case scan.Break: if ctx&breakOk == 0 { c.errorf(pos, "break not in for/while or switch statement") } case scan.Continue: if ctx&continueOk == 0 { c.errorf(pos, "continue not in for/while statement") } default: c.invalidAST(pos, "branch statement: %s", s.Text) } case *ast.BlockStmt: c.openScope(s) defer c.closeScope() c.stmtList(inner, s.Stmt) case *ast.DoStmt: inner |= breakOk | continueOk c.stmt(inner, s.Body) c.expr(&x, s.Cond) case *ast.ExprStmt: c.expr(&x, s.X) case *ast.GotoStmt: obj, _ := c.scope.LookupParent(Lab, s.Label.Name, scan.NoPos) if obj == nil { c.errorf(pos, "goto cannot jump to non-existent label %v", s.Label.Name) } case *ast.LabeledStmt: c.stmt(inner, s.Stmt) case *ast.IfStmt: c.expr(&x, s.Cond) c.stmt(inner, s.Body) if s.Else != nil { c.stmt(inner, s.Else) } case *ast.ForStmt: inner |= breakOk | continueOk c.simpleStmt(s.Init) if s.Cond != nil { c.expr(&x, s.Cond) } c.simpleStmt(s.Post) c.stmt(inner, s.Body) case *ast.ReturnStmt: if s.X != nil { c.expr(&x, s.X) } else { x.mode = novalue } case *ast.SwitchStmt: inner |= breakOk c.expr(&x, s.Tag) sawCases := make(map[constant.Value]scanner.Position) defaultPos := scan.NoPos for _, n := range s.Body.Stmt { xpos := n.Span().Start switch n := n.(type) { case *ast.CaseClause: if n.Value != nil { c.expr(&x, n.Value) xpos := x.pos() if x.typ == Typ[Invalid] { continue } _, err := strconv.Atoi(x.val.String()) if x.mode != constant_ || err != nil { c.errorf(xpos, "non-constant integer in case statement") continue } if cpos, found := sawCases[x.val]; found { c.errorf(xpos, "duplicate case value") c.errorf(cpos, "\tpreviously used here") } else { sawCases[x.val] = n.Case.Span().Start } } else if defaultPos != scan.NoPos { c.errorf(xpos, "multiple defaults in one switch") c.errorf(defaultPos, "\tthis is the first default label") } else { defaultPos = xpos } c.stmtList(inner, n.Body) default: c.errorf(xpos, "switch only supports case/default clauses, got %T", n) } } if len(s.Body.Stmt) == 0 { c.errorf(pos, "empty switch statement") } case *ast.WhileStmt: inner |= breakOk | continueOk c.expr(&x, s.Cond) c.stmt(inner, s.Body) case *ast.BinaryExpr: c.expr(&x, s) case *ast.UnaryExpr: c.expr(&x, s) default: c.errorf(pos, "invalid statement: %T", s) } }