[analyzer] Add support for CXXInheritedCtorInitExpr.

So far we've been dropping coverage every time we've encountered
a CXXInheritedCtorInitExpr. This patch attempts to add some
initial support for it.

Constructors for arguments of a CXXInheritedCtorInitExpr are still
not fully supported.

Differential Revision: https://reviews.llvm.org/D74735
This commit is contained in:
Artem Dergachev 2020-02-17 21:42:50 +03:00
parent deb116ee0a
commit a82ffe9d93
12 changed files with 309 additions and 73 deletions

View File

@ -41,6 +41,9 @@ public:
/// An implicit or explicit C++ constructor call
Constructor,
/// A C++ inherited constructor produced by a "using T::T" directive
InheritedConstructor,
/// A C++ allocation function call (operator `new`), via C++ new-expression
Allocator,
@ -84,6 +87,9 @@ public:
AnyCall(const CXXConstructExpr *NE)
: E(NE), D(NE->getConstructor()), K(Constructor) {}
AnyCall(const CXXInheritedCtorInitExpr *CIE)
: E(CIE), D(CIE->getConstructor()), K(InheritedConstructor) {}
AnyCall(const CXXDestructorDecl *D) : E(nullptr), D(D), K(Destructor) {}
AnyCall(const CXXConstructorDecl *D) : E(nullptr), D(D), K(Constructor) {}
@ -114,6 +120,8 @@ public:
return AnyCall(CXDE);
} else if (const auto *CXCE = dyn_cast<CXXConstructExpr>(E)) {
return AnyCall(CXCE);
} else if (const auto *CXCIE = dyn_cast<CXXInheritedCtorInitExpr>(E)) {
return AnyCall(CXCIE);
} else {
return None;
}
@ -169,6 +177,7 @@ public:
return cast<CallExpr>(E)->getCallReturnType(Ctx);
case Destructor:
case Constructor:
case InheritedConstructor:
case Allocator:
case Deallocator:
return cast<FunctionDecl>(D)->getReturnType();

View File

@ -110,6 +110,9 @@ public:
ConstructionContextItem(const CXXConstructExpr *CE, unsigned Index)
: Data(CE), Kind(ArgumentKind), Index(Index) {}
ConstructionContextItem(const CXXInheritedCtorInitExpr *CE, unsigned Index)
: Data(CE), Kind(ArgumentKind), Index(Index) {}
ConstructionContextItem(const ObjCMessageExpr *ME, unsigned Index)
: Data(ME), Kind(ArgumentKind), Index(Index) {}
@ -117,7 +120,7 @@ public:
ConstructionContextItem(const Expr *E, unsigned Index)
: Data(E), Kind(ArgumentKind), Index(Index) {
assert(isa<CallExpr>(E) || isa<CXXConstructExpr>(E) ||
isa<ObjCMessageExpr>(E));
isa<CXXInheritedCtorInitExpr>(E) || isa<ObjCMessageExpr>(E));
}
ConstructionContextItem(const CXXCtorInitializer *Init)

View File

@ -63,6 +63,9 @@ enum CallEventKind {
CE_BEG_CXX_INSTANCE_CALLS = CE_CXXMember,
CE_END_CXX_INSTANCE_CALLS = CE_CXXDestructor,
CE_CXXConstructor,
CE_CXXInheritedConstructor,
CE_BEG_CXX_CONSTRUCTOR_CALLS = CE_CXXConstructor,
CE_END_CXX_CONSTRUCTOR_CALLS = CE_CXXInheritedConstructor,
CE_CXXAllocator,
CE_BEG_FUNCTION_CALLS = CE_Function,
CE_END_FUNCTION_CALLS = CE_CXXAllocator,
@ -818,10 +821,38 @@ public:
}
};
/// Represents any constructor invocation. This includes regular constructors
/// and inherited constructors.
class AnyCXXConstructorCall : public AnyFunctionCall {
protected:
AnyCXXConstructorCall(const Expr *E, const MemRegion *Target,
ProgramStateRef St, const LocationContext *LCtx)
: AnyFunctionCall(E, St, LCtx) {
assert(E && (isa<CXXConstructExpr>(E) || isa<CXXInheritedCtorInitExpr>(E)));
// Target may be null when the region is unknown.
Data = Target;
}
void getExtraInvalidatedValues(ValueList &Values,
RegionAndSymbolInvalidationTraits *ETraits) const override;
void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
BindingsTy &Bindings) const override;
public:
/// Returns the value of the implicit 'this' object.
SVal getCXXThisVal() const;
static bool classof(const CallEvent *Call) {
return Call->getKind() >= CE_BEG_CXX_CONSTRUCTOR_CALLS &&
Call->getKind() <= CE_END_CXX_CONSTRUCTOR_CALLS;
}
};
/// Represents a call to a C++ constructor.
///
/// Example: \c T(1)
class CXXConstructorCall : public AnyFunctionCall {
class CXXConstructorCall : public AnyCXXConstructorCall {
friend class CallEventManager;
protected:
@ -834,17 +865,12 @@ protected:
/// \param LCtx The location context at this point in the program.
CXXConstructorCall(const CXXConstructExpr *CE, const MemRegion *Target,
ProgramStateRef St, const LocationContext *LCtx)
: AnyFunctionCall(CE, St, LCtx) {
Data = Target;
}
: AnyCXXConstructorCall(CE, Target, St, LCtx) {}
CXXConstructorCall(const CXXConstructorCall &Other) = default;
void cloneTo(void *Dest) const override { new (Dest) CXXConstructorCall(*this); }
void getExtraInvalidatedValues(ValueList &Values,
RegionAndSymbolInvalidationTraits *ETraits) const override;
public:
virtual const CXXConstructExpr *getOriginExpr() const {
return cast<CXXConstructExpr>(AnyFunctionCall::getOriginExpr());
@ -860,12 +886,6 @@ public:
return getOriginExpr()->getArg(Index);
}
/// Returns the value of the implicit 'this' object.
SVal getCXXThisVal() const;
void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
BindingsTy &Bindings) const override;
Kind getKind() const override { return CE_CXXConstructor; }
static bool classof(const CallEvent *CA) {
@ -873,6 +893,66 @@ public:
}
};
/// Represents a call to a C++ inherited constructor.
///
/// Example: \c class T : public S { using S::S; }; T(1);
class CXXInheritedConstructorCall : public AnyCXXConstructorCall {
friend class CallEventManager;
protected:
CXXInheritedConstructorCall(const CXXInheritedCtorInitExpr *CE,
const MemRegion *Target, ProgramStateRef St,
const LocationContext *LCtx)
: AnyCXXConstructorCall(CE, Target, St, LCtx) {}
CXXInheritedConstructorCall(const CXXInheritedConstructorCall &Other) =
default;
void cloneTo(void *Dest) const override {
new (Dest) CXXInheritedConstructorCall(*this);
}
public:
virtual const CXXInheritedCtorInitExpr *getOriginExpr() const {
return cast<CXXInheritedCtorInitExpr>(AnyFunctionCall::getOriginExpr());
}
const CXXConstructorDecl *getDecl() const override {
return getOriginExpr()->getConstructor();
}
/// Obtain the stack frame of the inheriting constructor. Argument expressions
/// can be found on the call site of that stack frame.
const StackFrameContext *getInheritingStackFrame() const;
/// Obtain the CXXConstructExpr for the sub-class that inherited the current
/// constructor (possibly indirectly). It's the statement that contains
/// argument expressions.
const CXXConstructExpr *getInheritingConstructor() const {
return cast<CXXConstructExpr>(getInheritingStackFrame()->getCallSite());
}
unsigned getNumArgs() const override {
return getInheritingConstructor()->getNumArgs();
}
const Expr *getArgExpr(unsigned Index) const override {
return getInheritingConstructor()->getArg(Index);
}
virtual SVal getArgSVal(unsigned Index) const override {
return getState()->getSVal(
getArgExpr(Index),
getInheritingStackFrame()->getParent()->getStackFrame());
}
Kind getKind() const override { return CE_CXXInheritedConstructor; }
static bool classof(const CallEvent *CA) {
return CA->getKind() == CE_CXXInheritedConstructor;
}
};
/// Represents the memory allocation call in a C++ new-expression.
///
/// This is a call to "operator new".
@ -1232,6 +1312,13 @@ public:
return create<CXXConstructorCall>(E, Target, State, LCtx);
}
CallEventRef<CXXInheritedConstructorCall>
getCXXInheritedConstructorCall(const CXXInheritedCtorInitExpr *E,
const MemRegion *Target, ProgramStateRef State,
const LocationContext *LCtx) {
return create<CXXInheritedConstructorCall>(E, Target, State, LCtx);
}
CallEventRef<CXXDestructorCall>
getCXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger,
const MemRegion *Target, bool IsBase,

View File

@ -527,6 +527,9 @@ public:
void VisitCXXConstructExpr(const CXXConstructExpr *E, ExplodedNode *Pred,
ExplodedNodeSet &Dst);
void VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E,
ExplodedNode *Pred, ExplodedNodeSet &Dst);
void VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest,
const Stmt *S, bool IsBaseDtor,
ExplodedNode *Pred, ExplodedNodeSet &Dst,
@ -807,10 +810,15 @@ private:
/// or unusable for any reason, a dummy temporary region is returned, and the
/// IsConstructorWithImproperlyModeledTargetRegion flag is set in \p CallOpts.
/// Returns the updated program state and the new object's this-region.
std::pair<ProgramStateRef, SVal> prepareForObjectConstruction(
std::pair<ProgramStateRef, SVal> handleConstructionContext(
const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
const ConstructionContext *CC, EvalCallOptions &CallOpts);
/// Common code that handles either a CXXConstructExpr or a
/// CXXInheritedCtorInitExpr.
void handleConstructor(const Expr *E, ExplodedNode *Pred,
ExplodedNodeSet &Dst);
/// Store the location of a C++ object corresponding to a statement
/// until the statement is actually encountered. For example, if a DeclStmt
/// has CXXConstructExpr as its initializer, the object would be considered

View File

@ -663,6 +663,7 @@ RetainSummaryManager::getSummary(AnyCall C,
switch (C.getKind()) {
case AnyCall::Function:
case AnyCall::Constructor:
case AnyCall::InheritedConstructor:
case AnyCall::Allocator:
case AnyCall::Deallocator:
Summ = getFunctionSummary(cast_or_null<FunctionDecl>(C.getDecl()));

View File

@ -889,24 +889,22 @@ void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
Params);
}
SVal CXXConstructorCall::getCXXThisVal() const {
SVal AnyCXXConstructorCall::getCXXThisVal() const {
if (Data)
return loc::MemRegionVal(static_cast<const MemRegion *>(Data));
return UnknownVal();
}
void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values,
void AnyCXXConstructorCall::getExtraInvalidatedValues(ValueList &Values,
RegionAndSymbolInvalidationTraits *ETraits) const {
if (Data) {
loc::MemRegionVal MV(static_cast<const MemRegion *>(Data));
if (SymbolRef Sym = MV.getAsSymbol(true))
ETraits->setTrait(Sym,
RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
Values.push_back(MV);
}
SVal V = getCXXThisVal();
if (SymbolRef Sym = V.getAsSymbol(true))
ETraits->setTrait(Sym,
RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
Values.push_back(V);
}
void CXXConstructorCall::getInitialStackFrameContents(
void AnyCXXConstructorCall::getInitialStackFrameContents(
const StackFrameContext *CalleeCtx,
BindingsTy &Bindings) const {
AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings);
@ -920,6 +918,14 @@ void CXXConstructorCall::getInitialStackFrameContents(
}
}
const StackFrameContext *
CXXInheritedConstructorCall::getInheritingStackFrame() const {
const StackFrameContext *SFC = getLocationContext()->getStackFrame();
while (isa<CXXInheritedCtorInitExpr>(SFC->getCallSite()))
SFC = SFC->getParent()->getStackFrame();
return SFC;
}
SVal CXXDestructorCall::getCXXThisVal() const {
if (Data)
return loc::MemRegionVal(DtorDataTy::getFromOpaqueValue(Data).getPointer());
@ -1392,17 +1398,20 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx))
return Out;
// All other cases are handled by getCall.
assert(isa<CXXConstructExpr>(CallSite) &&
"This is not an inlineable statement");
SValBuilder &SVB = State->getStateManager().getSValBuilder();
const auto *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl());
Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx);
SVal ThisVal = State->getSVal(ThisPtr);
return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite),
ThisVal.getAsRegion(), State, CallerCtx);
if (const auto *CE = dyn_cast<CXXConstructExpr>(CallSite))
return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx);
else if (const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(CallSite))
return getCXXInheritedConstructorCall(CIE, ThisVal.getAsRegion(), State,
CallerCtx);
else {
// All other cases are handled by getCall.
llvm_unreachable("This is not an inlineable statement");
}
}
// Fall back to the CFG. The only thing we haven't handled yet is

View File

@ -1212,7 +1212,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
// C++, OpenMP and ARC stuff we don't support yet.
case Expr::ObjCIndirectCopyRestoreExprClass:
case Stmt::CXXDependentScopeMemberExprClass:
case Stmt::CXXInheritedCtorInitExprClass:
case Stmt::CXXTryStmtClass:
case Stmt::CXXTypeidExprClass:
case Stmt::CXXUuidofExprClass:
@ -1618,6 +1617,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
case Stmt::CXXInheritedCtorInitExprClass:
Bldr.takeNodes(Pred);
VisitCXXInheritedCtorInitExpr(cast<CXXInheritedCtorInitExpr>(S), Pred,
Dst);
Bldr.addNodes(Dst);
break;
case Stmt::CXXNewExprClass: {
Bldr.takeNodes(Pred);

View File

@ -109,7 +109,7 @@ SVal ExprEngine::makeZeroElementRegion(ProgramStateRef State, SVal LValue,
return LValue;
}
std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
std::pair<ProgramStateRef, SVal> ExprEngine::handleConstructionContext(
const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
const ConstructionContext *CC, EvalCallOptions &CallOpts) {
SValBuilder &SVB = getSValBuilder();
@ -202,7 +202,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
CallerLCtx = CallerLCtx->getParent();
assert(!isa<BlockInvocationContext>(CallerLCtx));
}
return prepareForObjectConstruction(
return handleConstructionContext(
cast<Expr>(SFC->getCallSite()), State, CallerLCtx,
RTC->getConstructionContext(), CallOpts);
} else {
@ -247,7 +247,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
ProgramStateRef PreElideState = State;
EvalCallOptions PreElideCallOpts = CallOpts;
std::tie(State, V) = prepareForObjectConstruction(
std::tie(State, V) = handleConstructionContext(
CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts);
// FIXME: This definition of "copy elision has not failed" is unreliable.
@ -392,26 +392,32 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
State, loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)));
}
void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
ExplodedNode *Pred,
ExplodedNodeSet &destNodes) {
void ExprEngine::handleConstructor(const Expr *E,
ExplodedNode *Pred,
ExplodedNodeSet &destNodes) {
const auto *CE = dyn_cast<CXXConstructExpr>(E);
const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(E);
assert(CE || CIE);
const LocationContext *LCtx = Pred->getLocationContext();
ProgramStateRef State = Pred->getState();
SVal Target = UnknownVal();
if (Optional<SVal> ElidedTarget =
getObjectUnderConstruction(State, CE, LCtx)) {
// We've previously modeled an elidable constructor by pretending that it in
// fact constructs into the correct target. This constructor can therefore
// be skipped.
Target = *ElidedTarget;
StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);
State = finishObjectConstruction(State, CE, LCtx);
if (auto L = Target.getAs<Loc>())
State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType()));
Bldr.generateNode(CE, Pred, State);
return;
if (CE) {
if (Optional<SVal> ElidedTarget =
getObjectUnderConstruction(State, CE, LCtx)) {
// We've previously modeled an elidable constructor by pretending that it
// in fact constructs into the correct target. This constructor can
// therefore be skipped.
Target = *ElidedTarget;
StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);
State = finishObjectConstruction(State, CE, LCtx);
if (auto L = Target.getAs<Loc>())
State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType()));
Bldr.generateNode(CE, Pred, State);
return;
}
}
// FIXME: Handle arrays, which run the same constructor for every element.
@ -423,10 +429,16 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
assert(C || getCurrentCFGElement().getAs<CFGStmt>());
const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr;
switch (CE->getConstructionKind()) {
const CXXConstructExpr::ConstructionKind CK =
CE ? CE->getConstructionKind() : CIE->getConstructionKind();
switch (CK) {
case CXXConstructExpr::CK_Complete: {
// Inherited constructors are always base class constructors.
assert(CE && !CIE && "A complete constructor is inherited?!");
// The target region is found from construction context.
std::tie(State, Target) =
prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts);
handleConstructionContext(CE, State, LCtx, CC, CallOpts);
break;
}
case CXXConstructExpr::CK_VirtualBase: {
@ -455,9 +467,9 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
// FIXME: Instead of relying on the ParentMap, we should have the
// trigger-statement (InitListExpr in this case) passed down from CFG or
// otherwise always available during construction.
if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(CE))) {
if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(E))) {
MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, LCtx));
Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
break;
}
@ -468,14 +480,13 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
LCtx->getStackFrame());
SVal ThisVal = State->getSVal(ThisPtr);
if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) {
if (CK == CXXConstructExpr::CK_Delegating) {
Target = ThisVal;
} else {
// Cast to the base type.
bool IsVirtual =
(CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase);
SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(),
IsVirtual);
bool IsVirtual = (CK == CXXConstructExpr::CK_VirtualBase);
SVal BaseVal =
getStoreManager().evalDerivedToBase(ThisVal, E->getType(), IsVirtual);
Target = BaseVal;
}
break;
@ -487,23 +498,27 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
"Prepare for object construction");
ExplodedNodeSet DstPrepare;
StmtNodeBuilder BldrPrepare(Pred, DstPrepare, *currBldrCtx);
BldrPrepare.generateNode(CE, Pred, State, &T, ProgramPoint::PreStmtKind);
BldrPrepare.generateNode(E, Pred, State, &T, ProgramPoint::PreStmtKind);
assert(DstPrepare.size() <= 1);
if (DstPrepare.size() == 0)
return;
Pred = *BldrPrepare.begin();
}
const MemRegion *TargetRegion = Target.getAsRegion();
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXConstructorCall> Call =
CEMgr.getCXXConstructorCall(CE, Target.getAsRegion(), State, LCtx);
CallEventRef<> Call =
CIE ? (CallEventRef<>)CEMgr.getCXXInheritedConstructorCall(
CIE, TargetRegion, State, LCtx)
: (CallEventRef<>)CEMgr.getCXXConstructorCall(
CE, TargetRegion, State, LCtx);
ExplodedNodeSet DstPreVisit;
getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this);
getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, E, *this);
// FIXME: Is it possible and/or useful to do this before PreStmt?
ExplodedNodeSet PreInitialized;
{
if (CE) {
// FIXME: Is it possible and/or useful to do this before PreStmt?
StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx);
for (ExplodedNodeSet::iterator I = DstPreVisit.begin(),
E = DstPreVisit.end();
@ -528,6 +543,8 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
Bldr.generateNode(CE, *I, State, /*tag=*/nullptr,
ProgramPoint::PreStmtKind);
}
} else {
PreInitialized = DstPreVisit;
}
ExplodedNodeSet DstPreCall;
@ -537,7 +554,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
ExplodedNodeSet DstEvaluated;
StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);
if (CE->getConstructor()->isTrivial() &&
if (CE && CE->getConstructor()->isTrivial() &&
CE->getConstructor()->isCopyOrMoveConstructor() &&
!CallOpts.IsArrayCtorOrDtor) {
// FIXME: Handle other kinds of trivial constructors as well.
@ -560,9 +577,9 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
// paths when no-return temporary destructors are used for assertions.
const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext();
if (!ADC->getCFGBuildOptions().AddTemporaryDtors) {
const MemRegion *Target = Call->getCXXThisVal().getAsRegion();
if (Target && isa<CXXTempObjectRegion>(Target) &&
Call->getDecl()->getParent()->isAnyDestructorNoReturn()) {
if (TargetRegion && isa<CXXTempObjectRegion>(TargetRegion) &&
cast<CXXConstructorDecl>(Call->getDecl())
->getParent()->isAnyDestructorNoReturn()) {
// If we've inlined the constructor, then DstEvaluated would be empty.
// In this case we still want a sink, which could be implemented
@ -575,7 +592,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
"We should not have inlined this constructor!");
for (ExplodedNode *N : DstEvaluated) {
Bldr.generateSink(CE, N, N->getState());
Bldr.generateSink(E, N, N->getState());
}
// There is no need to run the PostCall and PostStmt checker
@ -595,7 +612,19 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
getCheckerManager().runCheckersForPostCall(DstPostCall,
DstPostArgumentCleanup,
*Call, *this);
getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this);
getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, E, *this);
}
void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
handleConstructor(CE, Pred, Dst);
}
void ExprEngine::VisitCXXInheritedCtorInitExpr(
const CXXInheritedCtorInitExpr *CE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
handleConstructor(CE, Pred, Dst);
}
void ExprEngine::VisitCXXDestructor(QualType ObjectType,

View File

@ -668,8 +668,8 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
assert(RTC->getStmt() == Call.getOriginExpr());
EvalCallOptions CallOpts; // FIXME: We won't really need those.
std::tie(State, Target) =
prepareForObjectConstruction(Call.getOriginExpr(), State, LCtx,
RTC->getConstructionContext(), CallOpts);
handleConstructionContext(Call.getOriginExpr(), State, LCtx,
RTC->getConstructionContext(), CallOpts);
const MemRegion *TargetR = Target.getAsRegion();
assert(TargetR);
// Invalidate the region so that it didn't look uninitialized. If this is
@ -789,6 +789,11 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
break;
}
case CE_CXXInheritedConstructor: {
// This doesn't really increase the cost of inlining ever, because
// the stack frame of the inherited constructor is trivial.
return CIP_Allowed;
}
case CE_CXXDestructor: {
if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
return CIP_DisallowedAlways;

View File

@ -542,6 +542,11 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
if (!Loc)
return true;
// Anonymous parameters of an inheriting constructor are live for the entire
// duration of the constructor.
if (isa<CXXInheritedCtorInitExpr>(Loc))
return true;
if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl()))
return true;

View File

@ -0,0 +1,59 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
void clang_analyzer_eval(bool);
namespace basic_tests {
struct A {
int x;
A(int x): x(x) {}
};
struct B : A {
using A::A;
};
struct C : B {
using B::B;
};
void test_B() {
B b(1);
clang_analyzer_eval(b.x == 1); // expected-warning{{TRUE}}
}
void test_C() {
C c(2);
clang_analyzer_eval(c.x == 2); // expected-warning{{TRUE}}
}
} // namespace basic_tests
namespace arguments_with_constructors {
struct S {
int x, y;
S(int x, int y): x(x), y(y) {}
~S() {}
};
struct A {
S s;
int z;
A(S s, int z) : s(s), z(z) {}
};
struct B : A {
using A::A;
};
void test_B() {
B b(S(1, 2), 3);
// FIXME: There should be no execution path on which this is false.
clang_analyzer_eval(b.s.x == 1); // expected-warning{{TRUE}}
// expected-warning@-1{{FALSE}}
// FIXME: There should be no execution path on which this is false.
clang_analyzer_eval(b.s.y == 2); // expected-warning{{TRUE}}
// expected-warning@-1{{FALSE}}
clang_analyzer_eval(b.z == 3); // expected-warning{{TRUE}}
}
} // namespace arguments_with_constructors

View File

@ -739,3 +739,18 @@ WeirdResult testOutParamWithWeirdResult() {
return outParamWithWeirdResult(&obj); // no-warning
}
} // namespace weird_result
namespace inherited_constructor_crash {
struct a {
a(int);
};
struct b : a {
// This is an "inherited constructor".
using a::a;
};
void test() {
// RetainCountChecker used to crash when looking for a summary
// for the inherited constructor invocation.
b(0);
}
} // namespace inherited_constructor_crash