diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp index 94119fc253523..967af83f62e04 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -1650,11 +1650,27 @@ static bool hasAnyBoundsAttributes(const FunctionDecl *FD) { }); } +// Constant fold a conditional expression 'cond ? A : B' to +// - 'A', if 'cond' has constant true value; +// - 'B', if 'cond' has constant false value. +static const Expr *tryConstantFoldConditionalExpr(const Expr *E, + const ASTContext &Ctx) { + // FIXME: more places can use this function + if (const auto *CE = dyn_cast(E)) { + bool CondEval; + + if (CE->getCond()->EvaluateAsBooleanCondition(CondEval, Ctx)) + return CondEval ? CE->getLHS() : CE->getRHS(); + } + return E; +} + // A pointer type expression is known to be null-terminated, if // 1. it is a string literal or `PredefinedExpr` (e.g., `__func__`); // 2. it has the form: E.c_str(), for any expression E of `std::string` type; // 3. it has `__null_terminated` type static bool isNullTermPointer(const Expr *Ptr, ASTContext &Ctx) { + Ptr = tryConstantFoldConditionalExpr(Ptr, Ctx); if (isa(Ptr->IgnoreParenImpCasts())) return true; if (isa(Ptr->IgnoreParenImpCasts())) diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-fold-conditional.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fold-conditional.cpp new file mode 100644 index 0000000000000..b4f30b533bc4b --- /dev/null +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-fold-conditional.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fsyntax-only -Wno-all -Wunsafe-buffer-usage -verify %s -std=c++20 +// RUN: %clang_cc1 -fsyntax-only -Wno-all -Wunsafe-buffer-usage -verify %s -x c +// expected-no-diagnostics + +typedef struct {} FILE; +int fprintf( FILE* stream, const char* format, ... ); +FILE * stderr; + +#define DEBUG_ASSERT_MESSAGE(name, assertion, label, message, file, line, value) \ + fprintf(stderr, "AssertMacros: %s, %s file: %s, line: %d, value: %lld\n", \ + assertion, (message!=0) ? message : "", file, line, (long long) (value)); + + +#define Require(assertion, exceptionLabel) \ + do \ + { \ + if ( __builtin_expect(!(assertion), 0) ) { \ + DEBUG_ASSERT_MESSAGE( \ + "DEBUG_ASSERT_COMPONENT_NAME_STRING", \ + #assertion, #exceptionLabel, 0, __FILE__, __LINE__, 0); \ + goto exceptionLabel; \ + } \ + } while ( 0 ) + + +void f(int x, int y) { + Require(x == y, L1); + L1: + return; +} +