clang  5.0.0
VirtualCallChecker.cpp
Go to the documentation of this file.
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"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/Support/SaveAndRestore.h"
23 #include "llvm/Support/raw_ostream.h"
24 
25 using namespace clang;
26 using namespace ento;
27 
28 namespace {
29 
30 class WalkAST : public StmtVisitor<WalkAST> {
31  const CheckerBase *Checker;
32  BugReporter &BR;
34 
35  /// The root constructor or destructor whose callees are being analyzed.
36  const CXXMethodDecl *RootMethod = nullptr;
37 
38  /// Whether the checker should walk into bodies of called functions.
39  /// Controlled by the "Interprocedural" analyzer-config option.
40  bool IsInterprocedural = false;
41 
42  /// Whether the checker should only warn for calls to pure virtual functions
43  /// (which is undefined behavior) or for all virtual functions (which may
44  /// may result in unexpected behavior).
45  bool ReportPureOnly = false;
46 
47  typedef const CallExpr * WorkListUnit;
48  typedef SmallVector<WorkListUnit, 20> DFSWorkList;
49 
50  /// A vector representing the worklist which has a chain of CallExprs.
51  DFSWorkList WList;
52 
53  // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
54  // body has not been visited yet.
55  // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
56  // body has been visited.
57  enum Kind { NotVisited,
58  PreVisited, /**< A CallExpr to this FunctionDecl is in the
59  worklist, but the body has not yet been
60  visited. */
61  PostVisited /**< A CallExpr to this FunctionDecl is in the
62  worklist, and the body has been visited. */
63  };
64 
65  /// A DenseMap that records visited states of FunctionDecls.
66  llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions;
67 
68  /// The CallExpr whose body is currently being visited. This is used for
69  /// generating bug reports. This is null while visiting the body of a
70  /// constructor or destructor.
71  const CallExpr *visitingCallExpr;
72 
73 public:
74  WalkAST(const CheckerBase *checker, BugReporter &br, AnalysisDeclContext *ac,
75  const CXXMethodDecl *rootMethod, bool isInterprocedural,
76  bool reportPureOnly)
77  : Checker(checker), BR(br), AC(ac), RootMethod(rootMethod),
78  IsInterprocedural(isInterprocedural), ReportPureOnly(reportPureOnly),
79  visitingCallExpr(nullptr) {
80  // Walking should always start from either a constructor or a destructor.
81  assert(isa<CXXConstructorDecl>(rootMethod) ||
82  isa<CXXDestructorDecl>(rootMethod));
83  }
84 
85  bool hasWork() const { return !WList.empty(); }
86 
87  /// This method adds a CallExpr to the worklist and marks the callee as
88  /// being PreVisited.
89  void Enqueue(WorkListUnit WLUnit) {
90  const FunctionDecl *FD = WLUnit->getDirectCallee();
91  if (!FD || !FD->getBody())
92  return;
93  Kind &K = VisitedFunctions[FD];
94  if (K != NotVisited)
95  return;
96  K = PreVisited;
97  WList.push_back(WLUnit);
98  }
99 
100  /// This method returns an item from the worklist without removing it.
101  WorkListUnit Dequeue() {
102  assert(!WList.empty());
103  return WList.back();
104  }
105 
106  void Execute() {
107  while (hasWork()) {
108  WorkListUnit WLUnit = Dequeue();
109  const FunctionDecl *FD = WLUnit->getDirectCallee();
110  assert(FD && FD->getBody());
111 
112  if (VisitedFunctions[FD] == PreVisited) {
113  // If the callee is PreVisited, walk its body.
114  // Visit the body.
115  SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit);
116  Visit(FD->getBody());
117 
118  // Mark the function as being PostVisited to indicate we have
119  // scanned the body.
120  VisitedFunctions[FD] = PostVisited;
121  continue;
122  }
123 
124  // Otherwise, the callee is PostVisited.
125  // Remove it from the worklist.
126  assert(VisitedFunctions[FD] == PostVisited);
127  WList.pop_back();
128  }
129  }
130 
131  // Stmt visitor methods.
132  void VisitCallExpr(CallExpr *CE);
133  void VisitCXXMemberCallExpr(CallExpr *CE);
134  void VisitStmt(Stmt *S) { VisitChildren(S); }
135  void VisitChildren(Stmt *S);
136 
137  void ReportVirtualCall(const CallExpr *CE, bool isPure);
138 
139 };
140 } // end anonymous namespace
141 
142 //===----------------------------------------------------------------------===//
143 // AST walking.
144 //===----------------------------------------------------------------------===//
145 
146 void WalkAST::VisitChildren(Stmt *S) {
147  for (Stmt *Child : S->children())
148  if (Child)
149  Visit(Child);
150 }
151 
152 void WalkAST::VisitCallExpr(CallExpr *CE) {
153  VisitChildren(CE);
154  if (IsInterprocedural)
155  Enqueue(CE);
156 }
157 
158 void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
159  VisitChildren(CE);
160  bool callIsNonVirtual = false;
161 
162  // Several situations to elide for checking.
163  if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
164  // If the member access is fully qualified (i.e., X::F), then treat
165  // this as a non-virtual call and do not warn.
166  if (CME->getQualifier())
167  callIsNonVirtual = true;
168 
169  if (Expr *base = CME->getBase()->IgnoreImpCasts()) {
170  // Elide analyzing the call entirely if the base pointer is not 'this'.
171  if (!isa<CXXThisExpr>(base))
172  return;
173 
174  // If the most derived class is marked final, we know that now subclass
175  // can override this member.
176  if (base->getBestDynamicClassType()->hasAttr<FinalAttr>())
177  callIsNonVirtual = true;
178  }
179  }
180 
181  // Get the callee.
182  const CXXMethodDecl *MD =
183  dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
184  if (MD && MD->isVirtual() && !callIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
185  !MD->getParent()->hasAttr<FinalAttr>())
186  ReportVirtualCall(CE, MD->isPure());
187 
188  if (IsInterprocedural)
189  Enqueue(CE);
190 }
191 
192 void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
193  if (ReportPureOnly && !isPure)
194  return;
195 
196  SmallString<100> buf;
197  llvm::raw_svector_ostream os(buf);
198 
199  // FIXME: The interprocedural diagnostic experience here is not good.
200  // Ultimately this checker should be re-written to be path sensitive.
201  // For now, only diagnose intraprocedurally, by default.
202  if (IsInterprocedural) {
203  os << "Call Path : ";
204  // Name of current visiting CallExpr.
205  os << *CE->getDirectCallee();
206 
207  // Name of the CallExpr whose body is current being walked.
208  if (visitingCallExpr)
209  os << " <-- " << *visitingCallExpr->getDirectCallee();
210  // Names of FunctionDecls in worklist with state PostVisited.
212  E = WList.begin(); I != E; --I) {
213  const FunctionDecl *FD = (*(I-1))->getDirectCallee();
214  assert(FD);
215  if (VisitedFunctions[FD] == PostVisited)
216  os << " <-- " << *FD;
217  }
218 
219  os << "\n";
220  }
221 
222  PathDiagnosticLocation CELoc =
223  PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
224  SourceRange R = CE->getCallee()->getSourceRange();
225 
226  os << "Call to ";
227  if (isPure)
228  os << "pure ";
229 
230  os << "virtual function during ";
231 
232  if (isa<CXXConstructorDecl>(RootMethod))
233  os << "construction ";
234  else
235  os << "destruction ";
236 
237  if (isPure)
238  os << "has undefined behavior";
239  else
240  os << "will not dispatch to derived class";
241 
242  BR.EmitBasicReport(AC->getDecl(), Checker,
243  "Call to virtual function during construction or "
244  "destruction",
245  "C++ Object Lifecycle", os.str(), CELoc, R);
246 }
247 
248 //===----------------------------------------------------------------------===//
249 // VirtualCallChecker
250 //===----------------------------------------------------------------------===//
251 
252 namespace {
253 class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
254 public:
255  DefaultBool isInterprocedural;
256  DefaultBool isPureOnly;
257 
258  void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
259  BugReporter &BR) const {
261 
262  // Check the constructors.
263  for (const auto *I : RD->ctors()) {
264  if (!I->isCopyOrMoveConstructor())
265  if (Stmt *Body = I->getBody()) {
266  WalkAST walker(this, BR, ADC, I, isInterprocedural, isPureOnly);
267  walker.Visit(Body);
268  walker.Execute();
269  }
270  }
271 
272  // Check the destructor.
273  if (CXXDestructorDecl *DD = RD->getDestructor())
274  if (Stmt *Body = DD->getBody()) {
275  WalkAST walker(this, BR, ADC, DD, isInterprocedural, isPureOnly);
276  walker.Visit(Body);
277  walker.Execute();
278  }
279  }
280 };
281 }
282 
283 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
284  VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
285  checker->isInterprocedural =
286  mgr.getAnalyzerOptions().getBooleanOption("Interprocedural", false,
287  checker);
288 
289  checker->isPureOnly =
290  mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false,
291  checker);
292 }
FunctionDecl - An instance of this class is created to represent a function declaration or definition...
Definition: Decl.h:1618
Stmt - This represents one statement.
Definition: Stmt.h:60
A helper class which wraps a boolean value set to false by default.
Definition: Checker.h:551
ctor_range ctors() const
Definition: DeclCXX.h:798
const Expr * getCallee() const
Definition: Expr.h:2246
Expr * IgnoreImpCasts() LLVM_READONLY
IgnoreImpCasts - Skip past any implicit casts which might surround this expression.
Definition: Expr.h:2847
bool hasAttr() const
Definition: DeclBase.h:521
AnalysisDeclContext contains the context data for the function or method under analysis.
bool isPure() const
Whether this virtual function is pure, i.e.
Definition: Decl.h:1898
const CXXRecordDecl * getParent() const
Returns the parent of this method declaration, which is the class in which this method is defined...
Definition: DeclCXX.h:2018
child_range children()
Definition: Stmt.cpp:208
detail::InMemoryDirectory::const_iterator I
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
Expr - This represents one expression.
Definition: Expr.h:105
bool isVirtual() const
Definition: DeclCXX.h:1947
Represents a C++ destructor within a class.
Definition: DeclCXX.h:2551
bool getBooleanOption(StringRef Name, bool DefaultVal, const ento::CheckerBase *C=nullptr, bool SearchInParents=false)
Interprets an option's string value as a boolean.
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:403
Stmt * getBody(const FunctionDecl *&Definition) const
getBody - Retrieve the body (definition) of the function.
Definition: Decl.cpp:2597
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
Kind
CHECKER * registerChecker()
Used to register checkers.
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:1903
StmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:178
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return 0.
Definition: Expr.cpp:1216
CXXDestructorDecl * getDestructor() const
Returns the destructor decl for this class.
Definition: DeclCXX.cpp:1437
detail::InMemoryDirectory::const_iterator E
AnalyzerOptions & getAnalyzerOptions()
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate.h) and friends (in DeclFriend.h).
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition: Expr.h:2378
Represents a C++ struct/union/class.
Definition: DeclCXX.h:267
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:245
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2206
A trivial tuple used to represent a source range.