clang API Documentation

VirtualCallChecker.cpp
Go to the documentation of this file.
00001 //=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 //  This file defines a checker that checks virtual function calls during 
00011 //  construction or destruction of C++ objects.
00012 //
00013 //===----------------------------------------------------------------------===//
00014 
00015 #include "ClangSACheckers.h"
00016 #include "clang/AST/DeclCXX.h"
00017 #include "clang/AST/StmtVisitor.h"
00018 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
00019 #include "clang/StaticAnalyzer/Core/Checker.h"
00020 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
00021 #include "llvm/ADT/SmallString.h"
00022 #include "llvm/Support/SaveAndRestore.h"
00023 #include "llvm/Support/raw_ostream.h"
00024 
00025 using namespace clang;
00026 using namespace ento;
00027 
00028 namespace {
00029 
00030 class WalkAST : public StmtVisitor<WalkAST> {
00031   const CheckerBase *Checker;
00032   BugReporter &BR;
00033   AnalysisDeclContext *AC;
00034 
00035   typedef const CallExpr * WorkListUnit;
00036   typedef SmallVector<WorkListUnit, 20> DFSWorkList;
00037 
00038   /// A vector representing the worklist which has a chain of CallExprs.
00039   DFSWorkList WList;
00040   
00041   // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
00042   // body has not been visited yet.
00043   // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
00044   // body has been visited.
00045   enum Kind { NotVisited,
00046               PreVisited,  /**< A CallExpr to this FunctionDecl is in the 
00047                                 worklist, but the body has not yet been
00048                                 visited. */
00049               PostVisited  /**< A CallExpr to this FunctionDecl is in the
00050                                 worklist, and the body has been visited. */
00051   };
00052 
00053   /// A DenseMap that records visited states of FunctionDecls.
00054   llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions;
00055 
00056   /// The CallExpr whose body is currently being visited.  This is used for
00057   /// generating bug reports.  This is null while visiting the body of a
00058   /// constructor or destructor.
00059   const CallExpr *visitingCallExpr;
00060   
00061 public:
00062   WalkAST(const CheckerBase *checker, BugReporter &br,
00063           AnalysisDeclContext *ac)
00064       : Checker(checker), BR(br), AC(ac), visitingCallExpr(nullptr) {}
00065 
00066   bool hasWork() const { return !WList.empty(); }
00067 
00068   /// This method adds a CallExpr to the worklist and marks the callee as
00069   /// being PreVisited.
00070   void Enqueue(WorkListUnit WLUnit) {
00071     const FunctionDecl *FD = WLUnit->getDirectCallee();
00072     if (!FD || !FD->getBody())
00073       return;    
00074     Kind &K = VisitedFunctions[FD];
00075     if (K != NotVisited)
00076       return;
00077     K = PreVisited;
00078     WList.push_back(WLUnit);
00079   }
00080 
00081   /// This method returns an item from the worklist without removing it.
00082   WorkListUnit Dequeue() {
00083     assert(!WList.empty());
00084     return WList.back();    
00085   }
00086   
00087   void Execute() {
00088     while (hasWork()) {
00089       WorkListUnit WLUnit = Dequeue();
00090       const FunctionDecl *FD = WLUnit->getDirectCallee();
00091       assert(FD && FD->getBody());
00092 
00093       if (VisitedFunctions[FD] == PreVisited) {
00094         // If the callee is PreVisited, walk its body.
00095         // Visit the body.
00096         SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit);
00097         Visit(FD->getBody());
00098         
00099         // Mark the function as being PostVisited to indicate we have
00100         // scanned the body.
00101         VisitedFunctions[FD] = PostVisited;
00102         continue;
00103       }
00104 
00105       // Otherwise, the callee is PostVisited.
00106       // Remove it from the worklist.
00107       assert(VisitedFunctions[FD] == PostVisited);
00108       WList.pop_back();
00109     }
00110   }
00111 
00112   // Stmt visitor methods.
00113   void VisitCallExpr(CallExpr *CE);
00114   void VisitCXXMemberCallExpr(CallExpr *CE);
00115   void VisitStmt(Stmt *S) { VisitChildren(S); }
00116   void VisitChildren(Stmt *S);
00117   
00118   void ReportVirtualCall(const CallExpr *CE, bool isPure);
00119 
00120 };
00121 } // end anonymous namespace
00122 
00123 //===----------------------------------------------------------------------===//
00124 // AST walking.
00125 //===----------------------------------------------------------------------===//
00126 
00127 void WalkAST::VisitChildren(Stmt *S) {
00128   for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
00129     if (Stmt *child = *I)
00130       Visit(child);
00131 }
00132 
00133 void WalkAST::VisitCallExpr(CallExpr *CE) {
00134   VisitChildren(CE);
00135   Enqueue(CE);
00136 }
00137 
00138 void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
00139   VisitChildren(CE);
00140   bool callIsNonVirtual = false;
00141   
00142   // Several situations to elide for checking.
00143   if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
00144     // If the member access is fully qualified (i.e., X::F), then treat
00145     // this as a non-virtual call and do not warn.
00146     if (CME->getQualifier())
00147       callIsNonVirtual = true;
00148 
00149     if (Expr *base = CME->getBase()->IgnoreImpCasts()) {
00150       // Elide analyzing the call entirely if the base pointer is not 'this'.
00151       if (!isa<CXXThisExpr>(base))
00152         return;
00153 
00154       // If the most derived class is marked final, we know that now subclass
00155       // can override this member.
00156       if (base->getBestDynamicClassType()->hasAttr<FinalAttr>())
00157         callIsNonVirtual = true;
00158     }
00159   }
00160 
00161   // Get the callee.
00162   const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee());
00163   if (MD && MD->isVirtual() && !callIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
00164       !MD->getParent()->hasAttr<FinalAttr>())
00165     ReportVirtualCall(CE, MD->isPure());
00166 
00167   Enqueue(CE);
00168 }
00169 
00170 void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
00171   SmallString<100> buf;
00172   llvm::raw_svector_ostream os(buf);
00173   
00174   os << "Call Path : ";
00175   // Name of current visiting CallExpr.
00176   os << *CE->getDirectCallee();
00177 
00178   // Name of the CallExpr whose body is current walking.
00179   if (visitingCallExpr)
00180     os << " <-- " << *visitingCallExpr->getDirectCallee();
00181   // Names of FunctionDecls in worklist with state PostVisited.
00182   for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(),
00183          E = WList.begin(); I != E; --I) {
00184     const FunctionDecl *FD = (*(I-1))->getDirectCallee();
00185     assert(FD);
00186     if (VisitedFunctions[FD] == PostVisited)
00187       os << " <-- " << *FD;
00188   }
00189 
00190   PathDiagnosticLocation CELoc =
00191     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
00192   SourceRange R = CE->getCallee()->getSourceRange();
00193   
00194   if (isPure) {
00195     os << "\n" <<  "Call pure virtual functions during construction or "
00196        << "destruction may leads undefined behaviour";
00197     BR.EmitBasicReport(AC->getDecl(), Checker,
00198                        "Call pure virtual function during construction or "
00199                        "Destruction",
00200                        "Cplusplus", os.str(), CELoc, R);
00201     return;
00202   }
00203   else {
00204     os << "\n" << "Call virtual functions during construction or "
00205        << "destruction will never go to a more derived class";
00206     BR.EmitBasicReport(AC->getDecl(), Checker,
00207                        "Call virtual function during construction or "
00208                        "Destruction",
00209                        "Cplusplus", os.str(), CELoc, R);
00210     return;
00211   }
00212 }
00213 
00214 //===----------------------------------------------------------------------===//
00215 // VirtualCallChecker
00216 //===----------------------------------------------------------------------===//
00217 
00218 namespace {
00219 class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
00220 public:
00221   void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
00222                     BugReporter &BR) const {
00223     WalkAST walker(this, BR, mgr.getAnalysisDeclContext(RD));
00224 
00225     // Check the constructors.
00226     for (const auto *I : RD->ctors()) {
00227       if (!I->isCopyOrMoveConstructor())
00228         if (Stmt *Body = I->getBody()) {
00229           walker.Visit(Body);
00230           walker.Execute();
00231         }
00232     }
00233 
00234     // Check the destructor.
00235     if (CXXDestructorDecl *DD = RD->getDestructor())
00236       if (Stmt *Body = DD->getBody()) {
00237         walker.Visit(Body);
00238         walker.Execute();
00239       }
00240   }
00241 };
00242 }
00243 
00244 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
00245   mgr.registerChecker<VirtualCallChecker>();
00246 }