From 3368958ac196c92175a7c76dc6f56d59447f300c Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Wed, 26 Aug 2020 12:55:10 -0700 Subject: [PATCH] GH-955: Prohibit `fallthrough` in typecasting `switch` cases --- src/Stmt.cc | 35 +++++++++- src/parse.y | 4 +- .../out | 3 + .../switch-type-cast-fallthrough-error.zeek | 64 +++++++++++++++++++ 4 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 testing/btest/Baseline/language.switch-type-cast-fallthrough-error/out create mode 100644 testing/btest/language/switch-type-cast-fallthrough-error.zeek diff --git a/src/Stmt.cc b/src/Stmt.cc index fe6bb431a5..cb3de6d2d7 100644 --- a/src/Stmt.cc +++ b/src/Stmt.cc @@ -484,6 +484,23 @@ static StmtTag get_last_stmt_tag(const Stmt* stmt) return get_last_stmt_tag(stmts->Stmts()[len - 1]); } +class FallthroughFinder : public TraversalCallback { + TraversalCode PreStmt(const Stmt* stmt) override + { + if ( stmt->Tag() == STMT_SWITCH ) + // Don't search within nested switch-statements. + return TC_ABORTSTMT; + + if ( stmt->Tag() != STMT_FALLTHROUGH ) + return TC_CONTINUE; + + reporter->PushLocation(stmt->GetLocationInfo()); + reporter->Error("invalid 'fallthrough' in type-casting 'case' block"); + reporter->PopLocation(); + return TC_CONTINUE; + } +}; + Case::Case(ListExprPtr arg_expr_cases, IDPList* arg_type_cases, StmtPtr arg_s) : expr_cases(std::move(arg_expr_cases)), type_cases(arg_type_cases), @@ -493,14 +510,26 @@ Case::Case(ListExprPtr arg_expr_cases, IDPList* arg_type_cases, if ( t != STMT_BREAK && t != STMT_FALLTHROUGH && t != STMT_RETURN ) Error("case block must end in break/fallthrough/return statement"); + + if ( type_cases && Body() ) + for ( const auto& id : *type_cases ) + if ( id->Name() ) + { + FallthroughFinder ff; + Body()->Traverse(&ff); + break; + } } Case::~Case() { - for ( const auto& id : *type_cases ) - Unref(id); + if ( type_cases ) + { + for ( const auto& id : *type_cases ) + Unref(id); - delete type_cases; + delete type_cases; + } } void Case::Describe(ODesc* d) const diff --git a/src/parse.y b/src/parse.y index 4b3b6afdbd..d0eeb91300 100644 --- a/src/parse.y +++ b/src/parse.y @@ -1638,13 +1638,13 @@ case_list: case: TOK_CASE expr_list ':' stmt_list - { $$ = new zeek::detail::Case({zeek::AdoptRef{}, $2}, 0, {zeek::AdoptRef{}, $4}); } + { $$ = new zeek::detail::Case({zeek::AdoptRef{}, $2}, nullptr, {zeek::AdoptRef{}, $4}); } | TOK_CASE case_type_list ':' stmt_list { $$ = new zeek::detail::Case(nullptr, $2, {zeek::AdoptRef{}, $4}); } | TOK_DEFAULT ':' stmt_list - { $$ = new zeek::detail::Case(nullptr, 0, {zeek::AdoptRef{}, $3}); } + { $$ = new zeek::detail::Case(nullptr, nullptr, {zeek::AdoptRef{}, $3}); } ; case_type_list: diff --git a/testing/btest/Baseline/language.switch-type-cast-fallthrough-error/out b/testing/btest/Baseline/language.switch-type-cast-fallthrough-error/out new file mode 100644 index 0000000000..2710ec6730 --- /dev/null +++ b/testing/btest/Baseline/language.switch-type-cast-fallthrough-error/out @@ -0,0 +1,3 @@ +error in /home/jon/pro/zeek/zeek/testing/btest/.tmp/language.switch-type-cast-fallthrough-error/switch-type-cast-fallthrough-error.zeek, line 9: invalid 'fallthrough' in type-casting 'case' block +error in /home/jon/pro/zeek/zeek/testing/btest/.tmp/language.switch-type-cast-fallthrough-error/switch-type-cast-fallthrough-error.zeek, line 20: invalid 'fallthrough' in type-casting 'case' block +error in /home/jon/pro/zeek/zeek/testing/btest/.tmp/language.switch-type-cast-fallthrough-error/switch-type-cast-fallthrough-error.zeek, line 22: invalid 'fallthrough' in type-casting 'case' block diff --git a/testing/btest/language/switch-type-cast-fallthrough-error.zeek b/testing/btest/language/switch-type-cast-fallthrough-error.zeek new file mode 100644 index 0000000000..7dc07511ed --- /dev/null +++ b/testing/btest/language/switch-type-cast-fallthrough-error.zeek @@ -0,0 +1,64 @@ +# @TEST-EXEC-FAIL: zeek -b %INPUT >out 2>&1 +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff out + +local x: any = 0; + +switch ( x ) { +case type count as i: + print "count", i; + fallthrough; # This is invalid +case type double as d: + print "double", d; + break; +} + +switch ( x ) { +case type count as i: + print "count", i; + + if ( i == 0 ) + fallthrough; # This is invalid + else + fallthrough; # This is invalid + + break; +case type double as d: + print "double", d; + break; +} + +switch ( x ) { +case type count as i: + print "count", i; + + switch ( x as count ) { + case 0: + fallthrough; # This is valid (inside nested switch statement) + case 1: + print "1"; + break; + } + + break; +case type double as d: + print "double", d; + break; +} + +switch ( x as count ) { +case 0: + print "0"; + fallthrough; +case 1: + print "1"; + break; +} + +switch ( x ) { +case type count: + print "count"; + fallthrough; +case type double: + print "double"; + break; +}