916 lines · cpp
1// RUN: %check_clang_tidy %s bugprone-infinite-loop %t \2// RUN: -- -- -fexceptions -fblocks -fno-delayed-template-parsing3 4void simple_infinite_loop1() {5 int i = 0;6 int j = 0;7 while (i < 10) {8 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]9 j++;10 }11 12 while (int k = 10) {13 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; it does not check any variables in the condition [bugprone-infinite-loop]14 j--;15 }16 17 while (int k = 10) {18 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; it does not check any variables in the condition [bugprone-infinite-loop]19 k--;20 }21 22 do {23 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]24 j++;25 } while (i < 10);26 27 for (i = 0; i < 10; ++j) {28 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]29 }30}31 32void simple_infinite_loop2() {33 int i = 0;34 int j = 0;35 int Limit = 10;36 while (i < Limit) {37 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, Limit) are updated in the loop body [bugprone-infinite-loop]38 j++;39 }40 41 while (int k = Limit) {42 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (Limit) are updated in the loop body [bugprone-infinite-loop]43 j--;44 }45 46 while (int k = Limit) {47 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (Limit) are updated in the loop body [bugprone-infinite-loop]48 k--;49 }50 51 do {52 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, Limit) are updated in the loop body [bugprone-infinite-loop]53 j++;54 } while (i < Limit);55 56 for (i = 0; i < Limit; ++j) {57 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, Limit) are updated in the loop body [bugprone-infinite-loop]58 }59}60 61void simple_not_infinite1() {62 int i = 0;63 int Limit = 100;64 while (i < Limit) {65 // Not an error since 'Limit' is updated.66 Limit--;67 }68 69 while (Limit--) {70 // Not an error since 'Limit' is updated.71 i++;72 }73 74 while ((Limit)--) {75 // Not an error since 'Limit' is updated.76 i++;77 }78 79 while ((Limit) -= 1) {80 // Not an error since 'Limit' is updated.81 }82 83 while (int k = Limit) {84 // Not an error since 'Limit' is updated.85 Limit--;86 }87 88 while (int k = Limit) {89 // Not an error since 'Limit' is updated90 (Limit)--;91 }92 93 while (int k = Limit--) {94 // Not an error since 'Limit' is updated.95 i++;96 }97 98 do {99 Limit--;100 } while (i < Limit);101 102 for (i = 0; i < Limit; Limit--) {103 }104 105 for (i = 0; i < Limit; (Limit) = Limit - 1) {106 }107 108 for (i = 0; i < Limit; (Limit) -= 1) {109 }110 111 for (i = 0; i < Limit; --(Limit)) {112 }113}114 115void simple_not_infinite2() {116 for (int i = 10; i-- > 0;) {117 // Not an error, since loop variable is modified in its condition part.118 }119}120 121int unknown_function();122 123void function_call() {124 int i = 0;125 while (i < unknown_function()) {126 // Not an error, since the function may return different values.127 }128 129 do {130 // Not an error, since the function may return different values.131 } while (i < unknown_function());132 133 for (i = 0; i < unknown_function();) {134 // Not an error, since the function may return different values.135 }136}137 138void escape_before1() {139 int i = 0;140 int Limit = 100;141 int *p = &i;142 while (i < Limit) {143 // Not an error, since *p is alias of i.144 (*p)++;145 }146 147 do {148 (*p)++;149 } while (i < Limit);150 151 for (i = 0; i < Limit; ++(*p)) {152 }153}154 155void escape_before2() {156 int i = 0;157 int Limit = 100;158 int &ii = i;159 while (i < Limit) {160 // Not an error, since ii is alias of i.161 ii++;162 }163 164 do {165 ii++;166 } while (i < Limit);167 168 for (i = 0; i < Limit; ++ii) {169 }170}171 172void escape_inside1() {173 int i = 0;174 int Limit = 100;175 int *p = &i;176 while (i < Limit) {177 // Not an error, since *p is alias of i.178 int *p = &i;179 (*p)++;180 }181 182 do {183 int *p = &i;184 (*p)++;185 } while (i < Limit);186}187 188void escape_inside2() {189 int i = 0;190 int Limit = 100;191 while (i < Limit) {192 // Not an error, since ii is alias of i.193 int &ii = i;194 ii++;195 }196 197 do {198 int &ii = i;199 ii++;200 } while (i < Limit);201}202 203void escape_after1() {204 int i = 0;205 int j = 0;206 int Limit = 10;207 208 while (i < Limit) {209 // False negative, but difficult to detect without CFG-based analysis210 }211 int *p = &i;212}213 214void escape_after2() {215 int i = 0;216 int j = 0;217 int Limit = 10;218 219 while (i < Limit) {220 // False negative, but difficult to detect without CFG-based analysis221 }222 int &ii = i;223}224 225int glob;226 227void global1(int &x) {228 int i = 0, Limit = 100;229 while (x < Limit) {230 // Not an error since 'x' can be an alias of 'glob'.231 glob++;232 }233}234 235void global2() {236 int i = 0, Limit = 100;237 while (glob < Limit) {238 // Since 'glob' is declared out of the function we do not warn.239 i++;240 }241}242 243struct X {244 int m;245 246 void change_m();247 248 void member_expr1(int i) {249 while (i < m) {250 // False negative: No warning, since skipping the case where a struct or251 // class can be found in its condition.252 ;253 }254 }255 256 void member_expr2(int i) {257 while (i < m) {258 --m;259 }260 }261 262 void member_expr3(int i) {263 while (i < m) {264 change_m();265 }266 }267};268 269void array_index() {270 int i = 0;271 int v[10];272 while (i < 10) {273 v[i++] = 0;274 }275 276 i = 0;277 do {278 v[i++] = 0;279 } while (i < 9);280 281 for (i = 0; i < 10;) {282 v[i++] = 0;283 }284 285 for (i = 0; i < 10; v[i++] = 0) {286 }287}288 289void no_loop_variable() {290 while (0)291 ;292}293 294void volatile_in_condition() {295 volatile int cond = 0;296 while (!cond) {297 }298}299 300namespace std {301template<typename T> class atomic {302 T val;303public:304 atomic(T v): val(v) {};305 operator T() { return val; };306};307}308 309void atomic_in_condition() {310 std::atomic<int> cond = 0;311 while (!cond) {312 }313}314 315void loop_exit1() {316 int i = 0;317 while (i) {318 if (unknown_function())319 break;320 }321}322 323void loop_exit2() {324 int i = 0;325 while (i) {326 if (unknown_function())327 return;328 }329}330 331void loop_exit3() {332 int i = 0;333 while (i) {334 if (unknown_function())335 goto end;336 }337 end:338 ;339}340 341void loop_exit4() {342 int i = 0;343 while (i) {344 if (unknown_function())345 throw 1;346 }347}348 349[[noreturn]] void exit(int);350 351void loop_exit5() {352 int i = 0;353 while (i) {354 if (unknown_function())355 exit(1);356 }357}358 359void loop_exit_in_lambda() {360 int i = 0;361 while (i) {362 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]363 auto l = []() { return 0; };364 }365}366 367void lambda_capture() {368 int i = 0;369 int Limit = 100;370 int *p = &i;371 while (i < Limit) {372 // Not an error, since i is captured by reference in a lambda.373 auto l = [&i]() { ++i; };374 }375 376 do {377 int *p = &i;378 (*p)++;379 } while (i < Limit);380}381 382template <typename T> void accept_callback(T t) {383 // Potentially call the callback.384 // Possibly on a background thread or something.385}386 387void accept_block(void (^)(void)) {388 // Potentially call the callback.389 // Possibly on a background thread or something.390}391 392void wait(void) {393 // Wait for the previously passed callback to be called.394}395 396void lambda_capture_from_outside() {397 bool finished = false;398 accept_callback([&]() {399 finished = true;400 });401 while (!finished) {402 wait();403 }404}405 406void lambda_capture_from_outside_by_value() {407 bool finished = false;408 accept_callback([finished]() {409 if (finished) {}410 });411 while (!finished) {412 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (finished) are updated in the loop body [bugprone-infinite-loop]413 wait();414 }415}416 417void lambda_capture_from_outside_but_unchanged() {418 bool finished = false;419 accept_callback([&finished]() {420 if (finished) {}421 });422 while (!finished) {423 // FIXME: Should warn.424 wait();425 }426}427 428void block_capture_from_outside() {429 __block bool finished = false;430 accept_block(^{431 finished = true;432 });433 while (!finished) {434 wait();435 }436}437 438void block_capture_from_outside_by_value() {439 bool finished = false;440 accept_block(^{441 if (finished) {}442 });443 while (!finished) {444 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (finished) are updated in the loop body [bugprone-infinite-loop]445 wait();446 }447}448 449void block_capture_from_outside_but_unchanged() {450 __block bool finished = false;451 accept_block(^{452 if (finished) {}453 });454 while (!finished) {455 // FIXME: Should warn.456 wait();457 }458}459 460void finish_at_any_time(bool *finished);461 462void lambda_capture_with_loop_inside_lambda_bad() {463 bool finished = false;464 auto lambda = [=]() {465 while (!finished) {466 // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: this loop is infinite; none of its condition variables (finished) are updated in the loop body [bugprone-infinite-loop]467 wait();468 }469 };470 finish_at_any_time(&finished);471 lambda();472}473 474void lambda_capture_with_loop_inside_lambda_bad_init_capture() {475 bool finished = false;476 auto lambda = [captured_finished=finished]() {477 while (!captured_finished) {478 // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: this loop is infinite; none of its condition variables (captured_finished) are updated in the loop body [bugprone-infinite-loop]479 wait();480 }481 };482 finish_at_any_time(&finished);483 lambda();484}485 486void lambda_capture_with_loop_inside_lambda_good() {487 bool finished = false;488 auto lambda = [&]() {489 while (!finished) {490 wait(); // No warning: the variable may be updated491 // from outside the lambda.492 }493 };494 finish_at_any_time(&finished);495 lambda();496}497 498void lambda_capture_with_loop_inside_lambda_good_init_capture() {499 bool finished = false;500 auto lambda = [&captured_finished=finished]() {501 while (!captured_finished) {502 wait(); // No warning: the variable may be updated503 // from outside the lambda.504 }505 };506 finish_at_any_time(&finished);507 lambda();508}509 510void block_capture_with_loop_inside_block_bad() {511 bool finished = false;512 auto block = ^() {513 while (!finished) {514 // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: this loop is infinite; none of its condition variables (finished) are updated in the loop body [bugprone-infinite-loop]515 wait();516 }517 };518 finish_at_any_time(&finished);519 block();520}521 522void block_capture_with_loop_inside_block_bad_simpler() {523 bool finished = false;524 auto block = ^() {525 while (!finished) {526 // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: this loop is infinite; none of its condition variables (finished) are updated in the loop body [bugprone-infinite-loop]527 wait();528 }529 };530 block();531}532 533void block_capture_with_loop_inside_block_good() {534 __block bool finished = false;535 auto block = ^() {536 while (!finished) {537 wait(); // No warning: the variable may be updated538 // from outside the block.539 }540 };541 finish_at_any_time(&finished);542 block();543}544 545void evaluatable(bool CondVar) {546 for (; false && CondVar;) {547 }548 while (false && CondVar) {549 }550 do {551 } while (false && CondVar);552}553 554struct logger {555 void (*debug)(struct logger *, const char *, ...);556};557 558int foo(void) {559 struct logger *pl = 0;560 int iterator = 0;561 while (iterator < 10) {562 char *l_tmp_msg = 0;563 pl->debug(pl, "%d: %s\n", iterator, l_tmp_msg);564 iterator++;565 }566 return 0;567}568 569struct AggregateWithReference {570 int &y;571};572 573void test_structured_bindings_good() {574 int x = 0;575 AggregateWithReference ref { x };576 auto &[y] = ref;577 for (; x < 10; ++y) {578 // No warning. The loop is finite because 'y' is a reference to 'x'.579 }580}581 582struct AggregateWithValue {583 int y;584};585 586void test_structured_bindings_bad() {587 int x = 0;588 AggregateWithValue val { x };589 auto &[y] = val;590 for (; x < 10; ++y) {591 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (x) are updated in the loop body [bugprone-infinite-loop]592 }593}594 595void test_volatile_cast() {596 // This is a no-op cast. Clang ignores the qualifier, we should too.597 for (int i = 0; (volatile int)i < 10;) {598 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]599 }600}601 602void test_volatile_concrete_address(int i, int size) {603 // No warning. The value behind the volatile concrete address604 // is beyond our control. It may change at any time.605 for (; *((volatile int *)0x1234) < size;) {606 }607 608 for (; *((volatile int *)(0x1234 + i)) < size;) {609 }610 611 for (; **((volatile int **)0x1234) < size;) {612 }613 614 volatile int *x = (volatile int *)0x1234;615 for (; *x < 10;) {616 }617 618 // FIXME: This one should probably also be suppressed.619 // Whatever the developer is doing here, they can do that again anywhere else620 // which basically makes it a global.621 for (; *(int *)0x1234 < size;) {622 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (size) are updated in the loop body [bugprone-infinite-loop]623 }624}625 626template <typename T>627int some_template_fn() { return 1; }628 629template <typename T>630void test_dependent_condition() {631 const int error = some_template_fn<T>();632 do {633 } while (false && error == 0);634 635 const int val = some_template_fn<T>();636 for (; !(val == 0 || true);) {637 }638 639 const int val2 = some_template_fn<T>();640 for (; !(val2 == 0 || false);) {641 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (val2) are updated in the loop body [bugprone-infinite-loop]642 }643 644 const int val3 = some_template_fn<T>();645 do {646 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (val3) are updated in the loop body [bugprone-infinite-loop]647 } while (1, (true) && val3 == 1);648 649 const int val4 = some_template_fn<T>();650 do {651 } while (1, (false) && val4 == 1);652}653 654void test_typeof() {655 __typeof__({656 for (int i = 0; i < 10; ++i) {657 }658 0;659 }) x;660}661 662void test_typeof_infinite() {663 __typeof__({664 for (int i = 0; i < 10;) {665 }666 0;667 }) x;668}669 670void test_typeof_while_infinite() {671 __typeof__({672 int i = 0;673 while (i < 10) {674 }675 0;676 }) x;677}678 679void test_typeof_dowhile_infinite() {680 __typeof__({681 int i = 0;682 do {683 684 } while (i < 10);685 0;686 }) x;687}688 689void test_local_static_recursion() {690 static int i = 10;691 int j = 0;692 693 i--;694 while (i >= 0)695 test_local_static_recursion(); // no warning, recursively decrement i696 for (; i >= 0;)697 test_local_static_recursion(); // no warning, recursively decrement i698 for (; i + j >= 0;)699 test_local_static_recursion(); // no warning, recursively decrement i700 for (; i >= 0; i--)701 ; // no warning, i decrements702 while (j >= 0)703 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (j) are updated in the loop body [bugprone-infinite-loop]704 test_local_static_recursion();705 706 int (*p)(int) = 0;707 708 while (i >= 0)709 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]710 p = 0;711 while (i >= 0)712 p(0); // we don't know what p points to so no warning713}714 715struct PairVal {716 int a;717 int b;718 PairVal(int a, int b) : a(a), b(b) {}719};720 721void structured_binding_infinite_loop1() {722 auto [x, y] = PairVal(0, 0);723 while (x < 10) {724 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (x) are updated in the loop body [bugprone-infinite-loop]725 y++;726 }727 while (y < 10) {728 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (y) are updated in the loop body [bugprone-infinite-loop]729 x++;730 }731}732 733void structured_binding_infinite_loop2() {734 auto [x, y] = PairVal(0, 0);735 while (x < 10) {736 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (x) are updated in the loop body [bugprone-infinite-loop]737 // No update to x or y738 }739 while (y < 10) {740 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (y) are updated in the loop body [bugprone-infinite-loop]741 // No update to x or y742 }743}744 745void structured_binding_not_infinite1() {746 auto [x, y] = PairVal(0, 0);747 while (x < 10) {748 x++;749 }750 while (y < 10) {751 y++;752 }753}754 755void volatile_structured_binding_in_condition() {756 volatile auto [x, y] = PairVal(0, 0);757 while (!x) {}758}759 760void test_local_static_structured_binding_recursion() {761 static auto [i, _] = PairVal(0, 0);762 int j = 0;763 764 i--;765 while (i >= 0)766 test_local_static_structured_binding_recursion(); // no warning, recursively decrement i767 for (; i >= 0;)768 test_local_static_structured_binding_recursion(); // no warning, recursively decrement i769 for (; i + j >= 0;)770 test_local_static_structured_binding_recursion(); // no warning, recursively decrement i771 for (; i >= 0; i--)772 ; // no warning, i decrements773 while (j >= 0)774 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (j) are updated in the loop body [bugprone-infinite-loop]775 test_local_static_structured_binding_recursion();776 777 int (*p)(int) = 0;778 779 while (i >= 0)780 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]781 p = 0;782 while (i >= 0)783 p(0); // we don't know what p points to so no warning784}785 786struct S { int a; };787void issue_138842_reduced() {788 int x = 10;789 auto [y] = S{1};790 791 while (y < x) {792 y++;793 }794}795 796namespace std {797template <typename T, typename U>798struct pair {799 T first;800 U second;801 802 pair(T a, U b) : first(a), second(b) {}803};804}805 806template <typename T, typename U>807void structured_binding_in_template_byval(T a, U b) {808 auto [c, d] = std::pair<T, U>(a,b);809 810 while (c < 10) {811 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (c) are updated in the loop body [bugprone-infinite-loop]812 d++;813 }814 815 while (c < 10) {816 c++; // no warning817 }818}819 820template <typename T, typename U>821void structured_binding_in_template_bylref(T a, U b) {822 auto p = std::pair<T, U>(a,b);823 auto& [c, d] = p;824 825 while (c < 10) {826 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (c) are updated in the loop body [bugprone-infinite-loop]827 d++;828 }829 830 while (c < 10) {831 c++; // no warning832 }833}834 835template <typename T, typename U>836void structured_binding_in_template_byrref(T a, U b) {837 auto p = std::pair<T, U>(a,b);838 auto&& [c, d] = p;839 840 while (c < 10) {841 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (c) are updated in the loop body [bugprone-infinite-loop]842 d++;843 }844 845 while (c < 10) {846 c++; // no warning847 }848}849 850void structured_binding_in_template_instantiation(int b) {851 structured_binding_in_template_byval(b, 0);852 structured_binding_in_template_bylref(b, 0);853 structured_binding_in_template_byrref(b, 0);854}855 856void array_structured_binding() {857 int arr[2] = {0, 0};858 auto [x, y] = arr;859 860 while (x < 10) {861 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (x) are updated in the loop body [bugprone-infinite-loop]862 y++;863 }864 865 while (y < 10) {866 y++; // no warning867 }868}869 870namespace std {871 using size_t = int;872 template <class> struct tuple_size;873 template <std::size_t, class> struct tuple_element;874 template <class...> class tuple;875 876namespace {877 template <class T, T v>878 struct size_helper { static const T value = v; };879} // namespace880 881template <class... T>882struct tuple_size<tuple<T...>> : size_helper<std::size_t, sizeof...(T)> {};883 884template <std::size_t I, class... T>885struct tuple_element<I, tuple<T...>> {886 using type = __type_pack_element<I, T...>;887};888 889template <class...> class tuple {};890 891template <std::size_t I, class... T>892typename tuple_element<I, tuple<T...>>::type get(tuple<T...>);893} // namespace std894 895std::tuple<int*, int> &get_chunk();896 897void test_structured_bindings_tuple() {898 auto [buffer, size ] = get_chunk();899 int maxLen = 8;900 901 while (size < maxLen) {902 // No warning. The loop is finite because 'size' is being incremented in each iteration and compared against 'maxLen' for termination903 buffer[size++] = 2;904 }905}906 907void test_structured_bindings_tuple_ref() {908 auto& [buffer, size ] = get_chunk();909 int maxLen = 8;910 911 while (size < maxLen) {912 // No warning. The loop is finite because 'size' is being incremented in each iteration and compared against 'maxLen' for termination913 buffer[size++] = 2;914 }915}916