r311877 - [analyzer][GSoC] Re-implemente current virtual calls checker in a path-sensitive way

Gabor Horvath via cfe-commits cfe-commits at lists.llvm.org
Mon Aug 28 01:44:43 PDT 2017


Author: xazax
Date: Mon Aug 28 01:44:43 2017
New Revision: 311877

URL: http://llvm.org/viewvc/llvm-project?rev=311877&view=rev
Log:
[analyzer][GSoC] Re-implemente current virtual calls checker in a path-sensitive way

Patch by: Xin Wang

Differential Revision: https://reviews.llvm.org/D34275

Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
    cfe/trunk/test/Analysis/virtualcall.cpp
    cfe/trunk/test/Analysis/virtualcall.h

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp?rev=311877&r1=311876&r2=311877&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp Mon Aug 28 01:44:43 2017
@@ -14,279 +14,272 @@
 
 #include "ClangSACheckers.h"
 #include "clang/AST/DeclCXX.h"
-#include "clang/AST/StmtVisitor.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/Support/SaveAndRestore.h"
-#include "llvm/Support/raw_ostream.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
 
 using namespace clang;
 using namespace ento;
 
 namespace {
-
-class WalkAST : public StmtVisitor<WalkAST> {
-  const CheckerBase *Checker;
-  BugReporter &BR;
-  AnalysisDeclContext *AC;
-
-  /// The root constructor or destructor whose callees are being analyzed.
-  const CXXMethodDecl *RootMethod = nullptr;
-
-  /// Whether the checker should walk into bodies of called functions.
-  /// Controlled by the "Interprocedural" analyzer-config option.
-  bool IsInterprocedural = false;
-
-  /// Whether the checker should only warn for calls to pure virtual functions
-  /// (which is undefined behavior) or for all virtual functions (which may
-  /// may result in unexpected behavior).
-  bool ReportPureOnly = false;
-
-  typedef const CallExpr * WorkListUnit;
-  typedef SmallVector<WorkListUnit, 20> DFSWorkList;
-
-  /// A vector representing the worklist which has a chain of CallExprs.
-  DFSWorkList WList;
-
-  // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
-  // body has not been visited yet.
-  // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
-  // body has been visited.
-  enum Kind { NotVisited,
-              PreVisited,  /**< A CallExpr to this FunctionDecl is in the
-                                worklist, but the body has not yet been
-                                visited. */
-              PostVisited  /**< A CallExpr to this FunctionDecl is in the
-                                worklist, and the body has been visited. */
-  };
-
-  /// A DenseMap that records visited states of FunctionDecls.
-  llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions;
-
-  /// The CallExpr whose body is currently being visited.  This is used for
-  /// generating bug reports.  This is null while visiting the body of a
-  /// constructor or destructor.
-  const CallExpr *visitingCallExpr;
-
-public:
-  WalkAST(const CheckerBase *checker, BugReporter &br, AnalysisDeclContext *ac,
-          const CXXMethodDecl *rootMethod, bool isInterprocedural,
-          bool reportPureOnly)
-      : Checker(checker), BR(br), AC(ac), RootMethod(rootMethod),
-        IsInterprocedural(isInterprocedural), ReportPureOnly(reportPureOnly),
-        visitingCallExpr(nullptr) {
-    // Walking should always start from either a constructor or a destructor.
-    assert(isa<CXXConstructorDecl>(rootMethod) ||
-           isa<CXXDestructorDecl>(rootMethod));
+enum class ObjectState : bool { CtorCalled, DtorCalled };
+} // end namespace
+  // FIXME: Ascending over StackFrameContext maybe another method.
+
+namespace llvm {
+template <> struct FoldingSetTrait<ObjectState> {
+  static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
+    ID.AddInteger(static_cast<int>(X));
   }
+};
+} // end namespace llvm
 
-  bool hasWork() const { return !WList.empty(); }
-
-  /// This method adds a CallExpr to the worklist and marks the callee as
-  /// being PreVisited.
-  void Enqueue(WorkListUnit WLUnit) {
-    const FunctionDecl *FD = WLUnit->getDirectCallee();
-    if (!FD || !FD->getBody())
-      return;
-    Kind &K = VisitedFunctions[FD];
-    if (K != NotVisited)
-      return;
-    K = PreVisited;
-    WList.push_back(WLUnit);
-  }
+namespace {
+class VirtualCallChecker
+    : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
+  mutable std::unique_ptr<BugType> BT;
 
-  /// This method returns an item from the worklist without removing it.
-  WorkListUnit Dequeue() {
-    assert(!WList.empty());
-    return WList.back();
-  }
+public:
+  // The flag to determine if pure virtual functions should be issued only.
+  DefaultBool IsPureOnly;
 
-  void Execute() {
-    while (hasWork()) {
-      WorkListUnit WLUnit = Dequeue();
-      const FunctionDecl *FD = WLUnit->getDirectCallee();
-      assert(FD && FD->getBody());
-
-      if (VisitedFunctions[FD] == PreVisited) {
-        // If the callee is PreVisited, walk its body.
-        // Visit the body.
-        SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit);
-        Visit(FD->getBody());
-
-        // Mark the function as being PostVisited to indicate we have
-        // scanned the body.
-        VisitedFunctions[FD] = PostVisited;
-        continue;
-      }
-
-      // Otherwise, the callee is PostVisited.
-      // Remove it from the worklist.
-      assert(VisitedFunctions[FD] == PostVisited);
-      WList.pop_back();
+  void checkBeginFunction(CheckerContext &C) const;
+  void checkEndFunction(CheckerContext &C) const;
+  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+
+private:
+  void registerCtorDtorCallInState(bool IsBeginFunction,
+                                   CheckerContext &C) const;
+  void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg,
+                 CheckerContext &C) const;
+
+  class VirtualBugVisitor : public BugReporterVisitorImpl<VirtualBugVisitor> {
+  private:
+    const MemRegion *ObjectRegion;
+    bool Found;
+
+  public:
+    VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {}
+
+    void Profile(llvm::FoldingSetNodeID &ID) const override {
+      static int X = 0;
+      ID.AddPointer(&X);
+      ID.AddPointer(ObjectRegion);
     }
-  }
-
-  // Stmt visitor methods.
-  void VisitCallExpr(CallExpr *CE);
-  void VisitCXXMemberCallExpr(CallExpr *CE);
-  void VisitStmt(Stmt *S) { VisitChildren(S); }
-  void VisitChildren(Stmt *S);
-
-  void ReportVirtualCall(const CallExpr *CE, bool isPure);
 
+    std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+                                                   const ExplodedNode *PrevN,
+                                                   BugReporterContext &BRC,
+                                                   BugReport &BR) override;
+  };
 };
-} // end anonymous namespace
+} // end namespace
 
-//===----------------------------------------------------------------------===//
-// AST walking.
-//===----------------------------------------------------------------------===//
-
-void WalkAST::VisitChildren(Stmt *S) {
-  for (Stmt *Child : S->children())
-    if (Child)
-      Visit(Child);
-}
+// GDM (generic data map) to the memregion of this for the ctor and dtor.
+REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
 
-void WalkAST::VisitCallExpr(CallExpr *CE) {
-  VisitChildren(CE);
-  if (IsInterprocedural)
-    Enqueue(CE);
+std::shared_ptr<PathDiagnosticPiece>
+VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N,
+                                                 const ExplodedNode *PrevN,
+                                                 BugReporterContext &BRC,
+                                                 BugReport &BR) {
+  // We need the last ctor/dtor which call the virtual function.
+  // The visitor walks the ExplodedGraph backwards.
+  if (Found)
+    return nullptr;
+
+  ProgramStateRef State = N->getState();
+  const LocationContext *LCtx = N->getLocationContext();
+  const CXXConstructorDecl *CD =
+      dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl());
+  const CXXDestructorDecl *DD =
+      dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl());
+
+  if (!CD && !DD)
+    return nullptr;
+
+  ProgramStateManager &PSM = State->getStateManager();
+  auto &SVB = PSM.getSValBuilder();
+  const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
+  if (!MD)
+    return nullptr;
+  auto ThiSVal =
+      State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
+  const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
+  if (!Reg)
+    return nullptr;
+  if (Reg != ObjectRegion)
+    return nullptr;
+
+  const Stmt *S = PathDiagnosticLocation::getStmt(N);
+  if (!S)
+    return nullptr;
+  Found = true;
+
+  std::string InfoText;
+  if (CD)
+    InfoText = "This constructor of an object of type '" +
+               CD->getNameAsString() +
+               "' has not returned when the virtual method was called";
+  else
+    InfoText = "This destructor of an object of type '" +
+               DD->getNameAsString() +
+               "' has not returned when the virtual method was called";
+
+  // Generate the extra diagnostic.
+  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+                             N->getLocationContext());
+  return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
 }
 
-void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
-  VisitChildren(CE);
-  bool callIsNonVirtual = false;
-
-  // Several situations to elide for checking.
-  if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
-    // If the member access is fully qualified (i.e., X::F), then treat
-    // this as a non-virtual call and do not warn.
+// The function to check if a callexpr is a virtual function.
+static bool isVirtualCall(const CallExpr *CE) {
+  bool CallIsNonVirtual = false;
+
+  if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
+    // The member access is fully qualified (i.e., X::F).
+    // Treat this as a non-virtual call and do not warn.
     if (CME->getQualifier())
-      callIsNonVirtual = true;
+      CallIsNonVirtual = true;
 
-    if (Expr *base = CME->getBase()->IgnoreImpCasts()) {
-      // Elide analyzing the call entirely if the base pointer is not 'this'.
-      if (!isa<CXXThisExpr>(base))
-        return;
-
-      // If the most derived class is marked final, we know that now subclass
-      // can override this member.
-      if (base->getBestDynamicClassType()->hasAttr<FinalAttr>())
-        callIsNonVirtual = true;
+    if (const Expr *Base = CME->getBase()->IgnoreImpCasts()) {
+      // The most derived class is marked final.
+      if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
+        CallIsNonVirtual = true;
     }
   }
 
-  // Get the callee.
   const CXXMethodDecl *MD =
       dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
-  if (MD && MD->isVirtual() && !callIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
+  if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
       !MD->getParent()->hasAttr<FinalAttr>())
-    ReportVirtualCall(CE, MD->isPure());
+    return true;
+  return false;
+}
+
+// The BeginFunction callback when enter a constructor or a destructor.
+void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
+  registerCtorDtorCallInState(true, C);
+}
 
-  if (IsInterprocedural)
-    Enqueue(CE);
+// The EndFunction callback when leave a constructor or a destructor.
+void VirtualCallChecker::checkEndFunction(CheckerContext &C) const {
+  registerCtorDtorCallInState(false, C);
 }
 
-void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
-  if (ReportPureOnly && !isPure)
+void VirtualCallChecker::checkPreCall(const CallEvent &Call,
+                                      CheckerContext &C) const {
+  const auto MC = dyn_cast<CXXMemberCall>(&Call);
+  if (!MC)
     return;
 
-  SmallString<100> buf;
-  llvm::raw_svector_ostream os(buf);
+  const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
+  if (!MD)
+    return;
+  ProgramStateRef State = C.getState();
+  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
 
-  // FIXME: The interprocedural diagnostic experience here is not good.
-  // Ultimately this checker should be re-written to be path sensitive.
-  // For now, only diagnose intraprocedurally, by default.
-  if (IsInterprocedural) {
-    os << "Call Path : ";
-    // Name of current visiting CallExpr.
-    os << *CE->getDirectCallee();
-
-    // Name of the CallExpr whose body is current being walked.
-    if (visitingCallExpr)
-      os << " <-- " << *visitingCallExpr->getDirectCallee();
-    // Names of FunctionDecls in worklist with state PostVisited.
-    for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(),
-         E = WList.begin(); I != E; --I) {
-      const FunctionDecl *FD = (*(I-1))->getDirectCallee();
-      assert(FD);
-      if (VisitedFunctions[FD] == PostVisited)
-        os << " <-- " << *FD;
-    }
+  if (IsPureOnly && !MD->isPure())
+    return;
+  if (!isVirtualCall(CE))
+    return;
 
-    os << "\n";
+  const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
+  const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
+  if (!ObState)
+    return;
+  // Check if a virtual method is called.
+  // The GDM of constructor and destructor should be true.
+  if (*ObState == ObjectState::CtorCalled) {
+    if (IsPureOnly && MD->isPure())
+      reportBug("Call to pure virtual function during construction", true, Reg,
+                C);
+    else if (!MD->isPure())
+      reportBug("Call to virtual function during construction", false, Reg, C);
+    else
+      reportBug("Call to pure virtual function during construction", false, Reg,
+                C);
   }
 
-  PathDiagnosticLocation CELoc =
-    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
-  SourceRange R = CE->getCallee()->getSourceRange();
-
-  os << "Call to ";
-  if (isPure)
-    os << "pure ";
+  if (*ObState == ObjectState::DtorCalled) {
+    if (IsPureOnly && MD->isPure())
+      reportBug("Call to pure virtual function during destruction", true, Reg,
+                C);
+    else if (!MD->isPure())
+      reportBug("Call to virtual function during destruction", false, Reg, C);
+    else
+      reportBug("Call to pure virtual function during construction", false, Reg,
+                C);
+  }
+}
 
-  os << "virtual function during ";
+void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
+                                                     CheckerContext &C) const {
+  const auto *LCtx = C.getLocationContext();
+  const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
+  if (!MD)
+    return;
 
-  if (isa<CXXConstructorDecl>(RootMethod))
-    os << "construction ";
-  else
-    os << "destruction ";
+  ProgramStateRef State = C.getState();
+  auto &SVB = C.getSValBuilder();
 
-  if (isPure)
-    os << "has undefined behavior";
-  else
-    os << "will not dispatch to derived class";
+  // Enter a constructor, set the corresponding memregion be true.
+  if (isa<CXXConstructorDecl>(MD)) {
+    auto ThiSVal =
+        State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
+    const MemRegion *Reg = ThiSVal.getAsRegion();
+    if (IsBeginFunction)
+      State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
+    else
+      State = State->remove<CtorDtorMap>(Reg);
 
-  BR.EmitBasicReport(AC->getDecl(), Checker,
-                     "Call to virtual function during construction or "
-                     "destruction",
-                     "C++ Object Lifecycle", os.str(), CELoc, R);
-}
+    C.addTransition(State);
+    return;
+  }
 
-//===----------------------------------------------------------------------===//
-// VirtualCallChecker
-//===----------------------------------------------------------------------===//
+  // Enter a Destructor, set the corresponding memregion be true.
+  if (isa<CXXDestructorDecl>(MD)) {
+    auto ThiSVal =
+        State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
+    const MemRegion *Reg = ThiSVal.getAsRegion();
+    if (IsBeginFunction)
+      State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
+    else
+      State = State->remove<CtorDtorMap>(Reg);
 
-namespace {
-class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
-public:
-  DefaultBool isInterprocedural;
-  DefaultBool isPureOnly;
+    C.addTransition(State);
+    return;
+  }
+}
 
-  void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
-                    BugReporter &BR) const {
-    AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(RD);
-
-    // Check the constructors.
-    for (const auto *I : RD->ctors()) {
-      if (!I->isCopyOrMoveConstructor())
-        if (Stmt *Body = I->getBody()) {
-          WalkAST walker(this, BR, ADC, I, isInterprocedural, isPureOnly);
-          walker.Visit(Body);
-          walker.Execute();
-        }
-    }
+void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
+                                   const MemRegion *Reg,
+                                   CheckerContext &C) const {
+  ExplodedNode *N;
+  if (IsSink)
+    N = C.generateErrorNode();
+  else
+    N = C.generateNonFatalErrorNode();
 
-    // Check the destructor.
-    if (CXXDestructorDecl *DD = RD->getDestructor())
-      if (Stmt *Body = DD->getBody()) {
-        WalkAST walker(this, BR, ADC, DD, isInterprocedural, isPureOnly);
-        walker.Visit(Body);
-        walker.Execute();
-      }
-  }
-};
+  if (!N)
+    return;
+  if (!BT)
+    BT.reset(new BugType(
+        this, "Call to virtual function during construction or destruction",
+        "C++ Object Lifecycle"));
+
+  auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N);
+  Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg));
+  C.emitReport(std::move(Reporter));
 }
 
 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
   VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
-  checker->isInterprocedural =
-      mgr.getAnalyzerOptions().getBooleanOption("Interprocedural", false,
-                                                checker);
-
-  checker->isPureOnly =
-      mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false,
-                                                checker);
+
+  checker->IsPureOnly =
+      mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false, checker);
 }

Modified: cfe/trunk/test/Analysis/virtualcall.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/virtualcall.cpp?rev=311877&r1=311876&r2=311877&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/virtualcall.cpp (original)
+++ cfe/trunk/test/Analysis/virtualcall.cpp Mon Aug 28 01:44:43 2017
@@ -1,98 +1,83 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -verify -std=c++11 %s
-// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-config optin.cplusplus.VirtualCall:Interprocedural=true -DINTERPROCEDURAL=1 -verify -std=c++11 %s
-// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-config optin.cplusplus.VirtualCall:PureOnly=true -DPUREONLY=1 -verify -std=c++11 %s
-
-/* When INTERPROCEDURAL is set, we expect diagnostics in all functions reachable
-   from a constructor or destructor. If it is not set, we expect diagnostics
-   only in the constructor or destructor.
-
-   When PUREONLY is set, we expect diagnostics only for calls to pure virtual
-   functions not to non-pure virtual functions.
-*/
+// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-output=text -verify -std=c++11 %s
+
+// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-config optin.cplusplus.VirtualCall:PureOnly=true -DPUREONLY=1 -analyzer-output=text -verify -std=c++11 %s
+
+#include "virtualcall.h"
 
 class A {
 public:
   A();
-  A(int i);
 
-  ~A() {};
-  
-  virtual int foo() = 0; // from Sema: expected-note {{'foo' declared here}}
+  ~A(){};
+
+  virtual int foo() = 0;
   virtual void bar() = 0;
   void f() {
     foo();
-#if INTERPROCEDURAL
-        // expected-warning-re at -2 {{{{^}}Call Path : foo <-- fCall to pure virtual function during construction has undefined behavior}}
-#endif
+	// expected-warning-re at -1 {{{{^}}Call to pure virtual function during construction}}
+	// expected-note-re at -2 {{{{^}}Call to pure virtual function during construction}}
   }
 };
 
 class B : public A {
 public:
-  B() {
-    foo();
+  B() { // expected-note {{Calling default constructor for 'A'}}
+    foo(); 
 #if !PUREONLY
-#if INTERPROCEDURAL
-        // expected-warning-re at -3 {{{{^}}Call Path : fooCall to virtual function during construction will not dispatch to derived class}}
-#else
-        // expected-warning-re at -5 {{{{^}}Call to virtual function during construction will not dispatch to derived class}}
+  	// expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+	// expected-note-re at -3 {{{{^}}This constructor of an object of type 'B' has not returned when the virtual method was called}}
+  	// expected-note-re at -4 {{{{^}}Call to virtual function during construction}}
 #endif
-#endif
-
   }
   ~B();
-  
+
   virtual int foo();
-  virtual void bar() { foo(); }
-#if INTERPROCEDURAL
-      // expected-warning-re at -2 {{{{^}}Call Path : foo <-- barCall to virtual function during destruction will not dispatch to derived class}}
+  virtual void bar() {
+    foo(); 
+#if !PUREONLY
+  	// expected-warning-re at -2 {{{{^}}Call to virtual function during destruction}}
+  	// expected-note-re at -3 {{{{^}}Call to virtual function during destruction}}
 #endif
+  } 
 };
 
-A::A() {
-  f();
-}
-
-A::A(int i) {
-  foo(); // From Sema: expected-warning {{call to pure virtual member function 'foo' has undefined behavior}}
-#if INTERPROCEDURAL
-      // expected-warning-re at -2 {{{{^}}Call Path : fooCall to pure virtual function during construction has undefined behavior}}
-#else
-      // expected-warning-re at -4 {{{{^}}Call to pure virtual function during construction has undefined behavior}}
-#endif
+A::A() { 
+  f(); 
+// expected-note-re at -1 {{{{^}}This constructor of an object of type 'A' has not returned when the virtual method was called}}
+// expected-note-re at -2 {{{{^}}Calling 'A::f'}}
 }
 
 B::~B() {
   this->B::foo(); // no-warning
   this->B::bar();
-  this->foo();
 #if !PUREONLY
-#if INTERPROCEDURAL
-      // expected-warning-re at -3 {{{{^}}Call Path : fooCall to virtual function during destruction will not dispatch to derived class}}
-#else
-      // expected-warning-re at -5 {{{{^}}Call to virtual function during destruction will not dispatch to derived class}}
+ 	 // expected-note-re at -2 {{{{^}}This destructor of an object of type '~B' has not returned when the virtual method was called}}
+ 	 // expected-note-re at -3 {{{{^}}Calling 'B::bar'}}
 #endif
+  this->foo(); 
+#if !PUREONLY
+ 	 // expected-warning-re at -2 {{{{^}}Call to virtual function during destruction}}
+ 	 // expected-note-re at -3 {{{{^}}This destructor of an object of type '~B' has not returned when the virtual method was called}}
+ 	 // expected-note-re at -4 {{{{^}}Call to virtual function during destruction}}
 #endif
-
+	
 }
 
 class C : public B {
 public:
   C();
   ~C();
-  
+
   virtual int foo();
   void f(int i);
 };
 
 C::C() {
-  f(foo());
+  f(foo()); 
 #if !PUREONLY
-#if INTERPROCEDURAL
-      // expected-warning-re at -3 {{{{^}}Call Path : fooCall to virtual function during construction will not dispatch to derived class}}
-#else
-      // expected-warning-re at -5 {{{{^}}Call to virtual function during construction will not dispatch to derived class}}
-#endif
+  	// expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+	// expected-note-re at -3 {{{{^}}This constructor of an object of type 'C' has not returned when the virtual method was called}}
+  	// expected-note-re at -4 {{{{^}}Call to virtual function during construction}}
 #endif
 }
 
@@ -112,30 +97,177 @@ public:
     foo(); // no-warning
   }
   ~E() { bar(); }
+#if !PUREONLY
+ 	 // expected-note-re at -2 2{{{{^}}Calling '~B'}}
+#endif
   int foo() override;
 };
 
-// Regression test: don't crash when there's no direct callee.
 class F {
 public:
   F() {
-    void (F::* ptr)() = &F::foo;
+    void (F::*ptr)() = &F::foo;
     (this->*ptr)();
   }
   void foo();
 };
 
-int main() {
-  A *a;
-  B *b;
-  C *c;
-  D *d;
-  E *e;
-  F *f;
+class G {
+public:
+  G() {}
+  virtual void bar();
+  void foo() {
+    bar(); // no warning
+  }
+};
+
+class H {
+public:
+  H() : initState(0) { init(); }
+  int initState;
+  virtual void f() const;
+  void init() {
+    if (initState)
+      f(); // no warning
+  }
+
+  H(int i) {
+    G g;
+    g.foo();
+    g.bar(); // no warning
+    f();     
+#if !PUREONLY
+  	// expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+	// expected-note-re at -3 {{{{^}}This constructor of an object of type 'H' has not returned when the virtual method was called}}
+  	// expected-note-re at -4 {{{{^}}Call to virtual function during construction}}
+#endif
+    H &h = *this;
+    h.f(); 
+#if !PUREONLY
+  	// expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+  	// expected-note-re at -3 {{{{^}}This constructor of an object of type 'H' has not returned when the virtual method was called}}
+  	// expected-note-re at -4 {{{{^}}Call to virtual function during construction}}
+#endif
+  }
+};
+
+class X {
+public:
+  X() {
+    g(); 
+#if !PUREONLY
+  	// expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+	// expected-note-re at -3 {{{{^}}This constructor of an object of type 'X' has not returned when the virtual method was called}}
+  	// expected-note-re at -4 {{{{^}}Call to virtual function during construction}}
+#endif
+  }
+  X(int i) {
+    if (i > 0) {
+#if !PUREONLY
+	// expected-note-re at -2 {{{{^}}Taking true branch}}
+	// expected-note-re at -3 {{{{^}}Taking false branch}}
+#endif
+      X x(i - 1);
+#if !PUREONLY
+	// expected-note-re at -2 {{{{^}}Calling constructor for 'X'}}
+#endif
+      x.g(); // no warning
+    }
+    g(); 
+#if !PUREONLY
+  	// expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+	// expected-note-re at -3 {{{{^}}This constructor of an object of type 'X' has not returned when the virtual method was called}}
+  	// expected-note-re at -4 {{{{^}}Call to virtual function during construction}}
+#endif
+  }
+  virtual void g();
+};
+
+class M;
+class N {
+public:
+  virtual void virtualMethod();
+  void callFooOfM(M *);
+};
+class M {
+public:
+  M() {
+    N n;
+    n.virtualMethod(); // no warning
+    n.callFooOfM(this);
+#if !PUREONLY
+  	// expected-note-re at -2 {{{{^}}This constructor of an object of type 'M' has not returned when the virtual method was called}}
+	// expected-note-re at -3 {{{{^}}Calling 'N::callFooOfM'}}
+#endif
+  }
+  virtual void foo();
+};
+void N::callFooOfM(M *m) {
+  m->foo(); 
+#if !PUREONLY
+  	// expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+  	// expected-note-re at -3 {{{{^}}Call to virtual function during construction}}
+#endif
 }
 
-#include "virtualcall.h"
+class Y {
+public:
+  virtual void foobar();
+  void fooY() {
+    F f1;
+    foobar(); 
+#if !PUREONLY
+  	// expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+  	// expected-note-re at -3 {{{{^}}Call to virtual function during construction}}
+#endif
+  }
+  Y() { fooY(); }
+#if !PUREONLY
+  	// expected-note-re at -2 {{{{^}}This constructor of an object of type 'Y' has not returned when the virtual method was called}}
+  	// expected-note-re at -3 {{{{^}}Calling 'Y::fooY'}}
+#endif
+};
 
-#define AS_SYSTEM
-#include "virtualcall.h"
-#undef AS_SYSTEM
+int main() {
+  B b;
+#if PUREONLY
+	//expected-note-re at -2 {{{{^}}Calling default constructor for 'B'}}
+#else 
+	//expected-note-re at -4 2{{{{^}}Calling default constructor for 'B'}}
+#endif
+  C c;
+#if !PUREONLY
+	//expected-note-re at -2 {{{{^}}Calling default constructor for 'C'}}
+#endif
+  D d;
+  E e;
+  F f;
+  G g;
+  H h;
+  H h1(1);
+#if !PUREONLY
+	//expected-note-re at -2 {{{{^}}Calling constructor for 'H'}}
+	//expected-note-re at -3 {{{{^}}Calling constructor for 'H'}}
+#endif
+  X x; 
+#if !PUREONLY
+	//expected-note-re at -2 {{{{^}}Calling default constructor for 'X'}}
+#endif
+  X x1(1);
+#if !PUREONLY
+	//expected-note-re at -2 {{{{^}}Calling constructor for 'X'}}
+#endif
+  M m;
+#if !PUREONLY
+	//expected-note-re at -2 {{{{^}}Calling default constructor for 'M'}}
+#endif
+  Y *y = new Y;
+  delete y;
+  header::Z z;
+#if !PUREONLY
+	// expected-note-re at -2 {{{{^}}Calling default constructor for 'Z'}}
+#endif
+}
+#if !PUREONLY
+	//expected-note-re at -2 2{{{{^}}Calling '~E'}}
+#endif

Modified: cfe/trunk/test/Analysis/virtualcall.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/virtualcall.h?rev=311877&r1=311876&r2=311877&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/virtualcall.h (original)
+++ cfe/trunk/test/Analysis/virtualcall.h Mon Aug 28 01:44:43 2017
@@ -1,36 +1,14 @@
-#ifdef AS_SYSTEM
-#pragma clang system_header
-
-namespace system {
-  class A {
-  public:
-    A() {
-      foo(); // no-warning
-    }
-
-    virtual int foo();
-  };
-}
-
-#else
-
 namespace header {
-  class A {
+  class Z {
   public:
-    A() {
+    Z() {
       foo();
 #if !PUREONLY
-#if INTERPROCEDURAL
-          // expected-warning-re at -3 {{{{^}}Call Path : fooCall to virtual function during construction will not dispatch to derived class}}
-#else
-          // expected-warning-re at -5 {{{{^}}Call to virtual function during construction will not dispatch to derived class}}
-#endif
+	// expected-warning-re at -2 {{{{^}}Call to virtual function during construction}}
+	// expected-note-re at -3 {{{{^}}This constructor of an object of type 'Z' has not returned when the virtual method was called}}
+	// expected-note-re at -4 {{{{^}}Call to virtual function during construction}}	
 #endif
-
     }
-
     virtual int foo();
   };
 }
-
-#endif




More information about the cfe-commits mailing list