1 //=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines a checker that checks virtual function calls during
11 // construction or destruction of C++ objects.
13 //===----------------------------------------------------------------------===//
15 #include "ClangSACheckers.h"
16 #include "clang/AST/DeclCXX.h"
17 #include "clang/AST/StmtVisitor.h"
18 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
19 #include "clang/StaticAnalyzer/Core/Checker.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/Support/SaveAndRestore.h"
23 #include "llvm/Support/raw_ostream.h"
25 using namespace clang;
30 class WalkAST : public StmtVisitor<WalkAST> {
31 const CheckerBase *Checker;
33 AnalysisDeclContext *AC;
35 /// The root constructor or destructor whose callees are being analyzed.
36 const CXXMethodDecl *RootMethod = nullptr;
38 /// Whether the checker should walk into bodies of called functions.
39 /// Controlled by the "Interprocedural" analyzer-config option.
40 bool IsInterprocedural = false;
42 /// Whether the checker should only warn for calls to pure virtual functions
43 /// (which is undefined behavior) or for all virtual functions (which may
44 /// may result in unexpected behavior).
45 bool ReportPureOnly = false;
47 typedef const CallExpr * WorkListUnit;
48 typedef SmallVector<WorkListUnit, 20> DFSWorkList;
50 /// A vector representing the worklist which has a chain of CallExprs.
53 // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
54 // body has not been visited yet.
55 // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
56 // body has been visited.
57 enum Kind { NotVisited,
58 PreVisited, /**< A CallExpr to this FunctionDecl is in the
59 worklist, but the body has not yet been
61 PostVisited /**< A CallExpr to this FunctionDecl is in the
62 worklist, and the body has been visited. */
65 /// A DenseMap that records visited states of FunctionDecls.
66 llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions;
68 /// The CallExpr whose body is currently being visited. This is used for
69 /// generating bug reports. This is null while visiting the body of a
70 /// constructor or destructor.
71 const CallExpr *visitingCallExpr;
74 WalkAST(const CheckerBase *checker, BugReporter &br, AnalysisDeclContext *ac,
75 const CXXMethodDecl *rootMethod, bool isInterprocedural,
77 : Checker(checker), BR(br), AC(ac), RootMethod(rootMethod),
78 IsInterprocedural(isInterprocedural), ReportPureOnly(reportPureOnly),
79 visitingCallExpr(nullptr) {
80 // Walking should always start from either a constructor or a destructor.
81 assert(isa<CXXConstructorDecl>(rootMethod) ||
82 isa<CXXDestructorDecl>(rootMethod));
85 bool hasWork() const { return !WList.empty(); }
87 /// This method adds a CallExpr to the worklist and marks the callee as
89 void Enqueue(WorkListUnit WLUnit) {
90 const FunctionDecl *FD = WLUnit->getDirectCallee();
91 if (!FD || !FD->getBody())
93 Kind &K = VisitedFunctions[FD];
97 WList.push_back(WLUnit);
100 /// This method returns an item from the worklist without removing it.
101 WorkListUnit Dequeue() {
102 assert(!WList.empty());
108 WorkListUnit WLUnit = Dequeue();
109 const FunctionDecl *FD = WLUnit->getDirectCallee();
110 assert(FD && FD->getBody());
112 if (VisitedFunctions[FD] == PreVisited) {
113 // If the callee is PreVisited, walk its body.
115 SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit);
116 Visit(FD->getBody());
118 // Mark the function as being PostVisited to indicate we have
120 VisitedFunctions[FD] = PostVisited;
124 // Otherwise, the callee is PostVisited.
125 // Remove it from the worklist.
126 assert(VisitedFunctions[FD] == PostVisited);
131 // Stmt visitor methods.
132 void VisitCallExpr(CallExpr *CE);
133 void VisitCXXMemberCallExpr(CallExpr *CE);
134 void VisitStmt(Stmt *S) { VisitChildren(S); }
135 void VisitChildren(Stmt *S);
137 void ReportVirtualCall(const CallExpr *CE, bool isPure);
140 } // end anonymous namespace
142 //===----------------------------------------------------------------------===//
144 //===----------------------------------------------------------------------===//
146 void WalkAST::VisitChildren(Stmt *S) {
147 for (Stmt *Child : S->children())
152 void WalkAST::VisitCallExpr(CallExpr *CE) {
154 if (IsInterprocedural)
158 void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
160 bool callIsNonVirtual = false;
162 // Several situations to elide for checking.
163 if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
164 // If the member access is fully qualified (i.e., X::F), then treat
165 // this as a non-virtual call and do not warn.
166 if (CME->getQualifier())
167 callIsNonVirtual = true;
169 if (Expr *base = CME->getBase()->IgnoreImpCasts()) {
170 // Elide analyzing the call entirely if the base pointer is not 'this'.
171 if (!isa<CXXThisExpr>(base))
174 // If the most derived class is marked final, we know that now subclass
175 // can override this member.
176 if (base->getBestDynamicClassType()->hasAttr<FinalAttr>())
177 callIsNonVirtual = true;
182 const CXXMethodDecl *MD =
183 dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
184 if (MD && MD->isVirtual() && !callIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
185 !MD->getParent()->hasAttr<FinalAttr>())
186 ReportVirtualCall(CE, MD->isPure());
188 if (IsInterprocedural)
192 void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
193 if (ReportPureOnly && !isPure)
196 SmallString<100> buf;
197 llvm::raw_svector_ostream os(buf);
199 // FIXME: The interprocedural diagnostic experience here is not good.
200 // Ultimately this checker should be re-written to be path sensitive.
201 // For now, only diagnose intraprocedurally, by default.
202 if (IsInterprocedural) {
203 os << "Call Path : ";
204 // Name of current visiting CallExpr.
205 os << *CE->getDirectCallee();
207 // Name of the CallExpr whose body is current being walked.
208 if (visitingCallExpr)
209 os << " <-- " << *visitingCallExpr->getDirectCallee();
210 // Names of FunctionDecls in worklist with state PostVisited.
211 for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(),
212 E = WList.begin(); I != E; --I) {
213 const FunctionDecl *FD = (*(I-1))->getDirectCallee();
215 if (VisitedFunctions[FD] == PostVisited)
216 os << " <-- " << *FD;
222 PathDiagnosticLocation CELoc =
223 PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
224 SourceRange R = CE->getCallee()->getSourceRange();
230 os << "virtual function during ";
232 if (isa<CXXConstructorDecl>(RootMethod))
233 os << "construction ";
235 os << "destruction ";
238 os << "has undefined behavior";
240 os << "will not dispatch to derived class";
242 BR.EmitBasicReport(AC->getDecl(), Checker,
243 "Call to virtual function during construction or "
245 "C++ Object Lifecycle", os.str(), CELoc, R);
248 //===----------------------------------------------------------------------===//
249 // VirtualCallChecker
250 //===----------------------------------------------------------------------===//
253 class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
255 DefaultBool isInterprocedural;
256 DefaultBool isPureOnly;
258 void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
259 BugReporter &BR) const {
260 AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(RD);
262 // Check the constructors.
263 for (const auto *I : RD->ctors()) {
264 if (!I->isCopyOrMoveConstructor())
265 if (Stmt *Body = I->getBody()) {
266 WalkAST walker(this, BR, ADC, I, isInterprocedural, isPureOnly);
272 // Check the destructor.
273 if (CXXDestructorDecl *DD = RD->getDestructor())
274 if (Stmt *Body = DD->getBody()) {
275 WalkAST walker(this, BR, ADC, DD, isInterprocedural, isPureOnly);
283 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
284 VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
285 checker->isInterprocedural =
286 mgr.getAnalyzerOptions().getBooleanOption("Interprocedural", false,
289 checker->isPureOnly =
290 mgr.getAnalyzerOptions().getBooleanOption("PureOnly", false,