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