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