brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.0 KiB · e69f52f Raw
461 lines · cpp
1// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 \2// RUN:    -analyzer-config eagerly-assume=false -verify %s3// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 \4// RUN:    -analyzer-config eagerly-assume=false -verify %s5// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 \6// RUN:    -analyzer-config elide-constructors=false -DNO_ELIDE_FLAG              \7// RUN:    -analyzer-config eagerly-assume=false -verify=expected,no-elide %s8// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 \9// RUN:    -analyzer-config elide-constructors=false                              \10// RUN:    -analyzer-config eagerly-assume=false -verify %s11 12// Copy elision always occurs in C++17, otherwise it's under13// an on-by-default flag.14#if __cplusplus >= 201703L15  #define ELIDE 116#else17  #ifndef NO_ELIDE_FLAG18    #define ELIDE 119  #endif20#endif21 22void clang_analyzer_eval(bool);23void clang_analyzer_dump(int);24 25namespace variable_functional_cast_crash {26 27struct A {28  A(int) {}29};30 31void foo() {32  A a = A(0);33}34 35struct B {36  A a;37  B(): a(A(0)) {}38};39 40} // namespace variable_functional_cast_crash41 42 43namespace ctor_initializer {44 45struct S {46  int x, y, z;47};48 49struct T {50  S s;51  int w;52  T(int w): s(), w(w) {}53};54 55class C {56  T t;57public:58  C() : t(T(4)) {59    S s = {1, 2, 3};60    t.s = s;61    // FIXME: Should be TRUE regardless of copy elision.62    clang_analyzer_eval(t.w == 4);63#ifdef ELIDE64    // expected-warning@-2{{TRUE}}65#else66    // expected-warning@-4{{UNKNOWN}}67#endif68  }69};70 71 72struct A {73  int x;74  A(): x(0) {}75  ~A() {}76};77 78struct B {79  A a;80  B() : a(A()) {}81};82 83void foo() {84  B b;85  clang_analyzer_eval(b.a.x == 0); // expected-warning{{TRUE}}86}87 88} // namespace ctor_initializer89 90 91namespace elision_on_ternary_op_branches {92class C1 {93  int x;94public:95  C1(int x): x(x) {}96  int getX() const { return x; }97  ~C1();98};99 100class C2 {101  int x;102  int y;103public:104  C2(int x, int y): x(x), y(y) {}105  int getX() const { return x; }106  int getY() const { return y; }107  ~C2();108};109 110void foo(int coin) {111  C1 c1 = coin ? C1(1) : C1(2);112  if (coin) {113    clang_analyzer_eval(c1.getX() == 1); // expected-warning{{TRUE}}114  } else {115    clang_analyzer_eval(c1.getX() == 2); // expected-warning{{TRUE}}116  }117  C2 c2 = coin ? C2(3, 4) : C2(5, 6);118  if (coin) {119    clang_analyzer_eval(c2.getX() == 3); // expected-warning{{TRUE}}120    clang_analyzer_eval(c2.getY() == 4); // expected-warning{{TRUE}}121  } else {122    clang_analyzer_eval(c2.getX() == 5); // expected-warning{{TRUE}}123    clang_analyzer_eval(c2.getY() == 6); // expected-warning{{TRUE}}124  }125}126} // namespace elision_on_ternary_op_branches127 128 129namespace address_vector_tests {130 131template <typename T> struct AddressVector {132  T *buf[20];133  int len;134 135  AddressVector() : len(0) {}136 137  void push(T *t) {138    buf[len] = t;139    ++len;140  }141};142 143class ClassWithoutDestructor {144  AddressVector<ClassWithoutDestructor> &v;145 146public:147  ClassWithoutDestructor(AddressVector<ClassWithoutDestructor> &v) : v(v) {148    push();149  }150 151  ClassWithoutDestructor(ClassWithoutDestructor &&c) : v(c.v) { push(); }152  ClassWithoutDestructor(const ClassWithoutDestructor &c) : v(c.v) { push(); }153 154  void push() { v.push(this); }155};156 157// Two warnings on no-elide: arg v holds the address of the temporary, and we158// are returning an object which holds v which holds the address of the temporary 159ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) {160  return ClassWithoutDestructor(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}161  // no-elide-warning@-1 {{Address of stack memory associated with temporary \162object of type 'ClassWithoutDestructor' is still \163referred to by the caller variable 'v' upon returning to the caller}}164}165// Two warnings on no-elide: arg v holds the address of the temporary, and we166// are returning an object which holds v which holds the address of the temporary 167ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) {168  return make1(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}169  // no-elide-warning@-1 {{Address of stack memory associated with temporary \170object of type 'ClassWithoutDestructor' is still \171referred to by the caller variable 'v' upon returning to the caller}}172}173// Two warnings on no-elide: arg v holds the address of the temporary, and we174// are returning an object which holds v which holds the address of the temporary 175ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) {176  return make2(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}177  // no-elide-warning@-1 {{Address of stack memory associated with temporary \178object of type 'ClassWithoutDestructor' is still \179referred to by the caller variable 'v' upon returning to the caller}}180}181 182void testMultipleReturns() {183  AddressVector<ClassWithoutDestructor> v;184  ClassWithoutDestructor c = make3(v);185 186#if ELIDE187  clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}188  clang_analyzer_eval(v.buf[0] == &c); // expected-warning{{TRUE}}189#else190  clang_analyzer_eval(v.len == 5); // expected-warning{{TRUE}}191  clang_analyzer_eval(v.buf[0] != v.buf[1]); // expected-warning{{TRUE}}192  clang_analyzer_eval(v.buf[1] != v.buf[2]); // expected-warning{{TRUE}}193  clang_analyzer_eval(v.buf[2] != v.buf[3]); // expected-warning{{TRUE}}194  clang_analyzer_eval(v.buf[3] != v.buf[4]); // expected-warning{{TRUE}}195  clang_analyzer_eval(v.buf[4] == &c); // expected-warning{{TRUE}}196#endif197}198 199void consume(ClassWithoutDestructor c) {200  c.push();201  // expected-warning@-1 {{Address of stack memory associated with local \202variable 'c' is still referred to by the caller variable 'v' upon returning \203to the caller}}204}205 206void testArgumentConstructorWithoutDestructor() {207  AddressVector<ClassWithoutDestructor> v;208 209  consume(make3(v));210 211#if ELIDE212  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}213  clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}214#else215  clang_analyzer_eval(v.len == 6); // expected-warning{{TRUE}}216  clang_analyzer_eval(v.buf[0] != v.buf[1]); // expected-warning{{TRUE}}217  clang_analyzer_eval(v.buf[1] != v.buf[2]); // expected-warning{{TRUE}}218  clang_analyzer_eval(v.buf[2] != v.buf[3]); // expected-warning{{TRUE}}219  clang_analyzer_eval(v.buf[3] != v.buf[4]); // expected-warning{{TRUE}}220  // We forced a push() in consume(), let's see if the address here matches221  // the address during construction.222  clang_analyzer_eval(v.buf[4] == v.buf[5]); // expected-warning{{TRUE}}223#endif224}225 226class ClassWithDestructor {227  AddressVector<ClassWithDestructor> &v;228 229public:230  ClassWithDestructor(AddressVector<ClassWithDestructor> &v) : v(v) {231    push();232  }233 234  ClassWithDestructor(ClassWithDestructor &&c) : v(c.v) { push(); }235  ClassWithDestructor(const ClassWithDestructor &c) : v(c.v) { push(); }236 237  ~ClassWithDestructor() { push(); }238 239  void push() { v.push(this); }240};241 242void testVariable() {243  AddressVector<ClassWithDestructor> v;244  {245    ClassWithDestructor c = ClassWithDestructor(v);246    // Check if the last destructor is an automatic destructor.247    // A temporary destructor would have fired by now.248#if ELIDE249    clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}250#else251    clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}252#endif253  }254#if ELIDE255  // 0. Construct the variable.256  // 1. Destroy the variable.257  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}258  clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}259#else260  // 0. Construct the temporary.261  // 1. Construct the variable.262  // 2. Destroy the temporary.263  // 3. Destroy the variable.264  clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}}265  clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}266  clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}}267#endif268}269 270struct TestCtorInitializer {271  ClassWithDestructor c;272  TestCtorInitializer(AddressVector<ClassWithDestructor> &refParam)273    : c(ClassWithDestructor(refParam)) {}274};275 276void testCtorInitializer() {277  AddressVector<ClassWithDestructor> v;278  {279    TestCtorInitializer t(v);280    // no-elide-warning@-1 {{Address of stack memory associated with temporary \281object of type 'ClassWithDestructor' is still referred \282to by the caller variable 'v' upon returning to the caller}}283    // Check if the last destructor is an automatic destructor.284    // A temporary destructor would have fired by now.285#if ELIDE286    clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}287#else288    clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}289#endif290  }291#if ELIDE292  // 0. Construct the member variable.293  // 1. Destroy the member variable.294  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}295  clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}296#else297  // 0. Construct the temporary.298  // 1. Construct the member variable.299  // 2. Destroy the temporary.300  // 3. Destroy the member variable.301  clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}}302  clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}303  clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}}304#endif305}306 307// Two warnings on no-elide: arg v holds the address of the temporary, and we308// are returning an object which holds v which holds the address of the temporary309ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) {310  return ClassWithDestructor(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}311  // no-elide-warning@-1 {{Address of stack memory associated with temporary \312object of type 'ClassWithDestructor' is still referred \313to by the caller variable 'v' upon returning to the caller}}314}315// Two warnings on no-elide: arg v holds the address of the temporary, and we316// are returning an object which holds v which holds the address of the temporary317ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) {318  return make1(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}319  // no-elide-warning@-1 {{Address of stack memory associated with temporary \320object of type 'ClassWithDestructor' is still referred \321to by the caller variable 'v' upon returning to the caller}}322}323// Two warnings on no-elide: arg v holds the address of the temporary, and we324// are returning an object which holds v which holds the address of the temporary325ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) {326  return make2(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}327  // no-elide-warning@-1 {{Address of stack memory associated with temporary \328object of type 'ClassWithDestructor' is still referred \329to by the caller variable 'v' upon returning to the caller}}330}331 332void testMultipleReturnsWithDestructors() {333  AddressVector<ClassWithDestructor> v;334  {335    ClassWithDestructor c = make3(v);336    // Check if the last destructor is an automatic destructor.337    // A temporary destructor would have fired by now.338#if ELIDE339    clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}340#else341    clang_analyzer_eval(v.len == 9); // expected-warning{{TRUE}}342#endif343  }344 345#if ELIDE346  // 0. Construct the variable. Yes, constructor in make1() constructs347  //    the variable 'c'.348  // 1. Destroy the variable.349  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}350  clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}351#else352  // 0. Construct the temporary in make1().353  // 1. Construct the temporary in make2().354  // 2. Destroy the temporary in make1().355  // 3. Construct the temporary in make3().356  // 4. Destroy the temporary in make2().357  // 5. Construct the temporary here.358  // 6. Destroy the temporary in make3().359  // 7. Construct the variable.360  // 8. Destroy the temporary here.361  // 9. Destroy the variable.362  clang_analyzer_eval(v.len == 10); // expected-warning{{TRUE}}363  clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}364  clang_analyzer_eval(v.buf[1] == v.buf[4]); // expected-warning{{TRUE}}365  clang_analyzer_eval(v.buf[3] == v.buf[6]); // expected-warning{{TRUE}}366  clang_analyzer_eval(v.buf[5] == v.buf[8]); // expected-warning{{TRUE}}367  clang_analyzer_eval(v.buf[7] == v.buf[9]); // expected-warning{{TRUE}}368#endif369}370 371void consume(ClassWithDestructor c) {372  c.push();373  // expected-warning@-1 {{Address of stack memory associated with local \374variable 'c' is still referred to by the caller variable 'v' upon returning \375to the caller}}376}377 378void testArgumentConstructorWithDestructor() {379  AddressVector<ClassWithDestructor> v;380 381  consume(make3(v));382 383#if ELIDE384  // 0. Construct the argument.385  // 1. Forced push() in consume().386  // 2. Destroy the argument.387  clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}388  clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}389  clang_analyzer_eval(v.buf[1] == v.buf[2]); // expected-warning{{TRUE}}390#else391  // 0. Construct the temporary in make1().392  // 1. Construct the temporary in make2().393  // 2. Destroy the temporary in make1().394  // 3. Construct the temporary in make3().395  // 4. Destroy the temporary in make2().396  // 5. Construct the temporary here.397  // 6. Destroy the temporary in make3().398  // 7. Construct the argument.399  // 8. Forced push() in consume().400  // 9. Destroy the argument. Notice the reverse order!401  // 10. Destroy the temporary here.402  clang_analyzer_eval(v.len == 11); // expected-warning{{TRUE}}403  clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}404  clang_analyzer_eval(v.buf[1] == v.buf[4]); // expected-warning{{TRUE}}405  clang_analyzer_eval(v.buf[3] == v.buf[6]); // expected-warning{{TRUE}}406  clang_analyzer_eval(v.buf[5] == v.buf[10]); // expected-warning{{TRUE}}407  clang_analyzer_eval(v.buf[7] == v.buf[8]); // expected-warning{{TRUE}}408  clang_analyzer_eval(v.buf[8] == v.buf[9]); // expected-warning{{TRUE}}409#endif410}411 412struct Foo {413  Foo(Foo **q) {414    *q = this;415  }416};417 418Foo make1(Foo **r) {419  return Foo(r);420  // no-elide-warning@-1 {{Address of stack memory associated with temporary \421object of type 'Foo' is still referred to by the caller \422variable 'z' upon returning to the caller}}423}424 425void test_copy_elision() {426  Foo *z;427  // If the copy elided, 'z' points to 'tmp', otherwise it's a dangling pointer.428  Foo tmp = make1(&z);429  (void)tmp;430}431 432} // namespace address_vector_tests433 434namespace arg_directly_from_return_in_loop {435 436struct Result {437  int value;438};439 440Result create() {441  return Result{10};442}443 444int accessValue(Result r) {445  return r.value;446}447 448void test() {449  for (int i = 0; i < 3; ++i) {450    int v = accessValue(create());451    if (i == 0) {452      clang_analyzer_dump(v); // expected-warning {{10 S32b}}453    } else {454      clang_analyzer_dump(v); // expected-warning {{10 S32b}}455                              // was {{reg_${{[0-9]+}}<int r.value> }} for C++11456    }457  }458}459 460} // namespace arg_directly_from_return_in_loop461