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:
@ -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.
|
||||
|
Reference in New Issue
Block a user