blob: f769ea6ade81dc51b40293f736d178076cfa12cf [file] [log] [blame]
Ted Kremenekde9f2532012-01-03 23:18:57 +00001//=======- 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 "clang/Analysis/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"
Benjamin Kramer8fe83e12012-02-04 13:45:25 +000022#include "llvm/ADT/SmallString.h"
Ted Kremenekde9f2532012-01-03 23:18:57 +000023
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) {
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +0000163 SmallString<100> buf;
Ted Kremenekde9f2532012-01-03 23:18:57 +0000164 llvm::raw_svector_ostream os(buf);
165
166 os << "Call Path : ";
167 // Name of current visiting CallExpr.
168 os << CE->getDirectCallee()->getNameAsString();
169
170 // Name of the CallExpr whose body is current walking.
171 if (visitingCallExpr)
172 os << " <-- " << visitingCallExpr->getDirectCallee()->getNameAsString();
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->getNameAsString();
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}