[clang] [Clang] Added clang diagnostic when snprintf/vsnprintf uses sizeof(de… (PR #170637)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 6 04:02:18 PST 2026
https://github.com/BgZun updated https://github.com/llvm/llvm-project/pull/170637
>From 254f9912b84a64d33b9bbf967ab0d50cfefd142b Mon Sep 17 00:00:00 2001
From: Bogdan Zunic <bzunic at cisco.com>
Date: Thu, 4 Dec 2025 02:00:59 -0800
Subject: [PATCH 1/7] [Clang] Added clang diagnostic when snprintf/vsnprintf
uses sizeof(dest) for the len parameter
---
clang/docs/ReleaseNotes.rst | 3 +
.../clang/Basic/DiagnosticSemaKinds.td | 4 +
clang/lib/Sema/SemaChecking.cpp | 73 +++++++++++--------
clang/test/SemaCXX/warn-memset-bad-sizeof.cpp | 9 +++
4 files changed, 58 insertions(+), 31 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 51f07256c5d9f..ee15880ce92ab 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -444,6 +444,9 @@ Improvements to Clang's diagnostics
comparison operators when mixed with bitwise operators in enum value initializers.
This can be locally disabled by explicitly casting the initializer value.
+- Clang now emits ``-Wsizeof-pointer-memaccess`` when snprintf/vsnprintf use the sizeof
+ the destination buffer(dynamically allocated) in the len parameter(#GH162366)
+
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 53aa86a7dabde..a496aee8e0c9e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -895,6 +895,10 @@ def warn_sizeof_pointer_type_memaccess : Warning<
"argument to 'sizeof' in %0 call is the same pointer type %1 as the "
"%select{destination|source}2; expected %3 or an explicit length">,
InGroup<SizeofPointerMemaccess>;
+def warn_sizeof_pointer_dest_type_memacess : Warning<
+ "argument to 'sizeof' in %0 call is the same expression as the "
+ "destination; did you mean to put an explicit length?">,
+ InGroup<SizeofPointerMemaccess>;
def warn_strlcpycat_wrong_size : Warning<
"size argument in %0 call appears to be size of the source; "
"expected the size of the destination">,
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index f4e58de91286b..00e0fc8521fa6 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1139,6 +1139,37 @@ static bool ProcessFormatStringLiteral(const Expr *FormatExpr,
return false;
}
+static const UnaryExprOrTypeTraitExpr *getAsSizeOfExpr(const Expr *E) {
+ if (const auto *Unary = dyn_cast<UnaryExprOrTypeTraitExpr>(E))
+ if (Unary->getKind() == UETT_SizeOf)
+ return Unary;
+ return nullptr;
+}
+
+/// If E is a sizeof expression, returns its argument expression,
+/// otherwise returns NULL.
+static const Expr *getSizeOfExprArg(const Expr *E) {
+ if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
+ if (!SizeOf->isArgumentType())
+ return SizeOf->getArgumentExpr()->IgnoreParenImpCasts();
+ return nullptr;
+}
+
+/// If E is a sizeof expression, returns its argument type.
+static QualType getSizeOfArgType(const Expr *E) {
+ if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
+ return SizeOf->getTypeOfArgument();
+ return QualType();
+}
+
+/// Check if two expressions refer to the same declaration.
+static bool referToTheSameDecl(const Expr *E1, const Expr *E2) {
+ if (const DeclRefExpr *D1 = dyn_cast_or_null<DeclRefExpr>(E1))
+ if (const DeclRefExpr *D2 = dyn_cast_or_null<DeclRefExpr>(E2))
+ return D1->getDecl() == D2->getDecl();
+ return false;
+}
+
void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
CallExpr *TheCall) {
if (TheCall->isValueDependent() || TheCall->isTypeDependent() ||
@@ -1449,6 +1480,17 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
}
}
DestinationSize = ComputeSizeArgument(0);
+ const Expr *SizeOfArg = TheCall->getArg(1)->IgnoreParenImpCasts();
+ const Expr *Dest = TheCall->getArg(0)->IgnoreParenImpCasts();
+ const Expr *SizeOfArgExpr = getSizeOfExprArg(SizeOfArg);
+ const QualType SizeOfArgType = getSizeOfArgType(SizeOfArg);
+ const Type *ExprType = SizeOfArgType.getTypePtrOrNull();
+ if (ExprType && ExprType->isPointerType() &&
+ referToTheSameDecl(SizeOfArgExpr, Dest)) {
+ DiagRuntimeBehavior(SizeOfArg->getExprLoc(), Dest,
+ PDiag(diag::warn_sizeof_pointer_dest_type_memacess)
+ << FD->getNameInfo().getName());
+ }
}
}
@@ -9979,29 +10021,6 @@ static const CXXRecordDecl *getContainedDynamicClass(QualType T,
return nullptr;
}
-static const UnaryExprOrTypeTraitExpr *getAsSizeOfExpr(const Expr *E) {
- if (const auto *Unary = dyn_cast<UnaryExprOrTypeTraitExpr>(E))
- if (Unary->getKind() == UETT_SizeOf)
- return Unary;
- return nullptr;
-}
-
-/// If E is a sizeof expression, returns its argument expression,
-/// otherwise returns NULL.
-static const Expr *getSizeOfExprArg(const Expr *E) {
- if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
- if (!SizeOf->isArgumentType())
- return SizeOf->getArgumentExpr()->IgnoreParenImpCasts();
- return nullptr;
-}
-
-/// If E is a sizeof expression, returns its argument type.
-static QualType getSizeOfArgType(const Expr *E) {
- if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
- return SizeOf->getTypeOfArgument();
- return QualType();
-}
-
namespace {
struct SearchNonTrivialToInitializeField
@@ -10499,14 +10518,6 @@ void Sema::CheckStrlcpycatArguments(const CallExpr *Call,
OS.str());
}
-/// Check if two expressions refer to the same declaration.
-static bool referToTheSameDecl(const Expr *E1, const Expr *E2) {
- if (const DeclRefExpr *D1 = dyn_cast_or_null<DeclRefExpr>(E1))
- if (const DeclRefExpr *D2 = dyn_cast_or_null<DeclRefExpr>(E2))
- return D1->getDecl() == D2->getDecl();
- return false;
-}
-
static const Expr *getStrlenExprArg(const Expr *E) {
if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
const FunctionDecl *FD = CE->getDirectCallee();
diff --git a/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp b/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp
index 0a78caa924ea9..bacb2c167af14 100644
--- a/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp
+++ b/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp
@@ -188,3 +188,12 @@ void strcpy_and_friends() {
strndup(FOO, sizeof(FOO)); // \
// expected-warning {{'strndup' call operates on objects of type 'const char' while the size is based on a different type 'const char *'}} expected-note{{did you mean to provide an explicit length?}}
}
+
+extern "C" int snprintf(char* buffer, __SIZE_TYPE__ buf_size, const char* format, ...);
+extern "C" void* malloc(unsigned size);
+
+void check_prints(){
+ char* a = (char*) malloc(20);
+ const char* b = "Hello World";
+ snprintf(a, sizeof(a), "%s", b); // expected-warning{{argument to 'sizeof' in 'snprintf' call is the same expression as the destination; did you mean to put an explicit length?}}
+}
>From 8340bb3e090d3cfe1800635bb69f5a064d4e48a2 Mon Sep 17 00:00:00 2001
From: Bogdan Zunic <bzunic at cisco.com>
Date: Tue, 9 Dec 2025 02:24:26 -0800
Subject: [PATCH 2/7] Moved diagnostic logic to a separate function
---
.../clang/Basic/DiagnosticSemaKinds.td | 4 -
clang/include/clang/Sema/Sema.h | 3 +
clang/lib/Sema/SemaChecking.cpp | 197 +++++++++---------
clang/test/SemaCXX/warn-memset-bad-sizeof.cpp | 2 +-
4 files changed, 106 insertions(+), 100 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a496aee8e0c9e..53aa86a7dabde 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -895,10 +895,6 @@ def warn_sizeof_pointer_type_memaccess : Warning<
"argument to 'sizeof' in %0 call is the same pointer type %1 as the "
"%select{destination|source}2; expected %3 or an explicit length">,
InGroup<SizeofPointerMemaccess>;
-def warn_sizeof_pointer_dest_type_memacess : Warning<
- "argument to 'sizeof' in %0 call is the same expression as the "
- "destination; did you mean to put an explicit length?">,
- InGroup<SizeofPointerMemaccess>;
def warn_strlcpycat_wrong_size : Warning<
"size argument in %0 call appears to be size of the source; "
"expected the size of the destination">,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index ae500139ee6f7..69c921c8d2d35 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3043,6 +3043,9 @@ class Sema final : public SemaBase {
void CheckMemaccessArguments(const CallExpr *Call, unsigned BId,
IdentifierInfo *FnName);
+ bool CheckSizeOfExpression(const Expr *SizeOfArg, const Expr *Dest,
+ llvm::FoldingSetNodeID SizeOfArgID,
+ IdentifierInfo *FnName);
// Warn if the user has made the 'size' argument to strlcpy or strlcat
// be the size of the source, instead of the destination.
void CheckStrlcpycatArguments(const CallExpr *Call, IdentifierInfo *FnName);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 00e0fc8521fa6..51d9cf75cc424 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1139,37 +1139,6 @@ static bool ProcessFormatStringLiteral(const Expr *FormatExpr,
return false;
}
-static const UnaryExprOrTypeTraitExpr *getAsSizeOfExpr(const Expr *E) {
- if (const auto *Unary = dyn_cast<UnaryExprOrTypeTraitExpr>(E))
- if (Unary->getKind() == UETT_SizeOf)
- return Unary;
- return nullptr;
-}
-
-/// If E is a sizeof expression, returns its argument expression,
-/// otherwise returns NULL.
-static const Expr *getSizeOfExprArg(const Expr *E) {
- if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
- if (!SizeOf->isArgumentType())
- return SizeOf->getArgumentExpr()->IgnoreParenImpCasts();
- return nullptr;
-}
-
-/// If E is a sizeof expression, returns its argument type.
-static QualType getSizeOfArgType(const Expr *E) {
- if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
- return SizeOf->getTypeOfArgument();
- return QualType();
-}
-
-/// Check if two expressions refer to the same declaration.
-static bool referToTheSameDecl(const Expr *E1, const Expr *E2) {
- if (const DeclRefExpr *D1 = dyn_cast_or_null<DeclRefExpr>(E1))
- if (const DeclRefExpr *D2 = dyn_cast_or_null<DeclRefExpr>(E2))
- return D1->getDecl() == D2->getDecl();
- return false;
-}
-
void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
CallExpr *TheCall) {
if (TheCall->isValueDependent() || TheCall->isTypeDependent() ||
@@ -1480,17 +1449,11 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
}
}
DestinationSize = ComputeSizeArgument(0);
- const Expr *SizeOfArg = TheCall->getArg(1)->IgnoreParenImpCasts();
+ const Expr *LenArg = TheCall->getArg(1)->IgnoreParenImpCasts();
const Expr *Dest = TheCall->getArg(0)->IgnoreParenImpCasts();
- const Expr *SizeOfArgExpr = getSizeOfExprArg(SizeOfArg);
- const QualType SizeOfArgType = getSizeOfArgType(SizeOfArg);
- const Type *ExprType = SizeOfArgType.getTypePtrOrNull();
- if (ExprType && ExprType->isPointerType() &&
- referToTheSameDecl(SizeOfArgExpr, Dest)) {
- DiagRuntimeBehavior(SizeOfArg->getExprLoc(), Dest,
- PDiag(diag::warn_sizeof_pointer_dest_type_memacess)
- << FD->getNameInfo().getName());
- }
+ llvm::FoldingSetNodeID SizeOfArgID;
+ IdentifierInfo *FnInfo = FD->getIdentifier();
+ CheckSizeOfExpression(LenArg, Dest, SizeOfArgID, FnInfo);
}
}
@@ -10021,6 +9984,29 @@ static const CXXRecordDecl *getContainedDynamicClass(QualType T,
return nullptr;
}
+static const UnaryExprOrTypeTraitExpr *getAsSizeOfExpr(const Expr *E) {
+ if (const auto *Unary = dyn_cast<UnaryExprOrTypeTraitExpr>(E))
+ if (Unary->getKind() == UETT_SizeOf)
+ return Unary;
+ return nullptr;
+}
+
+/// If E is a sizeof expression, returns its argument expression,
+/// otherwise returns NULL.
+static const Expr *getSizeOfExprArg(const Expr *E) {
+ if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
+ if (!SizeOf->isArgumentType())
+ return SizeOf->getArgumentExpr()->IgnoreParenImpCasts();
+ return nullptr;
+}
+
+/// If E is a sizeof expression, returns its argument type.
+static QualType getSizeOfArgType(const Expr *E) {
+ if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
+ return SizeOf->getTypeOfArgument();
+ return QualType();
+}
+
namespace {
struct SearchNonTrivialToInitializeField
@@ -10257,60 +10243,8 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
// actually comparing the expressions for equality. Because computing the
// expression IDs can be expensive, we only do this if the diagnostic is
// enabled.
- if (SizeOfArg &&
- !Diags.isIgnored(diag::warn_sizeof_pointer_expr_memaccess,
- SizeOfArg->getExprLoc())) {
- // We only compute IDs for expressions if the warning is enabled, and
- // cache the sizeof arg's ID.
- if (SizeOfArgID == llvm::FoldingSetNodeID())
- SizeOfArg->Profile(SizeOfArgID, Context, true);
- llvm::FoldingSetNodeID DestID;
- Dest->Profile(DestID, Context, true);
- if (DestID == SizeOfArgID) {
- // TODO: For strncpy() and friends, this could suggest sizeof(dst)
- // over sizeof(src) as well.
- unsigned ActionIdx = 0; // Default is to suggest dereferencing.
- StringRef ReadableName = FnName->getName();
-
- if (const UnaryOperator *UnaryOp = dyn_cast<UnaryOperator>(Dest))
- if (UnaryOp->getOpcode() == UO_AddrOf)
- ActionIdx = 1; // If its an address-of operator, just remove it.
- if (!PointeeTy->isIncompleteType() &&
- (Context.getTypeSize(PointeeTy) == Context.getCharWidth()))
- ActionIdx = 2; // If the pointee's size is sizeof(char),
- // suggest an explicit length.
-
- // If the function is defined as a builtin macro, do not show macro
- // expansion.
- SourceLocation SL = SizeOfArg->getExprLoc();
- SourceRange DSR = Dest->getSourceRange();
- SourceRange SSR = SizeOfArg->getSourceRange();
- SourceManager &SM = getSourceManager();
-
- if (SM.isMacroArgExpansion(SL)) {
- ReadableName = Lexer::getImmediateMacroName(SL, SM, LangOpts);
- SL = SM.getSpellingLoc(SL);
- DSR = SourceRange(SM.getSpellingLoc(DSR.getBegin()),
- SM.getSpellingLoc(DSR.getEnd()));
- SSR = SourceRange(SM.getSpellingLoc(SSR.getBegin()),
- SM.getSpellingLoc(SSR.getEnd()));
- }
-
- DiagRuntimeBehavior(SL, SizeOfArg,
- PDiag(diag::warn_sizeof_pointer_expr_memaccess)
- << ReadableName
- << PointeeTy
- << DestTy
- << DSR
- << SSR);
- DiagRuntimeBehavior(SL, SizeOfArg,
- PDiag(diag::warn_sizeof_pointer_expr_memaccess_note)
- << ActionIdx
- << SSR);
-
- break;
- }
- }
+ if (CheckSizeOfExpression(LenExpr, Dest, SizeOfArgID, FnName))
+ break;
// Also check for cases where the sizeof argument is the exact same
// type as the memory argument, and where it points to a user-defined
@@ -10413,6 +10347,71 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
}
}
+bool Sema::CheckSizeOfExpression(const Expr *LenExpr, const Expr *Dest,
+ llvm::FoldingSetNodeID SizeOfArgID,
+ IdentifierInfo *FnName) {
+ const Expr *SizeOfArg = getSizeOfExprArg(LenExpr);
+ QualType DestTy = Dest->getType();
+ QualType PointeeTy;
+ if (const PointerType *DestPtrTy = DestTy->getAs<PointerType>()) {
+ PointeeTy = DestPtrTy->getPointeeType();
+
+ // Catch "memset(p, 0, sizeof(p))" -- needs to be sizeof(*p). Do this by
+ // actually comparing the expressions for equality. Because computing the
+ // expression IDs can be expensive, we only do this if the diagnostic is
+ // enabled.
+ if (SizeOfArg && !Diags.isIgnored(diag::warn_sizeof_pointer_expr_memaccess,
+ SizeOfArg->getExprLoc())) {
+ // We only compute IDs for expressions if the warning is enabled, and
+ // cache the sizeof arg's ID.
+ if (SizeOfArgID == llvm::FoldingSetNodeID())
+ SizeOfArg->Profile(SizeOfArgID, Context, true);
+ llvm::FoldingSetNodeID DestID;
+ Dest->Profile(DestID, Context, true);
+ if (DestID == SizeOfArgID) {
+ // TODO: For strncpy() and friends, this could suggest sizeof(dst)
+ // over sizeof(src) as well.
+ unsigned ActionIdx = 0; // Default is to suggest dereferencing.
+ StringRef ReadableName = FnName->getName();
+
+ if (const UnaryOperator *UnaryOp = dyn_cast<UnaryOperator>(Dest))
+ if (UnaryOp->getOpcode() == UO_AddrOf)
+ ActionIdx = 1; // If its an address-of operator, just remove it.
+ if (!PointeeTy->isIncompleteType() &&
+ (Context.getTypeSize(PointeeTy) == Context.getCharWidth()))
+ ActionIdx = 2; // If the pointee's size is sizeof(char),
+ // suggest an explicit length.
+
+ // If the function is defined as a builtin macro, do not show macro
+ // expansion.
+ SourceLocation SL = SizeOfArg->getExprLoc();
+ SourceRange DSR = Dest->getSourceRange();
+ SourceRange SSR = SizeOfArg->getSourceRange();
+ SourceManager &SM = getSourceManager();
+
+ if (SM.isMacroArgExpansion(SL)) {
+ ReadableName = Lexer::getImmediateMacroName(SL, SM, LangOpts);
+ SL = SM.getSpellingLoc(SL);
+ DSR = SourceRange(SM.getSpellingLoc(DSR.getBegin()),
+ SM.getSpellingLoc(DSR.getEnd()));
+ SSR = SourceRange(SM.getSpellingLoc(SSR.getBegin()),
+ SM.getSpellingLoc(SSR.getEnd()));
+ }
+
+ DiagRuntimeBehavior(SL, SizeOfArg,
+ PDiag(diag::warn_sizeof_pointer_expr_memaccess)
+ << ReadableName << PointeeTy << DestTy << DSR
+ << SSR);
+ DiagRuntimeBehavior(SL, SizeOfArg,
+ PDiag(diag::warn_sizeof_pointer_expr_memaccess_note)
+ << ActionIdx << SSR);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
// A little helper routine: ignore addition and subtraction of integer literals.
// This intentionally does not ignore all integer constant expressions because
// we don't want to remove sizeof().
@@ -10518,6 +10517,14 @@ void Sema::CheckStrlcpycatArguments(const CallExpr *Call,
OS.str());
}
+/// Check if two expressions refer to the same declaration.
+static bool referToTheSameDecl(const Expr *E1, const Expr *E2) {
+ if (const DeclRefExpr *D1 = dyn_cast_or_null<DeclRefExpr>(E1))
+ if (const DeclRefExpr *D2 = dyn_cast_or_null<DeclRefExpr>(E2))
+ return D1->getDecl() == D2->getDecl();
+ return false;
+}
+
static const Expr *getStrlenExprArg(const Expr *E) {
if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
const FunctionDecl *FD = CE->getDirectCallee();
diff --git a/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp b/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp
index bacb2c167af14..8b305f3ba2884 100644
--- a/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp
+++ b/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp
@@ -195,5 +195,5 @@ extern "C" void* malloc(unsigned size);
void check_prints(){
char* a = (char*) malloc(20);
const char* b = "Hello World";
- snprintf(a, sizeof(a), "%s", b); // expected-warning{{argument to 'sizeof' in 'snprintf' call is the same expression as the destination; did you mean to put an explicit length?}}
+ snprintf(a, sizeof(a), "%s", b); // expected-warning{{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}} expected-note {{did you mean to provide an explicit length?}}
}
>From 539db29f7e1db2bf29287d891842646b1fbd299d Mon Sep 17 00:00:00 2001
From: Bogdan Zunic <bogdan.zunic at htecgroup.com>
Date: Tue, 16 Dec 2025 06:18:14 -0800
Subject: [PATCH 3/7] Refactor CheckSizeOfExpression, added more tests and
warnings when explicit casts are used in the dest parameter
---
clang/lib/Sema/SemaChecking.cpp | 116 +++++++++---------
clang/test/SemaCXX/warn-memset-bad-sizeof.cpp | 101 ++++++++++++++-
2 files changed, 161 insertions(+), 56 deletions(-)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 51d9cf75cc424..40f83ad2f2749 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1451,6 +1451,8 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
DestinationSize = ComputeSizeArgument(0);
const Expr *LenArg = TheCall->getArg(1)->IgnoreParenImpCasts();
const Expr *Dest = TheCall->getArg(0)->IgnoreParenImpCasts();
+ if (const auto *DestCast = dyn_cast_or_null<CastExpr>(Dest))
+ Dest = DestCast->getSubExprAsWritten();
llvm::FoldingSetNodeID SizeOfArgID;
IdentifierInfo *FnInfo = FD->getIdentifier();
CheckSizeOfExpression(LenArg, Dest, SizeOfArgID, FnInfo);
@@ -10351,63 +10353,67 @@ bool Sema::CheckSizeOfExpression(const Expr *LenExpr, const Expr *Dest,
llvm::FoldingSetNodeID SizeOfArgID,
IdentifierInfo *FnName) {
const Expr *SizeOfArg = getSizeOfExprArg(LenExpr);
+ if (!SizeOfArg)
+ return false;
+ if (Diags.isIgnored(diag::warn_sizeof_pointer_expr_memaccess,
+ SizeOfArg->getExprLoc()))
+ return false;
QualType DestTy = Dest->getType();
- QualType PointeeTy;
- if (const PointerType *DestPtrTy = DestTy->getAs<PointerType>()) {
- PointeeTy = DestPtrTy->getPointeeType();
-
- // Catch "memset(p, 0, sizeof(p))" -- needs to be sizeof(*p). Do this by
- // actually comparing the expressions for equality. Because computing the
- // expression IDs can be expensive, we only do this if the diagnostic is
- // enabled.
- if (SizeOfArg && !Diags.isIgnored(diag::warn_sizeof_pointer_expr_memaccess,
- SizeOfArg->getExprLoc())) {
- // We only compute IDs for expressions if the warning is enabled, and
- // cache the sizeof arg's ID.
- if (SizeOfArgID == llvm::FoldingSetNodeID())
- SizeOfArg->Profile(SizeOfArgID, Context, true);
- llvm::FoldingSetNodeID DestID;
- Dest->Profile(DestID, Context, true);
- if (DestID == SizeOfArgID) {
- // TODO: For strncpy() and friends, this could suggest sizeof(dst)
- // over sizeof(src) as well.
- unsigned ActionIdx = 0; // Default is to suggest dereferencing.
- StringRef ReadableName = FnName->getName();
-
- if (const UnaryOperator *UnaryOp = dyn_cast<UnaryOperator>(Dest))
- if (UnaryOp->getOpcode() == UO_AddrOf)
- ActionIdx = 1; // If its an address-of operator, just remove it.
- if (!PointeeTy->isIncompleteType() &&
- (Context.getTypeSize(PointeeTy) == Context.getCharWidth()))
- ActionIdx = 2; // If the pointee's size is sizeof(char),
- // suggest an explicit length.
-
- // If the function is defined as a builtin macro, do not show macro
- // expansion.
- SourceLocation SL = SizeOfArg->getExprLoc();
- SourceRange DSR = Dest->getSourceRange();
- SourceRange SSR = SizeOfArg->getSourceRange();
- SourceManager &SM = getSourceManager();
-
- if (SM.isMacroArgExpansion(SL)) {
- ReadableName = Lexer::getImmediateMacroName(SL, SM, LangOpts);
- SL = SM.getSpellingLoc(SL);
- DSR = SourceRange(SM.getSpellingLoc(DSR.getBegin()),
- SM.getSpellingLoc(DSR.getEnd()));
- SSR = SourceRange(SM.getSpellingLoc(SSR.getBegin()),
- SM.getSpellingLoc(SSR.getEnd()));
- }
+ const PointerType *DestPtrTy = DestTy->getAs<PointerType>();
+ if (!DestPtrTy)
+ return false;
- DiagRuntimeBehavior(SL, SizeOfArg,
- PDiag(diag::warn_sizeof_pointer_expr_memaccess)
- << ReadableName << PointeeTy << DestTy << DSR
- << SSR);
- DiagRuntimeBehavior(SL, SizeOfArg,
- PDiag(diag::warn_sizeof_pointer_expr_memaccess_note)
- << ActionIdx << SSR);
- return true;
- }
- }
+ QualType PointeeTy = DestPtrTy->getPointeeType();
+
+ // Catch "memset(p, 0, sizeof(p))" -- needs to be sizeof(*p). Do this by
+ // actually comparing the expressions for equality. Because computing the
+ // expression IDs can be expensive, we only do this if the diagnostic is
+ // enabled.
+ // We only compute IDs for expressions if the warning is enabled, and
+ // cache the sizeof arg's ID.
+ if (SizeOfArgID == llvm::FoldingSetNodeID())
+ SizeOfArg->Profile(SizeOfArgID, Context, true);
+
+ llvm::FoldingSetNodeID DestID;
+ Dest->Profile(DestID, Context, true);
+ if (DestID == SizeOfArgID) {
+ // TODO: For strncpy() and friends, this could suggest sizeof(dst)
+ // over sizeof(src) as well.
+ unsigned ActionIdx = 0; // Default is to suggest dereferencing.
+ StringRef ReadableName = FnName->getName();
+
+ if (const UnaryOperator *UnaryOp = dyn_cast<UnaryOperator>(Dest))
+ if (UnaryOp->getOpcode() == UO_AddrOf)
+ ActionIdx = 1; // If its an address-of operator, just remove it.
+ if (!PointeeTy->isIncompleteType() &&
+ (Context.getTypeSize(PointeeTy) == Context.getCharWidth()))
+ ActionIdx = 2; // If the pointee's size is sizeof(char),
+ // suggest an explicit length.
+
+ // If the function is defined as a builtin macro, do not show macro
+ // expansion.
+ SourceLocation SL = SizeOfArg->getExprLoc();
+ SourceRange DSR = Dest->getSourceRange();
+ SourceRange SSR = SizeOfArg->getSourceRange();
+ SourceManager &SM = getSourceManager();
+
+ if (SM.isMacroArgExpansion(SL)) {
+ ReadableName = Lexer::getImmediateMacroName(SL, SM, LangOpts);
+ SL = SM.getSpellingLoc(SL);
+ DSR = SourceRange(SM.getSpellingLoc(DSR.getBegin()),
+ SM.getSpellingLoc(DSR.getEnd()));
+ SSR = SourceRange(SM.getSpellingLoc(SSR.getBegin()),
+ SM.getSpellingLoc(SSR.getEnd()));
+ }
+
+ DiagRuntimeBehavior(SL, SizeOfArg,
+ PDiag(diag::warn_sizeof_pointer_expr_memaccess)
+ << ReadableName << PointeeTy << DestTy << DSR
+ << SSR);
+ DiagRuntimeBehavior(SL, SizeOfArg,
+ PDiag(diag::warn_sizeof_pointer_expr_memaccess_note)
+ << ActionIdx << SSR);
+ return true;
}
return false;
}
diff --git a/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp b/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp
index 8b305f3ba2884..99e4b24bd63b1 100644
--- a/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp
+++ b/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp
@@ -190,10 +190,109 @@ void strcpy_and_friends() {
}
extern "C" int snprintf(char* buffer, __SIZE_TYPE__ buf_size, const char* format, ...);
+extern "C" int vsnprintf(char* buffer, __SIZE_TYPE__ buf_size, const char* format, __builtin_va_list arg);
extern "C" void* malloc(unsigned size);
+// This is a dependent context by none of the actual operations are dependent.
+template <class T> void fooNone() {
+ char* a = (char*) malloc(20);
+ const char* b = "Hello World";
+ snprintf(a, sizeof(a), "%s", b); // #fooNone_diagnostic
+ // expected-warning@#fooNone_diagnostic {{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#fooNone_diagnostic {{did you mean to provide an explicit length?}}
+}
+
+template <class T> void fooType() {
+ T a = (T) malloc(20); // #fooType_error
+ const char* b = "Hello World";
+ snprintf((char *)a, sizeof(a), "%s", b);// #fooType_diagnostic
+}
+
+template <class T> void fooTypePtr() {
+ T *a = (T *) malloc(20);
+ const char* b = "Hello World";
+ snprintf((char *)a, sizeof(a), "%s", b);// #fooTypePtr_diagnostic
+}
+
void check_prints(){
char* a = (char*) malloc(20);
const char* b = "Hello World";
- snprintf(a, sizeof(a), "%s", b); // expected-warning{{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}} expected-note {{did you mean to provide an explicit length?}}
+ snprintf(a, sizeof(a), "%s", b); // #CheckBasePrint
+ // expected-warning@#CheckBasePrint {{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#CheckBasePrint {{did you mean to provide an explicit length?}}
+
+ snprintf((char*)a, sizeof(a), "%s", b); // #CheckCastPrint
+ // expected-warning@#CheckCastPrint {{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#CheckCastPrint {{did you mean to provide an explicit length?}}
+
+ __builtin_va_list list;
+ vsnprintf(a, sizeof(a), "%s", list); // #VSNprintCheck
+ // expected-warning@#VSNprintCheck {{'vsnprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#VSNprintCheck {{did you mean to provide an explicit length?}}
+
+ vsnprintf((char*)a, sizeof(a), "%s", list); // #VSNprintCastCheck
+ // expected-warning@#VSNprintCastCheck {{'vsnprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#VSNprintCastCheck {{did you mean to provide an explicit length?}}
+
+ //No diagnostic output when dest is an array
+ char c[20];
+ const char* d = "Hello World";
+ snprintf(c, sizeof(c), "%s", d); // expected-none
+
+ //No diagnostic output when len is a exact number
+ char* e = (char*) malloc(20);
+ const char* f = "Hello World";
+ snprintf(e, 20, "%s", f); // expected-none
+
+ //Template tests
+ fooNone<int>(); // #fooNone_int_call
+ // expected-warning@#fooNone_diagnostic {{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#fooNone_diagnostic {{did you mean to provide an explicit length?}}
+ // expected-note@#fooNone_int_call {{in instantiation of function template specialization 'fooNone<int>' requested here}}
+
+ fooNone<char>();// #fooNone_char_call
+ // expected-warning@#fooNone_diagnostic {{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#fooNone_diagnostic {{did you mean to provide an explicit length?}}
+ // expected-note@#fooNone_char_call {{in instantiation of function template specialization 'fooNone<char>' requested here}}
+
+ fooNone<char*>();// #fooNone_charptr_call
+ // expected-warning@#fooNone_diagnostic {{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#fooNone_diagnostic {{did you mean to provide an explicit length?}}
+ // expected-note@#fooNone_charptr_call {{in instantiation of function template specialization 'fooNone<char *>' requested here}}
+
+
+ fooNone<void>();// #fooNone_void_call
+ // expected-warning@#fooNone_diagnostic {{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#fooNone_diagnostic {{did you mean to provide an explicit length?}}
+ // expected-note@#fooNone_void_call {{in instantiation of function template specialization 'fooNone<void>' requested here}}
+
+ fooType<char*>();// #fooType_charptr_call
+ // expected-warning@#fooType_diagnostic {{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#fooType_diagnostic {{did you mean to provide an explicit length?}}
+ // expected-note@#fooType_charptr_call {{in instantiation of function template specialization 'fooType<char *>' requested here}}
+
+ fooType<void*>();// #fooType_voidptr_call
+ // expected-warning@#fooType_diagnostic {{'snprintf' call operates on objects of type 'void' while the size is based on a different type 'void *'}}
+ // expected-note@#fooType_diagnostic {{did you mean to dereference the argument to 'sizeof' (and multiply it by the number of elements)?}}
+ // expected-note@#fooType_voidptr_call {{in instantiation of function template specialization 'fooType<void *>' requested here}}
+
+ fooType<char>(); // #fooType_char_call
+ // expected-error@#fooType_error {{cast from pointer to smaller type 'char' loses information}}
+ // expected-note@#fooType_char_call {{in instantiation of function template specialization 'fooType<char>' requested here}}
+
+ fooTypePtr<char>(); // #fooTypePtr_char_call
+ // expected-warning@#fooTypePtr_diagnostic {{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#fooTypePtr_diagnostic {{did you mean to provide an explicit length?}}
+ // expected-note@#fooTypePtr_char_call {{in instantiation of function template specialization 'fooTypePtr<char>' requested here}}
+
+ fooTypePtr<char*>(); // #fooTypePtr_charptr_call
+ // expected-warning@#fooTypePtr_diagnostic {{'snprintf' call operates on objects of type 'char *' while the size is based on a different type 'char **'}}
+ // expected-note@#fooTypePtr_diagnostic {{did you mean to dereference the argument to 'sizeof' (and multiply it by the number of elements)?}}
+ // expected-note@#fooTypePtr_charptr_call {{in instantiation of function template specialization 'fooTypePtr<char *>' requested here}}
+
+ fooTypePtr<void*>(); // #fooTypePtr_void_call
+ // expected-warning@#fooTypePtr_diagnostic {{'snprintf' call operates on objects of type 'void *' while the size is based on a different type 'void **'}}
+ // expected-note@#fooTypePtr_diagnostic {{did you mean to dereference the argument to 'sizeof' (and multiply it by the number of elements)?}}
+ // expected-note@#fooTypePtr_void_call {{in instantiation of function template specialization 'fooTypePtr<void *>' requested here}}
+
}
>From 4a534ca3923569aed7becff3f7647d3cfb8cf5fd Mon Sep 17 00:00:00 2001
From: Bogdan Zunic <bzunic at cisco.com>
Date: Thu, 8 Jan 2026 06:46:24 -0800
Subject: [PATCH 4/7] Changed function name CheckSizeOfExpression to
CheckSizeofMemaccessArgument
---
clang/include/clang/Sema/Sema.h | 6 +++---
clang/lib/Sema/SemaChecking.cpp | 10 +++++-----
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 69c921c8d2d35..66fe6e448aad2 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3043,9 +3043,9 @@ class Sema final : public SemaBase {
void CheckMemaccessArguments(const CallExpr *Call, unsigned BId,
IdentifierInfo *FnName);
- bool CheckSizeOfExpression(const Expr *SizeOfArg, const Expr *Dest,
- llvm::FoldingSetNodeID SizeOfArgID,
- IdentifierInfo *FnName);
+ bool CheckSizeofMemaccessArgument(const Expr *SizeOfArg, const Expr *Dest,
+ llvm::FoldingSetNodeID SizeOfArgID,
+ IdentifierInfo *FnName);
// Warn if the user has made the 'size' argument to strlcpy or strlcat
// be the size of the source, instead of the destination.
void CheckStrlcpycatArguments(const CallExpr *Call, IdentifierInfo *FnName);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 40f83ad2f2749..de5bed9285cec 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1455,7 +1455,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
Dest = DestCast->getSubExprAsWritten();
llvm::FoldingSetNodeID SizeOfArgID;
IdentifierInfo *FnInfo = FD->getIdentifier();
- CheckSizeOfExpression(LenArg, Dest, SizeOfArgID, FnInfo);
+ CheckSizeofMemaccessArgument(LenArg, Dest, SizeOfArgID, FnInfo);
}
}
@@ -10245,7 +10245,7 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
// actually comparing the expressions for equality. Because computing the
// expression IDs can be expensive, we only do this if the diagnostic is
// enabled.
- if (CheckSizeOfExpression(LenExpr, Dest, SizeOfArgID, FnName))
+ if (CheckSizeofMemaccessArgument(LenExpr, Dest, SizeOfArgID, FnName))
break;
// Also check for cases where the sizeof argument is the exact same
@@ -10349,9 +10349,9 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
}
}
-bool Sema::CheckSizeOfExpression(const Expr *LenExpr, const Expr *Dest,
- llvm::FoldingSetNodeID SizeOfArgID,
- IdentifierInfo *FnName) {
+bool Sema::CheckSizeofMemaccessArgument(const Expr *LenExpr, const Expr *Dest,
+ llvm::FoldingSetNodeID SizeOfArgID,
+ IdentifierInfo *FnName) {
const Expr *SizeOfArg = getSizeOfExprArg(LenExpr);
if (!SizeOfArg)
return false;
>From 6dd15b43f63f860a4cd5bc3f7fc986b20ab08a39 Mon Sep 17 00:00:00 2001
From: Bogdan Zunic <bogdan.zunic at htecgroup.com>
Date: Tue, 13 Jan 2026 05:52:58 -0800
Subject: [PATCH 5/7] Removed unused vars and outdated comments
---
clang/include/clang/Sema/Sema.h | 1 -
clang/lib/Sema/SemaChecking.cpp | 17 +++++------------
2 files changed, 5 insertions(+), 13 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 66fe6e448aad2..3fb97a212fd77 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3044,7 +3044,6 @@ class Sema final : public SemaBase {
IdentifierInfo *FnName);
bool CheckSizeofMemaccessArgument(const Expr *SizeOfArg, const Expr *Dest,
- llvm::FoldingSetNodeID SizeOfArgID,
IdentifierInfo *FnName);
// Warn if the user has made the 'size' argument to strlcpy or strlcat
// be the size of the source, instead of the destination.
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index de5bed9285cec..58e2313a906b9 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1453,9 +1453,8 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
const Expr *Dest = TheCall->getArg(0)->IgnoreParenImpCasts();
if (const auto *DestCast = dyn_cast_or_null<CastExpr>(Dest))
Dest = DestCast->getSubExprAsWritten();
- llvm::FoldingSetNodeID SizeOfArgID;
IdentifierInfo *FnInfo = FD->getIdentifier();
- CheckSizeofMemaccessArgument(LenArg, Dest, SizeOfArgID, FnInfo);
+ CheckSizeofMemaccessArgument(LenArg, Dest, FnInfo);
}
}
@@ -10217,8 +10216,6 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
// We have special checking when the length is a sizeof expression.
QualType SizeOfArgTy = getSizeOfArgType(LenExpr);
- const Expr *SizeOfArg = getSizeOfExprArg(LenExpr);
- llvm::FoldingSetNodeID SizeOfArgID;
// Although widely used, 'bzero' is not a standard function. Be more strict
// with the argument types before allowing diagnostics and only allow the
@@ -10245,7 +10242,7 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
// actually comparing the expressions for equality. Because computing the
// expression IDs can be expensive, we only do this if the diagnostic is
// enabled.
- if (CheckSizeofMemaccessArgument(LenExpr, Dest, SizeOfArgID, FnName))
+ if (CheckSizeofMemaccessArgument(LenExpr, Dest, FnName))
break;
// Also check for cases where the sizeof argument is the exact same
@@ -10350,11 +10347,13 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call,
}
bool Sema::CheckSizeofMemaccessArgument(const Expr *LenExpr, const Expr *Dest,
- llvm::FoldingSetNodeID SizeOfArgID,
IdentifierInfo *FnName) {
+ llvm::FoldingSetNodeID SizeOfArgID;
const Expr *SizeOfArg = getSizeOfExprArg(LenExpr);
if (!SizeOfArg)
return false;
+ // Computing this warning is expensive, so we only do so if the warning is
+ // enabled.
if (Diags.isIgnored(diag::warn_sizeof_pointer_expr_memaccess,
SizeOfArg->getExprLoc()))
return false;
@@ -10365,12 +10364,6 @@ bool Sema::CheckSizeofMemaccessArgument(const Expr *LenExpr, const Expr *Dest,
QualType PointeeTy = DestPtrTy->getPointeeType();
- // Catch "memset(p, 0, sizeof(p))" -- needs to be sizeof(*p). Do this by
- // actually comparing the expressions for equality. Because computing the
- // expression IDs can be expensive, we only do this if the diagnostic is
- // enabled.
- // We only compute IDs for expressions if the warning is enabled, and
- // cache the sizeof arg's ID.
if (SizeOfArgID == llvm::FoldingSetNodeID())
SizeOfArg->Profile(SizeOfArgID, Context, true);
>From 63323155dc89734b9f5d006cfce300086531cf66 Mon Sep 17 00:00:00 2001
From: Bogdan Zunic <bogdan.zunic at htecgroup.com>
Date: Wed, 21 Jan 2026 04:32:54 -0800
Subject: [PATCH 6/7] Replaced IgnoreParenImpCasts with IgnoreCasts
---
clang/lib/Sema/SemaChecking.cpp | 6 ++----
clang/test/SemaCXX/warn-memset-bad-sizeof.cpp | 8 ++++++++
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 58e2313a906b9..50a1e44af7a7a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1449,10 +1449,8 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
}
}
DestinationSize = ComputeSizeArgument(0);
- const Expr *LenArg = TheCall->getArg(1)->IgnoreParenImpCasts();
- const Expr *Dest = TheCall->getArg(0)->IgnoreParenImpCasts();
- if (const auto *DestCast = dyn_cast_or_null<CastExpr>(Dest))
- Dest = DestCast->getSubExprAsWritten();
+ const Expr *LenArg = TheCall->getArg(1)->IgnoreCasts();
+ const Expr *Dest = TheCall->getArg(0)->IgnoreCasts();
IdentifierInfo *FnInfo = FD->getIdentifier();
CheckSizeofMemaccessArgument(LenArg, Dest, FnInfo);
}
diff --git a/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp b/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp
index 99e4b24bd63b1..6f1cd4dd639ec 100644
--- a/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp
+++ b/clang/test/SemaCXX/warn-memset-bad-sizeof.cpp
@@ -225,6 +225,10 @@ void check_prints(){
// expected-warning@#CheckCastPrint {{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
// expected-note@#CheckCastPrint {{did you mean to provide an explicit length?}}
+ snprintf((char*)(char*)a, sizeof(a), "%s", b); // #CheckDoubleCastPrint
+ // expected-warning@#CheckDoubleCastPrint {{'snprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#CheckDoubleCastPrint {{did you mean to provide an explicit length?}}
+
__builtin_va_list list;
vsnprintf(a, sizeof(a), "%s", list); // #VSNprintCheck
// expected-warning@#VSNprintCheck {{'vsnprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
@@ -234,6 +238,10 @@ void check_prints(){
// expected-warning@#VSNprintCastCheck {{'vsnprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
// expected-note@#VSNprintCastCheck {{did you mean to provide an explicit length?}}
+ vsnprintf((char*)(char*)a, sizeof(a), "%s", list); // #VSNprintDoubleCastCheck
+ // expected-warning@#VSNprintDoubleCastCheck {{'vsnprintf' call operates on objects of type 'char' while the size is based on a different type 'char *'}}
+ // expected-note@#VSNprintDoubleCastCheck {{did you mean to provide an explicit length?}}
+
//No diagnostic output when dest is an array
char c[20];
const char* d = "Hello World";
>From 95dccb76bfdf77f0505e33f23ffdadc7584d15fc Mon Sep 17 00:00:00 2001
From: Bogdan Zunic <bzunic at cisco.com>
Date: Fri, 6 Feb 2026 04:01:10 -0800
Subject: [PATCH 7/7] Fixup code style
---
clang/lib/Sema/SemaChecking.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 50a1e44af7a7a..20d842b420b8f 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -10373,9 +10373,9 @@ bool Sema::CheckSizeofMemaccessArgument(const Expr *LenExpr, const Expr *Dest,
unsigned ActionIdx = 0; // Default is to suggest dereferencing.
StringRef ReadableName = FnName->getName();
- if (const UnaryOperator *UnaryOp = dyn_cast<UnaryOperator>(Dest))
- if (UnaryOp->getOpcode() == UO_AddrOf)
- ActionIdx = 1; // If its an address-of operator, just remove it.
+ if (const UnaryOperator *UnaryOp = dyn_cast<UnaryOperator>(Dest);
+ UnaryOp && UnaryOp->getOpcode() == UO_AddrOf)
+ ActionIdx = 1; // If its an address-of operator, just remove it.
if (!PointeeTy->isIncompleteType() &&
(Context.getTypeSize(PointeeTy) == Context.getCharWidth()))
ActionIdx = 2; // If the pointee's size is sizeof(char),
More information about the cfe-commits
mailing list