Skip to content

[clang-tidy] Warn about misuse of sizeof operator in loops. #143205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jun 25, 2025
38 changes: 37 additions & 1 deletion clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ SizeofExpressionCheck::SizeofExpressionCheck(StringRef Name,
Options.get("WarnOnSizeOfPointerToAggregate", true)),
WarnOnSizeOfPointer(Options.get("WarnOnSizeOfPointer", false)),
WarnOnOffsetDividedBySizeOf(
Options.get("WarnOnOffsetDividedBySizeOf", true)) {}
Options.get("WarnOnOffsetDividedBySizeOf", true)),
WarnOnSizeOfInLoopTermination(
Options.get("WarnOnSizeOfInLoopTermination", true)) {}

void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "WarnOnSizeOfConstant", WarnOnSizeOfConstant);
Expand All @@ -86,13 +88,22 @@ void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "WarnOnSizeOfPointer", WarnOnSizeOfPointer);
Options.store(Opts, "WarnOnOffsetDividedBySizeOf",
WarnOnOffsetDividedBySizeOf);
Options.store(Opts, "WarnOnSizeOfInLoopTermination",
WarnOnSizeOfInLoopTermination);
}

void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
// FIXME:
// Some of the checks should not match in template code to avoid false
// positives if sizeof is applied on template argument.

auto LoopCondExpr =
[](const ast_matchers::internal::Matcher<Stmt> &InnerMatcher) {
return stmt(anyOf(forStmt(hasCondition(InnerMatcher)),
whileStmt(hasCondition(InnerMatcher)),
doStmt(hasCondition(InnerMatcher))));
};

const auto IntegerExpr = ignoringParenImpCasts(integerLiteral());
const auto ConstantExpr = ignoringParenImpCasts(
anyOf(integerLiteral(), unaryOperator(hasUnaryOperand(IntegerExpr)),
Expand Down Expand Up @@ -130,6 +141,14 @@ void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
this);
}

if (WarnOnSizeOfInLoopTermination) {
auto CondExpr = binaryOperator(
allOf(has(SizeOfExpr.bind("sizeof-expr")), isComparisonOperator()));
Finder->addMatcher(LoopCondExpr(anyOf(CondExpr, hasDescendant(CondExpr)))
.bind("loop-expr"),
this);
}

// Detect sizeof(kPtr) where kPtr is 'const char* kPtr = "abc"';
const auto CharPtrType = pointerType(pointee(isAnyCharacter()));
const auto ConstStrLiteralDecl =
Expand Down Expand Up @@ -353,6 +372,23 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
diag(E->getBeginLoc(),
"suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?")
<< E->getSourceRange();
} else if (const auto *E = Result.Nodes.getNodeAs<Stmt>("loop-expr")) {
auto *SizeofArgTy = Result.Nodes.getNodeAs<Type>("sizeof-arg-type");
if (const auto member = dyn_cast<MemberPointerType>(SizeofArgTy))
SizeofArgTy = member->getPointeeType().getTypePtr();

const auto *SzOfExpr = Result.Nodes.getNodeAs<Expr>("sizeof-expr");

if (const auto type = dyn_cast<ArrayType>(SizeofArgTy)) {
// check if the array element size is larger than one. If true,
// the size of the array is higher than the number of elements
CharUnits sSize = Ctx.getTypeSizeInChars(type->getElementType());
if (!sSize.isOne()) {
diag(SzOfExpr->getBeginLoc(),
"suspicious usage of 'sizeof' in the loop")
<< SzOfExpr->getSourceRange();
}
}
} else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-pointer")) {
if (Result.Nodes.getNodeAs<Type>("struct-type")) {
diag(E->getBeginLoc(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class SizeofExpressionCheck : public ClangTidyCheck {
const bool WarnOnSizeOfPointerToAggregate;
const bool WarnOnSizeOfPointer;
const bool WarnOnOffsetDividedBySizeOf;
const bool WarnOnSizeOfInLoopTermination;
};

} // namespace clang::tidy::bugprone
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ Changes in existing checks
<clang-tidy/checks/bugprone/optional-value-conversion>` check to detect
conversion in argument of ``std::make_optional``.

- Improved :doc: `bugprone-sizeof-expression
<clang-tidy/checks/bugprone/bugprone-sizeof-expression>` check by adding
`WarnOnSizeOfInLoopTermination` option to detect misuses of ``sizeof``
expression in loop conditions.

- Improved :doc:`bugprone-string-constructor
<clang-tidy/checks/bugprone/string-constructor>` check to find suspicious
calls of ``std::string`` constructor with char pointer, start position and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,12 @@ Options
When `true`, the check will warn on pointer arithmetic where the
element count is obtained from a division with ``sizeof(...)``,
e.g., ``Ptr + Bytes / sizeof(*T)``. Default is `true`.

.. option:: WarnOnSizeOfInLoopTermination

When `true`, the check will warn about incorrect use of sizeof expression
in loop termination condition. The warning triggers if the ``sizeof``
expression appears to be incorrectly used to determine the number of
array/buffer elements.
e.g, ``long arr[10]; for(int i = 0; i < sizeof(arr); i++) { ... }``. Default
is `true`.
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,69 @@ int Test2(MyConstChar* A) {
return sum;
}

struct A {
int array[10];
};

struct B {
struct A a;
};

void loop_access_elements(int num, struct B b) {
struct A arr[10];
char buf[20];

// CHECK-MESSAGES: :[[@LINE+1]]:22: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
for(int i = 0; i < sizeof(arr); i++) {
struct A a = arr[i];
}

// Loop warning should not trigger here, even though this code is incorrect
// CHECK-MESSAGES: :[[@LINE+2]]:22: warning: suspicious usage of 'sizeof(K)'; did you mean 'K'? [bugprone-sizeof-expression]
// CHECK-MESSAGES: :[[@LINE+1]]:32: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator [bugprone-sizeof-expression]
for(int i = 0; i < sizeof(10)/sizeof(A); i++) {
struct A a = arr[i];
}

// Should not warn here
for(int i = 0; i < sizeof(arr)/sizeof(A); i++) {}

// Should not warn here
for (int i = 0; i < 10; i++) {
if (sizeof(arr) != 0) {

}
}

for (int i = 0; i < 10; i++) {
// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
for (int j = 0; j < sizeof(arr); j++) {
}
}

// CHECK-MESSAGES: :[[@LINE+1]]:22: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
for(int j = 0; j < sizeof(b.a.array); j++) {}

// Should not warn here
for(int i = 0; i < sizeof(buf); i++) {}

// Should not warn here
for(int i = 0; i < (sizeof(arr) << 3); i++) {}

int i = 0;
// CHECK-MESSAGES: :[[@LINE+1]]:14: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
while(i <= sizeof(arr)) {i++;}

i = 0;
do {
i++;
// CHECK-MESSAGES: :[[@LINE+1]]:16: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
} while(i <= sizeof(arr));

// CHECK-MESSAGES: :[[@LINE+1]]:29: warning: suspicious usage of 'sizeof' in the loop [bugprone-sizeof-expression]
for(int i = 0, j = 0; i < sizeof(arr) && j < sizeof(buf); i++, j++) {}
}

template <int T>
int Foo() { int A[T]; return sizeof(T); }
// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of 'sizeof(K)'
Expand Down