| 1 | // RUN: %clang_cc1 -fsyntax-only -Wloop-analysis -verify -std=c++17 %s |
| 2 | |
| 3 | struct S { |
| 4 | bool stop() { return false; } |
| 5 | bool keep_running; |
| 6 | }; |
| 7 | |
| 8 | void by_ref(int &value) { } |
| 9 | void by_value(int value) { } |
| 10 | void by_pointer(int *value) {} |
| 11 | |
| 12 | void test1() { |
| 13 | S s; |
| 14 | for (; !s.stop();) {} |
| 15 | for (; s.keep_running;) {} |
| 16 | for (int i; i < 1; ++i) {} |
| 17 | for (int i; i < 1; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 18 | for (int i; i < 1; ) { ++i; } |
| 19 | for (int i; i < 1; ) { return; } |
| 20 | for (int i; i < 1; ) { break; } |
| 21 | for (int i; i < 1; ) { goto exit_loop; } |
| 22 | exit_loop: |
| 23 | for (int i; i < 1; ) { by_ref(i); } |
| 24 | for (int i; i < 1; ) { by_value(i); } // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 25 | for (int i; i < 1; ) { by_pointer(&i); } |
| 26 | |
| 27 | for (int i; i < 1; ++i) |
| 28 | for (int j; j < 1; ++j) |
| 29 | { } |
| 30 | for (int i; i < 1; ++i) |
| 31 | for (int j; j < 1; ++i) // expected-warning {{variable 'j' used in loop condition not modified in loop body}} |
| 32 | { } |
| 33 | for (int i; i < 1; ++i) |
| 34 | for (int j; i < 1; ++j) // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 35 | { } |
| 36 | |
| 37 | for (int *i, *j; i < j; ++i) {} |
| 38 | for (int *i, *j; i < j;) {} // expected-warning {{variables 'i' and 'j' used in loop condition not modified in loop body}} |
| 39 | |
| 40 | // Dereferencing pointers is ignored for now. |
| 41 | for (int *i; *i; ) {} |
| 42 | } |
| 43 | |
| 44 | void test2() { |
| 45 | int i, j, k; |
| 46 | int *ptr; |
| 47 | |
| 48 | // Testing CastExpr |
| 49 | for (; i; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 50 | for (; i; ) { i = 5; } |
| 51 | |
| 52 | // Testing BinaryOperator |
| 53 | for (; i < j; ) {} // expected-warning {{variables 'i' and 'j' used in loop condition not modified in loop body}} |
| 54 | for (; i < j; ) { i = 5; } |
| 55 | for (; i < j; ) { j = 5; } |
| 56 | |
| 57 | // Testing IntegerLiteral |
| 58 | for (; i < 5; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 59 | for (; i < 5; ) { i = 5; } |
| 60 | |
| 61 | // Testing FloatingLiteral |
| 62 | for (; i < 5.0; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 63 | for (; i < 5.0; ) { i = 5; } |
| 64 | |
| 65 | // Testing CharacterLiteral |
| 66 | for (; i == 'a'; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 67 | for (; i == 'a'; ) { i = 5; } |
| 68 | |
| 69 | // Testing CXXBoolLiteralExpr |
| 70 | for (; i == true; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 71 | for (; i == true; ) { i = 5; } |
| 72 | |
| 73 | // Testing GNUNullExpr |
| 74 | for (; ptr == __null; ) {} // expected-warning {{variable 'ptr' used in loop condition not modified in loop body}} |
| 75 | for (; ptr == __null; ) { ptr = &i; } |
| 76 | |
| 77 | // Testing UnaryOperator |
| 78 | for (; -i > 5; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 79 | for (; -i > 5; ) { ++i; } |
| 80 | |
| 81 | // Testing ImaginaryLiteral |
| 82 | for (; i != 3i; ) {} // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 83 | for (; i != 3i; ) { ++i; } |
| 84 | |
| 85 | // Testing ConditionalOperator |
| 86 | for (; i ? j : k; ) {} // expected-warning {{variables 'i', 'j', and 'k' used in loop condition not modified in loop body}} |
| 87 | for (; i ? j : k; ) { ++i; } |
| 88 | for (; i ? j : k; ) { ++j; } |
| 89 | for (; i ? j : k; ) { ++k; } |
| 90 | for (; i; ) { j = i ? i : i; } // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 91 | for (; i; ) { j = (i = 1) ? i : i; } |
| 92 | for (; i; ) { j = i ? i : ++i; } |
| 93 | |
| 94 | // Testing BinaryConditionalOperator |
| 95 | for (; i ?: j; ) {} // expected-warning {{variables 'i' and 'j' used in loop condition not modified in loop body}} |
| 96 | for (; i ?: j; ) { ++i; } |
| 97 | for (; i ?: j; ) { ++j; } |
| 98 | for (; i; ) { j = i ?: i; } // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 99 | |
| 100 | // Testing ParenExpr |
| 101 | for (; (i); ) { } // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 102 | for (; (i); ) { ++i; } |
| 103 | |
| 104 | // Testing non-evaluated variables |
| 105 | for (; i < sizeof(j); ) { } // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 106 | for (; i < sizeof(j); ) { ++j; } // expected-warning {{variable 'i' used in loop condition not modified in loop body}} |
| 107 | for (; i < sizeof(j); ) { ++i; } |
| 108 | } |
| 109 | |
| 110 | // False positive and how to silence. |
| 111 | void test3() { |
| 112 | int x; |
| 113 | int *ptr = &x; |
| 114 | for (;x<5;) { *ptr = 6; } // expected-warning {{variable 'x' used in loop condition not modified in loop body}} |
| 115 | |
| 116 | for (;x<5;) { |
| 117 | *ptr = 6; |
| 118 | (void)x; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | // Check ordering and printing of variables. Max variables is currently 4. |
| 123 | void test4() { |
| 124 | int a, b, c, d, e, f; |
| 125 | for (; a;); // expected-warning {{variable 'a' used in loop condition not modified in loop body}} |
| 126 | for (; a + b;); // expected-warning {{variables 'a' and 'b' used in loop condition not modified in loop body}} |
| 127 | for (; a + b + c;); // expected-warning {{variables 'a', 'b', and 'c' used in loop condition not modified in loop body}} |
| 128 | for (; a + b + c + d;); // expected-warning {{variables 'a', 'b', 'c', and 'd' used in loop condition not modified in loop body}} |
| 129 | for (; a + b + c + d + e;); // expected-warning {{variables used in loop condition not modified in loop body}} |
| 130 | for (; a + b + c + d + e + f;); // expected-warning {{variables used in loop condition not modified in loop body}} |
| 131 | for (; a + c + d + b;); // expected-warning {{variables 'a', 'c', 'd', and 'b' used in loop condition not modified in loop body}} |
| 132 | for (; d + c + b + a;); // expected-warning {{variables 'd', 'c', 'b', and 'a' used in loop condition not modified in loop body}} |
| 133 | } |
| 134 | |
| 135 | // Ensure that the warning doesn't fail when lots of variables are used |
| 136 | // in the conditional. |
| 137 | void test5() { |
| 138 | for (int a; a+a+a+a+a+a+a+a+a+a;); // \ |
| 139 | // expected-warning {{variable 'a' used in loop condition not modified in loop body}} |
| 140 | for (int a; a+a+a+a+a+a+a+a+a+a+a;); // \ |
| 141 | // expected-warning {{variable 'a' used in loop condition not modified in loop body}} |
| 142 | for (int a; a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a;); // \ |
| 143 | // expected-warning {{variable 'a' used in loop condition not modified in loop body}} |
| 144 | for (int a; a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a;);//\ |
| 145 | // expected-warning {{variable 'a' used in loop condition not modified in loop body}} |
| 146 | } |
| 147 | |
| 148 | // Ignore global variables and static variables. |
| 149 | int x6; |
| 150 | void test6() { |
| 151 | static int y; |
| 152 | for (;x6;); |
| 153 | for (;y;); |
| 154 | } |
| 155 | |
| 156 | void test7() { |
| 157 | int i; |
| 158 | for (;;i++) { // expected-note{{incremented here}} |
| 159 | if (true) test7(); |
| 160 | i++; // expected-warning{{incremented both}} |
| 161 | } |
| 162 | for (;;i++) { // expected-note{{incremented here}} |
| 163 | if (true) break; |
| 164 | ++i; // expected-warning{{incremented both}} |
| 165 | } |
| 166 | for (;;++i) { // expected-note{{incremented here}} |
| 167 | while (true) return; |
| 168 | i++; // expected-warning{{incremented both}} |
| 169 | } |
| 170 | for (;;++i) { // expected-note{{incremented here}} |
| 171 | ++i; // expected-warning{{incremented both}} |
| 172 | } |
| 173 | |
| 174 | for (;;i--) { // expected-note{{decremented here}} |
| 175 | if (true) test7(); |
| 176 | i--; // expected-warning{{decremented both}} |
| 177 | } |
| 178 | for (;;i--) { // expected-note{{decremented here}} |
| 179 | if (true) break; |
| 180 | --i; // expected-warning{{decremented both}} |
| 181 | } |
| 182 | for (;;--i) { // expected-note{{decremented here}} |
| 183 | while (true) return; |
| 184 | i--; // expected-warning{{decremented both}} |
| 185 | } |
| 186 | for (;;--i) { // expected-note{{decremented here}} |
| 187 | --i; // expected-warning{{decremented both}} |
| 188 | } |
| 189 | |
| 190 | // Don't warn when loop is only one statement. |
| 191 | for (;;++i) |
| 192 | i++; |
| 193 | for (;;--i) |
| 194 | --i; |
| 195 | |
| 196 | // Don't warn when loop has continue statement. |
| 197 | for (;;i++) { |
| 198 | if (true) continue; |
| 199 | i++; |
| 200 | } |
| 201 | for (;;i--) { |
| 202 | if (true) continue; |
| 203 | i--; |
| 204 | } |
| 205 | |
| 206 | // But do warn if the continue is in a nested loop. |
| 207 | for (;;i--) { // expected-note{{decremented here}} |
| 208 | for (int j = 0; j < 10; ++j) continue; |
| 209 | i--; // expected-warning{{decremented both}} |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | struct iterator { |
| 214 | iterator operator++() { return *this; } |
| 215 | iterator operator++(int) { return *this; } |
| 216 | iterator operator--() { return *this; } |
| 217 | iterator operator--(int) { return *this; } |
| 218 | }; |
| 219 | void test8() { |
| 220 | iterator i; |
| 221 | for (;;i++) { // expected-note{{incremented here}} |
| 222 | if (true) test7(); |
| 223 | i++; // expected-warning{{incremented both}} |
| 224 | } |
| 225 | for (;;i++) { // expected-note{{incremented here}} |
| 226 | if (true) break; |
| 227 | ++i; // expected-warning{{incremented both}} |
| 228 | } |
| 229 | for (;;++i) { // expected-note{{incremented here}} |
| 230 | while (true) return; |
| 231 | i++; // expected-warning{{incremented both}} |
| 232 | } |
| 233 | for (;;++i) { // expected-note{{incremented here}} |
| 234 | ++i; // expected-warning{{incremented both}} |
| 235 | } |
| 236 | |
| 237 | for (;;i--) { // expected-note{{decremented here}} |
| 238 | if (true) test7(); |
| 239 | i--; // expected-warning{{decremented both}} |
| 240 | } |
| 241 | for (;;i--) { // expected-note{{decremented here}} |
| 242 | if (true) break; |
| 243 | --i; // expected-warning{{decremented both}} |
| 244 | } |
| 245 | for (;;--i) { // expected-note{{decremented here}} |
| 246 | while (true) return; |
| 247 | i--; // expected-warning{{decremented both}} |
| 248 | } |
| 249 | for (;;--i) { // expected-note{{decremented here}} |
| 250 | --i; // expected-warning{{decremented both}} |
| 251 | } |
| 252 | |
| 253 | // Don't warn when loop is only one statement. |
| 254 | for (;;++i) |
| 255 | i++; |
| 256 | for (;;--i) |
| 257 | --i; |
| 258 | |
| 259 | // Don't warn when loop has continue statement. |
| 260 | for (;;i++) { |
| 261 | if (true) continue; |
| 262 | i++; |
| 263 | } |
| 264 | for (;;i--) { |
| 265 | if (true) continue; |
| 266 | i--; |
| 267 | } |
| 268 | |
| 269 | // But do warn if the continue is in a nested loop. |
| 270 | for (;;i--) { // expected-note{{decremented here}} |
| 271 | for (int j = 0; j < 10; ++j) continue; |
| 272 | i--; // expected-warning{{decremented both}} |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | int f(int); |
| 277 | void test9() { |
| 278 | // Don't warn when variable is defined by the loop condition. |
| 279 | for (int i = 0; int x = f(i); ++i) {} |
| 280 | } |
| 281 | |
| 282 | // Don't warn when decomposition variables are in the loop condition. |
| 283 | // TODO: BindingDecl's which make a copy should warn. |
| 284 | void test10() { |
| 285 | int arr[] = {1, 2, 3}; |
| 286 | for (auto[i, j, k] = arr;;) { } |
| 287 | for (auto[i, j, k] = arr; i < j; ++i, ++j) { } |
| 288 | |
| 289 | for (auto[i, j, k] = arr; i;) { } |
| 290 | for (auto[i, j, k] = arr; i < j;) { } |
| 291 | for (auto[i, j, k] = arr; i < j; ++arr[0]) { } |
| 292 | |
| 293 | int a = 1, b = 2; |
| 294 | for (auto[i, j, k] = arr; a < b;) { } // expected-warning{{variables 'a' and 'b' used in loop condition not modified in loop body}} |
| 295 | for (auto[i, j, k] = arr; a < b; ++a) { } |
| 296 | |
| 297 | for (auto [i, j, k] = arr; i < a;) { } |
| 298 | for (auto[i, j, k] = arr; i < a; ++a) { } |
| 299 | for (auto[i, j, k] = arr; i < a; ++i) { } |
| 300 | for (auto[i, j, k] = arr; i < a; ++arr[0]) { } |
| 301 | }; |
| 302 | |