VirtualCallChecker.cpp revision b2c60b04a597cc5ba4154837cf8e0a155a376fd7
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
24using namespace clang;
25using namespace ento;
26
27namespace {
28
29class 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
59public:
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
126void 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
132void WalkAST::VisitCallExpr(CallExpr *CE) {
133  VisitChildren(CE);
134  Enqueue(CE);
135}
136
137void 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
162void 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("Call pure virtual function during construction or "
190                       "Destruction",
191                       "Cplusplus",
192                       os.str(), CELoc, &R, 1);
193    return;
194  }
195  else {
196    os << "\n" << "Call virtual functions during construction or "
197       << "destruction will never go to a more derived class";
198    BR.EmitBasicReport("Call virtual function during construction or "
199                       "Destruction",
200                       "Cplusplus",
201                       os.str(), CELoc, &R, 1);
202    return;
203  }
204}
205
206//===----------------------------------------------------------------------===//
207// VirtualCallChecker
208//===----------------------------------------------------------------------===//
209
210namespace {
211class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
212public:
213  void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
214                    BugReporter &BR) const {
215    WalkAST walker(BR, mgr.getAnalysisDeclContext(RD));
216
217    // Check the constructors.
218    for (CXXRecordDecl::ctor_iterator I = RD->ctor_begin(), E = RD->ctor_end();
219         I != E; ++I) {
220      if (!I->isCopyOrMoveConstructor())
221        if (Stmt *Body = I->getBody()) {
222          walker.Visit(Body);
223          walker.Execute();
224        }
225    }
226
227    // Check the destructor.
228    if (CXXDestructorDecl *DD = RD->getDestructor())
229      if (Stmt *Body = DD->getBody()) {
230        walker.Visit(Body);
231        walker.Execute();
232      }
233  }
234};
235}
236
237void ento::registerVirtualCallChecker(CheckerManager &mgr) {
238  mgr.registerChecker<VirtualCallChecker>();
239}
240