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