[DFAJumpThreading] Remove incoming StartBlock from all phis when unfolding select...
[llvm-project.git] / clang / lib / StaticAnalyzer / Checkers / ContainerModeling.cpp
blob65a2ec4076fdf69d64f87c22c9c4c42ca7b72a0d
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.
11 //===----------------------------------------------------------------------===//
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"
23 #include "Iterator.h"
25 #include <utility>
27 using namespace clang;
28 using namespace ento;
29 using namespace iterator;
31 namespace {
33 class ContainerModeling
34 : public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> {
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;
60 public:
61 ContainerModeling() = default;
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;
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;
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},
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},
92 CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = {
93 {{{"erase"}, 2}, &ContainerModeling::handleErase},
94 {{{"erase_after"}, 2}, &ContainerModeling::handleEraseAfter},
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);
144 } // namespace
146 void ContainerModeling::checkPostCall(const CallEvent &Call,
147 CheckerContext &C) const {
148 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
149 if (!Func)
150 return;
152 if (Func->isOverloadedOperator()) {
153 const auto Op = Func->getOverloadedOperator();
154 if (Op == OO_Equal) {
155 // Overloaded 'operator=' must be a non-static member function.
156 const auto *InstCall = cast<CXXInstanceCall>(&Call);
157 if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
158 handleAssignment(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
159 Call.getArgSVal(0));
160 return;
163 handleAssignment(C, InstCall->getCXXThisVal());
164 return;
166 } else {
167 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
168 const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call);
169 if (Handler0) {
170 (this->**Handler0)(C, InstCall->getCXXThisVal(),
171 InstCall->getCXXThisExpr());
172 return;
175 const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call);
176 if (Handler1) {
177 (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
178 return;
181 const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call);
182 if (Handler2) {
183 (this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0),
184 Call.getArgSVal(1));
185 return;
188 const auto *OrigExpr = Call.getOriginExpr();
189 if (!OrigExpr)
190 return;
192 if (isBeginCall(Func)) {
193 handleBegin(C, OrigExpr, Call.getReturnValue(),
194 InstCall->getCXXThisVal());
195 return;
198 if (isEndCall(Func)) {
199 handleEnd(C, OrigExpr, Call.getReturnValue(),
200 InstCall->getCXXThisVal());
201 return;
207 void ContainerModeling::checkLiveSymbols(ProgramStateRef State,
208 SymbolReaper &SR) const {
209 // Keep symbolic expressions of container begins and ends alive
210 auto ContMap = State->get<ContainerMap>();
211 for (const auto &Cont : ContMap) {
212 const auto CData = Cont.second;
213 if (CData.getBegin()) {
214 SR.markLive(CData.getBegin());
215 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
216 SR.markLive(SIE->getLHS());
218 if (CData.getEnd()) {
219 SR.markLive(CData.getEnd());
220 if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
221 SR.markLive(SIE->getLHS());
226 void ContainerModeling::checkDeadSymbols(SymbolReaper &SR,
227 CheckerContext &C) const {
228 // Cleanup
229 auto State = C.getState();
231 auto ContMap = State->get<ContainerMap>();
232 for (const auto &Cont : ContMap) {
233 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 if (!hasLiveIterators(State, Cont.first)) {
237 State = State->remove<ContainerMap>(Cont.first);
242 C.addTransition(State);
245 void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE,
246 SVal RetVal, SVal Cont) const {
247 const auto *ContReg = Cont.getAsRegion();
248 if (!ContReg)
249 return;
251 ContReg = ContReg->getMostDerivedObjectRegion();
253 // If the container already has a begin symbol then use it. Otherwise first
254 // create a new one.
255 auto State = C.getState();
256 auto BeginSym = getContainerBegin(State, ContReg);
257 if (!BeginSym) {
258 State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
259 C.getLocationContext(), C.blockCount());
260 BeginSym = getContainerBegin(State, ContReg);
262 State = setIteratorPosition(State, RetVal,
263 IteratorPosition::getPosition(ContReg, BeginSym));
264 C.addTransition(State);
267 void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE,
268 SVal RetVal, SVal Cont) const {
269 const auto *ContReg = Cont.getAsRegion();
270 if (!ContReg)
271 return;
273 ContReg = ContReg->getMostDerivedObjectRegion();
275 // If the container already has an end symbol then use it. Otherwise first
276 // create a new one.
277 auto State = C.getState();
278 auto EndSym = getContainerEnd(State, ContReg);
279 if (!EndSym) {
280 State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
281 C.getLocationContext(), C.blockCount());
282 EndSym = getContainerEnd(State, ContReg);
284 State = setIteratorPosition(State, RetVal,
285 IteratorPosition::getPosition(ContReg, EndSym));
286 C.addTransition(State);
289 void ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont,
290 const Expr *CE, SVal OldCont) const {
291 const auto *ContReg = Cont.getAsRegion();
292 if (!ContReg)
293 return;
295 ContReg = ContReg->getMostDerivedObjectRegion();
297 // Assignment of a new value to a container always invalidates all its
298 // iterators
299 auto State = C.getState();
300 const auto CData = getContainerData(State, ContReg);
301 if (CData) {
302 State = invalidateAllIteratorPositions(State, ContReg);
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 if (!OldCont.isUndef()) {
308 const auto *OldContReg = OldCont.getAsRegion();
309 if (OldContReg) {
310 OldContReg = OldContReg->getMostDerivedObjectRegion();
311 const auto OldCData = getContainerData(State, OldContReg);
312 if (OldCData) {
313 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 State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
319 OldEndSym, BO_GE);
320 auto &SymMgr = C.getSymbolManager();
321 auto &SVB = C.getSValBuilder();
322 // Then generate and assign a new "end" symbol for the new container.
323 auto NewEndSym =
324 SymMgr.conjureSymbol(CE, C.getLocationContext(),
325 C.getASTContext().LongTy, C.blockCount());
326 State = assumeNoOverflow(State, NewEndSym, 4);
327 if (CData) {
328 State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
329 } else {
330 State = setContainerData(State, ContReg,
331 ContainerData::fromEnd(NewEndSym));
333 // Finally, replace the old "end" symbol in the already reassigned
334 // iterator positions with the new "end" symbol.
335 State = rebaseSymbolInIteratorPositionsIf(
336 State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
337 } else {
338 // There was no "end" symbol assigned yet to the old container,
339 // so reassign all iterator positions to the new container.
340 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
342 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 if (CData) {
346 State =
347 setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
348 } else {
349 State = setContainerData(State, ContReg,
350 ContainerData::fromBegin(OldBeginSym));
352 State =
353 setContainerData(State, OldContReg, OldCData->newBegin(nullptr));
355 } 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 State = reassignAllIteratorPositions(State, OldContReg, ContReg);
362 C.addTransition(State);
365 void ContainerModeling::handleAssign(CheckerContext &C, SVal Cont,
366 const Expr *ContE) const {
367 const auto *ContReg = Cont.getAsRegion();
368 if (!ContReg)
369 return;
371 ContReg = ContReg->getMostDerivedObjectRegion();
373 // The assign() operation invalidates all the iterators
374 auto State = C.getState();
375 State = invalidateAllIteratorPositions(State, ContReg);
376 C.addTransition(State);
379 void ContainerModeling::handleClear(CheckerContext &C, SVal Cont,
380 const Expr *ContE) const {
381 const auto *ContReg = Cont.getAsRegion();
382 if (!ContReg)
383 return;
385 ContReg = ContReg->getMostDerivedObjectRegion();
387 // The clear() operation invalidates all the iterators, except the past-end
388 // iterators of list-like containers
389 auto State = C.getState();
390 if (!hasSubscriptOperator(State, ContReg) ||
391 !backModifiable(State, ContReg)) {
392 const auto CData = getContainerData(State, ContReg);
393 if (CData) {
394 if (const auto EndSym = CData->getEnd()) {
395 State =
396 invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
397 C.addTransition(State);
398 return;
402 const NoteTag *ChangeTag =
403 getChangeTag(C, "became empty", ContReg, ContE);
404 State = invalidateAllIteratorPositions(State, ContReg);
405 C.addTransition(State, ChangeTag);
408 void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont,
409 const Expr *ContE) const {
410 const auto *ContReg = Cont.getAsRegion();
411 if (!ContReg)
412 return;
414 ContReg = ContReg->getMostDerivedObjectRegion();
416 // For deque-like containers invalidate all iterator positions
417 auto State = C.getState();
418 if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
419 State = invalidateAllIteratorPositions(State, ContReg);
420 C.addTransition(State);
421 return;
424 const auto CData = getContainerData(State, ContReg);
425 if (!CData)
426 return;
428 // For vector-like containers invalidate the past-end iterator positions
429 if (const auto EndSym = CData->getEnd()) {
430 if (hasSubscriptOperator(State, ContReg)) {
431 State = invalidateIteratorPositions(State, EndSym, BO_GE);
433 auto &SymMgr = C.getSymbolManager();
434 auto &BVF = SymMgr.getBasicVals();
435 auto &SVB = C.getSValBuilder();
436 const auto newEndSym =
437 SVB.evalBinOp(State, BO_Add,
438 nonloc::SymbolVal(EndSym),
439 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
440 SymMgr.getType(EndSym)).getAsSymbol();
441 const NoteTag *ChangeTag =
442 getChangeTag(C, "extended to the back by 1 position", ContReg, ContE);
443 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
444 C.addTransition(State, ChangeTag);
448 void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont,
449 const Expr *ContE) const {
450 const auto *ContReg = Cont.getAsRegion();
451 if (!ContReg)
452 return;
454 ContReg = ContReg->getMostDerivedObjectRegion();
456 auto State = C.getState();
457 const auto CData = getContainerData(State, ContReg);
458 if (!CData)
459 return;
461 if (const auto EndSym = CData->getEnd()) {
462 auto &SymMgr = C.getSymbolManager();
463 auto &BVF = SymMgr.getBasicVals();
464 auto &SVB = C.getSValBuilder();
465 const auto BackSym =
466 SVB.evalBinOp(State, BO_Sub,
467 nonloc::SymbolVal(EndSym),
468 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
469 SymMgr.getType(EndSym)).getAsSymbol();
470 const NoteTag *ChangeTag =
471 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 if (hasSubscriptOperator(State, ContReg) &&
476 backModifiable(State, ContReg)) {
477 State = invalidateIteratorPositions(State, BackSym, BO_GE);
478 State = setContainerData(State, ContReg, CData->newEnd(nullptr));
479 } else {
480 State = invalidateIteratorPositions(State, BackSym, BO_EQ);
482 auto newEndSym = BackSym;
483 State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
484 C.addTransition(State, ChangeTag);
488 void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont,
489 const Expr *ContE) const {
490 const auto *ContReg = Cont.getAsRegion();
491 if (!ContReg)
492 return;
494 ContReg = ContReg->getMostDerivedObjectRegion();
496 // For deque-like containers invalidate all iterator positions
497 auto State = C.getState();
498 if (hasSubscriptOperator(State, ContReg)) {
499 State = invalidateAllIteratorPositions(State, ContReg);
500 C.addTransition(State);
501 } else {
502 const auto CData = getContainerData(State, ContReg);
503 if (!CData)
504 return;
506 if (const auto BeginSym = CData->getBegin()) {
507 auto &SymMgr = C.getSymbolManager();
508 auto &BVF = SymMgr.getBasicVals();
509 auto &SVB = C.getSValBuilder();
510 const auto newBeginSym =
511 SVB.evalBinOp(State, BO_Sub,
512 nonloc::SymbolVal(BeginSym),
513 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
514 SymMgr.getType(BeginSym)).getAsSymbol();
515 const NoteTag *ChangeTag =
516 getChangeTag(C, "extended to the front by 1 position", ContReg, ContE);
517 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
518 C.addTransition(State, ChangeTag);
523 void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont,
524 const Expr *ContE) const {
525 const auto *ContReg = Cont.getAsRegion();
526 if (!ContReg)
527 return;
529 ContReg = ContReg->getMostDerivedObjectRegion();
531 auto State = C.getState();
532 const auto CData = getContainerData(State, ContReg);
533 if (!CData)
534 return;
536 // For deque-like containers invalidate all iterator positions. For list-like
537 // iterators only invalidate the first position
538 if (const auto BeginSym = CData->getBegin()) {
539 if (hasSubscriptOperator(State, ContReg)) {
540 State = invalidateIteratorPositions(State, BeginSym, BO_LE);
541 } else {
542 State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
544 auto &SymMgr = C.getSymbolManager();
545 auto &BVF = SymMgr.getBasicVals();
546 auto &SVB = C.getSValBuilder();
547 const auto newBeginSym =
548 SVB.evalBinOp(State, BO_Add,
549 nonloc::SymbolVal(BeginSym),
550 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
551 SymMgr.getType(BeginSym)).getAsSymbol();
552 const NoteTag *ChangeTag =
553 getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE);
554 State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
555 C.addTransition(State, ChangeTag);
559 void ContainerModeling::handleInsert(CheckerContext &C, SVal Cont,
560 SVal Iter) const {
561 const auto *ContReg = Cont.getAsRegion();
562 if (!ContReg)
563 return;
565 ContReg = ContReg->getMostDerivedObjectRegion();
567 auto State = C.getState();
568 const auto *Pos = getIteratorPosition(State, Iter);
569 if (!Pos)
570 return;
572 // For deque-like containers invalidate all iterator positions. For
573 // vector-like containers invalidate iterator positions after the insertion.
574 if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
575 if (frontModifiable(State, ContReg)) {
576 State = invalidateAllIteratorPositions(State, ContReg);
577 } else {
578 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
580 if (const auto *CData = getContainerData(State, ContReg)) {
581 if (const auto EndSym = CData->getEnd()) {
582 State = invalidateIteratorPositions(State, EndSym, BO_GE);
583 State = setContainerData(State, ContReg, CData->newEnd(nullptr));
586 C.addTransition(State);
590 void ContainerModeling::handleErase(CheckerContext &C, SVal Cont,
591 SVal Iter) const {
592 const auto *ContReg = Cont.getAsRegion();
593 if (!ContReg)
594 return;
596 ContReg = ContReg->getMostDerivedObjectRegion();
598 auto State = C.getState();
599 const auto *Pos = getIteratorPosition(State, Iter);
600 if (!Pos)
601 return;
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 if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
607 if (frontModifiable(State, ContReg)) {
608 State = invalidateAllIteratorPositions(State, ContReg);
609 } else {
610 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
612 if (const auto *CData = getContainerData(State, ContReg)) {
613 if (const auto EndSym = CData->getEnd()) {
614 State = invalidateIteratorPositions(State, EndSym, BO_GE);
615 State = setContainerData(State, ContReg, CData->newEnd(nullptr));
618 } else {
619 State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
621 C.addTransition(State);
624 void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1,
625 SVal Iter2) const {
626 const auto *ContReg = Cont.getAsRegion();
627 if (!ContReg)
628 return;
630 ContReg = ContReg->getMostDerivedObjectRegion();
631 auto State = C.getState();
632 const auto *Pos1 = getIteratorPosition(State, Iter1);
633 const auto *Pos2 = getIteratorPosition(State, Iter2);
634 if (!Pos1 || !Pos2)
635 return;
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 if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
642 if (frontModifiable(State, ContReg)) {
643 State = invalidateAllIteratorPositions(State, ContReg);
644 } else {
645 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
647 if (const auto *CData = getContainerData(State, ContReg)) {
648 if (const auto EndSym = CData->getEnd()) {
649 State = invalidateIteratorPositions(State, EndSym, BO_GE);
650 State = setContainerData(State, ContReg, CData->newEnd(nullptr));
653 } else {
654 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
655 Pos2->getOffset(), BO_LT);
657 C.addTransition(State);
660 void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
661 SVal Iter) const {
662 auto State = C.getState();
663 const auto *Pos = getIteratorPosition(State, Iter);
664 if (!Pos)
665 return;
667 // Invalidate the deleted iterator position, which is the position of the
668 // parameter plus one.
669 auto &SymMgr = C.getSymbolManager();
670 auto &BVF = SymMgr.getBasicVals();
671 auto &SVB = C.getSValBuilder();
672 const auto NextSym =
673 SVB.evalBinOp(State, BO_Add,
674 nonloc::SymbolVal(Pos->getOffset()),
675 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
676 SymMgr.getType(Pos->getOffset())).getAsSymbol();
677 State = invalidateIteratorPositions(State, NextSym, BO_EQ);
678 C.addTransition(State);
681 void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
682 SVal Iter1, SVal Iter2) const {
683 auto State = C.getState();
684 const auto *Pos1 = getIteratorPosition(State, Iter1);
685 const auto *Pos2 = getIteratorPosition(State, Iter2);
686 if (!Pos1 || !Pos2)
687 return;
689 // Invalidate the deleted iterator position range (first..last)
690 State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
691 Pos2->getOffset(), BO_LT);
692 C.addTransition(State);
695 const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C,
696 StringRef Text,
697 const MemRegion *ContReg,
698 const Expr *ContE) const {
699 StringRef Name;
700 // First try to get the name of the variable from the region
701 if (const auto *DR = dyn_cast<DeclRegion>(ContReg)) {
702 Name = DR->getDecl()->getName();
703 // If the region is not a `DeclRegion` then use the expression instead
704 } else if (const auto *DRE =
705 dyn_cast<DeclRefExpr>(ContE->IgnoreParenCasts())) {
706 Name = DRE->getDecl()->getName();
709 return C.getNoteTag(
710 [Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string {
711 if (!BR.isInteresting(ContReg))
712 return "";
714 SmallString<256> Msg;
715 llvm::raw_svector_ostream Out(Msg);
716 Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : "" )
717 << Text;
718 return std::string(Out.str());
722 void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State,
723 const char *NL, const char *Sep) const {
724 auto ContMap = State->get<ContainerMap>();
726 if (!ContMap.isEmpty()) {
727 Out << Sep << "Container Data :" << NL;
728 for (const auto &Cont : ContMap) {
729 Cont.first->dumpToStream(Out);
730 Out << " : [ ";
731 const auto CData = Cont.second;
732 if (CData.getBegin())
733 CData.getBegin()->dumpToStream(Out);
734 else
735 Out << "<Unknown>";
736 Out << " .. ";
737 if (CData.getEnd())
738 CData.getEnd()->dumpToStream(Out);
739 else
740 Out << "<Unknown>";
741 Out << " ]";
746 namespace {
748 bool isBeginCall(const FunctionDecl *Func) {
749 const auto *IdInfo = Func->getIdentifier();
750 if (!IdInfo)
751 return false;
752 return IdInfo->getName().ends_with_insensitive("begin");
755 bool isEndCall(const FunctionDecl *Func) {
756 const auto *IdInfo = Func->getIdentifier();
757 if (!IdInfo)
758 return false;
759 return IdInfo->getName().ends_with_insensitive("end");
762 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
763 const MemRegion *Reg) {
764 auto TI = getDynamicTypeInfo(State, Reg);
765 if (!TI.isValid())
766 return nullptr;
768 auto Type = TI.getType();
769 if (const auto *RefT = Type->getAs<ReferenceType>()) {
770 Type = RefT->getPointeeType();
773 return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
776 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
777 const auto *CRD = getCXXRecordDecl(State, Reg);
778 if (!CRD)
779 return false;
781 for (const auto *Method : CRD->methods()) {
782 if (!Method->isOverloadedOperator())
783 continue;
784 const auto OPK = Method->getOverloadedOperator();
785 if (OPK == OO_Subscript) {
786 return true;
789 return false;
792 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
793 const auto *CRD = getCXXRecordDecl(State, Reg);
794 if (!CRD)
795 return false;
797 for (const auto *Method : CRD->methods()) {
798 if (!Method->getDeclName().isIdentifier())
799 continue;
800 if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
801 return true;
804 return false;
807 bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
808 const auto *CRD = getCXXRecordDecl(State, Reg);
809 if (!CRD)
810 return false;
812 for (const auto *Method : CRD->methods()) {
813 if (!Method->getDeclName().isIdentifier())
814 continue;
815 if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
816 return true;
819 return false;
822 SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
823 const auto *CDataPtr = getContainerData(State, Cont);
824 if (!CDataPtr)
825 return nullptr;
827 return CDataPtr->getBegin();
830 SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
831 const auto *CDataPtr = getContainerData(State, Cont);
832 if (!CDataPtr)
833 return nullptr;
835 return CDataPtr->getEnd();
838 ProgramStateRef createContainerBegin(ProgramStateRef State,
839 const MemRegion *Cont, const Expr *E,
840 QualType T, const LocationContext *LCtx,
841 unsigned BlockCount) {
842 // Only create if it does not exist
843 const auto *CDataPtr = getContainerData(State, Cont);
844 if (CDataPtr && CDataPtr->getBegin())
845 return State;
847 auto &SymMgr = State->getSymbolManager();
848 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
849 "begin");
850 State = assumeNoOverflow(State, Sym, 4);
852 if (CDataPtr) {
853 const auto CData = CDataPtr->newBegin(Sym);
854 return setContainerData(State, Cont, CData);
857 const auto CData = ContainerData::fromBegin(Sym);
858 return setContainerData(State, Cont, CData);
861 ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
862 const Expr *E, QualType T,
863 const LocationContext *LCtx,
864 unsigned BlockCount) {
865 // Only create if it does not exist
866 const auto *CDataPtr = getContainerData(State, Cont);
867 if (CDataPtr && CDataPtr->getEnd())
868 return State;
870 auto &SymMgr = State->getSymbolManager();
871 const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
872 "end");
873 State = assumeNoOverflow(State, Sym, 4);
875 if (CDataPtr) {
876 const auto CData = CDataPtr->newEnd(Sym);
877 return setContainerData(State, Cont, CData);
880 const auto CData = ContainerData::fromEnd(Sym);
881 return setContainerData(State, Cont, CData);
884 ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
885 const ContainerData &CData) {
886 return State->set<ContainerMap>(Cont, CData);
889 template <typename Condition, typename Process>
890 ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
891 Process Proc) {
892 auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
893 auto RegionMap = State->get<IteratorRegionMap>();
894 bool Changed = false;
895 for (const auto &Reg : RegionMap) {
896 if (Cond(Reg.second)) {
897 RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
898 Changed = true;
902 if (Changed)
903 State = State->set<IteratorRegionMap>(RegionMap);
905 auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
906 auto SymbolMap = State->get<IteratorSymbolMap>();
907 Changed = false;
908 for (const auto &Sym : SymbolMap) {
909 if (Cond(Sym.second)) {
910 SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
911 Changed = true;
915 if (Changed)
916 State = State->set<IteratorSymbolMap>(SymbolMap);
918 return State;
921 ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
922 const MemRegion *Cont) {
923 auto MatchCont = [&](const IteratorPosition &Pos) {
924 return Pos.getContainer() == Cont;
926 auto Invalidate = [&](const IteratorPosition &Pos) {
927 return Pos.invalidate();
929 return processIteratorPositions(State, MatchCont, Invalidate);
932 ProgramStateRef
933 invalidateAllIteratorPositionsExcept(ProgramStateRef State,
934 const MemRegion *Cont, SymbolRef Offset,
935 BinaryOperator::Opcode Opc) {
936 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
937 return Pos.getContainer() == Cont &&
938 !compare(State, Pos.getOffset(), Offset, Opc);
940 auto Invalidate = [&](const IteratorPosition &Pos) {
941 return Pos.invalidate();
943 return processIteratorPositions(State, MatchContAndCompare, Invalidate);
946 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
947 SymbolRef Offset,
948 BinaryOperator::Opcode Opc) {
949 auto Compare = [&](const IteratorPosition &Pos) {
950 return compare(State, Pos.getOffset(), Offset, Opc);
952 auto Invalidate = [&](const IteratorPosition &Pos) {
953 return Pos.invalidate();
955 return processIteratorPositions(State, Compare, Invalidate);
958 ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
959 SymbolRef Offset1,
960 BinaryOperator::Opcode Opc1,
961 SymbolRef Offset2,
962 BinaryOperator::Opcode Opc2) {
963 auto Compare = [&](const IteratorPosition &Pos) {
964 return compare(State, Pos.getOffset(), Offset1, Opc1) &&
965 compare(State, Pos.getOffset(), Offset2, Opc2);
967 auto Invalidate = [&](const IteratorPosition &Pos) {
968 return Pos.invalidate();
970 return processIteratorPositions(State, Compare, Invalidate);
973 ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
974 const MemRegion *Cont,
975 const MemRegion *NewCont) {
976 auto MatchCont = [&](const IteratorPosition &Pos) {
977 return Pos.getContainer() == Cont;
979 auto ReAssign = [&](const IteratorPosition &Pos) {
980 return Pos.reAssign(NewCont);
982 return processIteratorPositions(State, MatchCont, ReAssign);
985 ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
986 const MemRegion *Cont,
987 const MemRegion *NewCont,
988 SymbolRef Offset,
989 BinaryOperator::Opcode Opc) {
990 auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
991 return Pos.getContainer() == Cont &&
992 !compare(State, Pos.getOffset(), Offset, Opc);
994 auto ReAssign = [&](const IteratorPosition &Pos) {
995 return Pos.reAssign(NewCont);
997 return processIteratorPositions(State, MatchContAndCompare, ReAssign);
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 SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
1006 auto LessThanEnd = [&](const IteratorPosition &Pos) {
1007 return compare(State, Pos.getOffset(), CondSym, Opc);
1009 auto RebaseSymbol = [&](const IteratorPosition &Pos) {
1010 return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
1011 NewSym));
1013 return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
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 SymbolRef NewSym) {
1022 auto &SymMgr = SVB.getSymbolManager();
1023 auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
1024 nonloc::SymbolVal(OldExpr),
1025 SymMgr.getType(OrigExpr));
1027 const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
1028 if (!DiffInt)
1029 return OrigExpr;
1031 return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
1032 SymMgr.getType(OrigExpr)).getAsSymbol();
1035 bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
1036 auto RegionMap = State->get<IteratorRegionMap>();
1037 for (const auto &Reg : RegionMap) {
1038 if (Reg.second.getContainer() == Cont)
1039 return true;
1042 auto SymbolMap = State->get<IteratorSymbolMap>();
1043 for (const auto &Sym : SymbolMap) {
1044 if (Sym.second.getContainer() == Cont)
1045 return true;
1048 return false;
1051 } // namespace
1053 void ento::registerContainerModeling(CheckerManager &mgr) {
1054 mgr.registerChecker<ContainerModeling>();
1057 bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) {
1058 if (!mgr.getLangOpts().CPlusPlus)
1059 return false;
1061 if (!mgr.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) {
1062 mgr.getASTContext().getDiagnostics().Report(
1063 diag::err_analyzer_checker_incompatible_analyzer_option)
1064 << "aggressive-binary-operation-simplification" << "false";
1065 return false;
1068 return true;