[clang] a82ffe9 - [analyzer] Add support for CXXInheritedCtorInitExpr.
Artem Dergachev via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 25 07:37:32 PST 2020
Author: Artem Dergachev
Date: 2020-02-25T18:37:23+03:00
New Revision: a82ffe9d93a24abf30bcf63081096ea18baf78dc
URL: https://github.com/llvm/llvm-project/commit/a82ffe9d93a24abf30bcf63081096ea18baf78dc
DIFF: https://github.com/llvm/llvm-project/commit/a82ffe9d93a24abf30bcf63081096ea18baf78dc.diff
LOG: [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
Added:
clang/test/Analysis/cxx-inherited-ctor-init-expr.cpp
Modified:
clang/include/clang/Analysis/AnyCall.h
clang/include/clang/Analysis/ConstructionContext.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
clang/lib/Analysis/RetainSummaryManager.cpp
clang/lib/StaticAnalyzer/Core/CallEvent.cpp
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
clang/test/Analysis/osobject-retain-release.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/AnyCall.h b/clang/include/clang/Analysis/AnyCall.h
index 97a94d299e64..16371eb1da18 100644
--- a/clang/include/clang/Analysis/AnyCall.h
+++ b/clang/include/clang/Analysis/AnyCall.h
@@ -41,6 +41,9 @@ class AnyCall {
/// 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 @@ class AnyCall {
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 @@ class AnyCall {
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 @@ class AnyCall {
return cast<CallExpr>(E)->getCallReturnType(Ctx);
case Destructor:
case Constructor:
+ case InheritedConstructor:
case Allocator:
case Deallocator:
return cast<FunctionDecl>(D)->getReturnType();
diff --git a/clang/include/clang/Analysis/ConstructionContext.h b/clang/include/clang/Analysis/ConstructionContext.h
index f1564f9fe740..4fa5c8b454a0 100644
--- a/clang/include/clang/Analysis/ConstructionContext.h
+++ b/clang/include/clang/Analysis/ConstructionContext.h
@@ -110,6 +110,9 @@ class ConstructionContextItem {
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 @@ class ConstructionContextItem {
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)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
index 5b9bbb197e82..60705dd27d6b 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -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 @@ class CXXDestructorCall : public CXXInstanceCall {
}
};
+/// 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 @@ class CXXConstructorCall : public AnyFunctionCall {
/// \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 @@ class CXXConstructorCall : public AnyFunctionCall {
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 @@ class CXXConstructorCall : public AnyFunctionCall {
}
};
+/// 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 @@ class CallEventManager {
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,
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 6e676c512b89..c66c54116a0c 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -527,6 +527,9 @@ class ExprEngine : public SubEngine {
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 @@ class ExprEngine : public SubEngine {
/// 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
diff --git a/clang/lib/Analysis/RetainSummaryManager.cpp b/clang/lib/Analysis/RetainSummaryManager.cpp
index 181ff1b11502..00bc854a8804 100644
--- a/clang/lib/Analysis/RetainSummaryManager.cpp
+++ b/clang/lib/Analysis/RetainSummaryManager.cpp
@@ -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()));
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 168d6fe6ec48..4fc23d8ac941 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -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
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index eb32a3276550..801b30a9ab6c 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -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);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index b816aab7c18f..d05b31a64427 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -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,
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 01a371e664b2..781cc9f7943d 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -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;
diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
index 8eaaa08dc44b..e655aae45832 100644
--- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -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;
diff --git a/clang/test/Analysis/cxx-inherited-ctor-init-expr.cpp b/clang/test/Analysis/cxx-inherited-ctor-init-expr.cpp
new file mode 100644
index 000000000000..ff82c129bc70
--- /dev/null
+++ b/clang/test/Analysis/cxx-inherited-ctor-init-expr.cpp
@@ -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 at -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 at -1{{FALSE}}
+
+ clang_analyzer_eval(b.z == 3); // expected-warning{{TRUE}}
+}
+} // namespace arguments_with_constructors
diff --git a/clang/test/Analysis/osobject-retain-release.cpp b/clang/test/Analysis/osobject-retain-release.cpp
index 42675fc70e78..41606a30c39f 100644
--- a/clang/test/Analysis/osobject-retain-release.cpp
+++ b/clang/test/Analysis/osobject-retain-release.cpp
@@ -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
More information about the cfe-commits
mailing list