diff --git a/clang/include/clang/Analysis/AnyCall.h b/clang/include/clang/Analysis/AnyCall.h --- a/clang/include/clang/Analysis/AnyCall.h +++ b/clang/include/clang/Analysis/AnyCall.h @@ -41,6 +41,9 @@ /// 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 @@ 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 @@ return AnyCall(CXDE); } else if (const auto *CXCE = dyn_cast(E)) { return AnyCall(CXCE); + } else if (const auto *CXCIE = dyn_cast(E)) { + return AnyCall(CXCIE); } else { return None; } @@ -169,6 +177,7 @@ return cast(E)->getCallReturnType(Ctx); case Destructor: case Constructor: + case InheritedConstructor: case Allocator: case Deallocator: return cast(D)->getReturnType(); diff --git a/clang/include/clang/Analysis/ConstructionContext.h b/clang/include/clang/Analysis/ConstructionContext.h --- a/clang/include/clang/Analysis/ConstructionContext.h +++ b/clang/include/clang/Analysis/ConstructionContext.h @@ -110,6 +110,9 @@ 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 @@ ConstructionContextItem(const Expr *E, unsigned Index) : Data(E), Kind(ArgumentKind), Index(Index) { assert(isa(E) || isa(E) || - isa(E)); + isa(E) || isa(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 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -63,6 +63,9 @@ 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 @@ } }; +/// 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(E) || isa(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 @@ /// \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(AnyFunctionCall::getOriginExpr()); @@ -860,12 +886,6 @@ 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 @@ } }; +/// 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(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(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 @@ return create(E, Target, State, LCtx); } + CallEventRef + getCXXInheritedConstructorCall(const CXXInheritedCtorInitExpr *E, + const MemRegion *Target, ProgramStateRef State, + const LocationContext *LCtx) { + return create(E, Target, State, LCtx); + } + CallEventRef 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 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -527,6 +527,9 @@ 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 @@ /// 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 prepareForObjectConstruction( + std::pair 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 --- a/clang/lib/Analysis/RetainSummaryManager.cpp +++ b/clang/lib/Analysis/RetainSummaryManager.cpp @@ -663,6 +663,7 @@ switch (C.getKind()) { case AnyCall::Function: case AnyCall::Constructor: + case AnyCall::InheritedConstructor: case AnyCall::Allocator: case AnyCall::Deallocator: Summ = getFunctionSummary(cast_or_null(C.getDecl())); diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -889,24 +889,22 @@ Params); } -SVal CXXConstructorCall::getCXXThisVal() const { +SVal AnyCXXConstructorCall::getCXXThisVal() const { if (Data) return loc::MemRegionVal(static_cast(Data)); return UnknownVal(); } -void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values, +void AnyCXXConstructorCall::getExtraInvalidatedValues(ValueList &Values, RegionAndSymbolInvalidationTraits *ETraits) const { - if (Data) { - loc::MemRegionVal MV(static_cast(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 @@ } } +const StackFrameContext * +CXXInheritedConstructorCall::getInheritingStackFrame() const { + const StackFrameContext *SFC = getLocationContext()->getStackFrame(); + while (isa(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 @@ if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx)) return Out; - // All other cases are handled by getCall. - assert(isa(CallSite) && - "This is not an inlineable statement"); - SValBuilder &SVB = State->getStateManager().getSValBuilder(); const auto *Ctor = cast(CalleeCtx->getDecl()); Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx); SVal ThisVal = State->getSVal(ThisPtr); - return getCXXConstructorCall(cast(CallSite), - ThisVal.getAsRegion(), State, CallerCtx); + if (const auto *CE = dyn_cast(CallSite)) + return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx); + else if (const auto *CIE = dyn_cast(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 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1212,7 +1212,6 @@ // 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 @@ Bldr.addNodes(Dst); break; + case Stmt::CXXInheritedCtorInitExprClass: + Bldr.takeNodes(Pred); + VisitCXXInheritedCtorInitExpr(cast(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 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -109,7 +109,7 @@ return LValue; } -std::pair ExprEngine::prepareForObjectConstruction( +std::pair ExprEngine::handleConstructionContext( const Expr *E, ProgramStateRef State, const LocationContext *LCtx, const ConstructionContext *CC, EvalCallOptions &CallOpts) { SValBuilder &SVB = getSValBuilder(); @@ -202,7 +202,7 @@ CallerLCtx = CallerLCtx->getParent(); assert(!isa(CallerLCtx)); } - return prepareForObjectConstruction( + return handleConstructionContext( cast(SFC->getCallSite()), State, CallerLCtx, RTC->getConstructionContext(), CallOpts); } else { @@ -247,7 +247,7 @@ 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 @@ 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(E); + const auto *CIE = dyn_cast(E); + assert(CE || CIE); + const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); SVal Target = UnknownVal(); - if (Optional 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()) - State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType())); - Bldr.generateNode(CE, Pred, State); - return; + if (CE) { + if (Optional 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()) + 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 @@ assert(C || getCurrentCFGElement().getAs()); 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 @@ // 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(LCtx->getParentMap().getParent(CE))) { + if (dyn_cast_or_null(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 @@ 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 @@ "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 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 @@ Bldr.generateNode(CE, *I, State, /*tag=*/nullptr, ProgramPoint::PreStmtKind); } + } else { + PreInitialized = DstPreVisit; } ExplodedNodeSet DstPreCall; @@ -537,7 +554,7 @@ 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 @@ // 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(Target) && - Call->getDecl()->getParent()->isAnyDestructorNoReturn()) { + if (TargetRegion && isa(TargetRegion) && + cast(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 @@ "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 @@ 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 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -668,8 +668,8 @@ 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 @@ 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 --- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -542,6 +542,11 @@ if (!Loc) return true; + // Anonymous parameters of an inheriting constructor are live for the entire + // duration of the constructor. + if (isa(Loc)) + return true; + if (LCtx->getAnalysis()->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 --- /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@-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 diff --git a/clang/test/Analysis/osobject-retain-release.cpp b/clang/test/Analysis/osobject-retain-release.cpp --- a/clang/test/Analysis/osobject-retain-release.cpp +++ b/clang/test/Analysis/osobject-retain-release.cpp @@ -739,3 +739,18 @@ 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