Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -615,6 +615,14 @@ def ContainerModeling : Checker<"ContainerModeling">, HelpText<"Models C++ containers">, + CheckerOptions<[ + CmdLineOption + ]>, Documentation, Hidden; Index: clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp +++ clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp @@ -65,6 +65,8 @@ public: ContainerModeling() = default; + DefaultBool AggressiveEraseModeling; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; @@ -606,7 +608,7 @@ auto RetPos = IteratorPosition::getPosition(Pos->getContainer(), Pos->getOffset()); State = setIteratorPosition(State, RetVal, RetPos); - + C.addTransition(State); } @@ -632,7 +634,7 @@ SymMgr.getType(Pos->getOffset())).getAsSymbol(); auto RetPos = IteratorPosition::getPosition(Pos->getContainer(), RetOffset); State = setIteratorPosition(State, RetVal, RetPos); - + C.addTransition(State); } @@ -677,7 +679,29 @@ SymMgr.getType(Pos->getOffset())).getAsSymbol(); auto RetPos = IteratorPosition::getPosition(Pos->getContainer(), RetOffset); State = setIteratorPosition(State, RetVal, RetPos); - + + if (AggressiveEraseModeling) { + if (const auto *CData = getContainerData(State, ContReg)) { + if (const SymbolRef EndSym = CData->getEnd()) { + const auto RetEnd = + SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(RetOffset), + nonloc::SymbolVal(EndSym), SVB.getConditionType()) + .getAs(); + if (RetEnd) { + ProgramStateRef StateEnd, StateNotEnd; + std::tie(StateEnd, StateNotEnd) = State->assume(*RetEnd); + if (StateEnd) { + C.addTransition(StateEnd); + } + if (StateNotEnd) { + C.addTransition(StateNotEnd); + } + return; + } + } + } + } + C.addTransition(State); } @@ -725,6 +749,11 @@ void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter, SVal RetVal) const { + const auto *ContReg = Cont.getAsRegion(); + if (!ContReg) + return; + + ContReg = ContReg->getMostDerivedObjectRegion(); auto State = C.getState(); const auto *Pos = getIteratorPosition(State, Iter); if (!Pos) @@ -748,6 +777,28 @@ auto RetPos = IteratorPosition::getPosition(Pos->getContainer(), RetOffset); State = setIteratorPosition(State, RetVal, RetPos); + if (AggressiveEraseModeling) { + if (const auto *CData = getContainerData(State, ContReg)) { + if (const SymbolRef EndSym = CData->getEnd()) { + const auto RetEnd = + SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(RetOffset), + nonloc::SymbolVal(EndSym), SVB.getConditionType()) + .getAs(); + if (RetEnd) { + ProgramStateRef StateEnd, StateNotEnd; + std::tie(StateEnd, StateNotEnd) = State->assume(*RetEnd); + if (StateEnd) { + C.addTransition(StateEnd); + } + if (StateNotEnd) { + C.addTransition(StateNotEnd); + } + return; + } + } + } + } + C.addTransition(State); } @@ -1131,7 +1182,11 @@ } // namespace void ento::registerContainerModeling(CheckerManager &mgr) { - mgr.registerChecker(); + auto *Checker = mgr.registerChecker(); + Checker->AggressiveEraseModeling = + mgr.getAnalyzerOptions().getCheckerBooleanOption( + "alpha.cplusplus.ContainerModeling", "AggressiveEraseModeling"); + } bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) { Index: clang/test/Analysis/analyzer-config.c =================================================================== --- clang/test/Analysis/analyzer-config.c +++ clang/test/Analysis/analyzer-config.c @@ -7,6 +7,7 @@ // CHECK-NEXT: alpha.clone.CloneChecker:IgnoredFilesPattern = "" // CHECK-NEXT: alpha.clone.CloneChecker:MinimumCloneComplexity = 50 // CHECK-NEXT: alpha.clone.CloneChecker:ReportNormalClones = true +// CHECK-NEXT: alpha.cplusplus.ContainerModeling:AggressiveEraseModeling = false // CHECK-NEXT: alpha.cplusplus.STLAlgorithmModeling:AggressiveStdFindModeling = false // CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtExec = 0x04 // CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtRead = 0x01 @@ -100,4 +101,4 @@ // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 97 +// CHECK-NEXT: num-entries = 98