Cppcheck
infer.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2023 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "infer.h"
20 
21 #include "calculate.h"
22 #include "errortypes.h"
23 #include "valueptr.h"
24 
25 #include <cassert>
26 #include <algorithm>
27 #include <functional>
28 #include <iterator>
29 #include <unordered_set>
30 #include <utility>
31 
32 class Token;
33 
34 template<class Predicate, class Compare>
35 static const ValueFlow::Value* getCompareValue(const std::list<ValueFlow::Value>& values, Predicate pred, Compare compare)
36 {
37  const ValueFlow::Value* result = nullptr;
38  for (const ValueFlow::Value& value : values) {
39  if (!pred(value))
40  continue;
41  if (result)
42  result = &std::min(value, *result, [compare](const ValueFlow::Value& x, const ValueFlow::Value& y) {
43  return compare(x.intvalue, y.intvalue);
44  });
45  else
46  result = &value;
47  }
48  return result;
49 }
50 
51 namespace {
52  struct Interval {
53  std::vector<MathLib::bigint> minvalue, maxvalue;
54  std::vector<const ValueFlow::Value*> minRef, maxRef;
55 
56  void setMinValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr)
57  {
58  minvalue = {x};
59  if (ref)
60  minRef = {ref};
61  }
62 
63  void setMaxValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr)
64  {
65  maxvalue = {x};
66  if (ref)
67  maxRef = {ref};
68  }
69 
70  bool isLessThan(MathLib::bigint x, std::vector<const ValueFlow::Value*>* ref = nullptr) const
71  {
72  if (!this->maxvalue.empty() && this->maxvalue.front() < x) {
73  if (ref)
74  *ref = maxRef;
75  return true;
76  }
77  return false;
78  }
79 
80  bool isGreaterThan(MathLib::bigint x, std::vector<const ValueFlow::Value*>* ref = nullptr) const
81  {
82  if (!this->minvalue.empty() && this->minvalue.front() > x) {
83  if (ref)
84  *ref = minRef;
85  return true;
86  }
87  return false;
88  }
89 
90  bool isScalar() const {
91  return minvalue.size() == 1 && minvalue == maxvalue;
92  }
93 
94  bool empty() const {
95  return minvalue.empty() && maxvalue.empty();
96  }
97 
98  bool isScalarOrEmpty() const {
99  return empty() || isScalar();
100  }
101 
102  MathLib::bigint getScalar() const
103  {
104  assert(isScalar());
105  return minvalue.front();
106  }
107 
108  std::vector<const ValueFlow::Value*> getScalarRef() const
109  {
110  assert(isScalar());
111  if (minRef != maxRef)
112  return merge(minRef, maxRef);
113  return minRef;
114  }
115 
116  static Interval fromInt(MathLib::bigint x, const ValueFlow::Value* ref = nullptr)
117  {
118  Interval result;
119  result.setMinValue(x, ref);
120  result.setMaxValue(x, ref);
121  return result;
122  }
123 
124  template<class Predicate>
125  static Interval fromValues(const std::list<ValueFlow::Value>& values, Predicate predicate)
126  {
127  Interval result;
128  const ValueFlow::Value* minValue = getCompareValue(values, predicate, std::less<MathLib::bigint>{});
129  if (minValue) {
130  if (minValue->isImpossible() && minValue->bound == ValueFlow::Value::Bound::Upper)
131  result.setMinValue(minValue->intvalue + 1, minValue);
132  if (minValue->isPossible() && minValue->bound == ValueFlow::Value::Bound::Lower)
133  result.setMinValue(minValue->intvalue, minValue);
134  if (!minValue->isImpossible() && (minValue->bound == ValueFlow::Value::Bound::Point || minValue->isKnown()) &&
135  std::count_if(values.begin(), values.end(), predicate) == 1)
136  return Interval::fromInt(minValue->intvalue, minValue);
137  }
138  const ValueFlow::Value* maxValue = getCompareValue(values, predicate, std::greater<MathLib::bigint>{});
139  if (maxValue) {
140  if (maxValue->isImpossible() && maxValue->bound == ValueFlow::Value::Bound::Lower)
141  result.setMaxValue(maxValue->intvalue - 1, maxValue);
142  if (maxValue->isPossible() && maxValue->bound == ValueFlow::Value::Bound::Upper)
143  result.setMaxValue(maxValue->intvalue, maxValue);
144  assert(!maxValue->isKnown());
145  }
146  return result;
147  }
148 
149  static Interval fromValues(const std::list<ValueFlow::Value>& values)
150  {
151  return Interval::fromValues(values, [](const ValueFlow::Value&) {
152  return true;
153  });
154  }
155 
156  template<class F>
157  static std::vector<MathLib::bigint> apply(const std::vector<MathLib::bigint>& x,
158  const std::vector<MathLib::bigint>& y,
159  F f)
160  {
161  if (x.empty())
162  return {};
163  if (y.empty())
164  return {};
165  return {f(x.front(), y.front())};
166  }
167 
168  static std::vector<const ValueFlow::Value*> merge(std::vector<const ValueFlow::Value*> x,
169  const std::vector<const ValueFlow::Value*>& y)
170  {
171  x.insert(x.end(), y.cbegin(), y.cend());
172  return x;
173  }
174 
175  friend Interval operator-(const Interval& lhs, const Interval& rhs)
176  {
177  Interval result;
178  result.minvalue = Interval::apply(lhs.minvalue, rhs.maxvalue, std::minus<MathLib::bigint>{});
179  result.maxvalue = Interval::apply(lhs.maxvalue, rhs.minvalue, std::minus<MathLib::bigint>{});
180  if (!result.minvalue.empty())
181  result.minRef = merge(lhs.minRef, rhs.maxRef);
182  if (!result.maxvalue.empty())
183  result.maxRef = merge(lhs.maxRef, rhs.minRef);
184  return result;
185  }
186 
187  static std::vector<int> equal(const Interval& lhs,
188  const Interval& rhs,
189  std::vector<const ValueFlow::Value*>* ref = nullptr)
190  {
191  if (!lhs.isScalar())
192  return {};
193  if (!rhs.isScalar())
194  return {};
195  if (ref)
196  *ref = merge(lhs.getScalarRef(), rhs.getScalarRef());
197  return {lhs.minvalue == rhs.minvalue};
198  }
199 
200  static std::vector<int> compare(const Interval& lhs,
201  const Interval& rhs,
202  std::vector<const ValueFlow::Value*>* ref = nullptr)
203  {
204  Interval diff = lhs - rhs;
205  if (diff.isGreaterThan(0, ref))
206  return {1};
207  if (diff.isLessThan(0, ref))
208  return {-1};
209  std::vector<int> eq = Interval::equal(lhs, rhs, ref);
210  if (!eq.empty()) {
211  if (eq.front() == 0)
212  return {1, -1};
213  return {0};
214  }
215  if (diff.isGreaterThan(-1, ref))
216  return {0, 1};
217  if (diff.isLessThan(1, ref))
218  return {0, -1};
219  return {};
220  }
221 
222  static std::vector<bool> compare(const std::string& op,
223  const Interval& lhs,
224  const Interval& rhs,
225  std::vector<const ValueFlow::Value*>* ref = nullptr)
226  {
227  std::vector<int> r = compare(lhs, rhs, ref);
228  if (r.empty())
229  return {};
230  bool b = calculate(op, r.front(), 0);
231  if (std::all_of(r.cbegin() + 1, r.cend(), [&](int i) {
232  return b == calculate(op, i, 0);
233  }))
234  return {b};
235  return {};
236  }
237  };
238 }
239 
240 static void addToErrorPath(ValueFlow::Value& value, const std::vector<const ValueFlow::Value*>& refs)
241 {
242  std::unordered_set<const Token*> locations;
243  for (const ValueFlow::Value* ref : refs) {
244  if (ref->condition && !value.condition)
245  value.condition = ref->condition;
246  std::copy_if(ref->errorPath.cbegin(),
247  ref->errorPath.cend(),
248  std::back_inserter(value.errorPath),
249  [&](const ErrorPathItem& e) {
250  return locations.insert(e.first).second;
251  });
252  std::copy_if(ref->debugPath.cbegin(),
253  ref->debugPath.cend(),
254  std::back_inserter(value.debugPath),
255  [&](const ErrorPathItem& e) {
256  return locations.insert(e.first).second;
257  });
258  }
259 }
260 
261 static void setValueKind(ValueFlow::Value& value, const std::vector<const ValueFlow::Value*>& refs)
262 {
263  bool isPossible = false;
264  bool isInconclusive = false;
265  for (const ValueFlow::Value* ref : refs) {
266  if (ref->isPossible())
267  isPossible = true;
268  if (ref->isInconclusive())
269  isInconclusive = true;
270  }
271  if (isInconclusive)
272  value.setInconclusive();
273  else if (isPossible)
274  value.setPossible();
275  else
276  value.setKnown();
277 }
278 
279 static bool inferNotEqual(const std::list<ValueFlow::Value>& values, MathLib::bigint x)
280 {
281  return std::any_of(values.cbegin(), values.cend(), [&](const ValueFlow::Value& value) {
282  return value.isImpossible() && value.intvalue == x;
283  });
284 }
285 
286 std::vector<ValueFlow::Value> infer(const ValuePtr<InferModel>& model,
287  const std::string& op,
288  std::list<ValueFlow::Value> lhsValues,
289  std::list<ValueFlow::Value> rhsValues)
290 {
291  std::vector<ValueFlow::Value> result;
292  auto notMatch = [&](const ValueFlow::Value& value) {
293  return !model->match(value);
294  };
295  lhsValues.remove_if(notMatch);
296  rhsValues.remove_if(notMatch);
297  if (lhsValues.empty() || rhsValues.empty())
298  return result;
299 
300  Interval lhs = Interval::fromValues(lhsValues);
301  Interval rhs = Interval::fromValues(rhsValues);
302 
303  if (op == "-") {
304  Interval diff = lhs - rhs;
305  if (diff.isScalar()) {
306  std::vector<const ValueFlow::Value*> refs = diff.getScalarRef();
307  ValueFlow::Value value(diff.getScalar());
308  addToErrorPath(value, refs);
309  setValueKind(value, refs);
310  result.push_back(std::move(value));
311  } else {
312  if (!diff.minvalue.empty()) {
313  ValueFlow::Value value(diff.minvalue.front() - 1);
314  value.setImpossible();
316  addToErrorPath(value, diff.minRef);
317  result.push_back(std::move(value));
318  }
319  if (!diff.maxvalue.empty()) {
320  ValueFlow::Value value(diff.maxvalue.front() + 1);
321  value.setImpossible();
323  addToErrorPath(value, diff.maxRef);
324  result.push_back(std::move(value));
325  }
326  }
327  } else if ((op == "!=" || op == "==") && lhs.isScalarOrEmpty() && rhs.isScalarOrEmpty()) {
328  if (lhs.isScalar() && rhs.isScalar()) {
329  std::vector<const ValueFlow::Value*> refs = Interval::merge(lhs.getScalarRef(), rhs.getScalarRef());
330  ValueFlow::Value value(calculate(op, lhs.getScalar(), rhs.getScalar()));
331  addToErrorPath(value, refs);
332  setValueKind(value, refs);
333  result.push_back(std::move(value));
334  } else {
335  std::vector<const ValueFlow::Value*> refs;
336  if (lhs.isScalar() && inferNotEqual(rhsValues, lhs.getScalar()))
337  refs = lhs.getScalarRef();
338  else if (rhs.isScalar() && inferNotEqual(lhsValues, rhs.getScalar()))
339  refs = rhs.getScalarRef();
340  if (!refs.empty()) {
341  ValueFlow::Value value(op == "!=");
342  addToErrorPath(value, refs);
343  setValueKind(value, refs);
344  result.push_back(std::move(value));
345  }
346  }
347  } else {
348  std::vector<const ValueFlow::Value*> refs;
349  std::vector<bool> r = Interval::compare(op, lhs, rhs, &refs);
350  if (!r.empty()) {
351  ValueFlow::Value value(r.front());
352  addToErrorPath(value, refs);
353  setValueKind(value, refs);
354  result.push_back(std::move(value));
355  }
356  }
357 
358  return result;
359 }
360 
361 std::vector<ValueFlow::Value> infer(const ValuePtr<InferModel>& model,
362  const std::string& op,
363  MathLib::bigint lhs,
364  std::list<ValueFlow::Value> rhsValues)
365 {
366  return infer(model, op, {model->yield(lhs)}, std::move(rhsValues));
367 }
368 
369 std::vector<ValueFlow::Value> infer(const ValuePtr<InferModel>& model,
370  const std::string& op,
371  std::list<ValueFlow::Value> lhsValues,
372  MathLib::bigint rhs)
373 {
374  return infer(model, op, std::move(lhsValues), {model->yield(rhs)});
375 }
376 
377 std::vector<MathLib::bigint> getMinValue(const ValuePtr<InferModel>& model, const std::list<ValueFlow::Value>& values)
378 {
379  return Interval::fromValues(values, [&](const ValueFlow::Value& v) {
380  return model->match(v);
381  }).minvalue;
382 }
383 std::vector<MathLib::bigint> getMaxValue(const ValuePtr<InferModel>& model, const std::list<ValueFlow::Value>& values)
384 {
385  return Interval::fromValues(values, [&](const ValueFlow::Value& v) {
386  return model->match(v);
387  }).maxvalue;
388 }
R calculate(const std::string &s, const T &x, const T &y, bool *error=nullptr)
Definition: calculate.h:50
long long bigint
Definition: mathlib.h:68
The token list that the TokenList generates is a linked-list of this class.
Definition: token.h:150
const std::list< ValueFlow::Value > & values() const
Definition: token.h:1197
void setImpossible()
Definition: vfvalue.h:369
ErrorPath debugPath
Definition: vfvalue.h:284
Bound bound
The value bound
Definition: vfvalue.h:265
ErrorPath errorPath
Definition: vfvalue.h:282
bool isImpossible() const
Definition: vfvalue.h:365
bool isKnown() const
Definition: vfvalue.h:353
const Token * condition
Condition that this value depends on.
Definition: vfvalue.h:280
long long intvalue
int value (or sometimes bool value?)
Definition: vfvalue.h:268
void setPossible()
Definition: vfvalue.h:357
void setInconclusive(bool inconclusive=true)
Definition: vfvalue.h:373
void setKnown()
Definition: vfvalue.h:349
bool isPossible() const
Definition: vfvalue.h:361
std::pair< const Token *, std::string > ErrorPathItem
Definition: errortypes.h:129
MathLib::value operator-(const MathLib::value &v1, const MathLib::value &v2)
Definition: mathlib.cpp:1320
std::vector< MathLib::bigint > getMinValue(const ValuePtr< InferModel > &model, const std::list< ValueFlow::Value > &values)
Definition: infer.cpp:377
static const ValueFlow::Value * getCompareValue(const std::list< ValueFlow::Value > &values, Predicate pred, Compare compare)
Definition: infer.cpp:35
static void addToErrorPath(ValueFlow::Value &value, const std::vector< const ValueFlow::Value * > &refs)
Definition: infer.cpp:240
static void setValueKind(ValueFlow::Value &value, const std::vector< const ValueFlow::Value * > &refs)
Definition: infer.cpp:261
std::vector< MathLib::bigint > getMaxValue(const ValuePtr< InferModel > &model, const std::list< ValueFlow::Value > &values)
Definition: infer.cpp:383
static bool inferNotEqual(const std::list< ValueFlow::Value > &values, MathLib::bigint x)
Definition: infer.cpp:279
std::vector< ValueFlow::Value > infer(const ValuePtr< InferModel > &model, const std::string &op, std::list< ValueFlow::Value > lhsValues, std::list< ValueFlow::Value > rhsValues)
Definition: infer.cpp:286