[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