]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / VirtualCallChecker.cpp
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 "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16 #include "clang/AST/DeclCXX.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"
24
25 using namespace clang;
26 using namespace ento;
27
28 namespace {
29 enum class ObjectState : bool { CtorCalled, DtorCalled };
30 } // end namespace
31   // FIXME: Ascending over StackFrameContext maybe another method.
32
33 namespace llvm {
34 template <> 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
41 namespace {
42 class VirtualCallChecker
43     : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
44   mutable std::unique_ptr<BugType> BT;
45
46 public:
47   // The flag to determine if pure virtual functions should be issued only.
48   DefaultBool IsPureOnly;
49
50   void checkBeginFunction(CheckerContext &C) const;
51   void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
52   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
53
54 private:
55   void registerCtorDtorCallInState(bool IsBeginFunction,
56                                    CheckerContext &C) const;
57   void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg,
58                  CheckerContext &C) const;
59
60   class VirtualBugVisitor : public BugReporterVisitor {
61   private:
62     const MemRegion *ObjectRegion;
63     bool Found;
64
65   public:
66     VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {}
67
68     void Profile(llvm::FoldingSetNodeID &ID) const override {
69       static int X = 0;
70       ID.AddPointer(&X);
71       ID.AddPointer(ObjectRegion);
72     }
73
74     std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
75                                                    BugReporterContext &BRC,
76                                                    BugReport &BR) override;
77   };
78 };
79 } // end namespace
80
81 // GDM (generic data map) to the memregion of this for the ctor and dtor.
82 REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
83
84 std::shared_ptr<PathDiagnosticPiece>
85 VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N,
86                                                  BugReporterContext &BRC,
87                                                  BugReport &) {
88   // We need the last ctor/dtor which call the virtual function.
89   // The visitor walks the ExplodedGraph backwards.
90   if (Found)
91     return nullptr;
92
93   ProgramStateRef State = N->getState();
94   const LocationContext *LCtx = N->getLocationContext();
95   const CXXConstructorDecl *CD =
96       dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl());
97   const CXXDestructorDecl *DD =
98       dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl());
99
100   if (!CD && !DD)
101     return nullptr;
102
103   ProgramStateManager &PSM = State->getStateManager();
104   auto &SVB = PSM.getSValBuilder();
105   const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
106   if (!MD)
107     return nullptr;
108   auto ThiSVal =
109       State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
110   const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
111   if (!Reg)
112     return nullptr;
113   if (Reg != ObjectRegion)
114     return nullptr;
115
116   const Stmt *S = PathDiagnosticLocation::getStmt(N);
117   if (!S)
118     return nullptr;
119   Found = true;
120
121   std::string InfoText;
122   if (CD)
123     InfoText = "This constructor of an object of type '" +
124                CD->getNameAsString() +
125                "' has not returned when the virtual method was called";
126   else
127     InfoText = "This destructor of an object of type '" +
128                DD->getNameAsString() +
129                "' has not returned when the virtual method was called";
130
131   // Generate the extra diagnostic.
132   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
133                              N->getLocationContext());
134   return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
135 }
136
137 // The function to check if a callexpr is a virtual function.
138 static bool isVirtualCall(const CallExpr *CE) {
139   bool CallIsNonVirtual = false;
140
141   if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
142     // The member access is fully qualified (i.e., X::F).
143     // Treat this as a non-virtual call and do not warn.
144     if (CME->getQualifier())
145       CallIsNonVirtual = true;
146
147     if (const Expr *Base = CME->getBase()) {
148       // The most derived class is marked final.
149       if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
150         CallIsNonVirtual = true;
151     }
152   }
153
154   const CXXMethodDecl *MD =
155       dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
156   if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
157       !MD->getParent()->hasAttr<FinalAttr>())
158     return true;
159   return false;
160 }
161
162 // The BeginFunction callback when enter a constructor or a destructor.
163 void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
164   registerCtorDtorCallInState(true, C);
165 }
166
167 // The EndFunction callback when leave a constructor or a destructor.
168 void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
169                                           CheckerContext &C) const {
170   registerCtorDtorCallInState(false, C);
171 }
172
173 void VirtualCallChecker::checkPreCall(const CallEvent &Call,
174                                       CheckerContext &C) const {
175   const auto MC = dyn_cast<CXXMemberCall>(&Call);
176   if (!MC)
177     return;
178
179   const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
180   if (!MD)
181     return;
182   ProgramStateRef State = C.getState();
183   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
184
185   if (IsPureOnly && !MD->isPure())
186     return;
187   if (!isVirtualCall(CE))
188     return;
189
190   const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
191   const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
192   if (!ObState)
193     return;
194   // Check if a virtual method is called.
195   // The GDM of constructor and destructor should be true.
196   if (*ObState == ObjectState::CtorCalled) {
197     if (IsPureOnly && MD->isPure())
198       reportBug("Call to pure virtual function during construction", true, Reg,
199                 C);
200     else if (!MD->isPure())
201       reportBug("Call to virtual function during construction", false, Reg, C);
202     else
203       reportBug("Call to pure virtual function during construction", false, Reg,
204                 C);
205   }
206
207   if (*ObState == ObjectState::DtorCalled) {
208     if (IsPureOnly && MD->isPure())
209       reportBug("Call to pure virtual function during destruction", true, Reg,
210                 C);
211     else if (!MD->isPure())
212       reportBug("Call to virtual function during destruction", false, Reg, C);
213     else
214       reportBug("Call to pure virtual function during construction", false, Reg,
215                 C);
216   }
217 }
218
219 void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
220                                                      CheckerContext &C) const {
221   const auto *LCtx = C.getLocationContext();
222   const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
223   if (!MD)
224     return;
225
226   ProgramStateRef State = C.getState();
227   auto &SVB = C.getSValBuilder();
228
229   // Enter a constructor, set the corresponding memregion be true.
230   if (isa<CXXConstructorDecl>(MD)) {
231     auto ThiSVal =
232         State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
233     const MemRegion *Reg = ThiSVal.getAsRegion();
234     if (IsBeginFunction)
235       State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
236     else
237       State = State->remove<CtorDtorMap>(Reg);
238
239     C.addTransition(State);
240     return;
241   }
242
243   // Enter a Destructor, set the corresponding memregion be true.
244   if (isa<CXXDestructorDecl>(MD)) {
245     auto ThiSVal =
246         State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
247     const MemRegion *Reg = ThiSVal.getAsRegion();
248     if (IsBeginFunction)
249       State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
250     else
251       State = State->remove<CtorDtorMap>(Reg);
252
253     C.addTransition(State);
254     return;
255   }
256 }
257
258 void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
259                                    const MemRegion *Reg,
260                                    CheckerContext &C) const {
261   ExplodedNode *N;
262   if (IsSink)
263     N = C.generateErrorNode();
264   else
265     N = C.generateNonFatalErrorNode();
266
267   if (!N)
268     return;
269   if (!BT)
270     BT.reset(new BugType(
271         this, "Call to virtual function during construction or destruction",
272         "C++ Object Lifecycle"));
273
274   auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N);
275   Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg));
276   C.emitReport(std::move(Reporter));
277 }
278
279 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
280   VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
281
282   checker->IsPureOnly =
283       mgr.getAnalyzerOptions().getCheckerBooleanOption("PureOnly", false,
284                                                        checker);
285 }