24 using namespace clang;
35 ID.AddInteger(static_cast<int>(X));
41 class VirtualCallChecker
42 :
public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
43 mutable std::unique_ptr<BugType> BT;
47 DefaultBool IsPureOnly;
49 void checkBeginFunction(CheckerContext &C)
const;
50 void checkEndFunction(
const ReturnStmt *RS, CheckerContext &C)
const;
51 void checkPreCall(
const CallEvent &Call, CheckerContext &C)
const;
54 void registerCtorDtorCallInState(
bool IsBeginFunction,
55 CheckerContext &C)
const;
56 void reportBug(StringRef Msg,
bool PureError,
const MemRegion *Reg,
57 CheckerContext &C)
const;
61 const MemRegion *ObjectRegion;
65 VirtualBugVisitor(
const MemRegion *R) : ObjectRegion(R), Found(
false) {}
67 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
70 ID.AddPointer(ObjectRegion);
73 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *N,
74 BugReporterContext &BRC,
75 BugReport &BR)
override;
83 std::shared_ptr<PathDiagnosticPiece>
84 VirtualCallChecker::VirtualBugVisitor::VisitNode(
const ExplodedNode *N,
85 BugReporterContext &BRC,
95 dyn_cast_or_null<CXXConstructorDecl>(LCtx->
getDecl());
97 dyn_cast_or_null<CXXDestructorDecl>(LCtx->
getDecl());
102 ProgramStateManager &PSM = State->getStateManager();
103 auto &SVB = PSM.getSValBuilder();
109 const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
112 if (Reg != ObjectRegion)
120 std::string InfoText;
122 InfoText =
"This constructor of an object of type '" +
124 "' has not returned when the virtual method was called";
126 InfoText =
"This destructor of an object of type '" +
127 DD->getNameAsString() +
128 "' has not returned when the virtual method was called";
131 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
132 N->getLocationContext());
133 return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText,
true);
138 bool CallIsNonVirtual =
false;
143 if (CME->getQualifier())
144 CallIsNonVirtual =
true;
146 if (
const Expr *
Base = CME->getBase()) {
148 if (
Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
149 CallIsNonVirtual =
true;
155 if (MD && MD->
isVirtual() && !CallIsNonVirtual && !MD->
hasAttr<FinalAttr>() &&
162 void VirtualCallChecker::checkBeginFunction(CheckerContext &C)
const {
163 registerCtorDtorCallInState(
true, C);
167 void VirtualCallChecker::checkEndFunction(
const ReturnStmt *RS,
168 CheckerContext &C)
const {
169 registerCtorDtorCallInState(
false, C);
172 void VirtualCallChecker::checkPreCall(
const CallEvent &Call,
173 CheckerContext &C)
const {
178 const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
182 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
184 if (IsPureOnly && !MD->
isPure())
189 const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
190 const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
195 if (*ObState == ObjectState::CtorCalled) {
196 if (IsPureOnly && MD->
isPure())
197 reportBug(
"Call to pure virtual function during construction",
true, Reg,
200 reportBug(
"Call to virtual function during construction",
false, Reg, C);
202 reportBug(
"Call to pure virtual function during construction",
false, Reg,
206 if (*ObState == ObjectState::DtorCalled) {
207 if (IsPureOnly && MD->
isPure())
208 reportBug(
"Call to pure virtual function during destruction",
true, Reg,
211 reportBug(
"Call to virtual function during destruction",
false, Reg, C);
213 reportBug(
"Call to pure virtual function during construction",
false, Reg,
218 void VirtualCallChecker::registerCtorDtorCallInState(
bool IsBeginFunction,
219 CheckerContext &C)
const {
220 const auto *LCtx = C.getLocationContext();
221 const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
226 auto &SVB = C.getSValBuilder();
229 if (isa<CXXConstructorDecl>(MD)) {
231 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
232 const MemRegion *Reg = ThiSVal.getAsRegion();
234 State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
236 State = State->remove<CtorDtorMap>(Reg);
238 C.addTransition(State);
243 if (isa<CXXDestructorDecl>(MD)) {
245 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
246 const MemRegion *Reg = ThiSVal.getAsRegion();
248 State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
250 State = State->remove<CtorDtorMap>(Reg);
252 C.addTransition(State);
257 void VirtualCallChecker::reportBug(StringRef Msg,
bool IsSink,
258 const MemRegion *Reg,
259 CheckerContext &C)
const {
262 N = C.generateErrorNode();
264 N = C.generateNonFatalErrorNode();
269 BT.reset(
new BugType(
270 this,
"Call to virtual function during construction or destruction",
271 "C++ Object Lifecycle"));
273 auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N);
274 Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg));
275 C.emitReport(std::move(Reporter));
278 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
279 VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
281 checker->IsPureOnly =
282 mgr.getAnalyzerOptions().getCheckerBooleanOption(checker,
"PureOnly");
285 bool ento::shouldRegisterVirtualCallChecker(
const LangOptions &LO) {
Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be placed into a PointerUnion...
Stmt - This represents one statement.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Represents a C++ constructor within a class.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Represents a non-static C++ member function call.
static void Profile(ObjectState X, FoldingSetNodeID &ID)
This represents one expression.
Represents a C++ destructor within a class.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
static const Stmt * getStmt(const ExplodedNode *N)
Given an exploded node, retrieve the statement that should be used for the diagnostic location...
bool isPure() const
Whether this virtual function is pure, i.e.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
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...
Represents a static or instance method of a struct/union/class.
Dataflow Directional Tag Classes.
const CXXRecordDecl * getParent() const
Return the parent of this method declaration, which is the class in which this method is defined...
const Decl * getDecl() const
const StackFrameContext * getStackFrame() const
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate.h) and friends (in DeclFriend.h).
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
static bool isVirtualCall(const CallExpr *CE)