clang 20.0.0git
VirtualCallChecker.cpp
Go to the documentation of this file.
1//=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines a checker that checks virtual method calls during
10// construction or destruction of C++ objects.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/AST/Attr.h"
15#include "clang/AST/DeclCXX.h"
24
25using namespace clang;
26using namespace ento;
27
28namespace {
29enum class ObjectState : bool { CtorCalled, DtorCalled };
30} // end namespace
31 // FIXME: Ascending over StackFrameContext maybe another method.
32
33namespace llvm {
34template <> struct FoldingSetTrait<ObjectState> {
35 static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
36 ID.AddInteger(static_cast<int>(X));
37 }
38};
39} // end namespace llvm
40
41namespace {
42class VirtualCallChecker
43 : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
44public:
45 // These are going to be null if the respective check is disabled.
46 mutable std::unique_ptr<BugType> BT_Pure, BT_Impure;
47 bool ShowFixIts = false;
48
49 void checkBeginFunction(CheckerContext &C) const;
50 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
51 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
52
53private:
54 void registerCtorDtorCallInState(bool IsBeginFunction,
55 CheckerContext &C) const;
56};
57} // end namespace
58
59// GDM (generic data map) to the memregion of this for the ctor and dtor.
60REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
61
62// The function to check if a callexpr is a virtual method call.
63static bool isVirtualCall(const CallExpr *CE) {
64 bool CallIsNonVirtual = false;
65
66 if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
67 // The member access is fully qualified (i.e., X::F).
68 // Treat this as a non-virtual call and do not warn.
69 if (CME->getQualifier())
70 CallIsNonVirtual = true;
71
72 if (const Expr *Base = CME->getBase()) {
73 // The most derived class is marked final.
74 if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
75 CallIsNonVirtual = true;
76 }
77 }
78
79 const CXXMethodDecl *MD =
80 dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
81 if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
82 !MD->getParent()->hasAttr<FinalAttr>())
83 return true;
84 return false;
85}
86
87// The BeginFunction callback when enter a constructor or a destructor.
88void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
89 registerCtorDtorCallInState(true, C);
90}
91
92// The EndFunction callback when leave a constructor or a destructor.
93void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
94 CheckerContext &C) const {
95 registerCtorDtorCallInState(false, C);
96}
97
98void VirtualCallChecker::checkPreCall(const CallEvent &Call,
99 CheckerContext &C) const {
100 const auto MC = dyn_cast<CXXMemberCall>(&Call);
101 if (!MC)
102 return;
103
104 const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
105 if (!MD)
106 return;
107
108 ProgramStateRef State = C.getState();
109 // Member calls are always represented by a call-expression.
110 const auto *CE = cast<CallExpr>(Call.getOriginExpr());
111 if (!isVirtualCall(CE))
112 return;
113
114 const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
115 const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
116 if (!ObState)
117 return;
118
119 bool IsPure = MD->isPureVirtual();
120
121 // At this point we're sure that we're calling a virtual method
122 // during construction or destruction, so we'll emit a report.
124 llvm::raw_svector_ostream OS(Msg);
125 OS << "Call to ";
126 if (IsPure)
127 OS << "pure ";
128 OS << "virtual method '" << MD->getParent()->getDeclName()
129 << "::" << MD->getDeclName() << "' during ";
130 if (*ObState == ObjectState::CtorCalled)
131 OS << "construction ";
132 else
133 OS << "destruction ";
134 if (IsPure)
135 OS << "has undefined behavior";
136 else
137 OS << "bypasses virtual dispatch";
138
139 ExplodedNode *N =
140 IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode();
141 if (!N)
142 return;
143
144 const std::unique_ptr<BugType> &BT = IsPure ? BT_Pure : BT_Impure;
145 if (!BT) {
146 // The respective check is disabled.
147 return;
148 }
149
150 auto Report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
151
152 if (ShowFixIts && !IsPure) {
153 // FIXME: These hints are valid only when the virtual call is made
154 // directly from the constructor/destructor. Otherwise the dispatch
155 // will work just fine from other callees, and the fix may break
156 // the otherwise correct program.
158 CE->getBeginLoc(), MD->getParent()->getNameAsString() + "::");
159 Report->addFixItHint(Fixit);
160 }
161
162 C.emitReport(std::move(Report));
163}
164
165void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
166 CheckerContext &C) const {
167 const auto *LCtx = C.getLocationContext();
168 const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
169 if (!MD)
170 return;
171
172 ProgramStateRef State = C.getState();
173 auto &SVB = C.getSValBuilder();
174
175 // Enter a constructor, set the corresponding memregion be true.
176 if (isa<CXXConstructorDecl>(MD)) {
177 auto ThiSVal =
178 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
179 const MemRegion *Reg = ThiSVal.getAsRegion();
180 if (IsBeginFunction)
181 State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
182 else
183 State = State->remove<CtorDtorMap>(Reg);
184
185 C.addTransition(State);
186 return;
187 }
188
189 // Enter a Destructor, set the corresponding memregion be true.
190 if (isa<CXXDestructorDecl>(MD)) {
191 auto ThiSVal =
192 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
193 const MemRegion *Reg = ThiSVal.getAsRegion();
194 if (IsBeginFunction)
195 State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
196 else
197 State = State->remove<CtorDtorMap>(Reg);
198
199 C.addTransition(State);
200 return;
201 }
202}
203
204void ento::registerVirtualCallModeling(CheckerManager &Mgr) {
205 Mgr.registerChecker<VirtualCallChecker>();
206}
207
208void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) {
209 auto *Chk = Mgr.getChecker<VirtualCallChecker>();
210 Chk->BT_Pure = std::make_unique<BugType>(Mgr.getCurrentCheckerName(),
211 "Pure virtual method call",
213}
214
215void ento::registerVirtualCallChecker(CheckerManager &Mgr) {
216 auto *Chk = Mgr.getChecker<VirtualCallChecker>();
218 Mgr.getCurrentCheckerName(), "PureOnly")) {
219 Chk->BT_Impure = std::make_unique<BugType>(
220 Mgr.getCurrentCheckerName(), "Unexpected loss of virtual dispatch",
222 Chk->ShowFixIts = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
223 Mgr.getCurrentCheckerName(), "ShowFixIts");
224 }
225}
226
227bool ento::shouldRegisterVirtualCallModeling(const CheckerManager &mgr) {
228 const LangOptions &LO = mgr.getLangOpts();
229 return LO.CPlusPlus;
230}
231
232bool ento::shouldRegisterPureVirtualCallChecker(const CheckerManager &mgr) {
233 const LangOptions &LO = mgr.getLangOpts();
234 return LO.CPlusPlus;
235}
236
237bool ento::shouldRegisterVirtualCallChecker(const CheckerManager &mgr) {
238 const LangOptions &LO = mgr.getLangOpts();
239 return LO.CPlusPlus;
240}
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
#define X(type, name)
Definition: Value.h:143
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
static bool isVirtualCall(const CallExpr *CE)
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:2064
bool isVirtual() const
Definition: DeclCXX.h:2119
const CXXRecordDecl * getParent() const
Return the parent of this method declaration, which is the class in which this method is defined.
Definition: DeclCXX.h:2190
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2830
bool hasAttr() const
Definition: DeclBase.h:584
This represents one expression.
Definition: Expr.h:110
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition: Diagnostic.h:71
static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)
Create a code modification hint that inserts the given code string at a specific location.
Definition: Diagnostic.h:97
bool isPureVirtual() const
Whether this virtual function is pure, i.e.
Definition: Decl.h:2285
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:476
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition: Expr.h:3187
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:315
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
Definition: Decl.h:292
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Definition: Stmt.h:3029
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
const LangOptions & getLangOpts() const
CheckerNameRef getCurrentCheckerName() const
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:97
const char *const CXXObjectLifecycle
The JSON file list parser is used to communicate input to InstallAPI.
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30
static void Profile(ObjectState X, FoldingSetNodeID &ID)