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