VirtualCallChecker.cpp revision 296417
1232950Stheraven//=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
2232950Stheraven//
3232950Stheraven//                     The LLVM Compiler Infrastructure
4232950Stheraven//
5232950Stheraven// This file is distributed under the University of Illinois Open Source
6232950Stheraven// License. See LICENSE.TXT for details.
7232950Stheraven//
8232950Stheraven//===----------------------------------------------------------------------===//
9232950Stheraven//
10232950Stheraven//  This file defines a checker that checks virtual function calls during
11232950Stheraven//  construction or destruction of C++ objects.
12232950Stheraven//
13232950Stheraven//===----------------------------------------------------------------------===//
14232950Stheraven
15232950Stheraven#include "ClangSACheckers.h"
16232950Stheraven#include "clang/AST/DeclCXX.h"
17232950Stheraven#include "clang/AST/StmtVisitor.h"
18232950Stheraven#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
19232950Stheraven#include "clang/StaticAnalyzer/Core/Checker.h"
20232950Stheraven#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
21232950Stheraven#include "llvm/ADT/SmallString.h"
22232950Stheraven#include "llvm/Support/SaveAndRestore.h"
23232950Stheraven#include "llvm/Support/raw_ostream.h"
24232950Stheraven
25232950Stheravenusing namespace clang;
26232950Stheravenusing namespace ento;
27227825Stheraven
28227825Stheravennamespace {
29227825Stheraven
30227825Stheravenclass WalkAST : public StmtVisitor<WalkAST> {
31227825Stheraven  const CheckerBase *Checker;
32227825Stheraven  BugReporter &BR;
33227825Stheraven  AnalysisDeclContext *AC;
34227825Stheraven
35227825Stheraven  typedef const CallExpr * WorkListUnit;
36227825Stheraven  typedef SmallVector<WorkListUnit, 20> DFSWorkList;
37227825Stheraven
38227825Stheraven  /// A vector representing the worklist which has a chain of CallExprs.
39227825Stheraven  DFSWorkList WList;
40227825Stheraven
41  // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
42  // body has not been visited yet.
43  // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
44  // body has been visited.
45  enum Kind { NotVisited,
46              PreVisited,  /**< A CallExpr to this FunctionDecl is in the
47                                worklist, but the body has not yet been
48                                visited. */
49              PostVisited  /**< A CallExpr to this FunctionDecl is in the
50                                worklist, and the body has been visited. */
51  };
52
53  /// A DenseMap that records visited states of FunctionDecls.
54  llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions;
55
56  /// The CallExpr whose body is currently being visited.  This is used for
57  /// generating bug reports.  This is null while visiting the body of a
58  /// constructor or destructor.
59  const CallExpr *visitingCallExpr;
60
61public:
62  WalkAST(const CheckerBase *checker, BugReporter &br,
63          AnalysisDeclContext *ac)
64      : Checker(checker), BR(br), AC(ac), visitingCallExpr(nullptr) {}
65
66  bool hasWork() const { return !WList.empty(); }
67
68  /// This method adds a CallExpr to the worklist and marks the callee as
69  /// being PreVisited.
70  void Enqueue(WorkListUnit WLUnit) {
71    const FunctionDecl *FD = WLUnit->getDirectCallee();
72    if (!FD || !FD->getBody())
73      return;
74    Kind &K = VisitedFunctions[FD];
75    if (K != NotVisited)
76      return;
77    K = PreVisited;
78    WList.push_back(WLUnit);
79  }
80
81  /// This method returns an item from the worklist without removing it.
82  WorkListUnit Dequeue() {
83    assert(!WList.empty());
84    return WList.back();
85  }
86
87  void Execute() {
88    while (hasWork()) {
89      WorkListUnit WLUnit = Dequeue();
90      const FunctionDecl *FD = WLUnit->getDirectCallee();
91      assert(FD && FD->getBody());
92
93      if (VisitedFunctions[FD] == PreVisited) {
94        // If the callee is PreVisited, walk its body.
95        // Visit the body.
96        SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit);
97        Visit(FD->getBody());
98
99        // Mark the function as being PostVisited to indicate we have
100        // scanned the body.
101        VisitedFunctions[FD] = PostVisited;
102        continue;
103      }
104
105      // Otherwise, the callee is PostVisited.
106      // Remove it from the worklist.
107      assert(VisitedFunctions[FD] == PostVisited);
108      WList.pop_back();
109    }
110  }
111
112  // Stmt visitor methods.
113  void VisitCallExpr(CallExpr *CE);
114  void VisitCXXMemberCallExpr(CallExpr *CE);
115  void VisitStmt(Stmt *S) { VisitChildren(S); }
116  void VisitChildren(Stmt *S);
117
118  void ReportVirtualCall(const CallExpr *CE, bool isPure);
119
120};
121} // end anonymous namespace
122
123//===----------------------------------------------------------------------===//
124// AST walking.
125//===----------------------------------------------------------------------===//
126
127void WalkAST::VisitChildren(Stmt *S) {
128  for (Stmt *Child : S->children())
129    if (Child)
130      Visit(Child);
131}
132
133void WalkAST::VisitCallExpr(CallExpr *CE) {
134  VisitChildren(CE);
135  Enqueue(CE);
136}
137
138void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
139  VisitChildren(CE);
140  bool callIsNonVirtual = false;
141
142  // Several situations to elide for checking.
143  if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
144    // If the member access is fully qualified (i.e., X::F), then treat
145    // this as a non-virtual call and do not warn.
146    if (CME->getQualifier())
147      callIsNonVirtual = true;
148
149    if (Expr *base = CME->getBase()->IgnoreImpCasts()) {
150      // Elide analyzing the call entirely if the base pointer is not 'this'.
151      if (!isa<CXXThisExpr>(base))
152        return;
153
154      // If the most derived class is marked final, we know that now subclass
155      // can override this member.
156      if (base->getBestDynamicClassType()->hasAttr<FinalAttr>())
157        callIsNonVirtual = true;
158    }
159  }
160
161  // Get the callee.
162  const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee());
163  if (MD && MD->isVirtual() && !callIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
164      !MD->getParent()->hasAttr<FinalAttr>())
165    ReportVirtualCall(CE, MD->isPure());
166
167  Enqueue(CE);
168}
169
170void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
171  SmallString<100> buf;
172  llvm::raw_svector_ostream os(buf);
173
174  os << "Call Path : ";
175  // Name of current visiting CallExpr.
176  os << *CE->getDirectCallee();
177
178  // Name of the CallExpr whose body is current walking.
179  if (visitingCallExpr)
180    os << " <-- " << *visitingCallExpr->getDirectCallee();
181  // Names of FunctionDecls in worklist with state PostVisited.
182  for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(),
183         E = WList.begin(); I != E; --I) {
184    const FunctionDecl *FD = (*(I-1))->getDirectCallee();
185    assert(FD);
186    if (VisitedFunctions[FD] == PostVisited)
187      os << " <-- " << *FD;
188  }
189
190  PathDiagnosticLocation CELoc =
191    PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
192  SourceRange R = CE->getCallee()->getSourceRange();
193
194  if (isPure) {
195    os << "\n" <<  "Call pure virtual functions during construction or "
196       << "destruction may leads undefined behaviour";
197    BR.EmitBasicReport(AC->getDecl(), Checker,
198                       "Call pure virtual function during construction or "
199                       "Destruction",
200                       "Cplusplus", os.str(), CELoc, R);
201    return;
202  }
203  else {
204    os << "\n" << "Call virtual functions during construction or "
205       << "destruction will never go to a more derived class";
206    BR.EmitBasicReport(AC->getDecl(), Checker,
207                       "Call virtual function during construction or "
208                       "Destruction",
209                       "Cplusplus", os.str(), CELoc, R);
210    return;
211  }
212}
213
214//===----------------------------------------------------------------------===//
215// VirtualCallChecker
216//===----------------------------------------------------------------------===//
217
218namespace {
219class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
220public:
221  void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
222                    BugReporter &BR) const {
223    WalkAST walker(this, BR, mgr.getAnalysisDeclContext(RD));
224
225    // Check the constructors.
226    for (const auto *I : RD->ctors()) {
227      if (!I->isCopyOrMoveConstructor())
228        if (Stmt *Body = I->getBody()) {
229          walker.Visit(Body);
230          walker.Execute();
231        }
232    }
233
234    // Check the destructor.
235    if (CXXDestructorDecl *DD = RD->getDestructor())
236      if (Stmt *Body = DD->getBody()) {
237        walker.Visit(Body);
238        walker.Execute();
239      }
240  }
241};
242}
243
244void ento::registerVirtualCallChecker(CheckerManager &mgr) {
245  mgr.registerChecker<VirtualCallChecker>();
246}
247