1 // RUN: %check_clang_tidy %s performance-inefficient-vector-operation %t -- \ 2 // RUN: -format-style=llvm \ 3 // RUN: -config='{CheckOptions: \ 4 // RUN: {performance-inefficient-vector-operation.EnableProto: true}}' 5 6 namespace std { 7 8 typedef decltype(sizeof 0) size_t; 9 10 template<class E> class initializer_list { 11 public: 12 using value_type = E; 13 using reference = E&; 14 using const_reference = const E&; 15 using size_type = size_t; 16 using iterator = const E*; 17 using const_iterator = const E*; 18 iterator p; 19 size_t sz; 20 initializer_list(); 21 size_t size() const; // number of elements 22 const E* begin() const; // first element 23 const E* end() const; // one past the last element 24 }; 25 26 // initializer list range access 27 template<class E> const E* begin(initializer_list<E> il); 28 template<class E> const E* end(initializer_list<E> il); 29 30 template <class T> 31 class vector { 32 public: 33 typedef T* iterator; 34 typedef const T* const_iterator; 35 typedef T& reference; 36 typedef const T& const_reference; 37 typedef size_t size_type; 38 39 explicit vector(); 40 explicit vector(size_type n); 41 42 void push_back(const T& val); 43 44 template <class... Args> void emplace_back(Args &&... args); 45 46 void reserve(size_t n); 47 void resize(size_t n); 48 49 size_t size() const; 50 const_reference operator[] (size_type) const; 51 reference operator[] (size_type); 52 53 const_iterator begin() const; 54 const_iterator end() const; 55 }; 56 } // namespace std 57 58 class Foo { 59 public: 60 explicit Foo(int); 61 }; 62 63 class Bar { 64 public: 65 Bar(int); 66 }; 67 68 int Op(int); 69 70 namespace proto2 { 71 class MessageLite {}; 72 class Message : public MessageLite {}; 73 } // namespace proto2 74 75 class FooProto : public proto2::Message { 76 public: 77 int *add_x(); // repeated int x; 78 void add_x(int x); 79 void mutable_x(); 80 void mutable_y(); 81 int add_z() const; // optional int add_z; 82 }; 83 84 class BarProto : public proto2::Message { 85 public: 86 int *add_x(); 87 void add_x(int x); 88 void mutable_x(); 89 void mutable_y(); 90 }; 91 f(std::vector<int> & t)92void f(std::vector<int>& t) { 93 { 94 std::vector<int> v0; 95 // CHECK-FIXES: v0.reserve(10); 96 for (int i = 0; i < 10; ++i) 97 v0.push_back(i); 98 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop 99 } 100 { 101 std::vector<int> v1; 102 // CHECK-FIXES: v1.reserve(10); 103 for (int i = 0; i < 10; i++) 104 v1.push_back(i); 105 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called 106 } 107 { 108 std::vector<int> v2; 109 // CHECK-FIXES: v2.reserve(10); 110 for (int i = 0; i < 10; ++i) 111 v2.push_back(0); 112 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called 113 } 114 { 115 std::vector<int> v3; 116 // CHECK-FIXES: v3.reserve(5); 117 for (int i = 0; i < 5; ++i) { 118 v3.push_back(i); 119 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called 120 } 121 // CHECK-FIXES-NOT: v3.reserve(10); 122 for (int i = 0; i < 10; ++i) { 123 // No fix for this loop as we encounter the prior loops. 124 v3.push_back(i); 125 } 126 } 127 { 128 std::vector<int> v4; 129 std::vector<int> v5; 130 v5.reserve(3); 131 // CHECK-FIXES: v4.reserve(10); 132 for (int i = 0; i < 10; ++i) 133 v4.push_back(i); 134 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called 135 } 136 { 137 std::vector<int> v6; 138 // CHECK-FIXES: v6.reserve(t.size()); 139 for (std::size_t i = 0; i < t.size(); ++i) { 140 v6.push_back(t[i]); 141 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called 142 } 143 } 144 { 145 std::vector<int> v7; 146 // CHECK-FIXES: v7.reserve(t.size() - 1); 147 for (std::size_t i = 0; i < t.size() - 1; ++i) { 148 v7.push_back(t[i]); 149 } // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called 150 } 151 { 152 std::vector<int> v8; 153 // CHECK-FIXES: v8.reserve(t.size()); 154 for (const auto &e : t) { 155 v8.push_back(e); 156 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called 157 } 158 } 159 { 160 std::vector<int> v9; 161 // CHECK-FIXES: v9.reserve(t.size()); 162 for (const auto &e : t) { 163 v9.push_back(Op(e)); 164 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called 165 } 166 } 167 { 168 std::vector<Foo> v10; 169 // CHECK-FIXES: v10.reserve(t.size()); 170 for (const auto &e : t) { 171 v10.push_back(Foo(e)); 172 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called 173 } 174 } 175 { 176 std::vector<Bar> v11; 177 // CHECK-FIXES: v11.reserve(t.size()); 178 for (const auto &e : t) { 179 v11.push_back(e); 180 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called 181 } 182 } 183 { 184 std::vector<Foo> v12; 185 // CHECK-FIXES: v12.reserve(t.size()); 186 for (const auto &e : t) { 187 v12.emplace_back(e); 188 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'emplace_back' is called 189 } 190 } 191 192 { 193 FooProto foo; 194 // CHECK-FIXES: foo.mutable_x()->Reserve(5); 195 for (int i = 0; i < 5; i++) { 196 foo.add_x(i); 197 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'add_x' is called inside a loop; consider pre-allocating the container capacity before the loop 198 } 199 } 200 201 // ---- Non-fixed Cases ---- 202 { 203 std::vector<int> z0; 204 z0.reserve(20); 205 // CHECK-FIXES-NOT: z0.reserve(10); 206 // There is a "reserve" call already. 207 for (int i = 0; i < 10; ++i) { 208 z0.push_back(i); 209 } 210 } 211 { 212 std::vector<int> z1; 213 z1.reserve(5); 214 // CHECK-FIXES-NOT: z1.reserve(10); 215 // There is a "reserve" call already. 216 for (int i = 0; i < 10; ++i) { 217 z1.push_back(i); 218 } 219 } 220 { 221 std::vector<int> z2; 222 z2.resize(5); 223 // CHECK-FIXES-NOT: z2.reserve(10); 224 // There is a ref usage of v before the loop. 225 for (int i = 0; i < 10; ++i) { 226 z2.push_back(i); 227 } 228 } 229 { 230 std::vector<int> z3; 231 z3.push_back(0); 232 // CHECK-FIXES-NOT: z3.reserve(10); 233 // There is a ref usage of v before the loop. 234 for (int i = 0; i < 10; ++i) { 235 z3.push_back(i); 236 } 237 } 238 { 239 std::vector<int> z4; 240 f(z4); 241 // CHECK-FIXES-NOT: z4.reserve(10); 242 // There is a ref usage of z4 before the loop. 243 for (int i = 0; i < 10; ++i) { 244 z4.push_back(i); 245 } 246 } 247 { 248 std::vector<int> z5(20); 249 // CHECK-FIXES-NOT: z5.reserve(10); 250 // z5 is not constructed with default constructor. 251 for (int i = 0; i < 10; ++i) { 252 z5.push_back(i); 253 } 254 } 255 { 256 std::vector<int> z6; 257 // CHECK-FIXES-NOT: z6.reserve(10); 258 // For-loop is not started with 0. 259 for (int i = 1; i < 10; ++i) { 260 z6.push_back(i); 261 } 262 } 263 { 264 std::vector<int> z7; 265 // CHECK-FIXES-NOT: z7.reserve(t.size()); 266 // z7 isn't referenced in for-loop body. 267 for (std::size_t i = 0; i < t.size(); ++i) { 268 t.push_back(i); 269 } 270 } 271 { 272 std::vector<int> z8; 273 int k; 274 // CHECK-FIXES-NOT: z8.reserve(10); 275 // For-loop isn't a fixable loop. 276 for (std::size_t i = 0; k < 10; ++i) { 277 z8.push_back(t[i]); 278 } 279 } 280 { 281 std::vector<int> z9; 282 // CHECK-FIXES-NOT: z9.reserve(i + 1); 283 // The loop end expression refers to the loop variable i. 284 for (int i = 0; i < i + 1; i++) 285 z9.push_back(i); 286 } 287 { 288 std::vector<int> z10; 289 int k; 290 // CHECK-FIXES-NOT: z10.reserve(10); 291 // For-loop isn't a fixable loop. 292 for (std::size_t i = 0; i < 10; ++k) { 293 z10.push_back(t[i]); 294 } 295 } 296 { 297 std::vector<int> z11; 298 // initializer_list should not trigger the check. 299 for (int e : {1, 2, 3, 4, 5}) { 300 z11.push_back(e); 301 } 302 } 303 { 304 std::vector<int> z12; 305 std::vector<int>* z13 = &t; 306 // We only support detecting the range init expression which references 307 // container directly. 308 // Complex range init expressions like `*z13` is not supported. 309 for (const auto &e : *z13) { 310 z12.push_back(e); 311 } 312 } 313 314 { 315 FooProto foo; 316 foo.mutable_x(); 317 // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5); 318 for (int i = 0; i < 5; i++) { 319 foo.add_x(i); 320 } 321 } 322 { 323 FooProto foo; 324 // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5); 325 for (int i = 0; i < 5; i++) { 326 foo.add_x(i); 327 foo.add_x(i); 328 } 329 } 330 { 331 FooProto foo; 332 // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5); 333 foo.add_x(-1); 334 for (int i = 0; i < 5; i++) { 335 foo.add_x(i); 336 } 337 } 338 { 339 FooProto foo; 340 BarProto bar; 341 bar.mutable_x(); 342 // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5); 343 for (int i = 0; i < 5; i++) { 344 foo.add_x(); 345 bar.add_x(); 346 } 347 } 348 { 349 FooProto foo; 350 foo.mutable_y(); 351 // CHECK-FIXES-NOT: foo.mutable_x()->Reserve(5); 352 for (int i = 0; i < 5; i++) { 353 foo.add_x(i); 354 } 355 } 356 { 357 FooProto foo; 358 // CHECK-FIXES-NOT: foo.mutable_z()->Reserve(5); 359 for (int i = 0; i < 5; i++) { 360 foo.add_z(); 361 } 362 } 363 } 364 365 struct StructWithFieldContainer { 366 std::vector<int> Numbers; getNumbersStructWithFieldContainer367 std::vector<int> getNumbers() const { 368 std::vector<int> Result; 369 // CHECK-FIXES: Result.reserve(Numbers.size()); 370 for (auto Number : Numbers) { 371 Result.push_back(Number); 372 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called 373 } 374 return Result; 375 } 376 }; 377 378 StructWithFieldContainer getStructWithField(); 379 foo(const StructWithFieldContainer & Src)380void foo(const StructWithFieldContainer &Src) { 381 std::vector<int> A; 382 // CHECK-FIXES: A.reserve(Src.Numbers.size()); 383 for (auto Number : Src.Numbers) { 384 A.push_back(Number); 385 // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'push_back' is called 386 } 387 std::vector<int> B; 388 for (auto Number : getStructWithField().Numbers) { 389 B.push_back(Number); 390 } 391 } 392 393 namespace gh95596 { 394 f(std::vector<int> & t)395void f(std::vector<int>& t) { 396 { 397 std::vector<int> gh95596_0; 398 // CHECK-FIXES: gh95596_0.reserve(10); 399 for (unsigned i = 0; i < 10; ++i) 400 gh95596_0.push_back(i); 401 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop 402 } 403 { 404 std::vector<int> gh95596_1; 405 // CHECK-FIXES: gh95596_1.reserve(10); 406 for (int i = 0U; i < 10; ++i) 407 gh95596_1.push_back(i); 408 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop 409 } 410 { 411 std::vector<int> gh95596_2; 412 // CHECK-FIXES: gh95596_2.reserve(10); 413 for (unsigned i = 0U; i < 10; ++i) 414 gh95596_2.push_back(i); 415 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop 416 } 417 { 418 std::vector<int> gh95596_3; 419 // CHECK-FIXES: gh95596_3.reserve(10U); 420 for (int i = 0; i < 10U; ++i) 421 gh95596_3.push_back(i); 422 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the container capacity before the loop 423 } 424 } 425 426 } // namespace gh95596 427