зеркало из https://github.com/microsoft/clang-1.git
[analyzer] Be more forgiving about calling methods on struct rvalues.
The problem is that the value of 'this' in a C++ member function call should always be a region (or NULL). However, if the object is an rvalue, it has no associated region (only a conjured symbol or LazyCompoundVal). For now, we handle this in two ways: 1) Actually respect MaterializeTemporaryExpr. Before, it was relying on CXXConstructExpr to create temporary regions for all struct values. Now it just does the right thing: if the value is not in a temporary region, create one. 2) Have CallEvent recognize the case where its 'this' pointer is a non-region, and just return UnknownVal to keep from confusing clients. The long-term problem is being tracked internally in <rdar://problem/12137950>, but this makes many test cases pass. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@163220 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
4e45dba1c0
Коммит
6ebea89be2
|
@ -504,13 +504,7 @@ public:
|
|||
virtual const Expr *getCXXThisExpr() const { return 0; }
|
||||
|
||||
/// \brief Returns the value of the implicit 'this' object.
|
||||
virtual SVal getCXXThisVal() const {
|
||||
const Expr *Base = getCXXThisExpr();
|
||||
// FIXME: This doesn't handle an overloaded ->* operator.
|
||||
if (!Base)
|
||||
return UnknownVal();
|
||||
return getSVal(Base);
|
||||
}
|
||||
virtual SVal getCXXThisVal() const;
|
||||
|
||||
virtual const FunctionDecl *getDecl() const;
|
||||
|
||||
|
|
|
@ -384,6 +384,25 @@ void CXXInstanceCall::getExtraInvalidatedRegions(RegionList &Regions) const {
|
|||
Regions.push_back(R);
|
||||
}
|
||||
|
||||
SVal CXXInstanceCall::getCXXThisVal() const {
|
||||
const Expr *Base = getCXXThisExpr();
|
||||
// FIXME: This doesn't handle an overloaded ->* operator.
|
||||
if (!Base)
|
||||
return UnknownVal();
|
||||
|
||||
SVal ThisVal = getSVal(Base);
|
||||
|
||||
// FIXME: This is only necessary because we can call member functions on
|
||||
// struct rvalues, which do not have regions we can use for a 'this' pointer.
|
||||
// Ideally this should eventually be changed to an assert, i.e. all
|
||||
// non-Unknown, non-null 'this' values should be loc::MemRegionVals.
|
||||
if (isa<DefinedSVal>(ThisVal))
|
||||
if (!ThisVal.getAsRegion() && !ThisVal.isConstant())
|
||||
return UnknownVal();
|
||||
|
||||
return ThisVal;
|
||||
}
|
||||
|
||||
|
||||
RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
|
||||
// Do we have a decl at all?
|
||||
|
|
|
@ -34,9 +34,10 @@ void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
|
|||
// the expression to the location of the object.
|
||||
SVal V = state->getSVal(tempExpr, LCtx);
|
||||
|
||||
// If the object is a record, the constructor will have already created
|
||||
// a temporary object region. If it is not, we need to copy the value over.
|
||||
if (!ME->getType()->isRecordType()) {
|
||||
// If the value is already a CXXTempObjectRegion, it is fine as it is.
|
||||
// Otherwise, create a new CXXTempObjectRegion, and copy the value into it.
|
||||
const MemRegion *MR = V.getAsRegion();
|
||||
if (!MR || !isa<CXXTempObjectRegion>(MR)) {
|
||||
const MemRegion *R =
|
||||
svalBuilder.getRegionManager().getCXXTempObjectRegion(ME, LCtx);
|
||||
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
// 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
|
||||
|
||||
void clang_analyzer_eval(int);
|
||||
|
||||
struct S {
|
||||
int field;
|
||||
|
||||
#if __cplusplus
|
||||
const struct S *getThis() const { return this; }
|
||||
#endif
|
||||
};
|
||||
|
||||
struct S getS();
|
||||
|
||||
|
||||
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}}
|
||||
#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}}
|
||||
#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}}
|
||||
}
|
||||
|
||||
|
||||
int getConstrainedFieldRef(const S &s) {
|
||||
if (s.field != 42) return 42;
|
||||
return s.field;
|
||||
}
|
||||
|
||||
bool checkThis(const S &s) {
|
||||
return s.getThis() == &s;
|
||||
}
|
||||
|
||||
void testReferenceArgument() {
|
||||
clang_analyzer_eval(getConstrainedFieldRef(getS()) == 42); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(checkThis(getS())); // expected-warning{{TRUE}}
|
||||
}
|
||||
#endif
|
|
@ -116,8 +116,13 @@ void testReferenceAddress(int &x) {
|
|||
|
||||
struct S { int &x; };
|
||||
|
||||
extern S *getS();
|
||||
clang_analyzer_eval(&getS()->x != 0); // expected-warning{{TRUE}}
|
||||
// FIXME: Should be TRUE. Fields of return-by-value structs are not yet
|
||||
// symbolicated. Tracked by <rdar://problem/12137950>.
|
||||
extern S getS();
|
||||
clang_analyzer_eval(&getS().x != 0); // expected-warning{{UNKNOWN}}
|
||||
|
||||
extern S *getSP();
|
||||
clang_analyzer_eval(&getSP()->x != 0); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
|
||||
|
@ -150,10 +155,3 @@ namespace rdar11212286 {
|
|||
return *x; // should warn here!
|
||||
}
|
||||
}
|
||||
|
||||
void testReferenceFieldAddress() {
|
||||
struct S { int &x; };
|
||||
|
||||
extern S getS();
|
||||
clang_analyzer_eval(&getS().x != 0); // expected-warning{{UNKNOWN}}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче