Cppcheck
checkuninitvar.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 
20 //---------------------------------------------------------------------------
21 #include "checkuninitvar.h"
22 
23 #include "astutils.h"
24 #include "ctu.h"
25 #include "errorlogger.h"
26 #include "library.h"
27 #include "mathlib.h"
28 #include "settings.h"
29 #include "symboldatabase.h"
30 #include "token.h"
31 #include "tokenize.h"
32 
33 #include "checknullpointer.h" // CheckNullPointer::isPointerDeref
34 
35 #include <algorithm>
36 #include <cassert>
37 #include <functional>
38 #include <initializer_list>
39 #include <list>
40 #include <map>
41 #include <unordered_set>
42 #include <vector>
43 
44 
45 namespace tinyxml2 {
46  class XMLElement;
47 }
48 
49 //---------------------------------------------------------------------------
50 
51 // CWE ids used:
53 
54 // Register this check class (by creating a static instance of it)
55 namespace {
56  CheckUninitVar instance;
57 }
58 
59 //---------------------------------------------------------------------------
60 
61 // get ast parent, skip possible address-of and casts
62 static const Token *getAstParentSkipPossibleCastAndAddressOf(const Token *vartok, bool *unknown)
63 {
64  if (unknown)
65  *unknown = false;
66  if (!vartok)
67  return nullptr;
68  const Token *parent = vartok->astParent();
69  while (Token::Match(parent, ".|::"))
70  parent = parent->astParent();
71  if (!parent)
72  return nullptr;
73  if (parent->isUnaryOp("&"))
74  parent = parent->astParent();
75  else if (parent->str() == "&" && vartok == parent->astOperand2() && Token::Match(parent->astOperand1()->previous(), "( %type% )")) {
76  parent = parent->astParent();
77  if (unknown)
78  *unknown = true;
79  }
80  while (parent && parent->isCast())
81  parent = parent->astParent();
82  return parent;
83 }
84 
85 static std::map<nonneg int, VariableValue> getVariableValues(const Token* tok) {
86  std::map<nonneg int, VariableValue> ret;
87  if (!tok || !tok->scope()->isExecutable())
88  return ret;
89  while (tok && tok->str() != "{") {
90  if (tok->str() == "}") {
91  if (tok->link()->isBinaryOp())
92  tok = tok->link()->previous();
93  else
94  break;
95  }
96  if (Token::Match(tok, "%var% =|{") && tok->next()->isBinaryOp() && tok->varId() && ret.count(tok->varId()) == 0) {
97  const Token* rhs = tok->next()->astOperand2();
98  if (rhs && rhs->hasKnownIntValue())
99  ret[tok->varId()] = VariableValue(rhs->getKnownIntValue());
100  }
101  tok = tok->previous();
102  }
103  return ret;
104 }
105 
106 bool CheckUninitVar::diag(const Token* tok)
107 {
108  if (!tok)
109  return true;
110  while (Token::Match(tok->astParent(), "*|&|."))
111  tok = tok->astParent();
112  return !mUninitDiags.insert(tok).second;
113 }
114 
116 {
117  logChecker("CheckUninitVar::check");
118 
119  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
120 
121  std::set<std::string> arrayTypeDefs;
122  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
123  if (Token::Match(tok, "%name% [") && tok->variable() && Token::Match(tok->variable()->typeStartToken(), "%type% %var% ;"))
124  arrayTypeDefs.insert(tok->variable()->typeStartToken()->str());
125  }
126 
127  // check every executable scope
128  for (const Scope &scope : symbolDatabase->scopeList) {
129  if (scope.isExecutable()) {
130  checkScope(&scope, arrayTypeDefs);
131  }
132  }
133 }
134 
135 void CheckUninitVar::checkScope(const Scope* scope, const std::set<std::string> &arrayTypeDefs)
136 {
137  for (const Variable &var : scope->varlist) {
138  if ((mTokenizer->isCPP() && var.type() && !var.isPointer() && var.type()->needInitialization != Type::NeedInitialization::True) ||
139  var.isStatic() || var.isExtern() || var.isReference())
140  continue;
141 
142  // don't warn for try/catch exception variable
143  if (var.isThrow())
144  continue;
145 
146  if (Token::Match(var.nameToken()->next(), "[({:]"))
147  continue;
148 
149  if (Token::Match(var.nameToken(), "%name% =")) { // Variable is initialized, but Rhs might be not
150  checkRhs(var.nameToken(), var, NO_ALLOC, 0U, emptyString);
151  continue;
152  }
153  if (Token::Match(var.nameToken(), "%name% ) (") && Token::simpleMatch(var.nameToken()->linkAt(2), ") =")) { // Function pointer is initialized, but Rhs might be not
154  checkRhs(var.nameToken()->linkAt(2)->next(), var, NO_ALLOC, 0U, emptyString);
155  continue;
156  }
157 
158  if (var.isArray() || var.isPointerToArray()) {
159  const Token *tok = var.nameToken()->next();
160  if (var.isPointerToArray())
161  tok = tok->next();
162  while (Token::simpleMatch(tok->link(), "] ["))
163  tok = tok->link()->next();
164  if (Token::Match(tok->link(), "] =|{|("))
165  continue;
166  }
167 
168  bool stdtype = var.typeStartToken()->isC() && arrayTypeDefs.find(var.typeStartToken()->str()) == arrayTypeDefs.end();
169  const Token* tok = var.typeStartToken();
170  for (; tok != var.nameToken() && tok->str() != "<"; tok = tok->next()) {
171  if (tok->isStandardType() || tok->isEnumType())
172  stdtype = true;
173  }
174  if (var.isArray() && !stdtype) { // std::array
175  if (!(var.isStlType() && Token::simpleMatch(var.typeStartToken(), "std :: array") && var.valueType() &&
177  continue;
178  }
179 
180  while (tok && tok->str() != ";")
181  tok = tok->next();
182  if (!tok)
183  continue;
184 
185  if (tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "for (") && Token::simpleMatch(tok->astParent()->link()->next(), "{") &&
186  checkLoopBody(tok->astParent()->link()->next(), var, var.isArray() ? ARRAY : NO_ALLOC, emptyString, true))
187  continue;
188 
189  if (var.isArray()) {
190  bool init = false;
191  for (const Token *parent = var.nameToken(); parent; parent = parent->astParent()) {
192  if (parent->str() == "=") {
193  init = true;
194  break;
195  }
196  }
197  if (!init) {
198  Alloc alloc = ARRAY;
199  std::map<nonneg int, VariableValue> variableValue = getVariableValues(var.typeStartToken());
200  checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue);
201  }
202  continue;
203  }
204  if (stdtype || var.isPointer()) {
205  Alloc alloc = NO_ALLOC;
206  std::map<nonneg int, VariableValue> variableValue = getVariableValues(var.typeStartToken());
207  checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue);
208  }
209  if (var.type())
210  checkStruct(tok, var);
211  }
212 
213  if (scope->function) {
214  for (const Variable &arg : scope->function->argumentList) {
215  if (arg.declarationId() && Token::Match(arg.typeStartToken(), "%type% * %name% [,)]")) {
216  // Treat the pointer as initialized until it is assigned by malloc
217  for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
218  if (!Token::Match(tok, "[;{}] %varid% =", arg.declarationId()))
219  continue;
220  const Token *allocFuncCallToken = findAllocFuncCallToken(tok->tokAt(2)->astOperand2(), mSettings->library);
221  if (!allocFuncCallToken)
222  continue;
223  const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(allocFuncCallToken);
224  if (!allocFunc || allocFunc->initData)
225  continue;
226 
227  if (arg.typeStartToken()->strAt(-1) == "struct" || (arg.type() && arg.type()->isStructType()))
228  checkStruct(tok, arg);
229  else if (arg.typeStartToken()->isStandardType() || arg.typeStartToken()->isEnumType()) {
230  Alloc alloc = NO_ALLOC;
231  std::map<nonneg int, VariableValue> variableValue;
232  checkScopeForVariable(tok->next(), arg, nullptr, nullptr, &alloc, emptyString, variableValue);
233  }
234  }
235  }
236  }
237  }
238 }
239 
240 void CheckUninitVar::checkStruct(const Token *tok, const Variable &structvar)
241 {
242  const Token *typeToken = structvar.typeStartToken();
243  while (Token::Match(typeToken, "%name% ::"))
244  typeToken = typeToken->tokAt(2);
245  const SymbolDatabase * symbolDatabase = mTokenizer->getSymbolDatabase();
246  for (const Scope *scope2 : symbolDatabase->classAndStructScopes) {
247  if (scope2->className == typeToken->str() && scope2->numConstructors == 0U) {
248  for (const Variable &var : scope2->varlist) {
249  if (var.isStatic() || var.hasDefault() || var.isArray() ||
250  (!mTokenizer->isC() && var.isClass() && (!var.type() || var.type()->needInitialization != Type::NeedInitialization::True)))
251  continue;
252 
253  // is the variable declared in a inner union?
254  bool innerunion = false;
255  for (const Scope *innerScope : scope2->nestedList) {
256  if (innerScope->type == Scope::eUnion) {
257  if (var.typeStartToken()->linenr() >= innerScope->bodyStart->linenr() &&
258  var.typeStartToken()->linenr() <= innerScope->bodyEnd->linenr()) {
259  innerunion = true;
260  break;
261  }
262  }
263  }
264 
265  if (!innerunion) {
266  Alloc alloc = NO_ALLOC;
267  const Token *tok2 = tok;
268  if (tok->str() == "}")
269  tok2 = tok2->next();
270  std::map<nonneg int, VariableValue> variableValue = getVariableValues(structvar.typeStartToken());
271  checkScopeForVariable(tok2, structvar, nullptr, nullptr, &alloc, var.name(), variableValue);
272  }
273  }
274  }
275  }
276 }
277 
279 {
280  v.notEqual = !v.notEqual;
281  return v;
282 }
283 static bool operator==(const VariableValue & v, MathLib::bigint i)
284 {
285  return v.notEqual ? (i != v.value) : (i == v.value);
286 }
287 static bool operator!=(const VariableValue & v, MathLib::bigint i)
288 {
289  return v.notEqual ? (i == v.value) : (i != v.value);
290 }
291 
292 static void conditionAlwaysTrueOrFalse(const Token *tok, const std::map<nonneg int, VariableValue> &variableValue, bool *alwaysTrue, bool *alwaysFalse)
293 {
294  if (!tok)
295  return;
296 
297  if (tok->hasKnownIntValue()) {
298  if (tok->getKnownIntValue() == 0)
299  *alwaysFalse = true;
300  else
301  *alwaysTrue = true;
302  return;
303  }
304 
305  if (tok->isName() || tok->str() == ".") {
306  while (tok && tok->str() == ".")
307  tok = tok->astOperand2();
308  const std::map<nonneg int, VariableValue>::const_iterator it = variableValue.find(tok ? tok->varId() : ~0U);
309  if (it != variableValue.end()) {
310  *alwaysTrue = (it->second != 0LL);
311  *alwaysFalse = (it->second == 0LL);
312  }
313  }
314 
315  else if (tok->isComparisonOp()) {
316  if (variableValue.empty()) {
317  return;
318  }
319 
320  const Token *vartok, *numtok;
321  if (tok->astOperand2() && tok->astOperand2()->isNumber()) {
322  vartok = tok->astOperand1();
323  numtok = tok->astOperand2();
324  } else if (tok->astOperand1() && tok->astOperand1()->isNumber()) {
325  vartok = tok->astOperand2();
326  numtok = tok->astOperand1();
327  } else {
328  return;
329  }
330 
331  while (vartok && vartok->str() == ".")
332  vartok = vartok->astOperand2();
333 
334  const std::map<nonneg int, VariableValue>::const_iterator it = variableValue.find(vartok ? vartok->varId() : ~0U);
335  if (it == variableValue.end())
336  return;
337 
338  if (tok->str() == "==")
339  *alwaysTrue = (it->second == MathLib::toBigNumber(numtok->str()));
340  else if (tok->str() == "!=")
341  *alwaysTrue = (it->second != MathLib::toBigNumber(numtok->str()));
342  else
343  return;
344  *alwaysFalse = !(*alwaysTrue);
345  }
346 
347  else if (tok->str() == "!") {
348  bool t=false,f=false;
349  conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t, &f);
350  if (t || f) {
351  *alwaysTrue = !t;
352  *alwaysFalse = !f;
353  }
354  }
355 
356  else if (tok->str() == "||") {
357  bool t1=false, f1=false;
358  conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t1, &f1);
359  bool t2=false, f2=false;
360  if (!t1)
361  conditionAlwaysTrueOrFalse(tok->astOperand2(), variableValue, &t2, &f2);
362  *alwaysTrue = (t1 || t2);
363  *alwaysFalse = (f1 && f2);
364  }
365 
366  else if (tok->str() == "&&") {
367  bool t1=false, f1=false;
368  conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t1, &f1);
369  bool t2=false, f2=false;
370  if (!f1)
371  conditionAlwaysTrueOrFalse(tok->astOperand2(), variableValue, &t2, &f2);
372  *alwaysTrue = (t1 && t2);
373  *alwaysFalse = (f1 || f2);
374  }
375 }
376 
377 static bool isVariableUsed(const Token *tok, const Variable& var)
378 {
379  if (!tok)
380  return false;
381  if (tok->str() == "&" && !tok->astOperand2())
382  return false;
383  if (tok->isConstOp())
384  return isVariableUsed(tok->astOperand1(),var) || isVariableUsed(tok->astOperand2(),var);
385  if (tok->varId() != var.declarationId())
386  return false;
387  if (!var.isArray())
388  return true;
389 
390  const Token *parent = tok->astParent();
391  while (Token::Match(parent, "[?:]"))
392  parent = parent->astParent();
393  // no dereference, then array is not "used"
394  if (!Token::Match(parent, "*|["))
395  return false;
396  const Token *parent2 = parent->astParent();
397  // TODO: handle function calls. There is a TODO assertion in TestUninitVar::uninitvar_arrays
398  return !parent2 || parent2->isConstOp() || (parent2->str() == "=" && parent2->astOperand2() == parent);
399 }
400 
401 bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn, Alloc* const alloc, const std::string &membervar, std::map<nonneg int, VariableValue>& variableValue)
402 {
403  const bool suppressErrors(possibleInit && *possibleInit); // Assume that this is a variable declaration, rather than a fundef
404  const bool printDebug = mSettings->debugwarnings;
405 
406  if (possibleInit)
407  *possibleInit = false;
408 
409  int number_of_if = 0;
410 
411  if (var.declarationId() == 0U)
412  return true;
413 
414  for (; tok; tok = tok->next()) {
415  // End of scope..
416  if (tok->str() == "}") {
417  if (number_of_if && possibleInit)
418  *possibleInit = true;
419 
420  // might be a noreturn function..
421  if (mTokenizer->isScopeNoReturn(tok)) {
422  if (noreturn)
423  *noreturn = true;
424  return false;
425  }
426 
427  break;
428  }
429 
430  // Unconditional inner scope, try, lambda, init list
431  if (tok->str() == "{" && Token::Match(tok->previous(), ",|;|{|}|]|try")) {
432  bool possibleInitInner = false;
433  if (checkScopeForVariable(tok->next(), var, &possibleInitInner, noreturn, alloc, membervar, variableValue))
434  return true;
435  tok = tok->link();
436  if (possibleInitInner) {
437  number_of_if = 1;
438  if (possibleInit)
439  *possibleInit = true;
440  }
441  continue;
442  }
443 
444  // track values of other variables..
445  if (Token::Match(tok->previous(), "[;{}.] %var% =")) {
446  if (tok->next()->astOperand2() && tok->next()->astOperand2()->hasKnownIntValue())
447  variableValue[tok->varId()] = VariableValue(tok->next()->astOperand2()->getKnownIntValue());
448  else if (Token::Match(tok->previous(), "[;{}] %var% = - %name% ;"))
449  variableValue[tok->varId()] = !VariableValue(0);
450  else
451  variableValue.erase(tok->varId());
452  }
453 
454  // Inner scope..
455  else if (Token::simpleMatch(tok, "if (")) {
456  bool alwaysTrue = false;
457  bool alwaysFalse = false;
458 
459  // Is variable assigned in condition?
460  if (!membervar.empty()) {
461  for (const Token *cond = tok->linkAt(1); cond != tok; cond = cond->previous()) {
462  if (cond->varId() == var.declarationId() && isMemberVariableAssignment(cond, membervar))
463  return true;
464  }
465  }
466 
467  conditionAlwaysTrueOrFalse(tok->next()->astOperand2(), variableValue, &alwaysTrue, &alwaysFalse);
468 
469  // initialization / usage in condition..
470  if (!alwaysTrue && checkIfForWhileHead(tok->next(), var, suppressErrors, bool(number_of_if == 0), *alloc, membervar))
471  return true;
472 
473  // checking if a not-zero variable is zero => bail out
474  nonneg int condVarId = 0;
475  VariableValue condVarValue(0);
476  const Token *condVarTok = nullptr;
477  if (alwaysFalse)
478  ;
479  else if (astIsVariableComparison(tok->next()->astOperand2(), "!=", "0", &condVarTok)) {
480  const std::map<nonneg int,VariableValue>::const_iterator it = variableValue.find(condVarTok->varId());
481  if (it != variableValue.cend() && it->second != 0)
482  return true; // this scope is not fully analysed => return true
483 
484  condVarId = condVarTok->varId();
485  condVarValue = !VariableValue(0);
486  } else if (Token::Match(tok->next()->astOperand2(), "==|!=")) {
487  const Token *condition = tok->next()->astOperand2();
488  const Token *lhs = condition->astOperand1();
489  const Token *rhs = condition->astOperand2();
490  const Token *vartok = (lhs && lhs->hasKnownIntValue()) ? rhs : lhs;
491  const Token *numtok = (lhs == vartok) ? rhs : lhs;
492  while (Token::simpleMatch(vartok, "."))
493  vartok = vartok->astOperand2();
494  if (vartok && vartok->varId() && numtok && numtok->hasKnownIntValue()) {
495  const std::map<nonneg int,VariableValue>::const_iterator it = variableValue.find(vartok->varId());
496  if (it != variableValue.cend() && it->second != numtok->getKnownIntValue())
497  return true; // this scope is not fully analysed => return true
498  condVarId = vartok->varId();
499  condVarValue = VariableValue(numtok->getKnownIntValue());
500  if (condition->str() == "!=")
501  condVarValue = !condVarValue;
502  }
503  }
504 
505  // goto the {
506  tok = tok->next()->link()->next();
507 
508  if (!tok)
509  break;
510  if (tok->str() == "{") {
511  bool possibleInitIf((!alwaysTrue && number_of_if > 0) || suppressErrors);
512  bool noreturnIf = false;
513  std::map<nonneg int, VariableValue> varValueIf(variableValue);
514  const bool initif = !alwaysFalse && checkScopeForVariable(tok->next(), var, &possibleInitIf, &noreturnIf, alloc, membervar, varValueIf);
515 
516  // bail out for such code:
517  // if (a) x=0; // conditional initialization
518  // if (b) return; // cppcheck doesn't know if b can be false when a is false.
519  // x++; // it's possible x is always initialized
520  if (!alwaysTrue && noreturnIf && number_of_if > 0) {
521  if (printDebug) {
522  std::string condition;
523  for (const Token *tok2 = tok->linkAt(-1); tok2 != tok; tok2 = tok2->next()) {
524  condition += tok2->str();
525  if (tok2->isName() && tok2->next()->isName())
526  condition += ' ';
527  }
528  reportError(tok, Severity::debug, "bailoutUninitVar", "bailout uninitialized variable checking for '" + var.name() + "'. can't determine if this condition can be false when previous condition is false: " + condition);
529  }
530  return true;
531  }
532 
533  if (alwaysTrue && (initif || noreturnIf))
534  return true;
535 
536  if (!alwaysFalse && !initif && !noreturnIf)
537  variableValue = varValueIf;
538 
539  if (initif && condVarId > 0)
540  variableValue[condVarId] = !condVarValue;
541 
542  // goto the }
543  tok = tok->link();
544 
545  if (!Token::simpleMatch(tok, "} else {")) {
546  if (initif || possibleInitIf) {
547  ++number_of_if;
548  if (number_of_if >= 2)
549  return true;
550  }
551  } else {
552  // goto the {
553  tok = tok->tokAt(2);
554 
555  bool possibleInitElse((!alwaysFalse && number_of_if > 0) || suppressErrors);
556  bool noreturnElse = false;
557  std::map<nonneg int, VariableValue> varValueElse(variableValue);
558  const bool initelse = !alwaysTrue && checkScopeForVariable(tok->next(), var, &possibleInitElse, &noreturnElse, alloc, membervar, varValueElse);
559 
560  if (!alwaysTrue && !initelse && !noreturnElse)
561  variableValue = varValueElse;
562 
563  if (initelse && condVarId > 0 && !noreturnIf && !noreturnElse)
564  variableValue[condVarId] = condVarValue;
565 
566  // goto the }
567  tok = tok->link();
568 
569  if ((alwaysFalse || initif || noreturnIf) &&
570  (alwaysTrue || initelse || noreturnElse))
571  return true;
572 
573  if (initif || initelse || possibleInitElse)
574  ++number_of_if;
575  if (!initif && !noreturnIf)
576  variableValue.insert(varValueIf.cbegin(), varValueIf.cend());
577  if (!initelse && !noreturnElse)
578  variableValue.insert(varValueElse.cbegin(), varValueElse.cend());
579  }
580  }
581  }
582 
583  // = { .. }
584  else if (Token::simpleMatch(tok, "= {") || (Token::Match(tok, "%name% {") && tok->variable() && tok == tok->variable()->nameToken())) {
585  // end token
586  const Token *end = tok->next()->link();
587 
588  // If address of variable is taken in the block then bail out
589  if (var.isPointer() || var.isArray()) {
590  if (Token::findmatch(tok->tokAt(2), "%varid%", end, var.declarationId()))
591  return true;
592  } else if (Token::findmatch(tok->tokAt(2), "& %varid%", end, var.declarationId())) {
593  return true;
594  }
595 
596  const Token *errorToken = nullptr;
597  visitAstNodes(tok->next(),
598  [&](const Token *child) {
599  if (child->isUnaryOp("&"))
600  return ChildrenToVisit::none;
601  if (child->str() == "," || child->str() == "{" || child->isConstOp())
602  return ChildrenToVisit::op1_and_op2;
603  if (child->str() == "." && Token::Match(child->astOperand1(), "%varid%", var.declarationId()) && child->astOperand2() && child->astOperand2()->str() == membervar) {
604  errorToken = child;
605  return ChildrenToVisit::done;
606  }
607  return ChildrenToVisit::none;
608  });
609 
610  if (errorToken) {
611  uninitStructMemberError(errorToken->astOperand2(), errorToken->astOperand1()->str() + "." + membervar);
612  return true;
613  }
614 
615  // Skip block
616  tok = end;
617  continue;
618  }
619 
620  // skip sizeof / offsetof
621  if (isUnevaluated(tok))
622  tok = tok->linkAt(1);
623 
624  // for/while..
625  else if (Token::Match(tok, "for|while (") || Token::simpleMatch(tok, "do {")) {
626  const bool forwhile = Token::Match(tok, "for|while (");
627 
628  // is variable initialized in for-head?
629  if (forwhile && checkIfForWhileHead(tok->next(), var, tok->str() == "for", false, *alloc, membervar))
630  return true;
631 
632  // goto the {
633  const Token *tok2 = forwhile ? tok->next()->link()->next() : tok->next();
634 
635  if (tok2 && tok2->str() == "{") {
636  const bool init = checkLoopBody(tok2, var, *alloc, membervar, (number_of_if > 0) || suppressErrors);
637 
638  // variable is initialized in the loop..
639  if (init)
640  return true;
641 
642  // is variable used in for-head?
643  bool initcond = false;
644  if (!suppressErrors) {
645  const Token *startCond = forwhile ? tok->next() : tok->next()->link()->tokAt(2);
646  initcond = checkIfForWhileHead(startCond, var, false, bool(number_of_if == 0), *alloc, membervar);
647  }
648 
649  // goto "}"
650  tok = tok2->link();
651 
652  // do-while => goto ")"
653  if (!forwhile) {
654  // Assert that the tokens are '} while ('
655  if (!Token::simpleMatch(tok, "} while (")) {
656  if (printDebug)
657  reportError(tok,Severity::debug,emptyString,"assertion failed '} while ('");
658  break;
659  }
660 
661  // Goto ')'
662  tok = tok->linkAt(2);
663 
664  if (!tok)
665  // bailout : invalid code / bad tokenizer
666  break;
667 
668  if (initcond)
669  // variable is initialized in while-condition
670  return true;
671  }
672  }
673  }
674 
675  // Unknown or unhandled inner scope
676  else if (Token::simpleMatch(tok, ") {") || (Token::Match(tok, "%name% {") && tok->str() != "try" && !(tok->variable() && tok == tok->variable()->nameToken()))) {
677  if (tok->str() == "struct" || tok->str() == "union") {
678  tok = tok->linkAt(1);
679  continue;
680  }
681  return true;
682  }
683 
684  // bailout if there is ({
685  if (Token::simpleMatch(tok, "( {")) {
686  return true;
687  }
688 
689  // bailout if there is assembler code or setjmp
690  if (Token::Match(tok, "asm|setjmp (")) {
691  return true;
692  }
693 
694  // bailout if there is a goto label
695  if (Token::Match(tok, "[;{}] %name% :")) {
696  return true;
697  }
698 
699  // bailout if there is a pointer to member
700  if (Token::Match(tok, "%varid% . *", var.declarationId())) {
701  return true;
702  }
703 
704  if (tok->str() == "?") {
705  if (!tok->astOperand2())
706  return true;
707  const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var);
708  const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var);
709  const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0);
710  if (err)
711  uninitvarError(tok, var.nameToken()->str(), *alloc);
712 
713  // Todo: skip expression if there is no error
714  return true;
715  }
716 
717  if (Token::Match(tok, "return|break|continue|throw|goto")) {
718  if (noreturn)
719  *noreturn = true;
720 
721  tok = tok->next();
722  while (tok && tok->str() != ";") {
723  // variable is seen..
724  if (tok->varId() == var.declarationId()) {
725  if (!membervar.empty()) {
726  if (!suppressErrors && Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar && Token::Match(tok->next()->astParent(), "%cop%|return|throw|?"))
727  uninitStructMemberError(tok, tok->str() + "." + membervar);
728  else if (tok->isCpp() && !suppressErrors && Token::Match(tok, "%name%") && Token::Match(tok->astParent(), "return|throw|?")) {
729  if (std::any_of(tok->values().cbegin(), tok->values().cend(), [](const ValueFlow::Value& v) {
730  return v.isUninitValue() && !v.isInconclusive();
731  }))
732  uninitStructMemberError(tok, tok->str() + "." + membervar);
733  }
734  }
735 
736  // Use variable
737  else if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc))
738  uninitvarError(tok, tok->str(), *alloc);
739 
740  return true;
741  }
742 
743  if (isUnevaluated(tok))
744  tok = tok->linkAt(1);
745 
746  else if (tok->str() == "?") {
747  if (!tok->astOperand2())
748  return true;
749  const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var);
750  const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var);
751  const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0);
752  if (err)
753  uninitvarError(tok, var.nameToken()->str(), *alloc);
754  return true;
755  }
756 
757  tok = tok->next();
758  }
759 
760  return (noreturn == nullptr);
761  }
762 
763  // variable is seen..
764  if (tok->varId() == var.declarationId()) {
765  // calling function that returns uninit data through pointer..
766  if (var.isPointer() && Token::simpleMatch(tok->next(), "=")) {
767  const Token *rhs = tok->next()->astOperand2();
768  while (rhs && rhs->isCast())
769  rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1();
770  if (rhs && Token::Match(rhs->previous(), "%name% (")) {
772  if (allocFunc && !allocFunc->initData) {
773  *alloc = NO_CTOR_CALL;
774  continue;
775  }
776  }
777  }
778  if (mTokenizer->isCPP() && var.isPointer() && (var.typeStartToken()->isStandardType() || var.typeStartToken()->isEnumType() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True)) && Token::simpleMatch(tok->next(), "= new")) {
779  *alloc = CTOR_CALL;
780 
781  // type has constructor(s)
782  if (var.typeScope() && var.typeScope()->numConstructors > 0)
783  return true;
784 
785  // standard or enum type: check if new initializes the allocated memory
786  if (var.typeStartToken()->isStandardType() || var.typeStartToken()->isEnumType()) {
787  // scalar new with initialization
788  if (Token::Match(tok->next(), "= new %type% ("))
789  return true;
790 
791  // array new
792  if (Token::Match(tok->next(), "= new %type% [") && Token::simpleMatch(tok->linkAt(4), "] ("))
793  return true;
794  }
795 
796  continue;
797  }
798 
799 
800  if (!membervar.empty()) {
801  if (isMemberVariableAssignment(tok, membervar)) {
802  checkRhs(tok, var, *alloc, number_of_if, membervar);
803  return true;
804  }
805 
806  if (isMemberVariableUsage(tok, var.isPointer(), *alloc, membervar)) {
807  uninitStructMemberError(tok, tok->str() + "." + membervar);
808  return true;
809  }
810 
811  if (Token::Match(tok->previous(), "[(,] %name% [,)]"))
812  return true;
813 
814  if (Token::Match(tok->previous(), "= %var% . %var% ;") && membervar == tok->strAt(2))
815  return true;
816 
817  } else {
818  // Use variable
819  if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc)) {
820  uninitvarError(tok, tok->str(), *alloc);
821  return true;
822  }
823 
824  const Token *parent = tok;
825  while (parent->astParent() && ((astIsLHS(parent) && parent->astParent()->str() == "[") || parent->astParent()->isUnaryOp("*"))) {
826  parent = parent->astParent();
827  if (parent->str() == "[") {
828  if (const Token *errorToken = checkExpr(parent->astOperand2(), var, *alloc, number_of_if==0)) {
829  if (!suppressErrors)
830  uninitvarError(errorToken, errorToken->expressionString(), *alloc);
831  return true;
832  }
833  }
834  }
835  if (Token::simpleMatch(parent->astParent(), "=") && astIsLHS(parent)) {
836  const Token *eq = parent->astParent();
837  if (const Token *errorToken = checkExpr(eq->astOperand2(), var, *alloc, number_of_if==0)) {
838  if (!suppressErrors)
839  uninitvarError(errorToken, errorToken->expressionString(), *alloc);
840  return true;
841  }
842  }
843 
844  // assume that variable is assigned
845  return true;
846  }
847  }
848  }
849 
850  return false;
851 }
852 
853 const Token* CheckUninitVar::checkExpr(const Token* tok, const Variable& var, const Alloc alloc, bool known, bool* bailout) const
854 {
855  if (!tok)
856  return nullptr;
857  if (isUnevaluated(tok->previous()))
858  return nullptr;
859 
860  if (tok->astOperand1()) {
861  bool bailout1 = false;
862  const Token *errorToken = checkExpr(tok->astOperand1(), var, alloc, known, &bailout1);
863  if (bailout && bailout1)
864  *bailout = true;
865  if (errorToken)
866  return errorToken;
867  if ((bailout1 || !known) && Token::Match(tok, "%oror%|&&|?"))
868  return nullptr;
869  }
870  if (tok->astOperand2())
871  return checkExpr(tok->astOperand2(), var, alloc, known, bailout);
872  if (tok->varId() == var.declarationId()) {
873  const Token *errorToken = isVariableUsage(tok, var.isPointer(), alloc);
874  if (errorToken)
875  return errorToken;
876  if (bailout)
877  *bailout = true;
878  }
879  return nullptr;
880 }
881 
882 bool CheckUninitVar::checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar)
883 {
884  const Token * const endpar = startparentheses->link();
885  if (Token::Match(startparentheses, "( ! %name% %oror%") && startparentheses->tokAt(2)->getValue(0))
886  suppressErrors = true;
887  for (const Token *tok = startparentheses->next(); tok && tok != endpar; tok = tok->next()) {
888  if (tok->varId() == var.declarationId()) {
889  if (Token::Match(tok, "%name% . %name%")) {
890  if (membervar.empty())
891  return true;
892  if (tok->strAt(2) == membervar) {
893  if (isMemberVariableAssignment(tok, membervar))
894  return true;
895 
896  if (!suppressErrors && isMemberVariableUsage(tok, var.isPointer(), alloc, membervar))
897  uninitStructMemberError(tok, tok->str() + "." + membervar);
898  }
899  continue;
900  }
901 
902  if (const Token *errorToken = isVariableUsage(tok, var.isPointer(), alloc)) {
903  if (suppressErrors)
904  continue;
905  uninitvarError(errorToken, errorToken->expressionString(), alloc);
906  }
907  return true;
908  }
909  // skip sizeof / offsetof
910  if (isUnevaluated(tok))
911  tok = tok->linkAt(1);
912  if ((!isuninit || !membervar.empty()) && tok->str() == "&&")
913  suppressErrors = true;
914  }
915  return false;
916 }
917 
918 /** recursively check loop, return error token */
919 const Token* CheckUninitVar::checkLoopBodyRecursive(const Token *start, const Variable& var, const Alloc alloc, const std::string &membervar, bool &bailout) const
920 {
921  assert(start->str() == "{");
922 
923  const Token *errorToken = nullptr;
924 
925  const Token *const end = start->link();
926  for (const Token *tok = start->next(); tok != end; tok = tok->next()) {
927  // skip sizeof / offsetof
928  if (isUnevaluated(tok)) {
929  tok = tok->linkAt(1);
930  continue;
931  }
932 
933  if (Token::Match(tok, "asm ( %str% ) ;")) {
934  bailout = true;
935  return nullptr;
936  }
937 
938  // for loop; skip third expression until loop body has been analyzed..
939  if (tok->str() == ";" && Token::simpleMatch(tok->astParent(), ";") && Token::simpleMatch(tok->astParent()->astParent(), "(")) {
940  const Token *top = tok->astParent()->astParent();
941  if (!Token::simpleMatch(top->previous(), "for (") || !Token::simpleMatch(top->link(), ") {"))
942  continue;
943  const Token *bodyStart = top->link()->next();
944  const Token *errorToken1 = checkLoopBodyRecursive(bodyStart, var, alloc, membervar, bailout);
945  if (!errorToken)
946  errorToken = errorToken1;
947  if (bailout)
948  return nullptr;
949  }
950  // for loop; skip loop body if there is third expression
951  if (Token::simpleMatch(tok, ") {") &&
952  Token::simpleMatch(tok->link()->previous(), "for (") &&
953  Token::simpleMatch(tok->link()->astOperand2(), ";") &&
954  Token::simpleMatch(tok->link()->astOperand2()->astOperand2(), ";")) {
955  tok = tok->linkAt(1);
956  }
957 
958  if (tok->str() == "{") {
959  // switch => bailout
960  if (tok->scope() && tok->scope()->type == Scope::ScopeType::eSwitch) {
961  bailout = true;
962  return nullptr;
963  }
964 
965  const Token *errorToken1 = checkLoopBodyRecursive(tok, var, alloc, membervar, bailout);
966  tok = tok->link();
967  if (Token::simpleMatch(tok, "} else {")) {
968  const Token *elseBody = tok->tokAt(2);
969  const Token *errorToken2 = checkLoopBodyRecursive(elseBody, var, alloc, membervar, bailout);
970  tok = elseBody->link();
971  if (errorToken1 && errorToken2)
972  return errorToken1;
973  if (errorToken2)
974  errorToken = errorToken2;
975  }
976  if (bailout)
977  return nullptr;
978  if (!errorToken)
979  errorToken = errorToken1;
980  }
981 
982  if (tok->varId() != var.declarationId())
983  continue;
984 
985  bool conditionalUsage = false;
986  for (const Token* parent = tok; parent; parent = parent->astParent()) {
987  if (Token::Match(parent->astParent(), "%oror%|&&|?") && astIsRHS(parent)) {
988  conditionalUsage = true;
989  break;
990  }
991  }
992 
993  if (!membervar.empty()) {
994  if (isMemberVariableAssignment(tok, membervar)) {
995  bool assign = true;
996  bool rhs = false;
997  // Used for tracking if an ")" is inner or outer
998  const Token *rpar = nullptr;
999  for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) {
1000  if (tok2->str() == "=")
1001  rhs = true;
1002 
1003  // Look at inner expressions but not outer expressions
1004  if (!rpar && tok2->str() == "(")
1005  rpar = tok2->link();
1006  else if (tok2->str() == ")") {
1007  // No rpar => this is an outer right parenthesis
1008  if (!rpar)
1009  break;
1010  if (rpar == tok2)
1011  rpar = nullptr;
1012  }
1013 
1014  if (tok2->str() == ";" || (!rpar && tok2->str() == ","))
1015  break;
1016  if (rhs && tok2->varId() == var.declarationId() && isMemberVariableUsage(tok2, var.isPointer(), alloc, membervar)) {
1017  assign = false;
1018  break;
1019  }
1020  }
1021  if (assign) {
1022  bailout = true;
1023  return nullptr;
1024  }
1025  }
1026  if (isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) {
1027  if (!conditionalUsage)
1028  return tok;
1029  if (!errorToken)
1030  errorToken = tok;
1031  } else if (Token::Match(tok->previous(), "[(,] %name% [,)]")) {
1032  bailout = true;
1033  return nullptr;
1034  }
1035  } else {
1036  if (const Token *errtok = isVariableUsage(tok, var.isPointer(), alloc)) {
1037  if (!conditionalUsage)
1038  return errtok;
1039  if (!errorToken)
1040  errorToken = errtok;
1041  } else if (tok->strAt(1) == "=") {
1042  bool varIsUsedInRhs = false;
1043  visitAstNodes(tok->next()->astOperand2(), [&](const Token * t) {
1044  if (!t)
1045  return ChildrenToVisit::none;
1046  if (t->varId() == var.declarationId()) {
1047  varIsUsedInRhs = true;
1048  return ChildrenToVisit::done;
1049  }
1050  if (isUnevaluated(t->previous()))
1051  return ChildrenToVisit::none;
1053  });
1054  if (!varIsUsedInRhs) {
1055  bailout = true;
1056  return nullptr;
1057  }
1058  } else {
1059  bailout = true;
1060  return nullptr;
1061  }
1062  }
1063  }
1064 
1065  return errorToken;
1066 }
1067 
1068 bool CheckUninitVar::checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors)
1069 {
1070  bool bailout = false;
1071  const Token *errorToken = checkLoopBodyRecursive(tok, var, alloc, membervar, bailout);
1072 
1073  if (!suppressErrors && !bailout && errorToken) {
1074  if (membervar.empty())
1075  uninitvarError(errorToken, errorToken->expressionString(), alloc);
1076  else
1077  uninitStructMemberError(errorToken, errorToken->expressionString() + "." + membervar);
1078  return true;
1079  }
1080 
1081  return bailout;
1082 }
1083 
1084 void CheckUninitVar::checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar)
1085 {
1086  bool rhs = false;
1087  int indent = 0;
1088  while (nullptr != (tok = tok->next())) {
1089  if (tok->str() == "=")
1090  rhs = true;
1091  else if (rhs && tok->varId() == var.declarationId()) {
1092  if (membervar.empty() && isVariableUsage(tok, var.isPointer(), alloc))
1093  uninitvarError(tok, tok->str(), alloc);
1094  else if (!membervar.empty() && isMemberVariableUsage(tok, var.isPointer(), alloc, membervar))
1095  uninitStructMemberError(tok, tok->str() + "." + membervar);
1096  else if (Token::Match(tok, "%var% ="))
1097  break;
1098  else if (Token::Match(tok->previous(), "[(,&]"))
1099  break;
1100  } else if (tok->str() == ";" || (indent==0 && tok->str() == ","))
1101  break;
1102  else if (tok->str() == "(")
1103  ++indent;
1104  else if (tok->str() == ")") {
1105  if (indent == 0)
1106  break;
1107  --indent;
1108  } else if (tok->str() == "?" && tok->astOperand2()) {
1109  const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var);
1110  const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var);
1111  const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0);
1112  if (err)
1113  uninitvarError(tok, var.nameToken()->str(), alloc);
1114  break;
1115  } else if (isUnevaluated(tok))
1116  tok = tok->linkAt(1);
1117 
1118  }
1119 }
1120 
1121 static bool astIsLhs(const Token *tok)
1122 {
1123  return tok && tok->astParent() && tok == tok->astParent()->astOperand1();
1124 }
1125 
1126 static bool astIsRhs(const Token *tok)
1127 {
1128  return tok && tok->astParent() && tok == tok->astParent()->astOperand2();
1129 }
1130 
1131 static bool isVoidCast(const Token *tok)
1132 {
1133  return Token::simpleMatch(tok, "(") && tok->isCast() && tok->valueType() && tok->valueType()->type == ValueType::Type::VOID && tok->valueType()->pointer == 0;
1134 }
1135 
1136 const Token* CheckUninitVar::isVariableUsage(const Token *vartok, const Library& library, bool pointer, Alloc alloc, int indirect)
1137 {
1138  const bool cpp = vartok->isCpp();
1139  const Token *valueExpr = vartok; // non-dereferenced , no address of value as variable
1140  while (Token::Match(valueExpr->astParent(), ".|::") && astIsRhs(valueExpr))
1141  valueExpr = valueExpr->astParent();
1142  // stuff we ignore..
1143  while (valueExpr->astParent()) {
1144  // *&x
1145  if (valueExpr->astParent()->isUnaryOp("&") && valueExpr->astParent()->astParent() && valueExpr->astParent()->astParent()->isUnaryOp("*"))
1146  valueExpr = valueExpr->astParent()->astParent();
1147  // (type &)x
1148  else if (valueExpr->astParent()->isCast() && valueExpr->astParent()->isUnaryOp("(") && Token::simpleMatch(valueExpr->astParent()->link()->previous(), "& )"))
1149  valueExpr = valueExpr->astParent();
1150  // designated initializers: {.x | { ... , .x
1151  else if (Token::simpleMatch(valueExpr->astParent(), ".") &&
1152  Token::Match(valueExpr->astParent()->previous(), ",|{"))
1153  valueExpr = valueExpr->astParent();
1154  else
1155  break;
1156  }
1157  if (!pointer) {
1158  if (Token::Match(vartok, "%name% [.(]") && vartok->variable() && !vartok->variable()->isPointer())
1159  return nullptr;
1160  while (Token::simpleMatch(valueExpr->astParent(), ".") && astIsLhs(valueExpr) && valueExpr->astParent()->valueType() && valueExpr->astParent()->valueType()->pointer == 0)
1161  valueExpr = valueExpr->astParent();
1162  }
1163  const Token *derefValue = nullptr; // dereferenced value expression
1164  if (alloc != NO_ALLOC) {
1165  const int arrayDim = (vartok->variable() && vartok->variable()->isArray()) ? vartok->variable()->dimensions().size() : 1;
1166  int deref = 0;
1167  derefValue = valueExpr;
1168  while (Token::Match(derefValue->astParent(), "+|-|*|[|.") ||
1169  (derefValue->astParent() && derefValue->astParent()->isCast()) ||
1170  (deref < arrayDim && Token::simpleMatch(derefValue->astParent(), "&") && derefValue->astParent()->isBinaryOp())) {
1171  const Token * const derefValueParent = derefValue->astParent();
1172  if (derefValueParent->str() == "*") {
1173  if (derefValueParent->isUnaryOp("*"))
1174  ++deref;
1175  else
1176  break;
1177  } else if (derefValueParent->str() == "[") {
1178  if (astIsLhs(derefValue))
1179  ++deref;
1180  else
1181  break;
1182  } else if (Token::Match(derefValueParent, "[+-]")) {
1183  if (deref >= arrayDim)
1184  break;
1185  } else if (derefValueParent->str() == ".")
1186  ++deref;
1187  derefValue = derefValueParent;
1188  if (deref < arrayDim)
1189  valueExpr = derefValue;
1190  }
1191  if (deref < arrayDim) {
1192  // todo compare deref with array dimensions
1193  derefValue = nullptr;
1194  }
1195  } else if (vartok->astParent() && vartok->astParent()->isUnaryOp("&")) {
1196  const Token *child = vartok->astParent();
1197  const Token *parent = child->astParent();
1198  while (parent && (parent->isCast() || parent->str() == "+")) {
1199  child = parent;
1200  parent = child->astParent();
1201  }
1202  if (parent && (parent->isUnaryOp("*") || (parent->str() == "[" && astIsLhs(child))))
1203  derefValue = parent;
1204  }
1205 
1206  if (!valueExpr->astParent())
1207  return nullptr;
1208 
1209  // FIXME handle address of!!
1210  if (derefValue && derefValue->astParent() && derefValue->astParent()->isUnaryOp("&"))
1211  return nullptr;
1212 
1213  // BAILOUT for binary & without parent
1214  if (Token::simpleMatch(valueExpr->astParent(), "&") && astIsRhs(valueExpr) && Token::Match(valueExpr->astParent()->tokAt(-3), "( %name% ) &"))
1215  return nullptr;
1216 
1217  // safe operations
1218  if (isVoidCast(valueExpr->astParent()))
1219  return nullptr;
1220  if (Token::simpleMatch(valueExpr->astParent(), ".")) {
1221  const Token *parent = valueExpr->astParent();
1222  while (Token::simpleMatch(parent, "."))
1223  parent = parent->astParent();
1224  if (isVoidCast(parent))
1225  return nullptr;
1226  }
1227  if (alloc != NO_ALLOC) {
1228  if (Token::Match(valueExpr->astParent(), "%comp%|%oror%|&&|?|!"))
1229  return nullptr;
1230  if (Token::Match(valueExpr->astParent(), "%or%|&") && valueExpr->astParent()->isBinaryOp())
1231  return nullptr;
1232  if (alloc == CTOR_CALL && derefValue && Token::simpleMatch(derefValue->astParent(), "(") && astIsLhs(derefValue))
1233  return nullptr;
1234  if (Token::simpleMatch(valueExpr->astParent(), "return"))
1235  return nullptr;
1236  }
1237 
1238  // Passing variable to function..
1239  if (Token::Match(valueExpr->astParent(), "[(,]") && (valueExpr->astParent()->str() == "," || astIsRhs(valueExpr))) {
1240  const Token *parent = valueExpr->astParent();
1241  while (Token::simpleMatch(parent, ","))
1242  parent = parent->astParent();
1243  if (Token::simpleMatch(parent, "{"))
1244  return valueExpr;
1245  const int use = isFunctionParUsage(valueExpr, library, pointer, alloc, indirect);
1246  return (use>0) ? valueExpr : nullptr;
1247  }
1248  if (derefValue && Token::Match(derefValue->astParent(), "[(,]") && (derefValue->astParent()->str() == "," || astIsRhs(derefValue))) {
1249  const int use = isFunctionParUsage(derefValue, library, false, NO_ALLOC, indirect);
1250  return (use>0) ? derefValue : nullptr;
1251  }
1252  if (valueExpr->astParent()->isUnaryOp("&")) {
1253  const Token *parent = valueExpr->astParent();
1254  if (Token::Match(parent->astParent(), "[(,]") && (parent->astParent()->str() == "," || astIsRhs(parent))) {
1255  const int use = isFunctionParUsage(valueExpr, library, pointer, alloc, indirect);
1256  return (use>0) ? valueExpr : nullptr;
1257  }
1258  return nullptr;
1259  }
1260 
1261  // Assignments;
1262  // * Is this LHS in assignment
1263  // * Passing address in RHS to pointer variable
1264  {
1265  const Token *tok = derefValue ? derefValue : valueExpr;
1266  if (alloc == NO_ALLOC) {
1267  while (tok->valueType() && tok->valueType()->pointer == 0 && Token::simpleMatch(tok->astParent(), "."))
1268  tok = tok->astParent();
1269  }
1270  if (Token::simpleMatch(tok->astParent(), "=")) {
1271  if (astIsLhs(tok)) {
1272  if (alloc == ARRAY || !derefValue || !derefValue->isUnaryOp("*") || !pointer)
1273  return nullptr;
1274  const Token* deref = derefValue->astOperand1();
1275  while (deref && deref->isCast())
1276  deref = deref->astOperand1();
1277  if (deref == vartok || Token::simpleMatch(deref, "+"))
1278  return nullptr;
1279  }
1280  if (alloc != NO_ALLOC && astIsRhs(valueExpr))
1281  return nullptr;
1282  }
1283  }
1284 
1285  // Initialize reference variable
1286  if (Token::Match((derefValue ? derefValue : vartok)->astParent(), "(|=") && astIsRhs(derefValue ? derefValue : vartok)) {
1287  const Token *rhstok = derefValue ? derefValue : vartok;
1288  const Token *lhstok = rhstok->astParent()->astOperand1();
1289  const Variable *lhsvar = lhstok ? lhstok->variable() : nullptr;
1290  if (lhsvar && lhsvar->isReference() && lhsvar->nameToken() == lhstok)
1291  return nullptr;
1292  }
1293 
1294  // range for loop
1295  if (Token::simpleMatch(valueExpr->astParent(), ":") &&
1296  valueExpr->astParent()->astParent() &&
1297  Token::simpleMatch(valueExpr->astParent()->astParent()->previous(), "for (")) {
1298  if (astIsLhs(valueExpr))
1299  return nullptr;
1300  // Taking value by reference?
1301  const Token *lhs = valueExpr->astParent()->astOperand1();
1302  if (lhs && lhs->variable() && lhs->variable()->nameToken() == lhs && lhs->variable()->isReference())
1303  return nullptr;
1304  }
1305 
1306  // Stream read/write
1307  // FIXME this code is a hack!!
1308  if (cpp && Token::Match(valueExpr->astParent(), "<<|>>")) {
1309  if (isLikelyStreamRead(vartok->previous()))
1310  return nullptr;
1311 
1312  if (const auto* vt = valueExpr->valueType()) {
1313  if (vt->type == ValueType::Type::VOID)
1314  return nullptr;
1315  // passing a char* to a stream will dereference it
1316  if ((alloc == CTOR_CALL || alloc == ARRAY) && vt->pointer && vt->type != ValueType::Type::CHAR && vt->type != ValueType::Type::WCHAR_T)
1317  return nullptr;
1318  }
1319  }
1320  if (astIsRhs(derefValue) && isLikelyStreamRead(derefValue->astParent()))
1321  return nullptr;
1322 
1323  // Assignment with overloaded &
1324  if (cpp && Token::simpleMatch(valueExpr->astParent(), "&") && astIsRhs(valueExpr)) {
1325  const Token *parent = valueExpr->astParent();
1326  while (Token::simpleMatch(parent, "&") && parent->isBinaryOp())
1327  parent = parent->astParent();
1328  if (!parent) {
1329  const Token *lhs = valueExpr->astParent();
1330  while (Token::simpleMatch(lhs, "&") && lhs->isBinaryOp())
1331  lhs = lhs->astOperand1();
1332  if (lhs && lhs->isName() && (!lhs->valueType() || lhs->valueType()->type <= ValueType::Type::CONTAINER))
1333  return nullptr; // <- possible assignment
1334  }
1335  }
1336 
1337  return derefValue ? derefValue : valueExpr;
1338 }
1339 
1340 const Token* CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const
1341 {
1342  return isVariableUsage(vartok, mSettings->library, pointer, alloc, indirect);
1343 }
1344 
1345 /***
1346  * Is function parameter "used" so a "usage of uninitialized variable" can
1347  * be written? If parameter is passed "by value" then it is "used". If it
1348  * is passed "by reference" then it is not necessarily "used".
1349  * @return -1 => unknown 0 => not used 1 => used
1350  */
1351 int CheckUninitVar::isFunctionParUsage(const Token *vartok, const Library& library, bool pointer, Alloc alloc, int indirect)
1352 {
1353  bool unknown = false;
1354  const Token *parent = getAstParentSkipPossibleCastAndAddressOf(vartok, &unknown);
1355  if (unknown || !Token::Match(parent, "[(,]"))
1356  return -1;
1357 
1358  // locate start parentheses in function call..
1359  int argumentNumber = 0;
1360  const Token *start = vartok;
1361  while (start && !Token::Match(start, "[;{}(]")) {
1362  if (start->str() == ")")
1363  start = start->link();
1364  else if (start->str() == ",")
1365  ++argumentNumber;
1366  start = start->previous();
1367  }
1368  if (!start)
1369  return -1;
1370 
1371  if (Token::simpleMatch(start->link(), ") {") && Token::Match(start->previous(), "if|for|while|switch"))
1372  return (!pointer || alloc == NO_ALLOC);
1373 
1374  // is this a function call?
1375  if (Token::Match(start->previous(), "%name% (")) {
1376  const bool address(vartok->previous()->str() == "&");
1377  const bool array(vartok->variable() && vartok->variable()->isArray());
1378  // check how function handle uninitialized data arguments..
1379  const Function *func = start->previous()->function();
1380  if (func) {
1381  const Variable *arg = func->getArgumentVar(argumentNumber);
1382  if (arg) {
1383  const Token *argStart = arg->typeStartToken();
1384  if (!address && !array && Token::Match(argStart, "%type% %name%| [,)]"))
1385  return 1;
1386  if (pointer && !address && alloc == NO_ALLOC && Token::Match(argStart, "%type% * %name% [,)]"))
1387  return 1;
1388  while (argStart->previous() && argStart->previous()->isName())
1389  argStart = argStart->previous();
1390  if (Token::Match(argStart, "const %type% & %name% [,)]")) {
1391  // If it's a record it's ok to pass a partially uninitialized struct.
1392  if (vartok->variable() && vartok->variable()->valueType() && vartok->variable()->valueType()->type == ValueType::Type::RECORD)
1393  return -1;
1394  return 1;
1395  }
1396  if ((pointer || address) && Token::Match(argStart, "const %type% %name% [") && Token::Match(argStart->linkAt(3), "] [,)]"))
1397  return 1;
1398  }
1399 
1400  } else if (Token::Match(start->previous(), "if|while|for")) {
1401  // control-flow statement reading the variable "by value"
1402  return alloc == NO_ALLOC;
1403  } else {
1404  const bool isnullbad = library.isnullargbad(start->previous(), argumentNumber + 1);
1405  if (indirect == 0 && pointer && !address && isnullbad && alloc == NO_ALLOC)
1406  return 1;
1407  bool hasIndirect = false;
1408  const bool isuninitbad = library.isuninitargbad(start->previous(), argumentNumber + 1, indirect, &hasIndirect);
1409  if (alloc != NO_ALLOC)
1410  return (isnullbad || hasIndirect) && isuninitbad;
1411  return isuninitbad && (!address || isnullbad);
1412  }
1413  }
1414 
1415  // unknown
1416  return -1;
1417 }
1418 
1419 int CheckUninitVar::isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const
1420 {
1421  return CheckUninitVar::isFunctionParUsage(vartok, mSettings->library, pointer, alloc, indirect);
1422 }
1423 
1424 bool CheckUninitVar::isMemberVariableAssignment(const Token *tok, const std::string &membervar) const
1425 {
1426  if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar) {
1427  if (Token::Match(tok->tokAt(3), "[=.[]"))
1428  return true;
1429  if (Token::Match(tok->tokAt(-2), "[(,=] &"))
1430  return true;
1431  if (isLikelyStreamRead(tok->previous()))
1432  return true;
1433  if ((tok->previous() && tok->previous()->isConstOp()) || Token::Match(tok->previous(), "[|="))
1434  ; // member variable usage
1435  else if (tok->tokAt(3)->isConstOp())
1436  ; // member variable usage
1437  else if (Token::Match(tok->previous(), "[(,] %name% . %name% [,)]") &&
1438  1 == isFunctionParUsage(tok, false, NO_ALLOC)) {
1439  return false;
1440  } else
1441  return true;
1442  } else if (tok->strAt(1) == "=")
1443  return true;
1444  else if (Token::Match(tok, "%var% . %name% (")) {
1445  const Token *ftok = tok->tokAt(2);
1446  if (!ftok->function() || !ftok->function()->isConst())
1447  // TODO: Try to determine if membervar is assigned in method
1448  return true;
1449  } else if (tok->strAt(-1) == "&") {
1450  if (Token::Match(tok->tokAt(-2), "[(,] & %name%")) {
1451  // locate start parentheses in function call..
1452  int argumentNumber = 0;
1453  const Token *ftok = tok;
1454  while (ftok && !Token::Match(ftok, "[;{}(]")) {
1455  if (ftok->str() == ")")
1456  ftok = ftok->link();
1457  else if (ftok->str() == ",")
1458  ++argumentNumber;
1459  ftok = ftok->previous();
1460  }
1461 
1462  // is this a function call?
1463  ftok = ftok ? ftok->previous() : nullptr;
1464  if (Token::Match(ftok, "%name% (")) {
1465  // check how function handle uninitialized data arguments..
1466  const Function *function = ftok->function();
1467 
1468  if (!function && mSettings) {
1469  // Function definition not seen, check if direction is specified in the library configuration
1470  const Library::ArgumentChecks::Direction argDirection = mSettings->library.getArgDirection(ftok, 1 + argumentNumber);
1471  if (argDirection == Library::ArgumentChecks::Direction::DIR_IN)
1472  return false;
1473  if (argDirection == Library::ArgumentChecks::Direction::DIR_OUT)
1474  return true;
1475  }
1476 
1477  const Variable *arg = function ? function->getArgumentVar(argumentNumber) : nullptr;
1478  const Token *argStart = arg ? arg->typeStartToken() : nullptr;
1479  while (argStart && argStart->previous() && argStart->previous()->isName())
1480  argStart = argStart->previous();
1481  if (Token::Match(argStart, "const struct| %type% * const| %name% [,)]"))
1482  return false;
1483  }
1484 
1485  else if (ftok && Token::simpleMatch(ftok->previous(), "= * ("))
1486  return false;
1487  }
1488  return true;
1489  }
1490  return false;
1491 }
1492 
1493 bool CheckUninitVar::isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const
1494 {
1495  if (Token::Match(tok->previous(), "[(,] %name% . %name% [,)]") &&
1496  tok->strAt(2) == membervar) {
1497  const int use = isFunctionParUsage(tok, isPointer, alloc);
1498  if (use == 1)
1499  return true;
1500  }
1501 
1502  if (isMemberVariableAssignment(tok, membervar))
1503  return false;
1504 
1505  if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar && !(tok->tokAt(-2)->variable() && tok->tokAt(-2)->variable()->isReference())) {
1506  const Token *parent = tok->next()->astParent();
1507  return !parent || !parent->isUnaryOp("&");
1508  }
1509  if (!isPointer && !Token::simpleMatch(tok->astParent(), ".") && Token::Match(tok->previous(), "[(,] %name% [,)]") && isVariableUsage(tok, isPointer, alloc))
1510  return true;
1511 
1512  if (!isPointer && Token::Match(tok->previous(), "= %name% ;")) {
1513  const Token* lhs = tok->previous()->astOperand1();
1514  return !(lhs && lhs->variable() && lhs->variable()->isReference() && lhs == lhs->variable()->nameToken());
1515  }
1516 
1517  // = *(&var);
1518  if (!isPointer &&
1519  Token::simpleMatch(tok->astParent(),"&") &&
1520  Token::simpleMatch(tok->astParent()->astParent(),"*") &&
1521  Token::Match(tok->astParent()->astParent()->astParent(), "= * (| &") &&
1522  tok->astParent()->astParent()->astParent()->astOperand2() == tok->astParent()->astParent())
1523  return true;
1524 
1525  // TODO: this used to be experimental - enable or remove see #5586
1526  if ((false) && // NOLINT(readability-simplify-boolean-expr)
1527  !isPointer &&
1528  Token::Match(tok->tokAt(-2), "[(,] & %name% [,)]") &&
1529  isVariableUsage(tok, isPointer, alloc))
1530  return true;
1531 
1532  return false;
1533 }
1534 
1535 void CheckUninitVar::uninitdataError(const Token *tok, const std::string &varname)
1536 {
1537  reportError(tok, Severity::error, "uninitdata", "$symbol:" + varname + "\nMemory is allocated but not initialized: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal);
1538 }
1539 
1540 void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname, ErrorPath errorPath)
1541 {
1542  if (diag(tok))
1543  return;
1544  errorPath.emplace_back(tok, "");
1545  reportError(errorPath,
1547  "legacyUninitvar",
1548  "$symbol:" + varname + "\nUninitialized variable: $symbol",
1551 }
1552 
1554 {
1555  if (!mSettings->isEnabled(&v))
1556  return;
1557  if (diag(tok))
1558  return;
1559  const Token* ltok = tok;
1560  if (tok && Token::simpleMatch(tok->astParent(), ".") && astIsRHS(tok))
1561  ltok = tok->astParent();
1562  const std::string& varname = ltok ? ltok->expressionString() : "x";
1563  ErrorPath errorPath = v.errorPath;
1564  errorPath.emplace_back(tok, "");
1565  auto severity = v.isKnown() ? Severity::error : Severity::warning;
1566  auto certainty = v.isInconclusive() ? Certainty::inconclusive : Certainty::normal;
1567  if (v.subexpressions.empty()) {
1568  reportError(errorPath,
1569  severity,
1570  "uninitvar",
1571  "$symbol:" + varname + "\nUninitialized variable: $symbol",
1573  certainty);
1574  return;
1575  }
1576  std::string vars = v.subexpressions.size() == 1 ? "variable: " : "variables: ";
1577  std::string prefix;
1578  for (const std::string& var : v.subexpressions) {
1579  vars += prefix + varname + "." + var;
1580  prefix = ", ";
1581  }
1582  reportError(errorPath,
1583  severity,
1584  "uninitvar",
1585  "$symbol:" + varname + "\nUninitialized " + vars,
1587  certainty);
1588 }
1589 
1590 void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string &membername)
1591 {
1592  reportError(tok,
1594  "uninitStructMember",
1595  "$symbol:" + membername + "\nUninitialized struct member: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal);
1596 }
1597 
1599 {
1600  logChecker("CheckUninitVar::valueFlowUninit");
1601 
1602  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1603 
1604  std::unordered_set<nonneg int> ids;
1605  for (const bool subfunction : {false, true}) {
1606  // check every executable scope
1607  for (const Scope* scope : symbolDatabase->functionScopes) {
1608  for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1609  if (isUnevaluated(tok)) {
1610  tok = tok->linkAt(1);
1611  continue;
1612  }
1613  if (ids.count(tok->exprId()) > 0)
1614  continue;
1615  if (!tok->variable() && !tok->isUnaryOp("*") && !tok->isUnaryOp("&"))
1616  continue;
1617  if (Token::Match(tok, "%name% ("))
1618  continue;
1619  const Token* parent = tok->astParent();
1620  while (Token::simpleMatch(parent, "."))
1621  parent = parent->astParent();
1622  if (parent && parent->isUnaryOp("&"))
1623  continue;
1624  if (isVoidCast(parent))
1625  continue;
1626  auto v = std::find_if(
1627  tok->values().cbegin(), tok->values().cend(), std::mem_fn(&ValueFlow::Value::isUninitValue));
1628  if (v == tok->values().cend())
1629  continue;
1630  if (v->tokvalue && ids.count(v->tokvalue->exprId()) > 0)
1631  continue;
1632  if (subfunction == (v->path == 0))
1633  continue;
1634  if (v->isInconclusive())
1635  continue;
1636  if (v->indirect > 1 || v->indirect < 0)
1637  continue;
1638  bool uninitderef = false;
1639  if (tok->variable()) {
1640  bool unknown;
1641  const bool isarray = tok->variable()->isArray();
1642  if (isarray && tok->variable()->isMember())
1643  continue; // Todo: this is a bailout
1644  const bool deref = CheckNullPointer::isPointerDeRef(tok, unknown, *mSettings);
1645  uninitderef = deref && v->indirect == 0;
1646  const bool isleaf = isLeafDot(tok) || uninitderef;
1647  if (!isleaf && Token::Match(tok->astParent(), ". %name%") && (tok->astParent()->next()->varId() || tok->astParent()->next()->isEnumerator()))
1648  continue;
1649  }
1650  const ExprUsage usage = getExprUsage(tok, v->indirect, *mSettings);
1651  if (usage == ExprUsage::NotUsed || usage == ExprUsage::Inconclusive)
1652  continue;
1653  if (!v->subexpressions.empty() && usage == ExprUsage::PassedByReference)
1654  continue;
1655  if (usage != ExprUsage::Used) {
1656  if (!(Token::Match(tok->astParent(), ". %name% (|[") && uninitderef) &&
1657  isVariableChanged(tok, v->indirect, *mSettings))
1658  continue;
1659  bool inconclusive = false;
1661  continue;
1662  }
1663  uninitvarError(tok, *v);
1664  ids.insert(tok->exprId());
1665  if (v->tokvalue)
1666  ids.insert(v->tokvalue->exprId());
1667  }
1668  }
1669  }
1670 }
1671 
1672 // NOLINTNEXTLINE(readability-non-const-parameter) - used as callback so we need to preserve the signature
1673 static bool isVariableUsage(const Settings &settings, const Token *vartok, MathLib::bigint *value)
1674 {
1675  (void)value;
1676  return CheckUninitVar::isVariableUsage(vartok, settings.library, true, CheckUninitVar::Alloc::ARRAY);
1677 }
1678 
1679 // a Clang-built executable will crash when using the anonymous MyFileInfo later on - so put it in a unique namespace for now
1680 // see https://trac.cppcheck.net/ticket/12108 for more details
1681 #ifdef __clang__
1682 inline namespace CheckUninitVar_internal
1683 #else
1684 namespace
1685 #endif
1686 {
1687  /* data for multifile checking */
1688  class MyFileInfo : public Check::FileInfo {
1689  public:
1690  /** function arguments that data are unconditionally read */
1691  std::list<CTU::FileInfo::UnsafeUsage> unsafeUsage;
1692 
1693  /** Convert data into xml string */
1694  std::string toString() const override
1695  {
1696  return CTU::toString(unsafeUsage);
1697  }
1698  };
1699 }
1700 
1701 Check::FileInfo *CheckUninitVar::getFileInfo(const Tokenizer &tokenizer, const Settings &settings) const
1702 {
1703  const std::list<CTU::FileInfo::UnsafeUsage> &unsafeUsage = CTU::getUnsafeUsage(tokenizer, settings, ::isVariableUsage);
1704  if (unsafeUsage.empty())
1705  return nullptr;
1706 
1707  auto *fileInfo = new MyFileInfo;
1708  fileInfo->unsafeUsage = unsafeUsage;
1709  return fileInfo;
1710 }
1711 
1712 Check::FileInfo * CheckUninitVar::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const
1713 {
1714  const std::list<CTU::FileInfo::UnsafeUsage> &unsafeUsage = CTU::loadUnsafeUsageListFromXml(xmlElement);
1715  if (unsafeUsage.empty())
1716  return nullptr;
1717 
1718  auto *fileInfo = new MyFileInfo;
1719  fileInfo->unsafeUsage = unsafeUsage;
1720  return fileInfo;
1721 }
1722 
1723 bool CheckUninitVar::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list<Check::FileInfo*> &fileInfo, const Settings& settings, ErrorLogger &errorLogger)
1724 {
1725  if (!ctu)
1726  return false;
1727  bool foundErrors = false;
1728  (void)settings; // This argument is unused
1729 
1730  const std::map<std::string, std::list<const CTU::FileInfo::CallBase *>> callsMap = ctu->getCallsMap();
1731 
1732  for (const Check::FileInfo* fi1 : fileInfo) {
1733  const MyFileInfo *fi = dynamic_cast<const MyFileInfo*>(fi1);
1734  if (!fi)
1735  continue;
1736  for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeUsage) {
1737  const CTU::FileInfo::FunctionCall *functionCall = nullptr;
1738 
1739  const std::list<ErrorMessage::FileLocation> &locationList =
1741  unsafeUsage,
1742  callsMap,
1743  "Using argument ARG",
1744  &functionCall,
1745  false);
1746  if (locationList.empty())
1747  continue;
1748 
1749  const ErrorMessage errmsg(locationList,
1750  emptyString,
1752  "Using argument " + unsafeUsage.myArgumentName + " that points at uninitialized variable " + functionCall->callArgumentExpression,
1753  "ctuuninitvar",
1756  errorLogger.reportErr(errmsg);
1757 
1758  foundErrors = true;
1759  }
1760  }
1761  return foundErrors;
1762 }
const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok)
Is given syntax tree a variable comparison against value.
Definition: astutils.cpp:351
bool isLikelyStreamRead(const Token *op)
do we see a likely write of rhs through overloaded operator s >> x; a & x;
Definition: astutils.cpp:3216
const Token * findAllocFuncCallToken(const Token *expr, const Library &library)
Find a allocation function call in expression, so result of expression is allocated memory/resource.
Definition: astutils.cpp:3480
bool astIsLHS(const Token *tok)
Definition: astutils.cpp:784
bool isLeafDot(const Token *tok)
Definition: astutils.cpp:3341
bool astIsRHS(const Token *tok)
Definition: astutils.cpp:797
ExprUsage getExprUsage(const Token *tok, int indirect, const Settings &settings)
Definition: astutils.cpp:3353
bool isVariableChangedByFunctionCall(const Token *tok, int indirect, nonneg int varid, const Settings &settings, bool *inconclusive)
Is variable changed by function call? In case the answer of the question is inconclusive,...
Definition: astutils.cpp:2258
bool isUnevaluated(const Token *tok)
Definition: astutils.cpp:3591
bool isVariableChanged(const Token *tok, int indirect, const Settings &settings, int depth)
Definition: astutils.cpp:2541
void visitAstNodes(T *ast, const TFunc &visitor)
Visit AST nodes recursively.
Definition: astutils.h:54
ExprUsage
Definition: astutils.h:439
@ PassedByReference
static bool operator!=(const VariableValue &v, MathLib::bigint i)
static bool operator==(const VariableValue &v, MathLib::bigint i)
static void conditionAlwaysTrueOrFalse(const Token *tok, const std::map< nonneg int, VariableValue > &variableValue, bool *alwaysTrue, bool *alwaysFalse)
static std::map< nonneg int, VariableValue > getVariableValues(const Token *tok)
static bool astIsRhs(const Token *tok)
static const CWE CWE_USE_OF_UNINITIALIZED_VARIABLE(457U)
static VariableValue operator!(VariableValue v)
static bool isVoidCast(const Token *tok)
static bool isVariableUsage(const Settings &settings, const Token *vartok, MathLib::bigint *value)
static bool isVariableUsed(const Token *tok, const Variable &var)
static bool astIsLhs(const Token *tok)
static const Token * getAstParentSkipPossibleCastAndAddressOf(const Token *vartok, bool *unknown)
std::string callArgumentExpression
Definition: ctu.h:98
static std::list< ErrorMessage::FileLocation > getErrorPath(InvalidValueType invalidValue, const UnsafeUsage &unsafeUsage, const std::map< std::string, std::list< const CallBase * >> &callsMap, const char info[], const FunctionCall **const functionCallPtr, bool warning)
Definition: ctu.cpp:554
std::map< std::string, std::list< const CallBase * > > getCallsMap() const
Definition: ctu.cpp:247
bool isPointerDeRef(const Token *tok, bool &unknown) const
Is there a pointer dereference? Everything that should result in a nullpointer dereference error mess...
Checking for uninitialized variables.
bool diag(const Token *tok)
void valueFlowUninit()
ValueFlow-based checking for uninitialized variables.
void check()
Check for uninitialized variables.
void checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar)
void uninitdataError(const Token *tok, const std::string &varname)
const Token * checkLoopBodyRecursive(const Token *start, const Variable &var, const Alloc alloc, const std::string &membervar, bool &bailout) const
recursively check loop, return error token
void uninitStructMemberError(const Token *tok, const std::string &membername)
bool isMemberVariableAssignment(const Token *tok, const std::string &membervar) const
std::set< const Token * > mUninitDiags
static int isFunctionParUsage(const Token *vartok, const Library &library, bool pointer, Alloc alloc, int indirect=0)
static const Token * isVariableUsage(const Token *vartok, const Library &library, bool pointer, Alloc alloc, int indirect=0)
bool checkIfForWhileHead(const Token *startparentheses, const Variable &var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar)
bool isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const
void checkStruct(const Token *tok, const Variable &structvar)
void uninitvarError(const Token *tok, const ValueFlow::Value &v)
bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list< Check::FileInfo * > &fileInfo, const Settings &settings, ErrorLogger &errorLogger) override
Analyse all file infos for all TU.
const Token * checkExpr(const Token *tok, const Variable &var, const Alloc alloc, bool known, bool *bailout=nullptr) const
void checkScope(const Scope *scope, const std::set< std::string > &arrayTypeDefs)
bool checkLoopBody(const Token *tok, const Variable &var, const Alloc alloc, const std::string &membervar, const bool suppressErrors)
Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const override
bool checkScopeForVariable(const Token *tok, const Variable &var, bool *const possibleInit, bool *const noreturn, Alloc *const alloc, const std::string &membervar, std::map< nonneg int, VariableValue > &variableValue)
Check::FileInfo * getFileInfo(const Tokenizer &tokenizer, const Settings &settings) const override
Parse current TU and extract file info.
Base class used for whole-program analysis.
Definition: check.h:103
void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg)
report an error
Definition: check.h:138
const Settings *const mSettings
Definition: check.h:134
const Tokenizer *const mTokenizer
Definition: check.h:133
void logChecker(const char id[])
log checker
Definition: check.cpp:129
This is an interface, which the class responsible of error logging should implement.
Definition: errorlogger.h:214
virtual void reportErr(const ErrorMessage &msg)=0
Information about found errors and warnings is directed here.
Wrapper for error messages, provided by reportErr()
Definition: errorlogger.h:48
const Variable * getArgumentVar(nonneg int num) const
std::list< Variable > argumentList
argument list, must remain list due to clangimport usage!
Library definitions handling.
Definition: library.h:52
ArgumentChecks::Direction getArgDirection(const Token *ftok, int argnr) const
Definition: library.cpp:1481
bool isuninitargbad(const Token *ftok, int argnr, int indirect=0, bool *hasIndirect=nullptr) const
Definition: library.cpp:1060
bool isnullargbad(const Token *ftok, int argnr) const
Definition: library.cpp:1047
const AllocFunc * getAllocFuncInfo(const Token *tok) const
get allocation info for function
Definition: library.cpp:1077
long long bigint
Definition: mathlib.h:68
static bigint toBigNumber(const std::string &str)
for conversion of numeric literals - for atoi-like conversions please use strToInt()
Definition: mathlib.cpp:368
std::list< Variable > varlist
std::vector< Scope * > nestedList
ScopeType type
Function * function
function info for this function
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
nonneg int numConstructors
std::string className
bool isExecutable() const
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:95
bool isEnabled(const ValueFlow::Value *value, bool inconclusiveCheck=false) const
Returns true if given value can be shown.
Definition: settings.cpp:274
Library library
Library.
Definition: settings.h:237
bool debugwarnings
Is –debug-warnings given?
Definition: settings.h:183
std::vector< const Scope * > functionScopes
Fast access to function scopes.
std::list< Scope > scopeList
Information about all namespaces/classes/structures.
std::vector< const Scope * > classAndStructScopes
Fast access to class and struct scopes.
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
const ValueFlow::Value * getValue(const MathLib::bigint val) const
Definition: token.cpp:2596
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
bool isC() const
Definition: token.cpp:2757
bool isName() const
Definition: token.h:361
static const Token * findmatch(const Token *const startTok, const char pattern[], const nonneg int varId=0)
Definition: token.cpp:1099
bool hasKnownIntValue() const
Definition: token.cpp:2553
bool isEnumType() const
Definition: token.h:561
MathLib::bigint getKnownIntValue() const
Definition: token.h:1218
bool isCpp() const
Definition: token.cpp:2752
const ValueType * valueType() const
Definition: token.h:331
const std::string & strAt(int index) const
Definition: token.cpp:457
void astOperand1(Token *tok)
Definition: token.cpp:1490
void function(const Function *f)
Associate this token with given function.
Definition: token.cpp:1127
nonneg int varId() const
Definition: token.h:870
std::string expressionString() const
Definition: token.cpp:1681
bool isCast() const
Definition: token.h:458
bool isUnaryOp(const std::string &s) const
Definition: token.h:413
const Token * tokAt(int index) const
Definition: token.cpp:427
bool isConstOp() const
Definition: token.h:385
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 isBinaryOp() const
Definition: token.h:410
nonneg int linenr() const
Definition: token.h:816
bool isStandardType() const
Definition: token.h:449
void variable(const Variable *v)
Associate this token with given variable.
Definition: token.h:1070
bool isComparisonOp() const
Definition: token.h:398
Token * next()
Definition: token.h:830
const std::list< ValueFlow::Value > & values() const
Definition: token.h:1197
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
The main purpose is to tokenize the source code.
Definition: tokenize.h:46
bool isScopeNoReturn(const Token *endScopeToken, bool *unknown=nullptr) const
Check if inner scope ends with a call to a noreturn function.
Definition: tokenize.cpp:8015
const Token * tokens() const
Definition: tokenize.h:592
bool isC() const
Is the code C.
Definition: tokenize.h:64
const SymbolDatabase * getSymbolDatabase() const
Definition: tokenize.h:563
bool isCPP() const
Is the code CPP.
Definition: tokenize.h:69
bool isStructType() const
enum Type::NeedInitialization needInitialization
ErrorPath errorPath
Definition: vfvalue.h:282
bool isUninitValue() const
Definition: vfvalue.h:223
bool isKnown() const
Definition: vfvalue.h:353
bool isInconclusive() const
Definition: vfvalue.h:378
std::vector< std::string > subexpressions
Definition: vfvalue.h:312
enum ValueType::Type type
nonneg int pointer
0=>not pointer, 1=>*, 2=>**, 3=>***, etc
const Token * containerTypeToken
The container type token.
Information about a member variable.
bool hasDefault() const
Does variable have a default value.
bool isClass() const
Is variable a user defined (or unknown) type.
bool isExtern() const
Is variable extern.
bool isReference() const
Is reference variable.
bool isStlType() const
Checks if the variable is an STL type ('std::') E.g.
const Type * type() const
Get Type pointer of known type.
bool isPointerToArray() const
Is variable a pointer to an array.
bool isThrow() const
Is variable a throw type.
const Scope * typeScope() const
Get Scope pointer of known type.
const std::string & name() const
Get name string.
bool isArray() const
Is variable an array.
const Token * nameToken() const
Get name token.
nonneg int declarationId() const
Get declaration ID (varId used for variable in its declaration).
const Token * typeStartToken() const
Get type start token.
bool isPointer() const
Is pointer variable.
bool isStatic() const
Is variable static.
const ValueType * valueType() const
std::string toString(Color c)
Definition: color.cpp:54
static const std::string emptyString
Definition: config.h:127
#define nonneg
Definition: config.h:138
std::list< ErrorPathItem > ErrorPath
Definition: errortypes.h:130
@ warning
Warning.
@ debug
Debug message.
@ error
Programming error.
CPPCHECKLIB std::list< FileInfo::UnsafeUsage > getUnsafeUsage(const Tokenizer &tokenizer, const Settings &settings, bool(*isUnsafeUsage)(const Settings &settings, const Token *argtok, MathLib::bigint *value))
Definition: ctu.cpp:472
CPPCHECKLIB std::string toString(const std::list< FileInfo::UnsafeUsage > &unsafeUsage)
Definition: ctu.cpp:151
CPPCHECKLIB std::list< FileInfo::UnsafeUsage > loadUnsafeUsageListFromXml(const tinyxml2::XMLElement *xmlElement)
Definition: ctu.cpp:257
Definition: check.h:31
std::string myArgumentName
Definition: ctu.h:72
@ DIR_IN
Input to called function. Data is treated as read-only.
@ DIR_OUT
Output to caller. Data is passed by reference or address and is potentially written.
MathLib::bigint value
static void indent(std::string &str, const nonneg int indent1, const nonneg int indent2)
Definition: token.cpp:1742
#define bailout(tokenlist, errorLogger, tok, what)
Definition: valueflow.cpp:144