[DFAJumpThreading] Remove incoming StartBlock from all phis when unfolding select...
[llvm-project.git] / clang / lib / StaticAnalyzer / Checkers / VirtualCallChecker.cpp
blob1c589e3468c2dd4b4ff380f055dc60cfdc98b882
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.
12 //===----------------------------------------------------------------------===//
14 #include "clang/AST/Attr.h"
15 #include "clang/AST/DeclCXX.h"
16 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
17 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19 #include "clang/StaticAnalyzer/Core/Checker.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
25 using namespace clang;
26 using namespace ento;
28 namespace {
29 enum class ObjectState : bool { CtorCalled, DtorCalled };
30 } // end namespace
31 // FIXME: Ascending over StackFrameContext maybe another method.
33 namespace llvm {
34 template <> struct FoldingSetTrait<ObjectState> {
35 static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
36 ID.AddInteger(static_cast<int>(X));
39 } // end namespace llvm
41 namespace {
42 class VirtualCallChecker
43 : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
44 public:
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;
49 void checkBeginFunction(CheckerContext &C) const;
50 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
51 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
53 private:
54 void registerCtorDtorCallInState(bool IsBeginFunction,
55 CheckerContext &C) const;
57 } // end namespace
59 // GDM (generic data map) to the memregion of this for the ctor and dtor.
60 REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
62 // The function to check if a callexpr is a virtual method call.
63 static bool isVirtualCall(const CallExpr *CE) {
64 bool CallIsNonVirtual = false;
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;
72 if (const Expr *Base = CME->getBase()) {
73 // The most derived class is marked final.
74 if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
75 CallIsNonVirtual = true;
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;
87 // The BeginFunction callback when enter a constructor or a destructor.
88 void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
89 registerCtorDtorCallInState(true, C);
92 // The EndFunction callback when leave a constructor or a destructor.
93 void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
94 CheckerContext &C) const {
95 registerCtorDtorCallInState(false, C);
98 void VirtualCallChecker::checkPreCall(const CallEvent &Call,
99 CheckerContext &C) const {
100 const auto MC = dyn_cast<CXXMemberCall>(&Call);
101 if (!MC)
102 return;
104 const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
105 if (!MD)
106 return;
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;
114 const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
115 const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
116 if (!ObState)
117 return;
119 bool IsPure = MD->isPure();
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.
123 SmallString<128> Msg;
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";
139 ExplodedNode *N =
140 IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode();
141 if (!N)
142 return;
144 const std::unique_ptr<BugType> &BT = IsPure ? BT_Pure : BT_Impure;
145 if (!BT) {
146 // The respective check is disabled.
147 return;
150 auto Report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
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.
157 FixItHint Fixit = FixItHint::CreateInsertion(
158 CE->getBeginLoc(), MD->getParent()->getNameAsString() + "::");
159 Report->addFixItHint(Fixit);
162 C.emitReport(std::move(Report));
165 void 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;
172 ProgramStateRef State = C.getState();
173 auto &SVB = C.getSValBuilder();
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);
185 C.addTransition(State);
186 return;
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);
199 C.addTransition(State);
200 return;
204 void ento::registerVirtualCallModeling(CheckerManager &Mgr) {
205 Mgr.registerChecker<VirtualCallChecker>();
208 void 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",
212 categories::CXXObjectLifecycle);
215 void ento::registerVirtualCallChecker(CheckerManager &Mgr) {
216 auto *Chk = Mgr.getChecker<VirtualCallChecker>();
217 if (!Mgr.getAnalyzerOptions().getCheckerBooleanOption(
218 Mgr.getCurrentCheckerName(), "PureOnly")) {
219 Chk->BT_Impure = std::make_unique<BugType>(
220 Mgr.getCurrentCheckerName(), "Unexpected loss of virtual dispatch",
221 categories::CXXObjectLifecycle);
222 Chk->ShowFixIts = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
223 Mgr.getCurrentCheckerName(), "ShowFixIts");
227 bool ento::shouldRegisterVirtualCallModeling(const CheckerManager &mgr) {
228 const LangOptions &LO = mgr.getLangOpts();
229 return LO.CPlusPlus;
232 bool ento::shouldRegisterPureVirtualCallChecker(const CheckerManager &mgr) {
233 const LangOptions &LO = mgr.getLangOpts();
234 return LO.CPlusPlus;
237 bool ento::shouldRegisterVirtualCallChecker(const CheckerManager &mgr) {
238 const LangOptions &LO = mgr.getLangOpts();
239 return LO.CPlusPlus;