// RUN: %check_clang_tidy %s bugprone-too-small-loop-variable %t -- -- --target=x86_64-linux long size() { return 294967296l; } //////////////////////////////////////////////////////////////////////////////// /// Test cases correctly caught by bugprone-too-small-loop-variable. void voidBadForLoop() { for (int i = 0; i < size(); ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] } } void voidBadForLoop2() { for (int i = 0; i < size() + 10; ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] } } void voidBadForLoop3() { for (int i = 0; i <= size() - 1; ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] } } void voidBadForLoop4() { for (int i = 0; size() > i; ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] } } void voidBadForLoop5() { for (int i = 0; size() - 1 >= i; ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] } } void voidBadForLoop6() { int i = 0; for (; i < size(); ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: loop variable has narrower type 'int' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] } } void voidForLoopUnsignedBound() { unsigned size = 3147483647; for (int i = 0; i < size; ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: loop variable has narrower type 'int' than iteration's upper bound 'unsigned int' [bugprone-too-small-loop-variable] } } // The iteration's upper bound has a template dependent value. template void doSomething() { for (short i = 0; i < size; ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] } } // The iteration's upper bound has a template dependent type. template void doSomething() { for (T i = 0; i < size(); ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: loop variable has narrower type 'short' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] } } void voidForLoopInstantiation() { // This line does not trigger the warning. doSomething(); // This one triggers the warning. doSomething(); } // A suspicious function used in a macro. #define SUSPICIOUS_SIZE (size()) void voidBadForLoopWithMacroBound() { for (short i = 0; i < SUSPICIOUS_SIZE; ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'long' [bugprone-too-small-loop-variable] } } //////////////////////////////////////////////////////////////////////////////// /// Correct loops: we should not warn here. // A simple use case when both expressions have the same type. void voidGoodForLoop() { for (long i = 0; i < size(); ++i) { // no warning } } // Other use case where both expressions have the same type, // but short expressions are converted to int by the compare operator. void voidGoodForLoop2() { short loopCond = 10; for (short i = 0; i < loopCond; ++i) { // no warning } } // Because of the integer literal, the iteration's upper bound is int, but we suppress the warning here. void voidForLoopShortPlusLiteral() { short size = 30000; for (short i = 0; i <= (size - 1); ++i) { // no warning } } // Addition of two short variables results in an int value, but we suppress this to avoid false positives. void voidForLoopShortPlusShort() { short size = 256; short increment = 14; for (short i = 0; i < size + increment; ++i) { // no warning } } // In this test case we have different integer types, but here the loop variable has the bigger type. // The iteration's bound is cast implicitly, not the loop variable. void voidForLoopBoundImplicitCast() { short start = 256; short end = 14; for (int i = start; i >= end; --i) { // no warning } } // Range based loop and other iterator based loops are ignored by this check. void voidRangeBasedForLoop() { int array[] = {1, 2, 3, 4, 5}; for (const int &i : array) { // no warning } } //////////////////////////////////////////////////////////////////////////////// /// Future possibilites to improve the check. // False positive: because of the int literal, iteration's upper bound has int type. void voidForLoopFalsePositive() { short size = 30000; bool cond = false; for (short i = 0; i < (cond ? 0 : size); ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'int' [bugprone-too-small-loop-variable] } } void voidForLoopFalsePositive2() { short size = 30000; bool cond = false; for (short i = 0; i < (!cond ? size : 0); ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'int' [bugprone-too-small-loop-variable] } } // False positive: The loop bound expression contains nested binary operators. void voidForLoopFalsePositive3() { short number = 30000; for (short i = 0; i < ((number & 0x7f) + 1); ++i) { // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: loop variable has narrower type 'short' than iteration's upper bound 'int' [bugprone-too-small-loop-variable] } } // TODO: handle while loop. void voidBadWhileLoop() { short i = 0; while (i < size()) { // missing warning ++i; } } // TODO: handle do-while loop. void voidBadDoWhileLoop() { short i = 0; do { ++i; } while (i < size()); // missing warning } // TODO: handle complex loop conditions. void voidComplexForCond() { bool additionalCond = true; for (int i = 0; i < size() && additionalCond; ++i) { // missing warning } } //////////////////////////////////////////////////////////////////////////////// /// Suspicious test cases ingored by this check. // Test case with a reverse iteration. // This is caught by -Wimplicit-int-conversion. void voidReverseForLoop() { for (short i = size() - 1; i >= 0; --i) { // no warning } } // Macro defined literals are used inside the loop condition. #define SIZE 125 #define SIZE2 (SIZE + 1) void voidForLoopWithMacroBound() { for (short i = 0; i < SIZE2; ++i) { // no warning } } // A suspicious loop is not caught if the iteration's upper bound is a literal. void voidForLoopWithLiteralBound() { for (short i = 0; i < 125; ++i) { // no warning } } // The used literal leads to an infinite loop. // This is caught by -Wtautological-constant-out-of-range-compare. void voidForLoopWithBigLiteralBound() { for (short i = 0; i < 294967296l; ++i) { // no warning } } enum eSizeType { START, Y, END }; // A suspicious loop is not caught if the iteration's upper bound is an enum value. void voidForLoopWithEnumBound() { for (short i = eSizeType::START; i < eSizeType::END; ++i) { // no warning } } enum eSizeType2 : long { START2 = 294967296l, Y2, END2 }; // The used enum value leads to an infinite loop. // This is caught by -Wtautological-constant-out-of-range-compare. void voidForLoopWithBigEnumBound() { for (short i = eSizeType2::START2; i < eSizeType2::END2; ++i) { // no warning } } // A suspicious loop is not caught if the iteration's upper bound is a constant variable. void voidForLoopWithConstBound() { const long size = 252l; for (short i = 0; i < size; ++i) { // no warning } } // The used constant variable leads to an infinite loop. // This is caught by -Wtautological-constant-out-of-range-compare. void voidForLoopWithBigConstBound() { const long size = 294967296l; for (short i = 0; i < size; ++i) { // no warning } }