/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- ContainerModeling.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 | | // Defines a modeling-checker for modeling STL container-like containers. |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #include "clang/AST/DeclTemplate.h" |
14 | | #include "clang/Driver/DriverDiagnostic.h" |
15 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
16 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
17 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
18 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" |
19 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
20 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
21 | | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" |
22 | | |
23 | | #include "Iterator.h" |
24 | | |
25 | | #include <utility> |
26 | | |
27 | | using namespace clang; |
28 | | using namespace ento; |
29 | | using namespace iterator; |
30 | | |
31 | | namespace { |
32 | | |
33 | | class ContainerModeling |
34 | | : public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> { |
35 | | |
36 | | void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal, |
37 | | SVal Cont) const; |
38 | | void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal, |
39 | | SVal Cont) const; |
40 | | void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr, |
41 | | SVal OldCont = UndefinedVal()) const; |
42 | | void handleAssign(CheckerContext &C, SVal Cont, const Expr *ContE) const; |
43 | | void handleClear(CheckerContext &C, SVal Cont, const Expr *ContE) const; |
44 | | void handlePushBack(CheckerContext &C, SVal Cont, const Expr *ContE) const; |
45 | | void handlePopBack(CheckerContext &C, SVal Cont, const Expr *ContE) const; |
46 | | void handlePushFront(CheckerContext &C, SVal Cont, const Expr *ContE) const; |
47 | | void handlePopFront(CheckerContext &C, SVal Cont, const Expr *ContE) const; |
48 | | void handleInsert(CheckerContext &C, SVal Cont, SVal Iter) const; |
49 | | void handleErase(CheckerContext &C, SVal Cont, SVal Iter) const; |
50 | | void handleErase(CheckerContext &C, SVal Cont, SVal Iter1, SVal Iter2) const; |
51 | | void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter) const; |
52 | | void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter1, |
53 | | SVal Iter2) const; |
54 | | const NoteTag *getChangeTag(CheckerContext &C, StringRef Text, |
55 | | const MemRegion *ContReg, |
56 | | const Expr *ContE) const; |
57 | | void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, |
58 | | const char *Sep) const override; |
59 | | |
60 | | public: |
61 | 21 | ContainerModeling() = default; |
62 | | |
63 | | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
64 | | void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; |
65 | | void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; |
66 | | |
67 | | using NoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, |
68 | | const Expr *) const; |
69 | | using OneItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, |
70 | | SVal) const; |
71 | | using TwoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, SVal, |
72 | | SVal) const; |
73 | | |
74 | | CallDescriptionMap<NoItParamFn> NoIterParamFunctions = { |
75 | | {{{"clear"}, 0}, &ContainerModeling::handleClear}, |
76 | | {{{"assign"}, 2}, &ContainerModeling::handleAssign}, |
77 | | {{{"push_back"}, 1}, &ContainerModeling::handlePushBack}, |
78 | | {{{"emplace_back"}, 1}, &ContainerModeling::handlePushBack}, |
79 | | {{{"pop_back"}, 0}, &ContainerModeling::handlePopBack}, |
80 | | {{{"push_front"}, 1}, &ContainerModeling::handlePushFront}, |
81 | | {{{"emplace_front"}, 1}, &ContainerModeling::handlePushFront}, |
82 | | {{{"pop_front"}, 0}, &ContainerModeling::handlePopFront}, |
83 | | }; |
84 | | |
85 | | CallDescriptionMap<OneItParamFn> OneIterParamFunctions = { |
86 | | {{{"insert"}, 2}, &ContainerModeling::handleInsert}, |
87 | | {{{"emplace"}, 2}, &ContainerModeling::handleInsert}, |
88 | | {{{"erase"}, 1}, &ContainerModeling::handleErase}, |
89 | | {{{"erase_after"}, 1}, &ContainerModeling::handleEraseAfter}, |
90 | | }; |
91 | | |
92 | | CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = { |
93 | | {{{"erase"}, 2}, &ContainerModeling::handleErase}, |
94 | | {{{"erase_after"}, 2}, &ContainerModeling::handleEraseAfter}, |
95 | | }; |
96 | | }; |
97 | | |
98 | | bool isBeginCall(const FunctionDecl *Func); |
99 | | bool isEndCall(const FunctionDecl *Func); |
100 | | bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg); |
101 | | bool frontModifiable(ProgramStateRef State, const MemRegion *Reg); |
102 | | bool backModifiable(ProgramStateRef State, const MemRegion *Reg); |
103 | | SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont); |
104 | | SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont); |
105 | | ProgramStateRef createContainerBegin(ProgramStateRef State, |
106 | | const MemRegion *Cont, const Expr *E, |
107 | | QualType T, const LocationContext *LCtx, |
108 | | unsigned BlockCount); |
109 | | ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, |
110 | | const Expr *E, QualType T, |
111 | | const LocationContext *LCtx, |
112 | | unsigned BlockCount); |
113 | | ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, |
114 | | const ContainerData &CData); |
115 | | ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State, |
116 | | const MemRegion *Cont); |
117 | | ProgramStateRef |
118 | | invalidateAllIteratorPositionsExcept(ProgramStateRef State, |
119 | | const MemRegion *Cont, SymbolRef Offset, |
120 | | BinaryOperator::Opcode Opc); |
121 | | ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, |
122 | | SymbolRef Offset, |
123 | | BinaryOperator::Opcode Opc); |
124 | | ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, |
125 | | SymbolRef Offset1, |
126 | | BinaryOperator::Opcode Opc1, |
127 | | SymbolRef Offset2, |
128 | | BinaryOperator::Opcode Opc2); |
129 | | ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State, |
130 | | const MemRegion *Cont, |
131 | | const MemRegion *NewCont); |
132 | | ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State, |
133 | | const MemRegion *Cont, |
134 | | const MemRegion *NewCont, |
135 | | SymbolRef Offset, |
136 | | BinaryOperator::Opcode Opc); |
137 | | ProgramStateRef rebaseSymbolInIteratorPositionsIf( |
138 | | ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym, |
139 | | SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc); |
140 | | SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr, |
141 | | SymbolRef OldSym, SymbolRef NewSym); |
142 | | bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont); |
143 | | |
144 | | } // namespace |
145 | | |
146 | | void ContainerModeling::checkPostCall(const CallEvent &Call, |
147 | 17.4k | CheckerContext &C) const { |
148 | 17.4k | const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); |
149 | 17.4k | if (!Func) |
150 | 0 | return; |
151 | | |
152 | 17.4k | if (Func->isOverloadedOperator()) { |
153 | 1.29k | const auto Op = Func->getOverloadedOperator(); |
154 | 1.29k | if (Op == OO_Equal) { |
155 | | // Overloaded 'operator=' must be a non-static member function. |
156 | 51 | const auto *InstCall = cast<CXXInstanceCall>(&Call); |
157 | 51 | if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) { |
158 | 27 | handleAssignment(C, InstCall->getCXXThisVal(), Call.getOriginExpr(), |
159 | 27 | Call.getArgSVal(0)); |
160 | 27 | return; |
161 | 27 | } |
162 | | |
163 | 24 | handleAssignment(C, InstCall->getCXXThisVal()); |
164 | 24 | return; |
165 | 51 | } |
166 | 16.1k | } else { |
167 | 16.1k | if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { |
168 | 3.28k | const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call); |
169 | 3.28k | if (Handler0) { |
170 | 187 | (this->**Handler0)(C, InstCall->getCXXThisVal(), |
171 | 187 | InstCall->getCXXThisExpr()); |
172 | 187 | return; |
173 | 187 | } |
174 | | |
175 | 3.09k | const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call); |
176 | 3.09k | if (Handler1) { |
177 | 326 | (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0)); |
178 | 326 | return; |
179 | 326 | } |
180 | | |
181 | 2.76k | const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call); |
182 | 2.76k | if (Handler2) { |
183 | 8 | (this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0), |
184 | 8 | Call.getArgSVal(1)); |
185 | 8 | return; |
186 | 8 | } |
187 | | |
188 | 2.76k | const auto *OrigExpr = Call.getOriginExpr(); |
189 | 2.76k | if (!OrigExpr) |
190 | 31 | return; |
191 | | |
192 | 2.73k | if (isBeginCall(Func)) { |
193 | 1.41k | handleBegin(C, OrigExpr, Call.getReturnValue(), |
194 | 1.41k | InstCall->getCXXThisVal()); |
195 | 1.41k | return; |
196 | 1.41k | } |
197 | | |
198 | 1.31k | if (isEndCall(Func)) { |
199 | 1.19k | handleEnd(C, OrigExpr, Call.getReturnValue(), |
200 | 1.19k | InstCall->getCXXThisVal()); |
201 | 1.19k | return; |
202 | 1.19k | } |
203 | 1.31k | } |
204 | 16.1k | } |
205 | 17.4k | } |
206 | | |
207 | | void ContainerModeling::checkLiveSymbols(ProgramStateRef State, |
208 | 40.1k | SymbolReaper &SR) const { |
209 | | // Keep symbolic expressions of container begins and ends alive |
210 | 40.1k | auto ContMap = State->get<ContainerMap>(); |
211 | 40.1k | for (const auto &Cont : ContMap) { |
212 | 34.0k | const auto CData = Cont.second; |
213 | 34.0k | if (CData.getBegin()) { |
214 | 30.4k | SR.markLive(CData.getBegin()); |
215 | 30.4k | if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin())) |
216 | 450 | SR.markLive(SIE->getLHS()); |
217 | 30.4k | } |
218 | 34.0k | if (CData.getEnd()) { |
219 | 16.9k | SR.markLive(CData.getEnd()); |
220 | 16.9k | if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd())) |
221 | 518 | SR.markLive(SIE->getLHS()); |
222 | 16.9k | } |
223 | 34.0k | } |
224 | 40.1k | } |
225 | | |
226 | | void ContainerModeling::checkDeadSymbols(SymbolReaper &SR, |
227 | 40.1k | CheckerContext &C) const { |
228 | | // Cleanup |
229 | 40.1k | auto State = C.getState(); |
230 | | |
231 | 40.1k | auto ContMap = State->get<ContainerMap>(); |
232 | 40.1k | for (const auto &Cont : ContMap) { |
233 | 34.0k | if (!SR.isLiveRegion(Cont.first)) { |
234 | | // We must keep the container data while it has live iterators to be able |
235 | | // to compare them to the begin and the end of the container. |
236 | 10.9k | if (!hasLiveIterators(State, Cont.first)) { |
237 | 990 | State = State->remove<ContainerMap>(Cont.first); |
238 | 990 | } |
239 | 10.9k | } |
240 | 34.0k | } |
241 | | |
242 | 40.1k | C.addTransition(State); |
243 | 40.1k | } |
244 | | |
245 | | void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE, |
246 | 1.41k | SVal RetVal, SVal Cont) const { |
247 | 1.41k | const auto *ContReg = Cont.getAsRegion(); |
248 | 1.41k | if (!ContReg) |
249 | 0 | return; |
250 | | |
251 | 1.41k | ContReg = ContReg->getMostDerivedObjectRegion(); |
252 | | |
253 | | // If the container already has a begin symbol then use it. Otherwise first |
254 | | // create a new one. |
255 | 1.41k | auto State = C.getState(); |
256 | 1.41k | auto BeginSym = getContainerBegin(State, ContReg); |
257 | 1.41k | if (!BeginSym) { |
258 | 1.12k | State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy, |
259 | 1.12k | C.getLocationContext(), C.blockCount()); |
260 | 1.12k | BeginSym = getContainerBegin(State, ContReg); |
261 | 1.12k | } |
262 | 1.41k | State = setIteratorPosition(State, RetVal, |
263 | 1.41k | IteratorPosition::getPosition(ContReg, BeginSym)); |
264 | 1.41k | C.addTransition(State); |
265 | 1.41k | } |
266 | | |
267 | | void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE, |
268 | 1.19k | SVal RetVal, SVal Cont) const { |
269 | 1.19k | const auto *ContReg = Cont.getAsRegion(); |
270 | 1.19k | if (!ContReg) |
271 | 0 | return; |
272 | | |
273 | 1.19k | ContReg = ContReg->getMostDerivedObjectRegion(); |
274 | | |
275 | | // If the container already has an end symbol then use it. Otherwise first |
276 | | // create a new one. |
277 | 1.19k | auto State = C.getState(); |
278 | 1.19k | auto EndSym = getContainerEnd(State, ContReg); |
279 | 1.19k | if (!EndSym) { |
280 | 857 | State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy, |
281 | 857 | C.getLocationContext(), C.blockCount()); |
282 | 857 | EndSym = getContainerEnd(State, ContReg); |
283 | 857 | } |
284 | 1.19k | State = setIteratorPosition(State, RetVal, |
285 | 1.19k | IteratorPosition::getPosition(ContReg, EndSym)); |
286 | 1.19k | C.addTransition(State); |
287 | 1.19k | } |
288 | | |
289 | | void ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont, |
290 | 51 | const Expr *CE, SVal OldCont) const { |
291 | 51 | const auto *ContReg = Cont.getAsRegion(); |
292 | 51 | if (!ContReg) |
293 | 0 | return; |
294 | | |
295 | 51 | ContReg = ContReg->getMostDerivedObjectRegion(); |
296 | | |
297 | | // Assignment of a new value to a container always invalidates all its |
298 | | // iterators |
299 | 51 | auto State = C.getState(); |
300 | 51 | const auto CData = getContainerData(State, ContReg); |
301 | 51 | if (CData) { |
302 | 51 | State = invalidateAllIteratorPositions(State, ContReg); |
303 | 51 | } |
304 | | |
305 | | // In case of move, iterators of the old container (except the past-end |
306 | | // iterators) remain valid but refer to the new container |
307 | 51 | if (!OldCont.isUndef()) { |
308 | 27 | const auto *OldContReg = OldCont.getAsRegion(); |
309 | 27 | if (OldContReg) { |
310 | 27 | OldContReg = OldContReg->getMostDerivedObjectRegion(); |
311 | 27 | const auto OldCData = getContainerData(State, OldContReg); |
312 | 27 | if (OldCData) { |
313 | 27 | if (const auto OldEndSym = OldCData->getEnd()) { |
314 | | // If we already assigned an "end" symbol to the old container, then |
315 | | // first reassign all iterator positions to the new container which |
316 | | // are not past the container (thus not greater or equal to the |
317 | | // current "end" symbol). |
318 | 27 | State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg, |
319 | 27 | OldEndSym, BO_GE); |
320 | 27 | auto &SymMgr = C.getSymbolManager(); |
321 | 27 | auto &SVB = C.getSValBuilder(); |
322 | | // Then generate and assign a new "end" symbol for the new container. |
323 | 27 | auto NewEndSym = |
324 | 27 | SymMgr.conjureSymbol(CE, C.getLocationContext(), |
325 | 27 | C.getASTContext().LongTy, C.blockCount()); |
326 | 27 | State = assumeNoOverflow(State, NewEndSym, 4); |
327 | 27 | if (CData) { |
328 | 27 | State = setContainerData(State, ContReg, CData->newEnd(NewEndSym)); |
329 | 27 | } else { |
330 | 0 | State = setContainerData(State, ContReg, |
331 | 0 | ContainerData::fromEnd(NewEndSym)); |
332 | 0 | } |
333 | | // Finally, replace the old "end" symbol in the already reassigned |
334 | | // iterator positions with the new "end" symbol. |
335 | 27 | State = rebaseSymbolInIteratorPositionsIf( |
336 | 27 | State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT); |
337 | 27 | } else { |
338 | | // There was no "end" symbol assigned yet to the old container, |
339 | | // so reassign all iterator positions to the new container. |
340 | 0 | State = reassignAllIteratorPositions(State, OldContReg, ContReg); |
341 | 0 | } |
342 | 27 | if (const auto OldBeginSym = OldCData->getBegin()) { |
343 | | // If we already assigned a "begin" symbol to the old container, then |
344 | | // assign it to the new container and remove it from the old one. |
345 | 27 | if (CData) { |
346 | 27 | State = |
347 | 27 | setContainerData(State, ContReg, CData->newBegin(OldBeginSym)); |
348 | 27 | } else { |
349 | 0 | State = setContainerData(State, ContReg, |
350 | 0 | ContainerData::fromBegin(OldBeginSym)); |
351 | 0 | } |
352 | 27 | State = |
353 | 27 | setContainerData(State, OldContReg, OldCData->newBegin(nullptr)); |
354 | 27 | } |
355 | 27 | } else { |
356 | | // There was neither "begin" nor "end" symbol assigned yet to the old |
357 | | // container, so reassign all iterator positions to the new container. |
358 | 0 | State = reassignAllIteratorPositions(State, OldContReg, ContReg); |
359 | 0 | } |
360 | 27 | } |
361 | 27 | } |
362 | 51 | C.addTransition(State); |
363 | 51 | } |
364 | | |
365 | | void ContainerModeling::handleAssign(CheckerContext &C, SVal Cont, |
366 | 24 | const Expr *ContE) const { |
367 | 24 | const auto *ContReg = Cont.getAsRegion(); |
368 | 24 | if (!ContReg) |
369 | 0 | return; |
370 | | |
371 | 24 | ContReg = ContReg->getMostDerivedObjectRegion(); |
372 | | |
373 | | // The assign() operation invalidates all the iterators |
374 | 24 | auto State = C.getState(); |
375 | 24 | State = invalidateAllIteratorPositions(State, ContReg); |
376 | 24 | C.addTransition(State); |
377 | 24 | } |
378 | | |
379 | | void ContainerModeling::handleClear(CheckerContext &C, SVal Cont, |
380 | 26 | const Expr *ContE) const { |
381 | 26 | const auto *ContReg = Cont.getAsRegion(); |
382 | 26 | if (!ContReg) |
383 | 0 | return; |
384 | | |
385 | 26 | ContReg = ContReg->getMostDerivedObjectRegion(); |
386 | | |
387 | | // The clear() operation invalidates all the iterators, except the past-end |
388 | | // iterators of list-like containers |
389 | 26 | auto State = C.getState(); |
390 | 26 | if (!hasSubscriptOperator(State, ContReg) || |
391 | 26 | !backModifiable(State, ContReg)12 ) { |
392 | 14 | const auto CData = getContainerData(State, ContReg); |
393 | 14 | if (CData) { |
394 | 14 | if (const auto EndSym = CData->getEnd()) { |
395 | 12 | State = |
396 | 12 | invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE); |
397 | 12 | C.addTransition(State); |
398 | 12 | return; |
399 | 12 | } |
400 | 14 | } |
401 | 14 | } |
402 | 14 | const NoteTag *ChangeTag = |
403 | 14 | getChangeTag(C, "became empty", ContReg, ContE); |
404 | 14 | State = invalidateAllIteratorPositions(State, ContReg); |
405 | 14 | C.addTransition(State, ChangeTag); |
406 | 14 | } |
407 | | |
408 | | void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont, |
409 | 51 | const Expr *ContE) const { |
410 | 51 | const auto *ContReg = Cont.getAsRegion(); |
411 | 51 | if (!ContReg) |
412 | 0 | return; |
413 | | |
414 | 51 | ContReg = ContReg->getMostDerivedObjectRegion(); |
415 | | |
416 | | // For deque-like containers invalidate all iterator positions |
417 | 51 | auto State = C.getState(); |
418 | 51 | if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)36 ) { |
419 | 12 | State = invalidateAllIteratorPositions(State, ContReg); |
420 | 12 | C.addTransition(State); |
421 | 12 | return; |
422 | 12 | } |
423 | | |
424 | 39 | const auto CData = getContainerData(State, ContReg); |
425 | 39 | if (!CData) |
426 | 0 | return; |
427 | | |
428 | | // For vector-like containers invalidate the past-end iterator positions |
429 | 39 | if (const auto EndSym = CData->getEnd()) { |
430 | 39 | if (hasSubscriptOperator(State, ContReg)) { |
431 | 24 | State = invalidateIteratorPositions(State, EndSym, BO_GE); |
432 | 24 | } |
433 | 39 | auto &SymMgr = C.getSymbolManager(); |
434 | 39 | auto &BVF = SymMgr.getBasicVals(); |
435 | 39 | auto &SVB = C.getSValBuilder(); |
436 | 39 | const auto newEndSym = |
437 | 39 | SVB.evalBinOp(State, BO_Add, |
438 | 39 | nonloc::SymbolVal(EndSym), |
439 | 39 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), |
440 | 39 | SymMgr.getType(EndSym)).getAsSymbol(); |
441 | 39 | const NoteTag *ChangeTag = |
442 | 39 | getChangeTag(C, "extended to the back by 1 position", ContReg, ContE); |
443 | 39 | State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); |
444 | 39 | C.addTransition(State, ChangeTag); |
445 | 39 | } |
446 | 39 | } |
447 | | |
448 | | void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont, |
449 | 23 | const Expr *ContE) const { |
450 | 23 | const auto *ContReg = Cont.getAsRegion(); |
451 | 23 | if (!ContReg) |
452 | 0 | return; |
453 | | |
454 | 23 | ContReg = ContReg->getMostDerivedObjectRegion(); |
455 | | |
456 | 23 | auto State = C.getState(); |
457 | 23 | const auto CData = getContainerData(State, ContReg); |
458 | 23 | if (!CData) |
459 | 0 | return; |
460 | | |
461 | 23 | if (const auto EndSym = CData->getEnd()) { |
462 | 23 | auto &SymMgr = C.getSymbolManager(); |
463 | 23 | auto &BVF = SymMgr.getBasicVals(); |
464 | 23 | auto &SVB = C.getSValBuilder(); |
465 | 23 | const auto BackSym = |
466 | 23 | SVB.evalBinOp(State, BO_Sub, |
467 | 23 | nonloc::SymbolVal(EndSym), |
468 | 23 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), |
469 | 23 | SymMgr.getType(EndSym)).getAsSymbol(); |
470 | 23 | const NoteTag *ChangeTag = |
471 | 23 | getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE); |
472 | | // For vector-like and deque-like containers invalidate the last and the |
473 | | // past-end iterator positions. For list-like containers only invalidate |
474 | | // the last position |
475 | 23 | if (hasSubscriptOperator(State, ContReg) && |
476 | 23 | backModifiable(State, ContReg)17 ) { |
477 | 17 | State = invalidateIteratorPositions(State, BackSym, BO_GE); |
478 | 17 | State = setContainerData(State, ContReg, CData->newEnd(nullptr)); |
479 | 17 | } else { |
480 | 6 | State = invalidateIteratorPositions(State, BackSym, BO_EQ); |
481 | 6 | } |
482 | 23 | auto newEndSym = BackSym; |
483 | 23 | State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); |
484 | 23 | C.addTransition(State, ChangeTag); |
485 | 23 | } |
486 | 23 | } |
487 | | |
488 | | void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont, |
489 | 42 | const Expr *ContE) const { |
490 | 42 | const auto *ContReg = Cont.getAsRegion(); |
491 | 42 | if (!ContReg) |
492 | 0 | return; |
493 | | |
494 | 42 | ContReg = ContReg->getMostDerivedObjectRegion(); |
495 | | |
496 | | // For deque-like containers invalidate all iterator positions |
497 | 42 | auto State = C.getState(); |
498 | 42 | if (hasSubscriptOperator(State, ContReg)) { |
499 | 12 | State = invalidateAllIteratorPositions(State, ContReg); |
500 | 12 | C.addTransition(State); |
501 | 30 | } else { |
502 | 30 | const auto CData = getContainerData(State, ContReg); |
503 | 30 | if (!CData) |
504 | 0 | return; |
505 | | |
506 | 30 | if (const auto BeginSym = CData->getBegin()) { |
507 | 30 | auto &SymMgr = C.getSymbolManager(); |
508 | 30 | auto &BVF = SymMgr.getBasicVals(); |
509 | 30 | auto &SVB = C.getSValBuilder(); |
510 | 30 | const auto newBeginSym = |
511 | 30 | SVB.evalBinOp(State, BO_Sub, |
512 | 30 | nonloc::SymbolVal(BeginSym), |
513 | 30 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), |
514 | 30 | SymMgr.getType(BeginSym)).getAsSymbol(); |
515 | 30 | const NoteTag *ChangeTag = |
516 | 30 | getChangeTag(C, "extended to the front by 1 position", ContReg, ContE); |
517 | 30 | State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); |
518 | 30 | C.addTransition(State, ChangeTag); |
519 | 30 | } |
520 | 30 | } |
521 | 42 | } |
522 | | |
523 | | void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont, |
524 | 21 | const Expr *ContE) const { |
525 | 21 | const auto *ContReg = Cont.getAsRegion(); |
526 | 21 | if (!ContReg) |
527 | 0 | return; |
528 | | |
529 | 21 | ContReg = ContReg->getMostDerivedObjectRegion(); |
530 | | |
531 | 21 | auto State = C.getState(); |
532 | 21 | const auto CData = getContainerData(State, ContReg); |
533 | 21 | if (!CData) |
534 | 0 | return; |
535 | | |
536 | | // For deque-like containers invalidate all iterator positions. For list-like |
537 | | // iterators only invalidate the first position |
538 | 21 | if (const auto BeginSym = CData->getBegin()) { |
539 | 21 | if (hasSubscriptOperator(State, ContReg)) { |
540 | 6 | State = invalidateIteratorPositions(State, BeginSym, BO_LE); |
541 | 15 | } else { |
542 | 15 | State = invalidateIteratorPositions(State, BeginSym, BO_EQ); |
543 | 15 | } |
544 | 21 | auto &SymMgr = C.getSymbolManager(); |
545 | 21 | auto &BVF = SymMgr.getBasicVals(); |
546 | 21 | auto &SVB = C.getSValBuilder(); |
547 | 21 | const auto newBeginSym = |
548 | 21 | SVB.evalBinOp(State, BO_Add, |
549 | 21 | nonloc::SymbolVal(BeginSym), |
550 | 21 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), |
551 | 21 | SymMgr.getType(BeginSym)).getAsSymbol(); |
552 | 21 | const NoteTag *ChangeTag = |
553 | 21 | getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE); |
554 | 21 | State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); |
555 | 21 | C.addTransition(State, ChangeTag); |
556 | 21 | } |
557 | 21 | } |
558 | | |
559 | | void ContainerModeling::handleInsert(CheckerContext &C, SVal Cont, |
560 | 194 | SVal Iter) const { |
561 | 194 | const auto *ContReg = Cont.getAsRegion(); |
562 | 194 | if (!ContReg) |
563 | 0 | return; |
564 | | |
565 | 194 | ContReg = ContReg->getMostDerivedObjectRegion(); |
566 | | |
567 | 194 | auto State = C.getState(); |
568 | 194 | const auto *Pos = getIteratorPosition(State, Iter); |
569 | 194 | if (!Pos) |
570 | 0 | return; |
571 | | |
572 | | // For deque-like containers invalidate all iterator positions. For |
573 | | // vector-like containers invalidate iterator positions after the insertion. |
574 | 194 | if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)134 ) { |
575 | 134 | if (frontModifiable(State, ContReg)) { |
576 | 60 | State = invalidateAllIteratorPositions(State, ContReg); |
577 | 74 | } else { |
578 | 74 | State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE); |
579 | 74 | } |
580 | 134 | if (const auto *CData = getContainerData(State, ContReg)) { |
581 | 128 | if (const auto EndSym = CData->getEnd()) { |
582 | 122 | State = invalidateIteratorPositions(State, EndSym, BO_GE); |
583 | 122 | State = setContainerData(State, ContReg, CData->newEnd(nullptr)); |
584 | 122 | } |
585 | 128 | } |
586 | 134 | C.addTransition(State); |
587 | 134 | } |
588 | 194 | } |
589 | | |
590 | | void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, |
591 | 120 | SVal Iter) const { |
592 | 120 | const auto *ContReg = Cont.getAsRegion(); |
593 | 120 | if (!ContReg) |
594 | 0 | return; |
595 | | |
596 | 120 | ContReg = ContReg->getMostDerivedObjectRegion(); |
597 | | |
598 | 120 | auto State = C.getState(); |
599 | 120 | const auto *Pos = getIteratorPosition(State, Iter); |
600 | 120 | if (!Pos) |
601 | 0 | return; |
602 | | |
603 | | // For deque-like containers invalidate all iterator positions. For |
604 | | // vector-like containers invalidate iterator positions at and after the |
605 | | // deletion. For list-like containers only invalidate the deleted position. |
606 | 120 | if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)96 ) { |
607 | 96 | if (frontModifiable(State, ContReg)) { |
608 | 24 | State = invalidateAllIteratorPositions(State, ContReg); |
609 | 72 | } else { |
610 | 72 | State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE); |
611 | 72 | } |
612 | 96 | if (const auto *CData = getContainerData(State, ContReg)) { |
613 | 92 | if (const auto EndSym = CData->getEnd()) { |
614 | 48 | State = invalidateIteratorPositions(State, EndSym, BO_GE); |
615 | 48 | State = setContainerData(State, ContReg, CData->newEnd(nullptr)); |
616 | 48 | } |
617 | 92 | } |
618 | 96 | } else { |
619 | 24 | State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ); |
620 | 24 | } |
621 | 120 | C.addTransition(State); |
622 | 120 | } |
623 | | |
624 | | void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1, |
625 | 8 | SVal Iter2) const { |
626 | 8 | const auto *ContReg = Cont.getAsRegion(); |
627 | 8 | if (!ContReg) |
628 | 0 | return; |
629 | | |
630 | 8 | ContReg = ContReg->getMostDerivedObjectRegion(); |
631 | 8 | auto State = C.getState(); |
632 | 8 | const auto *Pos1 = getIteratorPosition(State, Iter1); |
633 | 8 | const auto *Pos2 = getIteratorPosition(State, Iter2); |
634 | 8 | if (!Pos1 || !Pos2) |
635 | 0 | return; |
636 | | |
637 | | // For deque-like containers invalidate all iterator positions. For |
638 | | // vector-like containers invalidate iterator positions at and after the |
639 | | // deletion range. For list-like containers only invalidate the deleted |
640 | | // position range [first..last]. |
641 | 8 | if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) { |
642 | 8 | if (frontModifiable(State, ContReg)) { |
643 | 0 | State = invalidateAllIteratorPositions(State, ContReg); |
644 | 8 | } else { |
645 | 8 | State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE); |
646 | 8 | } |
647 | 8 | if (const auto *CData = getContainerData(State, ContReg)) { |
648 | 8 | if (const auto EndSym = CData->getEnd()) { |
649 | 4 | State = invalidateIteratorPositions(State, EndSym, BO_GE); |
650 | 4 | State = setContainerData(State, ContReg, CData->newEnd(nullptr)); |
651 | 4 | } |
652 | 8 | } |
653 | 8 | } else { |
654 | 0 | State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE, |
655 | 0 | Pos2->getOffset(), BO_LT); |
656 | 0 | } |
657 | 8 | C.addTransition(State); |
658 | 8 | } |
659 | | |
660 | | void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont, |
661 | 12 | SVal Iter) const { |
662 | 12 | auto State = C.getState(); |
663 | 12 | const auto *Pos = getIteratorPosition(State, Iter); |
664 | 12 | if (!Pos) |
665 | 0 | return; |
666 | | |
667 | | // Invalidate the deleted iterator position, which is the position of the |
668 | | // parameter plus one. |
669 | 12 | auto &SymMgr = C.getSymbolManager(); |
670 | 12 | auto &BVF = SymMgr.getBasicVals(); |
671 | 12 | auto &SVB = C.getSValBuilder(); |
672 | 12 | const auto NextSym = |
673 | 12 | SVB.evalBinOp(State, BO_Add, |
674 | 12 | nonloc::SymbolVal(Pos->getOffset()), |
675 | 12 | nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), |
676 | 12 | SymMgr.getType(Pos->getOffset())).getAsSymbol(); |
677 | 12 | State = invalidateIteratorPositions(State, NextSym, BO_EQ); |
678 | 12 | C.addTransition(State); |
679 | 12 | } |
680 | | |
681 | | void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont, |
682 | 0 | SVal Iter1, SVal Iter2) const { |
683 | 0 | auto State = C.getState(); |
684 | 0 | const auto *Pos1 = getIteratorPosition(State, Iter1); |
685 | 0 | const auto *Pos2 = getIteratorPosition(State, Iter2); |
686 | 0 | if (!Pos1 || !Pos2) |
687 | 0 | return; |
688 | | |
689 | | // Invalidate the deleted iterator position range (first..last) |
690 | 0 | State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT, |
691 | 0 | Pos2->getOffset(), BO_LT); |
692 | 0 | C.addTransition(State); |
693 | 0 | } |
694 | | |
695 | | const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C, |
696 | | StringRef Text, |
697 | | const MemRegion *ContReg, |
698 | 127 | const Expr *ContE) const { |
699 | 127 | StringRef Name; |
700 | | // First try to get the name of the variable from the region |
701 | 127 | if (const auto *DR = dyn_cast<DeclRegion>(ContReg)) { |
702 | 5 | Name = DR->getDecl()->getName(); |
703 | | // If the region is not a `DeclRegion` then use the expression instead |
704 | 122 | } else if (const auto *DRE = |
705 | 122 | dyn_cast<DeclRefExpr>(ContE->IgnoreParenCasts())) { |
706 | 122 | Name = DRE->getDecl()->getName(); |
707 | 122 | } |
708 | | |
709 | 127 | return C.getNoteTag( |
710 | 460 | [Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string { |
711 | 460 | if (!BR.isInteresting(ContReg)) |
712 | 430 | return ""; |
713 | | |
714 | 30 | SmallString<256> Msg; |
715 | 30 | llvm::raw_svector_ostream Out(Msg); |
716 | 30 | Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : ""0 ) |
717 | 30 | << Text; |
718 | 30 | return std::string(Out.str()); |
719 | 460 | }); |
720 | 127 | } |
721 | | |
722 | | void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State, |
723 | 36 | const char *NL, const char *Sep) const { |
724 | 36 | auto ContMap = State->get<ContainerMap>(); |
725 | | |
726 | 36 | if (!ContMap.isEmpty()) { |
727 | 36 | Out << Sep << "Container Data :" << NL; |
728 | 36 | for (const auto &Cont : ContMap) { |
729 | 36 | Cont.first->dumpToStream(Out); |
730 | 36 | Out << " : [ "; |
731 | 36 | const auto CData = Cont.second; |
732 | 36 | if (CData.getBegin()) |
733 | 36 | CData.getBegin()->dumpToStream(Out); |
734 | 0 | else |
735 | 0 | Out << "<Unknown>"; |
736 | 36 | Out << " .. "; |
737 | 36 | if (CData.getEnd()) |
738 | 18 | CData.getEnd()->dumpToStream(Out); |
739 | 18 | else |
740 | 18 | Out << "<Unknown>"; |
741 | 36 | Out << " ]"; |
742 | 36 | } |
743 | 36 | } |
744 | 36 | } |
745 | | |
746 | | namespace { |
747 | | |
748 | 2.73k | bool isBeginCall(const FunctionDecl *Func) { |
749 | 2.73k | const auto *IdInfo = Func->getIdentifier(); |
750 | 2.73k | if (!IdInfo) |
751 | 0 | return false; |
752 | 2.73k | return IdInfo->getName().ends_with_insensitive("begin"); |
753 | 2.73k | } |
754 | | |
755 | 1.31k | bool isEndCall(const FunctionDecl *Func) { |
756 | 1.31k | const auto *IdInfo = Func->getIdentifier(); |
757 | 1.31k | if (!IdInfo) |
758 | 0 | return false; |
759 | 1.31k | return IdInfo->getName().ends_with_insensitive("end"); |
760 | 1.31k | } |
761 | | |
762 | | const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State, |
763 | 1.06k | const MemRegion *Reg) { |
764 | 1.06k | auto TI = getDynamicTypeInfo(State, Reg); |
765 | 1.06k | if (!TI.isValid()) |
766 | 0 | return nullptr; |
767 | | |
768 | 1.06k | auto Type = TI.getType(); |
769 | 1.06k | if (const auto *RefT = Type->getAs<ReferenceType>()) { |
770 | 1.05k | Type = RefT->getPointeeType(); |
771 | 1.05k | } |
772 | | |
773 | 1.06k | return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl(); |
774 | 1.06k | } |
775 | | |
776 | 524 | bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) { |
777 | 524 | const auto *CRD = getCXXRecordDecl(State, Reg); |
778 | 524 | if (!CRD) |
779 | 8 | return false; |
780 | | |
781 | 12.6k | for (const auto *Method : CRD->methods())516 { |
782 | 12.6k | if (!Method->isOverloadedOperator()) |
783 | 10.8k | continue; |
784 | 1.83k | const auto OPK = Method->getOverloadedOperator(); |
785 | 1.83k | if (OPK == OO_Subscript) { |
786 | 345 | return true; |
787 | 345 | } |
788 | 1.83k | } |
789 | 171 | return false; |
790 | 516 | } |
791 | | |
792 | 274 | bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) { |
793 | 274 | const auto *CRD = getCXXRecordDecl(State, Reg); |
794 | 274 | if (!CRD) |
795 | 0 | return false; |
796 | | |
797 | 6.59k | for (const auto *Method : CRD->methods())274 { |
798 | 6.59k | if (!Method->getDeclName().isIdentifier()) |
799 | 2.11k | continue; |
800 | 4.48k | if (Method->getName() == "push_front" || Method->getName() == "pop_front"4.38k ) { |
801 | 96 | return true; |
802 | 96 | } |
803 | 4.48k | } |
804 | 178 | return false; |
805 | 274 | } |
806 | | |
807 | 267 | bool backModifiable(ProgramStateRef State, const MemRegion *Reg) { |
808 | 267 | const auto *CRD = getCXXRecordDecl(State, Reg); |
809 | 267 | if (!CRD) |
810 | 0 | return false; |
811 | | |
812 | 3.04k | for (const auto *Method : CRD->methods())267 { |
813 | 3.04k | if (!Method->getDeclName().isIdentifier()) |
814 | 1.74k | continue; |
815 | 1.29k | if (Method->getName() == "push_back" || Method->getName() == "pop_back"1.02k ) { |
816 | 267 | return true; |
817 | 267 | } |
818 | 1.29k | } |
819 | 0 | return false; |
820 | 267 | } |
821 | | |
822 | 2.53k | SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) { |
823 | 2.53k | const auto *CDataPtr = getContainerData(State, Cont); |
824 | 2.53k | if (!CDataPtr) |
825 | 1.12k | return nullptr; |
826 | | |
827 | 1.41k | return CDataPtr->getBegin(); |
828 | 2.53k | } |
829 | | |
830 | 2.05k | SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) { |
831 | 2.05k | const auto *CDataPtr = getContainerData(State, Cont); |
832 | 2.05k | if (!CDataPtr) |
833 | 270 | return nullptr; |
834 | | |
835 | 1.78k | return CDataPtr->getEnd(); |
836 | 2.05k | } |
837 | | |
838 | | ProgramStateRef createContainerBegin(ProgramStateRef State, |
839 | | const MemRegion *Cont, const Expr *E, |
840 | | QualType T, const LocationContext *LCtx, |
841 | 1.12k | unsigned BlockCount) { |
842 | | // Only create if it does not exist |
843 | 1.12k | const auto *CDataPtr = getContainerData(State, Cont); |
844 | 1.12k | if (CDataPtr && CDataPtr->getBegin()4 ) |
845 | 0 | return State; |
846 | | |
847 | 1.12k | auto &SymMgr = State->getSymbolManager(); |
848 | 1.12k | const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount, |
849 | 1.12k | "begin"); |
850 | 1.12k | State = assumeNoOverflow(State, Sym, 4); |
851 | | |
852 | 1.12k | if (CDataPtr) { |
853 | 4 | const auto CData = CDataPtr->newBegin(Sym); |
854 | 4 | return setContainerData(State, Cont, CData); |
855 | 4 | } |
856 | | |
857 | 1.12k | const auto CData = ContainerData::fromBegin(Sym); |
858 | 1.12k | return setContainerData(State, Cont, CData); |
859 | 1.12k | } |
860 | | |
861 | | ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, |
862 | | const Expr *E, QualType T, |
863 | | const LocationContext *LCtx, |
864 | 857 | unsigned BlockCount) { |
865 | | // Only create if it does not exist |
866 | 857 | const auto *CDataPtr = getContainerData(State, Cont); |
867 | 857 | if (CDataPtr && CDataPtr->getEnd()587 ) |
868 | 0 | return State; |
869 | | |
870 | 857 | auto &SymMgr = State->getSymbolManager(); |
871 | 857 | const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount, |
872 | 857 | "end"); |
873 | 857 | State = assumeNoOverflow(State, Sym, 4); |
874 | | |
875 | 857 | if (CDataPtr) { |
876 | 587 | const auto CData = CDataPtr->newEnd(Sym); |
877 | 587 | return setContainerData(State, Cont, CData); |
878 | 587 | } |
879 | | |
880 | 270 | const auto CData = ContainerData::fromEnd(Sym); |
881 | 270 | return setContainerData(State, Cont, CData); |
882 | 857 | } |
883 | | |
884 | | ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, |
885 | 2.36k | const ContainerData &CData) { |
886 | 2.36k | return State->set<ContainerMap>(Cont, CData); |
887 | 2.36k | } |
888 | | |
889 | | template <typename Condition, typename Process> |
890 | | ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond, |
891 | 695 | Process Proc) { |
892 | 695 | auto &RegionMapFactory = State->get_context<IteratorRegionMap>(); |
893 | 695 | auto RegionMap = State->get<IteratorRegionMap>(); |
894 | 695 | bool Changed = false; |
895 | 2.17k | for (const auto &Reg : RegionMap) { |
896 | 2.17k | if (Cond(Reg.second)) { |
897 | 1.19k | RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second)); |
898 | 1.19k | Changed = true; |
899 | 1.19k | } |
900 | 2.17k | } |
901 | | |
902 | 695 | if (Changed) |
903 | 660 | State = State->set<IteratorRegionMap>(RegionMap); |
904 | | |
905 | 695 | auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>(); |
906 | 695 | auto SymbolMap = State->get<IteratorSymbolMap>(); |
907 | 695 | Changed = false; |
908 | 695 | for (const auto &Sym : SymbolMap) { |
909 | 0 | if (Cond(Sym.second)) { |
910 | 0 | SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second)); |
911 | 0 | Changed = true; |
912 | 0 | } |
913 | 0 | } |
914 | | |
915 | 695 | if (Changed) |
916 | 0 | State = State->set<IteratorSymbolMap>(SymbolMap); |
917 | | |
918 | 695 | return State; |
919 | 695 | } ContainerModeling.cpp:llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> (anonymous namespace)::processIteratorPositions<(anonymous namespace)::invalidateAllIteratorPositionsExcept(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_0, (anonymous namespace)::invalidateAllIteratorPositionsExcept(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_1>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, (anonymous namespace)::invalidateAllIteratorPositionsExcept(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_0, (anonymous namespace)::invalidateAllIteratorPositionsExcept(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_1) Line | Count | Source | 891 | 12 | Process Proc) { | 892 | 12 | auto &RegionMapFactory = State->get_context<IteratorRegionMap>(); | 893 | 12 | auto RegionMap = State->get<IteratorRegionMap>(); | 894 | 12 | bool Changed = false; | 895 | 12 | for (const auto &Reg : RegionMap) { | 896 | 12 | if (Cond(Reg.second)) { | 897 | 12 | RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second)); | 898 | 12 | Changed = true; | 899 | 12 | } | 900 | 12 | } | 901 | | | 902 | 12 | if (Changed) | 903 | 12 | State = State->set<IteratorRegionMap>(RegionMap); | 904 | | | 905 | 12 | auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>(); | 906 | 12 | auto SymbolMap = State->get<IteratorSymbolMap>(); | 907 | 12 | Changed = false; | 908 | 12 | for (const auto &Sym : SymbolMap) { | 909 | 0 | if (Cond(Sym.second)) { | 910 | 0 | SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second)); | 911 | 0 | Changed = true; | 912 | 0 | } | 913 | 0 | } | 914 | | | 915 | 12 | if (Changed) | 916 | 0 | State = State->set<IteratorSymbolMap>(SymbolMap); | 917 | | | 918 | 12 | return State; | 919 | 12 | } |
ContainerModeling.cpp:llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> (anonymous namespace)::processIteratorPositions<(anonymous namespace)::invalidateAllIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*)::$_0, (anonymous namespace)::invalidateAllIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*)::$_1>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, (anonymous namespace)::invalidateAllIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*)::$_0, (anonymous namespace)::invalidateAllIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*)::$_1) Line | Count | Source | 891 | 197 | Process Proc) { | 892 | 197 | auto &RegionMapFactory = State->get_context<IteratorRegionMap>(); | 893 | 197 | auto RegionMap = State->get<IteratorRegionMap>(); | 894 | 197 | bool Changed = false; | 895 | 572 | for (const auto &Reg : RegionMap) { | 896 | 572 | if (Cond(Reg.second)) { | 897 | 506 | RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second)); | 898 | 506 | Changed = true; | 899 | 506 | } | 900 | 572 | } | 901 | | | 902 | 197 | if (Changed) | 903 | 194 | State = State->set<IteratorRegionMap>(RegionMap); | 904 | | | 905 | 197 | auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>(); | 906 | 197 | auto SymbolMap = State->get<IteratorSymbolMap>(); | 907 | 197 | Changed = false; | 908 | 197 | for (const auto &Sym : SymbolMap) { | 909 | 0 | if (Cond(Sym.second)) { | 910 | 0 | SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second)); | 911 | 0 | Changed = true; | 912 | 0 | } | 913 | 0 | } | 914 | | | 915 | 197 | if (Changed) | 916 | 0 | State = State->set<IteratorSymbolMap>(SymbolMap); | 917 | | | 918 | 197 | return State; | 919 | 197 | } |
ContainerModeling.cpp:llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> (anonymous namespace)::processIteratorPositions<(anonymous namespace)::invalidateIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_0, (anonymous namespace)::invalidateIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_1>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, (anonymous namespace)::invalidateIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_0, (anonymous namespace)::invalidateIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_1) Line | Count | Source | 891 | 432 | Process Proc) { | 892 | 432 | auto &RegionMapFactory = State->get_context<IteratorRegionMap>(); | 893 | 432 | auto RegionMap = State->get<IteratorRegionMap>(); | 894 | 432 | bool Changed = false; | 895 | 1.41k | for (const auto &Reg : RegionMap) { | 896 | 1.41k | if (Cond(Reg.second)) { | 897 | 620 | RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second)); | 898 | 620 | Changed = true; | 899 | 620 | } | 900 | 1.41k | } | 901 | | | 902 | 432 | if (Changed) | 903 | 412 | State = State->set<IteratorRegionMap>(RegionMap); | 904 | | | 905 | 432 | auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>(); | 906 | 432 | auto SymbolMap = State->get<IteratorSymbolMap>(); | 907 | 432 | Changed = false; | 908 | 432 | for (const auto &Sym : SymbolMap) { | 909 | 0 | if (Cond(Sym.second)) { | 910 | 0 | SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second)); | 911 | 0 | Changed = true; | 912 | 0 | } | 913 | 0 | } | 914 | | | 915 | 432 | if (Changed) | 916 | 0 | State = State->set<IteratorSymbolMap>(SymbolMap); | 917 | | | 918 | 432 | return State; | 919 | 432 | } |
Unexecuted instantiation: ContainerModeling.cpp:llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> (anonymous namespace)::processIteratorPositions<(anonymous namespace)::invalidateIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SymExpr const*, clang::BinaryOperatorKind, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_0, (anonymous namespace)::invalidateIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SymExpr const*, clang::BinaryOperatorKind, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_1>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, (anonymous namespace)::invalidateIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SymExpr const*, clang::BinaryOperatorKind, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_0, (anonymous namespace)::invalidateIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SymExpr const*, clang::BinaryOperatorKind, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_1) ContainerModeling.cpp:llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> (anonymous namespace)::processIteratorPositions<(anonymous namespace)::reassignAllIteratorPositionsUnless(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*, clang::ento::MemRegion const*, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_0, (anonymous namespace)::reassignAllIteratorPositionsUnless(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*, clang::ento::MemRegion const*, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_1>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, (anonymous namespace)::reassignAllIteratorPositionsUnless(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*, clang::ento::MemRegion const*, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_0, (anonymous namespace)::reassignAllIteratorPositionsUnless(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*, clang::ento::MemRegion const*, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_1) Line | Count | Source | 891 | 27 | Process Proc) { | 892 | 27 | auto &RegionMapFactory = State->get_context<IteratorRegionMap>(); | 893 | 27 | auto RegionMap = State->get<IteratorRegionMap>(); | 894 | 27 | bool Changed = false; | 895 | 90 | for (const auto &Reg : RegionMap) { | 896 | 90 | if (Cond(Reg.second)) { | 897 | 42 | RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second)); | 898 | 42 | Changed = true; | 899 | 42 | } | 900 | 90 | } | 901 | | | 902 | 27 | if (Changed) | 903 | 24 | State = State->set<IteratorRegionMap>(RegionMap); | 904 | | | 905 | 27 | auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>(); | 906 | 27 | auto SymbolMap = State->get<IteratorSymbolMap>(); | 907 | 27 | Changed = false; | 908 | 27 | for (const auto &Sym : SymbolMap) { | 909 | 0 | if (Cond(Sym.second)) { | 910 | 0 | SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second)); | 911 | 0 | Changed = true; | 912 | 0 | } | 913 | 0 | } | 914 | | | 915 | 27 | if (Changed) | 916 | 0 | State = State->set<IteratorSymbolMap>(SymbolMap); | 917 | | | 918 | 27 | return State; | 919 | 27 | } |
ContainerModeling.cpp:llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> (anonymous namespace)::processIteratorPositions<(anonymous namespace)::rebaseSymbolInIteratorPositionsIf(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SValBuilder&, clang::ento::SymExpr const*, clang::ento::SymExpr const*, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_0, (anonymous namespace)::rebaseSymbolInIteratorPositionsIf(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SValBuilder&, clang::ento::SymExpr const*, clang::ento::SymExpr const*, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_1>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, (anonymous namespace)::rebaseSymbolInIteratorPositionsIf(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SValBuilder&, clang::ento::SymExpr const*, clang::ento::SymExpr const*, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_0, (anonymous namespace)::rebaseSymbolInIteratorPositionsIf(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::SValBuilder&, clang::ento::SymExpr const*, clang::ento::SymExpr const*, clang::ento::SymExpr const*, clang::BinaryOperatorKind)::$_1) Line | Count | Source | 891 | 27 | Process Proc) { | 892 | 27 | auto &RegionMapFactory = State->get_context<IteratorRegionMap>(); | 893 | 27 | auto RegionMap = State->get<IteratorRegionMap>(); | 894 | 27 | bool Changed = false; | 895 | 90 | for (const auto &Reg : RegionMap) { | 896 | 90 | if (Cond(Reg.second)) { | 897 | 18 | RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second)); | 898 | 18 | Changed = true; | 899 | 18 | } | 900 | 90 | } | 901 | | | 902 | 27 | if (Changed) | 903 | 18 | State = State->set<IteratorRegionMap>(RegionMap); | 904 | | | 905 | 27 | auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>(); | 906 | 27 | auto SymbolMap = State->get<IteratorSymbolMap>(); | 907 | 27 | Changed = false; | 908 | 27 | for (const auto &Sym : SymbolMap) { | 909 | 0 | if (Cond(Sym.second)) { | 910 | 0 | SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second)); | 911 | 0 | Changed = true; | 912 | 0 | } | 913 | 0 | } | 914 | | | 915 | 27 | if (Changed) | 916 | 0 | State = State->set<IteratorSymbolMap>(SymbolMap); | 917 | | | 918 | 27 | return State; | 919 | 27 | } |
Unexecuted instantiation: ContainerModeling.cpp:llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> (anonymous namespace)::processIteratorPositions<(anonymous namespace)::reassignAllIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*, clang::ento::MemRegion const*)::$_0, (anonymous namespace)::reassignAllIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*, clang::ento::MemRegion const*)::$_1>(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, (anonymous namespace)::reassignAllIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*, clang::ento::MemRegion const*)::$_0, (anonymous namespace)::reassignAllIteratorPositions(llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>, clang::ento::MemRegion const*, clang::ento::MemRegion const*)::$_1) |
920 | | |
921 | | ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State, |
922 | 197 | const MemRegion *Cont) { |
923 | 572 | auto MatchCont = [&](const IteratorPosition &Pos) { |
924 | 572 | return Pos.getContainer() == Cont; |
925 | 572 | }; |
926 | 506 | auto Invalidate = [&](const IteratorPosition &Pos) { |
927 | 506 | return Pos.invalidate(); |
928 | 506 | }; |
929 | 197 | return processIteratorPositions(State, MatchCont, Invalidate); |
930 | 197 | } |
931 | | |
932 | | ProgramStateRef |
933 | | invalidateAllIteratorPositionsExcept(ProgramStateRef State, |
934 | | const MemRegion *Cont, SymbolRef Offset, |
935 | 12 | BinaryOperator::Opcode Opc) { |
936 | 12 | auto MatchContAndCompare = [&](const IteratorPosition &Pos) { |
937 | 12 | return Pos.getContainer() == Cont && |
938 | 12 | !compare(State, Pos.getOffset(), Offset, Opc); |
939 | 12 | }; |
940 | 12 | auto Invalidate = [&](const IteratorPosition &Pos) { |
941 | 12 | return Pos.invalidate(); |
942 | 12 | }; |
943 | 12 | return processIteratorPositions(State, MatchContAndCompare, Invalidate); |
944 | 12 | } |
945 | | |
946 | | ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, |
947 | | SymbolRef Offset, |
948 | 432 | BinaryOperator::Opcode Opc) { |
949 | 1.41k | auto Compare = [&](const IteratorPosition &Pos) { |
950 | 1.41k | return compare(State, Pos.getOffset(), Offset, Opc); |
951 | 1.41k | }; |
952 | 620 | auto Invalidate = [&](const IteratorPosition &Pos) { |
953 | 620 | return Pos.invalidate(); |
954 | 620 | }; |
955 | 432 | return processIteratorPositions(State, Compare, Invalidate); |
956 | 432 | } |
957 | | |
958 | | ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, |
959 | | SymbolRef Offset1, |
960 | | BinaryOperator::Opcode Opc1, |
961 | | SymbolRef Offset2, |
962 | 0 | BinaryOperator::Opcode Opc2) { |
963 | 0 | auto Compare = [&](const IteratorPosition &Pos) { |
964 | 0 | return compare(State, Pos.getOffset(), Offset1, Opc1) && |
965 | 0 | compare(State, Pos.getOffset(), Offset2, Opc2); |
966 | 0 | }; |
967 | 0 | auto Invalidate = [&](const IteratorPosition &Pos) { |
968 | 0 | return Pos.invalidate(); |
969 | 0 | }; |
970 | 0 | return processIteratorPositions(State, Compare, Invalidate); |
971 | 0 | } |
972 | | |
973 | | ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State, |
974 | | const MemRegion *Cont, |
975 | 0 | const MemRegion *NewCont) { |
976 | 0 | auto MatchCont = [&](const IteratorPosition &Pos) { |
977 | 0 | return Pos.getContainer() == Cont; |
978 | 0 | }; |
979 | 0 | auto ReAssign = [&](const IteratorPosition &Pos) { |
980 | 0 | return Pos.reAssign(NewCont); |
981 | 0 | }; |
982 | 0 | return processIteratorPositions(State, MatchCont, ReAssign); |
983 | 0 | } |
984 | | |
985 | | ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State, |
986 | | const MemRegion *Cont, |
987 | | const MemRegion *NewCont, |
988 | | SymbolRef Offset, |
989 | 27 | BinaryOperator::Opcode Opc) { |
990 | 90 | auto MatchContAndCompare = [&](const IteratorPosition &Pos) { |
991 | 90 | return Pos.getContainer() == Cont && |
992 | 90 | !compare(State, Pos.getOffset(), Offset, Opc)66 ; |
993 | 90 | }; |
994 | 42 | auto ReAssign = [&](const IteratorPosition &Pos) { |
995 | 42 | return Pos.reAssign(NewCont); |
996 | 42 | }; |
997 | 27 | return processIteratorPositions(State, MatchContAndCompare, ReAssign); |
998 | 27 | } |
999 | | |
1000 | | // This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`, |
1001 | | // `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator |
1002 | | // position offsets where `CondSym` is true. |
1003 | | ProgramStateRef rebaseSymbolInIteratorPositionsIf( |
1004 | | ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym, |
1005 | 27 | SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) { |
1006 | 90 | auto LessThanEnd = [&](const IteratorPosition &Pos) { |
1007 | 90 | return compare(State, Pos.getOffset(), CondSym, Opc); |
1008 | 90 | }; |
1009 | 27 | auto RebaseSymbol = [&](const IteratorPosition &Pos) { |
1010 | 18 | return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym, |
1011 | 18 | NewSym)); |
1012 | 18 | }; |
1013 | 27 | return processIteratorPositions(State, LessThanEnd, RebaseSymbol); |
1014 | 27 | } |
1015 | | |
1016 | | // This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`, |
1017 | | // `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression |
1018 | | // `OrigExpr`. |
1019 | | SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, |
1020 | | SymbolRef OrigExpr, SymbolRef OldExpr, |
1021 | 18 | SymbolRef NewSym) { |
1022 | 18 | auto &SymMgr = SVB.getSymbolManager(); |
1023 | 18 | auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr), |
1024 | 18 | nonloc::SymbolVal(OldExpr), |
1025 | 18 | SymMgr.getType(OrigExpr)); |
1026 | | |
1027 | 18 | const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>(); |
1028 | 18 | if (!DiffInt) |
1029 | 0 | return OrigExpr; |
1030 | | |
1031 | 18 | return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym), |
1032 | 18 | SymMgr.getType(OrigExpr)).getAsSymbol(); |
1033 | 18 | } |
1034 | | |
1035 | 10.9k | bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) { |
1036 | 10.9k | auto RegionMap = State->get<IteratorRegionMap>(); |
1037 | 11.2k | for (const auto &Reg : RegionMap) { |
1038 | 11.2k | if (Reg.second.getContainer() == Cont) |
1039 | 9.96k | return true; |
1040 | 11.2k | } |
1041 | | |
1042 | 994 | auto SymbolMap = State->get<IteratorSymbolMap>(); |
1043 | 994 | for (const auto &Sym : SymbolMap) { |
1044 | 30 | if (Sym.second.getContainer() == Cont) |
1045 | 4 | return true; |
1046 | 30 | } |
1047 | | |
1048 | 990 | return false; |
1049 | 994 | } |
1050 | | |
1051 | | } // namespace |
1052 | | |
1053 | 21 | void ento::registerContainerModeling(CheckerManager &mgr) { |
1054 | 21 | mgr.registerChecker<ContainerModeling>(); |
1055 | 21 | } |
1056 | | |
1057 | 74 | bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) { |
1058 | 74 | if (!mgr.getLangOpts().CPlusPlus) |
1059 | 2 | return false; |
1060 | | |
1061 | 72 | if (!mgr.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) { |
1062 | 4 | mgr.getASTContext().getDiagnostics().Report( |
1063 | 4 | diag::err_analyzer_checker_incompatible_analyzer_option) |
1064 | 4 | << "aggressive-binary-operation-simplification" << "false"; |
1065 | 4 | return false; |
1066 | 4 | } |
1067 | | |
1068 | 68 | return true; |
1069 | 72 | } |