| // 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}} | |
| } | |
| } |