From 1bac6a0fe5c5dc85f19959de1a5fd9f109165438 Mon Sep 17 00:00:00 2001 From: Dave Rigby Date: Thu, 17 Dec 2015 14:54:36 +0000 Subject: [PATCH] Add support for Clang Static Analyzer The Clang Static Analyzer[1] encounters some issues parsing GoogleTest's macros, mostly where GoogleTest has deliberately attempted to obfuscate the behavour of some code to work around compiler warnings. Specifically, GTest defines a AlwaysTrue() function which in reality always returns true, but who's definition lives in a seperate compilation unit which prevents compilers (and Clang analyzer) from knowing that certain paths are always (or never) executed. The net effect of this is that Clang analyzer raises a number of false positives. For example consider code of the form: 1 Foo* foo = nullptr; 2 ASSERT_NO_THROW(foo = some_function()); 3 foo->bar; In practice foo is guaranteed to be non-NULL at line 3, as if the assignment at line 2 failed then we wouldn't have reached line 3. However the expansion of the ASSERT_NO_THROW macro involves code of the form: if (AlwaysTrue()) { try { foo = ... } } ... The analyzer doesn't know that the conditional will /always/ be executed, so it belives there's a path where foo is not initialized at line 3 and hence raises a report. Solve this by making the function inline when running with clang analyzer, un-hiding the definition and allowing Clang to see that the conditional path is actually unconditional. [1]: http://clang-analyzer.llvm.org Change-Id: I72906e9f1f9c283bc52178730c99bf171a7c966a --- googletest/include/gtest/internal/gtest-internal.h | 7 +++++++ googletest/src/gtest.cc | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/googletest/include/gtest/internal/gtest-internal.h b/googletest/include/gtest/internal/gtest-internal.h index ebd1cf615d..54e56c9134 100644 --- a/googletest/include/gtest/internal/gtest-internal.h +++ b/googletest/include/gtest/internal/gtest-internal.h @@ -729,8 +729,15 @@ GTEST_API_ std::string GetCurrentOsStackTraceExceptTop( // Helpers for suppressing warnings on unreachable code or constant // condition. +// Always returns true. +#ifdef __clang_analyzer__ +// For Clang static analyzer we define this inline so Clang knows it +// always returns true. +GTEST_API_ bool AlwaysTrue() { return true; } +#else // Always returns true. GTEST_API_ bool AlwaysTrue(); +#endif // Always returns false. inline bool AlwaysFalse() { return !AlwaysTrue(); } diff --git a/googletest/src/gtest.cc b/googletest/src/gtest.cc index 2bac245d75..154122de48 100644 --- a/googletest/src/gtest.cc +++ b/googletest/src/gtest.cc @@ -4985,6 +4985,9 @@ class ClassUniqueToAlwaysTrue {}; bool IsTrue(bool condition) { return condition; } +// For Clang static analyzer we define this inline so Clang knows it +// always returns true - see gtest-internal.h +#ifndef __clang_analyzer__ bool AlwaysTrue() { #if GTEST_HAS_EXCEPTIONS // This condition is always false so AlwaysTrue() never actually throws, @@ -4994,6 +4997,7 @@ bool AlwaysTrue() { #endif // GTEST_HAS_EXCEPTIONS return true; } +#endif // If *pstr starts with the given prefix, modifies *pstr to be right // past the prefix and returns true; otherwise leaves *pstr unchanged