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