439 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			439 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config ipa=inlining -verify %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()
 | |
| {
 | |
|   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}}
 | |
|   }
 | |
| }
 | |
| 
 | |
| namespace OperatorNew {
 | |
|   class IntWrapper {
 | |
|   public:
 | |
|     int value;
 | |
| 
 | |
|     IntWrapper(int input) : value(input) {
 | |
|       // We don't want this constructor to be inlined unless we can actually
 | |
|       // use the proper region for operator new.
 | |
|       // See PR12014 and <rdar://problem/12180598>.
 | |
|       clang_analyzer_checkInlined(false); // no-warning
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   void test() {
 | |
|     IntWrapper *obj = new IntWrapper(42);
 | |
|     // should be TRUE
 | |
|     clang_analyzer_eval(obj->value == 42); // expected-warning{{UNKNOWN}}
 | |
|     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}}
 | |
| 
 | |
|     // should be TRUE
 | |
|     clang_analyzer_eval(obj->value == 42); // expected-warning{{UNKNOWN}}
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 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}}
 | |
|   }
 | |
| }
 | 
