Cppcheck
reverseanalyzer.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2024 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 "reverseanalyzer.h"
20 
21 #include "analyzer.h"
22 #include "astutils.h"
23 #include "errortypes.h"
24 #include "forwardanalyzer.h"
25 #include "mathlib.h"
26 #include "settings.h"
27 #include "symboldatabase.h"
28 #include "token.h"
29 #include "valueptr.h"
30 
31 #include <algorithm>
32 #include <cstddef>
33 #include <string>
34 #include <tuple>
35 #include <utility>
36 #include <vector>
37 
38 namespace {
39  struct ReverseTraversal {
40  ReverseTraversal(const ValuePtr<Analyzer>& analyzer, const TokenList& tokenlist, ErrorLogger& errorLogger, const Settings& settings)
41  : analyzer(analyzer), tokenlist(tokenlist), errorLogger(errorLogger), settings(settings)
42  {}
43  ValuePtr<Analyzer> analyzer;
44  const TokenList& tokenlist;
45  ErrorLogger& errorLogger;
46  const Settings& settings;
47 
48  std::pair<bool, bool> evalCond(const Token* tok) const {
49  std::vector<MathLib::bigint> result = analyzer->evaluate(tok);
50  // TODO: We should convert to bool
51  const bool checkThen = std::any_of(result.cbegin(), result.cend(), [](int x) {
52  return x == 1;
53  });
54  const bool checkElse = std::any_of(result.cbegin(), result.cend(), [](int x) {
55  return x == 0;
56  });
57  return std::make_pair(checkThen, checkElse);
58  }
59 
60  bool update(Token* tok) {
61  Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse);
62  if (action.isInconclusive() && !analyzer->lowerToInconclusive())
63  return false;
64  if (action.isInvalid())
65  return false;
66  if (!action.isNone())
67  analyzer->update(tok, action, Analyzer::Direction::Reverse);
68  return true;
69  }
70 
71  static Token* getParentFunction(Token* tok)
72  {
73  if (!tok)
74  return nullptr;
75  if (!tok->astParent())
76  return nullptr;
77  int argn = -1;
78  if (Token* ftok = getTokenArgumentFunction(tok, argn)) {
79  while (!Token::Match(ftok, "(|{")) {
80  if (!ftok)
81  return nullptr;
82  if (ftok->index() >= tok->index())
83  return nullptr;
84  if (!ftok->link() || ftok->str() == ")")
85  ftok = ftok->next();
86  else
87  ftok = ftok->link()->next();
88  }
89  if (ftok == tok)
90  return nullptr;
91  return ftok;
92  }
93  return nullptr;
94  }
95 
96  static Token* getTopFunction(Token* tok)
97  {
98  if (!tok)
99  return nullptr;
100  if (!tok->astParent())
101  return tok;
102  Token* parent = tok;
103  Token* top = tok;
104  while ((parent = getParentFunction(parent)))
105  top = parent;
106  return top;
107  }
108 
109  bool updateRecursive(Token* start) {
110  bool continueB = true;
111  visitAstNodes(start, [&](Token* tok) {
112  const Token* parent = tok->astParent();
113  while (Token::simpleMatch(parent, ":"))
114  parent = parent->astParent();
115  if (isUnevaluated(tok) || isDeadCode(tok, parent))
116  return ChildrenToVisit::none;
117  continueB &= update(tok);
118  if (continueB)
120  return ChildrenToVisit::done;
121  });
122  return continueB;
123  }
124 
125  Analyzer::Action analyzeRecursive(const Token* start) const {
127  visitAstNodes(start, [&](const Token* tok) {
128  result |= analyzer->analyze(tok, Analyzer::Direction::Reverse);
129  if (result.isModified())
130  return ChildrenToVisit::done;
132  });
133  return result;
134  }
135 
136  Analyzer::Action analyzeRange(const Token* start, const Token* end) const {
138  for (const Token* tok = start; tok && tok != end; tok = tok->next()) {
139  Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse);
140  if (action.isModified())
141  return action;
142  result |= action;
143  }
144  return result;
145  }
146 
147  Token* isDeadCode(Token* tok, const Token* end = nullptr) const {
148  int opSide = 0;
149  for (; tok && tok->astParent(); tok = tok->astParent()) {
150  if (tok == end)
151  break;
152  Token* parent = tok->astParent();
153  if (Token::simpleMatch(parent, ":")) {
154  if (astIsLHS(tok))
155  opSide = 1;
156  else if (astIsRHS(tok))
157  opSide = 2;
158  else
159  opSide = 0;
160  }
161  if (tok != parent->astOperand2())
162  continue;
163  if (Token::simpleMatch(parent, ":"))
164  parent = parent->astParent();
165  if (!Token::Match(parent, "%oror%|&&|?"))
166  continue;
167  const Token* condTok = parent->astOperand1();
168  if (!condTok)
169  continue;
170  bool checkThen, checkElse;
171  std::tie(checkThen, checkElse) = evalCond(condTok);
172 
173  if (parent->str() == "?") {
174  if (checkElse && opSide == 1)
175  return parent;
176  if (checkThen && opSide == 2)
177  return parent;
178  }
179  if (!checkThen && parent->str() == "&&")
180  return parent;
181  if (!checkElse && parent->str() == "||")
182  return parent;
183  }
184  return nullptr;
185  }
186 
187  void traverse(Token* start, const Token* end = nullptr) {
188  if (start == end)
189  return;
190  std::size_t i = start->index();
191  for (Token* tok = start->previous(); succeeds(tok, end); tok = tok->previous()) {
192  if (tok->index() >= i)
193  throw InternalError(tok, "Cyclic reverse analysis.");
194  i = tok->index();
195  if (tok == start || (tok->str() == "{" && (tok->scope()->type == Scope::ScopeType::eFunction ||
196  tok->scope()->type == Scope::ScopeType::eLambda))) {
197  const Function* f = tok->scope()->function;
198  if (f && f->isConstructor()) {
199  if (const Token* initList = f->constructorMemberInitialization())
200  traverse(tok->previous(), tok->tokAt(initList->index() - tok->index()));
201  }
202  break;
203  }
204  if (Token::Match(tok, "return|break|continue"))
205  break;
206  if (Token::Match(tok, "%name% :"))
207  break;
208  if (Token::simpleMatch(tok, ":"))
209  continue;
210  // Evaluate LHS of assignment before RHS
211  if (Token* assignTok = assignExpr(tok)) {
212  // If assignTok has broken ast then stop
213  if (!assignTok->astOperand1() || !assignTok->astOperand2())
214  break;
215  Token* assignTop = assignTok;
216  bool continueB = true;
217  while (assignTop->isAssignmentOp()) {
218  if (!Token::Match(assignTop->astOperand1(), "%assign%")) {
219  continueB &= updateRecursive(assignTop->astOperand1());
220  }
221  if (!assignTop->astParent())
222  break;
223  assignTop = assignTop->astParent();
224  }
225  // Is assignment in dead code
226  if (Token* parent = isDeadCode(assignTok)) {
227  tok = parent;
228  continue;
229  }
230  // Simple assign
231  if (assignTok->str() == "=" && (assignTok->astParent() == assignTop || assignTok == assignTop)) {
232  Analyzer::Action rhsAction =
233  analyzer->analyze(assignTok->astOperand2(), Analyzer::Direction::Reverse);
234  Analyzer::Action lhsAction =
235  analyzer->analyze(assignTok->astOperand1(), Analyzer::Direction::Reverse);
236  // Assignment from
237  if (rhsAction.isRead() && !lhsAction.isInvalid() && assignTok->astOperand1()->exprId() > 0) {
238  const std::string info = "Assignment from '" + assignTok->expressionString() + "'";
239  ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand1(), info);
240  if (a) {
241  valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()),
242  assignTok->astOperand2()->scope()->bodyEnd,
243  a,
244  tokenlist,
245  errorLogger,
246  settings);
247  }
248  // Assignment to
249  } else if (lhsAction.matches() && !assignTok->astOperand2()->hasKnownIntValue() &&
250  assignTok->astOperand2()->exprId() > 0 &&
251  isConstExpression(assignTok->astOperand2(), settings.library)) {
252  const std::string info = "Assignment to '" + assignTok->expressionString() + "'";
253  ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand2(), info);
254  if (a) {
255  valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()),
256  assignTok->astOperand2()->scope()->bodyEnd,
257  a,
258  tokenlist,
259  errorLogger,
260  settings);
261  valueFlowGenericReverse(assignTok->astOperand1()->previous(), end, a, tokenlist, errorLogger, settings);
262  }
263  }
264  }
265  if (!continueB)
266  break;
267  if (!updateRecursive(assignTop->astOperand2()))
268  break;
269  tok = previousBeforeAstLeftmostLeaf(assignTop)->next();
270  continue;
271  }
272  if (tok->str() == ")" && !isUnevaluated(tok)) {
273  if (Token* top = getTopFunction(tok->link())) {
274  if (!updateRecursive(top))
275  break;
277  if (next && precedes(next, tok))
278  tok = next->next();
279  }
280  continue;
281  }
282  if (tok->str() == "}") {
283  Token* condTok = getCondTokFromEnd(tok);
284  if (!condTok)
285  break;
286  Analyzer::Action condAction = analyzeRecursive(condTok);
287  const bool inLoop = condTok->astTop() && Token::Match(condTok->astTop()->previous(), "for|while (");
288  // Evaluate condition of for and while loops first
289  if (inLoop) {
290  if (Token::findmatch(tok->link(), "goto|break", tok))
291  break;
292  if (condAction.isModified())
293  break;
294  valueFlowGenericForward(condTok, analyzer, tokenlist, errorLogger, settings);
295  }
296  Token* thenEnd;
297  const bool hasElse = Token::simpleMatch(tok->link()->tokAt(-2), "} else {");
298  if (hasElse) {
299  thenEnd = tok->link()->tokAt(-2);
300  } else {
301  thenEnd = tok;
302  }
303 
304  Analyzer::Action thenAction = analyzeRange(thenEnd->link(), thenEnd);
306  if (hasElse) {
307  elseAction = analyzeRange(tok->link(), tok);
308  }
309  if (thenAction.isModified() && inLoop)
310  break;
311  if (thenAction.isModified() && !elseAction.isModified())
312  analyzer->assume(condTok, hasElse);
313  else if (elseAction.isModified() && !thenAction.isModified())
314  analyzer->assume(condTok, !hasElse);
315  // Bail if one of the branches are read to avoid FPs due to over constraints
316  else if (thenAction.isIdempotent() || elseAction.isIdempotent() || thenAction.isRead() ||
317  elseAction.isRead())
318  break;
319  if (thenAction.isInvalid() || elseAction.isInvalid())
320  break;
321 
322  if (!thenAction.isModified() && !elseAction.isModified())
323  valueFlowGenericForward(condTok, analyzer, tokenlist, errorLogger, settings);
324  else if (condAction.isRead())
325  break;
326  // If the condition modifies the variable then bail
327  if (condAction.isModified())
328  break;
329  tok = condTok->astTop()->previous();
330  continue;
331  }
332  if (tok->str() == "{") {
333  if (tok->previous() &&
334  (Token::simpleMatch(tok->previous(), "do") ||
335  (tok->strAt(-1) == ")" && Token::Match(tok->linkAt(-1)->previous(), "for|while (")))) {
336  Analyzer::Action action = analyzeRange(tok, tok->link());
337  if (action.isModified())
338  break;
339  }
340  Token* condTok = getCondTokFromEnd(tok->link());
341  if (condTok) {
342  Analyzer::Result r = valueFlowGenericForward(condTok, analyzer, tokenlist, errorLogger, settings);
343  if (r.action.isModified())
344  break;
345  }
346  if (Token::simpleMatch(tok->tokAt(-2), "} else {"))
347  tok = tok->linkAt(-2);
348  if (Token::simpleMatch(tok->previous(), ") {"))
349  tok = tok->previous()->link();
350  continue;
351  }
352  if (Token* next = isUnevaluated(tok)) {
353  tok = next;
354  continue;
355  }
356  if (Token* parent = isDeadCode(tok)) {
357  tok = parent;
358  continue;
359  }
360  if (tok->str() == "case") {
361  const Scope* scope = tok->scope();
362  while (scope && scope->type != Scope::eSwitch)
363  scope = scope->nestedIn;
364  if (!scope || scope->type != Scope::eSwitch)
365  break;
366  tok = tok->tokAt(scope->bodyStart->index() - tok->index() - 1);
367  continue;
368  }
369  if (!update(tok))
370  break;
371  }
372  }
373 
374  static Token* assignExpr(Token* tok) {
375  if (Token::Match(tok, ")|}"))
376  tok = tok->link();
377  while (tok->astParent() && (astIsRHS(tok) || !tok->astParent()->isBinaryOp())) {
378  if (tok->astParent()->isAssignmentOp())
379  return tok->astParent();
380  tok = tok->astParent();
381  }
382  return nullptr;
383  }
384 
385  static Token* isUnevaluated(Token* tok) {
386  if (Token::Match(tok, ")|>") && tok->link()) {
387  Token* start = tok->link();
388  if (::isUnevaluated(start->previous()))
389  return start->previous();
390  if (Token::simpleMatch(start, "<"))
391  return start;
392  }
393  return nullptr;
394  }
395  };
396 }
397 
398 void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const TokenList& tokenlist, ErrorLogger& errorLogger, const Settings& settings)
399 {
400  if (a->invalid())
401  return;
402  ReverseTraversal rt{a, tokenlist, errorLogger, settings};
403  rt.traverse(start, end);
404 }
bool precedes(const Token *tok1, const Token *tok2)
If tok2 comes after tok1.
Definition: astutils.cpp:987
const Token * getTokenArgumentFunction(const Token *tok, int &argn)
Return the token to the function and the argument number.
Definition: astutils.cpp:2355
bool astIsLHS(const Token *tok)
Definition: astutils.cpp:784
Token * getCondTokFromEnd(Token *endBlock)
Definition: astutils.cpp:882
bool astIsRHS(const Token *tok)
Definition: astutils.cpp:797
const Token * nextAfterAstRightmostLeaf(const Token *tok)
Definition: astutils.cpp:548
const Token * previousBeforeAstLeftmostLeaf(const Token *tok)
Definition: astutils.cpp:514
bool isConstExpression(const Token *tok, const Library &library)
Definition: astutils.cpp:2044
bool isUnevaluated(const Token *tok)
Definition: astutils.cpp:3591
bool succeeds(const Token *tok1, const Token *tok2)
If tok1 comes after tok2.
Definition: astutils.cpp:999
void visitAstNodes(T *ast, const TFunc &visitor)
Visit AST nodes recursively.
Definition: astutils.h:54
This is an interface, which the class responsible of error logging should implement.
Definition: errorlogger.h:214
const Token * constructorMemberInitialization() const
bool isConstructor() const
ScopeType type
const Scope * nestedIn
const Token * bodyStart
'{' token
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:95
Library library
Library.
Definition: settings.h:237
The token list that the TokenList generates is a linked-list of this class.
Definition: token.h:150
void str(T &&s)
Definition: token.h:179
nonneg int index() const
Definition: token.h:1247
Token * astTop()
Definition: token.h:1416
static bool Match(const Token *tok, const char pattern[], nonneg int varid=0)
Match given token (or list of tokens) to a pattern list.
Definition: token.cpp:722
static const Token * findmatch(const Token *const startTok, const char pattern[], const nonneg int varId=0)
Definition: token.cpp:1099
const std::string & strAt(int index) const
Definition: token.cpp:457
void astOperand1(Token *tok)
Definition: token.cpp:1490
const Token * tokAt(int index) const
Definition: token.cpp:427
void astOperand2(Token *tok)
Definition: token.cpp:1502
void scope(const Scope *s)
Associate this token with given scope.
Definition: token.h:1042
void link(Token *linkToToken)
Create link to given token.
Definition: token.h:1015
const Token * linkAt(int index) const
Definition: token.cpp:447
Token * previous()
Definition: token.h:862
bool isAssignmentOp() const
Definition: token.h:401
Token * next()
Definition: token.h:830
static bool simpleMatch(const Token *tok, const char(&pattern)[count])
Match given token (or list of tokens) to a pattern list.
Definition: token.h:252
void astParent(Token *tok)
Definition: token.cpp:1471
Analyzer::Result valueFlowGenericForward(Token *start, const Token *end, const ValuePtr< Analyzer > &a, const TokenList &tokenList, ErrorLogger &errorLogger, const Settings &settings)
static const Token * assignExpr(const Token *tok)
void valueFlowGenericReverse(Token *start, const Token *end, const ValuePtr< Analyzer > &a, const TokenList &tokenlist, ErrorLogger &errorLogger, const Settings &settings)
bool isNone() const
Definition: analyzer.h:83
bool matches() const
Definition: analyzer.h:107
bool isInvalid() const
Definition: analyzer.h:75
bool isInconclusive() const
Definition: analyzer.h:79
bool isIdempotent() const
Definition: analyzer.h:91
bool isModified() const
Definition: analyzer.h:87
bool isRead() const
Definition: analyzer.h:67
Simple container to be thrown when internal error is detected.
Definition: errortypes.h:36