Cppcheck
checkunusedvar.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 "checkunusedvar.h"
22 
23 #include "astutils.h"
24 #include "errortypes.h"
25 #include "fwdanalysis.h"
26 #include "library.h"
27 #include "settings.h"
28 #include "symboldatabase.h"
29 #include "token.h"
30 #include "tokenize.h"
31 #include "utils.h"
32 #include "valueflow.h"
33 
34 #include <algorithm>
35 #include <list>
36 #include <set>
37 #include <utility>
38 #include <vector>
39 //---------------------------------------------------------------------------
40 
41 // Register this check class (by creating a static instance of it)
42 namespace {
43  CheckUnusedVar instance;
44 }
45 
46 static const CWE CWE563(563U); // Assignment to Variable without Use ('Unused Variable')
47 static const CWE CWE665(665U); // Improper Initialization
48 
49 /** Is scope a raii class scope */
50 static bool isRaiiClassScope(const Scope *classScope)
51 {
52  return classScope && classScope->getDestructor() != nullptr;
53 }
54 
55 /** Is ValueType a raii class? */
56 static bool isRaiiClass(const ValueType *valueType, bool cpp, bool defaultReturn = true)
57 {
58  if (!cpp)
59  return false;
60 
61  if (!valueType)
62  return defaultReturn;
63 
64  if ((valueType->smartPointerType && isRaiiClassScope(valueType->smartPointerType->classScope)) || (!valueType->smartPointerType && valueType->type == ValueType::Type::SMART_POINTER))
65  return true;
66 
67  switch (valueType->type) {
68  case ValueType::Type::UNKNOWN_TYPE:
69  case ValueType::Type::NONSTD:
70  return defaultReturn;
71 
72  case ValueType::Type::RECORD:
73  if (isRaiiClassScope(valueType->typeScope))
74  return true;
75  return defaultReturn;
76 
77  case ValueType::Type::POD:
78  case ValueType::Type::SMART_POINTER:
79  case ValueType::Type::CONTAINER:
80  case ValueType::Type::ITERATOR:
81  case ValueType::Type::VOID:
82  case ValueType::Type::BOOL:
83  case ValueType::Type::CHAR:
84  case ValueType::Type::SHORT:
85  case ValueType::Type::WCHAR_T:
86  case ValueType::Type::INT:
87  case ValueType::Type::LONG:
88  case ValueType::Type::LONGLONG:
89  case ValueType::Type::UNKNOWN_INT:
90  case ValueType::Type::FLOAT:
91  case ValueType::Type::DOUBLE:
92  case ValueType::Type::LONGDOUBLE:
93  return false;
94  }
95 
96  return defaultReturn;
97 }
98 
99 /**
100  * @brief This class is used create a list of variables within a function.
101  */
102 class Variables {
103 public:
105 
106  /** Store information about variable usage */
108  public:
109  explicit VariableUsage(const Variable *var = nullptr,
110  VariableType type = standard,
111  bool read = false,
112  bool write = false,
113  bool modified = false,
114  bool allocateMemory = false) :
115  _var(var),
116  _lastAccess(var ? var->nameToken() : nullptr),
117  mType(type),
118  _read(read),
119  _write(write),
122 
123  /** variable is used.. set both read+write */
124  void use() {
125  _read = true;
126  _write = true;
127  }
128 
129  /** is variable unused? */
130  bool unused() const {
131  return (!_read && !_write);
132  }
133 
134  std::set<nonneg int> _aliases;
135  std::set<const Scope*> _assignments;
136 
137  const Variable* _var;
140  bool _read;
141  bool _write;
142  bool _modified; // read/modify/write
144  };
145 
146  void clear() {
147  mVarUsage.clear();
148  }
149  const std::map<nonneg int, VariableUsage> &varUsage() const {
150  return mVarUsage;
151  }
152  void addVar(const Variable *var, VariableType type, bool write_);
153  void allocateMemory(nonneg int varid, const Token* tok);
154  void read(nonneg int varid, const Token* tok);
155  void readAliases(nonneg int varid, const Token* tok);
156  void readAll(nonneg int varid, const Token* tok);
157  void write(nonneg int varid, const Token* tok);
158  void writeAliases(nonneg int varid, const Token* tok);
159  void writeAll(nonneg int varid, const Token* tok);
160  void use(nonneg int varid, const Token* tok);
161  void modified(nonneg int varid, const Token* tok);
162  VariableUsage *find(nonneg int varid);
163  void alias(nonneg int varid1, nonneg int varid2, bool replace);
164  void erase(nonneg int varid) {
165  mVarUsage.erase(varid);
166  }
167  void eraseAliases(nonneg int varid);
168  void eraseAll(nonneg int varid);
169  void clearAliases(nonneg int varid);
170 
171 private:
172 
173  std::map<nonneg int, VariableUsage> mVarUsage;
174 };
175 
176 
177 /**
178  * Alias the 2 given variables. Either replace the existing aliases if
179  * they exist or merge them. You would replace an existing alias when this
180  * assignment is in the same scope as the previous assignment. You might
181  * merge the aliases when this assignment is in a different scope from the
182  * previous assignment depending on the relationship of the 2 scopes.
183  */
184 void Variables::alias(nonneg int varid1, nonneg int varid2, bool replace)
185 {
186  VariableUsage *var1 = find(varid1);
187  VariableUsage *var2 = find(varid2);
188 
189  if (!var1 || !var2)
190  return;
191 
192  // alias to self
193  if (varid1 == varid2) {
194  var1->use();
195  return;
196  }
197 
198  if (replace) {
199  // remove var1 from all aliases
200  for (std::set<nonneg int>::const_iterator i = var1->_aliases.cbegin(); i != var1->_aliases.cend(); ++i) {
201  VariableUsage *temp = find(*i);
202 
203  if (temp)
204  temp->_aliases.erase(var1->_var->declarationId());
205  }
206 
207  // remove all aliases from var1
208  var1->_aliases.clear();
209  }
210 
211  // var1 gets all var2s aliases
212  for (std::set<nonneg int>::const_iterator i = var2->_aliases.cbegin(); i != var2->_aliases.cend(); ++i) {
213  if (*i != varid1)
214  var1->_aliases.insert(*i);
215  }
216 
217  // var2 is an alias of var1
218  var2->_aliases.insert(varid1);
219  var1->_aliases.insert(varid2);
220 
221  if (var2->mType == Variables::pointer) {
222  var2->_read = true;
223  }
224 }
225 
227 {
228  VariableUsage *usage = find(varid);
229 
230  if (usage) {
231  // remove usage from all aliases
232  for (std::set<nonneg int>::const_iterator i = usage->_aliases.cbegin(); i != usage->_aliases.cend(); ++i) {
233  VariableUsage *temp = find(*i);
234 
235  if (temp)
236  temp->_aliases.erase(usage->_var->declarationId());
237  }
238 
239  // remove all aliases from usage
240  usage->_aliases.clear();
241  }
242 }
243 
245 {
246  VariableUsage *usage = find(varid);
247 
248  if (usage) {
249  for (std::set<nonneg int>::const_iterator aliases = usage->_aliases.cbegin(); aliases != usage->_aliases.cend(); ++aliases)
250  erase(*aliases);
251  }
252 }
253 
255 {
256  eraseAliases(varid);
257  erase(varid);
258 }
259 
260 void Variables::addVar(const Variable *var,
261  VariableType type,
262  bool write_)
263 {
264  if (var->declarationId() > 0) {
265  mVarUsage.insert(std::make_pair(var->declarationId(), VariableUsage(var, type, false, write_, false)));
266  }
267 }
268 
269 void Variables::allocateMemory(nonneg int varid, const Token* tok)
270 {
271  VariableUsage *usage = find(varid);
272 
273  if (usage) {
274  usage->_allocateMemory = true;
275  usage->_lastAccess = tok;
276  }
277 }
278 
279 void Variables::read(nonneg int varid, const Token* tok)
280 {
281  VariableUsage *usage = find(varid);
282 
283  if (usage) {
284  usage->_read = true;
285  if (tok)
286  usage->_lastAccess = tok;
287  }
288 }
289 
290 void Variables::readAliases(nonneg int varid, const Token* tok)
291 {
292  const VariableUsage *usage = find(varid);
293 
294  if (usage) {
295  for (nonneg int const aliases : usage->_aliases) {
296  VariableUsage *aliased = find(aliases);
297 
298  if (aliased) {
299  aliased->_read = true;
300  aliased->_lastAccess = tok;
301  }
302  }
303  }
304 }
305 
306 void Variables::readAll(nonneg int varid, const Token* tok)
307 {
308  read(varid, tok);
309  readAliases(varid, tok);
310 }
311 
312 void Variables::write(nonneg int varid, const Token* tok)
313 {
314  VariableUsage *usage = find(varid);
315 
316  if (usage) {
317  usage->_write = true;
318  if (!usage->_var->isStatic() && !Token::simpleMatch(tok->next(), "= 0 ;"))
319  usage->_read = false;
320  usage->_lastAccess = tok;
321  }
322 }
323 
324 void Variables::writeAliases(nonneg int varid, const Token* tok)
325 {
326  VariableUsage *usage = find(varid);
327 
328  if (usage) {
329  for (std::set<nonneg int>::const_iterator aliases = usage->_aliases.cbegin(); aliases != usage->_aliases.cend(); ++aliases) {
330  VariableUsage *aliased = find(*aliases);
331 
332  if (aliased) {
333  aliased->_write = true;
334  aliased->_lastAccess = tok;
335  }
336  }
337  }
338 }
339 
340 void Variables::writeAll(nonneg int varid, const Token* tok)
341 {
342  write(varid, tok);
343  writeAliases(varid, tok);
344 }
345 
346 void Variables::use(nonneg int varid, const Token* tok)
347 {
348  VariableUsage *usage = find(varid);
349 
350  if (usage) {
351  usage->use();
352  usage->_lastAccess = tok;
353 
354  for (std::set<nonneg int>::const_iterator aliases = usage->_aliases.cbegin(); aliases != usage->_aliases.cend(); ++aliases) {
355  VariableUsage *aliased = find(*aliases);
356 
357  if (aliased) {
358  aliased->use();
359  aliased->_lastAccess = tok;
360  }
361  }
362  }
363 }
364 
365 void Variables::modified(nonneg int varid, const Token* tok)
366 {
367  VariableUsage *usage = find(varid);
368 
369  if (usage) {
370  if (!usage->_var->isStatic())
371  usage->_read = false;
372  usage->_modified = true;
373  usage->_lastAccess = tok;
374 
375  for (std::set<nonneg int>::const_iterator aliases = usage->_aliases.cbegin(); aliases != usage->_aliases.cend(); ++aliases) {
376  VariableUsage *aliased = find(*aliases);
377 
378  if (aliased) {
379  aliased->_modified = true;
380  aliased->_lastAccess = tok;
381  }
382  }
383  }
384 }
385 
387 {
388  if (varid) {
389  const std::map<nonneg int, VariableUsage>::iterator i = mVarUsage.find(varid);
390  if (i != mVarUsage.end())
391  return &i->second;
392  }
393  return nullptr;
394 }
395 
396 static const Token* doAssignment(Variables &variables, const Token *tok, bool dereference, const Scope *scope)
397 {
398  // a = a + b;
399  if (Token::Match(tok, "%var% = %var% !!;")) {
400  const Token* rhsVarTok = tok->tokAt(2);
401  if (tok->varId() == rhsVarTok->varId()) {
402  return rhsVarTok;
403  }
404  }
405 
406  if (Token::Match(tok, "%var% %assign%") && tok->strAt(1) != "=")
407  return tok->next();
408 
409  const Token* const tokOld = tok;
410 
411  // check for aliased variable
412  const nonneg int varid1 = tok->varId();
413  Variables::VariableUsage *var1 = variables.find(varid1);
414 
415  if (var1) {
416  // jump behind '='
417  tok = tok->next();
418  while (!tok->isAssignmentOp()) {
419  if (tok->varId())
420  variables.read(tok->varId(), tok);
421  tok = tok->next();
422  }
423  tok = tok->next();
424 
425  if (Token::Match(tok, "( const| struct|union| %type% * ) ( ("))
426  tok = tok->link()->next();
427 
428  if (Token::Match(tok, "( [(<] const| struct|union| %type% *| [>)]"))
429  tok = tok->next();
430 
431  if (Token::Match(tok, "(| &| %name%") ||
432  (Token::Match(tok->next(), "< const| struct|union| %type% *| > ( &| %name%"))) {
433  bool addressOf = false;
434 
435  if (Token::Match(tok, "%var% ."))
436  variables.use(tok->varId(), tok); // use = read + write
437 
438  // check for C style cast
439  if (tok->str() == "(") {
440  tok = tok->next();
441  if (tok->str() == "const")
442  tok = tok->next();
443 
444  if (Token::Match(tok, "struct|union"))
445  tok = tok->next();
446 
447  while ((tok->isName() && tok->varId() == 0) || (tok->str() == "*") || (tok->str() == ")"))
448  tok = tok->next();
449 
450  if (tok->str() == "&") {
451  addressOf = true;
452  tok = tok->next();
453  } else if (tok->str() == "(") {
454  tok = tok->next();
455  if (tok->str() == "&") {
456  addressOf = true;
457  tok = tok->next();
458  }
459  } else if (Token::Match(tok, "%cop% %var%")) {
460  variables.read(tok->next()->varId(), tok);
461  }
462  }
463 
464  // check for C++ style cast
465  else if (tok->str().find("cast") != std::string::npos &&
466  tok->strAt(1) == "<") {
467  tok = tok->tokAt(2);
468  if (tok->str() == "const")
469  tok = tok->next();
470 
471  if (Token::Match(tok, "struct|union"))
472  tok = tok->next();
473 
474  tok = tok->next();
475  if (!tok)
476  return tokOld;
477  if (tok->str() == "*")
478  tok = tok->next();
479 
480  tok = tok->tokAt(2);
481  if (!tok)
482  return tokOld;
483  if (tok->str() == "&") {
484  addressOf = true;
485  tok = tok->next();
486  }
487  }
488 
489  // no cast, no ?
490  else if (!Token::Match(tok, "%name% ?")) {
491  if (tok->str() == "&") {
492  addressOf = true;
493  tok = tok->next();
494  } else if (tok->str() == "new")
495  return tokOld;
496  }
497 
498  // check if variable is local
499  const nonneg int varid2 = tok->varId();
500  const Variables::VariableUsage* var2 = variables.find(varid2);
501 
502  if (var2) { // local variable (alias or read it)
503  if (var1->mType == Variables::pointer || var1->mType == Variables::pointerArray) {
504  if (dereference)
505  variables.read(varid2, tok);
506  else {
507  if (addressOf ||
508  var2->mType == Variables::array ||
509  var2->mType == Variables::pointer) {
510  bool replace = true;
511 
512  // pointerArray => don't replace
513  if (var1->mType == Variables::pointerArray)
514  replace = false;
515 
516  // check if variable declared in same scope
517  else if (scope == var1->_var->scope())
518  replace = true;
519 
520  // not in same scope as declaration
521  else {
522  // no other assignment in this scope
523  if (var1->_assignments.find(scope) == var1->_assignments.end() ||
524  scope->type == Scope::eSwitch) {
525  // nothing to replace
526  // cppcheck-suppress duplicateBranch - remove when TODO below is address
527  if (var1->_assignments.empty())
528  replace = false;
529 
530  // this variable has previous assignments
531  else {
532  // TODO: determine if existing aliases should be replaced or merged
533  replace = false;
534  }
535  }
536 
537  // assignment in this scope
538  else {
539  // replace when only one other assignment, merge them otherwise
540  replace = (var1->_assignments.size() == 1);
541  }
542  }
543 
544  variables.alias(varid1, varid2, replace);
545  } else if (tok->strAt(1) == "?") {
546  if (var2->mType == Variables::reference)
547  variables.readAliases(varid2, tok);
548  else
549  variables.read(varid2, tok);
550  } else {
551  variables.readAll(varid2, tok);
552  }
553  }
554  } else if (var1->mType == Variables::reference) {
555  variables.alias(varid1, varid2, true);
556  } else if (var1->mType == Variables::standard && addressOf) {
557  variables.alias(varid1, varid2, true);
558  } else {
559  if ((var2->mType == Variables::pointer || var2->mType == Variables::pointerArray) && tok->strAt(1) == "[")
560  variables.readAliases(varid2, tok);
561 
562  variables.read(varid2, tok);
563  }
564  } else { // not a local variable (or an unsupported local variable)
565  if (var1->mType == Variables::pointer && !dereference) {
566  // check if variable declaration is in this scope
567  if (var1->_var->scope() == scope) {
568  // If variable is used in RHS then "use" variable
569  for (const Token *rhs = tok; rhs && rhs->str() != ";"; rhs = rhs->next()) {
570  if (rhs->varId() == varid1) {
571  variables.use(varid1, tok);
572  break;
573  }
574  }
575  variables.clearAliases(varid1);
576  } else {
577  // no other assignment in this scope
578  if (var1->_assignments.find(scope) == var1->_assignments.end()) {
579  /**
580  * @todo determine if existing aliases should be discarded
581  */
582  }
583 
584  // this assignment replaces the last assignment in this scope
585  else {
586  // aliased variables in a larger scope are not supported
587  // remove all aliases
588  variables.clearAliases(varid1);
589  }
590  }
591  }
592  }
593  } else
594  tok = tokOld;
595 
596  var1->_assignments.insert(scope);
597  }
598 
599  // check for alias to struct member
600  // char c[10]; a.b = c;
601  else if (Token::Match(tok->tokAt(-2), "%name% .")) {
602  const Token *rhsVarTok = tok->tokAt(2);
603  if (rhsVarTok && rhsVarTok->varId()) {
604  const nonneg int varid2 = rhsVarTok->varId();
605  const Variables::VariableUsage *var2 = variables.find(varid2);
606 
607  // struct member aliased to local variable
608  if (var2 && (var2->mType == Variables::array ||
609  var2->mType == Variables::pointer)) {
610  // erase aliased variable and all variables that alias it
611  // to prevent false positives
612  variables.eraseAll(varid2);
613  }
614  }
615  }
616 
617  // Possible pointer alias
618  else if (Token::Match(tok, "%name% = %name% ;")) {
619  const nonneg int varid2 = tok->tokAt(2)->varId();
620  const Variables::VariableUsage *var2 = variables.find(varid2);
621  if (var2 && (var2->mType == Variables::array ||
622  var2->mType == Variables::pointer)) {
623  variables.use(varid2,tok);
624  }
625  }
626 
627  return tok;
628 }
629 
630 static bool isPartOfClassStructUnion(const Token* tok)
631 {
632  for (; tok; tok = tok->previous()) {
633  if (tok->str() == "}" || tok->str() == ")")
634  tok = tok->link();
635  else if (tok->str() == "(")
636  return false;
637  else if (tok->str() == "{") {
638  return (tok->strAt(-1) == "struct" || tok->strAt(-2) == "struct" || tok->strAt(-1) == "class" || tok->strAt(-2) == "class" || tok->strAt(-1) == "union" || tok->strAt(-2) == "union");
639  }
640  }
641  return false;
642 }
643 
644 static bool isVarDecl(const Token *tok)
645 {
646  return tok && tok->variable() && tok->variable()->nameToken() == tok;
647 }
648 
649 // Skip [ .. ]
650 static const Token * skipBrackets(const Token *tok)
651 {
652  while (tok && tok->str() == "[")
653  tok = tok->link()->next();
654  return tok;
655 }
656 
657 
658 // Skip [ .. ] . x
659 static const Token * skipBracketsAndMembers(const Token *tok)
660 {
661  while (tok) {
662  if (tok->str() == "[")
663  tok = tok->link()->next();
664  else if (Token::Match(tok, ". %name%"))
665  tok = tok->tokAt(2);
666  else
667  break;
668  }
669  return tok;
670 }
671 
672 static void useFunctionArgs(const Token *tok, Variables& variables)
673 {
674  // TODO: Match function args to see if they are const or not. Assume that const data is not written.
675  if (!tok)
676  return;
677  if (tok->str() == ",") {
678  useFunctionArgs(tok->astOperand1(), variables);
679  useFunctionArgs(tok->astOperand2(), variables);
680  } else if (Token::Match(tok, "[+:]") && (!tok->valueType() || tok->valueType()->pointer)) {
681  useFunctionArgs(tok->astOperand1(), variables);
682  useFunctionArgs(tok->astOperand2(), variables);
683  } else if (tok->variable() && tok->variable()->isArray()) {
684  variables.use(tok->varId(), tok);
685  }
686 }
687 
688 //---------------------------------------------------------------------------
689 // Usage of function variables
690 //---------------------------------------------------------------------------
692 {
693  // Find declarations if the scope is executable..
694  if (scope->isExecutable()) {
695  // Find declarations
696  for (std::list<Variable>::const_iterator i = scope->varlist.cbegin(); i != scope->varlist.cend(); ++i) {
697  if (i->isThrow() || i->isExtern())
698  continue;
700  if (i->isArray() && (i->nameToken()->previous()->str() == "*" || i->nameToken()->strAt(-2) == "*"))
702  else if (i->isArray() && i->nameToken()->previous()->str() == "&")
704  else if (i->isArray())
705  type = Variables::array;
706  else if (i->isReference() && !(i->valueType() && i->valueType()->type == ValueType::UNKNOWN_TYPE && Token::simpleMatch(i->typeStartToken(), "auto")))
707  type = Variables::reference;
708  else if (i->nameToken()->previous()->str() == "*" && i->nameToken()->strAt(-2) == "*")
710  else if (i->isPointerToArray())
712  else if (i->isPointer())
713  type = Variables::pointer;
714  else if (mTokenizer->isC() ||
715  i->typeEndToken()->isStandardType() ||
716  i->isStlType() ||
717  isRecordTypeWithoutSideEffects(i->type()) ||
718  mSettings->library.detectContainer(i->typeStartToken()) ||
719  mSettings->library.getTypeCheck("unusedvar", i->typeStartToken()->str()) == Library::TypeCheck::check)
720  type = Variables::standard;
721 
722  if (type == Variables::none || isPartOfClassStructUnion(i->typeStartToken()))
723  continue;
724  const Token* defValTok = i->nameToken()->next();
725  if (Token::Match(i->nameToken()->previous(), "* %var% ) (")) // function pointer. Jump behind parameter list.
726  defValTok = defValTok->linkAt(1)->next();
727  for (; defValTok; defValTok = defValTok->next()) {
728  if (defValTok->str() == "[")
729  defValTok = defValTok->link();
730  else if (defValTok->str() == "(" || defValTok->str() == "{" || defValTok->str() == "=" || defValTok->str() == ":") {
731  variables.addVar(&*i, type, true);
732  break;
733  } else if (defValTok->str() == ";" || defValTok->str() == "," || defValTok->str() == ")") {
734  variables.addVar(&*i, type, i->isStatic() && i->scope()->type != Scope::eFunction);
735  break;
736  }
737  }
738  if (i->isArray() && i->isClass() && // Array of class/struct members. Initialized by ctor except for std::array
739  !(i->isStlType() && i->valueType() && i->valueType()->containerTypeToken && i->valueType()->containerTypeToken->isStandardType()))
740  variables.write(i->declarationId(), i->nameToken());
741  if (i->isArray() && Token::Match(i->nameToken(), "%name% [ %var% ]")) // Array index variable read.
742  variables.read(i->nameToken()->tokAt(2)->varId(), i->nameToken());
743 
744  if (defValTok && defValTok->next()) {
745  // simple assignment "var = 123"
746  if (defValTok->str() == "=" && defValTok->next()->str() != "{") {
747  doAssignment(variables, i->nameToken(), false, scope);
748  } else {
749  // could be "var = {...}" OR "var{...}" (since C++11)
750  const Token* tokBraceStart = nullptr;
751  if (Token::simpleMatch(defValTok, "= {")) {
752  // "var = {...}"
753  tokBraceStart = defValTok->next();
754  } else if (defValTok->str() == "{") {
755  // "var{...}"
756  tokBraceStart = defValTok;
757  }
758  if (tokBraceStart) {
759  for (const Token* tok = tokBraceStart->next(); tok && tok != tokBraceStart->link(); tok = tok->next()) {
760  if (tok->varId()) {
761  // Variables used to initialize the array read.
762  variables.read(tok->varId(), i->nameToken());
763  }
764  }
765  }
766  }
767  }
768  }
769  }
770 
771  // Check variable usage
772  const Token *tok;
773  if (scope->type == Scope::eFunction)
774  tok = scope->bodyStart->next();
775  else
776  tok = scope->classDef->next();
777  for (; tok && tok != scope->bodyEnd; tok = tok->next()) {
778  if (tok->str() == "{" && tok != scope->bodyStart && !tok->previous()->varId()) {
779  if (std::any_of(scope->nestedList.cbegin(), scope->nestedList.cend(), [&](const Scope* s) {
780  return s->bodyStart == tok;
781  })) {
782  checkFunctionVariableUsage_iterateScopes(tok->scope(), variables); // Scan child scope
783  tok = tok->link();
784  }
785  if (!tok)
786  break;
787  }
788 
789  if (Token::Match(tok, "asm ( %str% )")) {
790  variables.clear();
791  break;
792  }
793 
794  // templates
795  if (tok->isName() && endsWith(tok->str(), '>')) {
796  // TODO: This is a quick fix to handle when constants are used
797  // as template parameters. Try to handle this better, perhaps
798  // only remove constants.
799  variables.clear();
800  }
801 
802  else if (Token::Match(tok->previous(), "[;{}]")) {
803  for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) {
804  if (tok2->varId()) {
805  // Is this a variable declaration?
806  const Variable *var = tok2->variable();
807  if (!var || var->nameToken() != tok2)
808  continue;
809 
810  // Mark template parameters used in declaration as use..
811  if (tok2->strAt(-1) == ">") {
812  for (const Token *tok3 = tok; tok3 != tok2; tok3 = tok3->next()) {
813  if (tok3->varId())
814  variables.use(tok3->varId(), tok3);
815  }
816  }
817 
818  // Skip variable declaration..
819  tok = tok2->next();
820  if (Token::Match(tok, "( %name% )")) // Simple initialization through copy ctor
821  tok = tok->next();
822  else if (Token::Match(tok, "= %var% ;")) { // Simple initialization
823  tok = tok->next();
824  if (!var->isReference())
825  variables.read(tok->varId(), tok);
826  } else if (tok->str() == "[" && Token::simpleMatch(skipBrackets(tok),"= {")) {
827  const Token * const rhs1 = skipBrackets(tok)->next();
828  for (const Token *rhs = rhs1->link(); rhs != rhs1; rhs = rhs->previous()) {
829  if (rhs->varId())
830  variables.readAll(rhs->varId(), rhs);
831  }
832  } else if (var->typeEndToken()->str() == ">") // Be careful with types like std::vector
833  tok = tok->previous();
834  break;
835  }
836  if (Token::Match(tok2, "[;({=]"))
837  break;
838  }
839  }
840  // Freeing memory (not considered "using" the pointer if it was also allocated in this function)
841  if ((Token::Match(tok, "%name% ( %var% )") && mSettings->library.getDeallocFuncInfo(tok)) ||
842  (tok->isCpp() && (Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")))) {
843  nonneg int varid = 0;
844  if (tok->str() != "delete") {
845  const Token *varTok = tok->tokAt(2);
846  varid = varTok->varId();
847  tok = varTok->next();
848  } else if (tok->strAt(1) == "[") {
849  const Token *varTok = tok->tokAt(3);
850  varid = varTok->varId();
851  tok = varTok;
852  } else {
853  varid = tok->next()->varId();
854  tok = tok->next();
855  }
856 
857  const Variables::VariableUsage *const var = variables.find(varid);
858  if (var) {
859  if (!var->_aliases.empty())
860  variables.use(varid, tok);
861  else if (!var->_allocateMemory)
862  variables.readAll(varid, tok);
863  }
864  }
865 
866  else if (Token::Match(tok, "return|throw")) {
867  for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) {
868  if (tok2->varId())
869  variables.readAll(tok2->varId(), tok);
870  else if (tok2->str() == ";")
871  break;
872  }
873  }
874 
875  // assignment
876  else if (Token::Match(tok, "*| ++|--| %name% ++|--| %assign%") ||
877  Token::Match(tok, "*| ( const| %type% *| ) %name% %assign%")) {
878  bool dereference = false;
879  bool pre = false;
880  bool post = false;
881 
882  if (tok->str() == "*") {
883  dereference = true;
884  tok = tok->next();
885  }
886 
887  if (Token::Match(tok, "( const| %type% *| ) %name% %assign%"))
888  tok = tok->link()->next();
889 
890  else if (tok->str() == "(")
891  tok = tok->next();
892 
893  if (tok->tokType() == Token::eIncDecOp) {
894  pre = true;
895  tok = tok->next();
896  }
897 
898  if (tok->next()->tokType() == Token::eIncDecOp)
899  post = true;
900 
901  const nonneg int varid1 = tok->varId();
902  const Token * const start = tok;
903 
904  // assignment in while head..
905  bool inwhile = false;
906  {
907  const Token *parent = tok->astParent();
908  while (parent) {
909  if (Token::simpleMatch(parent->previous(), "while (")) {
910  inwhile = true;
911  break;
912  }
913  parent = parent->astParent();
914  }
915  }
916 
917  tok = doAssignment(variables, tok, dereference, scope);
918 
919  if (tok && tok->isAssignmentOp() && tok->str() != "=") {
920  variables.use(varid1, tok);
921  if (Token::Match(tok, "%assign% %name%")) {
922  tok = tok->next();
923  variables.read(tok->varId(), tok);
924  }
925  }
926 
927  if (pre || post)
928  variables.use(varid1, tok);
929 
930  if (dereference) {
931  const Variables::VariableUsage *const var = variables.find(varid1);
932  if (var && var->mType == Variables::array)
933  variables.write(varid1, tok);
934  variables.writeAliases(varid1, tok);
935  variables.read(varid1, tok);
936  } else {
937  const Variables::VariableUsage *const var = variables.find(varid1);
938  if (var && (inwhile || start->strAt(-1) == ",")) {
939  variables.use(varid1, tok);
940  } else if (var && var->mType == Variables::reference) {
941  variables.writeAliases(varid1, tok);
942  variables.read(varid1, tok);
943  }
944  // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable
945  else if (var && var->mType == Variables::pointer &&
946  Token::Match(start, "%name% =") &&
948 
949  const Token *allocFuncCallToken = findAllocFuncCallToken(start->next()->astOperand2(), mSettings->library);
950  const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(allocFuncCallToken);
951 
952  bool allocateMemory = !allocFunc || Library::ismemory(allocFunc->groupId);
953 
954  if (allocFuncCallToken->str() == "new") {
955  const Token *type = allocFuncCallToken->next();
956 
957  // skip nothrow
958  if (type->isCpp() && (Token::simpleMatch(type, "( nothrow )") ||
959  Token::simpleMatch(type, "( std :: nothrow )")))
960  type = type->link()->next();
961 
962  // is it a user defined type?
963  if (!type->isStandardType()) {
964  const Variable *variable = start->variable();
965  if (!variable || !isRecordTypeWithoutSideEffects(variable->type()))
966  allocateMemory = false;
967  }
968  }
969 
970  if (allocateMemory)
971  variables.allocateMemory(varid1, tok);
972  else
973  variables.write(varid1, tok);
974  } else if (varid1 && Token::Match(tok, "%varid% .", varid1)) {
975  variables.read(varid1, tok);
976  variables.write(varid1, start);
977  } else {
978  variables.write(varid1, tok);
979  }
980  }
981 
982  const Variables::VariableUsage * const var2 = variables.find(tok->varId());
983  if (var2) {
984  if (var2->mType == Variables::reference) {
985  variables.writeAliases(tok->varId(), tok);
986  variables.read(tok->varId(), tok);
987  } else if (tok->varId() != varid1 && Token::Match(tok, "%name% .|["))
988  variables.read(tok->varId(), tok);
989  else if (tok->varId() != varid1 &&
990  var2->mType == Variables::standard &&
991  tok->strAt(-1) != "&")
992  variables.use(tok->varId(), tok);
993  }
994 
995  const Token * const equal = skipBracketsAndMembers(tok->next());
996 
997  // checked for chained assignments
998  if (tok != start && equal && equal->str() == "=") {
999  const nonneg int varId = tok->varId();
1000  const Variables::VariableUsage * const var = variables.find(varId);
1001 
1002  if (var && var->mType != Variables::reference) {
1003  variables.read(varId,tok);
1004  }
1005 
1006  tok = tok->previous();
1007  }
1008  }
1009 
1010  // assignment
1011  else if ((Token::Match(tok, "%name% [") && Token::simpleMatch(skipBracketsAndMembers(tok->next()), "=")) ||
1012  (tok->isUnaryOp("*") && astIsLHS(tok) && Token::simpleMatch(tok->astParent(), "=") && Token::simpleMatch(tok->astOperand1(), "+"))) {
1013  const Token *eq = tok;
1014  while (eq && !eq->isAssignmentOp())
1015  eq = eq->astParent();
1016 
1017  const bool deref = eq && eq->astOperand1() && eq->astOperand1()->valueType() && eq->astOperand1()->valueType()->pointer == 0U;
1018 
1019  if (tok->str() == "*") {
1020  tok = tok->tokAt(2);
1021  if (tok->str() == "(")
1022  tok = tok->link()->next();
1023  }
1024 
1025  const nonneg int varid = tok->varId();
1026  const Variables::VariableUsage *var = variables.find(varid);
1027 
1028  if (var) {
1029  // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable
1030  if (var->mType == Variables::pointer &&
1031  ((tok->isCpp() && Token::simpleMatch(skipBrackets(tok->next()), "= new")) ||
1032  (Token::Match(skipBrackets(tok->next()), "= %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2))))) {
1033  variables.allocateMemory(varid, tok);
1034  } else if (var->mType == Variables::pointer || var->mType == Variables::reference) {
1035  variables.read(varid, tok);
1036  variables.writeAliases(varid, tok);
1037  } else if (var->mType == Variables::pointerArray) {
1038  tok = doAssignment(variables, tok, deref, scope);
1039  } else
1040  variables.writeAll(varid, tok);
1041  }
1042  }
1043 
1044  else if (tok->isCpp() && Token::Match(tok, "[;{}] %var% <<")) {
1045  variables.erase(tok->next()->varId());
1046  }
1047 
1048  else if (Token::Match(tok, "& %var%")) {
1049  if (tok->astOperand2()) { // bitop
1050  variables.read(tok->next()->varId(), tok);
1051  } else // addressof
1052  variables.use(tok->next()->varId(), tok); // use = read + write
1053  } else if (Token::Match(tok, ">>|>>= %name%")) {
1054  if (isLikelyStreamRead(tok))
1055  variables.use(tok->next()->varId(), tok); // use = read + write
1056  else
1057  variables.read(tok->next()->varId(), tok);
1058  } else if (Token::Match(tok, "%var% >>|&") && Token::Match(tok->previous(), "[{};:]")) {
1059  variables.read(tok->varId(), tok);
1060  } else if (isLikelyStreamRead(tok->previous())) {
1061  variables.use(tok->varId(), tok);
1062  }
1063 
1064  // function parameter
1065  else if (Token::Match(tok, "[(,] %var% [")) {
1066  variables.use(tok->next()->varId(), tok); // use = read + write
1067  } else if (Token::Match(tok, "[(,] %var% [,)]") && tok->previous()->str() != "*") {
1068  variables.use(tok->next()->varId(), tok); // use = read + write
1069  } else if (Token::Match(tok, "[(,] & %var% [,)]")) {
1070  variables.eraseAll(tok->tokAt(2)->varId());
1071  } else if (Token::Match(tok, "[(,] (") &&
1072  Token::Match(tok->next()->link(), ") %var% [,)[]")) {
1073  variables.use(tok->next()->link()->next()->varId(), tok); // use = read + write
1074  } else if (Token::Match(tok, "[(,] *| *| %var%")) {
1075  const Token* vartok = tok->next();
1076  while (vartok->str() == "*")
1077  vartok = vartok->next();
1078  if (!(vartok->variable() && vartok == vartok->variable()->nameToken()) &&
1079  !(tok->str() == "(" && !Token::Match(tok->previous(), "%name%")))
1080  variables.use(vartok->varId(), vartok);
1081  }
1082 
1083  // function
1084  else if (Token::Match(tok, "%name% (")) {
1085  if (tok->varId() && !tok->function()) // operator()
1086  variables.use(tok->varId(), tok);
1087  else
1088  variables.read(tok->varId(), tok);
1089  useFunctionArgs(tok->next()->astOperand2(), variables);
1090  } else if (Token::Match(tok, "std :: ref ( %var% )")) {
1091  variables.eraseAll(tok->tokAt(4)->varId());
1092  }
1093 
1094  else if (Token::Match(tok->previous(), "[{,] %var% [,}]")) {
1095  variables.use(tok->varId(), tok);
1096  }
1097 
1098  else if (tok->varId() && Token::Match(tok, "%var% .")) {
1099  variables.use(tok->varId(), tok); // use = read + write
1100  }
1101 
1102  else if (tok->str() == ":" && (!tok->valueType() || tok->valueType()->pointer)) {
1103  if (tok->astOperand1())
1104  variables.use(tok->astOperand1()->varId(), tok->astOperand1());
1105  if (tok->astOperand2())
1106  variables.use(tok->astOperand2()->varId(), tok->astOperand2());
1107  }
1108 
1109  else if (tok->isExtendedOp() && tok->next() && tok->next()->varId() && tok->strAt(2) != "=" && !isVarDecl(tok->next())) {
1110  variables.readAll(tok->next()->varId(), tok);
1111  }
1112 
1113  else if (tok->varId() && !isVarDecl(tok) && tok->next() && (tok->next()->str() == ")" || tok->next()->isExtendedOp())) {
1114  if (Token::Match(tok->tokAt(-2), "%name% ( %var% [,)]") &&
1115  !(tok->tokAt(-2)->variable() && tok->tokAt(-2)->variable()->isReference()))
1116  variables.use(tok->varId(), tok);
1117  else
1118  variables.readAll(tok->varId(), tok);
1119  }
1120 
1121  else if (Token::Match(tok, "%var% ;") && Token::Match(tok->previous(), "[;{}:]")) {
1122  variables.readAll(tok->varId(), tok);
1123  }
1124 
1125  // ++|--
1126  else if (tok->next() && tok->next()->tokType() == Token::eIncDecOp && tok->next()->astOperand1() && tok->next()->astOperand1()->varId()) {
1127  if (tok->next()->astParent())
1128  variables.use(tok->next()->astOperand1()->varId(), tok);
1129  else
1130  variables.modified(tok->next()->astOperand1()->varId(), tok);
1131  }
1132 
1133  else if (tok->isAssignmentOp()) {
1134  for (const Token *tok2 = tok->next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) {
1135  if (tok2->varId()) {
1136  if (tok2->strAt(1) == "=")
1137  variables.write(tok2->varId(), tok);
1138  else if (tok2->next() && tok2->next()->isAssignmentOp())
1139  variables.use(tok2->varId(), tok);
1140  else
1141  variables.read(tok2->varId(), tok);
1142  }
1143  }
1144  } else if (tok->variable() && tok->variable()->isClass() && tok->variable()->type() &&
1145  (tok->variable()->type()->needInitialization == Type::NeedInitialization::False) &&
1146  tok->next()->str() == ";") {
1147  variables.write(tok->varId(), tok);
1148  }
1149  }
1150 }
1151 
1153 {
1155  return;
1156 
1157  logChecker("CheckUnusedVar::checkFunctionVariableUsage"); // style
1158 
1159  // Parse all executing scopes..
1160  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1161 
1162  auto reportLibraryCfgError = [this](const Token* tok, const std::string& typeName) {
1163  if (mSettings->checkLibrary) {
1164  reportError(tok,
1166  "checkLibraryCheckType",
1167  "--check-library: Provide <type-checks><unusedvar> configuration for " + typeName);
1168  }
1169  };
1170 
1171  // only check functions
1172  for (const Scope * scope : symbolDatabase->functionScopes) {
1173  // Bailout when there are lambdas or inline functions
1174  // TODO: Handle lambdas and inline functions properly
1175  if (scope->hasInlineOrLambdaFunction())
1176  continue;
1177 
1178  for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1179  if (findLambdaEndToken(tok))
1180  // todo: handle lambdas
1181  break;
1182  if (Token::simpleMatch(tok, "try {"))
1183  // todo: check try blocks
1184  tok = tok->linkAt(1);
1185  const Token *varDecl = nullptr;
1186  if (tok->variable() && tok->variable()->nameToken() == tok) {
1187  const Token * eq = tok->next();
1188  while (Token::simpleMatch(eq, "["))
1189  eq = eq->link()->next();
1190  if (Token::simpleMatch(eq, ") (") && Token::simpleMatch(eq->linkAt(1), ") ="))
1191  eq = eq->linkAt(1)->next();
1192  if (Token::simpleMatch(eq, "=")) {
1193  varDecl = tok;
1194  tok = eq;
1195  }
1196  }
1197  // not assignment/initialization/increment => continue
1198  const bool isAssignment = tok->isAssignmentOp() && tok->astOperand1();
1199  const bool isInitialization = (Token::Match(tok, "%var% (|{") && tok->variable() && tok->variable()->nameToken() == tok);
1200  const bool isIncrementOrDecrement = (tok->tokType() == Token::Type::eIncDecOp);
1201  if (!isAssignment && !isInitialization && !isIncrementOrDecrement)
1202  continue;
1203 
1204  bool isTrivialInit = false;
1205  if (isInitialization && Token::Match(tok, "%var% { }")) // don't warn for trivial initialization
1206  isTrivialInit = true;
1207 
1208  if (isIncrementOrDecrement && tok->astParent() && precedes(tok, tok->astOperand1()))
1209  continue;
1210 
1211  if (tok->str() == "=" && !(tok->valueType() && tok->valueType()->pointer) && isRaiiClass(tok->valueType(), tok->isCpp(), false))
1212  continue;
1213 
1214  const bool isPointer = tok->valueType() && (tok->valueType()->pointer || tok->valueType()->type == ValueType::SMART_POINTER);
1215 
1216  if (tok->isName()) {
1217  if (isRaiiClass(tok->valueType(), tok->isCpp(), false) ||
1218  (tok->valueType() && tok->valueType()->type == ValueType::RECORD &&
1220  continue;
1221  tok = tok->next();
1222  }
1223  if (!isInitialization && tok->astParent() && !tok->astParent()->isAssignmentOp() && tok->str() != "(") {
1224  const Token *parent = tok->astParent();
1225  while (Token::Match(parent, "%oror%|%comp%|!|&&"))
1226  parent = parent->astParent();
1227  if (!parent)
1228  continue;
1229  if (!Token::simpleMatch(parent->previous(), "if ("))
1230  continue;
1231  }
1232  // Do not warn about assignment with NULL
1233  if (isPointer && isNullOperand(tok->astOperand2()))
1234  continue;
1235 
1236  if (!tok->astOperand1())
1237  continue;
1238 
1239  const Token *iteratorToken = tok->astOperand1();
1240  while (Token::Match(iteratorToken, "[.*]"))
1241  iteratorToken = iteratorToken->astOperand1();
1242  if (iteratorToken && iteratorToken->variable() && iteratorToken->variable()->typeEndToken()->str().find("iterator") != std::string::npos)
1243  continue;
1244 
1245  const Token *op1tok = tok->astOperand1();
1246  while (Token::Match(op1tok, ".|[|*"))
1247  op1tok = op1tok->astOperand1();
1248 
1249  // Assignment in macro => do not warn
1250  if (isAssignment && tok->isExpandedMacro() && op1tok && op1tok->isExpandedMacro())
1251  continue;
1252 
1253  const Variable *op1Var = op1tok ? op1tok->variable() : nullptr;
1254  if (!op1Var && Token::Match(tok, "(|{") && tok->previous() && tok->previous()->variable())
1255  op1Var = tok->previous()->variable();
1256  std::string bailoutTypeName;
1257  if (op1Var) {
1258  if (op1Var->isReference() && op1Var->nameToken() != tok->astOperand1())
1259  // todo: check references
1260  continue;
1261 
1262  if (op1Var->isStatic())
1263  // todo: check static variables
1264  continue;
1265 
1266  if (op1Var->nameToken()->isAttributeUnused())
1267  continue;
1268 
1269  // Avoid FP for union..
1270  if (op1Var->type() && op1Var->type()->isUnionType())
1271  continue;
1272 
1273  // Bailout for unknown template classes, we have no idea what side effects such assignments have
1274  if (mTokenizer->isCPP() &&
1275  op1Var->isClass() &&
1276  (!op1Var->valueType() || op1Var->valueType()->type == ValueType::Type::UNKNOWN_TYPE)) {
1277  // Check in the library if we should bailout or not..
1278  std::string typeName = op1Var->getTypeName();
1279  if (startsWith(typeName, "::"))
1280  typeName.erase(typeName.begin(), typeName.begin() + 2);
1281  switch (mSettings->library.getTypeCheck("unusedvar", typeName)) {
1283  bailoutTypeName = typeName;
1284  break;
1286  break;
1289  continue;
1290  }
1291  }
1292  }
1293 
1294  // Is there a redundant assignment?
1295  const Token *start = tok->findExpressionStartEndTokens().second->next();
1296 
1297  const Token *expr = varDecl ? varDecl : tok->astOperand1();
1298 
1299  if (isInitialization)
1300  expr = tok->previous();
1301 
1302  // Is variable in lhs a union member?
1303  if (tok->previous() && tok->previous()->variable() && tok->previous()->variable()->nameToken()->scope()->type == Scope::eUnion)
1304  continue;
1305 
1306  FwdAnalysis fwdAnalysis(*mSettings);
1307  const Token* scopeEnd = ValueFlow::getEndOfExprScope(expr, scope, /*smallest*/ false);
1308  if (fwdAnalysis.unusedValue(expr, start, scopeEnd)) {
1309  if (!bailoutTypeName.empty()) {
1310  if (bailoutTypeName != "auto")
1311  reportLibraryCfgError(tok, bailoutTypeName);
1312  continue;
1313  }
1314  if (isTrivialInit && findExpressionChanged(expr, start, scopeEnd, *mSettings))
1315  continue;
1316 
1317  // warn
1318  if (!expr->variable() || !expr->variable()->isMaybeUnused())
1319  unreadVariableError(tok, expr->expressionString(), false);
1320  }
1321  }
1322 
1323  // varId, usage {read, write, modified}
1324  Variables variables;
1325 
1326  checkFunctionVariableUsage_iterateScopes(scope, variables);
1327 
1328 
1329  // Check usage of all variables in the current scope..
1330  for (std::map<nonneg int, Variables::VariableUsage>::const_iterator it = variables.varUsage().cbegin();
1331  it != variables.varUsage().cend();
1332  ++it) {
1333  const Variables::VariableUsage &usage = it->second;
1334 
1335  // variable has been marked as unused so ignore it
1336  if (usage._var->nameToken()->isAttributeUnused() || usage._var->nameToken()->isAttributeUsed())
1337  continue;
1338 
1339  // skip things that are only partially implemented to prevent false positives
1340  if (usage.mType == Variables::pointerPointer ||
1341  usage.mType == Variables::pointerArray ||
1343  continue;
1344 
1345  const std::string &varname = usage._var->name();
1346  const Variable* var = symbolDatabase->getVariableFromVarId(it->first);
1347 
1348  // variable has had memory allocated for it, but hasn't done
1349  // anything with that memory other than, perhaps, freeing it
1350  if (usage.unused() && !usage._modified && usage._allocateMemory)
1352 
1353  // variable has not been written, read, or modified
1354  else if (usage.unused() && !usage._modified) {
1355  if (!usage._var->isMaybeUnused()) {
1356  unusedVariableError(usage._var->nameToken(), varname);
1357  }
1358  }
1359  // variable has not been written but has been modified
1360  else if (usage._modified && !usage._write && !usage._allocateMemory && var && !var->isStlType()) {
1361  if (var->isStatic()) // static variables are initialized by default
1362  continue;
1363  unassignedVariableError(usage._var->nameToken(), varname);
1364  }
1365  // variable has been read but not written
1366  else if (!usage._write && !usage._allocateMemory && var && !var->isStlType() && !isEmptyType(var->type()) &&
1368  unassignedVariableError(usage._var->nameToken(), varname);
1369  else if (!usage._var->isMaybeUnused() && !usage._modified && !usage._read && var) {
1370  const Token* vnt = var->nameToken();
1371  bool error = false;
1372  if (vnt->next()->isSplittedVarDeclEq() || (!var->isReference() && vnt->next()->str() == "=")) {
1373  const Token* nextStmt = vnt->tokAt(2);
1374  if (nextStmt->isExpandedMacro()) {
1375  const Token* parent = nextStmt;
1376  while (parent->astParent() && parent == parent->astParent()->astOperand1())
1377  parent = parent->astParent();
1378  if (parent->isAssignmentOp() && parent->isExpandedMacro())
1379  continue;
1380  }
1381  while (nextStmt && nextStmt->str() != ";")
1382  nextStmt = nextStmt->next();
1383  error = precedes(usage._lastAccess, nextStmt);
1384  }
1385  if (error) {
1386  if (mTokenizer->isCPP() && var->isClass() &&
1387  (!var->valueType() || var->valueType()->type == ValueType::Type::UNKNOWN_TYPE)) {
1388  const std::string typeName = var->getTypeName();
1389  switch (mSettings->library.getTypeCheck("unusedvar", typeName)) {
1391  reportLibraryCfgError(vnt, typeName);
1392  break;
1394  break;
1397  error = false;
1398  }
1399  }
1400  if (error)
1401  unreadVariableError(vnt, varname, false);
1402  }
1403  }
1404  }
1405  }
1406 }
1407 
1408 void CheckUnusedVar::unusedVariableError(const Token *tok, const std::string &varname)
1409 {
1410  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedVariable"))
1411  return;
1412 
1413  reportError(tok, Severity::style, "unusedVariable", "$symbol:" + varname + "\nUnused variable: $symbol", CWE563, Certainty::normal);
1414 }
1415 
1416 void CheckUnusedVar::allocatedButUnusedVariableError(const Token *tok, const std::string &varname)
1417 {
1418  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedVariable"))
1419  return;
1420 
1421  reportError(tok, Severity::style, "unusedAllocatedMemory", "$symbol:" + varname + "\nVariable '$symbol' is allocated memory that is never used.", CWE563, Certainty::normal);
1422 }
1423 
1424 void CheckUnusedVar::unreadVariableError(const Token *tok, const std::string &varname, bool modified)
1425 {
1426  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedVariable"))
1427  return;
1428 
1429  if (modified)
1430  reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is modified but its new value is never used.", CWE563, Certainty::normal);
1431  else
1432  reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is assigned a value that is never used.", CWE563, Certainty::normal);
1433 }
1434 
1435 void CheckUnusedVar::unassignedVariableError(const Token *tok, const std::string &varname)
1436 {
1437  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedVariable"))
1438  return;
1439 
1440  reportError(tok, Severity::style, "unassignedVariable", "$symbol:" + varname + "\nVariable '$symbol' is not assigned a value.", CWE665, Certainty::normal);
1441 }
1442 
1443 //---------------------------------------------------------------------------
1444 // Check that all struct members are used
1445 //---------------------------------------------------------------------------
1447 {
1448  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedVariable"))
1449  return;
1450 
1451  logChecker("CheckUnusedVar::checkStructMemberUsage"); // style
1452 
1453  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
1454 
1455  for (const Scope &scope : symbolDatabase->scopeList) {
1456  if (scope.type != Scope::eStruct && scope.type != Scope::eClass && scope.type != Scope::eUnion)
1457  continue;
1458 
1459  if (scope.bodyStart->fileIndex() != 0 || scope.className.empty())
1460  continue;
1461 
1462  if (scope.classDef->isExpandedMacro())
1463  continue;
1464 
1465  // Packed struct => possibly used by lowlevel code. Struct members might be required by hardware.
1466  if (scope.bodyEnd->isAttributePacked())
1467  continue;
1468  if (mTokenizer->isPacked(scope.bodyStart))
1469  continue;
1470 
1471  // Bail out for template struct, members might be used in non-matching instantiations
1472  if (scope.className.find('<') != std::string::npos)
1473  continue;
1474 
1475  // bail out if struct is inherited
1476  const bool isInherited = std::any_of(symbolDatabase->scopeList.cbegin(), symbolDatabase->scopeList.cend(), [&](const Scope& derivedScope) {
1477  const Type* dType = derivedScope.definedType;
1478  return dType && std::any_of(dType->derivedFrom.cbegin(), dType->derivedFrom.cend(), [&](const Type::BaseInfo& derivedFrom) {
1479  return derivedFrom.type == scope.definedType && derivedFrom.access != AccessControl::Private;
1480  });
1481  });
1482 
1483  // bail out for extern/global struct
1484  bool bailout = false;
1485  for (const Variable* var : symbolDatabase->variableList()) {
1486  if (var && (var->isExtern() || (var->isGlobal() && !var->isStatic())) && var->typeEndToken()->str() == scope.className) {
1487  bailout = true;
1488  break;
1489  }
1490  if (bailout)
1491  break;
1492  }
1493  if (bailout)
1494  continue;
1495 
1496  // Bail out if some data is casted to struct..
1497  const std::string castPattern("( struct| " + scope.className + " * ) &| %name%");
1498  if (Token::findmatch(scope.bodyEnd, castPattern.c_str()))
1499  continue;
1500 
1501  // (struct S){..}
1502  const std::string initPattern("( struct| " + scope.className + " ) {");
1503  if (Token::findmatch(scope.bodyEnd, initPattern.c_str()))
1504  continue;
1505 
1506  // Bail out if struct is used in sizeof..
1507  for (const Token *tok = scope.bodyEnd; nullptr != (tok = Token::findsimplematch(tok, "sizeof ("));) {
1508  tok = tok->tokAt(2);
1509  if (Token::Match(tok, ("struct| " + scope.className).c_str())) {
1510  bailout = true;
1511  break;
1512  }
1513  }
1514  if (bailout)
1515  continue;
1516 
1517  for (const Variable &var : scope.varlist) {
1518  // only warn for variables without side effects
1520  continue;
1521  if (isInherited && !var.isPrivate())
1522  continue;
1523 
1524  // Check if the struct member variable is used anywhere in the file
1525  bool use = false;
1526  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
1527  if (Token::Match(tok, ". %name%") && !tok->next()->variable() && !tok->next()->function() && tok->next()->str() == var.name()) {
1528  // not known => assume variable is used
1529  use = true;
1530  break;
1531  }
1532  if (tok->variable() != &var)
1533  continue;
1534  if (tok != var.nameToken()) {
1535  use = true;
1536  break;
1537  }
1538  }
1539  if (!use) {
1540  std::string prefix = "struct";
1541  if (scope.type == Scope::ScopeType::eClass)
1542  prefix = "class";
1543  else if (scope.type == Scope::ScopeType::eUnion)
1544  prefix = "union";
1545  unusedStructMemberError(var.nameToken(), scope.className, var.name(), prefix);
1546  }
1547  }
1548  }
1549 }
1550 
1551 void CheckUnusedVar::unusedStructMemberError(const Token* tok, const std::string& structname, const std::string& varname, const std::string& prefix)
1552 {
1553  reportError(tok, Severity::style, "unusedStructMember", "$symbol:" + structname + "::" + varname + '\n' + prefix + " member '$symbol' is never used.", CWE563, Certainty::normal);
1554 }
1555 
1557 {
1558  // a type that has no side effects (no constructors and no members with constructors)
1559  /** @todo false negative: check constructors for side effects */
1560  const std::pair<std::map<const Type *,bool>::iterator,bool> found=mIsRecordTypeWithoutSideEffectsMap.insert(
1561  std::pair<const Type *,bool>(type,false)); //Initialize with side effects for possible recursions
1562  bool & withoutSideEffects = found.first->second;
1563  if (!found.second)
1564  return withoutSideEffects;
1565 
1566  // unknown types are assumed to have side effects
1567  if (!type || !type->classScope)
1568  return (withoutSideEffects = false);
1569 
1570  // Non-empty constructors => possible side effects
1571  for (const Function& f : type->classScope->functionList) {
1572  if (!f.isConstructor() && !f.isDestructor())
1573  continue;
1574  if (f.argDef && Token::simpleMatch(f.argDef->link(), ") ="))
1575  continue; // ignore default/deleted constructors
1576  const bool emptyBody = (f.functionScope && Token::simpleMatch(f.functionScope->bodyStart, "{ }"));
1577 
1578  const Token* nextToken = f.argDef->link();
1579  if (Token::simpleMatch(nextToken, ") :")) {
1580  // validating initialization list
1581  nextToken = nextToken->next(); // goto ":"
1582 
1583  for (const Token *initListToken = nextToken; Token::Match(initListToken, "[:,] %var% [({]"); initListToken = initListToken->linkAt(2)->next()) {
1584  const Token* varToken = initListToken->next();
1585  const Variable* variable = varToken->variable();
1586  if (variable && !isVariableWithoutSideEffects(*variable)) {
1587  return withoutSideEffects = false;
1588  }
1589 
1590  const Token* valueEnd = initListToken->linkAt(2);
1591  for (const Token* valueToken = initListToken->tokAt(3); valueToken != valueEnd; valueToken = valueToken->next()) {
1592  const Variable* initValueVar = valueToken->variable();
1593  if (initValueVar && !isVariableWithoutSideEffects(*initValueVar)) {
1594  return withoutSideEffects = false;
1595  }
1596  if ((valueToken->tokType() == Token::Type::eName) ||
1597  (valueToken->tokType() == Token::Type::eLambda) ||
1598  (valueToken->tokType() == Token::Type::eOther)) {
1599  return withoutSideEffects = false;
1600  }
1601  const Function* initValueFunc = valueToken->function();
1602  if (initValueFunc && !isFunctionWithoutSideEffects(*initValueFunc, valueToken,
1603  std::list<const Function*> {})) {
1604  return withoutSideEffects = false;
1605  }
1606  }
1607  }
1608  }
1609 
1610  if (!emptyBody)
1611  return (withoutSideEffects = false);
1612  }
1613 
1614  // Derived from type that has side effects?
1615  if (std::any_of(type->derivedFrom.cbegin(), type->derivedFrom.cend(), [this](const Type::BaseInfo& derivedFrom) {
1616  return !isRecordTypeWithoutSideEffects(derivedFrom.type);
1617  }))
1618  return (withoutSideEffects = false);
1619 
1620  // Is there a member variable with possible side effects
1621  for (const Variable& var : type->classScope->varlist) {
1622  withoutSideEffects = isVariableWithoutSideEffects(var, type);
1623  if (!withoutSideEffects) {
1624  return withoutSideEffects;
1625  }
1626  }
1627 
1628 
1629  return (withoutSideEffects = true);
1630 }
1631 
1633 {
1634  const Type* variableType = var.type();
1635  if (variableType && variableType != type) {
1636  if (!isRecordTypeWithoutSideEffects(variableType))
1637  return false;
1638  } else {
1639  if (WRONG_DATA(!var.valueType(), var.typeStartToken()))
1640  return false;
1641  const ValueType::Type valueType = var.valueType()->type;
1642  if ((valueType == ValueType::Type::UNKNOWN_TYPE) || (valueType == ValueType::Type::NONSTD))
1643  return false;
1644  }
1645 
1646  return true;
1647 }
1648 
1650 {
1651  // a type that has no variables and no constructor
1652 
1653  const std::pair<std::map<const Type *,bool>::iterator,bool> found=mIsEmptyTypeMap.insert(
1654  std::pair<const Type *,bool>(type,false));
1655  bool & emptyType=found.first->second;
1656  if (!found.second)
1657  return emptyType;
1658 
1659  if (type && type->classScope && type->classScope->numConstructors == 0 &&
1660  (type->classScope->varlist.empty())) {
1661  return (emptyType = std::all_of(type->derivedFrom.cbegin(), type->derivedFrom.cend(), [this](const Type::BaseInfo& bi) {
1662  return isEmptyType(bi.type);
1663  }));
1664  }
1665 
1666  // unknown types are assumed to be nonempty
1667  return (emptyType = false);
1668 }
1669 
1670 bool CheckUnusedVar::isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken,
1671  std::list<const Function*> checkedFuncs)
1672 {
1673  // no body to analyze
1674  if (!func.hasBody()) {
1675  return false;
1676  }
1677 
1678  for (const Token* argsToken = functionUsageToken->next(); !Token::simpleMatch(argsToken, ")"); argsToken = argsToken->next()) {
1679  const Variable* argVar = argsToken->variable();
1680  if (argVar && argVar->isGlobal()) {
1681  return false; // TODO: analyze global variable usage
1682  }
1683  }
1684 
1685  bool sideEffectReturnFound = false;
1686  std::set<const Variable*> pointersToGlobals;
1687  for (const Token* bodyToken = func.functionScope->bodyStart->next(); bodyToken != func.functionScope->bodyEnd;
1688  bodyToken = bodyToken->next()) {
1689  // check variable inside function body
1690  const Variable* bodyVariable = bodyToken->variable();
1691  if (bodyVariable) {
1692  if (!isVariableWithoutSideEffects(*bodyVariable)) {
1693  return false;
1694  }
1695  // check if global variable is changed
1696  if (bodyVariable->isGlobal() || (pointersToGlobals.find(bodyVariable) != pointersToGlobals.end())) {
1697  const int indirect = bodyVariable->isArray() ? bodyVariable->dimensions().size() : bodyVariable->isPointer();
1698  if (isVariableChanged(bodyToken, indirect, *mSettings)) {
1699  return false;
1700  }
1701  // check if pointer to global variable assigned to another variable (another_var = &global_var)
1702  if (Token::simpleMatch(bodyToken->tokAt(-1), "&") && Token::simpleMatch(bodyToken->tokAt(-2), "=")) {
1703  const Token* assigned_var_token = bodyToken->tokAt(-3);
1704  if (assigned_var_token && assigned_var_token->variable()) {
1705  pointersToGlobals.insert(assigned_var_token->variable());
1706  }
1707  }
1708  }
1709  }
1710 
1711  // check nested function
1712  const Function* bodyFunction = bodyToken->function();
1713  if (bodyFunction) {
1714  if (std::find(checkedFuncs.cbegin(), checkedFuncs.cend(), bodyFunction) != checkedFuncs.cend()) { // recursion found
1715  continue;
1716  }
1717  checkedFuncs.push_back(bodyFunction);
1718  if (!isFunctionWithoutSideEffects(*bodyFunction, bodyToken, checkedFuncs)) {
1719  return false;
1720  }
1721  }
1722 
1723  // check returned value
1724  if (Token::simpleMatch(bodyToken, "return")) {
1725  const Token* returnValueToken = bodyToken->next();
1726  // TODO: handle complex return expressions
1727  if (!Token::simpleMatch(returnValueToken->next(), ";")) {
1728  sideEffectReturnFound = true;
1729  continue;
1730  }
1731  // simple one-token return
1732  const Variable* returnVariable = returnValueToken->variable();
1733  if (returnValueToken->isLiteral() ||
1734  (returnVariable && isVariableWithoutSideEffects(*returnVariable))) {
1735  continue;
1736  }
1737  sideEffectReturnFound = true;
1738  }
1739 
1740  // unknown name
1741  if (bodyToken->isNameOnly()) {
1742  return false;
1743  }
1744  }
1745 
1746  return !sideEffectReturnFound;
1747 }
bool astIsContainer(const Token *tok)
Definition: astutils.cpp:244
bool precedes(const Token *tok1, const Token *tok2)
If tok2 comes after tok1.
Definition: astutils.cpp:987
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 * findExpressionChanged(const Token *expr, const Token *start, const Token *end, const Settings &settings, int depth)
Definition: astutils.cpp:3023
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 isNullOperand(const Token *expr)
Definition: astutils.cpp:3495
const Token * findLambdaEndToken(const Token *first)
find lambda function end token
Definition: astutils.cpp:3190
bool isVariableChanged(const Token *tok, int indirect, const Settings &settings, int depth)
Definition: astutils.cpp:2541
#define WRONG_DATA(COND, TOK)
Use WRONG_DATA in checkers to mark conditions that check that data is correct.
Definition: check.h:50
static const CWE CWE563(563U)
static const Token * doAssignment(Variables &variables, const Token *tok, bool dereference, const Scope *scope)
static const Token * skipBracketsAndMembers(const Token *tok)
static bool isRaiiClassScope(const Scope *classScope)
Is scope a raii class scope.
static const CWE CWE665(665U)
static bool isRaiiClass(const ValueType *valueType, bool cpp, bool defaultReturn=true)
Is ValueType a raii class?
static bool isPartOfClassStructUnion(const Token *tok)
static void useFunctionArgs(const Token *tok, Variables &variables)
static const Token * skipBrackets(const Token *tok)
static bool isVarDecl(const Token *tok)
Various small checks.
void unassignedVariableError(const Token *tok, const std::string &varname)
std::map< const Type *, bool > mIsEmptyTypeMap
bool isFunctionWithoutSideEffects(const Function &func, const Token *functionUsageToken, std::list< const Function * > checkedFuncs)
void allocatedButUnusedVariableError(const Token *tok, const std::string &varname)
bool isRecordTypeWithoutSideEffects(const Type *type)
void unusedVariableError(const Token *tok, const std::string &varname)
void unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname, const std::string &prefix="struct")
void checkStructMemberUsage()
Check that all struct members are used
void unreadVariableError(const Token *tok, const std::string &varname, bool modified)
void checkFunctionVariableUsage()
void checkFunctionVariableUsage_iterateScopes(const Scope *const scope, Variables &variables)
Check for unused function variables
bool isVariableWithoutSideEffects(const Variable &var, const Type *type=nullptr)
std::map< const Type *, bool > mIsRecordTypeWithoutSideEffectsMap
bool isEmptyType(const Type *type)
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
const Token * argDef
function argument start '(' in class definition
const Scope * functionScope
scope of function body
bool hasBody() const
bool isDestructor() const
bool isConstructor() const
Forward data flow analysis for checks.
Definition: fwdanalysis.h:38
bool unusedValue(const Token *expr, const Token *startToken, const Token *endToken)
Check if "expr" is used.
static bool ismemory(const int id)
is allocation type memory?
Definition: library.h:140
const AllocFunc * getAllocFuncInfo(const Token *tok) const
get allocation info for function
Definition: library.cpp:1077
TypeCheck getTypeCheck(std::string check, std::string typeName) const
Definition: library.cpp:1735
const AllocFunc * getDeallocFuncInfo(const Token *tok) const
get deallocation info for function
Definition: library.cpp:1086
const Container * detectContainer(const Token *typeStart) const
Definition: library.cpp:1231
std::list< Function > functionList
bool hasInlineOrLambdaFunction() const
std::list< Variable > varlist
std::vector< Scope * > nestedList
ScopeType type
const Function * getDestructor() const
Type * definedType
const Token * classDef
class/struct/union/namespace token
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
nonneg int numConstructors
std::string className
bool isExecutable() const
bool checkLibrary
Check for incomplete info in library files?
Definition: settings.h:135
Library library
Library.
Definition: settings.h:237
bool isPremiumEnabled(const char id[]) const
Is checker id enabled by premiumArgs.
Definition: settings.cpp:557
SimpleEnableGroup< Severity > severity
Definition: settings.h:358
bool isEnabled(T flag) const
Definition: settings.h:66
const Variable * getVariableFromVarId(nonneg int varId) const
const std::vector< const Variable * > & variableList() const
std::vector< const Scope * > functionScopes
Fast access to function scopes.
std::list< Scope > scopeList
Information about all namespaces/classes/structures.
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
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 isName() const
Definition: token.h:361
bool isAttributeUsed() const
Definition: token.h:482
static const Token * findmatch(const Token *const startTok, const char pattern[], const nonneg int varId=0)
Definition: token.cpp:1099
bool isExpandedMacro() const
Definition: token.h:455
bool isExtendedOp() const
Definition: token.h:391
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
bool isAttributeUnused() const
Definition: token.h:476
void astOperand1(Token *tok)
Definition: token.cpp:1490
void function(const Function *f)
Associate this token with given function.
Definition: token.cpp:1127
std::pair< const Token *, const Token * > findExpressionStartEndTokens() const
Definition: token.cpp:1548
nonneg int varId() const
Definition: token.h:870
std::string expressionString() const
Definition: token.cpp:1681
bool isUnaryOp(const std::string &s) const
Definition: token.h:413
bool isLiteral() const
Definition: token.h:368
static const Token * findsimplematch(const Token *const startTok, const char(&pattern)[count])
Definition: token.h:763
const Token * tokAt(int index) const
Definition: token.cpp:427
Token::Type tokType() const
Definition: token.h:343
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
@ eIncDecOp
Definition: token.h:163
Token * previous()
Definition: token.h:862
bool isAssignmentOp() const
Definition: token.h:401
bool isSplittedVarDeclEq() const
Definition: token.h:615
bool isStandardType() const
Definition: token.h:449
void variable(const Variable *v)
Associate this token with given variable.
Definition: token.h:1070
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
nonneg int fileIndex() const
Definition: token.h:809
bool isAttributePacked() const
Definition: token.h:518
void astParent(Token *tok)
Definition: token.cpp:1471
bool isPacked(const Token *bodyStart) const
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
Information about a class type.
std::vector< BaseInfo > derivedFrom
enum Type::NeedInitialization needInitialization
const Scope * classScope
bool isUnionType() const
Value type.
enum ValueType::Type type
nonneg int pointer
0=>not pointer, 1=>*, 2=>**, 3=>***, etc
const Scope * typeScope
if the type definition is seen this point out the type scope
const ::Type * smartPointerType
Smart pointer type.
Information about a member variable.
bool isClass() const
Is variable a user defined (or unknown) type.
bool isReference() const
Is reference variable.
std::string getTypeName() const
bool isMaybeUnused() const
bool isStlType() const
Checks if the variable is an STL type ('std::') E.g.
const Type * type() const
Get Type pointer of known type.
const Scope * scope() const
Get Scope pointer of enclosing scope.
bool isGlobal() const
Is variable global.
const std::string & name() const
Get name string.
const Token * typeEndToken() const
Get type end token.
bool isArray() const
Is variable an array.
const Token * nameToken() const
Get name token.
bool isPrivate() const
Is variable private.
nonneg int declarationId() const
Get declaration ID (varId used for variable in its declaration).
const Token * typeStartToken() const
Get type start token.
const std::vector< Dimension > & dimensions() const
Get array dimensions.
bool isPointer() const
Is pointer variable.
bool isStatic() const
Is variable static.
const ValueType * valueType() const
Store information about variable usage.
VariableUsage(const Variable *var=nullptr, VariableType type=standard, bool read=false, bool write=false, bool modified=false, bool allocateMemory=false)
void use()
variable is used.
bool unused() const
is variable unused?
std::set< nonneg int > _aliases
std::set< const Scope * > _assignments
This class is used create a list of variables within a function.
void eraseAliases(nonneg int varid)
void writeAll(nonneg int varid, const Token *tok)
void readAll(nonneg int varid, const Token *tok)
void writeAliases(nonneg int varid, const Token *tok)
void modified(nonneg int varid, const Token *tok)
void readAliases(nonneg int varid, const Token *tok)
std::map< nonneg int, VariableUsage > mVarUsage
void use(nonneg int varid, const Token *tok)
void read(nonneg int varid, const Token *tok)
VariableUsage * find(nonneg int varid)
void write(nonneg int varid, const Token *tok)
void clearAliases(nonneg int varid)
void erase(nonneg int varid)
void alias(nonneg int varid1, nonneg int varid2, bool replace)
Alias the 2 given variables.
void eraseAll(nonneg int varid)
void allocateMemory(nonneg int varid, const Token *tok)
const std::map< nonneg int, VariableUsage > & varUsage() const
void addVar(const Variable *var, VariableType type, bool write_)
#define nonneg
Definition: config.h:138
static void replace(std::string &source, const std::unordered_map< std::string, std::string > &substitutionMap)
@ style
Style warning.
@ information
Checking information.
@ error
Programming error.
const Token * getEndOfExprScope(const Token *tok, const Scope *defaultScope=nullptr, bool smallest=true)
Definition: valueflow.cpp:3974
bool startsWith(const std::string &str, const char start[], std::size_t startlen)
Definition: utils.h:94
bool endsWith(const std::string &str, char c)
Definition: utils.h:110
#define bailout(tokenlist, errorLogger, tok, what)
Definition: valueflow.cpp:144