Skip to content
36 changes: 35 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,21 @@ 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),
doStmt(InnerMatcher)));
};

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

if (WarnOnSizeOfInLoopTermination) {
Finder->addMatcher(LoopExpr(hasDescendant(binaryOperator(
allOf(has(SizeOfExpr.bind("sizeof-expr")),
isComparisonOperator()))))
.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 +371,22 @@ 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();

auto Loc = 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(Loc->getBeginLoc(), "suspicious usage of 'sizeof' in the loop")
<< Loc->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
7 changes: 7 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ 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. The check detects the use of ``sizeof``
opertaor to determine the number of elements in the array, where the
array size is higher than the number of elements in the array.

- 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,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]]: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++) {}

// 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++) {}

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
Loading