1 //===-- IteratorPastEndChecker.cpp --------------------------------*- C++ -*--//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Defines a checker for using iterators outside their range (past end). Usage
11 // means here dereferencing, incrementing etc.
13 //===----------------------------------------------------------------------===//
15 #include "ClangSACheckers.h"
16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17 #include "clang/StaticAnalyzer/Core/Checker.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23 using namespace clang;
27 struct IteratorPosition {
29 enum Kind { InRange, OutofRange } K;
30 IteratorPosition(Kind InK) : K(InK) {}
33 bool isInRange() const { return K == InRange; }
34 bool isOutofRange() const { return K == OutofRange; }
36 static IteratorPosition getInRange() { return IteratorPosition(InRange); }
37 static IteratorPosition getOutofRange() {
38 return IteratorPosition(OutofRange);
41 bool operator==(const IteratorPosition &X) const { return K == X.K; }
42 bool operator!=(const IteratorPosition &X) const { return K != X.K; }
43 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
46 typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
48 struct IteratorComparison {
50 RegionOrSymbol Left, Right;
54 IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
55 : Left(L), Right(R), Equality(Eq) {}
57 RegionOrSymbol getLeft() const { return Left; }
58 RegionOrSymbol getRight() const { return Right; }
59 bool isEquality() const { return Equality; }
60 bool operator==(const IteratorComparison &X) const {
61 return Left == X.Left && Right == X.Right && Equality == X.Equality;
63 bool operator!=(const IteratorComparison &X) const {
64 return Left != X.Left || Right != X.Right || Equality != X.Equality;
66 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
69 class IteratorPastEndChecker
71 check::PreCall, check::PostCall, check::PreStmt<CXXOperatorCallExpr>,
72 check::PostStmt<CXXConstructExpr>, check::PostStmt<DeclStmt>,
73 check::PostStmt<MaterializeTemporaryExpr>, check::BeginFunction,
74 check::DeadSymbols, eval::Assume, eval::Call> {
75 mutable IdentifierInfo *II_find = nullptr,
76 *II_find_end = nullptr, *II_find_first_of = nullptr,
77 *II_find_if = nullptr, *II_find_if_not = nullptr,
78 *II_lower_bound = nullptr, *II_upper_bound = nullptr,
79 *II_search = nullptr, *II_search_n = nullptr;
81 std::unique_ptr<BugType> PastEndBugType;
83 void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
84 const SVal &RVal, OverloadedOperatorKind Op) const;
85 void handleAccess(CheckerContext &C, const SVal &Val) const;
86 void handleDecrement(CheckerContext &C, const SVal &Val) const;
87 void handleEnd(CheckerContext &C, const SVal &RetVal) const;
89 bool evalFind(CheckerContext &C, const CallExpr *CE) const;
90 bool evalFindEnd(CheckerContext &C, const CallExpr *CE) const;
91 bool evalFindFirstOf(CheckerContext &C, const CallExpr *CE) const;
92 bool evalFindIf(CheckerContext &C, const CallExpr *CE) const;
93 bool evalFindIfNot(CheckerContext &C, const CallExpr *CE) const;
94 bool evalLowerBound(CheckerContext &C, const CallExpr *CE) const;
95 bool evalUpperBound(CheckerContext &C, const CallExpr *CE) const;
96 bool evalSearch(CheckerContext &C, const CallExpr *CE) const;
97 bool evalSearchN(CheckerContext &C, const CallExpr *CE) const;
98 void Find(CheckerContext &C, const CallExpr *CE) const;
100 void reportPastEndBug(const StringRef &Message, const SVal &Val,
101 CheckerContext &C, ExplodedNode *ErrNode) const;
102 void initIdentifiers(ASTContext &Ctx) const;
105 IteratorPastEndChecker();
107 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
108 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
109 void checkPreStmt(const CXXOperatorCallExpr *COCE, CheckerContext &C) const;
110 void checkBeginFunction(CheckerContext &C) const;
111 void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
112 void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
113 void checkPostStmt(const MaterializeTemporaryExpr *MTE,
114 CheckerContext &C) const;
115 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
116 ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
117 bool Assumption) const;
118 bool evalCall(const CallExpr *CE, CheckerContext &C) const;
122 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
123 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
126 REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
129 #define INIT_ID(Id) \
131 II_##Id = &Ctx.Idents.get(#Id)
135 bool isIteratorType(const QualType &Type);
136 bool isIterator(const CXXRecordDecl *CRD);
137 bool isEndCall(const FunctionDecl *Func);
138 bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
139 bool isAccessOperator(OverloadedOperatorKind OK);
140 bool isDecrementOperator(OverloadedOperatorKind OK);
141 BinaryOperator::Opcode getOpcode(const SymExpr *SE);
142 const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
143 const ProgramStateRef processComparison(ProgramStateRef State,
145 RegionOrSymbol RVal, bool Equal);
146 const ProgramStateRef saveComparison(ProgramStateRef State,
147 const SymExpr *Condition, const SVal &LVal,
148 const SVal &RVal, bool Eq);
149 const IteratorComparison *loadComparison(ProgramStateRef State,
150 const SymExpr *Condition);
151 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
153 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
154 RegionOrSymbol RegOrSym);
155 ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
156 IteratorPosition Pos);
157 ProgramStateRef setIteratorPosition(ProgramStateRef State,
158 RegionOrSymbol RegOrSym,
159 IteratorPosition Pos);
160 ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
161 RegionOrSymbol RegOrSym,
162 IteratorPosition Pos, bool Equal);
163 bool contradictingIteratorPositions(IteratorPosition Pos1,
164 IteratorPosition Pos2, bool Equal);
167 IteratorPastEndChecker::IteratorPastEndChecker() {
168 PastEndBugType.reset(
169 new BugType(this, "Iterator Past End", "Misuse of STL APIs"));
170 PastEndBugType->setSuppressOnSink(true);
173 void IteratorPastEndChecker::checkPreCall(const CallEvent &Call,
174 CheckerContext &C) const {
175 // Check for access past end
176 const auto *Func = Call.getDecl()->getAsFunction();
179 if (Func->isOverloadedOperator()) {
180 if (isAccessOperator(Func->getOverloadedOperator())) {
181 if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
182 handleAccess(C, InstCall->getCXXThisVal());
184 handleAccess(C, Call.getArgSVal(0));
190 void IteratorPastEndChecker::checkPostCall(const CallEvent &Call,
191 CheckerContext &C) const {
192 // Record end() iterators, iterator decrementation and comparison
193 const auto *Func = Call.getDecl()->getAsFunction();
196 if (Func->isOverloadedOperator()) {
197 const auto Op = Func->getOverloadedOperator();
198 if (isSimpleComparisonOperator(Op)) {
199 if (Func->isCXXInstanceMember()) {
200 const auto &InstCall = static_cast<const CXXInstanceCall &>(Call);
201 handleComparison(C, InstCall.getReturnValue(), InstCall.getCXXThisVal(),
202 InstCall.getArgSVal(0), Op);
204 handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
205 Call.getArgSVal(1), Op);
207 } else if (isDecrementOperator(Func->getOverloadedOperator())) {
208 if (Func->isCXXInstanceMember()) {
209 const auto &InstCall = static_cast<const CXXInstanceCall &>(Call);
210 handleDecrement(C, InstCall.getCXXThisVal());
212 handleDecrement(C, Call.getArgSVal(0));
215 } else if (Func->isCXXInstanceMember()) {
216 if (!isEndCall(Func))
218 if (!isIteratorType(Call.getResultType()))
220 handleEnd(C, Call.getReturnValue());
224 void IteratorPastEndChecker::checkPreStmt(const CXXOperatorCallExpr *COCE,
225 CheckerContext &C) const {
226 const auto *ThisExpr = COCE->getArg(0);
228 auto State = C.getState();
229 const auto *LCtx = C.getPredecessor()->getLocationContext();
231 const auto CurrentThis = State->getSVal(ThisExpr, LCtx);
232 if (const auto *Reg = CurrentThis.getAsRegion()) {
233 if (!Reg->getAs<CXXTempObjectRegion>())
235 const auto OldState = C.getPredecessor()->getFirstPred()->getState();
236 const auto OldThis = OldState->getSVal(ThisExpr, LCtx);
237 const auto *Pos = getIteratorPosition(OldState, OldThis);
240 State = setIteratorPosition(State, CurrentThis, *Pos);
241 C.addTransition(State);
245 void IteratorPastEndChecker::checkBeginFunction(CheckerContext &C) const {
246 // Copy state of iterator arguments to iterator parameters
247 auto State = C.getState();
248 const auto *LCtx = C.getLocationContext();
250 const auto *Site = cast<StackFrameContext>(LCtx)->getCallSite();
254 const auto *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
258 const auto *CE = dyn_cast<CallExpr>(Site);
264 for (const auto P : FD->parameters()) {
265 auto Param = State->getLValue(P, LCtx);
266 auto Arg = State->getSVal(CE->getArg(idx++), LCtx->getParent());
267 const auto *Pos = getIteratorPosition(State, Arg);
270 State = setIteratorPosition(State, Param, *Pos);
274 C.addTransition(State);
278 void IteratorPastEndChecker::checkPostStmt(const CXXConstructExpr *CCE,
279 CheckerContext &C) const {
280 // Transfer iterator state in case of copy or move by constructor
281 const auto *ctr = CCE->getConstructor();
282 if (!ctr->isCopyOrMoveConstructor())
284 const auto *RHSExpr = CCE->getArg(0);
286 auto State = C.getState();
287 const auto *LCtx = C.getLocationContext();
289 const auto RetVal = State->getSVal(CCE, LCtx);
291 const auto RHSVal = State->getSVal(RHSExpr, LCtx);
292 const auto *RHSPos = getIteratorPosition(State, RHSVal);
295 State = setIteratorPosition(State, RetVal, *RHSPos);
296 C.addTransition(State);
299 void IteratorPastEndChecker::checkPostStmt(const DeclStmt *DS,
300 CheckerContext &C) const {
301 // Transfer iterator state to new variable declaration
302 for (const auto *D : DS->decls()) {
303 const auto *VD = dyn_cast<VarDecl>(D);
304 if (!VD || !VD->hasInit())
307 auto State = C.getState();
308 const auto *LCtx = C.getPredecessor()->getLocationContext();
310 getIteratorPosition(State, State->getSVal(VD->getInit(), LCtx));
313 State = setIteratorPosition(State, State->getLValue(VD, LCtx), *Pos);
314 C.addTransition(State);
318 void IteratorPastEndChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
319 CheckerContext &C) const {
320 /* Transfer iterator state for to temporary objects */
321 auto State = C.getState();
322 const auto *LCtx = C.getPredecessor()->getLocationContext();
324 getIteratorPosition(State, State->getSVal(MTE->GetTemporaryExpr(), LCtx));
327 State = setIteratorPosition(State, State->getSVal(MTE, LCtx), *Pos);
328 C.addTransition(State);
331 void IteratorPastEndChecker::checkDeadSymbols(SymbolReaper &SR,
332 CheckerContext &C) const {
333 auto State = C.getState();
335 auto RegionMap = State->get<IteratorRegionMap>();
336 for (const auto Reg : RegionMap) {
337 if (!SR.isLiveRegion(Reg.first)) {
338 State = State->remove<IteratorRegionMap>(Reg.first);
342 auto SymbolMap = State->get<IteratorSymbolMap>();
343 for (const auto Sym : SymbolMap) {
344 if (SR.isDead(Sym.first)) {
345 State = State->remove<IteratorSymbolMap>(Sym.first);
349 auto ComparisonMap = State->get<IteratorComparisonMap>();
350 for (const auto Comp : ComparisonMap) {
351 if (SR.isDead(Comp.first)) {
352 State = State->remove<IteratorComparisonMap>(Comp.first);
357 ProgramStateRef IteratorPastEndChecker::evalAssume(ProgramStateRef State,
359 bool Assumption) const {
360 // Load recorded comparison and transfer iterator state between sides
361 // according to comparison operator and assumption
362 const auto *SE = Cond.getAsSymExpr();
366 auto Opc = getOpcode(SE);
367 if (Opc != BO_EQ && Opc != BO_NE)
370 bool Negated = false;
371 const auto *Comp = loadComparison(State, SE);
373 // Try negated comparison, which is a SymExpr to 0 integer comparison
374 const auto *SIE = dyn_cast<SymIntExpr>(SE);
378 if (SIE->getRHS() != 0)
382 Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
384 if (Opc != BO_EQ && Opc != BO_NE)
387 Comp = loadComparison(State, SE);
392 return processComparison(State, Comp->getLeft(), Comp->getRight(),
393 (Comp->isEquality() == Assumption) != Negated);
396 // FIXME: Evaluation of these STL calls should be moved to StdCLibraryFunctions
397 // checker (see patch r284960) or another similar checker for C++ STL
398 // functions (e.g. StdCXXLibraryFunctions or StdCppLibraryFunctions).
399 bool IteratorPastEndChecker::evalCall(const CallExpr *CE,
400 CheckerContext &C) const {
401 const FunctionDecl *FD = C.getCalleeDecl(CE);
405 ASTContext &Ctx = C.getASTContext();
406 initIdentifiers(Ctx);
408 if (FD->getKind() == Decl::Function) {
409 if (FD->isInStdNamespace()) {
410 if (FD->getIdentifier() == II_find) {
411 return evalFind(C, CE);
412 } else if (FD->getIdentifier() == II_find_end) {
413 return evalFindEnd(C, CE);
414 } else if (FD->getIdentifier() == II_find_first_of) {
415 return evalFindFirstOf(C, CE);
416 } else if (FD->getIdentifier() == II_find_if) {
417 return evalFindIf(C, CE);
418 } else if (FD->getIdentifier() == II_find_if) {
419 return evalFindIf(C, CE);
420 } else if (FD->getIdentifier() == II_find_if_not) {
421 return evalFindIfNot(C, CE);
422 } else if (FD->getIdentifier() == II_upper_bound) {
423 return evalUpperBound(C, CE);
424 } else if (FD->getIdentifier() == II_lower_bound) {
425 return evalLowerBound(C, CE);
426 } else if (FD->getIdentifier() == II_search) {
427 return evalSearch(C, CE);
428 } else if (FD->getIdentifier() == II_search_n) {
429 return evalSearchN(C, CE);
437 void IteratorPastEndChecker::handleComparison(CheckerContext &C,
441 OverloadedOperatorKind Op) const {
442 // Record the operands and the operator of the comparison for the next
443 // evalAssume, if the result is a symbolic expression. If it is a concrete
444 // value (only one branch is possible), then transfer the state between
445 // the operands according to the operator and the result
446 auto State = C.getState();
447 if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
448 const auto *LPos = getIteratorPosition(State, LVal);
449 const auto *RPos = getIteratorPosition(State, RVal);
452 State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
453 C.addTransition(State);
454 } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
455 if ((State = processComparison(
456 State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
457 (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
458 C.addTransition(State);
460 C.generateSink(State, C.getPredecessor());
465 void IteratorPastEndChecker::handleAccess(CheckerContext &C,
466 const SVal &Val) const {
467 auto State = C.getState();
468 const auto *Pos = getIteratorPosition(State, Val);
469 if (Pos && Pos->isOutofRange()) {
470 auto *N = C.generateNonFatalErrorNode(State);
474 reportPastEndBug("Iterator accessed past its end.", Val, C, N);
478 void IteratorPastEndChecker::handleDecrement(CheckerContext &C,
479 const SVal &Val) const {
480 auto State = C.getState();
481 const auto *Pos = getIteratorPosition(State, Val);
482 if (Pos && Pos->isOutofRange()) {
483 State = setIteratorPosition(State, Val, IteratorPosition::getInRange());
484 // FIXME: We could also check for iterators ahead of their beginnig in the
485 // future, but currently we do not care for such errors. We also
486 // assume that the iterator is not past its end by more then one
488 C.addTransition(State);
492 void IteratorPastEndChecker::handleEnd(CheckerContext &C,
493 const SVal &RetVal) const {
494 auto State = C.getState();
495 State = setIteratorPosition(State, RetVal, IteratorPosition::getOutofRange());
496 C.addTransition(State);
499 bool IteratorPastEndChecker::evalFind(CheckerContext &C,
500 const CallExpr *CE) const {
501 if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
502 isIteratorType(CE->getArg(1)->getType())) {
509 bool IteratorPastEndChecker::evalFindEnd(CheckerContext &C,
510 const CallExpr *CE) const {
511 if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
512 isIteratorType(CE->getArg(0)->getType()) &&
513 isIteratorType(CE->getArg(1)->getType()) &&
514 isIteratorType(CE->getArg(2)->getType()) &&
515 isIteratorType(CE->getArg(3)->getType())) {
522 bool IteratorPastEndChecker::evalFindFirstOf(CheckerContext &C,
523 const CallExpr *CE) const {
524 if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
525 isIteratorType(CE->getArg(0)->getType()) &&
526 isIteratorType(CE->getArg(1)->getType()) &&
527 isIteratorType(CE->getArg(2)->getType()) &&
528 isIteratorType(CE->getArg(3)->getType())) {
535 bool IteratorPastEndChecker::evalFindIf(CheckerContext &C,
536 const CallExpr *CE) const {
537 if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
538 isIteratorType(CE->getArg(1)->getType())) {
545 bool IteratorPastEndChecker::evalFindIfNot(CheckerContext &C,
546 const CallExpr *CE) const {
547 if (CE->getNumArgs() == 3 && isIteratorType(CE->getArg(0)->getType()) &&
548 isIteratorType(CE->getArg(1)->getType())) {
555 bool IteratorPastEndChecker::evalLowerBound(CheckerContext &C,
556 const CallExpr *CE) const {
557 if ((CE->getNumArgs() == 3 || CE->getNumArgs() == 4) &&
558 isIteratorType(CE->getArg(0)->getType()) &&
559 isIteratorType(CE->getArg(1)->getType())) {
566 bool IteratorPastEndChecker::evalUpperBound(CheckerContext &C,
567 const CallExpr *CE) const {
568 if ((CE->getNumArgs() == 3 || CE->getNumArgs() == 4) &&
569 isIteratorType(CE->getArg(0)->getType()) &&
570 isIteratorType(CE->getArg(1)->getType())) {
577 bool IteratorPastEndChecker::evalSearch(CheckerContext &C,
578 const CallExpr *CE) const {
579 if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
580 isIteratorType(CE->getArg(0)->getType()) &&
581 isIteratorType(CE->getArg(1)->getType()) &&
582 isIteratorType(CE->getArg(2)->getType()) &&
583 isIteratorType(CE->getArg(3)->getType())) {
590 bool IteratorPastEndChecker::evalSearchN(CheckerContext &C,
591 const CallExpr *CE) const {
592 if ((CE->getNumArgs() == 4 || CE->getNumArgs() == 5) &&
593 isIteratorType(CE->getArg(0)->getType()) &&
594 isIteratorType(CE->getArg(1)->getType())) {
601 void IteratorPastEndChecker::Find(CheckerContext &C, const CallExpr *CE) const {
602 auto state = C.getState();
603 auto &svalBuilder = C.getSValBuilder();
604 const auto *LCtx = C.getLocationContext();
606 auto RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
607 auto SecondParam = state->getSVal(CE->getArg(1), LCtx);
609 auto stateFound = state->BindExpr(CE, LCtx, RetVal);
610 auto stateNotFound = state->BindExpr(CE, LCtx, SecondParam);
612 C.addTransition(stateFound);
613 C.addTransition(stateNotFound);
616 void IteratorPastEndChecker::reportPastEndBug(const StringRef &Message,
619 ExplodedNode *ErrNode) const {
620 auto R = llvm::make_unique<BugReport>(*PastEndBugType, Message, ErrNode);
621 R->markInteresting(Val);
622 C.emitReport(std::move(R));
625 void IteratorPastEndChecker::initIdentifiers(ASTContext &Ctx) const {
628 INIT_ID(find_first_of);
630 INIT_ID(find_if_not);
631 INIT_ID(lower_bound);
632 INIT_ID(upper_bound);
639 bool isIteratorType(const QualType &Type) {
640 if (Type->isPointerType())
643 const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
644 return isIterator(CRD);
647 bool isIterator(const CXXRecordDecl *CRD) {
651 const auto Name = CRD->getName();
652 if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
653 Name.endswith_lower("it")))
656 bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
657 HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
658 for (const auto *Method : CRD->methods()) {
659 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
660 if (Ctor->isCopyConstructor()) {
661 HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
665 if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
666 HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
669 if (Method->isCopyAssignmentOperator()) {
670 HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
673 if (!Method->isOverloadedOperator())
675 const auto OPK = Method->getOverloadedOperator();
676 if (OPK == OO_PlusPlus) {
677 HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
678 HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
681 if (OPK == OO_Star) {
682 HasDerefOp = (Method->getNumParams() == 0);
687 return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
688 HasPostIncrOp && HasDerefOp;
691 bool isEndCall(const FunctionDecl *Func) {
692 const auto *IdInfo = Func->getIdentifier();
695 return IdInfo->getName().endswith_lower("end");
698 bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
699 return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
702 bool isAccessOperator(OverloadedOperatorKind OK) {
703 return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
704 OK == OO_Plus || OK == OO_PlusEqual || OK == OO_PlusPlus ||
708 bool isDecrementOperator(OverloadedOperatorKind OK) {
709 return OK == OO_MinusEqual || OK == OO_MinusMinus;
712 BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
713 if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
714 return BSE->getOpcode();
715 } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
716 const auto *COE = dyn_cast<CXXOperatorCallExpr>(SC->getStmt());
718 return BO_Comma; // Extremal value, neither EQ nor NE
719 if (COE->getOperator() == OO_EqualEqual) {
721 } else if (COE->getOperator() == OO_ExclaimEqual) {
724 return BO_Comma; // Extremal value, neither EQ nor NE
726 return BO_Comma; // Extremal value, neither EQ nor NE
729 const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
730 if (const auto Reg = Val.getAsRegion()) {
732 } else if (const auto Sym = Val.getAsSymbol()) {
734 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
735 return LCVal->getRegion();
737 return RegionOrSymbol();
740 const ProgramStateRef processComparison(ProgramStateRef State,
742 RegionOrSymbol RVal, bool Equal) {
743 const auto *LPos = getIteratorPosition(State, LVal);
744 const auto *RPos = getIteratorPosition(State, RVal);
746 State = adjustIteratorPosition(State, RVal, *LPos, Equal);
747 } else if (!LPos && RPos) {
748 State = adjustIteratorPosition(State, LVal, *RPos, Equal);
749 } else if (LPos && RPos) {
750 if (contradictingIteratorPositions(*LPos, *RPos, Equal)) {
757 const ProgramStateRef saveComparison(ProgramStateRef State,
758 const SymExpr *Condition, const SVal &LVal,
759 const SVal &RVal, bool Eq) {
760 const auto Left = getRegionOrSymbol(LVal);
761 const auto Right = getRegionOrSymbol(RVal);
764 return State->set<IteratorComparisonMap>(Condition,
765 IteratorComparison(Left, Right, Eq));
768 const IteratorComparison *loadComparison(ProgramStateRef State,
769 const SymExpr *Condition) {
770 return State->get<IteratorComparisonMap>(Condition);
773 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
775 if (const auto Reg = Val.getAsRegion()) {
776 return State->get<IteratorRegionMap>(Reg);
777 } else if (const auto Sym = Val.getAsSymbol()) {
778 return State->get<IteratorSymbolMap>(Sym);
779 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
780 return State->get<IteratorRegionMap>(LCVal->getRegion());
785 const IteratorPosition *getIteratorPosition(ProgramStateRef State,
786 RegionOrSymbol RegOrSym) {
787 if (RegOrSym.is<const MemRegion *>()) {
788 return State->get<IteratorRegionMap>(RegOrSym.get<const MemRegion *>());
789 } else if (RegOrSym.is<SymbolRef>()) {
790 return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
795 ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
796 IteratorPosition Pos) {
797 if (const auto Reg = Val.getAsRegion()) {
798 return State->set<IteratorRegionMap>(Reg, Pos);
799 } else if (const auto Sym = Val.getAsSymbol()) {
800 return State->set<IteratorSymbolMap>(Sym, Pos);
801 } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
802 return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
807 ProgramStateRef setIteratorPosition(ProgramStateRef State,
808 RegionOrSymbol RegOrSym,
809 IteratorPosition Pos) {
810 if (RegOrSym.is<const MemRegion *>()) {
811 return State->set<IteratorRegionMap>(RegOrSym.get<const MemRegion *>(),
813 } else if (RegOrSym.is<SymbolRef>()) {
814 return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
819 ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
820 RegionOrSymbol RegOrSym,
821 IteratorPosition Pos, bool Equal) {
823 if ((Pos.isInRange() && Equal) || (Pos.isOutofRange() && !Equal)) {
824 return setIteratorPosition(State, RegOrSym, IteratorPosition::getInRange());
825 } else if (Pos.isOutofRange() && Equal) {
826 return setIteratorPosition(State, RegOrSym,
827 IteratorPosition::getOutofRange());
833 bool contradictingIteratorPositions(IteratorPosition Pos1,
834 IteratorPosition Pos2, bool Equal) {
835 return ((Pos1 != Pos2) && Equal) ||
836 ((Pos1.isOutofRange() && Pos2.isOutofRange()) && !Equal);
840 void ento::registerIteratorPastEndChecker(CheckerManager &Mgr) {
841 Mgr.registerChecker<IteratorPastEndChecker>();