summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Withnall <philip@tecnocode.co.uk>2019-12-17 11:31:02 +0000
committerPhilip Withnall <philip@tecnocode.co.uk>2019-12-17 11:31:02 +0000
commitf08f000baed3c648e1ab21ea84e7961ec1649c04 (patch)
treebee01f78d5156dd26348137ca75109ca5dc568b1
parent0a1c3fa2c6f1498d17ad24fe976b17e3c81f3a90 (diff)
parent47e071d86e28b48d0d7a14619dd5bde6f5417579 (diff)
Merge branch 'run-cleanly-on-c++' into 'master'
Run cleanly on C++ code See merge request tartan/tartan!4
-rw-r--r--clang-plugin/assertion-extracter.cpp56
-rw-r--r--tests/Makefile.am4
-rw-r--r--tests/assertion-extraction-cpp.cpp32
-rw-r--r--tests/assertion.tail.c2
-rwxr-xr-xtests/wrapper-compiler-errors15
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