VirtualCallChecker.cpp revision 239462
1240116Smarcel//=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==// 2240116Smarcel// 3240116Smarcel// The LLVM Compiler Infrastructure 4240116Smarcel// 5240116Smarcel// This file is distributed under the University of Illinois Open Source 6240116Smarcel// License. See LICENSE.TXT for details. 7240116Smarcel// 8240116Smarcel//===----------------------------------------------------------------------===// 9240116Smarcel// 10240116Smarcel// This file defines a checker that checks virtual function calls during 11240116Smarcel// construction or destruction of C++ objects. 12240116Smarcel// 13240116Smarcel//===----------------------------------------------------------------------===// 14240116Smarcel 15240116Smarcel#include "ClangSACheckers.h" 16240116Smarcel#include "clang/AST/DeclCXX.h" 17240116Smarcel#include "clang/AST/StmtVisitor.h" 18240116Smarcel#include "llvm/Support/SaveAndRestore.h" 19240116Smarcel#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 20240116Smarcel#include "clang/StaticAnalyzer/Core/Checker.h" 21240116Smarcel#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 22240116Smarcel#include "llvm/ADT/SmallString.h" 23240116Smarcel 24240116Smarcelusing namespace clang; 25240116Smarcelusing namespace ento; 26240116Smarcel 27240116Smarcelnamespace { 28240116Smarcel 29240116Smarcelclass WalkAST : public StmtVisitor<WalkAST> { 30240116Smarcel BugReporter &BR; 31240116Smarcel AnalysisDeclContext *AC; 32240116Smarcel 33240116Smarcel typedef const CallExpr * WorkListUnit; 34240116Smarcel typedef SmallVector<WorkListUnit, 20> DFSWorkList; 35240116Smarcel 36240116Smarcel /// A vector representing the worklist which has a chain of CallExprs. 37240116Smarcel DFSWorkList WList; 38240116Smarcel 39240116Smarcel // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the 40240116Smarcel // body has not been visited yet. 41240116Smarcel // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the 42240116Smarcel // body has been visited. 43240116Smarcel enum Kind { NotVisited, 44240116Smarcel PreVisited, /**< A CallExpr to this FunctionDecl is in the 45240116Smarcel worklist, but the body has not yet been 46240116Smarcel visited. */ 47240116Smarcel PostVisited /**< A CallExpr to this FunctionDecl is in the 48240116Smarcel worklist, and the body has been visited. */ 49240116Smarcel }; 50240116Smarcel 51240116Smarcel /// A DenseMap that records visited states of FunctionDecls. 52240116Smarcel llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions; 53240116Smarcel 54240116Smarcel /// The CallExpr whose body is currently being visited. This is used for 55240116Smarcel /// generating bug reports. This is null while visiting the body of a 56240116Smarcel /// constructor or destructor. 57240116Smarcel const CallExpr *visitingCallExpr; 58240116Smarcel 59240116Smarcelpublic: 60240116Smarcel WalkAST(BugReporter &br, AnalysisDeclContext *ac) 61240116Smarcel : BR(br), 62240116Smarcel AC(ac), 63240116Smarcel visitingCallExpr(0) {} 64240116Smarcel 65240116Smarcel bool hasWork() const { return !WList.empty(); } 66240116Smarcel 67240116Smarcel /// This method adds a CallExpr to the worklist and marks the callee as 68240116Smarcel /// being PreVisited. 69240116Smarcel void Enqueue(WorkListUnit WLUnit) { 70240116Smarcel const FunctionDecl *FD = WLUnit->getDirectCallee(); 71240116Smarcel if (!FD || !FD->getBody()) 72240116Smarcel return; 73240116Smarcel Kind &K = VisitedFunctions[FD]; 74240116Smarcel if (K != NotVisited) 75240116Smarcel return; 76240116Smarcel K = PreVisited; 77240116Smarcel WList.push_back(WLUnit); 78240116Smarcel } 79240116Smarcel 80240116Smarcel /// This method returns an item from the worklist without removing it. 81240116Smarcel WorkListUnit Dequeue() { 82240116Smarcel assert(!WList.empty()); 83240116Smarcel return WList.back(); 84240116Smarcel } 85240116Smarcel 86240116Smarcel void Execute() { 87240116Smarcel while (hasWork()) { 88240116Smarcel WorkListUnit WLUnit = Dequeue(); 89240116Smarcel const FunctionDecl *FD = WLUnit->getDirectCallee(); 90240116Smarcel assert(FD && FD->getBody()); 91240116Smarcel 92240116Smarcel if (VisitedFunctions[FD] == PreVisited) { 93240116Smarcel // If the callee is PreVisited, walk its body. 94240116Smarcel // Visit the body. 95240116Smarcel SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit); 96240116Smarcel Visit(FD->getBody()); 97240116Smarcel 98240116Smarcel // Mark the function as being PostVisited to indicate we have 99240116Smarcel // scanned the body. 100240116Smarcel VisitedFunctions[FD] = PostVisited; 101240116Smarcel continue; 102240116Smarcel } 103240116Smarcel 104240116Smarcel // Otherwise, the callee is PostVisited. 105240116Smarcel // Remove it from the worklist. 106240116Smarcel assert(VisitedFunctions[FD] == PostVisited); 107240116Smarcel WList.pop_back(); 108240116Smarcel } 109240116Smarcel } 110240116Smarcel 111240116Smarcel // Stmt visitor methods. 112240116Smarcel void VisitCallExpr(CallExpr *CE); 113240116Smarcel void VisitCXXMemberCallExpr(CallExpr *CE); 114240116Smarcel void VisitStmt(Stmt *S) { VisitChildren(S); } 115240116Smarcel void VisitChildren(Stmt *S); 116240116Smarcel 117240116Smarcel void ReportVirtualCall(const CallExpr *CE, bool isPure); 118240116Smarcel 119240116Smarcel}; 120240116Smarcel} // end anonymous namespace 121240116Smarcel 122240116Smarcel//===----------------------------------------------------------------------===// 123240116Smarcel// AST walking. 124240116Smarcel//===----------------------------------------------------------------------===// 125240116Smarcel 126240116Smarcelvoid WalkAST::VisitChildren(Stmt *S) { 127240116Smarcel for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) 128240116Smarcel if (Stmt *child = *I) 129240116Smarcel Visit(child); 130240116Smarcel} 131240116Smarcel 132240116Smarcelvoid WalkAST::VisitCallExpr(CallExpr *CE) { 133240116Smarcel VisitChildren(CE); 134240116Smarcel Enqueue(CE); 135240116Smarcel} 136240116Smarcel 137240116Smarcelvoid WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) { 138240116Smarcel VisitChildren(CE); 139240116Smarcel bool callIsNonVirtual = false; 140240116Smarcel 141240116Smarcel // Several situations to elide for checking. 142240116Smarcel if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) { 143240116Smarcel // If the member access is fully qualified (i.e., X::F), then treat 144240116Smarcel // this as a non-virtual call and do not warn. 145240116Smarcel if (CME->getQualifier()) 146240116Smarcel callIsNonVirtual = true; 147240116Smarcel 148240116Smarcel // Elide analyzing the call entirely if the base pointer is not 'this'. 149240116Smarcel if (Expr *base = CME->getBase()->IgnoreImpCasts()) 150240116Smarcel if (!isa<CXXThisExpr>(base)) 151240116Smarcel return; 152240116Smarcel } 153240116Smarcel 154240116Smarcel // Get the callee. 155240116Smarcel const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee()); 156240116Smarcel if (MD && MD->isVirtual() && !callIsNonVirtual) 157240116Smarcel ReportVirtualCall(CE, MD->isPure()); 158240116Smarcel 159240116Smarcel Enqueue(CE); 160240116Smarcel} 161240116Smarcel 162240116Smarcelvoid WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) { 163240116Smarcel SmallString<100> buf; 164240116Smarcel llvm::raw_svector_ostream os(buf); 165240116Smarcel 166240116Smarcel os << "Call Path : "; 167240116Smarcel // Name of current visiting CallExpr. 168240116Smarcel os << *CE->getDirectCallee(); 169240116Smarcel 170240116Smarcel // Name of the CallExpr whose body is current walking. 171240116Smarcel if (visitingCallExpr) 172240116Smarcel os << " <-- " << *visitingCallExpr->getDirectCallee(); 173240116Smarcel // Names of FunctionDecls in worklist with state PostVisited. 174240116Smarcel for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(), 175240116Smarcel E = WList.begin(); I != E; --I) { 176240116Smarcel const FunctionDecl *FD = (*(I-1))->getDirectCallee(); 177240116Smarcel assert(FD); 178240116Smarcel if (VisitedFunctions[FD] == PostVisited) 179240116Smarcel os << " <-- " << *FD; 180240116Smarcel } 181240116Smarcel 182240116Smarcel PathDiagnosticLocation CELoc = 183240116Smarcel PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 184240116Smarcel SourceRange R = CE->getCallee()->getSourceRange(); 185240116Smarcel 186240116Smarcel if (isPure) { 187240116Smarcel os << "\n" << "Call pure virtual functions during construction or " 188240116Smarcel << "destruction may leads undefined behaviour"; 189240116Smarcel BR.EmitBasicReport(AC->getDecl(), 190240116Smarcel "Call pure virtual function during construction or " 191240116Smarcel "Destruction", 192240116Smarcel "Cplusplus", 193240116Smarcel os.str(), CELoc, &R, 1); 194240116Smarcel return; 195240116Smarcel } 196240116Smarcel else { 197240116Smarcel os << "\n" << "Call virtual functions during construction or " 198240116Smarcel << "destruction will never go to a more derived class"; 199240116Smarcel BR.EmitBasicReport(AC->getDecl(), 200240116Smarcel "Call virtual function during construction or " 201240116Smarcel "Destruction", 202240116Smarcel "Cplusplus", 203240116Smarcel os.str(), CELoc, &R, 1); 204240116Smarcel return; 205240116Smarcel } 206240116Smarcel} 207240116Smarcel 208240116Smarcel//===----------------------------------------------------------------------===// 209240116Smarcel// VirtualCallChecker 210240116Smarcel//===----------------------------------------------------------------------===// 211240116Smarcel 212240116Smarcelnamespace { 213240116Smarcelclass VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > { 214240116Smarcelpublic: 215240116Smarcel void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr, 216240116Smarcel BugReporter &BR) const { 217240116Smarcel WalkAST walker(BR, mgr.getAnalysisDeclContext(RD)); 218240116Smarcel 219240116Smarcel // Check the constructors. 220240116Smarcel for (CXXRecordDecl::ctor_iterator I = RD->ctor_begin(), E = RD->ctor_end(); 221240116Smarcel I != E; ++I) { 222240116Smarcel if (!I->isCopyOrMoveConstructor()) 223240116Smarcel if (Stmt *Body = I->getBody()) { 224240116Smarcel walker.Visit(Body); 225240116Smarcel walker.Execute(); 226240116Smarcel } 227240116Smarcel } 228240116Smarcel 229240116Smarcel // Check the destructor. 230240116Smarcel if (CXXDestructorDecl *DD = RD->getDestructor()) 231240116Smarcel if (Stmt *Body = DD->getBody()) { 232240116Smarcel walker.Visit(Body); 233240116Smarcel walker.Execute(); 234240116Smarcel } 235240116Smarcel } 236240116Smarcel}; 237240116Smarcel} 238240116Smarcel 239240116Smarcelvoid ento::registerVirtualCallChecker(CheckerManager &mgr) { 240240116Smarcel mgr.registerChecker<VirtualCallChecker>(); 241240116Smarcel} 242240116Smarcel