зеркало из 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; }
|
virtual const Expr *getCXXThisExpr() const { return 0; }
|
||||||
|
|
||||||
/// \brief Returns the value of the implicit 'this' object.
|
/// \brief Returns the value of the implicit 'this' object.
|
||||||
virtual SVal getCXXThisVal() const {
|
virtual SVal getCXXThisVal() const;
|
||||||
const Expr *Base = getCXXThisExpr();
|
|
||||||
// FIXME: This doesn't handle an overloaded ->* operator.
|
|
||||||
if (!Base)
|
|
||||||
return UnknownVal();
|
|
||||||
return getSVal(Base);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const FunctionDecl *getDecl() const;
|
virtual const FunctionDecl *getDecl() const;
|
||||||
|
|
||||||
|
|
|
@ -384,6 +384,25 @@ void CXXInstanceCall::getExtraInvalidatedRegions(RegionList &Regions) const {
|
||||||
Regions.push_back(R);
|
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 {
|
RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
|
||||||
// Do we have a decl at all?
|
// 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.
|
// the expression to the location of the object.
|
||||||
SVal V = state->getSVal(tempExpr, LCtx);
|
SVal V = state->getSVal(tempExpr, LCtx);
|
||||||
|
|
||||||
// If the object is a record, the constructor will have already created
|
// If the value is already a CXXTempObjectRegion, it is fine as it is.
|
||||||
// a temporary object region. If it is not, we need to copy the value over.
|
// Otherwise, create a new CXXTempObjectRegion, and copy the value into it.
|
||||||
if (!ME->getType()->isRecordType()) {
|
const MemRegion *MR = V.getAsRegion();
|
||||||
|
if (!MR || !isa<CXXTempObjectRegion>(MR)) {
|
||||||
const MemRegion *R =
|
const MemRegion *R =
|
||||||
svalBuilder.getRegionManager().getCXXTempObjectRegion(ME, LCtx);
|
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; };
|
struct S { int &x; };
|
||||||
|
|
||||||
extern S *getS();
|
// FIXME: Should be TRUE. Fields of return-by-value structs are not yet
|
||||||
clang_analyzer_eval(&getS()->x != 0); // expected-warning{{TRUE}}
|
// 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!
|
return *x; // should warn here!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void testReferenceFieldAddress() {
|
|
||||||
struct S { int &x; };
|
|
||||||
|
|
||||||
extern S getS();
|
|
||||||
clang_analyzer_eval(&getS().x != 0); // expected-warning{{UNKNOWN}}
|
|
||||||
}
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче