xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp (revision b3512d3a489b93c86e3ae6a663893700925c66a3)
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"
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 
23 using namespace clang;
24 using namespace ento;
25 
26 namespace {
27 
28 class 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 
58 public:
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 
125 void 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 
131 void WalkAST::VisitCallExpr(CallExpr *CE) {
132   VisitChildren(CE);
133   Enqueue(CE);
134 }
135 
136 void 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 
161 void 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 
209 namespace {
210 class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
211 public:
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 
236 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
237   mgr.registerChecker<VirtualCallChecker>();
238 }
239