xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
1*e038c9c4Sjoerg //===-- STLAlgorithmModeling.cpp -----------------------------------*- C++ -*--//
2*e038c9c4Sjoerg //
3*e038c9c4Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*e038c9c4Sjoerg // See https://llvm.org/LICENSE.txt for license information.
5*e038c9c4Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*e038c9c4Sjoerg //
7*e038c9c4Sjoerg //===----------------------------------------------------------------------===//
8*e038c9c4Sjoerg //
9*e038c9c4Sjoerg // Models STL algorithms.
10*e038c9c4Sjoerg //
11*e038c9c4Sjoerg //===----------------------------------------------------------------------===//
12*e038c9c4Sjoerg 
13*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
15*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
16*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
17*e038c9c4Sjoerg 
18*e038c9c4Sjoerg #include "Iterator.h"
19*e038c9c4Sjoerg 
20*e038c9c4Sjoerg using namespace clang;
21*e038c9c4Sjoerg using namespace ento;
22*e038c9c4Sjoerg using namespace iterator;
23*e038c9c4Sjoerg 
24*e038c9c4Sjoerg namespace {
25*e038c9c4Sjoerg 
26*e038c9c4Sjoerg class STLAlgorithmModeling : public Checker<eval::Call> {
27*e038c9c4Sjoerg   bool evalFind(CheckerContext &C, const CallExpr *CE) const;
28*e038c9c4Sjoerg 
29*e038c9c4Sjoerg   void Find(CheckerContext &C, const CallExpr *CE, unsigned paramNum) const;
30*e038c9c4Sjoerg 
31*e038c9c4Sjoerg   using FnCheck = bool (STLAlgorithmModeling::*)(CheckerContext &,
32*e038c9c4Sjoerg                                                 const CallExpr *) const;
33*e038c9c4Sjoerg 
34*e038c9c4Sjoerg   const CallDescriptionMap<FnCheck> Callbacks = {
35*e038c9c4Sjoerg     {{{"std", "find"}, 3}, &STLAlgorithmModeling::evalFind},
36*e038c9c4Sjoerg     {{{"std", "find"}, 4}, &STLAlgorithmModeling::evalFind},
37*e038c9c4Sjoerg     {{{"std", "find_if"}, 3}, &STLAlgorithmModeling::evalFind},
38*e038c9c4Sjoerg     {{{"std", "find_if"}, 4}, &STLAlgorithmModeling::evalFind},
39*e038c9c4Sjoerg     {{{"std", "find_if_not"}, 3}, &STLAlgorithmModeling::evalFind},
40*e038c9c4Sjoerg     {{{"std", "find_if_not"}, 4}, &STLAlgorithmModeling::evalFind},
41*e038c9c4Sjoerg     {{{"std", "find_first_of"}, 4}, &STLAlgorithmModeling::evalFind},
42*e038c9c4Sjoerg     {{{"std", "find_first_of"}, 5}, &STLAlgorithmModeling::evalFind},
43*e038c9c4Sjoerg     {{{"std", "find_first_of"}, 6}, &STLAlgorithmModeling::evalFind},
44*e038c9c4Sjoerg     {{{"std", "find_end"}, 4}, &STLAlgorithmModeling::evalFind},
45*e038c9c4Sjoerg     {{{"std", "find_end"}, 5}, &STLAlgorithmModeling::evalFind},
46*e038c9c4Sjoerg     {{{"std", "find_end"}, 6}, &STLAlgorithmModeling::evalFind},
47*e038c9c4Sjoerg     {{{"std", "lower_bound"}, 3}, &STLAlgorithmModeling::evalFind},
48*e038c9c4Sjoerg     {{{"std", "lower_bound"}, 4}, &STLAlgorithmModeling::evalFind},
49*e038c9c4Sjoerg     {{{"std", "upper_bound"}, 3}, &STLAlgorithmModeling::evalFind},
50*e038c9c4Sjoerg     {{{"std", "upper_bound"}, 4}, &STLAlgorithmModeling::evalFind},
51*e038c9c4Sjoerg     {{{"std", "search"}, 3}, &STLAlgorithmModeling::evalFind},
52*e038c9c4Sjoerg     {{{"std", "search"}, 4}, &STLAlgorithmModeling::evalFind},
53*e038c9c4Sjoerg     {{{"std", "search"}, 5}, &STLAlgorithmModeling::evalFind},
54*e038c9c4Sjoerg     {{{"std", "search"}, 6}, &STLAlgorithmModeling::evalFind},
55*e038c9c4Sjoerg     {{{"std", "search_n"}, 4}, &STLAlgorithmModeling::evalFind},
56*e038c9c4Sjoerg     {{{"std", "search_n"}, 5}, &STLAlgorithmModeling::evalFind},
57*e038c9c4Sjoerg     {{{"std", "search_n"}, 6}, &STLAlgorithmModeling::evalFind},
58*e038c9c4Sjoerg   };
59*e038c9c4Sjoerg 
60*e038c9c4Sjoerg public:
61*e038c9c4Sjoerg   STLAlgorithmModeling() = default;
62*e038c9c4Sjoerg 
63*e038c9c4Sjoerg   bool AggressiveStdFindModeling;
64*e038c9c4Sjoerg 
65*e038c9c4Sjoerg   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
66*e038c9c4Sjoerg }; //
67*e038c9c4Sjoerg 
evalCall(const CallEvent & Call,CheckerContext & C) const68*e038c9c4Sjoerg bool STLAlgorithmModeling::evalCall(const CallEvent &Call,
69*e038c9c4Sjoerg                                     CheckerContext &C) const {
70*e038c9c4Sjoerg   const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
71*e038c9c4Sjoerg   if (!CE)
72*e038c9c4Sjoerg     return false;
73*e038c9c4Sjoerg 
74*e038c9c4Sjoerg   const FnCheck *Handler = Callbacks.lookup(Call);
75*e038c9c4Sjoerg   if (!Handler)
76*e038c9c4Sjoerg     return false;
77*e038c9c4Sjoerg 
78*e038c9c4Sjoerg   return (this->**Handler)(C, CE);
79*e038c9c4Sjoerg }
80*e038c9c4Sjoerg 
evalFind(CheckerContext & C,const CallExpr * CE) const81*e038c9c4Sjoerg bool STLAlgorithmModeling::evalFind(CheckerContext &C,
82*e038c9c4Sjoerg                                     const CallExpr *CE) const {
83*e038c9c4Sjoerg   // std::find()-like functions either take their primary range in the first
84*e038c9c4Sjoerg   // two parameters, or if the first parameter is "execution policy" then in
85*e038c9c4Sjoerg   // the second and third. This means that the second parameter must always be
86*e038c9c4Sjoerg   // an iterator.
87*e038c9c4Sjoerg   if (!isIteratorType(CE->getArg(1)->getType()))
88*e038c9c4Sjoerg     return false;
89*e038c9c4Sjoerg 
90*e038c9c4Sjoerg   // If no "execution policy" parameter is used then the first argument is the
91*e038c9c4Sjoerg   // beginning of the range.
92*e038c9c4Sjoerg   if (isIteratorType(CE->getArg(0)->getType())) {
93*e038c9c4Sjoerg     Find(C, CE, 0);
94*e038c9c4Sjoerg     return true;
95*e038c9c4Sjoerg   }
96*e038c9c4Sjoerg 
97*e038c9c4Sjoerg   // If "execution policy" parameter is used then the second argument is the
98*e038c9c4Sjoerg   // beginning of the range.
99*e038c9c4Sjoerg   if (isIteratorType(CE->getArg(2)->getType())) {
100*e038c9c4Sjoerg     Find(C, CE, 1);
101*e038c9c4Sjoerg     return true;
102*e038c9c4Sjoerg   }
103*e038c9c4Sjoerg 
104*e038c9c4Sjoerg   return false;
105*e038c9c4Sjoerg }
106*e038c9c4Sjoerg 
Find(CheckerContext & C,const CallExpr * CE,unsigned paramNum) const107*e038c9c4Sjoerg void STLAlgorithmModeling::Find(CheckerContext &C, const CallExpr *CE,
108*e038c9c4Sjoerg                                 unsigned paramNum) const {
109*e038c9c4Sjoerg   auto State = C.getState();
110*e038c9c4Sjoerg   auto &SVB = C.getSValBuilder();
111*e038c9c4Sjoerg   const auto *LCtx = C.getLocationContext();
112*e038c9c4Sjoerg 
113*e038c9c4Sjoerg   SVal RetVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
114*e038c9c4Sjoerg   SVal Param = State->getSVal(CE->getArg(paramNum), LCtx);
115*e038c9c4Sjoerg 
116*e038c9c4Sjoerg   auto StateFound = State->BindExpr(CE, LCtx, RetVal);
117*e038c9c4Sjoerg 
118*e038c9c4Sjoerg   // If we have an iterator position for the range-begin argument then we can
119*e038c9c4Sjoerg   // assume that in case of successful search the position of the found element
120*e038c9c4Sjoerg   // is not ahead of it.
121*e038c9c4Sjoerg   // FIXME: Reverse iterators
122*e038c9c4Sjoerg   const auto *Pos = getIteratorPosition(State, Param);
123*e038c9c4Sjoerg   if (Pos) {
124*e038c9c4Sjoerg     StateFound = createIteratorPosition(StateFound, RetVal, Pos->getContainer(),
125*e038c9c4Sjoerg                                         CE, LCtx, C.blockCount());
126*e038c9c4Sjoerg     const auto *NewPos = getIteratorPosition(StateFound, RetVal);
127*e038c9c4Sjoerg     assert(NewPos && "Failed to create new iterator position.");
128*e038c9c4Sjoerg 
129*e038c9c4Sjoerg     SVal GreaterOrEqual = SVB.evalBinOp(StateFound, BO_GE,
130*e038c9c4Sjoerg                                         nonloc::SymbolVal(NewPos->getOffset()),
131*e038c9c4Sjoerg                                         nonloc::SymbolVal(Pos->getOffset()),
132*e038c9c4Sjoerg                                         SVB.getConditionType());
133*e038c9c4Sjoerg     assert(GreaterOrEqual.getAs<DefinedSVal>() &&
134*e038c9c4Sjoerg            "Symbol comparison must be a `DefinedSVal`");
135*e038c9c4Sjoerg     StateFound = StateFound->assume(GreaterOrEqual.castAs<DefinedSVal>(), true);
136*e038c9c4Sjoerg   }
137*e038c9c4Sjoerg 
138*e038c9c4Sjoerg   Param = State->getSVal(CE->getArg(paramNum + 1), LCtx);
139*e038c9c4Sjoerg 
140*e038c9c4Sjoerg   // If we have an iterator position for the range-end argument then we can
141*e038c9c4Sjoerg   // assume that in case of successful search the position of the found element
142*e038c9c4Sjoerg   // is ahead of it.
143*e038c9c4Sjoerg   // FIXME: Reverse iterators
144*e038c9c4Sjoerg   Pos = getIteratorPosition(State, Param);
145*e038c9c4Sjoerg   if (Pos) {
146*e038c9c4Sjoerg     StateFound = createIteratorPosition(StateFound, RetVal, Pos->getContainer(),
147*e038c9c4Sjoerg                                         CE, LCtx, C.blockCount());
148*e038c9c4Sjoerg     const auto *NewPos = getIteratorPosition(StateFound, RetVal);
149*e038c9c4Sjoerg     assert(NewPos && "Failed to create new iterator position.");
150*e038c9c4Sjoerg 
151*e038c9c4Sjoerg     SVal Less = SVB.evalBinOp(StateFound, BO_LT,
152*e038c9c4Sjoerg                               nonloc::SymbolVal(NewPos->getOffset()),
153*e038c9c4Sjoerg                               nonloc::SymbolVal(Pos->getOffset()),
154*e038c9c4Sjoerg                               SVB.getConditionType());
155*e038c9c4Sjoerg     assert(Less.getAs<DefinedSVal>() &&
156*e038c9c4Sjoerg            "Symbol comparison must be a `DefinedSVal`");
157*e038c9c4Sjoerg     StateFound = StateFound->assume(Less.castAs<DefinedSVal>(), true);
158*e038c9c4Sjoerg   }
159*e038c9c4Sjoerg 
160*e038c9c4Sjoerg   C.addTransition(StateFound);
161*e038c9c4Sjoerg 
162*e038c9c4Sjoerg   if (AggressiveStdFindModeling) {
163*e038c9c4Sjoerg     auto StateNotFound = State->BindExpr(CE, LCtx, Param);
164*e038c9c4Sjoerg     C.addTransition(StateNotFound);
165*e038c9c4Sjoerg   }
166*e038c9c4Sjoerg }
167*e038c9c4Sjoerg 
168*e038c9c4Sjoerg } // namespace
169*e038c9c4Sjoerg 
registerSTLAlgorithmModeling(CheckerManager & Mgr)170*e038c9c4Sjoerg void ento::registerSTLAlgorithmModeling(CheckerManager &Mgr) {
171*e038c9c4Sjoerg   auto *Checker = Mgr.registerChecker<STLAlgorithmModeling>();
172*e038c9c4Sjoerg   Checker->AggressiveStdFindModeling =
173*e038c9c4Sjoerg       Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker,
174*e038c9c4Sjoerg                                                   "AggressiveStdFindModeling");
175*e038c9c4Sjoerg }
176*e038c9c4Sjoerg 
shouldRegisterSTLAlgorithmModeling(const CheckerManager & mgr)177*e038c9c4Sjoerg bool ento::shouldRegisterSTLAlgorithmModeling(const CheckerManager &mgr) {
178*e038c9c4Sjoerg   return true;
179*e038c9c4Sjoerg }
180*e038c9c4Sjoerg 
181