0

Add Clang static analysis control to all assert functions in logging.h

This CL adds static analysis hints to all assertion functions in logging.h. On builds with static analysis enabled, ANALYSIS_ASSUME_TRUE generates code which prevents the analyzer from evaluating codepaths that are asserted to be impossible.

R=brucedawson@chromium.org,danakj@chromium.org
BUG=327707

Review-Url: https://codereview.chromium.org/2729503004
Cr-Commit-Position: refs/heads/master@{#466129}
This commit is contained in:
kmarshall
2017-04-20 14:05:26 -07:00
committed by Commit bot
parent 2269e30d4e
commit fe2f09f858

@ -304,6 +304,30 @@ typedef bool (*LogMessageHandlerFunction)(int severity,
BASE_EXPORT void SetLogMessageHandler(LogMessageHandlerFunction handler);
BASE_EXPORT LogMessageHandlerFunction GetLogMessageHandler();
// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints
// to Clang which control what code paths are statically analyzed,
// and is meant to be used in conjunction with assert & assert-like functions.
// The expression is passed straight through if analysis isn't enabled.
#if defined(__clang_analyzer__)
inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) {
return false;
}
inline constexpr bool AnalyzerAssumeTrue(bool arg) {
// AnalyzerNoReturn() is invoked and analysis is terminated if |arg| is
// false.
return arg || AnalyzerNoReturn();
}
#define ANALYZER_ASSUME_TRUE(arg) ::logging::AnalyzerAssumeTrue(!!(arg))
#else // !defined(__clang_analyzer__)
#define ANALYZER_ASSUME_TRUE(arg) (arg)
#endif // defined(__clang_analyzer__)
typedef int LogSeverity;
const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity
// Note: the log severities are used to index into the array of names,
@ -423,8 +447,9 @@ const LogSeverity LOG_0 = LOG_ERROR;
// TODO(akalin): Add more VLOG variants, e.g. VPLOG.
#define LOG_ASSERT(condition) \
LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
#define LOG_ASSERT(condition) \
LOG_IF(FATAL, !(ANALYZER_ASSUME_TRUE(condition))) \
<< "Assert failed: " #condition ". "
#if defined(OS_WIN)
#define PLOG_STREAM(severity) \
@ -600,10 +625,10 @@ class CheckOpResult {
// Do as much work as possible out of line to reduce inline code size.
#define CHECK(condition) \
LAZY_STREAM(::logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \
!(condition))
!ANALYZER_ASSUME_TRUE(condition))
#define PCHECK(condition) \
LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \
#define PCHECK(condition) \
LAZY_STREAM(PLOG_STREAM(FATAL), !ANALYZER_ASSUME_TRUE(condition)) \
<< "Check failed: " #condition ". "
#endif // _PREFAST_
@ -700,17 +725,21 @@ std::string* MakeCheckOpString<std::string, std::string>(
// The (int, int) specialization works around the issue that the compiler
// will not instantiate the template version of the function on values of
// unnamed enum type - see comment below.
//
// The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under
// static analysis builds, blocks analysis of the current path if the
// condition is false.
#define DEFINE_CHECK_OP_IMPL(name, op) \
template <class t1, class t2> \
inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
const char* names) { \
if (v1 op v2) \
if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
return NULL; \
else \
return ::logging::MakeCheckOpString(v1, v2, names); \
} \
inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
if (v1 op v2) \
if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
return NULL; \
else \
return ::logging::MakeCheckOpString(v1, v2, names); \
@ -813,35 +842,15 @@ const LogSeverity LOG_DCHECK = LOG_INFO;
LAZY_STREAM(PLOG_STREAM(DCHECK), false) \
<< "Check failed: " #condition ". "
#elif defined(__clang_analyzer__)
// Keeps the static analyzer from proceeding along the current codepath,
// otherwise false positive errors may be generated by null pointer checks.
inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) {
return false;
}
#define DCHECK(condition) \
LAZY_STREAM( \
LOG_STREAM(DCHECK), \
DCHECK_IS_ON() ? (logging::AnalyzerNoReturn() || !(condition)) : false) \
<< "Check failed: " #condition ". "
#define DPCHECK(condition) \
LAZY_STREAM( \
PLOG_STREAM(DCHECK), \
DCHECK_IS_ON() ? (logging::AnalyzerNoReturn() || !(condition)) : false) \
<< "Check failed: " #condition ". "
#else
#else // !(defined(_PREFAST_) && defined(OS_WIN))
#if DCHECK_IS_ON()
#define DCHECK(condition) \
LAZY_STREAM(LOG_STREAM(DCHECK), !(condition)) \
#define DCHECK(condition) \
LAZY_STREAM(LOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \
<< "Check failed: " #condition ". "
#define DPCHECK(condition) \
LAZY_STREAM(PLOG_STREAM(DCHECK), !(condition)) \
#define DPCHECK(condition) \
LAZY_STREAM(PLOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \
<< "Check failed: " #condition ". "
#else // DCHECK_IS_ON()
@ -851,7 +860,7 @@ inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) {
#endif // DCHECK_IS_ON()
#endif
#endif // defined(_PREFAST_) && defined(OS_WIN)
// Helper macro for binary operators.
// Don't use this macro directly in your code, use DCHECK_EQ et al below.