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  static Token* jumpToStart(Token* tok)
110  {
111  if (Token::simpleMatch(tok->tokAt(-2), "} else {"))
112  tok = tok->linkAt(-2);
113  if (Token::simpleMatch(tok->previous(), ") {"))
114  return tok->previous()->link();
115  if (Token::simpleMatch(tok->previous(), "do {"))
116  return tok->previous();
117  return tok;
118  }
119 
120  bool updateRecursive(Token* start) {
121  bool continueB = true;
122  visitAstNodes(start, [&](Token* tok) {
123  const Token* parent = tok->astParent();
124  while (Token::simpleMatch(parent, ":"))
125  parent = parent->astParent();
126  if (isUnevaluated(tok) || isDeadCode(tok, parent))
127  return ChildrenToVisit::none;
128  continueB &= update(tok);
129  if (continueB)
131  return ChildrenToVisit::done;
132  });
133  return continueB;
134  }
135 
136  Analyzer::Action analyzeRecursive(const Token* start) const {
138  visitAstNodes(start, [&](const Token* tok) {
139  result |= analyzer->analyze(tok, Analyzer::Direction::Reverse);
140  if (result.isModified())
141  return ChildrenToVisit::done;
143  });
144  return result;
145  }
146 
147  Analyzer::Action analyzeRange(const Token* start, const Token* end) const {
149  for (const Token* tok = start; tok && tok != end; tok = tok->next()) {
150  Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse);
151  if (action.isModified())
152  return action;
153  result |= action;
154  }
155  return result;
156  }
157 
158  Token* isDeadCode(Token* tok, const Token* end = nullptr) const {
159  int opSide = 0;
160  for (; tok && tok->astParent(); tok = tok->astParent()) {
161  if (tok == end)
162  break;
163  Token* parent = tok->astParent();
164  if (Token::simpleMatch(parent, ":")) {
165  if (astIsLHS(tok))
166  opSide = 1;
167  else if (astIsRHS(tok))
168  opSide = 2;
169  else
170  opSide = 0;
171  }
172  if (tok != parent->astOperand2())
173  continue;
174  if (Token::simpleMatch(parent, ":"))
175  parent = parent->astParent();
176  if (!Token::Match(parent, "%oror%|&&|?"))
177  continue;
178  const Token* condTok = parent->astOperand1();
179  if (!condTok)
180  continue;
181  bool checkThen, checkElse;
182  std::tie(checkThen, checkElse) = evalCond(condTok);
183 
184  if (parent->str() == "?") {
185  if (checkElse && opSide == 1)
186  return parent;
187  if (checkThen && opSide == 2)
188  return parent;
189  }
190  if (!checkThen && parent->str() == "&&")
191  return parent;
192  if (!checkElse && parent->str() == "||")
193  return parent;
194  }
195  return nullptr;
196  }
197 
198  void traverse(Token* start, const Token* end = nullptr) {
199  if (start == end)
200  return;
201  std::size_t i = start->index();
202  for (Token* tok = start->previous(); succeeds(tok, end); tok = tok->previous()) {
203  if (tok->index() >= i)
204  throw InternalError(tok, "Cyclic reverse analysis.");
205  i = tok->index();
206  if (tok == start || (tok->str() == "{" && (tok->scope()->type == Scope::ScopeType::eFunction ||
207  tok->scope()->type == Scope::ScopeType::eLambda))) {
208  const Function* f = tok->scope()->function;
209  if (f && f->isConstructor()) {
210  if (const Token* initList = f->constructorMemberInitialization())
211  traverse(tok->previous(), tok->tokAt(initList->index() - tok->index()));
212  }
213  break;
214  }
215  if (Token::Match(tok, "return|break|continue"))
216  break;
217  if (Token::Match(tok, "%name% :"))
218  break;
219  if (Token::simpleMatch(tok, ":"))
220  continue;
221  // Evaluate LHS of assignment before RHS
222  if (Token* assignTok = assignExpr(tok)) {
223  // If assignTok has broken ast then stop
224  if (!assignTok->astOperand1() || !assignTok->astOperand2())
225  break;
226  Token* assignTop = assignTok;
227  bool continueB = true;
228  while (assignTop->isAssignmentOp()) {
229  if (!Token::Match(assignTop->astOperand1(), "%assign%")) {
230  continueB &= updateRecursive(assignTop->astOperand1());
231  }
232  if (!assignTop->astParent())
233  break;
234  assignTop = assignTop->astParent();
235  }
236  // Is assignment in dead code
237  if (Token* parent = isDeadCode(assignTok)) {
238  tok = parent;
239  continue;
240  }
241  // Simple assign
242  if (assignTok->str() == "=" && (assignTok->astParent() == assignTop || assignTok == assignTop)) {
243  Analyzer::Action rhsAction =
244  analyzer->analyze(assignTok->astOperand2(), Analyzer::Direction::Reverse);
245  Analyzer::Action lhsAction =
246  analyzer->analyze(assignTok->astOperand1(), Analyzer::Direction::Reverse);
247  // Assignment from
248  if (rhsAction.isRead() && !lhsAction.isInvalid() && assignTok->astOperand1()->exprId() > 0) {
249  const std::string info = "Assignment from '" + assignTok->expressionString() + "'";
250  ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand1(), info);
251  if (a) {
252  valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()),
253  assignTok->astOperand2()->scope()->bodyEnd,
254  a,
255  tokenlist,
256  errorLogger,
257  settings);
258  }
259  // Assignment to
260  } else if (lhsAction.matches() && !assignTok->astOperand2()->hasKnownIntValue() &&
261  assignTok->astOperand2()->exprId() > 0 &&
262  isConstExpression(assignTok->astOperand2(), settings.library)) {
263  const std::string info = "Assignment to '" + assignTok->expressionString() + "'";
264  ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand2(), info);
265  if (a) {
266  valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()),
267  assignTok->astOperand2()->scope()->bodyEnd,
268  a,
269  tokenlist,
270  errorLogger,
271  settings);
272  valueFlowGenericReverse(assignTok->astOperand1()->previous(), end, a, tokenlist, errorLogger, settings);
273  }
274  }
275  }
276  if (!continueB)
277  break;
278  if (!updateRecursive(assignTop->astOperand2()))
279  break;
280  tok = previousBeforeAstLeftmostLeaf(assignTop)->next();
281  continue;
282  }
283  if (tok->str() == ")" && !isUnevaluated(tok)) {
284  if (Token* top = getTopFunction(tok->link())) {
285  if (!updateRecursive(top))
286  break;
288  if (next && precedes(next, tok))
289  tok = next->next();
290  }
291  continue;
292  }
293  if (tok->str() == "}") {
294  Token* condTok = getCondTokFromEnd(tok);
295  if (!condTok)
296  break;
297  Analyzer::Action condAction = analyzeRecursive(condTok);
298  const bool inLoop = condTok->astTop() && Token::Match(condTok->astTop()->previous(), "for|while (");
299  // Evaluate condition of for and while loops first
300  if (inLoop) {
301  if (Token::findmatch(tok->link(), "goto|break", tok))
302  break;
303  if (condAction.isModified())
304  break;
305  valueFlowGenericForward(condTok, analyzer, tokenlist, errorLogger, settings);
306  }
307  Token* thenEnd;
308  const bool hasElse = Token::simpleMatch(tok->link()->tokAt(-2), "} else {");
309  if (hasElse) {
310  thenEnd = tok->link()->tokAt(-2);
311  } else {
312  thenEnd = tok;
313  }
314 
315  Analyzer::Action thenAction = analyzeRange(thenEnd->link(), thenEnd);
317  if (hasElse) {
318  elseAction = analyzeRange(tok->link(), tok);
319  }
320  if (thenAction.isModified() && inLoop)
321  break;
322  if (thenAction.isModified() && !elseAction.isModified())
323  analyzer->assume(condTok, hasElse);
324  else if (elseAction.isModified() && !thenAction.isModified())
325  analyzer->assume(condTok, !hasElse);
326  // Bail if one of the branches are read to avoid FPs due to over constraints
327  else if (thenAction.isIdempotent() || elseAction.isIdempotent() || thenAction.isRead() ||
328  elseAction.isRead())
329  break;
330  if (thenAction.isInvalid() || elseAction.isInvalid())
331  break;
332 
333  if (!thenAction.isModified() && !elseAction.isModified())
334  valueFlowGenericForward(condTok, analyzer, tokenlist, errorLogger, settings);
335  else if (condAction.isRead())
336  break;
337  // If the condition modifies the variable then bail
338  if (condAction.isModified())
339  break;
340  tok = jumpToStart(tok->link());
341  continue;
342  }
343  if (tok->str() == "{") {
344  if (tok->previous() &&
345  (Token::simpleMatch(tok->previous(), "do") ||
346  (tok->strAt(-1) == ")" && Token::Match(tok->linkAt(-1)->previous(), "for|while (")))) {
347  Analyzer::Action action = analyzeRange(tok, tok->link());
348  if (action.isModified())
349  break;
350  }
351  Token* condTok = getCondTokFromEnd(tok->link());
352  if (condTok) {
353  Analyzer::Result r = valueFlowGenericForward(condTok, analyzer, tokenlist, errorLogger, settings);
354  if (r.action.isModified())
355  break;
356  }
357  tok = jumpToStart(tok);
358  continue;
359  }
360  if (Token* next = isUnevaluated(tok)) {
361  tok = next;
362  continue;
363  }
364  if (Token* parent = isDeadCode(tok)) {
365  tok = parent;
366  continue;
367  }
368  if (tok->str() == "case") {
369  const Scope* scope = tok->scope();
370  while (scope && scope->type != Scope::eSwitch)
371  scope = scope->nestedIn;
372  if (!scope || scope->type != Scope::eSwitch)
373  break;
374  tok = tok->tokAt(scope->bodyStart->index() - tok->index() - 1);
375  continue;
376  }
377  if (!update(tok))
378  break;
379  }
380  }
381 
382  static Token* assignExpr(Token* tok) {
383  if (Token::Match(tok, ")|}"))
384  tok = tok->link();
385  while (tok->astParent() && (astIsRHS(tok) || !tok->astParent()->isBinaryOp())) {
386  if (tok->astParent()->isAssignmentOp())
387  return tok->astParent();
388  tok = tok->astParent();
389  }
390  return nullptr;
391  }
392 
393  static Token* isUnevaluated(Token* tok) {
394  if (Token::Match(tok, ")|>") && tok->link()) {
395  Token* start = tok->link();
396  if (::isUnevaluated(start->previous()))
397  return start->previous();
398  if (Token::simpleMatch(start, "<"))
399  return start;
400  }
401  return nullptr;
402  }
403  };
404 }
405 
406 void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const TokenList& tokenlist, ErrorLogger& errorLogger, const Settings& settings)
407 {
408  if (a->invalid())
409  return;
410  ReverseTraversal rt{a, tokenlist, errorLogger, settings};
411  rt.traverse(start, end);
412 }
bool precedes(const Token *tok1, const Token *tok2)
If tok2 comes after tok1.
Definition: astutils.cpp:994
const Token * getTokenArgumentFunction(const Token *tok, int &argn)
Return the token to the function and the argument number.
Definition: astutils.cpp:2360
bool astIsLHS(const Token *tok)
Definition: astutils.cpp:784
Token * getCondTokFromEnd(Token *endBlock)
Definition: astutils.cpp:889
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:2049
bool isUnevaluated(const Token *tok)
Definition: astutils.cpp:3612
bool succeeds(const Token *tok1, const Token *tok2)
If tok1 comes after tok2.
Definition: astutils.cpp:1006
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:688
static const Token * findmatch(const Token *const startTok, const char pattern[], const nonneg int varId=0)
Definition: token.cpp:1065
const std::string & strAt(int index) const
Definition: token.cpp:423
void astOperand1(Token *tok)
Definition: token.cpp:1456
const Token * tokAt(int index) const
Definition: token.cpp:393
void astOperand2(Token *tok)
Definition: token.cpp:1468
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:413
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:1437
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