478 lines
10 KiB
C++
478 lines
10 KiB
C++
|
// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify %s
|
||
|
// RUN: %clang_cc1 -analyze -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(); // sanity 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}}
|
||
|
}
|
||
|
}
|