Home | History | Annotate | Download | only in Checkers
      1 //=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 //  This file defines a checker that checks virtual function calls during
     11 //  construction or destruction of C++ objects.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "ClangSACheckers.h"
     16 #include "clang/AST/DeclCXX.h"
     17 #include "clang/AST/StmtVisitor.h"
     18 #include "llvm/Support/SaveAndRestore.h"
     19 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
     20 #include "clang/StaticAnalyzer/Core/Checker.h"
     21 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
     22 #include "llvm/ADT/SmallString.h"
     23 
     24 using namespace clang;
     25 using namespace ento;
     26 
     27 namespace {
     28 
     29 class WalkAST : public StmtVisitor<WalkAST> {
     30   BugReporter &BR;
     31   AnalysisDeclContext *AC;
     32 
     33   typedef const CallExpr * WorkListUnit;
     34   typedef SmallVector<WorkListUnit, 20> DFSWorkList;
     35 
     36   /// A vector representing the worklist which has a chain of CallExprs.
     37   DFSWorkList WList;
     38 
     39   // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
     40   // body has not been visited yet.
     41   // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
     42   // body has been visited.
     43   enum Kind { NotVisited,
     44               PreVisited,  /**< A CallExpr to this FunctionDecl is in the
     45                                 worklist, but the body has not yet been
     46                                 visited. */
     47               PostVisited  /**< A CallExpr to this FunctionDecl is in the
     48                                 worklist, and the body has been visited. */
     49   } K;
     50 
     51   /// A DenseMap that records visited states of FunctionDecls.
     52   llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions;
     53 
     54   /// The CallExpr whose body is currently being visited.  This is used for
     55   /// generating bug reports.  This is null while visiting the body of a
     56   /// constructor or destructor.
     57   const CallExpr *visitingCallExpr;
     58 
     59 public:
     60   WalkAST(BugReporter &br, AnalysisDeclContext *ac)
     61     : BR(br),
     62       AC(ac),
     63       visitingCallExpr(0) {}
     64 
     65   bool hasWork() const { return !WList.empty(); }
     66 
     67   /// This method adds a CallExpr to the worklist and marks the callee as
     68   /// being PreVisited.
     69   void Enqueue(WorkListUnit WLUnit) {
     70     const FunctionDecl *FD = WLUnit->getDirectCallee();
     71     if (!FD || !FD->getBody())
     72       return;
     73     Kind &K = VisitedFunctions[FD];
     74     if (K != NotVisited)
     75       return;
     76     K = PreVisited;
     77     WList.push_back(WLUnit);
     78   }
     79 
     80   /// This method returns an item from the worklist without removing it.
     81   WorkListUnit Dequeue() {
     82     assert(!WList.empty());
     83     return WList.back();
     84   }
     85 
     86   void Execute() {
     87     while (hasWork()) {
     88       WorkListUnit WLUnit = Dequeue();
     89       const FunctionDecl *FD = WLUnit->getDirectCallee();
     90       assert(FD && FD->getBody());
     91 
     92       if (VisitedFunctions[FD] == PreVisited) {
     93         // If the callee is PreVisited, walk its body.
     94         // Visit the body.
     95         SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit);
     96         Visit(FD->getBody());
     97 
     98         // Mark the function as being PostVisited to indicate we have
     99         // scanned the body.
    100         VisitedFunctions[FD] = PostVisited;
    101         continue;
    102       }
    103 
    104       // Otherwise, the callee is PostVisited.
    105       // Remove it from the worklist.
    106       assert(VisitedFunctions[FD] == PostVisited);
    107       WList.pop_back();
    108     }
    109   }
    110 
    111   // Stmt visitor methods.
    112   void VisitCallExpr(CallExpr *CE);
    113   void VisitCXXMemberCallExpr(CallExpr *CE);
    114   void VisitStmt(Stmt *S) { VisitChildren(S); }
    115   void VisitChildren(Stmt *S);
    116 
    117   void ReportVirtualCall(const CallExpr *CE, bool isPure);
    118 
    119 };
    120 } // end anonymous namespace
    121 
    122 //===----------------------------------------------------------------------===//
    123 // AST walking.
    124 //===----------------------------------------------------------------------===//
    125 
    126 void WalkAST::VisitChildren(Stmt *S) {
    127   for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
    128     if (Stmt *child = *I)
    129       Visit(child);
    130 }
    131 
    132 void WalkAST::VisitCallExpr(CallExpr *CE) {
    133   VisitChildren(CE);
    134   Enqueue(CE);
    135 }
    136 
    137 void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
    138   VisitChildren(CE);
    139   bool callIsNonVirtual = false;
    140 
    141   // Several situations to elide for checking.
    142   if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
    143     // If the member access is fully qualified (i.e., X::F), then treat
    144     // this as a non-virtual call and do not warn.
    145     if (CME->getQualifier())
    146       callIsNonVirtual = true;
    147 
    148     // Elide analyzing the call entirely if the base pointer is not 'this'.
    149     if (Expr *base = CME->getBase()->IgnoreImpCasts())
    150       if (!isa<CXXThisExpr>(base))
    151         return;
    152   }
    153 
    154   // Get the callee.
    155   const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee());
    156   if (MD && MD->isVirtual() && !callIsNonVirtual)
    157     ReportVirtualCall(CE, MD->isPure());
    158 
    159   Enqueue(CE);
    160 }
    161 
    162 void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
    163   SmallString<100> buf;
    164   llvm::raw_svector_ostream os(buf);
    165 
    166   os << "Call Path : ";
    167   // Name of current visiting CallExpr.
    168   os << *CE->getDirectCallee();
    169 
    170   // Name of the CallExpr whose body is current walking.
    171   if (visitingCallExpr)
    172     os << " <-- " << *visitingCallExpr->getDirectCallee();
    173   // Names of FunctionDecls in worklist with state PostVisited.
    174   for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(),
    175          E = WList.begin(); I != E; --I) {
    176     const FunctionDecl *FD = (*(I-1))->getDirectCallee();
    177     assert(FD);
    178     if (VisitedFunctions[FD] == PostVisited)
    179       os << " <-- " << *FD;
    180   }
    181 
    182   PathDiagnosticLocation CELoc =
    183     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
    184   SourceRange R = CE->getCallee()->getSourceRange();
    185 
    186   if (isPure) {
    187     os << "\n" <<  "Call pure virtual functions during construction or "
    188        << "destruction may leads undefined behaviour";
    189     BR.EmitBasicReport(AC->getDecl(),
    190                        "Call pure virtual function during construction or "
    191                        "Destruction",
    192                        "Cplusplus",
    193                        os.str(), CELoc, &R, 1);
    194     return;
    195   }
    196   else {
    197     os << "\n" << "Call virtual functions during construction or "
    198        << "destruction will never go to a more derived class";
    199     BR.EmitBasicReport(AC->getDecl(),
    200                        "Call virtual function during construction or "
    201                        "Destruction",
    202                        "Cplusplus",
    203                        os.str(), CELoc, &R, 1);
    204     return;
    205   }
    206 }
    207 
    208 //===----------------------------------------------------------------------===//
    209 // VirtualCallChecker
    210 //===----------------------------------------------------------------------===//
    211 
    212 namespace {
    213 class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
    214 public:
    215   void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
    216                     BugReporter &BR) const {
    217     WalkAST walker(BR, mgr.getAnalysisDeclContext(RD));
    218 
    219     // Check the constructors.
    220     for (CXXRecordDecl::ctor_iterator I = RD->ctor_begin(), E = RD->ctor_end();
    221          I != E; ++I) {
    222       if (!I->isCopyOrMoveConstructor())
    223         if (Stmt *Body = I->getBody()) {
    224           walker.Visit(Body);
    225           walker.Execute();
    226         }
    227     }
    228 
    229     // Check the destructor.
    230     if (CXXDestructorDecl *DD = RD->getDestructor())
    231       if (Stmt *Body = DD->getBody()) {
    232         walker.Visit(Body);
    233         walker.Execute();
    234       }
    235   }
    236 };
    237 }
    238 
    239 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
    240   mgr.registerChecker<VirtualCallChecker>();
    241 }
    242