diff options
-rw-r--r-- | clang-plugin/assertion-extracter.cpp | 56 | ||||
-rw-r--r-- | tests/Makefile.am | 4 | ||||
-rw-r--r-- | tests/assertion-extraction-cpp.cpp | 32 | ||||
-rw-r--r-- | tests/assertion.tail.c | 2 | ||||
-rwxr-xr-x | tests/wrapper-compiler-errors | 15 |
5 files changed, 103 insertions, 6 deletions
diff --git a/clang-plugin/assertion-extracter.cpp b/clang-plugin/assertion-extracter.cpp index 61ec110..9b64bfd 100644 --- a/clang-plugin/assertion-extracter.cpp +++ b/clang-plugin/assertion-extracter.cpp @@ -378,6 +378,19 @@ AssertionExtracter::is_assertion_stmt (Stmt& stmt, const ASTContext& context) * I ↦ TRUE */ return dyn_cast<Expr> (&stmt); } + case Stmt::StmtClass::ExprWithCleanupsClass: { + /* Handle an expression that introduces a cleanup to be run at the end + * of evaluation; most likely a C++ temporary object. + * Transformations: + * S ↦ calc(S) */ + ExprWithCleanups& expr_cleanup = cast<ExprWithCleanups> (stmt); + + Stmt *sub_expr = expr_cleanup.getSubExpr (); + if (sub_expr == NULL) + return NULL; + + return is_assertion_stmt (*sub_expr, context); + } case Stmt::StmtClass::ParenExprClass: { /* Handle a parenthesised expression. * Transformations: @@ -415,6 +428,20 @@ AssertionExtracter::is_assertion_stmt (Stmt& stmt, const ASTContext& context) return is_assertion_stmt (*sub_expr, context); } + case Stmt::StmtClass::CXXTryStmtClass: { + /* Handle a C++ try statement. We assume any assertions in any of the + * handler blocks happen after program state is modified, so we only + * consider the try block. + * Transformations: + * try { S1 } catch (…) { S2 } [ … catch (…) { Sn } ] ↦ calc(S1) */ + CXXTryStmt& try_stmt = cast<CXXTryStmt> (stmt); + + CompoundStmt* try_block = try_stmt.getTryBlock (); + if (try_block == NULL) + return NULL; + + return is_assertion_stmt (*try_block, context); + } case Stmt::StmtClass::GCCAsmStmtClass: case Stmt::StmtClass::MSAsmStmtClass: /* Inline assembly. There is no way we are parsing this, so @@ -441,10 +468,29 @@ AssertionExtracter::is_assertion_stmt (Stmt& stmt, const ASTContext& context) * Transformations: * S1 op S2 ↦ NULL */ case Stmt::StmtClass::ForStmtClass: + case Stmt::StmtClass::CXXForRangeStmtClass: /* Handle a for statement. We assume these *always* change * program state. * Transformations: * for (…) { … } ↦ NULL */ + case Stmt::StmtClass::AtomicExprClass: + /* Handle a C++11 or C11 atomic builtin. This definitely modifies + * program state. + * Transformations: + * __atomic_builtin (…) ↦ NULL */ + case Stmt::StmtClass::CXXDeleteExprClass: + case Stmt::StmtClass::CXXNewExprClass: + case Stmt::StmtClass::CXXThrowExprClass: + /* Handle a C++ delete, new, or throw expression. These definitely + * modify program state. + * Transformations: + * delete S ↦ NULL + * new S (…) ↦ NULL + * throw S ↦ NULL */ + case Stmt::StmtClass::CXXMemberCallExprClass: + case Stmt::StmtClass::CXXOperatorCallExprClass: + /* Handle a C++ call. These could possibly contain some form of + * assertion, but not one that we recognize currently. */ case Stmt::StmtClass::WhileStmtClass: { /* Handle a while(…) { … } block. Because we don't want to solve * the halting problem, just assume all while statements cannot @@ -471,6 +517,9 @@ AssertionExtracter::is_assertion_stmt (Stmt& stmt, const ASTContext& context) static Expr* _simplify_boolean_expr (Expr* expr, const ASTContext& context) { + if (ExprWithCleanups* expr_cleanup = dyn_cast<ExprWithCleanups> (expr)) + expr = expr_cleanup->getSubExpr (); + expr = expr->IgnoreParens (); DEBUG ("Simplifying boolean expression of type " << @@ -686,6 +735,8 @@ _assertion_is_gobject_type_check (Expr& assertion_expr, case Expr::UnaryOperatorClass: case Expr::ConditionalOperatorClass: case Expr::CallExprClass: + case Expr::CXXMemberCallExprClass: + case Expr::CXXOperatorCallExprClass: case Expr::ImplicitCastExprClass: { /* These can’t be type checks. */ return 0; @@ -819,7 +870,12 @@ _assertion_is_explicit_nonnull_check (Expr& assertion_expr, * detecting them requires a formal program transformation which * has not been implemented yet. */ case Expr::CallExprClass: + case Expr::CXXMemberCallExprClass: /* Function calls can’t be nonnull checks. */ + case Expr::CXXOperatorCallExprClass: + /* An overloaded operator might be a nonnull check, but only on a C++ + * object, which we choose not to handle because objects might implement + * any sort of weird behaviour in their overloaded operators. */ case Expr::IntegerLiteralClass: { /* Integer literals can’t be nonnull checks. */ return 0; diff --git a/tests/Makefile.am b/tests/Makefile.am index 75f069f..87b1713 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -2,12 +2,14 @@ # compile successfully without Tartan, and which should fail compilation # with Tartan (and -Werror). -TEST_EXTENSIONS = .c +TEST_EXTENSIONS = .c .cpp AM_TESTS_ENVIRONMENT = export abs_top_builddir=$(abs_top_builddir); C_LOG_COMPILER = $(top_srcdir)/tests/wrapper-compiler-errors +CPP_LOG_COMPILER = $(C_LOG_COMPILER) c_tests = \ assertion-extraction.c \ + assertion-extraction-cpp.cpp \ assertion-extraction-return.c \ gsignal-connect.c \ gvariant-builder.c \ diff --git a/tests/assertion-extraction-cpp.cpp b/tests/assertion-extraction-cpp.cpp new file mode 100644 index 0000000..8d52f0c --- /dev/null +++ b/tests/assertion-extraction-cpp.cpp @@ -0,0 +1,32 @@ +/* Template: assertion */ + +/* + * null passed to a callee that requires a non-null argument + * assertion_func (NULL, 1, obj); + * ~~~~ ^ + * null passed to a callee that requires a non-null argument + * assertion_func (NULL, 3, NULL); + * ~~~~ ^ + */ +{ + struct Temp { + operator bool () { return true; } + ~Temp () {} + }; + g_return_if_fail (Temp () && some_str); +} + +/* + * null passed to a callee that requires a non-null argument + * assertion_func (NULL, 1, obj); + * ~~~~ ^ + * null passed to a callee that requires a non-null argument + * assertion_func (NULL, 3, NULL); + * ~~~~ ^ + */ +{ + try { + g_return_if_fail (some_str); + } catch (...) { + } +} diff --git a/tests/assertion.tail.c b/tests/assertion.tail.c index 4a50091..be5f93e 100644 --- a/tests/assertion.tail.c +++ b/tests/assertion.tail.c @@ -4,7 +4,7 @@ int main (void) { - GObject *obj = g_malloc (5); + GObject *obj = (GObject *) g_malloc (5); /* Various NULL and non-NULL calls to the function. */ assertion_func ("str", 0, obj); diff --git a/tests/wrapper-compiler-errors b/tests/wrapper-compiler-errors index 941024a..bc9fe3f 100755 --- a/tests/wrapper-compiler-errors +++ b/tests/wrapper-compiler-errors @@ -21,6 +21,7 @@ # asserts there’s no error. input_filename=$1 +input_ext=${input_filename##*.} temp_dir=`mktemp -d`/${abs_top_builddir} tests_dir=`dirname $0` tartan=${tests_dir}/../scripts/tartan @@ -51,14 +52,14 @@ mkdir -p ${temp_dir}/${srcdir} tail -n +3 "${input_filename}" > "${temp_dir}/${input_filename}.tail" csplit --keep-files --elide-empty-files --silent \ --prefix="${temp_dir}/${input_filename}_" \ - --suffix-format='%02d.c' \ + --suffix-format="%02d${input_ext}" \ "${temp_dir}/${input_filename}.tail" '/^\/\*/' '{*}' echo "" num=0 -while [[ -f `printf "${temp_dir}/${input_filename}_%02d.c" ${num}` ]]; do - section_filename=`printf ${temp_dir}/${input_filename}_%02d.c ${num}` +while [[ -f `printf "${temp_dir}/${input_filename}_%02d${input_ext}" ${num}` ]]; do + section_filename=`printf ${temp_dir}/${input_filename}_%02d${input_ext} ${num}` expected_error_filename=`printf ${temp_dir}/${input_filename}_%02d.expected ${num}` actual_error_filename=`printf ${temp_dir}/${input_filename}_%02d.actual ${num}` @@ -88,6 +89,12 @@ while [[ -f `printf "${temp_dir}/${input_filename}_%02d.c" ${num}` ]]; do expect_error=true fi + case $input_ext in + c) lang_options=-std=c89 ;; + cpp) lang_options=-xc++ ;; + *) lang_options= + esac + # Run the compiler. # e.g. Set # TARTAN_TEST_OPTIONS="-analyzer-checker=debug.ViewExplodedGraph" to @@ -95,7 +102,7 @@ while [[ -f `printf "${temp_dir}/${input_filename}_%02d.c" ${num}` ]]; do TARTAN_PLUGIN=$tartan_plugin \ TARTAN_OPTIONS=--quiet \ $tartan \ - -cc1 -analyze -std=c89 -Wno-visibility $TARTAN_TEST_OPTIONS \ + -cc1 -analyze $lang_options -Wno-visibility $TARTAN_TEST_OPTIONS \ `pkg-config --cflags glib-2.0` \ $system_includes \ $section_filename > $actual_error_filename 2>&1 |