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