Files
clang-p2996/clang/test/Analysis/ctor.mm
Balazs Benics f8643a9b31 [analyzer] Prefer wrapping SymbolicRegions by ElementRegions
It turns out that in certain cases `SymbolRegions` are wrapped by
`ElementRegions`; in others, it's not. This discrepancy can cause the
analyzer not to recognize if the two regions are actually referring to
the same entity, which then can lead to unreachable paths discovered.

Consider this example:

```lang=C++
struct Node { int* ptr; };
void with_structs(Node* n1) {
  Node c = *n1; // copy
  Node* n2 = &c;
  clang_analyzer_dump(*n1); // lazy...
  clang_analyzer_dump(*n2); // lazy...
  clang_analyzer_dump(n1->ptr); // rval(n1->ptr): reg_$2<int * SymRegion{reg_$0<struct Node * n1>}.ptr>
  clang_analyzer_dump(n2->ptr); // rval(n2->ptr): reg_$1<int * Element{SymRegion{reg_$0<struct Node * n1>},0 S64b,struct Node}.ptr>
  clang_analyzer_eval(n1->ptr != n2->ptr); // UNKNOWN, bad!
  (void)(*n1);
  (void)(*n2);
}
```

The copy of `n1` will insert a new binding to the store; but for doing
that it actually must create a `TypedValueRegion` which it could pass to
the `LazyCompoundVal`. Since the memregion in question is a
`SymbolicRegion` - which is untyped, it needs to first wrap it into an
`ElementRegion` basically implementing this untyped -> typed conversion
for the sake of passing it to the `LazyCompoundVal`.
So, this is why we have `Element{SymRegion{.}, 0,struct Node}` for `n1`.

The problem appears if the analyzer evaluates a read from the expression
`n1->ptr`. The same logic won't apply for `SymbolRegionValues`, since
they accept raw `SubRegions`, hence the `SymbolicRegion` won't be
wrapped into an `ElementRegion` in that case.

Later when we arrive at the equality comparison, we cannot prove that
they are equal.

For more details check the corresponding thread on discourse:
https://discourse.llvm.org/t/are-symbolicregions-really-untyped/64406

---

In this patch, I'm eagerly wrapping each `SymbolicRegion` by an
`ElementRegion`; basically canonicalizing to this form.
It seems reasonable to do so since any object can be thought of as a single
array of that object; so this should not make much of a difference.

The tests also underpin this assumption, as only a few were broken by
this change; and actually fixed a FIXME along the way.

About the second example, which does the same copy operation - but on
the heap - it will be fixed by the next patch.

Reviewed By: martong

Differential Revision: https://reviews.llvm.org/D132142
2022-09-13 08:58:46 +02:00

862 lines
22 KiB
Plaintext

// RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -DI386 -analyzer-checker=core,debug.ExprInspection -fobjc-arc -analyzer-config c++-inlining=constructors -Wno-null-dereference -std=c++11 -verify -analyzer-config eagerly-assume=false %s
// RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -DI386 -analyzer-checker=core,debug.ExprInspection -fobjc-arc -analyzer-config c++-inlining=constructors -Wno-null-dereference -std=c++11 -verify -DTEST_INLINABLE_ALLOCATORS -analyzer-config eagerly-assume=false %s
// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin12 -analyzer-checker=core,debug.ExprInspection -fobjc-arc -analyzer-config c++-inlining=constructors -Wno-null-dereference -std=c++11 -verify -analyzer-config eagerly-assume=false %s
// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin12 -analyzer-checker=core,debug.ExprInspection -fobjc-arc -analyzer-config c++-inlining=constructors -Wno-null-dereference -std=c++11 -verify -DTEST_INLINABLE_ALLOCATORS -analyzer-config eagerly-assume=false %s
#include "Inputs/system-header-simulator-cxx.h"
void clang_analyzer_eval(bool);
void clang_analyzer_checkInlined(bool);
// A simplified version of std::move.
template <typename T>
T &&move(T &obj) {
return static_cast<T &&>(obj);
}
struct Wrapper {
__strong id obj;
};
void test() {
Wrapper w;
// force a diagnostic
*(char *)0 = 1; // expected-warning{{Dereference of null pointer}}
}
struct IntWrapper {
int x;
};
void testCopyConstructor() {
IntWrapper a;
a.x = 42;
IntWrapper b(a);
clang_analyzer_eval(b.x == 42); // expected-warning{{TRUE}}
}
struct NonPODIntWrapper {
int x;
virtual int get();
};
void testNonPODCopyConstructor() {
NonPODIntWrapper a;
a.x = 42;
NonPODIntWrapper b(a);
clang_analyzer_eval(b.x == 42); // expected-warning{{TRUE}}
}
namespace ConstructorVirtualCalls {
class A {
public:
int *out1, *out2, *out3;
virtual int get() { return 1; }
A(int *out1) {
*out1 = get();
}
};
class B : public A {
public:
virtual int get() { return 2; }
B(int *out1, int *out2) : A(out1) {
*out2 = get();
}
};
class C : public B {
public:
virtual int get() { return 3; }
C(int *out1, int *out2, int *out3) : B(out1, out2) {
*out3 = get();
}
};
void test() {
int a, b, c;
C obj(&a, &b, &c);
clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(b == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(c == 3); // expected-warning{{TRUE}}
clang_analyzer_eval(obj.get() == 3); // expected-warning{{TRUE}}
// Correctness check for devirtualization.
A *base = &obj;
clang_analyzer_eval(base->get() == 3); // expected-warning{{TRUE}}
}
}
namespace TemporaryConstructor {
class BoolWrapper {
public:
BoolWrapper() {
clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
value = true;
}
bool value;
};
void test() {
// PR13717 - Don't crash when a CXXTemporaryObjectExpr is inlined.
if (BoolWrapper().value)
return;
}
}
namespace ConstructorUsedAsRValue {
using TemporaryConstructor::BoolWrapper;
bool extractValue(BoolWrapper b) {
return b.value;
}
void test() {
bool result = extractValue(BoolWrapper());
clang_analyzer_eval(result); // expected-warning{{TRUE}}
}
}
namespace PODUninitialized {
class POD {
public:
int x, y;
};
class PODWrapper {
public:
POD p;
};
class NonPOD {
public:
int x, y;
NonPOD() {}
NonPOD(const NonPOD &Other)
: x(Other.x), y(Other.y) // expected-warning {{undefined}}
{
}
NonPOD(NonPOD &&Other)
: x(Other.x), y(Other.y) // expected-warning {{undefined}}
{
}
NonPOD &operator=(const NonPOD &Other)
{
x = Other.x;
y = Other.y; // expected-warning {{undefined}}
return *this;
}
NonPOD &operator=(NonPOD &&Other)
{
x = Other.x;
y = Other.y; // expected-warning {{undefined}}
return *this;
}
};
class NonPODWrapper {
public:
class Inner {
public:
int x, y;
Inner() {}
Inner(const Inner &Other)
: x(Other.x), y(Other.y) // expected-warning {{undefined}}
{
}
Inner(Inner &&Other)
: x(Other.x), y(Other.y) // expected-warning {{undefined}}
{
}
Inner &operator=(const Inner &Other)
{
x = Other.x; // expected-warning {{undefined}}
y = Other.y;
return *this;
}
Inner &operator=(Inner &&Other)
{
x = Other.x; // expected-warning {{undefined}}
y = Other.y;
return *this;
}
};
Inner p;
};
void testPOD(const POD &pp) {
POD p;
p.x = 1;
POD p2 = p; // no-warning
clang_analyzer_eval(p2.x == 1); // expected-warning{{TRUE}}
POD p3 = move(p); // no-warning
clang_analyzer_eval(p3.x == 1); // expected-warning{{TRUE}}
// Use rvalues as well.
clang_analyzer_eval(POD(p3).x == 1); // expected-warning{{TRUE}}
// Copy from symbolic references correctly.
POD p4 = pp;
// Make sure that p4.x contains a symbol after copy.
if (p4.x > 0)
clang_analyzer_eval(p4.x > 0); // expected-warning{{TRUE}}
clang_analyzer_eval(pp.x == p4.x); // expected-warning{{TRUE}}
PODWrapper w;
w.p.y = 1;
PODWrapper w2 = w; // no-warning
clang_analyzer_eval(w2.p.y == 1); // expected-warning{{TRUE}}
PODWrapper w3 = move(w); // no-warning
clang_analyzer_eval(w3.p.y == 1); // expected-warning{{TRUE}}
// Use rvalues as well.
clang_analyzer_eval(PODWrapper(w3).p.y == 1); // expected-warning{{TRUE}}
}
void testNonPOD() {
NonPOD p;
p.x = 1;
NonPOD p2 = p;
}
void testNonPODMove() {
NonPOD p;
p.x = 1;
NonPOD p2 = move(p);
}
void testNonPODWrapper() {
NonPODWrapper w;
w.p.y = 1;
NonPODWrapper w2 = w;
}
void testNonPODWrapperMove() {
NonPODWrapper w;
w.p.y = 1;
NonPODWrapper w2 = move(w);
}
// Not strictly about constructors, but trivial assignment operators should
// essentially work the same way.
namespace AssignmentOperator {
void testPOD() {
POD p;
p.x = 1;
POD p2;
p2 = p; // no-warning
clang_analyzer_eval(p2.x == 1); // expected-warning{{TRUE}}
POD p3;
p3 = move(p); // no-warning
clang_analyzer_eval(p3.x == 1); // expected-warning{{TRUE}}
PODWrapper w;
w.p.y = 1;
PODWrapper w2;
w2 = w; // no-warning
clang_analyzer_eval(w2.p.y == 1); // expected-warning{{TRUE}}
PODWrapper w3;
w3 = move(w); // no-warning
clang_analyzer_eval(w3.p.y == 1); // expected-warning{{TRUE}}
}
void testReturnValue() {
POD p;
p.x = 1;
POD p2;
clang_analyzer_eval(&(p2 = p) == &p2); // expected-warning{{TRUE}}
PODWrapper w;
w.p.y = 1;
PODWrapper w2;
clang_analyzer_eval(&(w2 = w) == &w2); // expected-warning{{TRUE}}
}
void testNonPOD() {
NonPOD p;
p.x = 1;
NonPOD p2;
p2 = p;
}
void testNonPODMove() {
NonPOD p;
p.x = 1;
NonPOD p2;
p2 = move(p);
}
void testNonPODWrapper() {
NonPODWrapper w;
w.p.y = 1;
NonPODWrapper w2;
w2 = w;
}
void testNonPODWrapperMove() {
NonPODWrapper w;
w.p.y = 1;
NonPODWrapper w2;
w2 = move(w);
}
}
}
namespace ArrayMembers {
struct Primitive {
int values[3];
};
void testPrimitive() {
Primitive a = { { 1, 2, 3 } };
clang_analyzer_eval(a.values[0] == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[1] == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[2] == 3); // expected-warning{{TRUE}}
Primitive b = a;
clang_analyzer_eval(b.values[0] == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(b.values[1] == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(b.values[2] == 3); // expected-warning{{TRUE}}
Primitive c;
c = b;
clang_analyzer_eval(c.values[0] == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(c.values[1] == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(c.values[2] == 3); // expected-warning{{TRUE}}
}
struct NestedPrimitive {
int values[2][3];
};
void testNestedPrimitive() {
NestedPrimitive a = { { { 0, 0, 0 }, { 1, 2, 3 } } };
clang_analyzer_eval(a.values[1][0] == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[1][1] == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[1][2] == 3); // expected-warning{{TRUE}}
NestedPrimitive b = a;
clang_analyzer_eval(b.values[1][0] == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(b.values[1][1] == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(b.values[1][2] == 3); // expected-warning{{TRUE}}
NestedPrimitive c;
c = b;
clang_analyzer_eval(c.values[1][0] == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(c.values[1][1] == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(c.values[1][2] == 3); // expected-warning{{TRUE}}
}
struct POD {
IntWrapper values[3];
};
void testPOD() {
POD a = { { { 1 }, { 2 }, { 3 } } };
clang_analyzer_eval(a.values[0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[2].x == 3); // expected-warning{{TRUE}}
POD b = a;
clang_analyzer_eval(b.values[0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(b.values[1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(b.values[2].x == 3); // expected-warning{{TRUE}}
POD c;
c = b;
clang_analyzer_eval(c.values[0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(c.values[1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(c.values[2].x == 3); // expected-warning{{TRUE}}
}
struct NestedPOD {
IntWrapper values[2][3];
};
void testNestedPOD() {
NestedPOD a = { { { { 0 }, { 0 }, { 0 } }, { { 1 }, { 2 }, { 3 } } } };
clang_analyzer_eval(a.values[1][0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[1][1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[1][2].x == 3); // expected-warning{{TRUE}}
NestedPOD b = a;
clang_analyzer_eval(b.values[1][0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(b.values[1][1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(b.values[1][2].x == 3); // expected-warning{{TRUE}}
NestedPOD c;
c = b;
clang_analyzer_eval(c.values[1][0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(c.values[1][1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(c.values[1][2].x == 3); // expected-warning{{TRUE}}
}
struct NonPOD {
NonPODIntWrapper values[3];
};
void testNonPOD() {
NonPOD a;
a.values[0].x = 1;
a.values[1].x = 2;
a.values[2].x = 3;
clang_analyzer_eval(a.values[0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[2].x == 3); // expected-warning{{TRUE}}
NonPOD b = a;
clang_analyzer_eval(b.values[0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(b.values[1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(b.values[2].x == 3); // expected-warning{{TRUE}}
NonPOD c;
c = b;
clang_analyzer_eval(c.values[0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(c.values[1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(c.values[2].x == 3); // expected-warning{{TRUE}}
}
struct NestedNonPOD {
NonPODIntWrapper values[2][3];
};
void testNestedNonPOD() {
NestedNonPOD a;
a.values[0][0].x = 0;
a.values[0][1].x = 0;
a.values[0][2].x = 0;
a.values[1][0].x = 1;
a.values[1][1].x = 2;
a.values[1][2].x = 3;
clang_analyzer_eval(a.values[1][0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[1][1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[1][2].x == 3); // expected-warning{{TRUE}}
NestedNonPOD b = a;
clang_analyzer_eval(b.values[1][0].x == 1); // expected-warning{{UNKNOWN}}
clang_analyzer_eval(b.values[1][1].x == 2); // expected-warning{{UNKNOWN}}
clang_analyzer_eval(b.values[1][2].x == 3); // expected-warning{{UNKNOWN}}
NestedNonPOD c;
c = b;
clang_analyzer_eval(c.values[1][0].x == 1); // expected-warning{{UNKNOWN}}
clang_analyzer_eval(c.values[1][1].x == 2); // expected-warning{{UNKNOWN}}
clang_analyzer_eval(c.values[1][2].x == 3); // expected-warning{{UNKNOWN}}
}
struct NonPODDefaulted {
NonPODIntWrapper values[3];
NonPODDefaulted() = default;
NonPODDefaulted(const NonPODDefaulted &) = default;
NonPODDefaulted &operator=(const NonPODDefaulted &) = default;
};
void testNonPODDefaulted() {
NonPODDefaulted a;
a.values[0].x = 1;
a.values[1].x = 2;
a.values[2].x = 3;
clang_analyzer_eval(a.values[0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(a.values[2].x == 3); // expected-warning{{TRUE}}
NonPODDefaulted b = a;
clang_analyzer_eval(b.values[0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(b.values[1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(b.values[2].x == 3); // expected-warning{{TRUE}}
NonPODDefaulted c;
c = b;
clang_analyzer_eval(c.values[0].x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(c.values[1].x == 2); // expected-warning{{TRUE}}
clang_analyzer_eval(c.values[2].x == 3); // expected-warning{{TRUE}}
}
};
namespace VirtualInheritance {
int counter;
struct base {
base() {
++counter;
}
};
struct virtual_subclass : public virtual base {
virtual_subclass() {}
};
struct double_subclass : public virtual_subclass {
double_subclass() {}
};
void test() {
counter = 0;
double_subclass obj;
clang_analyzer_eval(counter == 1); // expected-warning{{TRUE}}
}
struct double_virtual_subclass : public virtual virtual_subclass {
double_virtual_subclass() {}
};
void testVirtual() {
counter = 0;
double_virtual_subclass obj;
clang_analyzer_eval(counter == 1); // expected-warning{{TRUE}}
}
}
namespace ZeroInitialization {
struct raw_pair {
int p1;
int p2;
};
void testVarDecl() {
raw_pair p{};
clang_analyzer_eval(p.p1 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(p.p2 == 0); // expected-warning{{TRUE}}
}
void testTemporary() {
clang_analyzer_eval(raw_pair().p1 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(raw_pair().p2 == 0); // expected-warning{{TRUE}}
}
void testArray() {
raw_pair p[2] = {};
clang_analyzer_eval(p[0].p1 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(p[0].p2 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(p[1].p1 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(p[1].p2 == 0); // expected-warning{{TRUE}}
}
void testNew() {
raw_pair *pp = new raw_pair();
clang_analyzer_eval(pp->p1 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(pp->p2 == 0); // expected-warning{{TRUE}}
}
void testArrayNew() {
raw_pair *p = new raw_pair[2]();
clang_analyzer_eval(p[0].p1 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(p[0].p2 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(p[1].p1 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(p[1].p2 == 0); // expected-warning{{TRUE}}
}
struct initializing_pair {
public:
int x;
raw_pair y;
initializing_pair() : x(), y() {}
};
void testFieldInitializers() {
initializing_pair p;
clang_analyzer_eval(p.x == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(p.y.p1 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(p.y.p2 == 0); // expected-warning{{TRUE}}
}
struct subclass : public raw_pair {
subclass() = default;
};
void testSubclass() {
subclass p;
clang_analyzer_eval(p.p1 == 0); // expected-warning{{garbage}}
}
struct initializing_subclass : public raw_pair {
initializing_subclass() : raw_pair() {}
};
void testInitializingSubclass() {
initializing_subclass p;
clang_analyzer_eval(p.p1 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(p.p2 == 0); // expected-warning{{TRUE}}
}
struct pair_wrapper {
pair_wrapper() : p() {}
raw_pair p;
};
struct virtual_subclass : public virtual pair_wrapper {
virtual_subclass() {}
};
struct double_virtual_subclass : public virtual_subclass {
double_virtual_subclass() {
// This previously caused a crash because the pair_wrapper subobject was
// initialized twice.
}
};
class Empty {
public:
static int glob;
Empty(); // No body.
Empty(int x); // Body below.
};
class PairContainer : public Empty {
public:
raw_pair p;
int q;
PairContainer() : Empty(), p() {
// This previously caused a crash because the empty base class looked
// like an initialization of 'p'.
}
PairContainer(int) : Empty(), p() {
// Test inlining something else here.
}
PairContainer(double): Empty(1), p() {
clang_analyzer_eval(p.p1 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(p.p2 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(q == 1); // expected-warning{{TRUE}}
// This one's indeed UNKNOWN. Definitely not TRUE.
clang_analyzer_eval(p.p2 == glob); // expected-warning{{UNKNOWN}}
}
};
Empty::Empty(int x) {
static_cast<PairContainer *>(this)->p.p1 = x;
static_cast<PairContainer *>(this)->q = x;
// Our static member will store the old garbage values of fields that aren't
// yet initialized. It's not certainly garbage though (i.e. the constructor
// could have been called on an initialized piece of memory), so no
// uninitialized value warning here, and it should be a symbol, not
// undefined value, for later comparison.
glob = static_cast<PairContainer *>(this)->p.p2;
}
class Empty2 {
public:
static int glob_p1, glob_p2;
Empty2(); // Body below.
};
class PairDoubleEmptyContainer: public Empty, public Empty2 {
public:
raw_pair p;
PairDoubleEmptyContainer(): Empty(), Empty2(), p() {
clang_analyzer_eval(p.p1 == 0); // expected-warning{{TRUE}}
clang_analyzer_eval(p.p2 == 0); // expected-warning{{TRUE}}
// This is indeed UNKNOWN.
clang_analyzer_eval(p.p1 == glob_p1); // expected-warning{{UNKNOWN}}
clang_analyzer_eval(p.p2 == glob_p2); // expected-warning{{UNKNOWN}}
}
};
Empty2::Empty2() {
glob_p1 = static_cast<PairDoubleEmptyContainer *>(this)->p.p1;
glob_p2 = static_cast<PairDoubleEmptyContainer *>(this)->p.p2;
}
class PairContainerContainer {
int padding;
PairContainer pc;
public:
PairContainerContainer() : pc(1) {}
};
}
namespace InitializerList {
struct List {
bool usedInitializerList;
List() : usedInitializerList(false) {}
List(std::initializer_list<int>) : usedInitializerList(true) {}
};
void testStatic() {
List defaultCtor;
clang_analyzer_eval(!defaultCtor.usedInitializerList); // expected-warning{{TRUE}}
List list{1, 2};
clang_analyzer_eval(list.usedInitializerList); // expected-warning{{TRUE}}
}
void testDynamic() {
List *list = new List{1, 2};
clang_analyzer_eval(list->usedInitializerList); // expected-warning{{TRUE}}
}
}
namespace PR19579 {
class C {};
void f() {
C();
int a;
extern void use(int);
use(a); // expected-warning{{uninitialized}}
}
void g() {
struct S {
C c;
int i;
};
// This order triggers the initialization of the inner "a" after the
// constructor for "C" is run, which used to confuse the analyzer
// (is "C()" the initialization of "a"?).
struct S s = {
C(),
({
int a, b = 0;
0;
})
};
}
}
namespace NoCrashOnEmptyBaseOptimization {
struct NonEmptyBase {
int X;
explicit NonEmptyBase(int X) : X(X) {}
};
struct EmptyBase {};
struct S : NonEmptyBase, EmptyBase {
S() : NonEmptyBase(0), EmptyBase() {}
};
void testSCtorNoCrash() {
S s;
}
}
namespace EmptyBaseAssign {
struct B1 {};
struct B2 { int x; };
struct D: public B1, public B2 {
const D &operator=(const D &d) {
*((B2 *)this) = d;
*((B1 *)this) = d;
return *this;
}
};
void test() {
D d1;
d1.x = 1;
D d2;
d2 = d1;
clang_analyzer_eval(d2.x == 1); // expected-warning{{TRUE}}
}
}
namespace vbase_zero_init {
class A {
virtual void foo();
};
class B {
virtual void bar();
public:
static int glob_y, glob_z, glob_w;
int x;
B(); // Body below.
};
class C : virtual public A {
public:
int y;
};
class D : public B, public C {
public:
// 'z', unlike 'w', resides in an area that would have been within padding of
// base class 'C' if it wasn't part of 'D', but only on 64-bit systems.
int z, w;
// Initialization order: A(), B(), C().
D() : A(), C() {
clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(y == 0); // expected-warning{{TRUE}}
#ifdef I386
clang_analyzer_eval(z == 3); // expected-warning{{TRUE}}
#else
// FIXME: Should be TRUE. Initialized in B().
clang_analyzer_eval(z == 3); // expected-warning{{UNKNOWN}}
#endif
clang_analyzer_eval(w == 4); // expected-warning{{TRUE}}
// FIXME: Should be UNKNOWN. Changed in B() since glob_y was assigned.
clang_analyzer_eval(y == glob_y); // expected-warning{{TRUE}}
#ifdef I386
clang_analyzer_eval(z == glob_z); // expected-warning{{UNKNOWN}}
#else
// FIXME: Should be UNKNOWN. Changed in B() since glob_z was assigned.
clang_analyzer_eval(z == glob_z); // expected-warning{{TRUE}}
#endif
clang_analyzer_eval(w == glob_w); // expected-warning{{UNKNOWN}}
} // no-crash
};
B::B() : x(1) {
// Our static members will store the old garbage values of fields that aren't
// yet initialized. These aren't certainly garbage though (i.e. the
// constructor could have been called on an initialized piece of memory),
// so no uninitialized value warning here, and these should be symbols, not
// undefined values, for later comparison.
glob_y = static_cast<D *>(this)->y;
glob_z = static_cast<D *>(this)->z;
glob_w = static_cast<D *>(this)->w;
static_cast<D *>(this)->y = 2;
static_cast<D *>(this)->z = 3;
static_cast<D *>(this)->w = 4;
}
}