Cppcheck
checkclass.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 #include "checkclass.h"
21 
22 #include "astutils.h"
23 #include "library.h"
24 #include "settings.h"
25 #include "standards.h"
26 #include "symboldatabase.h"
27 #include "errorlogger.h"
28 #include "errortypes.h"
29 #include "platform.h"
30 #include "token.h"
31 #include "tokenize.h"
32 #include "tokenlist.h"
33 #include "utils.h"
34 #include "valueflow.h"
35 
36 #include <algorithm>
37 #include <cctype>
38 #include <cstring>
39 #include <iterator>
40 #include <utility>
41 #include <unordered_map>
42 
43 #include "xml.h"
44 
45 namespace CTU {
46  class FileInfo;
47 }
48 
49 //---------------------------------------------------------------------------
50 
51 // Register CheckClass..
52 namespace {
53  CheckClass instance;
54 }
55 
56 static const CWE CWE398(398U); // Indicator of Poor Code Quality
57 static const CWE CWE404(404U); // Improper Resource Shutdown or Release
58 static const CWE CWE665(665U); // Improper Initialization
59 static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
60 static const CWE CWE762(762U); // Mismatched Memory Management Routines
61 
62 static const CWE CWE_ONE_DEFINITION_RULE(758U);
63 
64 static const char * getFunctionTypeName(Function::Type type)
65 {
66  switch (type) {
68  return "constructor";
70  return "copy constructor";
72  return "move constructor";
74  return "destructor";
76  return "function";
78  return "operator=";
79  case Function::eLambda:
80  return "lambda";
81  }
82  return "";
83 }
84 
85 static bool isVariableCopyNeeded(const Variable &var, Function::Type type)
86 {
87  bool isOpEqual = false;
88  switch (type) {
90  isOpEqual = true;
91  break;
94  break;
95  default:
96  return true;
97  }
98 
99  return (!var.hasDefault() || isOpEqual) && // default init does not matter for operator=
100  (var.isPointer() ||
102  (var.valueType() && var.valueType()->type >= ValueType::Type::CHAR));
103 }
104 
105 static bool isVclTypeInit(const Type *type)
106 {
107  if (!type)
108  return false;
109  return std::any_of(type->derivedFrom.begin(), type->derivedFrom.end(), [&](const Type::BaseInfo& baseInfo) {
110  if (!baseInfo.type)
111  return true;
112  if (isVclTypeInit(baseInfo.type))
113  return true;
114  return false;
115  });
116 }
117 //---------------------------------------------------------------------------
118 
119 CheckClass::CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
120  : Check(myName(), tokenizer, settings, errorLogger),
121  mSymbolDatabase(tokenizer?tokenizer->getSymbolDatabase():nullptr)
122 {}
123 
124 //---------------------------------------------------------------------------
125 // ClassCheck: Check that all class constructors are ok.
126 //---------------------------------------------------------------------------
127 
129 {
130  const bool printStyle = mSettings->severity.isEnabled(Severity::style);
131  const bool printWarnings = mSettings->severity.isEnabled(Severity::warning);
132  if (!printStyle && !printWarnings && !mSettings->isPremiumEnabled("uninitMemberVar"))
133  return;
134 
135  logChecker("CheckClass::checkConstructors"); // style,warning
136 
137  const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
138  for (const Scope * scope : mSymbolDatabase->classAndStructScopes) {
139  if (mSettings->hasLib("vcl") && isVclTypeInit(scope->definedType))
140  continue;
141 
142  const bool unusedTemplate = Token::simpleMatch(scope->classDef->previous(), ">");
143 
144  const bool usedInUnion = std::any_of(mSymbolDatabase->scopeList.cbegin(), mSymbolDatabase->scopeList.cend(), [&](const Scope& unionScope) {
145  if (unionScope.type != Scope::eUnion)
146  return false;
147  return std::any_of(unionScope.varlist.cbegin(), unionScope.varlist.cend(), [&](const Variable& var) {
148  return var.type() && var.type()->classScope == scope;
149  });
150  });
151 
152  // There are no constructors.
153  if (scope->numConstructors == 0 && printStyle && !usedInUnion) {
154  // If there is a private variable, there should be a constructor..
155  int needInit = 0, haveInit = 0;
156  std::vector<const Variable*> uninitVars;
157  for (const Variable &var : scope->varlist) {
158  if (var.isPrivate() && !var.isStatic() &&
159  (!var.isClass() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True))) {
160  ++needInit;
161  if (!var.isInit() && !var.hasDefault() && var.nameToken()->scope() == scope) // don't warn for anonymous union members
162  uninitVars.emplace_back(&var);
163  else
164  ++haveInit;
165  }
166  }
167  if (needInit > haveInit) {
168  if (haveInit == 0)
169  noConstructorError(scope->classDef, scope->className, scope->classDef->str() == "struct");
170  else
171  for (const Variable* uv : uninitVars)
172  uninitVarError(uv->typeStartToken(), uv->scope()->className, uv->name());
173  }
174  }
175 
176  if (!printWarnings)
177  continue;
178 
179  // #3196 => bailout if there are nested unions
180  // TODO: handle union variables better
181  {
182  const bool bailout = std::any_of(scope->nestedList.cbegin(), scope->nestedList.cend(), [](const Scope* nestedScope) {
183  return nestedScope->type == Scope::eUnion;
184  });
185  if (bailout)
186  continue;
187  }
188 
189 
190  std::vector<Usage> usageList = createUsageList(scope);
191 
192  for (const Function &func : scope->functionList) {
193  if (!(func.isConstructor() && (func.hasBody() || (func.isDefault() && func.type == Function::eConstructor))) &&
194  !(func.type == Function::eOperatorEqual && func.hasBody()))
195  continue; // a defaulted constructor does not initialize primitive members
196 
197  // Bail: If initializer list is not recognized as a variable or type then skip since parsing is incomplete
198  if (unusedTemplate && func.type == Function::eConstructor) {
199  const Token *initList = func.constructorMemberInitialization();
200  if (Token::Match(initList, ": %name% (") && initList->next()->tokType() == Token::eName)
201  break;
202  }
203 
204  // Mark all variables not used
205  clearAllVar(usageList);
206 
207  // Variables with default initializers
208  for (Usage &usage : usageList) {
209  const Variable& var = *usage.var;
210 
211  // check for C++11 initializer
212  if (var.hasDefault() && func.type != Function::eOperatorEqual && func.type != Function::eCopyConstructor) { // variable still needs to be copied
213  usage.init = true;
214  }
215  }
216 
217  std::list<const Function *> callstack;
218  initializeVarList(func, callstack, scope, usageList);
219 
220  // Assign 1 union member => assign all union members
221  for (const Usage &usage : usageList) {
222  const Variable& var = *usage.var;
223  if (!usage.assign && !usage.init)
224  continue;
225  const Scope* varScope1 = var.nameToken()->scope();
226  while (varScope1->type == Scope::ScopeType::eStruct)
227  varScope1 = varScope1->nestedIn;
228  if (varScope1->type == Scope::ScopeType::eUnion) {
229  for (Usage &usage2 : usageList) {
230  const Variable& var2 = *usage2.var;
231  if (usage2.assign || usage2.init || var2.isStatic())
232  continue;
233  const Scope* varScope2 = var2.nameToken()->scope();
234  while (varScope2->type == Scope::ScopeType::eStruct)
235  varScope2 = varScope2->nestedIn;
236  if (varScope1 == varScope2)
237  usage2.assign = true;
238  }
239  }
240  }
241 
242  // Check if any variables are uninitialized
243  for (const Usage &usage : usageList) {
244  const Variable& var = *usage.var;
245 
246  if (usage.assign || usage.init || var.isStatic())
247  continue;
248 
249  if (var.valueType() && var.valueType()->pointer == 0 && var.type() && var.type()->needInitialization == Type::NeedInitialization::False && var.type()->derivedFrom.empty())
250  continue;
251 
252  if (var.isConst() && func.isOperator()) // We can't set const members in assignment operator
253  continue;
254 
255  // Check if this is a class constructor
256  if (!var.isPointer() && !var.isPointerArray() && var.isClass() && func.type == Function::eConstructor) {
257  // Unknown type so assume it is initialized
258  if (!var.type()) {
259  if (var.isStlType() && var.valueType() && var.valueType()->containerTypeToken && var.getTypeName() == "std::array") {
260  const Token* ctt = var.valueType()->containerTypeToken;
261  if (!ctt->isStandardType() &&
262  (!ctt->type() || ctt->type()->needInitialization != Type::NeedInitialization::True) &&
263  !mSettings->library.podtype(ctt->str())) // TODO: handle complex type expression
264  continue;
265  }
266  else
267  continue;
268  }
269 
270  // Known type that doesn't need initialization or
271  // known type that has member variables of an unknown type
273  continue;
274  }
275 
276  // Check if type can't be copied
277  if (!var.isPointer() && !var.isPointerArray() && var.typeScope()) {
278  if (func.type == Function::eMoveConstructor) {
279  if (canNotMove(var.typeScope()))
280  continue;
281  } else {
282  if (canNotCopy(var.typeScope()))
283  continue;
284  }
285  }
286 
287  // Is there missing member copy in copy/move constructor or assignment operator?
288  bool missingCopy = false;
289 
290  // Don't warn about unknown types in copy constructors since we
291  // don't know if they can be copied or not..
292  if (!isVariableCopyNeeded(var, func.type)) {
293  if (!printInconclusive)
294  continue;
295 
296  missingCopy = true;
297  }
298 
299  // It's non-static and it's not initialized => error
300  if (func.type == Function::eOperatorEqual) {
301  const Token *operStart = func.arg;
302 
303  bool classNameUsed = false;
304  for (const Token *operTok = operStart; operTok != operStart->link(); operTok = operTok->next()) {
305  if (operTok->str() == scope->className) {
306  classNameUsed = true;
307  break;
308  }
309  }
310 
311  if (classNameUsed && mSettings->library.getTypeCheck("operatorEqVarError", var.getTypeName()) != Library::TypeCheck::suppress)
312  operatorEqVarError(func.token, scope->className, var.name(), missingCopy);
314  // If constructor is not in scope then we maybe using a constructor from a different template specialization
315  if (!precedes(scope->bodyStart, func.tokenDef))
316  continue;
317  const Scope *varType = var.typeScope();
318  if (!varType || varType->type != Scope::eUnion) {
319  const bool derived = scope != var.scope();
320  if (func.type == Function::eConstructor &&
322  func.argCount() == 0 && func.functionScope &&
323  func.arg && func.arg->link()->next() == func.functionScope->bodyStart &&
324  func.functionScope->bodyStart->link() == func.functionScope->bodyStart->next()) {
325  // don't warn about user defined default constructor when there are other constructors
326  if (printInconclusive)
327  uninitVarError(func.token, func.access == AccessControl::Private, func.type, var.scope()->className, var.name(), derived, true);
328  } else if (missingCopy)
329  missingMemberCopyError(func.token, func.type, var.scope()->className, var.name());
330  else
331  uninitVarError(func.token, func.access == AccessControl::Private, func.type, var.scope()->className, var.name(), derived, false);
332  }
333  }
334  }
335  }
336  }
337 }
338 
340 {
341  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("noExplicitConstructor"))
342  return;
343 
344  logChecker("CheckClass::checkExplicitConstructors"); // style
345 
346  for (const Scope * scope : mSymbolDatabase->classAndStructScopes) {
347  // Do not perform check, if the class/struct has not any constructors
348  if (scope->numConstructors == 0)
349  continue;
350 
351  // Is class abstract? Maybe this test is over-simplification, but it will suffice for simple cases,
352  // and it will avoid false positives.
353  const bool isAbstractClass = std::any_of(scope->functionList.cbegin(), scope->functionList.cend(), [](const Function& func) {
354  return func.isPure();
355  });
356 
357  // Abstract classes can't be instantiated. But if there is C++11
358  // "misuse" by derived classes then these constructors must be explicit.
359  if (isAbstractClass && mSettings->standards.cpp >= Standards::CPP11)
360  continue;
361 
362  for (const Function &func : scope->functionList) {
363 
364  // We are looking for constructors, which are meeting following criteria:
365  // 1) Constructor is declared with a single parameter
366  // 2) Constructor is not declared as explicit
367  // 3) It is not a copy/move constructor of non-abstract class
368  // 4) Constructor is not marked as delete (programmer can mark the default constructor as deleted, which is ok)
369  if (!func.isConstructor() || func.isDelete() || (!func.hasBody() && func.access == AccessControl::Private))
370  continue;
371 
372  if (!func.isExplicit() &&
373  func.argCount() > 0 && func.minArgCount() < 2 &&
376  !(func.templateDef && Token::simpleMatch(func.argumentList.front().typeEndToken(), "...")) &&
377  func.argumentList.front().getTypeName() != "std::initializer_list") {
379  }
380  }
381  }
382 }
383 
384 static bool hasNonCopyableBase(const Scope *scope, bool *unknown)
385 {
386  // check if there is base class that is not copyable
387  for (const Type::BaseInfo &baseInfo : scope->definedType->derivedFrom) {
388  if (!baseInfo.type || !baseInfo.type->classScope) {
389  *unknown = true;
390  continue;
391  }
392 
393  if (hasNonCopyableBase(baseInfo.type->classScope, unknown))
394  return true;
395 
396  for (const Function &func : baseInfo.type->classScope->functionList) {
397  if (func.type != Function::eCopyConstructor)
398  continue;
399  if (func.access == AccessControl::Private || func.isDelete()) {
400  *unknown = false;
401  return true;
402  }
403  }
404  }
405  return false;
406 }
407 
409 {
411  return;
412 
413  logChecker("CheckClass::checkCopyConstructors"); // warning
414 
415  for (const Scope * scope : mSymbolDatabase->classAndStructScopes) {
416  std::map<int, const Token*> allocatedVars;
417 
418  for (const Function &func : scope->functionList) {
419  if (func.type != Function::eConstructor || !func.functionScope)
420  continue;
421  const Token* tok = func.token->linkAt(1);
422  for (const Token* const end = func.functionScope->bodyStart; tok != end; tok = tok->next()) {
423  if (Token::Match(tok, "%var% ( new") ||
424  (Token::Match(tok, "%var% ( %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) {
425  const Variable* var = tok->variable();
426  if (var && var->isPointer() && var->scope() == scope)
427  allocatedVars[tok->varId()] = tok;
428  }
429  }
430  for (const Token* const end = func.functionScope->bodyEnd; tok != end; tok = tok->next()) {
431  if (Token::Match(tok, "%var% = new") ||
432  (Token::Match(tok, "%var% = %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) {
433  const Variable* var = tok->variable();
434  if (var && var->isPointer() && var->scope() == scope && !var->isStatic())
435  allocatedVars[tok->varId()] = tok;
436  }
437  }
438  }
439 
440  if (!allocatedVars.empty()) {
441  const Function *funcCopyCtor = nullptr;
442  const Function *funcOperatorEq = nullptr;
443  const Function *funcDestructor = nullptr;
444  for (const Function &func : scope->functionList) {
445  if (func.type == Function::eCopyConstructor)
446  funcCopyCtor = &func;
447  else if (func.type == Function::eOperatorEqual)
448  funcOperatorEq = &func;
449  else if (func.type == Function::eDestructor)
450  funcDestructor = &func;
451  }
452  if (!funcCopyCtor || funcCopyCtor->isDefault()) {
453  bool unknown = false;
454  if (!hasNonCopyableBase(scope, &unknown) && !unknown)
455  noCopyConstructorError(scope, funcCopyCtor, allocatedVars.cbegin()->second, unknown);
456  }
457  if (!funcOperatorEq || funcOperatorEq->isDefault()) {
458  bool unknown = false;
459  if (!hasNonCopyableBase(scope, &unknown) && !unknown)
460  noOperatorEqError(scope, funcOperatorEq, allocatedVars.cbegin()->second, unknown);
461  }
462  if (!funcDestructor || funcDestructor->isDefault()) {
463  const Token * mustDealloc = nullptr;
464  for (std::map<int, const Token*>::const_iterator it = allocatedVars.cbegin(); it != allocatedVars.cend(); ++it) {
465  if (!Token::Match(it->second, "%var% [(=] new %type%")) {
466  mustDealloc = it->second;
467  break;
468  }
469  if (it->second->valueType() && it->second->valueType()->isIntegral()) {
470  mustDealloc = it->second;
471  break;
472  }
473  const Variable *var = it->second->variable();
474  if (var && var->typeScope() && var->typeScope()->functionList.empty() && var->type()->derivedFrom.empty()) {
475  mustDealloc = it->second;
476  break;
477  }
478  }
479  if (mustDealloc)
480  noDestructorError(scope, funcDestructor, mustDealloc);
481  }
482  }
483 
484  std::set<const Token*> copiedVars;
485  const Token* copyCtor = nullptr;
486  for (const Function &func : scope->functionList) {
487  if (func.type != Function::eCopyConstructor)
488  continue;
489  copyCtor = func.tokenDef;
490  if (!func.functionScope) {
491  allocatedVars.clear();
492  break;
493  }
494  const Token* tok = func.tokenDef->linkAt(1)->next();
495  if (tok->str()==":") {
496  tok=tok->next();
497  while (Token::Match(tok, "%name% (")) {
498  if (allocatedVars.find(tok->varId()) != allocatedVars.end()) {
499  if (tok->varId() && Token::Match(tok->tokAt(2), "%name% . %name% )"))
500  copiedVars.insert(tok);
501  else if (!Token::Match(tok->tokAt(2), "%any% )"))
502  allocatedVars.erase(tok->varId()); // Assume memory is allocated
503  }
504  tok = tok->linkAt(1)->tokAt(2);
505  }
506  }
507  for (tok = func.functionScope->bodyStart; tok != func.functionScope->bodyEnd; tok = tok->next()) {
508  if ((tok->isCpp() && Token::Match(tok, "%var% = new")) ||
509  (Token::Match(tok, "%var% = %name% (") && (mSettings->library.getAllocFuncInfo(tok->tokAt(2)) || mSettings->library.getReallocFuncInfo(tok->tokAt(2))))) {
510  allocatedVars.erase(tok->varId());
511  } else if (Token::Match(tok, "%var% = %name% . %name% ;") && allocatedVars.find(tok->varId()) != allocatedVars.end()) {
512  copiedVars.insert(tok);
513  }
514  }
515  break;
516  }
517  if (copyCtor && !copiedVars.empty()) {
518  for (const Token *cv : copiedVars)
519  copyConstructorShallowCopyError(cv, cv->str());
520  // throw error if count mismatch
521  /* FIXME: This doesn't work. See #4154
522  for (std::map<int, const Token*>::const_iterator i = allocatedVars.begin(); i != allocatedVars.end(); ++i) {
523  copyConstructorMallocError(copyCtor, i->second, i->second->str());
524  }
525  */
526  }
527  }
528 }
529 
530 /* This doesn't work. See #4154
531  void CheckClass::copyConstructorMallocError(const Token *cctor, const Token *alloc, const std::string& varname)
532  {
533  std::list<const Token*> callstack;
534  callstack.push_back(cctor);
535  callstack.push_back(alloc);
536  reportError(callstack, Severity::warning, "copyCtorNoAllocation", "Copy constructor does not allocate memory for member '" + varname + "' although memory has been allocated in other constructors.");
537  }
538  */
539 
540 void CheckClass::copyConstructorShallowCopyError(const Token *tok, const std::string& varname)
541 {
542  reportError(tok, Severity::warning, "copyCtorPointerCopying",
543  "$symbol:" + varname + "\nValue of pointer '$symbol', which points to allocated memory, is copied in copy constructor instead of allocating new memory.", CWE398, Certainty::normal);
544 }
545 
546 static std::string noMemberErrorMessage(const Scope *scope, const char function[], bool isdefault)
547 {
548  const std::string &classname = scope ? scope->className : "class";
549  const std::string type = (scope && scope->type == Scope::eStruct) ? "Struct" : "Class";
550  const bool isDestructor = (function[0] == 'd');
551  std::string errmsg = "$symbol:" + classname + '\n';
552 
553  if (isdefault) {
554  errmsg += type + " '$symbol' has dynamic memory/resource allocation(s). The " + function + " is explicitly defaulted but the default " + function + " does not work well.";
555  if (isDestructor)
556  errmsg += " It is recommended to define the " + std::string(function) + '.';
557  else
558  errmsg += " It is recommended to define or delete the " + std::string(function) + '.';
559  } else {
560  errmsg += type + " '$symbol' does not have a " + function + " which is recommended since it has dynamic memory/resource allocation(s).";
561  }
562 
563  return errmsg;
564 }
565 
566 void CheckClass::noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive)
567 {
568  reportError(alloc, Severity::warning, "noCopyConstructor", noMemberErrorMessage(scope, "copy constructor", isdefault), CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal);
569 }
570 
571 void CheckClass::noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive)
572 {
573  reportError(alloc, Severity::warning, "noOperatorEq", noMemberErrorMessage(scope, "operator=", isdefault), CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal);
574 }
575 
576 void CheckClass::noDestructorError(const Scope *scope, bool isdefault, const Token *alloc)
577 {
578  reportError(alloc, Severity::warning, "noDestructor", noMemberErrorMessage(scope, "destructor", isdefault), CWE398, Certainty::normal);
579 }
580 
581 bool CheckClass::canNotCopy(const Scope *scope)
582 {
583  bool constructor = false;
584  bool publicAssign = false;
585  bool publicCopy = false;
586 
587  for (const Function &func : scope->functionList) {
588  if (func.isConstructor())
589  constructor = true;
590  if (func.access != AccessControl::Public)
591  continue;
592  if (func.type == Function::eCopyConstructor) {
593  publicCopy = true;
594  break;
595  }
596  if (func.type == Function::eOperatorEqual) {
597  publicAssign = true;
598  break;
599  }
600  }
601 
602  return constructor && !(publicAssign || publicCopy);
603 }
604 
605 bool CheckClass::canNotMove(const Scope *scope)
606 {
607  bool constructor = false;
608  bool publicAssign = false;
609  bool publicCopy = false;
610  bool publicMove = false;
611 
612  for (const Function &func : scope->functionList) {
613  if (func.isConstructor())
614  constructor = true;
615  if (func.access != AccessControl::Public)
616  continue;
617  if (func.type == Function::eCopyConstructor) {
618  publicCopy = true;
619  break;
620  }
621  if (func.type == Function::eMoveConstructor) {
622  publicMove = true;
623  break;
624  }
625  if (func.type == Function::eOperatorEqual) {
626  publicAssign = true;
627  break;
628  }
629  }
630 
631  return constructor && !(publicAssign || publicCopy || publicMove);
632 }
633 
634 static void getAllVariableMembers(const Scope *scope, std::vector<const Variable *>& varList)
635 {
636  std::transform(scope->varlist.cbegin(), scope->varlist.cend(), std::back_inserter(varList), [](const Variable& var) {
637  return &var;
638  });
639  if (scope->definedType) {
640  for (const Type::BaseInfo& baseInfo: scope->definedType->derivedFrom) {
641  if (scope->definedType == baseInfo.type)
642  continue;
643  const Scope *baseClass = baseInfo.type ? baseInfo.type->classScope : nullptr;
644  if (baseClass && baseClass->isClassOrStruct() && baseClass->numConstructors == 0)
645  getAllVariableMembers(baseClass, varList);
646  }
647  }
648 }
649 
650 std::vector<CheckClass::Usage> CheckClass::createUsageList(const Scope *scope)
651 {
652  std::vector<Usage> ret;
653  std::vector<const Variable *> varlist;
654  getAllVariableMembers(scope, varlist);
655  ret.reserve(varlist.size());
656  std::transform(varlist.cbegin(), varlist.cend(), std::back_inserter(ret), [](const Variable* var) {
657  return Usage(var);
658  });
659  return ret;
660 }
661 
662 void CheckClass::assignVar(std::vector<Usage> &usageList, nonneg int varid)
663 {
664  auto it = std::find_if(usageList.begin(), usageList.end(), [varid](const Usage& usage) {
665  return usage.var->declarationId() == varid;
666  });
667  if (it != usageList.end())
668  it->assign = true;
669 }
670 
671 void CheckClass::assignVar(std::vector<Usage> &usageList, const Token* vartok)
672 {
673  if (vartok->varId() > 0) {
674  assignVar(usageList, vartok->varId());
675  return;
676  }
677  auto it = std::find_if(usageList.begin(), usageList.end(), [vartok](const Usage& usage) {
678  // FIXME: This is a workaround when varid is not set for a derived member
679  return usage.var->name() == vartok->str();
680  });
681  if (it != usageList.end())
682  it->assign = true;
683 }
684 
685 void CheckClass::initVar(std::vector<Usage> &usageList, nonneg int varid)
686 {
687  auto it = std::find_if(usageList.begin(), usageList.end(), [varid](const Usage& usage) {
688  return usage.var->declarationId() == varid;
689  });
690  if (it != usageList.end())
691  it->init = true;
692 }
693 
694 void CheckClass::assignAllVar(std::vector<Usage> &usageList)
695 {
696  for (Usage & i : usageList)
697  i.assign = true;
698 }
699 
700 void CheckClass::assignAllVarsVisibleFromScope(std::vector<Usage>& usageList, const Scope* scope)
701 {
702  for (Usage& usage : usageList) {
703  if (usage.var->scope() == scope)
704  usage.assign = true;
705  }
706 
707  // Iterate through each base class...
708  for (const Type::BaseInfo& i : scope->definedType->derivedFrom) {
709  const Type *derivedFrom = i.type;
710 
711  if (derivedFrom && derivedFrom->classScope)
712  assignAllVarsVisibleFromScope(usageList, derivedFrom->classScope);
713  }
714 }
715 
716 void CheckClass::clearAllVar(std::vector<Usage> &usageList)
717 {
718  for (Usage & i : usageList) {
719  i.assign = false;
720  i.init = false;
721  }
722 }
723 
725 {
726  // Iterate through each base class...
727  for (const Type::BaseInfo & i : scope->definedType->derivedFrom) {
728  const Type *derivedFrom = i.type;
729 
730  // Check if base class exists in database
731  if (derivedFrom && derivedFrom->classScope) {
732  const std::list<Function>& functionList = derivedFrom->classScope->functionList;
733 
734  if (std::any_of(functionList.cbegin(), functionList.cend(), [&](const Function& func) {
735  return func.tokenDef->str() == tok->str() && !func.isStatic() && !func.isConst();
736  }))
737  return true;
738 
739  if (isBaseClassMutableMemberFunc(tok, derivedFrom->classScope))
740  return true;
741  }
742 
743  // Base class not found so assume it is in it.
744  else
745  return true;
746  }
747 
748  return false;
749 }
750 
751 void CheckClass::initializeVarList(const Function &func, std::list<const Function *> &callstack, const Scope *scope, std::vector<Usage> &usage) const
752 {
753  if (!func.functionScope)
754  return;
755 
756  bool initList = func.isConstructor();
757  const Token *ftok = func.arg->link()->next();
758  int level = 0;
759  for (; ftok && ftok != func.functionScope->bodyEnd; ftok = ftok->next()) {
760  // Class constructor.. initializing variables like this
761  // clKalle::clKalle() : var(value) { }
762  if (initList) {
763  if (level == 0 && Token::Match(ftok, "%name% {|(") && Token::Match(ftok->linkAt(1), "}|) ,|{")) {
764  if (ftok->str() != func.name()) {
765  if (ftok->varId())
766  initVar(usage, ftok->varId());
767  else { // base class constructor
768  for (Usage& u : usage) {
769  if (u.var->scope() != scope) // assume that all variables are initialized in base class
770  u.init = true;
771  }
772  }
773  } else { // c++11 delegate constructor
774  const Function *member = ftok->function();
775  // member function not found => assume it initializes all members
776  if (!member) {
777  assignAllVar(usage);
778  return;
779  }
780 
781  // recursive call
782  // assume that all variables are initialized
783  if (std::find(callstack.cbegin(), callstack.cend(), member) != callstack.cend()) {
784  /** @todo false negative: just bail */
785  assignAllVar(usage);
786  return;
787  }
788 
789  // member function has implementation
790  if (member->hasBody()) {
791  // initialize variable use list using member function
792  callstack.push_back(member);
793  initializeVarList(*member, callstack, scope, usage);
794  callstack.pop_back();
795  }
796 
797  // there is a called member function, but it has no implementation, so we assume it initializes everything
798  else {
799  assignAllVar(usage);
800  }
801  }
802  } else if (level != 0 && Token::Match(ftok, "%name% =")) // assignment in the initializer: var(value = x)
803  assignVar(usage, ftok->varId());
804 
805  // Level handling
806  if (ftok->link() && Token::Match(ftok, "(|<"))
807  level++;
808  else if (ftok->str() == "{") {
809  if (level != 0 ||
810  (Token::Match(ftok->previous(), "%name%|>") && Token::Match(ftok->link(), "} ,|{")))
811  level++;
812  else
813  initList = false;
814  } else if (ftok->link() && Token::Match(ftok, ")|>|}"))
815  level--;
816  }
817 
818  if (initList)
819  continue;
820 
821  // Variable getting value from stream?
822  if (Token::Match(ftok, ">>|& %name%") && isLikelyStreamRead(ftok)) {
823  assignVar(usage, ftok->next()->varId());
824  }
825 
826  // If assignment comes after an && or || this is really inconclusive because of short circuiting
827  if (Token::Match(ftok, "%oror%|&&"))
828  continue;
829 
830  if (Token::simpleMatch(ftok, "( !"))
831  ftok = ftok->next();
832 
833  // Using the operator= function to initialize all variables..
834  if (Token::Match(ftok->next(), "return| (| * this )| =")) {
835  assignAllVar(usage);
836  break;
837  }
838 
839  // Using swap to assign all variables..
840  if (func.type == Function::eOperatorEqual && Token::Match(ftok, "[;{}] %name% (") && Token::Match(ftok->linkAt(2), ") . %name% ( *| this ) ;")) {
841  assignAllVar(usage);
842  break;
843  }
844 
845  // Calling member variable function?
846  if (Token::Match(ftok->next(), "%var% . %name% (") && !(ftok->next()->valueType() && ftok->next()->valueType()->pointer)) {
847  if (std::any_of(scope->varlist.cbegin(), scope->varlist.cend(), [&](const Variable& var) {
848  return var.declarationId() == ftok->next()->varId();
849  }))
850  /** @todo false negative: we assume function changes variable state */
851  assignVar(usage, ftok->next()->varId());
852 
853  ftok = ftok->tokAt(2);
854  }
855 
856  if (!Token::Match(ftok->next(), "::| %name%") &&
857  !Token::Match(ftok->next(), "*| this . %name%") &&
858  !Token::Match(ftok->next(), "* %name% =") &&
859  !Token::Match(ftok->next(), "( * this ) . %name%"))
860  continue;
861 
862  // Goto the first token in this statement..
863  ftok = ftok->next();
864 
865  // skip "return"
866  if (ftok->str() == "return")
867  ftok = ftok->next();
868 
869  // Skip "( * this )"
870  if (Token::simpleMatch(ftok, "( * this ) .")) {
871  ftok = ftok->tokAt(5);
872  }
873 
874  // Skip "this->"
875  if (Token::simpleMatch(ftok, "this ."))
876  ftok = ftok->tokAt(2);
877 
878  // Skip "classname :: "
879  if (Token::Match(ftok, ":: %name%"))
880  ftok = ftok->next();
881  while (Token::Match(ftok, "%name% ::"))
882  ftok = ftok->tokAt(2);
883 
884  // Clearing all variables..
885  if (Token::Match(ftok, "::| memset ( this ,")) {
886  assignAllVar(usage);
887  return;
888  }
889 
890  // Ticket #7068
891  if (Token::Match(ftok, "::| memset ( &| this . %name%")) {
892  if (ftok->str() == "::")
893  ftok = ftok->next();
894  int offsetToMember = 4;
895  if (ftok->strAt(2) == "&")
896  ++offsetToMember;
897  assignVar(usage, ftok->tokAt(offsetToMember)->varId());
898  ftok = ftok->linkAt(1);
899  continue;
900  }
901 
902  // Clearing array..
903  if (Token::Match(ftok, "::| memset ( %name% ,")) {
904  if (ftok->str() == "::")
905  ftok = ftok->next();
906  assignVar(usage, ftok->tokAt(2)->varId());
907  ftok = ftok->linkAt(1);
908  continue;
909  }
910 
911  // Calling member function?
912  if (Token::simpleMatch(ftok, "operator= (")) {
913  if (ftok->function()) {
914  const Function *member = ftok->function();
915  // recursive call
916  // assume that all variables are initialized
917  if (std::find(callstack.cbegin(), callstack.cend(), member) != callstack.cend()) {
918  /** @todo false negative: just bail */
919  assignAllVar(usage);
920  return;
921  }
922 
923  // member function has implementation
924  if (member->hasBody()) {
925  // initialize variable use list using member function
926  callstack.push_back(member);
927  initializeVarList(*member, callstack, scope, usage);
928  callstack.pop_back();
929  }
930 
931  // assume that a base class call to operator= assigns all its base members (but not more)
932  else if (func.tokenDef->str() == ftok->str() && isBaseClassMutableMemberFunc(ftok, scope)) {
933  if (member->nestedIn)
935  }
936 
937  // there is a called member function, but it has no implementation, so we assume it initializes everything
938  else {
939  assignAllVar(usage);
940  }
941  }
942 
943  // using default operator =, assume everything initialized
944  else {
945  assignAllVar(usage);
946  }
947  } else if (Token::Match(ftok, "::| %name% (") && !Token::Match(ftok, "if|while|for")) {
948  if (ftok->str() == "::")
949  ftok = ftok->next();
950 
951  // Passing "this" => assume that everything is initialized
952  for (const Token *tok2 = ftok->next()->link(); tok2 && tok2 != ftok; tok2 = tok2->previous()) {
953  if (tok2->str() == "this") {
954  assignAllVar(usage);
955  return;
956  }
957  }
958 
959  // check if member function
960  if (ftok->function() && ftok->function()->nestedIn == scope &&
961  !ftok->function()->isConstructor()) {
962  const Function *member = ftok->function();
963 
964  // recursive call
965  // assume that all variables are initialized
966  if (std::find(callstack.cbegin(), callstack.cend(), member) != callstack.cend()) {
967  assignAllVar(usage);
968  return;
969  }
970 
971  // member function has implementation
972  if (member->hasBody()) {
973  // initialize variable use list using member function
974  callstack.push_back(member);
975  initializeVarList(*member, callstack, scope, usage);
976  callstack.pop_back();
977 
978  // Assume that variables that are passed to it are initialized..
979  for (const Token *tok2 = ftok; tok2; tok2 = tok2->next()) {
980  if (Token::Match(tok2, "[;{}]"))
981  break;
982  if (Token::Match(tok2, "[(,] &| %name% [,)]")) {
983  tok2 = tok2->next();
984  if (tok2->str() == "&")
985  tok2 = tok2->next();
986  if (isVariableChangedByFunctionCall(tok2, tok2->previous()->str() == "&", tok2->varId(), *mSettings, nullptr))
987  assignVar(usage, tok2->varId());
988  }
989  }
990  }
991 
992  // there is a called member function, but it has no implementation, so we assume it initializes everything (if it can mutate state)
993  else if (!member->isConst() && !member->isStatic()) {
994  assignAllVar(usage);
995  }
996 
997  // const method, assume it assigns all mutable members
998  else if (member->isConst()) {
999  for (Usage& i: usage) {
1000  if (i.var->isMutable())
1001  i.assign = true;
1002  }
1003  }
1004  }
1005 
1006  // not member function
1007  else {
1008  // could be a base class virtual function, so we assume it initializes everything
1009  if (!func.isConstructor() && isBaseClassMutableMemberFunc(ftok, scope)) {
1010  /** @todo False Negative: we should look at the base class functions to see if they
1011  * call any derived class virtual functions that change the derived class state
1012  */
1013  assignAllVar(usage);
1014  }
1015 
1016  // has friends, so we assume it initializes everything
1017  if (!scope->definedType->friendList.empty())
1018  assignAllVar(usage);
1019 
1020  // the function is external and it's neither friend nor inherited virtual function.
1021  // assume all variables that are passed to it are initialized..
1022  else {
1023  for (const Token *tok = ftok->tokAt(2); tok && tok != ftok->next()->link(); tok = tok->next()) {
1024  if (tok->isName()) {
1025  assignVar(usage, tok->varId());
1026  }
1027  }
1028  }
1029  }
1030  }
1031 
1032  // Assignment of member variable?
1033  else if (Token::Match(ftok, "%name% =")) {
1034  assignVar(usage, ftok);
1035  bool bailout = ftok->variable() && ftok->variable()->isReference();
1036  const Token* tok2 = ftok->tokAt(2);
1037  if (tok2->str() == "&") {
1038  tok2 = tok2->next();
1039  bailout = true;
1040  }
1041  if (tok2->variable() && (bailout || tok2->variable()->isArray()) && tok2->strAt(1) != "[")
1042  assignVar(usage, tok2->varId());
1043  }
1044 
1045  // Assignment of array item of member variable?
1046  else if (Token::Match(ftok, "%name% [|.")) {
1047  const Token *tok2 = ftok;
1048  while (tok2) {
1049  if (tok2->strAt(1) == "[")
1050  tok2 = tok2->next()->link();
1051  else if (Token::Match(tok2->next(), ". %name%"))
1052  tok2 = tok2->tokAt(2);
1053  else
1054  break;
1055  }
1056  if (tok2 && tok2->strAt(1) == "=")
1057  assignVar(usage, ftok->varId());
1058  }
1059 
1060  // Assignment of array item of member variable?
1061  else if (Token::Match(ftok, "* %name% =")) {
1062  assignVar(usage, ftok->next()->varId());
1063  } else if (Token::Match(ftok, "* this . %name% =")) {
1064  assignVar(usage, ftok->tokAt(3)->varId());
1065  } else if (astIsRangeBasedForDecl(ftok)) {
1066  if (const Variable* rangeVar = ftok->astParent()->astOperand1()->variable()) {
1067  if (rangeVar->isReference() && !rangeVar->isConst())
1068  assignVar(usage, ftok->varId());
1069  }
1070  }
1071 
1072  // The functions 'clear' and 'Clear' are supposed to initialize variable.
1073  if (Token::Match(ftok, "%name% . clear|Clear (")) {
1074  assignVar(usage, ftok->varId());
1075  }
1076  }
1077 }
1078 
1079 void CheckClass::noConstructorError(const Token *tok, const std::string &classname, bool isStruct)
1080 {
1081  // For performance reasons the constructor might be intentionally missing. Therefore this is not a "warning"
1082  const std::string message {"The " + std::string(isStruct ? "struct" : "class") + " '$symbol' does not declare a constructor although it has private member variables which likely require initialization."};
1083  const std::string verbose {message + " Member variables of native types, pointers, or references are left uninitialized when the class is instantiated. That may cause bugs or undefined behavior."};
1084  reportError(tok, Severity::style, "noConstructor", "$symbol:" + classname + '\n' + message + '\n' + verbose, CWE398, Certainty::normal);
1085 }
1086 
1087 void CheckClass::noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct)
1088 {
1089  const std::string message(std::string(isStruct ? "Struct" : "Class") + " '$symbol' has a constructor with 1 argument that is not explicit.");
1090  const std::string verbose(message + " Such, so called \"Converting constructors\", should in general be explicit for type safety reasons as that prevents unintended implicit conversions.");
1091  reportError(tok, Severity::style, "noExplicitConstructor", "$symbol:" + classname + '\n' + message + '\n' + verbose, CWE398, Certainty::normal);
1092 }
1093 
1094 void CheckClass::uninitVarError(const Token *tok, bool isprivate, Function::Type functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive)
1095 {
1096  std::string ctor;
1097  if (functionType == Function::eCopyConstructor)
1098  ctor = "copy ";
1099  else if (functionType == Function::eMoveConstructor)
1100  ctor = "move ";
1101  std::string message("Member variable '$symbol' is not initialized in the " + ctor + "constructor.");
1102  if (derived)
1103  message += " Maybe it should be initialized directly in the class " + classname + "?";
1104  std::string id = std::string("uninit") + (derived ? "Derived" : "") + "MemberVar" + (isprivate ? "Private" : "");
1105  const std::string verbose {message + " Member variables of native types, pointers, or references are left uninitialized when the class is instantiated. That may cause bugs or undefined behavior."};
1106  reportError(tok, Severity::warning, id, "$symbol:" + classname + "::" + varname + '\n' + message + '\n' + verbose, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal);
1107 }
1108 
1109 void CheckClass::uninitVarError(const Token *tok, const std::string &classname, const std::string &varname)
1110 {
1111  const std::string message("Member variable '$symbol' is not initialized."); // report missing in-class initializer
1112  const std::string verbose {message + " Member variables of native types, pointers, or references are left uninitialized when the class is instantiated. That may cause bugs or undefined behavior."};
1113  const std::string id = std::string("uninitMemberVarPrivate");
1114  reportError(tok, Severity::warning, id, "$symbol:" + classname + "::" + varname + '\n' + message + '\n' + verbose, CWE398, Certainty::normal);
1115 }
1116 
1117 void CheckClass::missingMemberCopyError(const Token *tok, Function::Type functionType, const std::string& classname, const std::string& varname)
1118 {
1119  const std::string ctor(functionType == Function::Type::eCopyConstructor ? "copy" : "move");
1120  const std::string action(functionType == Function::Type::eCopyConstructor ? "copied?" : "moved?");
1121  const std::string message =
1122  "$symbol:" + classname + "::" + varname + "\n" +
1123  "Member variable '$symbol' is not assigned in the " + ctor + " constructor. Should it be " + action;
1124  reportError(tok, Severity::warning, "missingMemberCopy", message, CWE398, Certainty::inconclusive);
1125 }
1126 
1127 void CheckClass::operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive)
1128 {
1129  reportError(tok, Severity::warning, "operatorEqVarError", "$symbol:" + classname + "::" + varname + "\nMember variable '$symbol' is not assigned a value in '" + classname + "::operator='.", CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal);
1130 }
1131 
1132 //---------------------------------------------------------------------------
1133 // ClassCheck: Use initialization list instead of assignment
1134 //---------------------------------------------------------------------------
1135 
1137 {
1139  return;
1140 
1141  logChecker("CheckClass::initializationListUsage"); // performance
1142 
1143  for (const Scope *scope : mSymbolDatabase->functionScopes) {
1144  // Check every constructor
1145  if (!scope->function || !scope->function->isConstructor())
1146  continue;
1147 
1148  // Do not warn when a delegate constructor is called
1149  if (const Token *initList = scope->function->constructorMemberInitialization()) {
1150  if (Token::Match(initList, ": %name% {|(") && initList->strAt(1) == scope->className)
1151  continue;
1152  }
1153 
1154  const Scope* owner = scope->functionOf;
1155  for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
1156  if (Token::Match(tok, "%name% (")) // Assignments might depend on this function call or if/for/while/switch statement from now on.
1157  break;
1158  if (Token::Match(tok, "try|do {"))
1159  break;
1160  if (!Token::Match(tok, "%var% =") || tok->strAt(-1) == "*" || tok->strAt(-1) == ".")
1161  continue;
1162 
1163  const Variable* var = tok->variable();
1164  if (!var || var->scope() != owner || var->isStatic())
1165  continue;
1166  if (var->isPointer() || var->isReference() || var->isEnumType())
1167  continue;
1168  if (!WRONG_DATA(!var->valueType(), tok) && var->valueType()->type > ValueType::Type::ITERATOR)
1169  continue;
1170 
1171  // bailout: multi line lambda in rhs => do not warn
1172  if (findLambdaEndToken(tok->tokAt(2)) && tok->tokAt(2)->findExpressionStartEndTokens().second->linenr() > tok->tokAt(2)->linenr())
1173  continue;
1174 
1175  // Access local var member in rhs => do not warn
1176  bool localmember = false;
1177  visitAstNodes(tok->next()->astOperand2(),
1178  [&](const Token *rhs) {
1179  if (rhs->str() == "." && rhs->astOperand1() && rhs->astOperand1()->variable() && rhs->astOperand1()->variable()->isLocal())
1180  localmember = true;
1181  return ChildrenToVisit::op1_and_op2;
1182  });
1183  if (localmember)
1184  continue;
1185 
1186  bool allowed = true;
1187  visitAstNodes(tok->next()->astOperand2(),
1188  [&](const Token *tok2) {
1189  const Variable* var2 = tok2->variable();
1190  if (var2) {
1191  if (var2->scope() == owner && tok2->strAt(-1)!=".") { // Is there a dependency between two member variables?
1192  allowed = false;
1193  return ChildrenToVisit::done;
1194  }
1195  if (var2->isArray() && var2->isLocal()) { // Can't initialize with a local array
1196  allowed = false;
1197  return ChildrenToVisit::done;
1198  }
1199  } else if (tok2->str() == "this") { // 'this' instance is not completely constructed in initialization list
1200  allowed = false;
1201  return ChildrenToVisit::done;
1202  } else if (Token::Match(tok2, "%name% (") && tok2->strAt(-1) != "." && isMemberFunc(owner, tok2)) { // Member function called?
1203  allowed = false;
1204  return ChildrenToVisit::done;
1205  }
1207  });
1208  if (!allowed)
1209  continue;
1210 
1211  suggestInitializationList(tok, tok->str());
1212  }
1213  }
1214 }
1215 
1216 void CheckClass::suggestInitializationList(const Token* tok, const std::string& varname)
1217 {
1218  reportError(tok, Severity::performance, "useInitializationList", "$symbol:" + varname + "\nVariable '$symbol' is assigned in constructor body. Consider performing initialization in initialization list.\n"
1219  "When an object of a class is created, the constructors of all member variables are called consecutively "
1220  "in the order the variables are declared, even if you don't explicitly write them to the initialization list. You "
1221  "could avoid assigning '$symbol' a value by passing the value to the constructor in the initialization list.", CWE398, Certainty::normal);
1222 }
1223 
1224 //---------------------------------------------------------------------------
1225 // ClassCheck: Unused private functions
1226 //---------------------------------------------------------------------------
1227 
1228 static bool checkFunctionUsage(const Function *privfunc, const Scope* scope)
1229 {
1230  if (!scope)
1231  return true; // Assume it is used, if scope is not seen
1232 
1233  for (std::list<Function>::const_iterator func = scope->functionList.cbegin(); func != scope->functionList.cend(); ++func) {
1234  if (func->functionScope) {
1235  if (Token::Match(func->tokenDef, "%name% (")) {
1236  for (const Token *ftok = func->tokenDef->tokAt(2); ftok && ftok->str() != ")"; ftok = ftok->next()) {
1237  if (Token::Match(ftok, "= %name% [(,)]") && ftok->strAt(1) == privfunc->name())
1238  return true;
1239  if (ftok->str() == "(")
1240  ftok = ftok->link();
1241  }
1242  }
1243  for (const Token *ftok = func->functionScope->classDef->linkAt(1); ftok != func->functionScope->bodyEnd; ftok = ftok->next()) {
1244  if (ftok->function() == privfunc)
1245  return true;
1246  if (ftok->varId() == 0U && ftok->str() == privfunc->name()) // TODO: This condition should be redundant
1247  return true;
1248  }
1249  } else if ((func->type != Function::eCopyConstructor &&
1250  func->type != Function::eOperatorEqual) ||
1251  func->access != AccessControl::Private) // Assume it is used, if a function implementation isn't seen, but empty private copy constructors and assignment operators are OK
1252  return true;
1253  }
1254 
1255  const std::map<std::string, Type*>::const_iterator end = scope->definedTypesMap.cend();
1256  for (std::map<std::string, Type*>::const_iterator iter = scope->definedTypesMap.cbegin(); iter != end; ++iter) {
1257  const Type *type = iter->second;
1258  if (type->enclosingScope == scope && checkFunctionUsage(privfunc, type->classScope))
1259  return true;
1260  }
1261 
1262  for (const Variable &var : scope->varlist) {
1263  if (var.isStatic()) {
1264  const Token* tok = Token::findmatch(scope->bodyStart, "%varid% =|(|{", var.declarationId());
1265  if (tok)
1266  tok = tok->tokAt(2);
1267  while (tok && tok->str() != ";") {
1268  if (tok->function() == privfunc)
1269  return true;
1270  tok = tok->next();
1271  }
1272  }
1273  }
1274 
1275  return false; // Unused in this scope
1276 }
1277 
1279 {
1280  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedPrivateFunction"))
1281  return;
1282 
1283  logChecker("CheckClass::privateFunctions"); // style
1284 
1285  for (const Scope * scope : mSymbolDatabase->classAndStructScopes) {
1286 
1287  // do not check borland classes with properties..
1288  if (Token::findsimplematch(scope->bodyStart, "; __property ;", scope->bodyEnd))
1289  continue;
1290 
1291  std::list<const Function*> privateFuncs;
1292  for (const Function &func : scope->functionList) {
1293  // Get private functions..
1294  if (func.type == Function::eFunction && func.access == AccessControl::Private && !func.isOperator()) // TODO: There are smarter ways to check private operator usage
1295  privateFuncs.push_back(&func);
1296  }
1297 
1298  // Bailout for overridden virtual functions of base classes
1299  if (!scope->definedType->derivedFrom.empty()) {
1300  // Check virtual functions
1301  for (std::list<const Function*>::iterator it = privateFuncs.begin(); it != privateFuncs.end();) {
1302  if ((*it)->isImplicitlyVirtual(true)) // Give true as default value to be returned if we don't see all base classes
1303  it = privateFuncs.erase(it);
1304  else
1305  ++it;
1306  }
1307  }
1308 
1309  while (!privateFuncs.empty()) {
1310  const auto& pf = privateFuncs.front();
1311  if (pf->retDef && pf->retDef->isAttributeMaybeUnused()) {
1312  privateFuncs.pop_front();
1313  continue;
1314  }
1315  // Check that all private functions are used
1316  bool used = checkFunctionUsage(pf, scope); // Usage in this class
1317  // Check in friend classes
1318  const std::vector<Type::FriendInfo>& friendList = scope->definedType->friendList;
1319  for (int i = 0; i < friendList.size() && !used; i++) {
1320  if (friendList[i].type)
1321  used = checkFunctionUsage(pf, friendList[i].type->classScope);
1322  else
1323  used = true; // Assume, it is used if we do not see friend class
1324  }
1325 
1326  if (!used)
1327  unusedPrivateFunctionError(pf->tokenDef, scope->className, pf->name());
1328 
1329  privateFuncs.pop_front();
1330  }
1331  }
1332 }
1333 
1334 void CheckClass::unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname)
1335 {
1336  reportError(tok, Severity::style, "unusedPrivateFunction", "$symbol:" + classname + "::" + funcname + "\nUnused private function: '$symbol'", CWE398, Certainty::normal);
1337 }
1338 
1339 //---------------------------------------------------------------------------
1340 // ClassCheck: Check that memset is not used on classes
1341 //---------------------------------------------------------------------------
1342 
1343 static const Scope* findFunctionOf(const Scope* scope)
1344 {
1345  while (scope) {
1346  if (scope->type == Scope::eFunction)
1347  return scope->functionOf;
1348  scope = scope->nestedIn;
1349  }
1350  return nullptr;
1351 }
1352 
1354 {
1355  logChecker("CheckClass::checkMemset");
1356  const bool printWarnings = mSettings->severity.isEnabled(Severity::warning);
1357  for (const Scope *scope : mSymbolDatabase->functionScopes) {
1358  for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {
1359  if (Token::Match(tok, "memset|memcpy|memmove (")) {
1360  const Token* arg1 = tok->tokAt(2);
1361  const Token* arg3 = arg1->nextArgument();
1362  if (arg3)
1363  arg3 = arg3->nextArgument();
1364  if (!arg3)
1365  // weird, shouldn't happen: memset etc should have
1366  // 3 arguments.
1367  continue;
1368 
1369  const Token *typeTok = nullptr;
1370  const Scope *type = nullptr;
1371  const Token* sizeofTok = arg3->previous()->astOperand2(); // try to find sizeof() in argument expression
1372  if (sizeofTok && sizeofTok->astOperand1() && Token::simpleMatch(sizeofTok->astOperand1()->previous(), "sizeof ("))
1373  sizeofTok = sizeofTok->astOperand1();
1374  else if (sizeofTok && sizeofTok->astOperand2() && Token::simpleMatch(sizeofTok->astOperand2()->previous(), "sizeof ("))
1375  sizeofTok = sizeofTok->astOperand2();
1376  if (Token::simpleMatch(sizeofTok, "("))
1377  sizeofTok = sizeofTok->previous();
1378  if (Token::Match(sizeofTok, "sizeof ( %type% )"))
1379  typeTok = sizeofTok->tokAt(2);
1380  else if (Token::Match(sizeofTok, "sizeof ( %type% :: %type% )"))
1381  typeTok = sizeofTok->tokAt(4);
1382  else if (Token::Match(sizeofTok, "sizeof ( struct %type% )"))
1383  typeTok = sizeofTok->tokAt(3);
1384  else if (Token::simpleMatch(sizeofTok, "sizeof ( * this )") || Token::simpleMatch(arg1, "this ,")) {
1385  type = findFunctionOf(sizeofTok->scope());
1386  } else if (Token::Match(arg1, "&|*|%var%")) {
1387  int numIndirToVariableType = 0; // Offset to the actual type in terms of dereference/addressof
1388  for (;; arg1 = arg1->next()) {
1389  if (arg1->str() == "&")
1390  ++numIndirToVariableType;
1391  else if (arg1->str() == "*")
1392  --numIndirToVariableType;
1393  else
1394  break;
1395  }
1396 
1397  const Variable * const var = arg1->variable();
1398  if (var && arg1->strAt(1) == ",") {
1399  if (var->isArrayOrPointer()) {
1400  const Token *endTok = var->typeEndToken();
1401  while (Token::simpleMatch(endTok, "*")) {
1402  ++numIndirToVariableType;
1403  endTok = endTok->previous();
1404  }
1405  }
1406 
1407  if (var->isArray())
1408  numIndirToVariableType += int(var->dimensions().size());
1409 
1410  if (numIndirToVariableType == 1)
1411  type = var->typeScope();
1412 
1413  if (!type && !var->isPointer() && !Token::simpleMatch(var->typeStartToken(), "std :: array") &&
1415  memsetError(tok, tok->str(), var->getTypeName(), {}, /*isContainer*/ true);
1416  }
1417  }
1418  }
1419 
1420  // No type defined => The tokens didn't match
1421  if (!typeTok && !type)
1422  continue;
1423 
1424  if (typeTok && typeTok->str() == "(")
1425  typeTok = typeTok->next();
1426 
1427  if (!type && typeTok->type())
1428  type = typeTok->type()->classScope;
1429 
1430  if (type) {
1431  const std::set<const Scope *> parsedTypes;
1432  checkMemsetType(scope, tok, type, false, parsedTypes);
1433  }
1434  } else if (tok->variable() && tok->variable()->isPointer() && tok->variable()->typeScope() && Token::Match(tok, "%var% = %name% (")) {
1435  const Library::AllocFunc* alloc = mSettings->library.getAllocFuncInfo(tok->tokAt(2));
1436  if (!alloc)
1437  alloc = mSettings->library.getReallocFuncInfo(tok->tokAt(2));
1438  if (!alloc || alloc->bufferSize == Library::AllocFunc::BufferSize::none)
1439  continue;
1440  const std::set<const Scope *> parsedTypes;
1441  checkMemsetType(scope, tok->tokAt(2), tok->variable()->typeScope(), true, parsedTypes);
1442 
1443  if (printWarnings && tok->variable()->typeScope()->numConstructors > 0)
1444  mallocOnClassWarning(tok, tok->strAt(2), tok->variable()->typeScope()->classDef);
1445  }
1446  }
1447  }
1448 }
1449 
1450 void CheckClass::checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::set<const Scope *> parsedTypes)
1451 {
1452  // If type has been checked there is no need to check it again
1453  if (parsedTypes.find(type) != parsedTypes.end())
1454  return;
1455  parsedTypes.insert(type);
1456 
1457  const bool printPortability = mSettings->severity.isEnabled(Severity::portability);
1458 
1459  // recursively check all parent classes
1460  for (const Type::BaseInfo & i : type->definedType->derivedFrom) {
1461  const Type* derivedFrom = i.type;
1462  if (derivedFrom && derivedFrom->classScope)
1463  checkMemsetType(start, tok, derivedFrom->classScope, allocation, parsedTypes);
1464  }
1465 
1466  // Warn if type is a class that contains any virtual functions
1467  for (const Function &func : type->functionList) {
1468  if (func.hasVirtualSpecifier()) {
1469  if (allocation)
1470  mallocOnClassError(tok, tok->str(), type->classDef, "virtual function");
1471  else
1472  memsetError(tok, tok->str(), "virtual function", type->classDef->str());
1473  }
1474  }
1475 
1476  // Warn if type is a class or struct that contains any std::* variables
1477  for (const Variable &var : type->varlist) {
1478  if (var.isReference() && !var.isStatic()) {
1479  memsetErrorReference(tok, tok->str(), type->classDef->str());
1480  continue;
1481  }
1482  // don't warn if variable static or const, pointer or array of pointers
1483  if (!var.isStatic() && !var.isConst() && !var.isPointer() && (!var.isArray() || var.typeEndToken()->str() != "*")) {
1484  const Token *tok1 = var.typeStartToken();
1485  const Scope *typeScope = var.typeScope();
1486 
1487  std::string typeName;
1488  if (Token::Match(tok1, "%type% ::")) {
1489  const Token *typeTok = tok1;
1490  while (Token::Match(typeTok, "%type% ::")) {
1491  typeName += typeTok->str() + "::";
1492  typeTok = typeTok->tokAt(2);
1493  }
1494  typeName += typeTok->str();
1495  }
1496 
1497  // check for std:: type
1498  if (var.isStlType() && typeName != "std::array" && !mSettings->library.podtype(typeName)) {
1499  if (allocation)
1500  mallocOnClassError(tok, tok->str(), type->classDef, "'" + typeName + "'");
1501  else
1502  memsetError(tok, tok->str(), "'" + typeName + "'", type->classDef->str());
1503  }
1504 
1505  // check for known type
1506  else if (typeScope && typeScope != type)
1507  checkMemsetType(start, tok, typeScope, allocation, parsedTypes);
1508 
1509  // check for float
1510  else if (printPortability && var.isFloatingType() && tok->str() == "memset")
1511  memsetErrorFloat(tok, type->classDef->str());
1512  }
1513  }
1514 }
1515 
1516 void CheckClass::mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok)
1517 {
1518  std::list<const Token *> toks = { tok, classTok };
1519  reportError(toks, Severity::warning, "mallocOnClassWarning",
1520  "$symbol:" + memfunc +"\n"
1521  "Memory for class instance allocated with $symbol(), but class provides constructors.\n"
1522  "Memory for class instance allocated with $symbol(), but class provides constructors. This is unsafe, "
1523  "since no constructor is called and class members remain uninitialized. Consider using 'new' instead.", CWE762, Certainty::normal);
1524 }
1525 
1526 void CheckClass::mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname)
1527 {
1528  std::list<const Token *> toks = { tok, classTok };
1529  reportError(toks, Severity::error, "mallocOnClassError",
1530  "$symbol:" + memfunc +"\n"
1531  "$symbol:" + classname +"\n"
1532  "Memory for class instance allocated with " + memfunc + "(), but class contains a " + classname + ".\n"
1533  "Memory for class instance allocated with " + memfunc + "(), but class a " + classname + ". This is unsafe, "
1534  "since no constructor is called and class members remain uninitialized. Consider using 'new' instead.", CWE665, Certainty::normal);
1535 }
1536 
1537 void CheckClass::memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type, bool isContainer)
1538 {
1539  const std::string typeStr = isContainer ? std::string() : (type + " that contains a ");
1540  const std::string msg = "$symbol:" + memfunc + "\n"
1541  "$symbol:" + classname + "\n"
1542  "Using '" + memfunc + "' on " + typeStr + classname + ".\n"
1543  "Using '" + memfunc + "' on " + typeStr + classname + " is unsafe, because constructor, destructor "
1544  "and copy operator calls are omitted. These are necessary for this non-POD type to ensure that a valid object "
1545  "is created.";
1546  reportError(tok, Severity::error, "memsetClass", msg, CWE762, Certainty::normal);
1547 }
1548 
1549 void CheckClass::memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type)
1550 {
1551  reportError(tok, Severity::error, "memsetClassReference",
1552  "$symbol:" + memfunc +"\n"
1553  "Using '" + memfunc + "' on " + type + " that contains a reference.", CWE665, Certainty::normal);
1554 }
1555 
1556 void CheckClass::memsetErrorFloat(const Token *tok, const std::string &type)
1557 {
1558  reportError(tok, Severity::portability, "memsetClassFloat", "Using memset() on " + type + " which contains a floating point number.\n"
1559  "Using memset() on " + type + " which contains a floating point number."
1560  " This is not portable because memset() sets each byte of a block of memory to a specific value and"
1561  " the actual representation of a floating-point value is implementation defined."
1562  " Note: In case of an IEEE754-1985 compatible implementation setting all bits to zero results in the value 0.0.", CWE758, Certainty::normal);
1563 }
1564 
1565 
1566 //---------------------------------------------------------------------------
1567 // ClassCheck: "C& operator=(const C&) { ... return *this; }"
1568 // operator= should return a reference to *this
1569 //---------------------------------------------------------------------------
1570 
1572 {
1573  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("operatorEqRetRefThis"))
1574  return;
1575 
1576  logChecker("CheckClass::operatorEqRetRefThis"); // style
1577 
1578  for (const Scope * scope : mSymbolDatabase->classAndStructScopes) {
1579  for (std::list<Function>::const_iterator func = scope->functionList.cbegin(); func != scope->functionList.cend(); ++func) {
1580  if (func->type == Function::eOperatorEqual && func->hasBody()) {
1581  // make sure return signature is correct
1582  if (func->retType == func->nestedIn->definedType && func->tokenDef->strAt(-1) == "&") {
1583  checkReturnPtrThis(scope, &(*func), func->functionScope->bodyStart, func->functionScope->bodyEnd);
1584  }
1585  }
1586  }
1587  }
1588 }
1589 
1590 void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last)
1591 {
1592  std::set<const Function*> analyzedFunctions;
1593  checkReturnPtrThis(scope, func, tok, last, analyzedFunctions);
1594 }
1595 
1596 void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last, std::set<const Function*>& analyzedFunctions)
1597 {
1598  bool foundReturn = false;
1599 
1600  const Token* const startTok = tok;
1601 
1602  for (; tok && tok != last; tok = tok->next()) {
1603  // check for return of reference to this
1604 
1605  if (const Token* lScope = isLambdaCaptureList(tok)) // skip lambda
1606  tok = lScope->link();
1607 
1608  if (tok->str() != "return")
1609  continue;
1610 
1611  foundReturn = true;
1612 
1613  const Token *retExpr = tok->astOperand1();
1614  if (retExpr && retExpr->str() == "=")
1615  retExpr = retExpr->astOperand1();
1616  if (retExpr && retExpr->isUnaryOp("*") && Token::simpleMatch(retExpr->astOperand1(), "this"))
1617  continue;
1618 
1619  std::string cast("( " + scope->className + " & )");
1620  if (Token::simpleMatch(tok->next(), cast.c_str(), cast.size()))
1621  tok = tok->tokAt(4);
1622 
1623  // check if a function is called
1624  if (tok->strAt(2) == "(" &&
1625  tok->linkAt(2)->next()->str() == ";") {
1626  // check if it is a member function
1627  for (std::list<Function>::const_iterator it = scope->functionList.cbegin(); it != scope->functionList.cend(); ++it) {
1628  // check for a regular function with the same name and a body
1629  if (it->type == Function::eFunction && it->hasBody() &&
1630  it->token->str() == tok->next()->str()) {
1631  // check for the proper return type
1632  if (it->tokenDef->previous()->str() == "&" &&
1633  it->tokenDef->strAt(-2) == scope->className) {
1634  // make sure it's not a const function
1635  if (!it->isConst()) {
1636  /** @todo make sure argument types match */
1637  // avoid endless recursions
1638  if (analyzedFunctions.find(&*it) == analyzedFunctions.end()) {
1639  analyzedFunctions.insert(&*it);
1640  checkReturnPtrThis(scope, &*it, it->arg->link()->next(), it->arg->link()->next()->link(),
1641  analyzedFunctions);
1642  }
1643  // just bail for now
1644  else
1645  return;
1646  }
1647  }
1648  }
1649  }
1650  }
1651 
1652  // check if *this is returned
1653  else if (!(Token::simpleMatch(tok->next(), "operator= (") ||
1654  Token::simpleMatch(tok->next(), "this . operator= (") ||
1655  (Token::Match(tok->next(), "%type% :: operator= (") &&
1656  tok->next()->str() == scope->className)))
1658  }
1659  if (foundReturn) {
1660  return;
1661  }
1662  if (startTok->next() == last) {
1663  const std::string tmp("( const " + scope->className + " &");
1664  if (Token::simpleMatch(func->argDef, tmp.c_str(), tmp.size())) {
1665  // Typical wrong way to suppress default assignment operator by declaring it and leaving empty
1667  } else {
1669  }
1670  return;
1671  }
1672  if (mSettings->library.isScopeNoReturn(last, nullptr)) {
1673  // Typical wrong way to prohibit default assignment operator
1674  // by always throwing an exception or calling a noreturn function
1676  return;
1677  }
1678 
1680 }
1681 
1683 {
1684  reportError(tok, Severity::style, "operatorEqRetRefThis", "'operator=' should return reference to 'this' instance.", CWE398, Certainty::normal);
1685 }
1686 
1688 {
1689  reportError(tok, Severity::style, "operatorEqShouldBeLeftUnimplemented", "'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.", CWE398, Certainty::normal);
1690 }
1691 
1693 {
1694  if (error) {
1695  reportError(tok, Severity::error, "operatorEqMissingReturnStatement", "No 'return' statement in non-void function causes undefined behavior.", CWE398, Certainty::normal);
1696  } else {
1698  }
1699 }
1700 
1701 //---------------------------------------------------------------------------
1702 // ClassCheck: "C& operator=(const C& rhs) { if (this == &rhs) ... }"
1703 // operator= should check for assignment to self
1704 //
1705 // For simple classes, an assignment to self check is only a potential optimization.
1706 //
1707 // For classes that allocate dynamic memory, assignment to self can be a real error
1708 // if it is deallocated and allocated again without being checked for.
1709 //
1710 // This check is not valid for classes with multiple inheritance because a
1711 // class can have multiple addresses so there is no trivial way to check for
1712 // assignment to self.
1713 //---------------------------------------------------------------------------
1714 
1716 {
1718  return;
1719 
1720  logChecker("CheckClass::operatorEqToSelf"); // warning
1721 
1722  for (const Scope * scope : mSymbolDatabase->classAndStructScopes) {
1723  // skip classes with multiple inheritance
1724  if (scope->definedType->derivedFrom.size() > 1)
1725  continue;
1726 
1727  for (const Function &func : scope->functionList) {
1728  if (func.type == Function::eOperatorEqual && func.hasBody()) {
1729  // make sure that the operator takes an object of the same type as *this, otherwise we can't detect self-assignment checks
1730  if (func.argumentList.empty())
1731  continue;
1732  const Token* typeTok = func.argumentList.front().typeEndToken();
1733  while (typeTok->str() == "const" || typeTok->str() == "&" || typeTok->str() == "*")
1734  typeTok = typeTok->previous();
1735  if (typeTok->str() != scope->className)
1736  continue;
1737 
1738  // make sure return signature is correct
1739  if (Token::Match(func.retDef, "%type% &") && func.retDef->str() == scope->className) {
1740  // find the parameter name
1741  const Token *rhs = func.argumentList.cbegin()->nameToken();
1742  const Token* out_ifStatementScopeStart = nullptr;
1743  if (!hasAssignSelf(&func, rhs, out_ifStatementScopeStart)) {
1744  if (hasAllocation(&func, scope))
1746  } else if (out_ifStatementScopeStart != nullptr) {
1747  if (hasAllocationInIfScope(&func, scope, out_ifStatementScopeStart))
1749  }
1750  }
1751  }
1752  }
1753  }
1754 }
1755 
1756 bool CheckClass::hasAllocationInIfScope(const Function *func, const Scope* scope, const Token *ifStatementScopeStart) const
1757 {
1758  const Token *end;
1759  if (ifStatementScopeStart->str() == "{")
1760  end = ifStatementScopeStart->link();
1761  else
1762  end = func->functionScope->bodyEnd;
1763  return hasAllocation(func, scope, ifStatementScopeStart, end);
1764 }
1765 
1766 bool CheckClass::hasAllocation(const Function *func, const Scope* scope) const
1767 {
1768  return hasAllocation(func, scope, func->functionScope->bodyStart, func->functionScope->bodyEnd);
1769 }
1770 
1771 bool CheckClass::hasAllocation(const Function *func, const Scope* scope, const Token *start, const Token *end) const
1772 {
1773  if (!end)
1774  end = func->functionScope->bodyEnd;
1775  for (const Token *tok = start; tok && (tok != end); tok = tok->next()) {
1776  if (((tok->isCpp() && Token::Match(tok, "%var% = new")) ||
1777  (Token::Match(tok, "%var% = %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) &&
1778  isMemberVar(scope, tok))
1779  return true;
1780 
1781  // check for deallocating memory
1782  const Token *var;
1783  if (Token::Match(tok, "%name% ( %var%") && mSettings->library.getDeallocFuncInfo(tok))
1784  var = tok->tokAt(2);
1785  else if (tok->isCpp() && Token::Match(tok, "delete [ ] %var%"))
1786  var = tok->tokAt(3);
1787  else if (tok->isCpp() && Token::Match(tok, "delete %var%"))
1788  var = tok->next();
1789  else
1790  continue;
1791  // Check for assignment to the deleted pointer (only if its a member of the class)
1792  if (isMemberVar(scope, var)) {
1793  for (const Token *tok1 = var->next(); tok1 && (tok1 != end); tok1 = tok1->next()) {
1794  if (Token::Match(tok1, "%varid% =", var->varId()))
1795  return true;
1796  }
1797  }
1798  }
1799 
1800  return false;
1801 }
1802 
1803 static bool isTrueKeyword(const Token* tok)
1804 {
1805  return tok->hasKnownIntValue() && tok->getKnownIntValue() == 1;
1806 }
1807 
1808 static bool isFalseKeyword(const Token* tok)
1809 {
1810  return tok->hasKnownIntValue() && tok->getKnownIntValue() == 0;
1811 }
1812 
1813 /*
1814  * Checks if self-assignment test is inverse
1815  * For example 'if (this == &rhs)'
1816  */
1818 {
1819  bool res = true;
1820  for (const Token *itr = tok; itr && itr->str()!="("; itr=itr->astParent()) {
1821  if (Token::simpleMatch(itr, "!=") && (isTrueKeyword(itr->astOperand1()) || isTrueKeyword(itr->astOperand2()))) {
1822  res = !res;
1823  } else if (Token::simpleMatch(itr, "!=") && ((Token::simpleMatch(itr->astOperand1(), "this") && Token::simpleMatch(itr->astOperand2(), "&") && Token::simpleMatch(itr->astOperand2()->next(), rhs->str().c_str(), rhs->str().size()))
1824  || (Token::simpleMatch(itr->astOperand2(), "this") && Token::simpleMatch(itr->astOperand1(), "&") && Token::simpleMatch(itr->astOperand1()->next(), rhs->str().c_str(), rhs->str().size())))) {
1825  res = !res;
1826  } else if (Token::simpleMatch(itr, "!=") && (isFalseKeyword(itr->astOperand1()) || isFalseKeyword(itr->astOperand2()))) {
1827  //Do nothing
1828  } else if (Token::simpleMatch(itr, "!")) {
1829  res = !res;
1830  } else if (Token::simpleMatch(itr, "==") && (isFalseKeyword(itr->astOperand1()) || isFalseKeyword(itr->astOperand2()))) {
1831  res = !res;
1832  } else if (Token::simpleMatch(itr, "==") && (isTrueKeyword(itr->astOperand1()) || isTrueKeyword(itr->astOperand2()))) {
1833  //Do nothing
1834  } else if (Token::simpleMatch(itr, "==") && ((Token::simpleMatch(itr->astOperand1(), "this") && Token::simpleMatch(itr->astOperand2(), "&") && Token::simpleMatch(itr->astOperand2()->next(), rhs->str().c_str(), rhs->str().size()))
1835  || (Token::simpleMatch(itr->astOperand2(), "this") && Token::simpleMatch(itr->astOperand1(), "&") && Token::simpleMatch(itr->astOperand1()->next(), rhs->str().c_str(), rhs->str().size())))) {
1836  //Do nothing
1837  } else {
1838  return Bool::BAILOUT;
1839  }
1840  }
1841  if (res)
1842  return Bool::TRUE;
1843  return Bool::FALSE;
1844 }
1845 
1846 const Token * CheckClass::getIfStmtBodyStart(const Token *tok, const Token *rhs)
1847 {
1848  const Token *top = tok->astTop();
1849  if (Token::simpleMatch(top->link(), ") {")) {
1850  switch (isInverted(tok->astParent(), rhs)) {
1851  case Bool::BAILOUT:
1852  return nullptr;
1853  case Bool::TRUE:
1854  return top->link()->next();
1855  case Bool::FALSE:
1856  return top->link()->next()->link();
1857  }
1858  }
1859  return nullptr;
1860 }
1861 
1862 bool CheckClass::hasAssignSelf(const Function *func, const Token *rhs, const Token *&out_ifStatementScopeStart)
1863 {
1864  if (!rhs)
1865  return false;
1866  const Token *last = func->functionScope->bodyEnd;
1867  for (const Token *tok = func->functionScope->bodyStart; tok && tok != last; tok = tok->next()) {
1868  if (!Token::simpleMatch(tok, "if ("))
1869  continue;
1870 
1871  bool ret = false;
1872  visitAstNodes(tok->next()->astOperand2(),
1873  [&](const Token *tok2) {
1874  if (!Token::Match(tok2, "==|!="))
1875  return ChildrenToVisit::op1_and_op2;
1876  if (Token::simpleMatch(tok2->astOperand1(), "this"))
1877  tok2 = tok2->astOperand2();
1878  else if (Token::simpleMatch(tok2->astOperand2(), "this"))
1879  tok2 = tok2->astOperand1();
1880  else
1881  return ChildrenToVisit::op1_and_op2;
1882  if (tok2 && tok2->isUnaryOp("&") && tok2->astOperand1()->str() == rhs->str())
1883  ret = true;
1884  if (ret) {
1885  out_ifStatementScopeStart = getIfStmtBodyStart(tok2, rhs);
1886  }
1888  });
1889  if (ret)
1890  return ret;
1891  }
1892 
1893  return false;
1894 }
1895 
1897 {
1898  reportError(tok, Severity::warning, "operatorEqToSelf",
1899  "'operator=' should check for assignment to self to avoid problems with dynamic memory.\n"
1900  "'operator=' should check for assignment to self to ensure that each block of dynamically "
1901  "allocated memory is owned and managed by only one instance of the class.", CWE398, Certainty::normal);
1902 }
1903 
1904 //---------------------------------------------------------------------------
1905 // A destructor in a base class should be virtual
1906 //---------------------------------------------------------------------------
1907 
1909 {
1910  // This error should only be given if:
1911  // * base class doesn't have virtual destructor
1912  // * derived class has non-empty destructor (only c++03, in c++11 it's UB see paragraph 3 in [expr.delete])
1913  // * base class is deleted
1914  // unless inconclusive in which case:
1915  // * A class with any virtual functions should have a destructor that is either public and virtual or protected
1916  const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
1917 
1918  std::list<const Function *> inconclusiveErrors;
1919 
1920  logChecker("CheckClass::virtualDestructor");
1921 
1922  for (const Scope * scope : mSymbolDatabase->classAndStructScopes) {
1923 
1924  // Skip base classes (unless inconclusive)
1925  if (scope->definedType->derivedFrom.empty()) {
1926  if (printInconclusive) {
1927  const Function *destructor = scope->getDestructor();
1928  if (destructor && !destructor->hasVirtualSpecifier() && destructor->access == AccessControl::Public) {
1929  if (std::any_of(scope->functionList.cbegin(), scope->functionList.cend(), [](const Function& func) {
1930  return func.hasVirtualSpecifier();
1931  }))
1932  inconclusiveErrors.push_back(destructor);
1933  }
1934  }
1935  continue;
1936  }
1937 
1938  // Check if destructor is empty and non-empty ..
1940  // Find the destructor
1941  const Function *destructor = scope->getDestructor();
1942 
1943  // Check for destructor with implementation
1944  if (!destructor || !destructor->hasBody())
1945  continue;
1946 
1947  // Empty destructor
1948  if (destructor->token->linkAt(3) == destructor->token->tokAt(4))
1949  continue;
1950  }
1951 
1952  const Token *derived = scope->classDef;
1953  const Token *derivedClass = derived->next();
1954 
1955  // Iterate through each base class...
1956  for (const Type::BaseInfo & j : scope->definedType->derivedFrom) {
1957  // Check if base class is public and exists in database
1958  if (j.access != AccessControl::Private && j.type) {
1959  const Type *derivedFrom = j.type;
1960  const Scope *derivedFromScope = derivedFrom->classScope;
1961  if (!derivedFromScope)
1962  continue;
1963 
1964  // Check for this pattern:
1965  // 1. Base class pointer is given the address of derived class instance
1966  // 2. Base class pointer is deleted
1967  //
1968  // If this pattern is not seen then bailout the checking of these base/derived classes
1969  {
1970  // pointer variables of type 'Base *'
1971  std::set<int> baseClassPointers;
1972 
1973  for (const Variable* var : mSymbolDatabase->variableList()) {
1974  if (var && var->isPointer() && var->type() == derivedFrom)
1975  baseClassPointers.insert(var->declarationId());
1976  }
1977 
1978  // pointer variables of type 'Base *' that should not be deleted
1979  std::set<int> dontDelete;
1980 
1981  // No deletion of derived class instance through base class pointer found => the code is ok
1982  bool ok = true;
1983 
1984  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
1985  if (Token::Match(tok, "[;{}] %var% =") &&
1986  baseClassPointers.find(tok->next()->varId()) != baseClassPointers.end()) {
1987  // new derived class..
1988  const std::string tmp("new " + derivedClass->str());
1989  if (Token::simpleMatch(tok->tokAt(3), tmp.c_str(), tmp.size())) {
1990  dontDelete.insert(tok->next()->varId());
1991  }
1992  }
1993 
1994  // Delete base class pointer that might point at derived class
1995  else if (Token::Match(tok, "delete %var% ;") &&
1996  dontDelete.find(tok->next()->varId()) != dontDelete.end()) {
1997  ok = false;
1998  break;
1999  }
2000  }
2001 
2002  // No base class pointer that points at a derived class is deleted
2003  if (ok)
2004  continue;
2005  }
2006 
2007  // Find the destructor declaration for the base class.
2008  const Function *baseDestructor = derivedFromScope->getDestructor();
2009 
2010  // Check that there is a destructor..
2011  if (!baseDestructor) {
2012  if (derivedFrom->derivedFrom.empty()) {
2013  virtualDestructorError(derivedFrom->classDef, derivedFrom->name(), derivedClass->str(), false);
2014  }
2015  } else if (!baseDestructor->hasVirtualSpecifier()) {
2016  // TODO: This is just a temporary fix, better solution is needed.
2017  // Skip situations where base class has base classes of its own, because
2018  // some of the base classes might have virtual destructor.
2019  // Proper solution is to check all of the base classes. If base class is not
2020  // found or if one of the base classes has virtual destructor, error should not
2021  // be printed. See TODO test case "virtualDestructorInherited"
2022  if (derivedFrom->derivedFrom.empty()) {
2023  // Make sure that the destructor is public (protected or private
2024  // would not compile if inheritance is used in a way that would
2025  // cause the bug we are trying to find here.)
2026  if (baseDestructor->access == AccessControl::Public) {
2027  virtualDestructorError(baseDestructor->token, derivedFrom->name(), derivedClass->str(), false);
2028  // check for duplicate error and remove it if found
2029  const std::list<const Function *>::iterator found = find(inconclusiveErrors.begin(), inconclusiveErrors.end(), baseDestructor);
2030  if (found != inconclusiveErrors.end())
2031  inconclusiveErrors.erase(found);
2032  }
2033  }
2034  }
2035  }
2036  }
2037  }
2038 
2039  for (const Function *func : inconclusiveErrors)
2040  virtualDestructorError(func->tokenDef, func->name(), emptyString, true);
2041 }
2042 
2043 void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive)
2044 {
2045  if (inconclusive) {
2047  reportError(tok, Severity::warning, "virtualDestructor", "$symbol:" + Base + "\nClass '$symbol' which has virtual members does not have a virtual destructor.", CWE404, Certainty::inconclusive);
2048  } else {
2049  reportError(tok, Severity::error, "virtualDestructor",
2050  "$symbol:" + Base +"\n"
2051  "$symbol:" + Derived +"\n"
2052  "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor.\n"
2053  "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor. "
2054  "If you destroy instances of the derived class by deleting a pointer that points to the base class, only "
2055  "the destructor of the base class is executed. Thus, dynamic memory that is managed by the derived class "
2056  "could leak. This can be avoided by adding a virtual destructor to the base class.", CWE404, Certainty::normal);
2057  }
2058 }
2059 
2060 //---------------------------------------------------------------------------
2061 // warn for "this-x". The indented code may be "this->x"
2062 //---------------------------------------------------------------------------
2063 
2065 {
2067  return;
2068 
2069  logChecker("CheckClass::thisSubtraction"); // warning
2070 
2071  const Token *tok = mTokenizer->tokens();
2072  for (;;) {
2073  tok = Token::findmatch(tok, "this - %name%");
2074  if (!tok)
2075  break;
2076 
2077  if (tok->strAt(-1) != "*")
2078  thisSubtractionError(tok);
2079 
2080  tok = tok->next();
2081  }
2082 }
2083 
2085 {
2086  reportError(tok, Severity::warning, "thisSubtraction", "Suspicious pointer subtraction. Did you intend to write '->'?", CWE398, Certainty::normal);
2087 }
2088 
2089 //---------------------------------------------------------------------------
2090 // can member function be const?
2091 //---------------------------------------------------------------------------
2092 
2094 {
2095  // This is an inconclusive check. False positives: #3322.
2097  return;
2098 
2100  !mSettings->isPremiumEnabled("functionConst") &&
2101  !mSettings->isPremiumEnabled("functionStatic"))
2102  return;
2103 
2104  logChecker("CheckClass::checkConst"); // style,inconclusive
2105 
2106  for (const Scope * scope : mSymbolDatabase->classAndStructScopes) {
2107  for (const Function &func : scope->functionList) {
2108  // does the function have a body?
2109  if (func.type != Function::eFunction || !func.hasBody())
2110  continue;
2111  // don't warn for friend/static/virtual functions
2112  if (func.isFriend() || func.isStatic() || func.hasVirtualSpecifier())
2113  continue;
2114 
2115  // don't suggest const when returning non-const pointer/reference, but still suggest static
2116  auto isPointerOrReference = [this](const Token* start, const Token* end) -> bool {
2117  bool inTemplArgList = false, isConstTemplArg = false;
2118  for (const Token* tok = start; tok != end; tok = tok->next()) {
2119  if (tok->str() == "{") // end of trailing return type
2120  return false;
2121  if (tok->str() == "<") {
2122  if (!tok->link())
2123  mSymbolDatabase->debugMessage(tok, "debug", "CheckClass::checkConst found unlinked template argument list '" + tok->expressionString() + "'.");
2124  inTemplArgList = true;
2125  }
2126  else if (tok->str() == ">") {
2127  inTemplArgList = false;
2128  isConstTemplArg = false;
2129  }
2130  else if (tok->str() == "const") {
2131  if (!inTemplArgList)
2132  return false;
2133  isConstTemplArg = true;
2134  }
2135  else if (!isConstTemplArg && Token::Match(tok, "*|&"))
2136  return true;
2137  }
2138  return false;
2139  };
2140 
2141  const bool returnsPtrOrRef = isPointerOrReference(func.retDef, func.tokenDef);
2142 
2143  if (Function::returnsPointer(&func, /*unknown*/ true) || Function::returnsReference(&func, /*unknown*/ true, /*includeRValueRef*/ true)) { // returns const/non-const depending on template arg
2144  bool isTemplateArg = false;
2145  for (const Token* tok2 = func.retDef; precedes(tok2, func.token); tok2 = tok2->next())
2146  if (tok2->isTemplateArg() && tok2->str() == "const") {
2147  isTemplateArg = true;
2148  break;
2149  }
2150  if (isTemplateArg)
2151  continue;
2152  }
2153 
2154  if (func.isOperator()) { // Operator without return type: conversion operator
2155  const std::string& opName = func.tokenDef->str();
2156  if (opName.compare(8, 5, "const") != 0 && (endsWith(opName,'&') || endsWith(opName,'*')))
2157  continue;
2158  } else if (mSettings->library.isSmartPointer(func.retDef)) {
2159  // Don't warn if a std::shared_ptr etc is returned
2160  continue;
2161  } else {
2162  // don't warn for unknown types..
2163  // LPVOID, HDC, etc
2164  if (func.retDef->str().size() > 2 && !func.retDef->type() && func.retDef->isUpperCaseName())
2165  continue;
2166  }
2167 
2168  // check if base class function is virtual
2169  if (!scope->definedType->derivedFrom.empty() && func.isImplicitlyVirtual(true))
2170  continue;
2171 
2172  MemberAccess memberAccessed = MemberAccess::NONE;
2173  // if nothing non-const was found. write error..
2174  if (!checkConstFunc(scope, &func, memberAccessed))
2175  continue;
2176 
2177  const bool suggestStatic = memberAccessed != MemberAccess::MEMBER && !func.isOperator();
2178  if ((returnsPtrOrRef || func.isConst()) && !suggestStatic)
2179  continue;
2180 
2181  std::string classname = scope->className;
2182  const Scope *nest = scope->nestedIn;
2183  while (nest && nest->type != Scope::eGlobal) {
2184  classname = std::string(nest->className + "::" + classname);
2185  nest = nest->nestedIn;
2186  }
2187 
2188  // get function name
2189  std::string functionName = (func.tokenDef->isName() ? "" : "operator") + func.tokenDef->str();
2190 
2191  if (func.tokenDef->str() == "(")
2192  functionName += ")";
2193  else if (func.tokenDef->str() == "[")
2194  functionName += "]";
2195 
2196  if (func.isInline())
2197  checkConstError(func.token, classname, functionName, suggestStatic);
2198  else // not inline
2199  checkConstError2(func.token, func.tokenDef, classname, functionName, suggestStatic);
2200  }
2201  }
2202 }
2203 
2204 // tok should point at "this"
2205 static const Token* getFuncTokFromThis(const Token* tok) {
2206  if (!Token::simpleMatch(tok->next(), "."))
2207  return nullptr;
2208  tok = tok->tokAt(2);
2209  while (Token::Match(tok, "%name% ::"))
2210  tok = tok->tokAt(2);
2211  return Token::Match(tok, "%name% (") ? tok : nullptr;
2212 }
2213 
2214 bool CheckClass::isMemberVar(const Scope *scope, const Token *tok) const
2215 {
2216  bool again = false;
2217 
2218  // try to find the member variable
2219  do {
2220  again = false;
2221 
2222  if (tok->str() == "this")
2223  return !getFuncTokFromThis(tok); // function calls are handled elsewhere
2224  if (Token::simpleMatch(tok->tokAt(-3), "( * this )"))
2225  return true;
2226  if (Token::Match(tok->tokAt(-3), "%name% ) . %name%")) {
2227  tok = tok->tokAt(-3);
2228  again = true;
2229  } else if (Token::Match(tok->tokAt(-2), "%name% . %name%")) {
2230  tok = tok->tokAt(-2);
2231  again = true;
2232  } else if (Token::Match(tok->tokAt(-2), "] . %name%")) {
2233  tok = tok->linkAt(-2)->previous();
2234  again = true;
2235  } else if (tok->str() == "]") {
2236  tok = tok->link()->previous();
2237  again = true;
2238  }
2239  } while (again);
2240 
2241  if (tok->isKeyword() || tok->isStandardType())
2242  return false;
2243 
2244  for (const Variable& var : scope->varlist) {
2245  if (var.name() == tok->str()) {
2246  if (Token::Match(tok, "%name% ::"))
2247  continue;
2248  const Token* fqTok = tok;
2249  while (Token::Match(fqTok->tokAt(-2), "%name% ::"))
2250  fqTok = fqTok->tokAt(-2);
2251  if (fqTok->strAt(-1) == "::")
2252  fqTok = fqTok->previous();
2253  bool isMember = tok == fqTok;
2254  std::string scopeStr;
2255  const Scope* curScope = scope;
2256  while (!isMember && curScope && curScope->type != Scope::ScopeType::eGlobal) {
2257  scopeStr.insert(0, curScope->className + " :: ");
2258  isMember = Token::Match(fqTok, scopeStr.c_str());
2259 
2260  curScope = curScope->nestedIn;
2261  }
2262  if (isMember) {
2263  if (tok->varId() == 0)
2264  mSymbolDatabase->debugMessage(tok, "varid0", "CheckClass::isMemberVar found used member variable \'" + tok->str() + "\' with varid 0");
2265 
2266  return !var.isStatic();
2267  }
2268  }
2269  }
2270 
2271  // not found in this class
2272  if (!scope->definedType->derivedFrom.empty()) {
2273  // check each base class
2274  for (const Type::BaseInfo & i : scope->definedType->derivedFrom) {
2275  // find the base class
2276  const Type *derivedFrom = i.type;
2277 
2278  // find the function in the base class
2279  if (derivedFrom && derivedFrom->classScope && derivedFrom->classScope != scope) {
2280  if (isMemberVar(derivedFrom->classScope, tok))
2281  return true;
2282  }
2283  }
2284  }
2285 
2286  return false;
2287 }
2288 
2289 bool CheckClass::isMemberFunc(const Scope *scope, const Token *tok)
2290 {
2291  if (!tok->function()) {
2292  for (const Function &func : scope->functionList) {
2293  if (func.name() == tok->str()) {
2294  const Token* tok2 = tok->tokAt(2);
2295  int argsPassed = tok2->str() == ")" ? 0 : 1;
2296  for (;;) {
2297  tok2 = tok2->nextArgument();
2298  if (tok2)
2299  argsPassed++;
2300  else
2301  break;
2302  }
2303  if (argsPassed == func.argCount() ||
2304  (func.isVariadic() && argsPassed >= (func.argCount() - 1)) ||
2305  (argsPassed < func.argCount() && argsPassed >= func.minArgCount()))
2306  return true;
2307  }
2308  }
2309  } else if (tok->function()->nestedIn == scope)
2310  return !tok->function()->isStatic();
2311 
2312  // not found in this class
2313  if (!scope->definedType->derivedFrom.empty()) {
2314  // check each base class
2315  for (const Type::BaseInfo & i : scope->definedType->derivedFrom) {
2316  // find the base class
2317  const Type *derivedFrom = i.type;
2318 
2319  // find the function in the base class
2320  if (derivedFrom && derivedFrom->classScope && derivedFrom->classScope != scope) {
2321  if (isMemberFunc(derivedFrom->classScope, tok))
2322  return true;
2323  }
2324  }
2325  }
2326 
2327  return false;
2328 }
2329 
2330 bool CheckClass::isConstMemberFunc(const Scope *scope, const Token *tok)
2331 {
2332  if (!tok->function())
2333  return false;
2334  if (tok->function()->nestedIn == scope)
2335  return tok->function()->isConst();
2336 
2337  // not found in this class
2338  if (!scope->definedType->derivedFrom.empty()) {
2339  // check each base class
2340  for (const Type::BaseInfo & i : scope->definedType->derivedFrom) {
2341  // find the base class
2342  const Type *derivedFrom = i.type;
2343 
2344  // find the function in the base class
2345  if (derivedFrom && derivedFrom->classScope) {
2346  if (isConstMemberFunc(derivedFrom->classScope, tok))
2347  return true;
2348  }
2349  }
2350  }
2351 
2352  return false;
2353 }
2354 
2355 const std::set<std::string> CheckClass::stl_containers_not_const = { "map", "unordered_map", "std :: map|unordered_map <" }; // start pattern
2356 
2357 bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, MemberAccess& memberAccessed) const
2358 {
2360  return false;
2361 
2362  auto getFuncTok = [](const Token* tok) -> const Token* {
2363  if (Token::simpleMatch(tok, "this"))
2364  tok = getFuncTokFromThis(tok);
2365  bool isReturn = false;
2366  if ((Token::Match(tok, "%name% (|{") || (isReturn = Token::simpleMatch(tok->astParent(), "return {"))) && !tok->isStandardType() && !tok->isKeyword()) {
2367  if (isReturn)
2368  tok = tok->astParent();
2369  return tok;
2370  }
2371  return nullptr;
2372  };
2373 
2374  auto checkFuncCall = [this, &memberAccessed](const Token* funcTok, const Scope* scope, const Function* func) {
2375  if (isMemberFunc(scope, funcTok) && (funcTok->strAt(-1) != "." || Token::simpleMatch(funcTok->tokAt(-2), "this ."))) {
2376  const bool isSelf = func == funcTok->function();
2377  if (!isConstMemberFunc(scope, funcTok) && !isSelf)
2378  return false;
2379  memberAccessed = (isSelf && memberAccessed != MemberAccess::MEMBER) ? MemberAccess::SELF : MemberAccess::MEMBER;
2380  }
2381 
2382  if (const Function* f = funcTok->function()) { // check known function
2383  const std::vector<const Token*> args = getArguments(funcTok);
2384  const auto argMax = std::min<nonneg int>(args.size(), f->argCount());
2385 
2386  for (nonneg int argIndex = 0; argIndex < argMax; ++argIndex) {
2387  const Variable* const argVar = f->getArgumentVar(argIndex);
2388  if (!argVar || ((argVar->isArrayOrPointer() || argVar->isReference()) &&
2389  !(argVar->valueType() && argVar->valueType()->isConst(argVar->valueType()->pointer)))) { // argument might be modified
2390  const Token* arg = args[argIndex];
2391  // Member variable given as parameter
2392  const Token* varTok = previousBeforeAstLeftmostLeaf(arg);
2393  if (!varTok)
2394  return false;
2395  varTok = varTok->next();
2396  if ((varTok->isName() && isMemberVar(scope, varTok)) || (varTok->isUnaryOp("&") && (varTok = varTok->astOperand1()) && isMemberVar(scope, varTok))) {
2397  const Variable* var = varTok->variable();
2398  if (!var || (!var->isMutable() && !var->isConst()))
2399  return false;
2400  }
2401  }
2402  }
2403  return true;
2404  }
2405 
2406  // Member variable given as parameter to unknown function
2407  const Token *lpar = funcTok->next();
2408  if (Token::simpleMatch(lpar, "( ) ("))
2409  lpar = lpar->tokAt(2);
2410  for (const Token* tok = lpar->next(); tok && tok != funcTok->next()->link(); tok = tok->next()) {
2411  if (tok->str() == "(")
2412  tok = tok->link();
2413  else if ((tok->isName() && isMemberVar(scope, tok)) || (tok->isUnaryOp("&") && (tok = tok->astOperand1()) && isMemberVar(scope, tok))) {
2414  const Variable* var = tok->variable();
2415  if (!var || (!var->isMutable() && !var->isConst()))
2416  return false;
2417  }
2418  }
2419  return true;
2420  };
2421 
2422  // if the function doesn't have any assignment nor function call,
2423  // it can be a const function..
2424  for (const Token *tok1 = func->functionScope->bodyStart; tok1 && tok1 != func->functionScope->bodyEnd; tok1 = tok1->next()) {
2425  if (tok1->isName() && isMemberVar(scope, tok1)) {
2426  memberAccessed = MemberAccess::MEMBER;
2427  const Variable* v = tok1->variable();
2428  if (v && v->isMutable())
2429  continue;
2430 
2431  if (tok1->str() == "this") {
2432  if (tok1->previous()->isAssignmentOp())
2433  return false;
2434  if (Token::Match(tok1->previous(), "( this . * %var% )")) // call using ptr to member function TODO: check constness
2435  return false;
2436  if (Token::simpleMatch(tok1->astParent(), "*") && tok1->astParent()->astParent() && tok1->astParent()->astParent()->isIncDecOp())
2437  return false;
2438  }
2439 
2440  // non const pointer cast
2441  if (tok1->valueType() && tok1->valueType()->pointer > 0 && tok1->astParent() && tok1->astParent()->isCast() &&
2442  !(tok1->astParent()->valueType() &&
2443  (tok1->astParent()->valueType()->pointer == 0 || tok1->astParent()->valueType()->isConst(tok1->astParent()->valueType()->pointer))))
2444  return false;
2445 
2446  const Token* lhs = tok1->previous();
2447  if (lhs->str() == "(" && tok1->astParent() && tok1->astParent()->astParent())
2448  lhs = tok1->astParent()->astParent();
2449  else if (lhs->str() == "?" && lhs->astParent())
2450  lhs = lhs->astParent();
2451  else if (lhs->str() == ":" && lhs->astParent() && lhs->astParent()->astParent() && lhs->astParent()->str() == "?")
2452  lhs = lhs->astParent()->astParent();
2453  if (lhs->str() == "&") {
2454  const Token* const top = lhs->astTop();
2455  if (top->isAssignmentOp()) {
2456  if (Token::simpleMatch(top->astOperand2(), "{") && !top->astOperand2()->previous()->function()) // TODO: check usage in init list
2457  return false;
2458  if (top->previous()->variable()) {
2459  if (top->previous()->variable()->typeStartToken()->strAt(-1) != "const" && top->previous()->variable()->isPointer())
2460  return false;
2461  }
2462  }
2463  } else if (lhs->str() == ":" && lhs->astParent() && lhs->astParent()->str() == "(") { // range-based for-loop (C++11)
2464  // TODO: We could additionally check what is done with the elements to avoid false negatives. Here we just rely on "const" keyword being used.
2465  if (lhs->astParent()->strAt(1) != "const")
2466  return false;
2467  } else {
2468  if (lhs->isAssignmentOp()) {
2469  const Variable* lhsVar = lhs->previous()->variable();
2470  if (lhsVar && !lhsVar->isConst() && lhsVar->isReference() && lhs == lhsVar->nameToken()->next())
2471  return false;
2472  }
2473  }
2474 
2475  const Token* jumpBackToken = nullptr;
2476  const Token *lastVarTok = tok1;
2477  const Token *end = tok1;
2478  for (;;) {
2479  if (Token::Match(end->next(), ". %name%")) {
2480  end = end->tokAt(2);
2481  if (end->varId())
2482  lastVarTok = end;
2483  } else if (end->strAt(1) == "[") {
2484  if (end->varId()) {
2485  const Variable *var = end->variable();
2486  if (var && var->isStlType(stl_containers_not_const))
2487  return false;
2488  const Token* assignTok = end->next()->astParent();
2489  if (var && assignTok && assignTok->isAssignmentOp() && assignTok->astOperand1() && assignTok->astOperand1()->variable()) {
2490  // cppcheck-suppress shadowFunction - TODO: fix this
2491  const Variable* assignVar = assignTok->astOperand1()->variable();
2492  if (assignVar->isPointer() && !assignVar->isConst() && var->typeScope()) {
2493  const auto& funcMap = var->typeScope()->functionMap;
2494  // if there is no operator that is const and returns a non-const pointer, func cannot be const
2495  if (std::none_of(funcMap.cbegin(), funcMap.cend(), [](const std::pair<std::string, const Function*>& fm) {
2496  return fm.second->isConst() && fm.first == "operator[]" && !Function::returnsConst(fm.second);
2497  }))
2498  return false;
2499  }
2500  }
2501  }
2502  if (!jumpBackToken)
2503  jumpBackToken = end->next(); // Check inside the [] brackets
2504  end = end->linkAt(1);
2505  } else if (end->strAt(1) == ")")
2506  end = end->next();
2507  else
2508  break;
2509  }
2510 
2511  auto hasOverloadedMemberAccess = [](const Token* end, const Scope* scope) -> bool {
2512  if (!end || !scope || !Token::simpleMatch(end->astParent(), "."))
2513  return false;
2514  const std::string op = "operator" + end->astParent()->originalName();
2515  auto it = std::find_if(scope->functionList.begin(), scope->functionList.end(), [&op](const Function& f) {
2516  return f.isConst() && f.name() == op;
2517  });
2518  if (it == scope->functionList.end() || !it->retType || !it->retType->classScope)
2519  return false;
2520  const Function* func = it->retType->classScope->findFunction(end, /*requireConst*/ true);
2521  return func && func->isConst();
2522  };
2523 
2524  if (end->strAt(1) == "(") {
2525  const Variable *var = lastVarTok->variable();
2526  if (!var)
2527  return false;
2528  if ((var->isStlType() // assume all std::*::size() and std::*::empty() are const
2529  && (Token::Match(end, "size|empty|cend|crend|cbegin|crbegin|max_size|length|count|capacity|get_allocator|c_str|str ( )") || Token::Match(end, "rfind|copy"))) ||
2530 
2531  (lastVarTok->valueType() && lastVarTok->valueType()->container &&
2534  && (tok1->previous()->isComparisonOp() ||
2535  (tok1->previous()->isAssignmentOp() && tok1->tokAt(-2)->variable() && Token::Match(tok1->tokAt(-2)->variable()->typeEndToken(), "const_iterator|const_reverse_iterator"))))) {
2536  // empty body
2537  }
2538  else if (var->smartPointerType() && var->smartPointerType()->classScope && isConstMemberFunc(var->smartPointerType()->classScope, end)) {
2539  // empty body
2540  } else if (var->isSmartPointer() && Token::simpleMatch(tok1->next(), ".") && tok1->next()->originalName().empty() && mSettings->library.isFunctionConst(end)) {
2541  // empty body
2542  } else if (hasOverloadedMemberAccess(end, var->typeScope())) {
2543  // empty body
2544  } else if (!var->typeScope() || (end->function() != func && !isConstMemberFunc(var->typeScope(), end))) {
2545  if (!mSettings->library.isFunctionConst(end))
2546  return false;
2547  }
2548  }
2549 
2550  // Assignment
2551  else if (end->next()->isAssignmentOp())
2552  return false;
2553 
2554  // Streaming
2555  else if (end->strAt(1) == "<<" && tok1->strAt(-1) != "<<")
2556  return false;
2557  else if (isLikelyStreamRead(tok1->previous()))
2558  return false;
2559 
2560  // ++/--
2561  else if (end->next()->tokType() == Token::eIncDecOp || tok1->previous()->tokType() == Token::eIncDecOp)
2562  return false;
2563 
2564 
2565  const Token* start = tok1;
2566  while (tok1->strAt(-1) == ")")
2567  tok1 = tok1->linkAt(-1);
2568 
2569  if (start->strAt(-1) == "delete")
2570  return false;
2571 
2572  tok1 = jumpBackToken?jumpBackToken:end; // Jump back to first [ to check inside, or jump to end of expression
2573  if (tok1 == end && Token::Match(end->previous(), ". %name% ( !!)") && !checkFuncCall(tok1, scope, func)) // function call on member
2574  return false;
2575  }
2576 
2577  // streaming: <<
2578  else if (Token::simpleMatch(tok1->previous(), ") <<") &&
2579  isMemberVar(scope, tok1->tokAt(-2))) {
2580  const Variable* var = tok1->tokAt(-2)->variable();
2581  if (!var || !var->isMutable())
2582  return false;
2583  }
2584 
2585  // streaming: >> *this
2586  else if (Token::simpleMatch(tok1, ">> * this") && isLikelyStreamRead(tok1)) {
2587  return false;
2588  }
2589 
2590  // function/constructor call, return init list
2591  else if (const Token* funcTok = getFuncTok(tok1)) {
2592  if (!checkFuncCall(funcTok, scope, func))
2593  return false;
2594  } else if (Token::simpleMatch(tok1, "> (") && (!tok1->link() || !Token::Match(tok1->link()->previous(), "static_cast|const_cast|dynamic_cast|reinterpret_cast"))) {
2595  return false;
2596  }
2597  }
2598 
2599  return true;
2600 }
2601 
2602 void CheckClass::checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic)
2603 {
2604  checkConstError2(tok, nullptr, classname, funcname, suggestStatic);
2605 }
2606 
2607 void CheckClass::checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic)
2608 {
2609  std::list<const Token *> toks{ tok1 };
2610  if (tok2)
2611  toks.push_back(tok2);
2612  if (!suggestStatic)
2613  reportError(toks, Severity::style, "functionConst",
2614  "$symbol:" + classname + "::" + funcname +"\n"
2615  "Technically the member function '$symbol' can be const.\n"
2616  "The member function '$symbol' can be made a const "
2617  "function. Making this function 'const' should not cause compiler errors. "
2618  "Even though the function can be made const function technically it may not make "
2619  "sense conceptually. Think about your design and the task of the function first - is "
2620  "it a function that must not change object internal state?", CWE398, Certainty::inconclusive);
2621  else
2622  reportError(toks, Severity::performance, "functionStatic",
2623  "$symbol:" + classname + "::" + funcname +"\n"
2624  "Technically the member function '$symbol' can be static (but you may consider moving to unnamed namespace).\n"
2625  "The member function '$symbol' can be made a static "
2626  "function. Making a function static can bring a performance benefit since no 'this' instance is "
2627  "passed to the function. This change should not cause compiler errors but it does not "
2628  "necessarily make sense conceptually. Think about your design and the task of the function first - "
2629  "is it a function that must not access members of class instances? And maybe it is more appropriate "
2630  "to move this function to an unnamed namespace.", CWE398, Certainty::inconclusive);
2631 }
2632 
2633 //---------------------------------------------------------------------------
2634 // ClassCheck: Check that initializer list is in declared order.
2635 //---------------------------------------------------------------------------
2636 
2637 namespace { // avoid one-definition-rule violation
2638  struct VarInfo {
2639  VarInfo(const Variable *_var, const Token *_tok)
2640  : var(_var), tok(_tok) {}
2641 
2642  const Variable *var;
2643  const Token *tok;
2644  std::vector<const Variable*> initArgs;
2645  };
2646 }
2647 
2649 {
2650  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("initializerList"))
2651  return;
2652 
2653  // This check is not inconclusive. However it only determines if the initialization
2654  // order is incorrect. It does not determine if being out of order causes
2655  // a real error. Out of order is not necessarily an error but you can never
2656  // have an error if the list is in order so this enforces defensive programming.
2658  return;
2659 
2660  logChecker("CheckClass::initializerListOrder"); // style,inconclusive
2661 
2662  for (const Scope * scope : mSymbolDatabase->classAndStructScopes) {
2663 
2664  // iterate through all member functions looking for constructors
2665  for (std::list<Function>::const_iterator func = scope->functionList.cbegin(); func != scope->functionList.cend(); ++func) {
2666  if (func->isConstructor() && func->hasBody()) {
2667  // check for initializer list
2668  const Token *tok = func->arg->link()->next();
2669 
2670  if (tok->str() == ":") {
2671  std::vector<VarInfo> vars;
2672  tok = tok->next();
2673 
2674  // find all variable initializations in list
2675  for (; tok && tok != func->functionScope->bodyStart; tok = tok->next()) {
2676  if (Token::Match(tok, "%name% (|{")) {
2677  const Token* const end = tok->linkAt(1);
2678  const Variable *var = scope->getVariable(tok->str());
2679  if (var)
2680  vars.emplace_back(var, tok);
2681  else
2682  tok = end;
2683 
2684  for (; tok != end; tok = tok->next()) {
2685  if (const Variable* argVar = scope->getVariable(tok->str())) {
2686  if (scope != argVar->scope())
2687  continue;
2688  if (argVar->isStatic())
2689  continue;
2690  if (tok->variable() && tok->variable()->isArgument())
2691  continue;
2692  if (var->isPointer() && (argVar->isArray() || Token::simpleMatch(tok->astParent(), "&")))
2693  continue;
2694  if (var->isReference())
2695  continue;
2696  if (Token::simpleMatch(tok->astParent(), "="))
2697  continue;
2698  vars.back().initArgs.emplace_back(argVar);
2699  }
2700  }
2701  }
2702  }
2703 
2704  for (int j = 0; j < vars.size(); j++) {
2705  // check for use of uninitialized arguments
2706  for (const auto& arg : vars[j].initArgs)
2707  if (vars[j].var->index() < arg->index())
2708  initializerListError(vars[j].tok, vars[j].var->nameToken(), scope->className, vars[j].var->name(), arg->name());
2709 
2710  // need at least 2 members to have out of order initialization
2711  if (j == 0)
2712  continue;
2713  // check for out of order initialization
2714  if (vars[j].var->index() < vars[j - 1].var->index())
2715  initializerListError(vars[j].tok,vars[j].var->nameToken(), scope->className, vars[j].var->name());
2716  }
2717  }
2718  }
2719  }
2720  }
2721 }
2722 
2723 void CheckClass::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname, const std::string& argname)
2724 {
2725  std::list<const Token *> toks = { tok1, tok2 };
2726  const std::string msg = argname.empty() ?
2727  "Member variable '$symbol' is in the wrong place in the initializer list." :
2728  "Member variable '$symbol' uses an uninitialized argument '" + argname + "' due to the order of declarations.";
2729  reportError(toks, Severity::style, "initializerList",
2730  "$symbol:" + classname + "::" + varname + '\n' +
2731  msg + '\n' +
2732  msg + ' ' +
2733  "Members are initialized in the order they are declared, not in the "
2734  "order they are in the initializer list. Keeping the initializer list "
2735  "in the same order that the members were declared prevents order dependent "
2736  "initialization errors.", CWE398, Certainty::inconclusive);
2737 }
2738 
2739 
2740 //---------------------------------------------------------------------------
2741 // Check for self initialization in initialization list
2742 //---------------------------------------------------------------------------
2743 
2745 {
2746  logChecker("CheckClass::checkSelfInitialization");
2747 
2748  for (const Scope *scope : mSymbolDatabase->functionScopes) {
2749  const Function* function = scope->function;
2750  if (!function || !function->isConstructor())
2751  continue;
2752 
2753  const Token* tok = function->arg->link()->next();
2754  if (tok->str() != ":")
2755  continue;
2756 
2757  for (; tok != scope->bodyStart; tok = tok->next()) {
2758  if (Token::Match(tok, "[:,] %var% (|{")) {
2759  const Token* varTok = tok->next();
2760  if (Token::Match(varTok->astParent(), "(|{")) {
2761  if (const Token* initTok = varTok->astParent()->astOperand2()) {
2762  if (initTok->varId() == varTok->varId())
2763  selfInitializationError(tok, varTok->str());
2764  else if (initTok->isCast() && ((initTok->astOperand1() && initTok->astOperand1()->varId() == varTok->varId()) || (initTok->astOperand2() && initTok->astOperand2()->varId() == varTok->varId())))
2765  selfInitializationError(tok, varTok->str());
2766  }
2767  }
2768  }
2769  }
2770  }
2771 }
2772 
2773 void CheckClass::selfInitializationError(const Token* tok, const std::string& varname)
2774 {
2775  reportError(tok, Severity::error, "selfInitialization", "$symbol:" + varname + "\nMember variable '$symbol' is initialized by itself.", CWE665, Certainty::normal);
2776 }
2777 
2778 
2779 //---------------------------------------------------------------------------
2780 // Check for virtual function calls in constructor/destructor
2781 //---------------------------------------------------------------------------
2782 
2784 {
2786  return;
2787  logChecker("CheckClass::checkVirtualFunctionCallInConstructor"); // warning
2788  std::map<const Function *, std::list<const Token *>> virtualFunctionCallsMap;
2789  for (const Scope *scope : mSymbolDatabase->functionScopes) {
2790  if (scope->function == nullptr || !scope->function->hasBody() ||
2791  !(scope->function->isConstructor() ||
2792  scope->function->isDestructor()))
2793  continue;
2794 
2795  const std::list<const Token *> & virtualFunctionCalls = getVirtualFunctionCalls(*scope->function, virtualFunctionCallsMap);
2796  for (const Token *callToken : virtualFunctionCalls) {
2797  std::list<const Token *> callstack(1, callToken);
2798  getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, callToken, callstack);
2799  if (callstack.empty())
2800  continue;
2801  const Function* const func = callstack.back()->function();
2802  if (!(func->hasVirtualSpecifier() || func->hasOverrideSpecifier()))
2803  continue;
2804  if (func->isPure())
2805  pureVirtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str());
2806  else if (!func->hasFinalSpecifier() &&
2807  !(func->nestedIn && func->nestedIn->classDef && func->nestedIn->classDef->isFinalType()))
2808  virtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str());
2809  }
2810  }
2811 }
2812 
2813 const std::list<const Token *> & CheckClass::getVirtualFunctionCalls(const Function & function,
2814  std::map<const Function *, std::list<const Token *>> & virtualFunctionCallsMap)
2815 {
2816  const std::map<const Function *, std::list<const Token *>>::const_iterator found = virtualFunctionCallsMap.find(&function);
2817  if (found != virtualFunctionCallsMap.end())
2818  return found->second;
2819 
2820  virtualFunctionCallsMap[&function] = std::list<const Token *>();
2821  std::list<const Token *> & virtualFunctionCalls = virtualFunctionCallsMap.find(&function)->second;
2822 
2823  if (!function.hasBody() || !function.functionScope)
2824  return virtualFunctionCalls;
2825 
2826  for (const Token *tok = function.arg->link(); tok != function.functionScope->bodyEnd; tok = tok->next()) {
2827  if (function.type != Function::eConstructor &&
2828  function.type != Function::eCopyConstructor &&
2829  function.type != Function::eMoveConstructor &&
2830  function.type != Function::eDestructor) {
2831  if ((Token::simpleMatch(tok, ") {") && tok->link() && Token::Match(tok->link()->previous(), "if|switch")) ||
2832  Token::simpleMatch(tok, "else {")) {
2833  // Assume pure virtual function call is prevented by "if|else|switch" condition
2834  tok = tok->linkAt(1);
2835  continue;
2836  }
2837  }
2838  if (tok->scope()->type == Scope::eLambda)
2839  tok = tok->scope()->bodyEnd->next();
2840 
2841  const Function * callFunction = tok->function();
2842  if (!callFunction ||
2843  function.nestedIn != callFunction->nestedIn ||
2844  Token::simpleMatch(tok->previous(), ".") ||
2845  !(tok->astParent() && (tok->astParent()->str() == "(" || (tok->astParent()->str() == "::" && Token::simpleMatch(tok->astParent()->astParent(), "(")))))
2846  continue;
2847 
2848  if (tok->previous() &&
2849  tok->previous()->str() == "(") {
2850  const Token * prev = tok->previous();
2851  if (prev->previous() &&
2852  (mSettings->library.ignorefunction(tok->str())
2853  || mSettings->library.ignorefunction(prev->previous()->str())))
2854  continue;
2855  }
2856 
2857  if (callFunction->isImplicitlyVirtual()) {
2858  if (!callFunction->isPure() && Token::simpleMatch(tok->previous(), "::"))
2859  continue;
2860  virtualFunctionCalls.push_back(tok);
2861  continue;
2862  }
2863 
2864  const std::list<const Token *> & virtualFunctionCallsOfTok = getVirtualFunctionCalls(*callFunction, virtualFunctionCallsMap);
2865  if (!virtualFunctionCallsOfTok.empty())
2866  virtualFunctionCalls.push_back(tok);
2867  }
2868  return virtualFunctionCalls;
2869 }
2870 
2872  std::map<const Function *, std::list<const Token *>> & virtualFunctionCallsMap,
2873  const Token * callToken,
2874  std::list<const Token *> & pureFuncStack)
2875 {
2876  const Function *callFunction = callToken->function();
2877  if (callFunction->isImplicitlyVirtual() && (!callFunction->isPure() || !callFunction->hasBody())) {
2878  pureFuncStack.push_back(callFunction->tokenDef);
2879  return;
2880  }
2881  std::map<const Function *, std::list<const Token *>>::const_iterator found = virtualFunctionCallsMap.find(callFunction);
2882  if (found == virtualFunctionCallsMap.cend() || found->second.empty()) {
2883  pureFuncStack.clear();
2884  return;
2885  }
2886  const Token * firstCall = *found->second.cbegin();
2887  pureFuncStack.push_back(firstCall);
2888  getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, firstCall, pureFuncStack);
2889 }
2890 
2892  const Function * scopeFunction,
2893  const std::list<const Token *> & tokStack,
2894  const std::string &funcname)
2895 {
2896  if (scopeFunction && !mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("virtualCallInConstructor"))
2897  return;
2898 
2899  const char * scopeFunctionTypeName = scopeFunction ? getFunctionTypeName(scopeFunction->type) : "constructor";
2900 
2901  ErrorPath errorPath;
2902  std::transform(tokStack.cbegin(), tokStack.cend(), std::back_inserter(errorPath), [](const Token* tok) {
2903  return ErrorPathItem(tok, "Calling " + tok->str());
2904  });
2905  int lineNumber = 1;
2906  if (!errorPath.empty()) {
2907  lineNumber = errorPath.front().first->linenr();
2908  errorPath.back().second = funcname + " is a virtual function";
2909  }
2910 
2911  std::string constructorName;
2912  if (scopeFunction) {
2913  const Token *endToken = scopeFunction->argDef->link()->next();
2914  if (scopeFunction->type == Function::Type::eDestructor)
2915  constructorName = "~";
2916  for (const Token *tok = scopeFunction->tokenDef; tok != endToken; tok = tok->next()) {
2917  if (!constructorName.empty() && Token::Match(tok->previous(), "%name%|%num% %name%|%num%"))
2918  constructorName += ' ';
2919  constructorName += tok->str();
2920  if (tok->str() == ")")
2921  break;
2922  }
2923  }
2924 
2925  reportError(errorPath, Severity::style, "virtualCallInConstructor",
2926  "Virtual function '" + funcname + "' is called from " + scopeFunctionTypeName + " '" + constructorName + "' at line " + std::to_string(lineNumber) + ". Dynamic binding is not used.", CWE(0U), Certainty::normal);
2927 }
2928 
2930  const Function * scopeFunction,
2931  const std::list<const Token *> & tokStack,
2932  const std::string &purefuncname)
2933 {
2934  const char * scopeFunctionTypeName = scopeFunction ? getFunctionTypeName(scopeFunction->type) : "constructor";
2935 
2936  ErrorPath errorPath;
2937  std::transform(tokStack.cbegin(), tokStack.cend(), std::back_inserter(errorPath), [](const Token* tok) {
2938  return ErrorPathItem(tok, "Calling " + tok->str());
2939  });
2940  if (!errorPath.empty())
2941  errorPath.back().second = purefuncname + " is a pure virtual function without body";
2942 
2943  reportError(errorPath, Severity::warning, "pureVirtualCall",
2944  "$symbol:" + purefuncname +"\n"
2945  "Call of pure virtual function '$symbol' in " + scopeFunctionTypeName + ".\n"
2946  "Call of pure virtual function '$symbol' in " + scopeFunctionTypeName + ". The call will fail during runtime.", CWE(0U), Certainty::normal);
2947 }
2948 
2949 
2950 //---------------------------------------------------------------------------
2951 // Check for members hiding inherited members with the same name
2952 //---------------------------------------------------------------------------
2953 
2955 {
2957  return;
2958 
2959  logChecker("CheckClass::checkDuplInheritedMembers"); // warning
2960 
2961  // Iterate over all classes
2962  for (const Type &classIt : mSymbolDatabase->typeList) {
2963  // Iterate over the parent classes
2964  checkDuplInheritedMembersRecursive(&classIt, &classIt);
2965  }
2966 }
2967 
2968 namespace {
2969  struct DuplMemberInfo {
2970  DuplMemberInfo(const Variable* cv, const Variable* pcv, const Type::BaseInfo* pc) : classVar(cv), parentClassVar(pcv), parentClass(pc) {}
2971  const Variable* classVar;
2972  const Variable* parentClassVar;
2973  const Type::BaseInfo* parentClass;
2974  };
2975  struct DuplMemberFuncInfo {
2976  DuplMemberFuncInfo(const Function* cf, const Function* pcf, const Type::BaseInfo* pc) : classFunc(cf), parentClassFunc(pcf), parentClass(pc) {}
2977  const Function* classFunc;
2978  const Function* parentClassFunc;
2979  const Type::BaseInfo* parentClass;
2980  };
2981 }
2982 
2983 static std::vector<DuplMemberInfo> getDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase, bool skipPrivate = true)
2984 {
2985  std::vector<DuplMemberInfo> results;
2986  for (const Type::BaseInfo &parentClassIt : typeBase->derivedFrom) {
2987  // Check if there is info about the 'Base' class
2988  if (!parentClassIt.type || !parentClassIt.type->classScope)
2989  continue;
2990  // Don't crash on recursive templates
2991  if (parentClassIt.type == typeBase)
2992  continue;
2993  // Check if they have a member variable in common
2994  for (const Variable &classVarIt : typeCurrent->classScope->varlist) {
2995  for (const Variable &parentClassVarIt : parentClassIt.type->classScope->varlist) {
2996  if (classVarIt.name() == parentClassVarIt.name() && (!parentClassVarIt.isPrivate() || !skipPrivate)) // Check if the class and its parent have a common variable
2997  results.emplace_back(&classVarIt, &parentClassVarIt, &parentClassIt);
2998  }
2999  }
3000  if (typeCurrent != parentClassIt.type) {
3001  const auto recursive = getDuplInheritedMembersRecursive(typeCurrent, parentClassIt.type, skipPrivate);
3002  results.insert(results.end(), recursive.begin(), recursive.end());
3003  }
3004  }
3005  return results;
3006 }
3007 
3008 static std::vector<DuplMemberFuncInfo> getDuplInheritedMemberFunctionsRecursive(const Type* typeCurrent, const Type* typeBase, bool skipPrivate = true)
3009 {
3010  std::vector<DuplMemberFuncInfo> results;
3011  for (const Type::BaseInfo &parentClassIt : typeBase->derivedFrom) {
3012  // Check if there is info about the 'Base' class
3013  if (!parentClassIt.type || !parentClassIt.type->classScope)
3014  continue;
3015  // Don't crash on recursive templates
3016  if (parentClassIt.type == typeBase)
3017  continue;
3018  for (const Function& classFuncIt : typeCurrent->classScope->functionList) {
3019  if (classFuncIt.isImplicitlyVirtual())
3020  continue;
3021  for (const Function& parentClassFuncIt : parentClassIt.type->classScope->functionList) {
3022  if (classFuncIt.name() == parentClassFuncIt.name() &&
3023  (parentClassFuncIt.access != AccessControl::Private || !skipPrivate) &&
3024  !classFuncIt.isConstructor() && !classFuncIt.isDestructor() &&
3025  classFuncIt.argsMatch(parentClassIt.type->classScope, parentClassFuncIt.argDef, classFuncIt.argDef, emptyString, 0) &&
3026  (classFuncIt.isConst() == parentClassFuncIt.isConst() || Function::returnsConst(&classFuncIt) == Function::returnsConst(&parentClassFuncIt)) &&
3027  !(classFuncIt.isDelete() || parentClassFuncIt.isDelete()))
3028  results.emplace_back(&classFuncIt, &parentClassFuncIt, &parentClassIt);
3029  }
3030  }
3031  if (typeCurrent != parentClassIt.type) {
3032  const auto recursive = getDuplInheritedMemberFunctionsRecursive(typeCurrent, parentClassIt.type);
3033  results.insert(results.end(), recursive.begin(), recursive.end());
3034  }
3035  }
3036  return results;
3037 }
3038 
3039 void CheckClass::checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase)
3040 {
3041  const auto resultsVar = getDuplInheritedMembersRecursive(typeCurrent, typeBase);
3042  for (const auto& r : resultsVar) {
3043  duplInheritedMembersError(r.classVar->nameToken(), r.parentClassVar->nameToken(),
3044  typeCurrent->name(), r.parentClass->type->name(), r.classVar->name(),
3045  typeCurrent->classScope->type == Scope::eStruct,
3046  r.parentClass->type->classScope->type == Scope::eStruct);
3047  }
3048 
3049  const auto resultsFunc = getDuplInheritedMemberFunctionsRecursive(typeCurrent, typeBase);
3050  for (const auto& r : resultsFunc) {
3051  duplInheritedMembersError(r.classFunc->token, r.parentClassFunc->token,
3052  typeCurrent->name(), r.parentClass->type->name(), r.classFunc->name(),
3053  typeCurrent->classScope->type == Scope::eStruct,
3054  r.parentClass->type->classScope->type == Scope::eStruct, /*isFunction*/ true);
3055  }
3056 }
3057 
3058 void CheckClass::duplInheritedMembersError(const Token *tok1, const Token* tok2,
3059  const std::string &derivedName, const std::string &baseName,
3060  const std::string &memberName, bool derivedIsStruct, bool baseIsStruct, bool isFunction)
3061 {
3062  ErrorPath errorPath;
3063  const std::string member = isFunction ? "function" : "variable";
3064  errorPath.emplace_back(tok2, "Parent " + member + " '" + baseName + "::" + memberName + "'");
3065  errorPath.emplace_back(tok1, "Derived " + member + " '" + derivedName + "::" + memberName + "'");
3066 
3067  const std::string symbols = "$symbol:" + derivedName + "\n$symbol:" + memberName + "\n$symbol:" + baseName;
3068 
3069  const std::string message = "The " + std::string(derivedIsStruct ? "struct" : "class") + " '" + derivedName +
3070  "' defines member " + member + " with name '" + memberName + "' also defined in its parent " +
3071  std::string(baseIsStruct ? "struct" : "class") + " '" + baseName + "'.";
3072  reportError(errorPath, Severity::warning, "duplInheritedMember", symbols + '\n' + message, CWE398, Certainty::normal);
3073 }
3074 
3075 
3076 //---------------------------------------------------------------------------
3077 // Check that copy constructor and operator defined together
3078 //---------------------------------------------------------------------------
3079 
3080 enum class CtorType {
3081  NO,
3082  WITHOUT_BODY,
3083  WITH_BODY
3084 };
3085 
3087 {
3088  // This is disabled because of #8388
3089  // The message must be clarified. How is the behaviour different?
3090  // cppcheck-suppress unreachableCode - remove when code is enabled again
3091  if ((true) || !mSettings->severity.isEnabled(Severity::warning)) // NOLINT(readability-simplify-boolean-expr)
3092  return;
3093 
3094  // logChecker
3095 
3096  for (const Scope * scope : mSymbolDatabase->classAndStructScopes) {
3097 
3098  const bool hasNonStaticVars = std::any_of(scope->varlist.begin(), scope->varlist.end(), [](const Variable& var) {
3099  return !var.isStatic();
3100  });
3101  if (!hasNonStaticVars)
3102  continue;
3103 
3104  CtorType copyCtors = CtorType::NO;
3105  bool moveCtor = false;
3106  CtorType assignmentOperators = CtorType::NO;
3107 
3108  for (const Function &func : scope->functionList) {
3109  if (copyCtors == CtorType::NO && func.type == Function::eCopyConstructor) {
3110  copyCtors = func.hasBody() ? CtorType::WITH_BODY : CtorType::WITHOUT_BODY;
3111  }
3112  if (assignmentOperators == CtorType::NO && func.type == Function::eOperatorEqual) {
3113  const Variable * variable = func.getArgumentVar(0);
3114  if (variable && variable->type() && variable->type()->classScope == scope) {
3115  assignmentOperators = func.hasBody() ? CtorType::WITH_BODY : CtorType::WITHOUT_BODY;
3116  }
3117  }
3118  if (func.type == Function::eMoveConstructor) {
3119  moveCtor = true;
3120  break;
3121  }
3122  }
3123 
3124  if (moveCtor)
3125  continue;
3126 
3127  // No method defined
3128  if (copyCtors != CtorType::WITH_BODY && assignmentOperators != CtorType::WITH_BODY)
3129  continue;
3130 
3131  // both methods are defined
3132  if (copyCtors != CtorType::NO && assignmentOperators != CtorType::NO)
3133  continue;
3134 
3135  copyCtorAndEqOperatorError(scope->classDef, scope->className, scope->type == Scope::eStruct, copyCtors == CtorType::WITH_BODY);
3136  }
3137 }
3138 
3139 void CheckClass::copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor)
3140 {
3141  const std::string message = "$symbol:" + classname + "\n"
3142  "The " + std::string(isStruct ? "struct" : "class") + " '$symbol' has '" +
3144  "' but lack of '" + getFunctionTypeName(hasCopyCtor ? Function::eOperatorEqual : Function::eCopyConstructor) +
3145  "'.";
3146  reportError(tok, Severity::warning, "copyCtorAndEqOperator", message);
3147 }
3148 
3150 {
3151  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("missingOverride"))
3152  return;
3154  return;
3155  logChecker("CheckClass::checkMissingOverride"); // style,c++03
3156  for (const Scope * classScope : mSymbolDatabase->classAndStructScopes) {
3157  if (!classScope->definedType || classScope->definedType->derivedFrom.empty())
3158  continue;
3159  for (const Function &func : classScope->functionList) {
3160  if (func.hasOverrideSpecifier() || func.hasFinalSpecifier())
3161  continue;
3162  const Function *baseFunc = func.getOverriddenFunction();
3163  if (baseFunc)
3164  overrideError(baseFunc, &func);
3165  }
3166  }
3167 }
3168 
3169 void CheckClass::overrideError(const Function *funcInBase, const Function *funcInDerived)
3170 {
3171  const std::string functionName = funcInDerived ? ((funcInDerived->isDestructor() ? "~" : "") + funcInDerived->name()) : "";
3172  const std::string funcType = (funcInDerived && funcInDerived->isDestructor()) ? "destructor" : "function";
3173 
3174  ErrorPath errorPath;
3175  if (funcInBase && funcInDerived) {
3176  errorPath.emplace_back(funcInBase->tokenDef, "Virtual " + funcType + " in base class");
3177  errorPath.emplace_back(funcInDerived->tokenDef, char(std::toupper(funcType[0])) + funcType.substr(1) + " in derived class");
3178  }
3179 
3180  reportError(errorPath, Severity::style, "missingOverride",
3181  "$symbol:" + functionName + "\n"
3182  "The " + funcType + " '$symbol' overrides a " + funcType + " in a base class but is not marked with a 'override' specifier.",
3183  CWE(0U) /* Unknown CWE! */,
3185 }
3186 
3187 void CheckClass::uselessOverrideError(const Function *funcInBase, const Function *funcInDerived, bool isSameCode)
3188 {
3189  const std::string functionName = funcInDerived ? ((funcInDerived->isDestructor() ? "~" : "") + funcInDerived->name()) : "";
3190  const std::string funcType = (funcInDerived && funcInDerived->isDestructor()) ? "destructor" : "function";
3191 
3192  ErrorPath errorPath;
3193  if (funcInBase && funcInDerived) {
3194  errorPath.emplace_back(funcInBase->tokenDef, "Virtual " + funcType + " in base class");
3195  errorPath.emplace_back(funcInDerived->tokenDef, char(std::toupper(funcType[0])) + funcType.substr(1) + " in derived class");
3196  }
3197 
3198  std::string errStr = "\nThe " + funcType + " '$symbol' overrides a " + funcType + " in a base class but ";
3199  if (isSameCode) {
3200  errStr += "is identical to the overridden function";
3201  }
3202  else
3203  errStr += "just delegates back to the base class.";
3204  reportError(errorPath, Severity::style, "uselessOverride",
3205  "$symbol:" + functionName +
3206  errStr,
3207  CWE(0U) /* Unknown CWE! */,
3209 }
3210 
3211 static const Token* getSingleFunctionCall(const Scope* scope) {
3212  const Token* const start = scope->bodyStart->next();
3213  const Token* const end = Token::findsimplematch(start, ";", 1, scope->bodyEnd);
3214  if (!end || end->next() != scope->bodyEnd)
3215  return nullptr;
3216  const Token* ftok = start;
3217  if (ftok->str() == "return")
3218  ftok = ftok->astOperand1();
3219  else {
3220  while (Token::Match(ftok, "%name%|::"))
3221  ftok = ftok->next();
3222  }
3223  if (Token::simpleMatch(ftok, "(") && ftok->previous()->function())
3224  return ftok->previous();
3225  return nullptr;
3226 }
3227 
3228 static bool compareTokenRanges(const Token* start1, const Token* end1, const Token* start2, const Token* end2) {
3229  const Token* tok1 = start1;
3230  const Token* tok2 = start2;
3231  bool isEqual = false;
3232  while (tok1 && tok2) {
3233  if (tok1->str() != tok2->str())
3234  break;
3235  if (tok1->str() == "this")
3236  break;
3237  if (tok1->isExpandedMacro() || tok2->isExpandedMacro())
3238  break;
3239  if (tok1 == end1 && tok2 == end2) {
3240  isEqual = true;
3241  break;
3242  }
3243  tok1 = tok1->next();
3244  tok2 = tok2->next();
3245  }
3246  return isEqual;
3247 }
3248 
3250 {
3251  if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("uselessOverride"))
3252  return;
3253 
3254  logChecker("CheckClass::checkUselessOverride"); // style
3255 
3256  for (const Scope* classScope : mSymbolDatabase->classAndStructScopes) {
3257  if (!classScope->definedType || classScope->definedType->derivedFrom.size() != 1)
3258  continue;
3259  for (const Function& func : classScope->functionList) {
3260  if (!func.functionScope)
3261  continue;
3262  if (func.hasFinalSpecifier())
3263  continue;
3264  const Function* baseFunc = func.getOverriddenFunction();
3265  if (!baseFunc || baseFunc->isPure() || baseFunc->access != func.access)
3266  continue;
3267  if (std::any_of(classScope->functionList.begin(), classScope->functionList.end(), [&func](const Function& f) { // check for overloads
3268  if (&f == &func)
3269  return false;
3270  return f.name() == func.name();
3271  }))
3272  continue;
3273  if (func.token->isExpandedMacro() || baseFunc->token->isExpandedMacro())
3274  continue;
3275  if (baseFunc->functionScope) {
3276  bool isSameCode = compareTokenRanges(baseFunc->argDef, baseFunc->argDef->link(), func.argDef, func.argDef->link()); // function arguments
3277  if (isSameCode) {
3278  isSameCode = compareTokenRanges(baseFunc->functionScope->bodyStart, baseFunc->functionScope->bodyEnd, // function body
3280 
3281  if (isSameCode) {
3282  // bailout for shadowed members
3283  if (!classScope->definedType ||
3284  !getDuplInheritedMembersRecursive(classScope->definedType, classScope->definedType, /*skipPrivate*/ false).empty() ||
3285  !getDuplInheritedMemberFunctionsRecursive(classScope->definedType, classScope->definedType, /*skipPrivate*/ false).empty())
3286  continue;
3287  uselessOverrideError(baseFunc, &func, true);
3288  continue;
3289  }
3290  }
3291  }
3292  if (const Token* const call = getSingleFunctionCall(func.functionScope)) {
3293  if (call->function() != baseFunc)
3294  continue;
3295  std::vector<const Token*> funcArgs = getArguments(func.tokenDef);
3296  std::vector<const Token*> callArgs = getArguments(call);
3297  if (funcArgs.size() != callArgs.size() ||
3298  !std::equal(funcArgs.begin(), funcArgs.end(), callArgs.begin(), [](const Token* t1, const Token* t2) {
3299  return t1->str() == t2->str();
3300  }))
3301  continue;
3302  uselessOverrideError(baseFunc, &func);
3303  }
3304  }
3305  }
3306 }
3307 
3308 static const Variable* getSingleReturnVar(const Scope* scope) {
3309  if (!scope || !scope->bodyStart)
3310  return nullptr;
3311  const Token* const start = scope->bodyStart->next();
3312  const Token* const end = Token::findsimplematch(start, ";", 1, scope->bodyEnd);
3313  if (!end || end->next() != scope->bodyEnd)
3314  return nullptr;
3315  if (!start->astOperand1() || start->str() != "return")
3316  return nullptr;
3317  return start->astOperand1()->variable();
3318 }
3319 
3321 {
3323  return;
3324 
3325  logChecker("CheckClass::checkReturnByReference"); // performance
3326 
3327  for (const Scope* classScope : mSymbolDatabase->classAndStructScopes) {
3328  for (const Function& func : classScope->functionList) {
3330  continue;
3331  if (func.isImplicitlyVirtual())
3332  continue;
3333  if (func.isOperator())
3334  continue;
3335  if (const Library::Container* container = mSettings->library.detectContainer(func.retDef))
3336  if (container->view)
3337  continue;
3338  if (const Variable* var = getSingleReturnVar(func.functionScope)) {
3339  if (!var->valueType())
3340  continue;
3341  if (var->isArgument())
3342  continue;
3343  const bool isContainer = var->valueType()->type == ValueType::Type::CONTAINER && var->valueType()->container;
3344  const bool isView = isContainer && var->valueType()->container->view;
3345  bool warn = isContainer && !isView;
3346  if (!warn && !isView) {
3347  const std::size_t size = ValueFlow::getSizeOf(*var->valueType(), *mSettings);
3348  if (size > 2 * mSettings->platform.sizeof_pointer)
3349  warn = true;
3350  }
3351  if (warn)
3352  returnByReferenceError(&func, var);
3353  }
3354  }
3355  }
3356 }
3357 
3359 {
3360  const Token* tok = func ? func->tokenDef : nullptr;
3361  const std::string message = "Function '" + (func ? func->name() : "func") + "()' should return member '" + (var ? var->name() : "var") + "' by const reference.";
3362  reportError(tok, Severity::performance, "returnByReference", message);
3363 }
3364 
3366 {
3368  return;
3369 
3370  logChecker("CheckClass::checkThisUseAfterFree"); // warning
3371 
3372  for (const Scope * classScope : mSymbolDatabase->classAndStructScopes) {
3373 
3374  for (const Variable &var : classScope->varlist) {
3375  // Find possible "self pointer".. pointer/smartpointer member variable of "self" type.
3376  if (var.valueType() && var.valueType()->smartPointerType != classScope->definedType && var.valueType()->typeScope != classScope) {
3377  const ValueType valueType = ValueType::parseDecl(var.typeStartToken(), *mSettings);
3378  if (valueType.smartPointerType != classScope->definedType)
3379  continue;
3380  }
3381 
3382  // If variable is not static, check that "this" is assigned
3383  if (!var.isStatic()) {
3384  bool hasAssign = false;
3385  for (const Function &func : classScope->functionList) {
3386  if (func.type != Function::Type::eFunction || !func.hasBody())
3387  continue;
3388  for (const Token *tok = func.functionScope->bodyStart; tok != func.functionScope->bodyEnd; tok = tok->next()) {
3389  if (Token::Match(tok, "%varid% = this|shared_from_this", var.declarationId())) {
3390  hasAssign = true;
3391  break;
3392  }
3393  }
3394  if (hasAssign)
3395  break;
3396  }
3397  if (!hasAssign)
3398  continue;
3399  }
3400 
3401  // Check usage of self pointer..
3402  for (const Function &func : classScope->functionList) {
3403  if (func.type != Function::Type::eFunction || !func.hasBody())
3404  continue;
3405 
3406  const Token * freeToken = nullptr;
3407  std::set<const Function *> callstack;
3408  checkThisUseAfterFreeRecursive(classScope, &func, &var, std::move(callstack), freeToken);
3409  }
3410  }
3411  }
3412 }
3413 
3414 bool CheckClass::checkThisUseAfterFreeRecursive(const Scope *classScope, const Function *func, const Variable *selfPointer, std::set<const Function *> callstack, const Token *&freeToken)
3415 {
3416  if (!func || !func->functionScope)
3417  return false;
3418 
3419  // avoid recursion
3420  if (callstack.count(func))
3421  return false;
3422  callstack.insert(func);
3423 
3424  const Token * const bodyStart = func->functionScope->bodyStart;
3425  const Token * const bodyEnd = func->functionScope->bodyEnd;
3426  for (const Token *tok = bodyStart; tok != bodyEnd; tok = tok->next()) {
3427  const bool isDestroyed = freeToken != nullptr && !func->isStatic();
3428  if (Token::Match(tok, "delete %var% ;") && selfPointer == tok->next()->variable()) {
3429  freeToken = tok;
3430  tok = tok->tokAt(2);
3431  } else if (Token::Match(tok, "%var% . reset ( )") && selfPointer == tok->variable())
3432  freeToken = tok;
3433  else if (Token::Match(tok->previous(), "!!. %name% (") && tok->function() && tok->function()->nestedIn == classScope) {
3434  if (isDestroyed) {
3435  thisUseAfterFree(selfPointer->nameToken(), freeToken, tok);
3436  return true;
3437  }
3438  if (checkThisUseAfterFreeRecursive(classScope, tok->function(), selfPointer, callstack, freeToken))
3439  return true;
3440  } else if (isDestroyed && Token::Match(tok->previous(), "!!. %name%") && tok->variable() && tok->variable()->scope() == classScope && !tok->variable()->isStatic() && !tok->variable()->isArgument()) {
3441  thisUseAfterFree(selfPointer->nameToken(), freeToken, tok);
3442  return true;
3443  } else if (freeToken && Token::Match(tok, "return|throw")) {
3444  // TODO
3445  return tok->str() == "throw";
3446  } else if (tok->str() == "{" && tok->scope()->type == Scope::ScopeType::eLambda) {
3447  tok = tok->link();
3448  }
3449  }
3450  return false;
3451 }
3452 
3453 void CheckClass::thisUseAfterFree(const Token *self, const Token *free, const Token *use)
3454 {
3455  std::string selfPointer = self ? self->str() : "ptr";
3456  const ErrorPath errorPath = { ErrorPathItem(self, "Assuming '" + selfPointer + "' is used as 'this'"), ErrorPathItem(free, "Delete '" + selfPointer + "', invalidating 'this'"), ErrorPathItem(use, "Call method when 'this' is invalid") };
3457  const std::string usestr = use ? use->str() : "x";
3458  const std::string usemsg = use && use->function() ? ("Calling method '" + usestr + "()'") : ("Using member '" + usestr + "'");
3459  reportError(errorPath, Severity::warning, "thisUseAfterFree",
3460  "$symbol:" + selfPointer + "\n" +
3461  usemsg + " when 'this' might be invalid",
3462  CWE(0), Certainty::normal);
3463 }
3464 
3466 {
3468  return;
3469  logChecker("CheckClass::checkUnsafeClassRefMember"); // warning,safeChecks
3470  for (const Scope * classScope : mSymbolDatabase->classAndStructScopes) {
3471  for (const Function &func : classScope->functionList) {
3472  if (!func.hasBody() || !func.isConstructor())
3473  continue;
3474 
3475  const Token *initList = func.constructorMemberInitialization();
3476  while (Token::Match(initList, "[:,] %name% (")) {
3477  if (Token::Match(initList->tokAt(2), "( %var% )")) {
3478  const Variable * const memberVar = initList->next()->variable();
3479  const Variable * const argVar = initList->tokAt(3)->variable();
3480  if (memberVar && argVar && memberVar->isConst() && memberVar->isReference() && argVar->isArgument() && argVar->isConst() && argVar->isReference())
3481  unsafeClassRefMemberError(initList->next(), classScope->className + "::" + memberVar->name());
3482  }
3483  initList = initList->linkAt(2)->next();
3484  }
3485  }
3486  }
3487 }
3488 
3489 void CheckClass::unsafeClassRefMemberError(const Token *tok, const std::string &varname)
3490 {
3491  reportError(tok, Severity::warning, "unsafeClassRefMember",
3492  "$symbol:" + varname + "\n"
3493  "Unsafe class: The const reference member '$symbol' is initialized by a const reference constructor argument. You need to be careful about lifetime issues.\n"
3494  "Unsafe class checking: The const reference member '$symbol' is initialized by a const reference constructor argument. You need to be careful about lifetime issues. If you pass a local variable or temporary value in this constructor argument, be extra careful. If the argument is always some global object that is never destroyed then this is safe usage. However it would be defensive to make the member '$symbol' a non-reference variable or a smart pointer.",
3495  CWE(0), Certainty::normal);
3496 }
3497 
3498 // a Clang-built executable will crash when using the anonymous MyFileInfo later on - so put it in a unique namespace for now
3499 // see https://trac.cppcheck.net/ticket/12108 for more details
3500 #ifdef __clang__
3501 inline namespace CheckClass_internal
3502 #else
3503 namespace
3504 #endif
3505 {
3506  /* multifile checking; one definition rule violations */
3507  class MyFileInfo : public Check::FileInfo {
3508  public:
3509  struct NameLoc {
3510  std::string className;
3511  std::string fileName;
3512  int lineNumber;
3513  int column;
3514  std::size_t hash;
3515 
3516  bool isSameLocation(const NameLoc& other) const {
3517  return fileName == other.fileName &&
3518  lineNumber == other.lineNumber &&
3519  column == other.column;
3520  }
3521  };
3522  std::vector<NameLoc> classDefinitions;
3523 
3524  /** Convert data into xml string */
3525  std::string toString() const override
3526  {
3527  std::string ret;
3528  for (const NameLoc &nameLoc: classDefinitions) {
3529  ret += "<class name=\"" + ErrorLogger::toxml(nameLoc.className) +
3530  "\" file=\"" + ErrorLogger::toxml(nameLoc.fileName) +
3531  "\" line=\"" + std::to_string(nameLoc.lineNumber) +
3532  "\" col=\"" + std::to_string(nameLoc.column) +
3533  "\" hash=\"" + std::to_string(nameLoc.hash) +
3534  "\"/>\n";
3535  }
3536  return ret;
3537  }
3538  };
3539 }
3540 
3541 Check::FileInfo *CheckClass::getFileInfo(const Tokenizer &tokenizer, const Settings& /*settings*/) const
3542 {
3543  if (!tokenizer.isCPP())
3544  return nullptr;
3545 
3546  // One definition rule
3547  std::vector<MyFileInfo::NameLoc> classDefinitions;
3548  for (const Scope * classScope : tokenizer.getSymbolDatabase()->classAndStructScopes) {
3549  if (classScope->isAnonymous())
3550  continue;
3551 
3552  if (classScope->classDef && Token::simpleMatch(classScope->classDef->previous(), ">"))
3553  continue;
3554 
3555  // the full definition must be compared
3556  const bool fullDefinition = std::all_of(classScope->functionList.cbegin(),
3557  classScope->functionList.cend(),
3558  [](const Function& f) {
3559  return f.hasBody();
3560  });
3561  if (!fullDefinition)
3562  continue;
3563 
3564  std::string name;
3565  const Scope *scope = classScope;
3566  while (scope->isClassOrStruct() && !classScope->className.empty()) {
3567  if (Token::Match(scope->classDef, "struct|class %name% :: %name%")) {
3568  // TODO handle such classnames
3569  name.clear();
3570  break;
3571  }
3572  name = scope->className + "::" + name;
3573  scope = scope->nestedIn;
3574  }
3575  if (name.empty())
3576  continue;
3577  name.erase(name.size() - 2);
3578  if (scope->type != Scope::ScopeType::eGlobal)
3579  continue;
3580 
3581  MyFileInfo::NameLoc nameLoc;
3582  nameLoc.className = std::move(name);
3583  nameLoc.fileName = tokenizer.list.file(classScope->classDef);
3584  nameLoc.lineNumber = classScope->classDef->linenr();
3585  nameLoc.column = classScope->classDef->column();
3586 
3587  // Calculate hash from the full class/struct definition
3588  std::string def;
3589  for (const Token *tok = classScope->classDef; tok != classScope->bodyEnd; tok = tok->next())
3590  def += tok->str();
3591  for (const Function &f: classScope->functionList) {
3592  if (f.functionScope && f.functionScope->nestedIn != classScope) {
3593  for (const Token *tok = f.functionScope->bodyStart; tok != f.functionScope->bodyEnd; tok = tok->next())
3594  def += tok->str();
3595  }
3596  }
3597  nameLoc.hash = std::hash<std::string> {}(def);
3598 
3599  classDefinitions.push_back(std::move(nameLoc));
3600  }
3601 
3602  if (classDefinitions.empty())
3603  return nullptr;
3604 
3605  auto *fileInfo = new MyFileInfo;
3606  fileInfo->classDefinitions.swap(classDefinitions);
3607  return fileInfo;
3608 }
3609 
3610 Check::FileInfo * CheckClass::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const
3611 {
3612  auto *fileInfo = new MyFileInfo;
3613  for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) {
3614  if (std::strcmp(e->Name(), "class") != 0)
3615  continue;
3616  const char *name = e->Attribute("name");
3617  const char *file = e->Attribute("file");
3618  const char *line = e->Attribute("line");
3619  const char *col = e->Attribute("col");
3620  const char *hash = e->Attribute("hash");
3621  if (name && file && line && col && hash) {
3622  MyFileInfo::NameLoc nameLoc;
3623  nameLoc.className = name;
3624  nameLoc.fileName = file;
3625  nameLoc.lineNumber = strToInt<int>(line);
3626  nameLoc.column = strToInt<int>(col);
3627  nameLoc.hash = strToInt<std::size_t>(hash);
3628  fileInfo->classDefinitions.push_back(std::move(nameLoc));
3629  }
3630  }
3631  if (fileInfo->classDefinitions.empty()) {
3632  delete fileInfo;
3633  fileInfo = nullptr;
3634  }
3635  return fileInfo;
3636 }
3637 
3638 bool CheckClass::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list<Check::FileInfo*> &fileInfo, const Settings& settings, ErrorLogger &errorLogger)
3639 {
3640  bool foundErrors = false;
3641  (void)ctu; // This argument is unused
3642  (void)settings; // This argument is unused
3643 
3644  std::unordered_map<std::string, MyFileInfo::NameLoc> all;
3645 
3646  CheckClass dummy(nullptr, &settings, &errorLogger);
3647  dummy.
3648  logChecker("CheckClass::analyseWholeProgram");
3649 
3650  for (const Check::FileInfo* fi1 : fileInfo) {
3651  const MyFileInfo *fi = dynamic_cast<const MyFileInfo*>(fi1);
3652  if (!fi)
3653  continue;
3654  for (const MyFileInfo::NameLoc &nameLoc : fi->classDefinitions) {
3655  auto it = all.find(nameLoc.className);
3656  if (it == all.end()) {
3657  all[nameLoc.className] = nameLoc;
3658  continue;
3659  }
3660  if (it->second.hash == nameLoc.hash)
3661  continue;
3662  // Same location, sometimes the hash is different wrongly (possibly because of different token simplifications).
3663  if (it->second.isSameLocation(nameLoc))
3664  continue;
3665 
3666  std::list<ErrorMessage::FileLocation> locationList;
3667  locationList.emplace_back(nameLoc.fileName, nameLoc.lineNumber, nameLoc.column);
3668  locationList.emplace_back(it->second.fileName, it->second.lineNumber, it->second.column);
3669 
3670  const ErrorMessage errmsg(std::move(locationList),
3671  emptyString,
3673  "$symbol:" + nameLoc.className +
3674  "\nThe one definition rule is violated, different classes/structs have the same name '$symbol'",
3675  "ctuOneDefinitionRuleViolation",
3678  errorLogger.reportErr(errmsg);
3679 
3680  foundErrors = true;
3681  }
3682  }
3683  return foundErrors;
3684 }
3685 
3686 
std::vector< const Token * > getArguments(const Token *ftok)
Get arguments (AST)
Definition: astutils.cpp:3078
bool astIsRangeBasedForDecl(const Token *tok)
Is given token a range-declaration in a range-based for loop.
Definition: astutils.cpp:321
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 * findLambdaEndToken(const Token *first)
find lambda function end token
Definition: astutils.cpp:3190
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
const Token * previousBeforeAstLeftmostLeaf(const Token *tok)
Definition: astutils.cpp:514
void visitAstNodes(T *ast, const TFunc &visitor)
Visit AST nodes recursively.
Definition: astutils.h:54
bool isEqual(T x, T y)
Definition: calculate.h:28
#define WRONG_DATA(COND, TOK)
Use WRONG_DATA in checkers to mark conditions that check that data is correct.
Definition: check.h:50
static bool isVclTypeInit(const Type *type)
Definition: checkclass.cpp:105
static std::vector< DuplMemberFuncInfo > getDuplInheritedMemberFunctionsRecursive(const Type *typeCurrent, const Type *typeBase, bool skipPrivate=true)
static const Token * getFuncTokFromThis(const Token *tok)
static const CWE CWE398(398U)
static const CWE CWE762(762U)
static const CWE CWE_ONE_DEFINITION_RULE(758U)
static std::vector< DuplMemberInfo > getDuplInheritedMembersRecursive(const Type *typeCurrent, const Type *typeBase, bool skipPrivate=true)
CtorType
static const Token * getSingleFunctionCall(const Scope *scope)
static const CWE CWE758(758U)
static const CWE CWE665(665U)
static bool isTrueKeyword(const Token *tok)
static std::string noMemberErrorMessage(const Scope *scope, const char function[], bool isdefault)
Definition: checkclass.cpp:546
static const Variable * getSingleReturnVar(const Scope *scope)
static bool checkFunctionUsage(const Function *privfunc, const Scope *scope)
static const Scope * findFunctionOf(const Scope *scope)
static bool isVariableCopyNeeded(const Variable &var, Function::Type type)
Definition: checkclass.cpp:85
static const CWE CWE404(404U)
static bool hasNonCopyableBase(const Scope *scope, bool *unknown)
Definition: checkclass.cpp:384
static const char * getFunctionTypeName(Function::Type type)
Definition: checkclass.cpp:64
static bool compareTokenRanges(const Token *start1, const Token *end1, const Token *start2, const Token *end2)
static void getAllVariableMembers(const Scope *scope, std::vector< const Variable * > &varList)
Definition: checkclass.cpp:634
static bool isFalseKeyword(const Token *tok)
Check classes.
Definition: checkclass.h:52
static bool isConstMemberFunc(const Scope *scope, const Token *tok)
bool checkThisUseAfterFreeRecursive(const Scope *classScope, const Function *func, const Variable *selfPointer, std::set< const Function * > callstack, const Token *&freeToken)
Helper for checkThisUseAfterFree.
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.
void operatorEqMissingReturnStatementError(const Token *tok, bool error)
static void getFirstVirtualFunctionCallStack(std::map< const Function *, std::list< const Token * >> &virtualFunctionCallsMap, const Token *callToken, std::list< const Token * > &pureFuncStack)
looks for the first virtual function call stack
bool checkConstFunc(const Scope *scope, const Function *func, MemberAccess &memberAccessed) const
void overrideError(const Function *funcInBase, const Function *funcInDerived)
const SymbolDatabase * mSymbolDatabase
Definition: checkclass.h:178
Check::FileInfo * getFileInfo(const Tokenizer &tokenizer, const Settings &settings) const override
Parse current TU and extract file info.
bool hasAllocation(const Function *func, const Scope *scope) const
void checkCopyCtorAndEqOperator()
Check that copy constructor and operator defined together.
void checkOverride()
Check that the override keyword is used when overriding virtual functions.
void uninitVarError(const Token *tok, bool isprivate, Function::Type functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive)
void thisSubtraction()
warn for "this-x".
void checkThisUseAfterFree()
When "self pointer" is destroyed, 'this' might become invalid.
void initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname, const std::string &argname={})
void checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last)
void noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct)
void operatorEqRetRefThis()
'operator=' should return reference to *this
void operatorEqToSelfError(const Token *tok)
void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive)
void duplInheritedMembersError(const Token *tok1, const Token *tok2, const std::string &derivedName, const std::string &baseName, const std::string &memberName, bool derivedIsStruct, bool baseIsStruct, bool isFunction=false)
void uselessOverrideError(const Function *funcInBase, const Function *funcInDerived, bool isSameCode=false)
static void initVar(std::vector< Usage > &usageList, nonneg int varid)
initialize a variable in the varlist
Definition: checkclass.cpp:685
static bool canNotCopy(const Scope *scope)
Definition: checkclass.cpp:581
void mallocOnClassWarning(const Token *tok, const std::string &memfunc, const Token *classTok)
void copyConstructorShallowCopyError(const Token *tok, const std::string &varname)
Definition: checkclass.cpp:540
void unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname)
void checkUnsafeClassRefMember()
Unsafe class check - const reference member.
void virtualFunctionCallInConstructorError(const Function *scopeFunction, const std::list< const Token * > &tokStack, const std::string &funcname)
CheckClass()
This constructor is used when registering the CheckClass.
Definition: checkclass.h:59
static bool hasAssignSelf(const Function *func, const Token *rhs, const Token *&out_ifStatementScopeStart)
void thisSubtractionError(const Token *tok)
void checkReturnByReference()
Check that large members are returned by reference from getter function.
void checkDuplInheritedMembers()
Check duplicated inherited members.
void operatorEqToSelf()
'operator=' should check for assignment to self
static void assignAllVar(std::vector< Usage > &usageList)
set all variables in list assigned
Definition: checkclass.cpp:694
static bool isBaseClassMutableMemberFunc(const Token *tok, const Scope *scope)
Definition: checkclass.cpp:724
void selfInitializationError(const Token *tok, const std::string &varname)
bool isMemberVar(const Scope *scope, const Token *tok) const
void operatorEqShouldBeLeftUnimplementedError(const Token *tok)
const std::list< const Token * > & getVirtualFunctionCalls(const Function &function, std::map< const Function *, std::list< const Token * >> &virtualFunctionCallsMap)
gives a list of tokens where virtual functions are called directly or indirectly
void mallocOnClassError(const Token *tok, const std::string &memfunc, const Token *classTok, const std::string &classname)
static bool canNotMove(const Scope *scope)
Definition: checkclass.cpp:605
void noConstructorError(const Token *tok, const std::string &classname, bool isStruct)
void privateFunctions()
Check that all private functions are called
void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive)
void checkUselessOverride()
Check that the overriden function is not identical to the base function.
static void clearAllVar(std::vector< Usage > &usageList)
set all variables in list not assigned and not initialized
Definition: checkclass.cpp:716
static bool isMemberFunc(const Scope *scope, const Token *tok)
void initializerListOrder()
Check initializer list order.
void initializationListUsage()
Suggest using initialization list.
static void assignAllVarsVisibleFromScope(std::vector< Usage > &usageList, const Scope *scope)
set all variable in list assigned, if visible from given scope
Definition: checkclass.cpp:700
void checkDuplInheritedMembersRecursive(const Type *typeCurrent, const Type *typeBase)
void initializeVarList(const Function &func, std::list< const Function * > &callstack, const Scope *scope, std::vector< Usage > &usage) const
parse a scope for a constructor or member function and set the "init" flags in the provided varlist
Definition: checkclass.cpp:751
void checkMemset()
Check that the memsets are valid.
void noDestructorError(const Scope *scope, bool isdefault, const Token *alloc)
Definition: checkclass.cpp:576
void missingMemberCopyError(const Token *tok, Function::Type functionType, const std::string &classname, const std::string &varname)
void checkVirtualFunctionCallInConstructor()
call of virtual function in constructor/destructor
static std::vector< Usage > createUsageList(const Scope *scope)
Create usage list that contains all scope members and also members of base classes without constructo...
Definition: checkclass.cpp:650
void memsetErrorFloat(const Token *tok, const std::string &type)
void noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive)
Definition: checkclass.cpp:566
void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic)
void constructors()
Check that all class constructors are ok
Definition: checkclass.cpp:128
void checkSelfInitialization()
Check for initialization of a member with itself.
static void assignVar(std::vector< Usage > &usageList, nonneg int varid)
assign a variable in the varlist
Definition: checkclass.cpp:662
void checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic)
static Bool isInverted(const Token *tok, const Token *rhs)
void operatorEqRetRefThisError(const Token *tok)
void checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::set< const Scope * > parsedTypes)
static const Token * getIfStmtBodyStart(const Token *tok, const Token *rhs)
void noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive)
Definition: checkclass.cpp:571
static const std::set< std::string > stl_containers_not_const
Set of the STL types whose operator[] is not const.
Definition: checkclass.h:62
Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const override
void virtualDestructor()
The destructor in a base class should be virtual.
void copyconstructors()
Definition: checkclass.cpp:408
void suggestInitializationList(const Token *tok, const std::string &varname)
void returnByReferenceError(const Function *func, const Variable *var)
void memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type)
void pureVirtualFunctionCallInConstructorError(const Function *scopeFunction, const std::list< const Token * > &tokStack, const std::string &purefuncname)
void unsafeClassRefMemberError(const Token *tok, const std::string &varname)
void memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type, bool isContainer=false)
void checkConst()
can member function be const?
void thisUseAfterFree(const Token *self, const Token *free, const Token *use)
void checkExplicitConstructors()
Check that constructors with single parameter are explicit, if they has to be.
Definition: checkclass.cpp:339
void copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor)
bool hasAllocationInIfScope(const Function *func, const Scope *scope, const Token *ifStatementScopeStart) const
Base class used for whole-program analysis.
Definition: check.h:103
Interface class that cppcheck uses to communicate with the checks.
Definition: check.h:59
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 std::string & name() const
class name, used to generate documentation
Definition: check.h:88
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.
static std::string toxml(const std::string &str)
Convert XML-sensitive characters into XML entities.
Wrapper for error messages, provided by reportErr()
Definition: errorlogger.h:48
bool isStatic() const
AccessControl access
public/protected/private
nonneg int minArgCount() const
bool hasFinalSpecifier() const
const std::string & name() const
const Token * argDef
function argument start '(' in class definition
bool isPure() const
const Scope * functionScope
scope of function body
const Function * getOverriddenFunction(bool *foundAllBaseClasses=nullptr) const
get function in base class that is overridden
static bool returnsConst(const Function *function, bool unknown=false)
bool isInline() const
bool hasBody() const
Type type
constructor, destructor, ...
bool isOperator() const
static bool returnsPointer(const Function *function, bool unknown=false)
static bool returnsReference(const Function *function, bool unknown=false, bool includeRValueRef=false)
bool isExplicit() const
bool argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length) const
const Variable * getArgumentVar(nonneg int num) const
nonneg int argCount() const
const Token * retDef
function return type token
bool isImplicitlyVirtual(bool defaultVal=false) const
check if this function is virtual in the base classes
static bool returnsStandardType(const Function *function, bool unknown=false)
bool isDefault() const
bool isVariadic() const
const Token * constructorMemberInitialization() const
const Token * token
function name token in implementation
const ::Type * retType
function return type
const Token * arg
function argument start '('
const Scope * nestedIn
Scope the function is declared in.
bool hasOverrideSpecifier() const
bool hasVirtualSpecifier() const
const Token * tokenDef
function name token in class definition
bool isDelete() const
const Token * templateDef
points to 'template <' before function
bool isFriend() const
std::list< Variable > argumentList
argument list, must remain list due to clangimport usage!
bool isDestructor() const
bool isConstructor() const
bool isConst() const
Yield getYield(const std::string &function) const
Definition: library.h:255
const Container * detectContainerOrIterator(const Token *typeStart, bool *isIterator=nullptr, bool withoutStd=false) const
Definition: library.cpp:1241
bool isSmartPointer(const Token *tok) const
Definition: library.cpp:1697
const AllocFunc * getAllocFuncInfo(const Token *tok) const
get allocation info for function
Definition: library.cpp:1077
bool isScopeNoReturn(const Token *end, std::string *unknownFunc) const
Definition: library.cpp:1141
TypeCheck getTypeCheck(std::string check, std::string typeName) const
Definition: library.cpp:1735
const AllocFunc * getReallocFuncInfo(const Token *tok) const
get reallocation info for function
Definition: library.cpp:1095
const PodType * podtype(const std::string &name) const
Definition: library.h:447
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
bool isFunctionConst(const std::string &functionName, bool pure) const
Definition: library.cpp:1518
bool ignorefunction(const std::string &functionName) const
Definition: library.cpp:1497
std::size_t sizeof_pointer
Definition: platform.h:102
std::list< Function > functionList
std::map< std::string, Type * > definedTypesMap
std::list< Variable > varlist
nonneg int numCopyOrMoveConstructors
std::multimap< std::string, const Function * > functionMap
std::vector< Scope * > nestedList
ScopeType type
const Function * getDestructor() const
Type * definedType
Function * function
function info for this function
const Function * findFunction(const Token *tok, bool requireConst=false) const
find a function
const Scope * nestedIn
const Token * classDef
class/struct/union/namespace token
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
nonneg int numConstructors
std::string className
const Scope * functionOf
scope this function belongs to
const Variable * getVariable(const std::string &varname) const
get variable from name
bool isClassOrStruct() 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 hasLib(const std::string &lib) const
Is library specified?
Definition: settings.h:439
Library library
Library.
Definition: settings.h:237
SafeChecks safeChecks
Definition: settings.h:356
Platform platform
Definition: settings.h:255
bool isPremiumEnabled(const char id[]) const
Is checker id enabled by premiumArgs.
Definition: settings.cpp:557
SimpleEnableGroup< Certainty > certainty
Definition: settings.h:359
SimpleEnableGroup< Severity > severity
Definition: settings.h:358
Standards standards
Struct contains standards settings.
Definition: settings.h:366
bool isEnabled(T flag) const
Definition: settings.h:66
const std::vector< const Variable * > & variableList() const
void debugMessage(const Token *tok, const std::string &type, const std::string &msg) const
output a debug message
std::list< Type > typeList
Fast access to types.
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.
const std::string & file(const Token *tok) const
get filename for given token
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
Token * astTop()
Definition: token.h:1416
static bool Match(const Token *tok, const char pattern[], nonneg int varid=0)
Match given token (or list of tokens) to a pattern list.
Definition: token.cpp:722
bool isKeyword() const
Definition: token.h:358
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
MathLib::bigint getKnownIntValue() const
Definition: token.h:1218
bool isExpandedMacro() const
Definition: token.h:455
bool isUpperCaseName() const
Definition: token.cpp:237
bool isCpp() const
Definition: token.cpp:2752
bool isFinalType() const
Definition: token.h:671
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
std::pair< const Token *, const Token * > findExpressionStartEndTokens() const
Definition: token.cpp:1548
nonneg int varId() const
Definition: token.h:870
bool isUnaryOp(const std::string &s) const
Definition: token.h:413
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
@ eName
Definition: token.h:161
@ eIncDecOp
Definition: token.h:163
Token * previous()
Definition: token.h:862
void type(const ::Type *t)
Associate this token with given type.
Definition: token.cpp:2367
bool isAssignmentOp() const
Definition: token.h:401
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
const Token * nextArgument() const
Definition: token.cpp:903
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
const Token * tokens() const
Definition: tokenize.h:592
TokenList list
Token list: stores all tokens.
Definition: tokenize.h:590
const SymbolDatabase * getSymbolDatabase() const
Definition: tokenize.h:563
bool isCPP() const
Is the code CPP.
Definition: tokenize.h:69
bool hasIfdef(const Token *start, const Token *end) const
Information about a class type.
const Scope * enclosingScope
const Token * classDef
Points to "class" token.
std::vector< BaseInfo > derivedFrom
std::string name() const
std::vector< FriendInfo > friendList
enum Type::NeedInitialization needInitialization
const Scope * classScope
Value type.
enum ValueType::Type type
const Library::Container * container
If the type is a container defined in a cfg file, this is the used.
bool isConst(nonneg int indirect=0) const
nonneg int pointer
0=>not pointer, 1=>*, 2=>**, 3=>***, etc
const Token * containerTypeToken
The container type token.
const Scope * typeScope
if the type definition is seen this point out the type scope
static ValueType parseDecl(const Token *type, const Settings &settings)
const ::Type * smartPointerType
Smart pointer type.
Information about a member variable.
bool hasDefault() const
Does variable have a default value.
bool isArgument() const
Is variable a function argument.
bool isEnumType() const
Determine whether it's an enumeration type.
bool isClass() const
Is variable a user defined (or unknown) type.
bool isArrayOrPointer() const
Is array or pointer variable.
const Type * smartPointerType() const
bool isReference() const
Is reference variable.
bool isFloatingType() const
Determine whether it's a floating number type.
std::string getTypeName() const
bool isStlType() const
Checks if the variable is an STL type ('std::') E.g.
bool isSmartPointer() const
const Type * type() const
Get Type pointer of known type.
const Scope * scope() const
Get Scope pointer of enclosing scope.
const Scope * typeScope() const
Get Scope pointer of known type.
const std::string & name() const
Get name string.
const Token * typeEndToken() const
Get type end token.
bool isConst() const
Is variable const.
bool isPointerArray() const
Is variable an array of pointers.
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).
bool isMutable() const
Is variable mutable.
const Token * typeStartToken() const
Get type start token.
bool isInit() const
Is variable initialized in its declaration.
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
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::pair< const Token *, std::string > ErrorPathItem
Definition: errortypes.h:129
std::list< ErrorPathItem > ErrorPath
Definition: errortypes.h:130
@ warning
Warning.
@ portability
Portability warning.
@ style
Style warning.
@ performance
Performance warning.
@ error
Programming error.
Whole program analysis (ctu=Cross Translation Unit)
Definition: check.h:35
size_t getSizeOf(const ValueType &vt, const Settings &settings, int maxRecursion=0)
Definition: valueflow.cpp:1197
static constexpr char CWE[]
Definition: resultstree.cpp:67
Information about a member variable.
Definition: checkclass.h:315
BufferSize bufferSize
Definition: library.h:85
bool classes
Public interface of classes.
Definition: settings.h:334
enum Standards::cppstd_t cpp
AccessControl access
const Type * type
const Token * isLambdaCaptureList(const Token *tok)
bool endsWith(const std::string &str, char c)
Definition: utils.h:110
#define bailout(tokenlist, errorLogger, tok, what)
Definition: valueflow.cpp:144