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