| // RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify -analyzer-config eagerly-assume=false %s | |
| void clang_analyzer_eval(bool); | |
| void clang_analyzer_dump(int); | |
| typedef __typeof__(sizeof(int)) size_t; | |
| void *conjure(); | |
| void exit(int); | |
| struct S; | |
| S *global_s; | |
| // Recursive operator kinda placement new. | |
| void *operator new(size_t size, S *place); | |
| enum class ConstructionKind : char { | |
| Garbage, | |
| Recursive | |
| }; | |
| struct S { | |
| public: | |
| int x; | |
| S(): x(1) {} | |
| S(int y): x(y) {} | |
| S(ConstructionKind k) { | |
| switch (k) { | |
| case ConstructionKind::Recursive: { // Call one more operator new 'r'ecursively. | |
| S *s = new (nullptr) S(5); | |
| x = s->x + 1; | |
| global_s = s; | |
| return; | |
| } | |
| case ConstructionKind::Garbage: { | |
| // Leaves garbage in 'x'. | |
| } | |
| } | |
| } | |
| ~S() {} | |
| }; | |
| // Do not try this at home! | |
| void *operator new(size_t size, S *place) { | |
| if (!place) | |
| return new S(); | |
| return place; | |
| } | |
| void testThatCharConstructorIndeedYieldsGarbage() { | |
| S *s = new S(ConstructionKind::Garbage); | |
| clang_analyzer_eval(s->x == 0); // expected-warning{{The left operand of '==' is a garbage value [core.UndefinedBinaryOperatorResult]}} | |
| clang_analyzer_eval(s->x == 1); | |
| s->x += 1; | |
| delete s; | |
| } | |
| void testChainedOperatorNew() { | |
| S *s; | |
| // * Evaluate standard new. | |
| // * Evaluate constructor S(3). | |
| // * Bind value for standard new. | |
| // * Evaluate our custom new. | |
| // * Evaluate constructor S(Garbage). | |
| // * Bind value for our custom new. | |
| s = new (new S(3)) S(ConstructionKind::Garbage); | |
| clang_analyzer_eval(s->x == 3); // expected-warning{{TRUE}} | |
| // expected-warning@+9{{Potential leak of memory pointed to by 's'}} | |
| // * Evaluate standard new. | |
| // * Evaluate constructor S(Garbage). | |
| // * Bind value for standard new. | |
| // * Evaluate our custom new. | |
| // * Evaluate constructor S(4). | |
| // * Bind value for our custom new. | |
| s = new (new S(ConstructionKind::Garbage)) S(4); | |
| clang_analyzer_eval(s->x == 4); // expected-warning{{TRUE}} | |
| delete s; | |
| // -> Enter our custom new (nullptr). | |
| // * Evaluate standard new. | |
| // * Inline constructor S(). | |
| // * Bind value for standard new. | |
| // <- Exit our custom new (nullptr). | |
| // * Evaluate constructor S(Garbage). | |
| // * Bind value for our custom new. | |
| s = new (nullptr) S(ConstructionKind::Garbage); | |
| clang_analyzer_eval(s->x == 1); // expected-warning{{TRUE}} | |
| delete s; | |
| // -> Enter our custom new (nullptr). | |
| // * Evaluate standard new. | |
| // * Inline constructor S(). | |
| // * Bind value for standard new. | |
| // <- Exit our custom new (nullptr). | |
| // -> Enter constructor S(Recursive). | |
| // -> Enter our custom new (nullptr). | |
| // * Evaluate standard new. | |
| // * Inline constructor S(). | |
| // * Bind value for standard new. | |
| // <- Exit our custom new (nullptr). | |
| // * Evaluate constructor S(5). | |
| // * Bind value for our custom new (nullptr). | |
| // * Assign that value to global_s. | |
| // <- Exit constructor S(Recursive). | |
| // * Bind value for our custom new (nullptr). | |
| global_s = nullptr; | |
| s = new (nullptr) S(ConstructionKind::Recursive); | |
| clang_analyzer_eval(global_s); // expected-warning{{TRUE}} | |
| clang_analyzer_eval(global_s->x == 5); // expected-warning{{TRUE}} | |
| clang_analyzer_eval(s->x == 6); // expected-warning{{TRUE}} | |
| delete s; | |
| } |