1*0fca6ea1SDimitry Andric //===-- STLAlgorithmModeling.cpp ----------------------------------*- C++ -*--// 25ffd83dbSDimitry Andric // 35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ffd83dbSDimitry Andric // 75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 85ffd83dbSDimitry Andric // 95ffd83dbSDimitry Andric // Models STL algorithms. 105ffd83dbSDimitry Andric // 115ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 125ffd83dbSDimitry Andric 135ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 145ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 15349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 165ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 175ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 185ffd83dbSDimitry Andric 195ffd83dbSDimitry Andric #include "Iterator.h" 205ffd83dbSDimitry Andric 215ffd83dbSDimitry Andric using namespace clang; 225ffd83dbSDimitry Andric using namespace ento; 235ffd83dbSDimitry Andric using namespace iterator; 245ffd83dbSDimitry Andric 255ffd83dbSDimitry Andric namespace { 265ffd83dbSDimitry Andric 275ffd83dbSDimitry Andric class STLAlgorithmModeling : public Checker<eval::Call> { 285ffd83dbSDimitry Andric bool evalFind(CheckerContext &C, const CallExpr *CE) const; 295ffd83dbSDimitry Andric 305ffd83dbSDimitry Andric void Find(CheckerContext &C, const CallExpr *CE, unsigned paramNum) const; 315ffd83dbSDimitry Andric 325ffd83dbSDimitry Andric using FnCheck = bool (STLAlgorithmModeling::*)(CheckerContext &, 335ffd83dbSDimitry Andric const CallExpr *) const; 345ffd83dbSDimitry Andric 355ffd83dbSDimitry Andric const CallDescriptionMap<FnCheck> Callbacks = { 36*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "find"}, 3}, &STLAlgorithmModeling::evalFind}, 37*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "find"}, 4}, &STLAlgorithmModeling::evalFind}, 38*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "find_if"}, 3}, 39*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 40*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "find_if"}, 4}, 41*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 42*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "find_if_not"}, 3}, 43*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 44*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "find_if_not"}, 4}, 45*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 46*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "find_first_of"}, 4}, 47*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 48*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "find_first_of"}, 5}, 49*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 50*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "find_first_of"}, 6}, 51*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 52*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "find_end"}, 4}, 53*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 54*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "find_end"}, 5}, 55*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 56*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "find_end"}, 6}, 57*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 58*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "lower_bound"}, 3}, 59*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 60*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "lower_bound"}, 4}, 61*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 62*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "upper_bound"}, 3}, 63*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 64*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "upper_bound"}, 4}, 65*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 66*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "search"}, 3}, 67*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 68*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "search"}, 4}, 69*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 70*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "search"}, 5}, 71*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 72*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "search"}, 6}, 73*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 74*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "search_n"}, 4}, 75*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 76*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "search_n"}, 5}, 77*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 78*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"std", "search_n"}, 6}, 79*0fca6ea1SDimitry Andric &STLAlgorithmModeling::evalFind}, 805ffd83dbSDimitry Andric }; 815ffd83dbSDimitry Andric 825ffd83dbSDimitry Andric public: 835ffd83dbSDimitry Andric STLAlgorithmModeling() = default; 845ffd83dbSDimitry Andric 8506c3fb27SDimitry Andric bool AggressiveStdFindModeling = false; 865ffd83dbSDimitry Andric 875ffd83dbSDimitry Andric bool evalCall(const CallEvent &Call, CheckerContext &C) const; 885ffd83dbSDimitry Andric }; // 895ffd83dbSDimitry Andric 905ffd83dbSDimitry Andric bool STLAlgorithmModeling::evalCall(const CallEvent &Call, 915ffd83dbSDimitry Andric CheckerContext &C) const { 925ffd83dbSDimitry Andric const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 935ffd83dbSDimitry Andric if (!CE) 945ffd83dbSDimitry Andric return false; 955ffd83dbSDimitry Andric 965ffd83dbSDimitry Andric const FnCheck *Handler = Callbacks.lookup(Call); 975ffd83dbSDimitry Andric if (!Handler) 985ffd83dbSDimitry Andric return false; 995ffd83dbSDimitry Andric 1005ffd83dbSDimitry Andric return (this->**Handler)(C, CE); 1015ffd83dbSDimitry Andric } 1025ffd83dbSDimitry Andric 1035ffd83dbSDimitry Andric bool STLAlgorithmModeling::evalFind(CheckerContext &C, 1045ffd83dbSDimitry Andric const CallExpr *CE) const { 1055ffd83dbSDimitry Andric // std::find()-like functions either take their primary range in the first 1065ffd83dbSDimitry Andric // two parameters, or if the first parameter is "execution policy" then in 1075ffd83dbSDimitry Andric // the second and third. This means that the second parameter must always be 1085ffd83dbSDimitry Andric // an iterator. 1095ffd83dbSDimitry Andric if (!isIteratorType(CE->getArg(1)->getType())) 1105ffd83dbSDimitry Andric return false; 1115ffd83dbSDimitry Andric 1125ffd83dbSDimitry Andric // If no "execution policy" parameter is used then the first argument is the 1135ffd83dbSDimitry Andric // beginning of the range. 1145ffd83dbSDimitry Andric if (isIteratorType(CE->getArg(0)->getType())) { 1155ffd83dbSDimitry Andric Find(C, CE, 0); 1165ffd83dbSDimitry Andric return true; 1175ffd83dbSDimitry Andric } 1185ffd83dbSDimitry Andric 1195ffd83dbSDimitry Andric // If "execution policy" parameter is used then the second argument is the 1205ffd83dbSDimitry Andric // beginning of the range. 1215ffd83dbSDimitry Andric if (isIteratorType(CE->getArg(2)->getType())) { 1225ffd83dbSDimitry Andric Find(C, CE, 1); 1235ffd83dbSDimitry Andric return true; 1245ffd83dbSDimitry Andric } 1255ffd83dbSDimitry Andric 1265ffd83dbSDimitry Andric return false; 1275ffd83dbSDimitry Andric } 1285ffd83dbSDimitry Andric 1295ffd83dbSDimitry Andric void STLAlgorithmModeling::Find(CheckerContext &C, const CallExpr *CE, 1305ffd83dbSDimitry Andric unsigned paramNum) const { 1315ffd83dbSDimitry Andric auto State = C.getState(); 1325ffd83dbSDimitry Andric auto &SVB = C.getSValBuilder(); 1335ffd83dbSDimitry Andric const auto *LCtx = C.getLocationContext(); 1345ffd83dbSDimitry Andric 1355ffd83dbSDimitry Andric SVal RetVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); 1365ffd83dbSDimitry Andric SVal Param = State->getSVal(CE->getArg(paramNum), LCtx); 1375ffd83dbSDimitry Andric 1385ffd83dbSDimitry Andric auto StateFound = State->BindExpr(CE, LCtx, RetVal); 1395ffd83dbSDimitry Andric 1405ffd83dbSDimitry Andric // If we have an iterator position for the range-begin argument then we can 1415ffd83dbSDimitry Andric // assume that in case of successful search the position of the found element 1425ffd83dbSDimitry Andric // is not ahead of it. 1435ffd83dbSDimitry Andric // FIXME: Reverse iterators 1445ffd83dbSDimitry Andric const auto *Pos = getIteratorPosition(State, Param); 1455ffd83dbSDimitry Andric if (Pos) { 1465ffd83dbSDimitry Andric StateFound = createIteratorPosition(StateFound, RetVal, Pos->getContainer(), 1475ffd83dbSDimitry Andric CE, LCtx, C.blockCount()); 1485ffd83dbSDimitry Andric const auto *NewPos = getIteratorPosition(StateFound, RetVal); 1495ffd83dbSDimitry Andric assert(NewPos && "Failed to create new iterator position."); 1505ffd83dbSDimitry Andric 1515ffd83dbSDimitry Andric SVal GreaterOrEqual = SVB.evalBinOp(StateFound, BO_GE, 1525ffd83dbSDimitry Andric nonloc::SymbolVal(NewPos->getOffset()), 1535ffd83dbSDimitry Andric nonloc::SymbolVal(Pos->getOffset()), 1545ffd83dbSDimitry Andric SVB.getConditionType()); 15581ad6265SDimitry Andric assert(isa<DefinedSVal>(GreaterOrEqual) && 1565ffd83dbSDimitry Andric "Symbol comparison must be a `DefinedSVal`"); 1575ffd83dbSDimitry Andric StateFound = StateFound->assume(GreaterOrEqual.castAs<DefinedSVal>(), true); 1585ffd83dbSDimitry Andric } 1595ffd83dbSDimitry Andric 1605ffd83dbSDimitry Andric Param = State->getSVal(CE->getArg(paramNum + 1), LCtx); 1615ffd83dbSDimitry Andric 1625ffd83dbSDimitry Andric // If we have an iterator position for the range-end argument then we can 1635ffd83dbSDimitry Andric // assume that in case of successful search the position of the found element 1645ffd83dbSDimitry Andric // is ahead of it. 1655ffd83dbSDimitry Andric // FIXME: Reverse iterators 1665ffd83dbSDimitry Andric Pos = getIteratorPosition(State, Param); 1675ffd83dbSDimitry Andric if (Pos) { 1685ffd83dbSDimitry Andric StateFound = createIteratorPosition(StateFound, RetVal, Pos->getContainer(), 1695ffd83dbSDimitry Andric CE, LCtx, C.blockCount()); 1705ffd83dbSDimitry Andric const auto *NewPos = getIteratorPosition(StateFound, RetVal); 1715ffd83dbSDimitry Andric assert(NewPos && "Failed to create new iterator position."); 1725ffd83dbSDimitry Andric 1735ffd83dbSDimitry Andric SVal Less = SVB.evalBinOp(StateFound, BO_LT, 1745ffd83dbSDimitry Andric nonloc::SymbolVal(NewPos->getOffset()), 1755ffd83dbSDimitry Andric nonloc::SymbolVal(Pos->getOffset()), 1765ffd83dbSDimitry Andric SVB.getConditionType()); 17781ad6265SDimitry Andric assert(isa<DefinedSVal>(Less) && 1785ffd83dbSDimitry Andric "Symbol comparison must be a `DefinedSVal`"); 1795ffd83dbSDimitry Andric StateFound = StateFound->assume(Less.castAs<DefinedSVal>(), true); 1805ffd83dbSDimitry Andric } 1815ffd83dbSDimitry Andric 1825ffd83dbSDimitry Andric C.addTransition(StateFound); 1835ffd83dbSDimitry Andric 1845ffd83dbSDimitry Andric if (AggressiveStdFindModeling) { 1855ffd83dbSDimitry Andric auto StateNotFound = State->BindExpr(CE, LCtx, Param); 1865ffd83dbSDimitry Andric C.addTransition(StateNotFound); 1875ffd83dbSDimitry Andric } 1885ffd83dbSDimitry Andric } 1895ffd83dbSDimitry Andric 1905ffd83dbSDimitry Andric } // namespace 1915ffd83dbSDimitry Andric 1925ffd83dbSDimitry Andric void ento::registerSTLAlgorithmModeling(CheckerManager &Mgr) { 1935ffd83dbSDimitry Andric auto *Checker = Mgr.registerChecker<STLAlgorithmModeling>(); 1945ffd83dbSDimitry Andric Checker->AggressiveStdFindModeling = 1955ffd83dbSDimitry Andric Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, 1965ffd83dbSDimitry Andric "AggressiveStdFindModeling"); 1975ffd83dbSDimitry Andric } 1985ffd83dbSDimitry Andric 1995ffd83dbSDimitry Andric bool ento::shouldRegisterSTLAlgorithmModeling(const CheckerManager &mgr) { 2005ffd83dbSDimitry Andric return true; 2015ffd83dbSDimitry Andric } 2025ffd83dbSDimitry Andric 203