const Expr *RetE = RS->getRetValue();
if (!RetE)
return;
-
- SVal V = C.getState()->getSVal(RetE, C.getLocationContext());
+
+ const LocationContext *LCtx = C.getLocationContext();
+ SVal V = C.getState()->getSVal(RetE, LCtx);
const MemRegion *R = V.getAsRegion();
if (!R)
return;
// Return stack memory in an ancestor stack frame is fine.
- const StackFrameContext *SFC = SS->getStackFrame();
- if (SFC != C.getLocationContext()->getCurrentStackFrame())
+ const StackFrameContext *CurFrame = LCtx->getCurrentStackFrame();
+ const StackFrameContext *MemFrame = SS->getStackFrame();
+ if (MemFrame != CurFrame)
return;
// Automatic reference counting automatically copies blocks.
isa<BlockDataRegion>(R))
return;
+ // Returning a record by value is fine. (In this case, the returned
+ // expression will be a copy-constructor.)
+ if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType())
+ return;
+
EmitStackError(C, R, RetE);
}
case Expr::MaterializeTemporaryExprClass: {
Bldr.takeNodes(Pred);
- const MaterializeTemporaryExpr *Materialize
- = cast<MaterializeTemporaryExpr>(S);
- if (Materialize->getType()->isRecordType())
- Dst.Add(Pred);
- else
- CreateCXXTemporaryObject(Materialize, Pred, Dst);
+ const MaterializeTemporaryExpr *MTE = cast<MaterializeTemporaryExpr>(S);
+ CreateCXXTemporaryObject(MTE, Pred, Dst);
Bldr.addNodes(Dst);
break;
}
case CK_NonAtomicToAtomic:
// True no-ops.
case CK_NoOp:
+ case CK_ConstructorConversion:
case CK_UserDefinedConversion:
case CK_FunctionToPointerDecay: {
// Copy the SVal of Ex to CastE.
case CK_BaseToDerivedMemberPointer:
case CK_DerivedToBaseMemberPointer:
case CK_ReinterpretMemberPointer:
- case CK_ConstructorConversion:
case CK_VectorSplat:
case CK_LValueBitCast: {
// Recover some path-sensitivty by conjuring a new value.
// Bind the temporary object to the value of the expression. Then bind
// the expression to the location of the object.
- SVal V = state->getSVal(tempExpr, Pred->getLocationContext());
+ SVal V = state->getSVal(tempExpr, LCtx);
- const MemRegion *R =
- svalBuilder.getRegionManager().getCXXTempObjectRegion(ME, 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()) {
+ const MemRegion *R =
+ svalBuilder.getRegionManager().getCXXTempObjectRegion(ME, LCtx);
+
+ SVal L = loc::MemRegionVal(R);
+ state = state->bindLoc(L, V);
+ V = L;
+ }
- state = state->bindLoc(loc::MemRegionVal(R), V);
- Bldr.generateNode(ME, Pred, state->BindExpr(ME, LCtx, loc::MemRegionVal(R)));
+ Bldr.generateNode(ME, Pred, state->BindExpr(ME, LCtx, V));
}
void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
// FIXME: This will eventually need to handle new-expressions as well.
}
- // If we couldn't find an existing region to construct into, we'll just
- // generate a symbolic region, which is fine.
+ // If we couldn't find an existing region to construct into, assume we're
+ // constructing a temporary.
+ if (!Target) {
+ MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
+ Target = MRMgr.getCXXTempObjectRegion(CE, LCtx);
+ }
break;
}
if (!shouldInlineCXX(getAnalysisManager()))
return false;
- // Only inline constructors and destructors if we built the CFGs for them
- // properly.
- const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
- if (!ADC->getCFGBuildOptions().AddImplicitDtors ||
- !ADC->getCFGBuildOptions().AddInitializers)
- return false;
-
const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call);
// FIXME: We don't handle constructors or destructors for arrays properly.
if (Target && isa<ElementRegion>(Target))
return false;
+ // If the destructor is trivial, it's always safe to inline the constructor.
+ if (Ctor.getDecl()->getParent()->hasTrivialDestructor())
+ break;
+
+ // For other types, only inline constructors if we built the CFGs for the
+ // destructor properly.
+ const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
+ assert(ADC->getCFGBuildOptions().AddInitializers && "No CFG initializers");
+ if (!ADC->getCFGBuildOptions().AddImplicitDtors)
+ return false;
+
// FIXME: This is a hack. We don't handle temporary destructors
// right now, so we shouldn't inline their constructors.
const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr();
// Only inline constructors and destructors if we built the CFGs for them
// properly.
const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
- if (!ADC->getCFGBuildOptions().AddImplicitDtors ||
- !ADC->getCFGBuildOptions().AddInitializers)
+ if (!ADC->getCFGBuildOptions().AddImplicitDtors)
return false;
const CXXDestructorCall &Dtor = cast<CXXDestructorCall>(Call);
// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-store region -analyzer-ipa=inlining -verify %s
-// XFAIL: *
void clang_analyzer_eval(bool);
clang_analyzer_eval(a); // expected-warning{{TRUE}}
}
-
-// FIXME: These require constructor inlining to be enabled.
-
void f1() {
A x(3);
- // should be TRUE
- clang_analyzer_eval(x.getx() == 3); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(x.getx() == 3); // expected-warning{{TRUE}}
}
void f2() {
const A &x = A(3);
- // should be TRUE
- clang_analyzer_eval(x.getx() == 3); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(x.getx() == 3); // expected-warning{{TRUE}}
}
void f3() {
const A &x = (A)3;
- // should be TRUE
- clang_analyzer_eval(x.getx() == 3); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(x.getx() == 3); // expected-warning{{TRUE}}
+}
+
+void f4() {
+ A x = 3;
+ clang_analyzer_eval(x.getx() == 3); // expected-warning{{TRUE}}
}
}
+struct PtrWrapper {
+ int *x;
+
+ PtrWrapper(int *input) : x(input) {}
+};
+
+PtrWrapper *testNewInvalidation() {
+ // Ensure that we don't consider this a leak.
+ return new PtrWrapper(static_cast<int *>(malloc(4)));
+}
+
+
//--------------------------------
// Incorrectly-modelled behavior
//--------------------------------