xref: /minix3/external/bsd/llvm/dist/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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