1234287Sdim//=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==// 2234287Sdim// 3234287Sdim// The LLVM Compiler Infrastructure 4234287Sdim// 5234287Sdim// This file is distributed under the University of Illinois Open Source 6234287Sdim// License. See LICENSE.TXT for details. 7234287Sdim// 8234287Sdim//===----------------------------------------------------------------------===// 9234287Sdim// 10234287Sdim// This file defines a checker that checks virtual function calls during 11234287Sdim// construction or destruction of C++ objects. 12234287Sdim// 13234287Sdim//===----------------------------------------------------------------------===// 14234287Sdim 15234287Sdim#include "ClangSACheckers.h" 16234287Sdim#include "clang/AST/DeclCXX.h" 17234287Sdim#include "clang/AST/StmtVisitor.h" 18249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 19249423Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 20234287Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 21234287Sdim#include "llvm/ADT/SmallString.h" 22249423Sdim#include "llvm/Support/SaveAndRestore.h" 23249423Sdim#include "llvm/Support/raw_ostream.h" 24234287Sdim 25234287Sdimusing namespace clang; 26234287Sdimusing namespace ento; 27234287Sdim 28234287Sdimnamespace { 29234287Sdim 30234287Sdimclass WalkAST : public StmtVisitor<WalkAST> { 31234287Sdim BugReporter &BR; 32234287Sdim AnalysisDeclContext *AC; 33234287Sdim 34234287Sdim typedef const CallExpr * WorkListUnit; 35234287Sdim typedef SmallVector<WorkListUnit, 20> DFSWorkList; 36234287Sdim 37234287Sdim /// A vector representing the worklist which has a chain of CallExprs. 38234287Sdim DFSWorkList WList; 39234287Sdim 40234287Sdim // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the 41234287Sdim // body has not been visited yet. 42234287Sdim // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the 43234287Sdim // body has been visited. 44234287Sdim enum Kind { NotVisited, 45234287Sdim PreVisited, /**< A CallExpr to this FunctionDecl is in the 46234287Sdim worklist, but the body has not yet been 47234287Sdim visited. */ 48234287Sdim PostVisited /**< A CallExpr to this FunctionDecl is in the 49234287Sdim worklist, and the body has been visited. */ 50239462Sdim }; 51234287Sdim 52234287Sdim /// A DenseMap that records visited states of FunctionDecls. 53234287Sdim llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions; 54234287Sdim 55234287Sdim /// The CallExpr whose body is currently being visited. This is used for 56234287Sdim /// generating bug reports. This is null while visiting the body of a 57234287Sdim /// constructor or destructor. 58234287Sdim const CallExpr *visitingCallExpr; 59234287Sdim 60234287Sdimpublic: 61234287Sdim WalkAST(BugReporter &br, AnalysisDeclContext *ac) 62234287Sdim : BR(br), 63234287Sdim AC(ac), 64234287Sdim visitingCallExpr(0) {} 65234287Sdim 66234287Sdim bool hasWork() const { return !WList.empty(); } 67234287Sdim 68234287Sdim /// This method adds a CallExpr to the worklist and marks the callee as 69234287Sdim /// being PreVisited. 70234287Sdim void Enqueue(WorkListUnit WLUnit) { 71234287Sdim const FunctionDecl *FD = WLUnit->getDirectCallee(); 72234287Sdim if (!FD || !FD->getBody()) 73234287Sdim return; 74234287Sdim Kind &K = VisitedFunctions[FD]; 75234287Sdim if (K != NotVisited) 76234287Sdim return; 77234287Sdim K = PreVisited; 78234287Sdim WList.push_back(WLUnit); 79234287Sdim } 80234287Sdim 81234287Sdim /// This method returns an item from the worklist without removing it. 82234287Sdim WorkListUnit Dequeue() { 83234287Sdim assert(!WList.empty()); 84234287Sdim return WList.back(); 85234287Sdim } 86234287Sdim 87234287Sdim void Execute() { 88234287Sdim while (hasWork()) { 89234287Sdim WorkListUnit WLUnit = Dequeue(); 90234287Sdim const FunctionDecl *FD = WLUnit->getDirectCallee(); 91234287Sdim assert(FD && FD->getBody()); 92234287Sdim 93234287Sdim if (VisitedFunctions[FD] == PreVisited) { 94234287Sdim // If the callee is PreVisited, walk its body. 95234287Sdim // Visit the body. 96234287Sdim SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit); 97234287Sdim Visit(FD->getBody()); 98234287Sdim 99234287Sdim // Mark the function as being PostVisited to indicate we have 100234287Sdim // scanned the body. 101234287Sdim VisitedFunctions[FD] = PostVisited; 102234287Sdim continue; 103234287Sdim } 104234287Sdim 105234287Sdim // Otherwise, the callee is PostVisited. 106234287Sdim // Remove it from the worklist. 107234287Sdim assert(VisitedFunctions[FD] == PostVisited); 108234287Sdim WList.pop_back(); 109234287Sdim } 110234287Sdim } 111234287Sdim 112234287Sdim // Stmt visitor methods. 113234287Sdim void VisitCallExpr(CallExpr *CE); 114234287Sdim void VisitCXXMemberCallExpr(CallExpr *CE); 115234287Sdim void VisitStmt(Stmt *S) { VisitChildren(S); } 116234287Sdim void VisitChildren(Stmt *S); 117234287Sdim 118234287Sdim void ReportVirtualCall(const CallExpr *CE, bool isPure); 119234287Sdim 120234287Sdim}; 121234287Sdim} // end anonymous namespace 122234287Sdim 123234287Sdim//===----------------------------------------------------------------------===// 124234287Sdim// AST walking. 125234287Sdim//===----------------------------------------------------------------------===// 126234287Sdim 127234287Sdimvoid WalkAST::VisitChildren(Stmt *S) { 128234287Sdim for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) 129234287Sdim if (Stmt *child = *I) 130234287Sdim Visit(child); 131234287Sdim} 132234287Sdim 133234287Sdimvoid WalkAST::VisitCallExpr(CallExpr *CE) { 134234287Sdim VisitChildren(CE); 135234287Sdim Enqueue(CE); 136234287Sdim} 137234287Sdim 138234287Sdimvoid WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) { 139234287Sdim VisitChildren(CE); 140234287Sdim bool callIsNonVirtual = false; 141234287Sdim 142234287Sdim // Several situations to elide for checking. 143234287Sdim if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) { 144234287Sdim // If the member access is fully qualified (i.e., X::F), then treat 145234287Sdim // this as a non-virtual call and do not warn. 146234287Sdim if (CME->getQualifier()) 147234287Sdim callIsNonVirtual = true; 148234287Sdim 149234287Sdim // Elide analyzing the call entirely if the base pointer is not 'this'. 150234287Sdim if (Expr *base = CME->getBase()->IgnoreImpCasts()) 151234287Sdim if (!isa<CXXThisExpr>(base)) 152234287Sdim return; 153234287Sdim } 154234287Sdim 155234287Sdim // Get the callee. 156234287Sdim const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee()); 157234287Sdim if (MD && MD->isVirtual() && !callIsNonVirtual) 158234287Sdim ReportVirtualCall(CE, MD->isPure()); 159234287Sdim 160234287Sdim Enqueue(CE); 161234287Sdim} 162234287Sdim 163234287Sdimvoid WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) { 164234287Sdim SmallString<100> buf; 165234287Sdim llvm::raw_svector_ostream os(buf); 166234287Sdim 167234287Sdim os << "Call Path : "; 168234287Sdim // Name of current visiting CallExpr. 169234287Sdim os << *CE->getDirectCallee(); 170234287Sdim 171234287Sdim // Name of the CallExpr whose body is current walking. 172234287Sdim if (visitingCallExpr) 173234287Sdim os << " <-- " << *visitingCallExpr->getDirectCallee(); 174234287Sdim // Names of FunctionDecls in worklist with state PostVisited. 175234287Sdim for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(), 176234287Sdim E = WList.begin(); I != E; --I) { 177234287Sdim const FunctionDecl *FD = (*(I-1))->getDirectCallee(); 178234287Sdim assert(FD); 179234287Sdim if (VisitedFunctions[FD] == PostVisited) 180234287Sdim os << " <-- " << *FD; 181234287Sdim } 182234287Sdim 183234287Sdim PathDiagnosticLocation CELoc = 184234287Sdim PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); 185234287Sdim SourceRange R = CE->getCallee()->getSourceRange(); 186234287Sdim 187234287Sdim if (isPure) { 188234287Sdim os << "\n" << "Call pure virtual functions during construction or " 189234287Sdim << "destruction may leads undefined behaviour"; 190234287Sdim BR.EmitBasicReport(AC->getDecl(), 191234287Sdim "Call pure virtual function during construction or " 192234287Sdim "Destruction", 193234287Sdim "Cplusplus", 194263508Sdim os.str(), CELoc, R); 195234287Sdim return; 196234287Sdim } 197234287Sdim else { 198234287Sdim os << "\n" << "Call virtual functions during construction or " 199234287Sdim << "destruction will never go to a more derived class"; 200234287Sdim BR.EmitBasicReport(AC->getDecl(), 201234287Sdim "Call virtual function during construction or " 202234287Sdim "Destruction", 203234287Sdim "Cplusplus", 204263508Sdim os.str(), CELoc, R); 205234287Sdim return; 206234287Sdim } 207234287Sdim} 208234287Sdim 209234287Sdim//===----------------------------------------------------------------------===// 210234287Sdim// VirtualCallChecker 211234287Sdim//===----------------------------------------------------------------------===// 212234287Sdim 213234287Sdimnamespace { 214234287Sdimclass VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > { 215234287Sdimpublic: 216234287Sdim void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr, 217234287Sdim BugReporter &BR) const { 218234287Sdim WalkAST walker(BR, mgr.getAnalysisDeclContext(RD)); 219234287Sdim 220234287Sdim // Check the constructors. 221234287Sdim for (CXXRecordDecl::ctor_iterator I = RD->ctor_begin(), E = RD->ctor_end(); 222234287Sdim I != E; ++I) { 223234287Sdim if (!I->isCopyOrMoveConstructor()) 224234287Sdim if (Stmt *Body = I->getBody()) { 225234287Sdim walker.Visit(Body); 226234287Sdim walker.Execute(); 227234287Sdim } 228234287Sdim } 229234287Sdim 230234287Sdim // Check the destructor. 231234287Sdim if (CXXDestructorDecl *DD = RD->getDestructor()) 232234287Sdim if (Stmt *Body = DD->getBody()) { 233234287Sdim walker.Visit(Body); 234234287Sdim walker.Execute(); 235234287Sdim } 236234287Sdim } 237234287Sdim}; 238234287Sdim} 239234287Sdim 240234287Sdimvoid ento::registerVirtualCallChecker(CheckerManager &mgr) { 241234287Sdim mgr.registerChecker<VirtualCallChecker>(); 242234287Sdim} 243