Skip to content
29 changes: 28 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,20 @@ 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 LoopExpr =
[](const ast_matchers::internal::Matcher<Stmt> &InnerMatcher) {
return stmt(anyOf(forStmt(InnerMatcher), whileStmt(InnerMatcher)));
};

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

if (WarnOnSizeOfInLoopTermination) {
Finder->addMatcher(
LoopExpr(has(binaryOperator(has(SizeOfExpr)))).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 +367,19 @@ 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();
}

if (const auto type = dyn_cast<ArrayType>(SizeofArgTy)) {
CharUnits sSize = Ctx.getTypeSizeInChars(type->getElementType());
if (!sSize.isOne()) {
diag(E->getBeginLoc(), "suspicious usage of 'sizeof' in the loop")
<< E->getSourceRange();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this line? I think this argument is never printed because we don't have %0 is message

Copy link
Contributor Author

@malavikasamak malavikasamak Jun 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check here is ensuring the size of the array is not equal to the number of elements in the array. If they are equal, using the sizeof operator in the condition of the loop would be acceptable and unlikely to cause an out of bound access. I will add a comment explaining this.

}
}
} 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
6 changes: 6 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ Changes in existing checks
tolerating fix-it breaking compilation when functions is used as pointers
to avoid matching usage of functions within the current compilation unit.

- Improved :doc: `bugprone-sizeof-expression
<clang-tidy/checks/bugprone/bugprone-sizeof-expression>` check.

Introduced WarnOnSizeOfInLoopTermination sub-class to detect misuses of sizeof expression
in loop conditions.

Removed checks
^^^^^^^^^^^^^^

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,11 @@ 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,53 @@ 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]]:5: 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]]:24: warning: suspicious usage of 'sizeof(K)'; did you mean 'K'? [bugprone-sizeof-expression]
// CHECK-MESSAGES: :[[@LINE+1]]:34: 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++) {}

// CHECK-MESSAGES: :[[@LINE+1]]:5: 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++) {}

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

i = 0;
do {
i++;
} while(i <= sizeof(arr));
}

// Add cases for while loop

// Add cases for do-while loop

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