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