Add "fallthrough" keyword, require a flow statement to end case blocks.

Case blocks in switch statements now must end in a break, return, or
fallthrough statement to give best mix of safety, readability, and
flexibility.

The new fallthrough keyword explicitly allows control to be passed to the
next case block in a switch statement.

Addresses #754.
This commit is contained in:
Jon Siwek 2013-01-16 16:17:17 -06:00
parent 8695053e27
commit be71a42f4c
8 changed files with 150 additions and 33 deletions

View file

@ -23,7 +23,7 @@ const char* stmt_name(BroStmtTag t)
"print", "event", "expr", "if", "when", "switch",
"for", "next", "break", "return", "add", "delete",
"list", "bodylist",
"<init>",
"<init>", "fallthrough",
"null",
};
@ -584,6 +584,29 @@ bool IfStmt::DoUnserialize(UnserialInfo* info)
return s2 != 0;
}
static BroStmtTag get_last_stmt_tag(const Stmt* stmt)
{
if ( ! stmt ) return STMT_NULL;
if ( stmt->Tag() != STMT_LIST ) return stmt->Tag();
const StmtList* stmts = stmt->AsStmtList();
int len = stmts->Stmts().length();
if ( len == 0 ) return STMT_LIST;
return get_last_stmt_tag(stmts->Stmts()[len - 1]);
}
Case::Case(ListExpr* c, Stmt* arg_s)
: cases(simplify_expr_list(c, SIMPLIFY_GENERAL)), s(arg_s)
{
BroStmtTag t = get_last_stmt_tag(Body());
if ( t != STMT_BREAK && t != STMT_FALLTHROUGH && t != STMT_RETURN )
Error("case block must end in break/fallthrough/return statement");
}
Case::~Case()
{
Unref(cases);
@ -701,9 +724,6 @@ SwitchStmt::SwitchStmt(Expr* index, case_list* arg_cases) :
const Case* c = (*cases)[i];
const ListExpr* le = c->Cases();
if ( ! c->Body() || c->Body()->AsStmtList()->Stmts().length() == 0 )
c->Error("empty case label body does nothing");
if ( le )
{
if ( ! le->Type()->AsTypeList()->AllMatch(e->Type(), false) )
@ -798,12 +818,18 @@ Val* SwitchStmt::DoExec(Frame* f, Val* v, stmt_flow_type& flow) const
if ( matching_label_idx == -1 )
return 0;
const Case* c = (*cases)[matching_label_idx];
for ( int i = matching_label_idx; i < cases->length(); ++i )
{
const Case* c = (*cases)[i];
flow = FLOW_NEXT;
rval = c->Body()->Exec(f, flow);
flow = FLOW_NEXT;
rval = c->Body()->Exec(f, flow);
if ( flow == FLOW_BREAK )
if ( flow == FLOW_BREAK || flow == FLOW_RETURN )
break;
}
if ( flow != FLOW_RETURN )
flow = FLOW_NEXT;
return rval;
@ -1461,6 +1487,47 @@ bool BreakStmt::DoUnserialize(UnserialInfo* info)
return true;
}
Val* FallthroughStmt::Exec(Frame* /* f */, stmt_flow_type& flow) const
{
RegisterAccess();
flow = FLOW_FALLTHROUGH;
return 0;
}
int FallthroughStmt::IsPure() const
{
return 1;
}
void FallthroughStmt::Describe(ODesc* d) const
{
Stmt::Describe(d);
Stmt::DescribeDone(d);
}
TraversalCode FallthroughStmt::Traverse(TraversalCallback* cb) const
{
TraversalCode tc = cb->PreStmt(this);
HANDLE_TC_STMT_PRE(tc);
tc = cb->PostStmt(this);
HANDLE_TC_STMT_POST(tc);
}
IMPLEMENT_SERIAL(FallthroughStmt, SER_FALLTHROUGH_STMT);
bool FallthroughStmt::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_FALLTHROUGH_STMT, Stmt);
return true;
}
bool FallthroughStmt::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Stmt);
return true;
}
ReturnStmt::ReturnStmt(Expr* arg_e) : ExprStmt(STMT_RETURN, arg_e)
{
Scope* s = current_scope();