444 lines
10 KiB
C++
444 lines
10 KiB
C++
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config ipa=inlining -analyzer-config c++-allocator-inlining=true -verify -analyzer-config eagerly-assume=false %s
|
|
|
|
void clang_analyzer_eval(bool);
|
|
void clang_analyzer_checkInlined(bool);
|
|
|
|
typedef __typeof__(sizeof(int)) size_t;
|
|
extern "C" void *malloc(size_t);
|
|
|
|
// This is the standard placement new.
|
|
inline void* operator new(size_t, void* __p) throw()
|
|
{
|
|
clang_analyzer_checkInlined(true);// expected-warning{{TRUE}}
|
|
return __p;
|
|
}
|
|
|
|
|
|
class A {
|
|
public:
|
|
int getZero() { return 0; }
|
|
virtual int getNum() { return 0; }
|
|
};
|
|
|
|
void test(A &a) {
|
|
clang_analyzer_eval(a.getZero() == 0); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(a.getNum() == 0); // expected-warning{{UNKNOWN}}
|
|
|
|
A copy(a);
|
|
clang_analyzer_eval(copy.getZero() == 0); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(copy.getNum() == 0); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
|
|
class One : public A {
|
|
public:
|
|
virtual int getNum() { return 1; }
|
|
};
|
|
|
|
void testPathSensitivity(int x) {
|
|
A a;
|
|
One b;
|
|
|
|
A *ptr;
|
|
switch (x) {
|
|
case 0:
|
|
ptr = &a;
|
|
break;
|
|
case 1:
|
|
ptr = &b;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
// This should be true on both branches.
|
|
clang_analyzer_eval(ptr->getNum() == x); // expected-warning {{TRUE}}
|
|
}
|
|
|
|
|
|
namespace PureVirtualParent {
|
|
class Parent {
|
|
public:
|
|
virtual int pureVirtual() const = 0;
|
|
int callVirtual() const {
|
|
return pureVirtual();
|
|
}
|
|
};
|
|
|
|
class Child : public Parent {
|
|
public:
|
|
virtual int pureVirtual() const {
|
|
clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
|
|
return 42;
|
|
}
|
|
};
|
|
|
|
void testVirtual() {
|
|
Child x;
|
|
|
|
clang_analyzer_eval(x.pureVirtual() == 42); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(x.callVirtual() == 42); // expected-warning{{TRUE}}
|
|
}
|
|
}
|
|
|
|
|
|
namespace PR13569 {
|
|
class Parent {
|
|
protected:
|
|
int m_parent;
|
|
virtual int impl() const = 0;
|
|
|
|
Parent() : m_parent(0) {}
|
|
|
|
public:
|
|
int interface() const {
|
|
clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
|
|
return impl();
|
|
}
|
|
};
|
|
|
|
class Child : public Parent {
|
|
protected:
|
|
virtual int impl() const {
|
|
clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
|
|
return m_parent + m_child;
|
|
}
|
|
|
|
public:
|
|
Child() : m_child(0) {}
|
|
|
|
int m_child;
|
|
};
|
|
|
|
void testVirtual() {
|
|
Child x;
|
|
x.m_child = 42;
|
|
|
|
// Don't crash when inlining and devirtualizing.
|
|
x.interface();
|
|
}
|
|
|
|
|
|
class Grandchild : public Child {};
|
|
|
|
void testDevirtualizeToMiddle() {
|
|
Grandchild x;
|
|
x.m_child = 42;
|
|
|
|
// Don't crash when inlining and devirtualizing.
|
|
x.interface();
|
|
}
|
|
}
|
|
|
|
namespace PR13569_virtual {
|
|
class Parent {
|
|
protected:
|
|
int m_parent;
|
|
virtual int impl() const = 0;
|
|
|
|
Parent() : m_parent(0) {}
|
|
|
|
public:
|
|
int interface() const {
|
|
clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
|
|
return impl();
|
|
}
|
|
};
|
|
|
|
class Child : virtual public Parent {
|
|
protected:
|
|
virtual int impl() const {
|
|
clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
|
|
return m_parent + m_child;
|
|
}
|
|
|
|
public:
|
|
Child() : m_child(0) {}
|
|
|
|
int m_child;
|
|
};
|
|
|
|
void testVirtual() {
|
|
Child x;
|
|
x.m_child = 42;
|
|
|
|
// Don't crash when inlining and devirtualizing.
|
|
x.interface();
|
|
}
|
|
|
|
|
|
class Grandchild : virtual public Child {};
|
|
|
|
void testDevirtualizeToMiddle() {
|
|
Grandchild x;
|
|
x.m_child = 42;
|
|
|
|
// Don't crash when inlining and devirtualizing.
|
|
x.interface();
|
|
}
|
|
}
|
|
|
|
namespace Invalidation {
|
|
struct X {
|
|
void touch(int &x) const {
|
|
x = 0;
|
|
}
|
|
|
|
void touch2(int &x) const;
|
|
|
|
virtual void touchV(int &x) const {
|
|
x = 0;
|
|
}
|
|
|
|
virtual void touchV2(int &x) const;
|
|
|
|
int test() const {
|
|
// We were accidentally not invalidating under inlining
|
|
// at one point for virtual methods with visible definitions.
|
|
int a, b, c, d;
|
|
touch(a);
|
|
touch2(b);
|
|
touchV(c);
|
|
touchV2(d);
|
|
return a + b + c + d; // no-warning
|
|
}
|
|
};
|
|
}
|
|
|
|
namespace DefaultArgs {
|
|
int takesDefaultArgs(int i = 42) {
|
|
return -i;
|
|
}
|
|
|
|
void testFunction() {
|
|
clang_analyzer_eval(takesDefaultArgs(1) == -1); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(takesDefaultArgs() == -42); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
class Secret {
|
|
public:
|
|
static const int value = 40 + 2;
|
|
int get(int i = value) {
|
|
return i;
|
|
}
|
|
};
|
|
|
|
void testMethod() {
|
|
Secret obj;
|
|
clang_analyzer_eval(obj.get(1) == 1); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(obj.get() == 42); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(Secret::value == 42); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
enum ABC {
|
|
A = 0,
|
|
B = 1,
|
|
C = 2
|
|
};
|
|
|
|
int enumUser(ABC input = B) {
|
|
return static_cast<int>(input);
|
|
}
|
|
|
|
void testEnum() {
|
|
clang_analyzer_eval(enumUser(C) == 2); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(enumUser() == 1); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
|
|
int exprUser(int input = 2 * 4) {
|
|
return input;
|
|
}
|
|
|
|
int complicatedExprUser(int input = 2 * Secret::value) {
|
|
return input;
|
|
}
|
|
|
|
void testExprs() {
|
|
clang_analyzer_eval(exprUser(1) == 1); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(exprUser() == 8); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(complicatedExprUser(1) == 1); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(complicatedExprUser() == 84); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
int defaultReference(const int &input = 42) {
|
|
return -input;
|
|
}
|
|
int defaultReferenceZero(const int &input = 0) {
|
|
return -input;
|
|
}
|
|
|
|
void testReference() {
|
|
clang_analyzer_eval(defaultReference(1) == -1); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(defaultReference() == -42); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(defaultReferenceZero(1) == -1); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(defaultReferenceZero() == 0); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
double defaultFloatReference(const double &i = 42) {
|
|
return -i;
|
|
}
|
|
double defaultFloatReferenceZero(const double &i = 0) {
|
|
return -i;
|
|
}
|
|
|
|
void testFloatReference() {
|
|
clang_analyzer_eval(defaultFloatReference(1) == -1); // expected-warning{{UNKNOWN}}
|
|
clang_analyzer_eval(defaultFloatReference() == -42); // expected-warning{{UNKNOWN}}
|
|
|
|
clang_analyzer_eval(defaultFloatReferenceZero(1) == -1); // expected-warning{{UNKNOWN}}
|
|
clang_analyzer_eval(defaultFloatReferenceZero() == 0); // expected-warning{{UNKNOWN}}
|
|
}
|
|
|
|
char defaultString(const char *s = "abc") {
|
|
return s[1];
|
|
}
|
|
|
|
void testString() {
|
|
clang_analyzer_eval(defaultString("xyz") == 'y'); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(defaultString() == 'b'); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
const void * const void_string = "abc";
|
|
|
|
void testBitcastedString() {
|
|
clang_analyzer_eval(0 != void_string); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval('b' == ((char *)void_string)[1]); // expected-warning{{TRUE}}
|
|
}
|
|
}
|
|
|
|
namespace OperatorNew {
|
|
class IntWrapper {
|
|
public:
|
|
int value;
|
|
|
|
IntWrapper(int input) : value(input) {
|
|
clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
|
|
}
|
|
};
|
|
|
|
void test() {
|
|
IntWrapper *obj = new IntWrapper(42);
|
|
clang_analyzer_eval(obj->value == 42); // expected-warning{{TRUE}}
|
|
delete obj;
|
|
}
|
|
|
|
void testPlacement() {
|
|
IntWrapper *obj = static_cast<IntWrapper *>(malloc(sizeof(IntWrapper)));
|
|
IntWrapper *alias = new (obj) IntWrapper(42);
|
|
|
|
clang_analyzer_eval(alias == obj); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(obj->value == 42); // expected-warning{{TRUE}}
|
|
// Because malloc() was never free()d:
|
|
// expected-warning@-2{{Potential leak of memory pointed to by 'alias'}}
|
|
}
|
|
}
|
|
|
|
|
|
namespace VirtualWithSisterCasts {
|
|
// This entire set of tests exercises casts from sister classes and
|
|
// from classes outside the hierarchy, which can very much confuse
|
|
// code that uses DynamicTypeInfo or needs to construct CXXBaseObjectRegions.
|
|
// These examples used to cause crashes in +Asserts builds.
|
|
struct Parent {
|
|
virtual int foo();
|
|
int x;
|
|
};
|
|
|
|
struct A : Parent {
|
|
virtual int foo() { return 42; }
|
|
};
|
|
|
|
struct B : Parent {
|
|
virtual int foo();
|
|
};
|
|
|
|
struct Grandchild : public A {};
|
|
|
|
struct Unrelated {};
|
|
|
|
void testDowncast(Parent *b) {
|
|
A *a = (A *)(void *)b;
|
|
clang_analyzer_eval(a->foo() == 42); // expected-warning{{UNKNOWN}}
|
|
|
|
a->x = 42;
|
|
clang_analyzer_eval(a->x == 42); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
void testRelated(B *b) {
|
|
A *a = (A *)(void *)b;
|
|
clang_analyzer_eval(a->foo() == 42); // expected-warning{{UNKNOWN}}
|
|
|
|
a->x = 42;
|
|
clang_analyzer_eval(a->x == 42); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
void testUnrelated(Unrelated *b) {
|
|
A *a = (A *)(void *)b;
|
|
clang_analyzer_eval(a->foo() == 42); // expected-warning{{UNKNOWN}}
|
|
|
|
a->x = 42;
|
|
clang_analyzer_eval(a->x == 42); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
void testCastViaNew(B *b) {
|
|
Grandchild *g = new (b) Grandchild();
|
|
clang_analyzer_eval(g->foo() == 42); // expected-warning{{TRUE}}
|
|
|
|
g->x = 42;
|
|
clang_analyzer_eval(g->x == 42); // expected-warning{{TRUE}}
|
|
}
|
|
}
|
|
|
|
|
|
namespace QualifiedCalls {
|
|
void test(One *object) {
|
|
// This uses the One class from the top of the file.
|
|
clang_analyzer_eval(object->getNum() == 1); // expected-warning{{UNKNOWN}}
|
|
clang_analyzer_eval(object->One::getNum() == 1); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(object->A::getNum() == 0); // expected-warning{{TRUE}}
|
|
|
|
// getZero is non-virtual.
|
|
clang_analyzer_eval(object->getZero() == 0); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(object->One::getZero() == 0); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(object->A::getZero() == 0); // expected-warning{{TRUE}}
|
|
}
|
|
}
|
|
|
|
|
|
namespace rdar12409977 {
|
|
struct Base {
|
|
int x;
|
|
};
|
|
|
|
struct Parent : public Base {
|
|
virtual Parent *vGetThis();
|
|
Parent *getThis() { return vGetThis(); }
|
|
};
|
|
|
|
struct Child : public Parent {
|
|
virtual Child *vGetThis() { return this; }
|
|
};
|
|
|
|
void test() {
|
|
Child obj;
|
|
obj.x = 42;
|
|
|
|
// Originally, calling a devirtualized method with a covariant return type
|
|
// caused a crash because the return value had the wrong type. When we then
|
|
// go to layer a CXXBaseObjectRegion on it, the base isn't a direct base of
|
|
// the object region and we get an assertion failure.
|
|
clang_analyzer_eval(obj.getThis()->x == 42); // expected-warning{{TRUE}}
|
|
}
|
|
}
|
|
|
|
namespace bug16307 {
|
|
void one_argument(int a) { }
|
|
void call_with_less() {
|
|
reinterpret_cast<void (*)()>(one_argument)(); // expected-warning{{Function taking 1 argument is called with fewer (0)}}
|
|
}
|
|
}
|