
In C++, rvalues that need to have their address taken (for example, to be passed to a function by const reference) will be wrapped in a MaterializeTemporaryExpr, which lets CodeGen know to create a temporary region to store this value. However, MaterializeTemporaryExprs are /not/ created when a method is called on an rvalue struct, even though the 'this' pointer needs a valid value. CodeGen works around this by creating a temporary region anyway; now, so does the analyzer. The analyzer also does this when accessing a field of a struct rvalue. This is a little unfortunate, since the rest of the struct will soon be thrown away, but it does make things consistent with the rest of the analyzer. This allows us to bring back the assumption that all known 'this' values are Locs. This is a revised version of r164828-9, reverted in r164876-7. <rdar://problem/12137950> llvm-svn: 166120
161 lines
4.3 KiB
C++
161 lines
4.3 KiB
C++
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -verify -x c %s
|
|
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -verify -x c++ -analyzer-config c++-inlining=constructors %s
|
|
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -DINLINE -verify -x c %s
|
|
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -DINLINE -verify -x c++ -analyzer-config c++-inlining=constructors %s
|
|
|
|
void clang_analyzer_eval(int);
|
|
|
|
struct S {
|
|
int field;
|
|
|
|
#if __cplusplus
|
|
const struct S *getThis() const { return this; }
|
|
const struct S *operator +() const { return this; }
|
|
|
|
bool check() const { return this == this; }
|
|
bool operator !() const { return this != this; }
|
|
|
|
int operator *() const { return field; }
|
|
#endif
|
|
};
|
|
|
|
#if __cplusplus
|
|
const struct S *operator -(const struct S &s) { return &s; }
|
|
bool operator ~(const struct S &s) { return &s != &s; }
|
|
#endif
|
|
|
|
|
|
#ifdef INLINE
|
|
struct S getS() {
|
|
struct S s = { 42 };
|
|
return s;
|
|
}
|
|
#else
|
|
struct S getS();
|
|
#endif
|
|
|
|
|
|
void testAssignment() {
|
|
struct S s = getS();
|
|
|
|
if (s.field != 42) return;
|
|
clang_analyzer_eval(s.field == 42); // expected-warning{{TRUE}}
|
|
|
|
s.field = 0;
|
|
clang_analyzer_eval(s.field == 0); // expected-warning{{TRUE}}
|
|
|
|
#if __cplusplus
|
|
clang_analyzer_eval(s.getThis() == &s); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(+s == &s); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(-s == &s); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(s.check()); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(!s); // expected-warning{{FALSE}}
|
|
clang_analyzer_eval(~s); // expected-warning{{FALSE}}
|
|
|
|
clang_analyzer_eval(*s == 0); // expected-warning{{TRUE}}
|
|
#endif
|
|
}
|
|
|
|
|
|
void testImmediateUse() {
|
|
int x = getS().field;
|
|
|
|
if (x != 42) return;
|
|
clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
|
|
|
|
#if __cplusplus
|
|
clang_analyzer_eval((void *)getS().getThis() == (void *)&x); // expected-warning{{FALSE}}
|
|
clang_analyzer_eval((void *)+getS() == (void *)&x); // expected-warning{{FALSE}}
|
|
clang_analyzer_eval((void *)-getS() == (void *)&x); // expected-warning{{FALSE}}
|
|
|
|
clang_analyzer_eval(getS().check()); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(!getS()); // expected-warning{{FALSE}}
|
|
clang_analyzer_eval(~getS()); // expected-warning{{FALSE}}
|
|
#endif
|
|
}
|
|
|
|
int getConstrainedField(struct S s) {
|
|
if (s.field != 42) return 42;
|
|
return s.field;
|
|
}
|
|
|
|
int getAssignedField(struct S s) {
|
|
s.field = 42;
|
|
return s.field;
|
|
}
|
|
|
|
void testArgument() {
|
|
clang_analyzer_eval(getConstrainedField(getS()) == 42); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(getAssignedField(getS()) == 42); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
|
|
//--------------------
|
|
// C++-only tests
|
|
//--------------------
|
|
|
|
#if __cplusplus
|
|
void testReferenceAssignment() {
|
|
const S &s = getS();
|
|
|
|
if (s.field != 42) return;
|
|
clang_analyzer_eval(s.field == 42); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(s.getThis() == &s); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(+s == &s); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(s.check()); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(!s); // expected-warning{{FALSE}}
|
|
clang_analyzer_eval(~s); // expected-warning{{FALSE}}
|
|
|
|
clang_analyzer_eval(*s == 42); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
|
|
int getConstrainedFieldRef(const S &s) {
|
|
if (s.field != 42) return 42;
|
|
return s.field;
|
|
}
|
|
|
|
bool checkThis(const S &s) {
|
|
return s.getThis() == &s;
|
|
}
|
|
|
|
bool checkThisOp(const S &s) {
|
|
return +s == &s;
|
|
}
|
|
|
|
bool checkThisStaticOp(const S &s) {
|
|
return -s == &s;
|
|
}
|
|
|
|
void testReferenceArgument() {
|
|
clang_analyzer_eval(getConstrainedFieldRef(getS()) == 42); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(checkThis(getS())); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(checkThisOp(getS())); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(checkThisStaticOp(getS())); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
|
|
int getConstrainedFieldOp(S s) {
|
|
if (*s != 42) return 42;
|
|
return *s;
|
|
}
|
|
|
|
int getConstrainedFieldRefOp(const S &s) {
|
|
if (*s != 42) return 42;
|
|
return *s;
|
|
}
|
|
|
|
void testImmediateUseOp() {
|
|
int x = *getS();
|
|
if (x != 42) return;
|
|
clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
|
|
|
|
clang_analyzer_eval(getConstrainedFieldOp(getS()) == 42); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(getConstrainedFieldRefOp(getS()) == 42); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
#endif
|