blob: 08d86abec648bde5f016f3e81e1ed66738237767 [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"
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27
28class WalkAST : public StmtVisitor<WalkAST> {
29 BugReporter &BR;
30 AnalysisDeclContext *AC;
31
32 typedef const CallExpr * WorkListUnit;
33 typedef SmallVector<WorkListUnit, 20> DFSWorkList;
34
35 /// A vector representing the worklist which has a chain of CallExprs.
36 DFSWorkList WList;
37
38 // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
39 // body has not been visited yet.
40 // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
41 // body has been visited.
42 enum Kind { NotVisited,
43 PreVisited, /**< A CallExpr to this FunctionDecl is in the
44 worklist, but the body has not yet been
45 visited. */
46 PostVisited /**< A CallExpr to this FunctionDecl is in the
47 worklist, and the body has been visited. */
48 } K;
49
50 /// A DenseMap that records visited states of FunctionDecls.
51 llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions;
52
53 /// The CallExpr whose body is currently being visited. This is used for
54 /// generating bug reports. This is null while visiting the body of a
55 /// constructor or destructor.
56 const CallExpr *visitingCallExpr;
57
58public:
59 WalkAST(BugReporter &br, AnalysisDeclContext *ac)
60 : BR(br),
61 AC(ac),
62 visitingCallExpr(0) {}
63
64 bool hasWork() const { return !WList.empty(); }
65
66 /// This method adds a CallExpr to the worklist and marks the callee as
67 /// being PreVisited.
68 void Enqueue(WorkListUnit WLUnit) {
69 const FunctionDecl *FD = WLUnit->getDirectCallee();
70 if (!FD || !FD->getBody())
71 return;
72 Kind &K = VisitedFunctions[FD];
73 if (K != NotVisited)
74 return;
75 K = PreVisited;
76 WList.push_back(WLUnit);
77 }
78
79 /// This method returns an item from the worklist without removing it.
80 WorkListUnit Dequeue() {
81 assert(!WList.empty());
82 return WList.back();
83 }
84
85 void Execute() {
86 while (hasWork()) {
87 WorkListUnit WLUnit = Dequeue();
88 const FunctionDecl *FD = WLUnit->getDirectCallee();
89 assert(FD && FD->getBody());
90
91 if (VisitedFunctions[FD] == PreVisited) {
92 // If the callee is PreVisited, walk its body.
93 // Visit the body.
94 SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit);
95 Visit(FD->getBody());
96
97 // Mark the function as being PostVisited to indicate we have
98 // scanned the body.
99 VisitedFunctions[FD] = PostVisited;
100 continue;
101 }
102
103 // Otherwise, the callee is PostVisited.
104 // Remove it from the worklist.
105 assert(VisitedFunctions[FD] == PostVisited);
106 WList.pop_back();
107 }
108 }
109
110 // Stmt visitor methods.
111 void VisitCallExpr(CallExpr *CE);
112 void VisitCXXMemberCallExpr(CallExpr *CE);
113 void VisitStmt(Stmt *S) { VisitChildren(S); }
114 void VisitChildren(Stmt *S);
115
116 void ReportVirtualCall(const CallExpr *CE, bool isPure);
117
118};
119} // end anonymous namespace
120
121//===----------------------------------------------------------------------===//
122// AST walking.
123//===----------------------------------------------------------------------===//
124
125void WalkAST::VisitChildren(Stmt *S) {
126 for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
127 if (Stmt *child = *I)
128 Visit(child);
129}
130
131void WalkAST::VisitCallExpr(CallExpr *CE) {
132 VisitChildren(CE);
133 Enqueue(CE);
134}
135
136void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
137 VisitChildren(CE);
138 bool callIsNonVirtual = false;
139
140 // Several situations to elide for checking.
141 if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
142 // If the member access is fully qualified (i.e., X::F), then treat
143 // this as a non-virtual call and do not warn.
144 if (CME->getQualifier())
145 callIsNonVirtual = true;
146
147 // Elide analyzing the call entirely if the base pointer is not 'this'.
148 if (Expr *base = CME->getBase()->IgnoreImpCasts())
149 if (!isa<CXXThisExpr>(base))
150 return;
151 }
152
153 // Get the callee.
154 const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee());
155 if (MD && MD->isVirtual() && !callIsNonVirtual)
156 ReportVirtualCall(CE, MD->isPure());
157
158 Enqueue(CE);
159}
160
161void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
162 llvm::SmallString<100> buf;
163 llvm::raw_svector_ostream os(buf);
164
165 os << "Call Path : ";
166 // Name of current visiting CallExpr.
167 os << CE->getDirectCallee()->getNameAsString();
168
169 // Name of the CallExpr whose body is current walking.
170 if (visitingCallExpr)
171 os << " <-- " << visitingCallExpr->getDirectCallee()->getNameAsString();
172 // Names of FunctionDecls in worklist with state PostVisited.
173 for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(),
174 E = WList.begin(); I != E; --I) {
175 const FunctionDecl *FD = (*(I-1))->getDirectCallee();
176 assert(FD);
177 if (VisitedFunctions[FD] == PostVisited)
178 os << " <-- " << FD->getNameAsString();
179 }
180
181 PathDiagnosticLocation CELoc =
182 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
183 SourceRange R = CE->getCallee()->getSourceRange();
184
185 if (isPure) {
186 os << "\n" << "Call pure virtual functions during construction or "
187 << "destruction may leads undefined behaviour";
188 BR.EmitBasicReport("Call pure virtual function during construction or "
189 "Destruction",
190 "Cplusplus",
191 os.str(), CELoc, &R, 1);
192 return;
193 }
194 else {
195 os << "\n" << "Call virtual functions during construction or "
196 << "destruction will never go to a more derived class";
197 BR.EmitBasicReport("Call virtual function during construction or "
198 "Destruction",
199 "Cplusplus",
200 os.str(), CELoc, &R, 1);
201 return;
202 }
203}
204
205//===----------------------------------------------------------------------===//
206// VirtualCallChecker
207//===----------------------------------------------------------------------===//
208
209namespace {
210class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
211public:
212 void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
213 BugReporter &BR) const {
214 WalkAST walker(BR, mgr.getAnalysisDeclContext(RD));
215
216 // Check the constructors.
217 for (CXXRecordDecl::ctor_iterator I = RD->ctor_begin(), E = RD->ctor_end();
218 I != E; ++I) {
219 if (!I->isCopyOrMoveConstructor())
220 if (Stmt *Body = I->getBody()) {
221 walker.Visit(Body);
222 walker.Execute();
223 }
224 }
225
226 // Check the destructor.
227 if (CXXDestructorDecl *DD = RD->getDestructor())
228 if (Stmt *Body = DD->getBody()) {
229 walker.Visit(Body);
230 walker.Execute();
231 }
232 }
233};
234}
235
236void ento::registerVirtualCallChecker(CheckerManager &mgr) {
237 mgr.registerChecker<VirtualCallChecker>();
238}