[cfe-commits] r147494 - in /cfe/trunk: lib/StaticAnalyzer/Checkers/CMakeLists.txt lib/StaticAnalyzer/Checkers/Checkers.td lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp test/Analysis/virtualcall.cpp

Ted Kremenek kremenek at apple.com
Tue Jan 3 15:18:58 PST 2012


Author: kremenek
Date: Tue Jan  3 17:18:57 2012
New Revision: 147494

URL: http://llvm.org/viewvc/llvm-project?rev=147494&view=rev
Log:
Add initial version of checker to check if virtual member functions are called transitively
from C++ constructors or destructors.  Checker by Lei Zhang with a few tweaks by Ted Kremenek.

Added:
    cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
    cfe/trunk/test/Analysis/virtualcall.cpp
Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
    cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt?rev=147494&r1=147493&r2=147494&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt Tue Jan  3 17:18:57 2012
@@ -62,6 +62,7 @@
   UnixAPIChecker.cpp
   UnreachableCodeChecker.cpp
   VLASizeChecker.cpp
+  VirtualCallChecker.cpp
   )
 
 add_dependencies(clangStaticAnalyzerCheckers

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td?rev=147494&r1=147493&r2=147494&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td Tue Jan  3 17:18:57 2012
@@ -164,6 +164,10 @@
   HelpText<"Check improper uses of STL vector iterators">,
   DescFile<"IteratorsChecker.cpp">;
 
+def VirtualCallChecker : Checker<"VirtualCall">,
+  HelpText<"Check virtual function calls during construction or destruction">, 
+  DescFile<"VirtualCallChecker.cpp">;
+
 } // end: "cplusplus.experimental"
 
 //===----------------------------------------------------------------------===//

Added: cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp?rev=147494&view=auto
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp (added)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp Tue Jan  3 17:18:57 2012
@@ -0,0 +1,238 @@
+//=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines a checker that checks virtual function calls during 
+//  construction or destruction of C++ objects.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/Support/SaveAndRestore.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class WalkAST : public StmtVisitor<WalkAST> {
+  BugReporter &BR;
+  AnalysisDeclContext *AC;
+
+  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. */
+  } K;
+
+  /// 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(BugReporter &br, AnalysisDeclContext *ac)
+    : BR(br),
+      AC(ac),
+      visitingCallExpr(0) {}
+  
+  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);
+  }
+
+  /// This method returns an item from the worklist without removing it.
+  WorkListUnit Dequeue() {
+    assert(!WList.empty());
+    return WList.back();    
+  }
+  
+  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();
+    }
+  }
+
+  // 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);
+
+};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// AST walking.
+//===----------------------------------------------------------------------===//
+
+void WalkAST::VisitChildren(Stmt *S) {
+  for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
+    if (Stmt *child = *I)
+      Visit(child);
+}
+
+void WalkAST::VisitCallExpr(CallExpr *CE) {
+  VisitChildren(CE);
+  Enqueue(CE);
+}
+
+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.
+    if (CME->getQualifier())
+      callIsNonVirtual = true;
+
+    // Elide analyzing the call entirely if the base pointer is not 'this'.
+    if (Expr *base = CME->getBase()->IgnoreImpCasts())
+      if (!isa<CXXThisExpr>(base))
+        return;
+  }
+
+  // Get the callee.
+  const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee());
+  if (MD && MD->isVirtual() && !callIsNonVirtual)
+    ReportVirtualCall(CE, MD->isPure());
+
+  Enqueue(CE);
+}
+
+void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
+  llvm::SmallString<100> buf;
+  llvm::raw_svector_ostream os(buf);
+  
+  os << "Call Path : ";
+  // Name of current visiting CallExpr.
+  os << CE->getDirectCallee()->getNameAsString();
+
+  // Name of the CallExpr whose body is current walking.
+  if (visitingCallExpr)
+    os << " <-- " << visitingCallExpr->getDirectCallee()->getNameAsString();
+  // 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->getNameAsString();
+  }
+
+  PathDiagnosticLocation CELoc =
+    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+  SourceRange R = CE->getCallee()->getSourceRange();
+  
+  if (isPure) {
+    os << "\n" <<  "Call pure virtual functions during construction or "
+       << "destruction may leads undefined behaviour";
+    BR.EmitBasicReport("Call pure virtual function during construction or "
+                       "Destruction",
+                       "Cplusplus",
+                       os.str(), CELoc, &R, 1);
+    return;
+  }
+  else {
+    os << "\n" << "Call virtual functions during construction or "
+       << "destruction will never go to a more derived class";
+    BR.EmitBasicReport("Call virtual function during construction or "
+                       "Destruction",
+                       "Cplusplus",
+                       os.str(), CELoc, &R, 1);
+    return;
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// VirtualCallChecker
+//===----------------------------------------------------------------------===//
+
+namespace {
+class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
+public:
+  void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
+                    BugReporter &BR) const {
+    WalkAST walker(BR, mgr.getAnalysisDeclContext(RD));
+
+    // Check the constructors.
+    for (CXXRecordDecl::ctor_iterator I = RD->ctor_begin(), E = RD->ctor_end();
+         I != E; ++I) {
+      if (!I->isCopyOrMoveConstructor())
+        if (Stmt *Body = I->getBody()) {
+          walker.Visit(Body);
+          walker.Execute();
+        }
+    }
+
+    // Check the destructor.
+    if (CXXDestructorDecl *DD = RD->getDestructor())
+      if (Stmt *Body = DD->getBody()) {
+        walker.Visit(Body);
+        walker.Execute();
+      }
+  }
+};
+}
+
+void ento::registerVirtualCallChecker(CheckerManager &mgr) {
+  mgr.registerChecker<VirtualCallChecker>();
+}

Added: cfe/trunk/test/Analysis/virtualcall.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/virtualcall.cpp?rev=147494&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/virtualcall.cpp (added)
+++ cfe/trunk/test/Analysis/virtualcall.cpp Tue Jan  3 17:18:57 2012
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=experimental.cplusplus.VirtualCall -analyzer-store region -verify %s
+
+class A {
+public:
+  A();
+  ~A() {};
+  
+  virtual int foo() = 0;
+  virtual void bar() = 0;
+  void f() {
+    foo(); // expected-warning{{Call pure virtual functions during construction or destruction may leads undefined behaviour}}
+  }
+};
+
+class B : public A {
+public:
+  B() {
+    foo(); // expected-warning{{Call virtual functions during construction or destruction will never go to a more derived class}}
+  }
+  ~B();
+  
+  virtual int foo();
+  virtual void bar() { foo(); }  // expected-warning{{Call virtual functions during construction or destruction will never go to a more derived class}}
+};
+
+A::A() {
+  f();
+}
+
+B::~B() {
+  this->B::foo(); // no-warning
+  this->B::bar();
+  this->foo(); // expected-warning{{Call virtual functions during construction or destruction will never go to a more derived class}}
+}
+
+class C : public B {
+public:
+  C();
+  ~C();
+  
+  virtual int foo();
+  void f(int i);
+};
+
+C::C() {
+  f(foo()); // expected-warning{{Call virtual functions during construction or destruction will never go to a more derived class}}
+}
+
+int main() {
+  A *a;
+  B *b;
+  C *c;
+}





More information about the cfe-commits mailing list