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