|  | // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s | 
|  | // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -DCONSTRUCTORS=1 -analyzer-config c++-inlining=constructors -verify %s | 
|  |  | 
|  | void clang_analyzer_eval(bool); | 
|  | void clang_analyzer_checkInlined(bool); | 
|  |  | 
|  | class A { | 
|  | protected: | 
|  | int x; | 
|  | }; | 
|  |  | 
|  | class B : public A { | 
|  | public: | 
|  | void f(); | 
|  | }; | 
|  |  | 
|  | void B::f() { | 
|  | x = 3; | 
|  | } | 
|  |  | 
|  |  | 
|  | class C : public B { | 
|  | public: | 
|  | void g() { | 
|  | // This used to crash because we are upcasting through two bases. | 
|  | x = 5; | 
|  | } | 
|  | }; | 
|  |  | 
|  |  | 
|  | namespace VirtualBaseClasses { | 
|  | class A { | 
|  | protected: | 
|  | int x; | 
|  | }; | 
|  |  | 
|  | class B : public virtual A { | 
|  | public: | 
|  | int getX() { return x; } | 
|  | }; | 
|  |  | 
|  | class C : public virtual A { | 
|  | public: | 
|  | void setX() { x = 42; } | 
|  | }; | 
|  |  | 
|  | class D : public B, public C {}; | 
|  | class DV : virtual public B, public C {}; | 
|  | class DV2 : public B, virtual public C {}; | 
|  |  | 
|  | void test() { | 
|  | D d; | 
|  | d.setX(); | 
|  | clang_analyzer_eval(d.getX() == 42); // expected-warning{{TRUE}} | 
|  |  | 
|  | DV dv; | 
|  | dv.setX(); | 
|  | clang_analyzer_eval(dv.getX() == 42); // expected-warning{{TRUE}} | 
|  |  | 
|  | DV2 dv2; | 
|  | dv2.setX(); | 
|  | clang_analyzer_eval(dv2.getX() == 42); // expected-warning{{TRUE}} | 
|  | } | 
|  |  | 
|  |  | 
|  | // Make sure we're consistent about the offset of the A subobject within an | 
|  | // Intermediate virtual base class. | 
|  | class Padding1 { int unused; }; | 
|  | class Padding2 { int unused; }; | 
|  | class Intermediate : public Padding1, public A, public Padding2 {}; | 
|  |  | 
|  | class BI : public virtual Intermediate { | 
|  | public: | 
|  | int getX() { return x; } | 
|  | }; | 
|  |  | 
|  | class CI : public virtual Intermediate { | 
|  | public: | 
|  | void setX() { x = 42; } | 
|  | }; | 
|  |  | 
|  | class DI : public BI, public CI {}; | 
|  |  | 
|  | void testIntermediate() { | 
|  | DI d; | 
|  | d.setX(); | 
|  | clang_analyzer_eval(d.getX() == 42); // expected-warning{{TRUE}} | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | namespace DynamicVirtualUpcast { | 
|  | class A { | 
|  | public: | 
|  | virtual ~A(); | 
|  | }; | 
|  |  | 
|  | class B : virtual public A {}; | 
|  | class C : virtual public B {}; | 
|  | class D : virtual public C {}; | 
|  |  | 
|  | bool testCast(A *a) { | 
|  | return dynamic_cast<B*>(a) && dynamic_cast<C*>(a); | 
|  | } | 
|  |  | 
|  | void test() { | 
|  | D d; | 
|  | clang_analyzer_eval(testCast(&d)); // expected-warning{{TRUE}} | 
|  | } | 
|  | } | 
|  |  | 
|  | namespace DynamicMultipleInheritanceUpcast { | 
|  | class B { | 
|  | public: | 
|  | virtual ~B(); | 
|  | }; | 
|  | class C { | 
|  | public: | 
|  | virtual ~C(); | 
|  | }; | 
|  | class D : public B, public C {}; | 
|  |  | 
|  | bool testCast(B *a) { | 
|  | return dynamic_cast<C*>(a); | 
|  | } | 
|  |  | 
|  | void test() { | 
|  | D d; | 
|  | clang_analyzer_eval(testCast(&d)); // expected-warning{{TRUE}} | 
|  | } | 
|  |  | 
|  |  | 
|  | class DV : virtual public B, virtual public C {}; | 
|  |  | 
|  | void testVirtual() { | 
|  | DV d; | 
|  | clang_analyzer_eval(testCast(&d)); // expected-warning{{TRUE}} | 
|  | } | 
|  | } | 
|  |  | 
|  | namespace LazyBindings { | 
|  | struct Base { | 
|  | int x; | 
|  | }; | 
|  |  | 
|  | struct Derived : public Base { | 
|  | int y; | 
|  | }; | 
|  |  | 
|  | struct DoubleDerived : public Derived { | 
|  | int z; | 
|  | }; | 
|  |  | 
|  | int getX(const Base &obj) { | 
|  | return obj.x; | 
|  | } | 
|  |  | 
|  | int getY(const Derived &obj) { | 
|  | return obj.y; | 
|  | } | 
|  |  | 
|  | void testDerived() { | 
|  | Derived d; | 
|  | d.x = 1; | 
|  | d.y = 2; | 
|  | clang_analyzer_eval(getX(d) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d) == 2); // expected-warning{{TRUE}} | 
|  |  | 
|  | Base b(d); | 
|  | clang_analyzer_eval(getX(b) == 1); // expected-warning{{TRUE}} | 
|  |  | 
|  | Derived d2(d); | 
|  | clang_analyzer_eval(getX(d2) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d2) == 2); // expected-warning{{TRUE}} | 
|  | } | 
|  |  | 
|  | void testDoubleDerived() { | 
|  | DoubleDerived d; | 
|  | d.x = 1; | 
|  | d.y = 2; | 
|  | clang_analyzer_eval(getX(d) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d) == 2); // expected-warning{{TRUE}} | 
|  |  | 
|  | Base b(d); | 
|  | clang_analyzer_eval(getX(b) == 1); // expected-warning{{TRUE}} | 
|  |  | 
|  | Derived d2(d); | 
|  | clang_analyzer_eval(getX(d2) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d2) == 2); // expected-warning{{TRUE}} | 
|  |  | 
|  | DoubleDerived d3(d); | 
|  | clang_analyzer_eval(getX(d3) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d3) == 2); // expected-warning{{TRUE}} | 
|  | } | 
|  |  | 
|  | namespace WithOffset { | 
|  | struct Offset { | 
|  | int padding; | 
|  | }; | 
|  |  | 
|  | struct OffsetDerived : private Offset, public Base { | 
|  | int y; | 
|  | }; | 
|  |  | 
|  | struct DoubleOffsetDerived : public OffsetDerived { | 
|  | int z; | 
|  | }; | 
|  |  | 
|  | int getY(const OffsetDerived &obj) { | 
|  | return obj.y; | 
|  | } | 
|  |  | 
|  | void testDerived() { | 
|  | OffsetDerived d; | 
|  | d.x = 1; | 
|  | d.y = 2; | 
|  | clang_analyzer_eval(getX(d) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d) == 2); // expected-warning{{TRUE}} | 
|  |  | 
|  | Base b(d); | 
|  | clang_analyzer_eval(getX(b) == 1); // expected-warning{{TRUE}} | 
|  |  | 
|  | OffsetDerived d2(d); | 
|  | clang_analyzer_eval(getX(d2) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d2) == 2); // expected-warning{{TRUE}} | 
|  | } | 
|  |  | 
|  | void testDoubleDerived() { | 
|  | DoubleOffsetDerived d; | 
|  | d.x = 1; | 
|  | d.y = 2; | 
|  | clang_analyzer_eval(getX(d) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d) == 2); // expected-warning{{TRUE}} | 
|  |  | 
|  | Base b(d); | 
|  | clang_analyzer_eval(getX(b) == 1); // expected-warning{{TRUE}} | 
|  |  | 
|  | OffsetDerived d2(d); | 
|  | clang_analyzer_eval(getX(d2) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d2) == 2); // expected-warning{{TRUE}} | 
|  |  | 
|  | DoubleOffsetDerived d3(d); | 
|  | clang_analyzer_eval(getX(d3) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d3) == 2); // expected-warning{{TRUE}} | 
|  | } | 
|  | } | 
|  |  | 
|  | namespace WithVTable { | 
|  | struct DerivedVTBL : public Base { | 
|  | int y; | 
|  | virtual void method(); | 
|  | }; | 
|  |  | 
|  | struct DoubleDerivedVTBL : public DerivedVTBL { | 
|  | int z; | 
|  | }; | 
|  |  | 
|  | int getY(const DerivedVTBL &obj) { | 
|  | return obj.y; | 
|  | } | 
|  |  | 
|  | int getZ(const DoubleDerivedVTBL &obj) { | 
|  | return obj.z; | 
|  | } | 
|  |  | 
|  | void testDerived() { | 
|  | DerivedVTBL d; | 
|  | d.x = 1; | 
|  | d.y = 2; | 
|  | clang_analyzer_eval(getX(d) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d) == 2); // expected-warning{{TRUE}} | 
|  |  | 
|  | Base b(d); | 
|  | clang_analyzer_eval(getX(b) == 1); // expected-warning{{TRUE}} | 
|  |  | 
|  | #if CONSTRUCTORS | 
|  | DerivedVTBL d2(d); | 
|  | clang_analyzer_eval(getX(d2) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d2) == 2); // expected-warning{{TRUE}} | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #if CONSTRUCTORS | 
|  | void testDoubleDerived() { | 
|  | DoubleDerivedVTBL d; | 
|  | d.x = 1; | 
|  | d.y = 2; | 
|  | d.z = 3; | 
|  | clang_analyzer_eval(getX(d) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d) == 2); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getZ(d) == 3); // expected-warning{{TRUE}} | 
|  |  | 
|  | Base b(d); | 
|  | clang_analyzer_eval(getX(b) == 1); // expected-warning{{TRUE}} | 
|  |  | 
|  | DerivedVTBL d2(d); | 
|  | clang_analyzer_eval(getX(d2) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d2) == 2); // expected-warning{{TRUE}} | 
|  |  | 
|  | DoubleDerivedVTBL d3(d); | 
|  | clang_analyzer_eval(getX(d3) == 1); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getY(d3) == 2); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(getZ(d3) == 3); // expected-warning{{TRUE}} | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #if CONSTRUCTORS | 
|  | namespace Nested { | 
|  | struct NonTrivialCopy { | 
|  | int padding; | 
|  | NonTrivialCopy() {} | 
|  | NonTrivialCopy(const NonTrivialCopy &) {} | 
|  | }; | 
|  |  | 
|  | struct FullyDerived : private NonTrivialCopy, public Derived { | 
|  | int z; | 
|  | }; | 
|  |  | 
|  | struct Wrapper { | 
|  | FullyDerived d; | 
|  | int zz; | 
|  |  | 
|  | Wrapper(const FullyDerived &d) : d(d), zz(0) {} | 
|  | }; | 
|  |  | 
|  | void test5() { | 
|  | Wrapper w((FullyDerived())); | 
|  | w.d.x = 1; | 
|  |  | 
|  | Wrapper w2(w); | 
|  | clang_analyzer_eval(getX(w2.d) == 1); // expected-warning{{TRUE}} | 
|  | } | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | namespace Redeclaration { | 
|  | class Base; | 
|  |  | 
|  | class Base { | 
|  | public: | 
|  | virtual int foo(); | 
|  | int get() { return value; } | 
|  |  | 
|  | int value; | 
|  | }; | 
|  |  | 
|  | class Derived : public Base { | 
|  | public: | 
|  | virtual int bar(); | 
|  | }; | 
|  |  | 
|  | void test(Derived d) { | 
|  | d.foo(); // don't crash | 
|  | d.bar(); // basic correctness check | 
|  |  | 
|  | Base &b = d; | 
|  | b.foo(); // don't crash | 
|  |  | 
|  | d.value = 42; // don't crash | 
|  | clang_analyzer_eval(d.get() == 42); // expected-warning{{TRUE}} | 
|  | clang_analyzer_eval(b.get() == 42); // expected-warning{{TRUE}} | 
|  | } | 
|  | }; | 
|  |  | 
|  | namespace PR15394 { | 
|  | namespace Original { | 
|  | class Base { | 
|  | public: | 
|  | virtual int f() = 0; | 
|  | int i; | 
|  | }; | 
|  |  | 
|  | class Derived1 : public Base { | 
|  | public: | 
|  | int j; | 
|  | }; | 
|  |  | 
|  | class Derived2 : public Derived1 { | 
|  | public: | 
|  | virtual int f() { | 
|  | clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | 
|  | return i + j; | 
|  | } | 
|  | }; | 
|  |  | 
|  | void testXXX() { | 
|  | Derived1 *d1p = reinterpret_cast<Derived1*>(new Derived2); | 
|  | d1p->i = 1; | 
|  | d1p->j = 2; | 
|  | clang_analyzer_eval(d1p->f() == 3); // expected-warning{{TRUE}} | 
|  | } | 
|  | } | 
|  |  | 
|  | namespace VirtualInDerived { | 
|  | class Base { | 
|  | public: | 
|  | int i; | 
|  | }; | 
|  |  | 
|  | class Derived1 : public Base { | 
|  | public: | 
|  | virtual int f() = 0; | 
|  | int j; | 
|  | }; | 
|  |  | 
|  | class Derived2 : public Derived1 { | 
|  | public: | 
|  | virtual int f() { | 
|  | clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | 
|  | return i + j; | 
|  | } | 
|  | }; | 
|  |  | 
|  | void test() { | 
|  | Derived1 *d1p = reinterpret_cast<Derived1*>(new Derived2); | 
|  | d1p->i = 1; | 
|  | d1p->j = 2; | 
|  | clang_analyzer_eval(d1p->f() == 3); // expected-warning{{TRUE}} | 
|  | } | 
|  | } | 
|  |  | 
|  | namespace NoCast { | 
|  | class Base { | 
|  | public: | 
|  | int i; | 
|  | }; | 
|  |  | 
|  | class Derived1 : public Base { | 
|  | public: | 
|  | virtual int f() = 0; | 
|  | int j; | 
|  | }; | 
|  |  | 
|  | class Derived2 : public Derived1 { | 
|  | public: | 
|  | virtual int f() { | 
|  | clang_analyzer_checkInlined(true); // expected-warning{{TRUE}} | 
|  | return i + j; | 
|  | } | 
|  | }; | 
|  |  | 
|  | void test() { | 
|  | Derived1 *d1p = new Derived2; | 
|  | d1p->i = 1; | 
|  | d1p->j = 2; | 
|  | clang_analyzer_eval(d1p->f() == 3); // expected-warning{{TRUE}} | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | namespace Bug16309 { | 
|  | struct Incomplete; | 
|  |  | 
|  | struct Base { virtual ~Base(); }; | 
|  |  | 
|  | struct Derived : public Base { int x; }; | 
|  |  | 
|  | void* f(Incomplete *i) { | 
|  | Base *b = reinterpret_cast<Base *>(i); | 
|  | // This used to crash because of the reinterpret_cast above. | 
|  | Derived *d = dynamic_cast<Derived *>(b); | 
|  | return d; | 
|  | } | 
|  |  | 
|  | // And check that reinterpret+dynamic casts work correctly after the fix. | 
|  | void g() { | 
|  | Derived d; | 
|  | d.x = 47; | 
|  | Base *b = &d; | 
|  | Incomplete *i = reinterpret_cast<Incomplete *>(b); | 
|  | Base *b2 = reinterpret_cast<Base *>(i); | 
|  | Derived *d2 = dynamic_cast<Derived *>(b2); | 
|  | clang_analyzer_eval(d2->x == 47); // expected-warning{{TRUE}} | 
|  | } | 
|  | } |