forked from OSchip/llvm-project
[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:
parent
deb116ee0a
commit
a82ffe9d93
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue