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