41 #include <unordered_map>
70 return "copy constructor";
72 return "move constructor";
87 bool isOpEqual =
false;
112 if (isVclTypeInit(baseInfo.type))
120 :
Check(myName(), tokenizer, settings, errorLogger),
121 mSymbolDatabase(tokenizer?tokenizer->getSymbolDatabase():nullptr)
145 if (unionScope.type != Scope::eUnion)
147 return std::any_of(unionScope.varlist.cbegin(), unionScope.varlist.cend(), [&](const Variable& var) {
148 return var.type() && var.type()->classScope == scope;
155 int needInit = 0, haveInit = 0;
156 std::vector<const Variable*> uninitVars;
162 uninitVars.emplace_back(&var);
167 if (needInit > haveInit) {
171 for (
const Variable* uv : uninitVars)
172 uninitVarError(uv->typeStartToken(), uv->scope()->className, uv->name());
183 return nestedScope->type == Scope::eUnion;
208 for (
Usage &usage : usageList) {
217 std::list<const Function *> callstack;
221 for (
const Usage &usage : usageList) {
223 if (!usage.assign && !usage.init)
226 while (varScope1->
type == Scope::ScopeType::eStruct)
228 if (varScope1->
type == Scope::ScopeType::eUnion) {
229 for (
Usage &usage2 : usageList) {
231 if (usage2.assign || usage2.init || var2.
isStatic())
234 while (varScope2->
type == Scope::ScopeType::eStruct)
236 if (varScope1 == varScope2)
237 usage2.assign =
true;
243 for (
const Usage &usage : usageList) {
246 if (usage.assign || usage.init || var.
isStatic())
288 bool missingCopy =
false;
293 if (!printInconclusive)
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;
319 const bool derived = scope != var.
scope();
326 if (printInconclusive)
328 }
else if (missingCopy)
344 logChecker(
"CheckClass::checkExplicitConstructors");
354 return func.isPure();
377 func.
argumentList.front().getTypeName() !=
"std::initializer_list") {
413 logChecker(
"CheckClass::checkCopyConstructors");
416 std::map<int, const Token*> allocatedVars;
427 allocatedVars[tok->
varId()] = tok;
435 allocatedVars[tok->
varId()] = tok;
440 if (!allocatedVars.empty()) {
441 const Function *funcCopyCtor =
nullptr;
442 const Function *funcOperatorEq =
nullptr;
443 const Function *funcDestructor =
nullptr;
446 funcCopyCtor = &func;
448 funcOperatorEq = &func;
450 funcDestructor = &func;
452 if (!funcCopyCtor || funcCopyCtor->
isDefault()) {
453 bool unknown =
false;
457 if (!funcOperatorEq || funcOperatorEq->
isDefault()) {
458 bool unknown =
false;
460 noOperatorEqError(scope, funcOperatorEq, allocatedVars.cbegin()->second, unknown);
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;
469 if (it->second->valueType() && it->second->valueType()->isIntegral()) {
470 mustDealloc = it->second;
473 const Variable *var = it->second->variable();
475 mustDealloc = it->second;
484 std::set<const Token*> copiedVars;
485 const Token* copyCtor =
nullptr;
491 allocatedVars.clear();
495 if (tok->
str()==
":") {
498 if (allocatedVars.find(tok->
varId()) != allocatedVars.end()) {
500 copiedVars.insert(tok);
502 allocatedVars.erase(tok->
varId());
510 allocatedVars.erase(tok->
varId());
511 }
else if (
Token::Match(tok,
"%var% = %name% . %name% ;") && allocatedVars.find(tok->
varId()) != allocatedVars.end()) {
512 copiedVars.insert(tok);
517 if (copyCtor && !copiedVars.empty()) {
518 for (
const Token *cv : copiedVars)
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);
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';
554 errmsg += type +
" '$symbol' has dynamic memory/resource allocation(s). The " +
function +
" is explicitly defaulted but the default " +
function +
" does not work well.";
556 errmsg +=
" It is recommended to define the " + std::string(
function) +
'.';
558 errmsg +=
" It is recommended to define or delete the " + std::string(
function) +
'.';
560 errmsg += type +
" '$symbol' does not have a " +
function +
" which is recommended since it has dynamic memory/resource allocation(s).";
583 bool constructor =
false;
584 bool publicAssign =
false;
585 bool publicCopy =
false;
602 return constructor && !(publicAssign || publicCopy);
607 bool constructor =
false;
608 bool publicAssign =
false;
609 bool publicCopy =
false;
610 bool publicMove =
false;
631 return constructor && !(publicAssign || publicCopy || publicMove);
636 std::transform(scope->
varlist.cbegin(), scope->
varlist.cend(), std::back_inserter(varList), [](
const Variable& var) {
652 std::vector<Usage> ret;
653 std::vector<const Variable *> varlist;
655 ret.reserve(varlist.size());
656 std::transform(varlist.cbegin(), varlist.cend(), std::back_inserter(ret), [](
const Variable* var) {
664 auto it = std::find_if(usageList.begin(), usageList.end(), [varid](
const Usage& usage) {
665 return usage.var->declarationId() == varid;
667 if (it != usageList.end())
673 if (vartok->
varId() > 0) {
677 auto it = std::find_if(usageList.begin(), usageList.end(), [vartok](
const Usage& usage) {
679 return usage.var->name() == vartok->str();
681 if (it != usageList.end())
687 auto it = std::find_if(usageList.begin(), usageList.end(), [varid](
const Usage& usage) {
688 return usage.var->declarationId() == varid;
690 if (it != usageList.end())
696 for (
Usage & i : usageList)
702 for (
Usage& usage : usageList) {
703 if (usage.var->scope() == scope)
718 for (
Usage & i : usageList) {
734 if (std::any_of(functionList.cbegin(), functionList.cend(), [&](
const Function& func) {
735 return func.tokenDef->str() == tok->str() && !func.isStatic() && !func.isConst();
764 if (ftok->
str() != func.
name()) {
768 for (
Usage& u : usage) {
769 if (u.var->scope() != scope)
783 if (std::find(callstack.cbegin(), callstack.cend(), member) != callstack.cend()) {
792 callstack.push_back(member);
794 callstack.pop_back();
802 }
else if (level != 0 &&
Token::Match(ftok,
"%name% ="))
808 else if (ftok->
str() ==
"{") {
848 return var.declarationId() == ftok->next()->varId();
853 ftok = ftok->
tokAt(2);
866 if (ftok->
str() ==
"return")
871 ftok = ftok->
tokAt(5);
876 ftok = ftok->
tokAt(2);
882 ftok = ftok->
tokAt(2);
891 if (
Token::Match(ftok,
"::| memset ( &| this . %name%")) {
892 if (ftok->
str() ==
"::")
894 int offsetToMember = 4;
895 if (ftok->
strAt(2) ==
"&")
904 if (ftok->
str() ==
"::")
917 if (std::find(callstack.cbegin(), callstack.cend(), member) != callstack.cend()) {
926 callstack.push_back(member);
928 callstack.pop_back();
948 if (ftok->
str() ==
"::")
953 if (tok2->str() ==
"this") {
961 !ftok->
function()->isConstructor()) {
966 if (std::find(callstack.cbegin(), callstack.cend(), member) != callstack.cend()) {
974 callstack.push_back(member);
976 callstack.pop_back();
979 for (
const Token *tok2 = ftok; tok2; tok2 = tok2->
next()) {
984 if (tok2->str() ==
"&")
999 for (
Usage& i: usage) {
1000 if (i.var->isMutable())
1023 for (
const Token *tok = ftok->
tokAt(2); tok && tok != ftok->
next()->
link(); tok = tok->next()) {
1024 if (tok->isName()) {
1037 if (tok2->
str() ==
"&") {
1038 tok2 = tok2->
next();
1047 const Token *tok2 = ftok;
1049 if (tok2->
strAt(1) ==
"[")
1052 tok2 = tok2->
tokAt(2);
1056 if (tok2 && tok2->
strAt(1) ==
"=")
1067 if (rangeVar->isReference() && !rangeVar->isConst())
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."};
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.");
1101 std::string message(
"Member variable '$symbol' is not initialized in the " + ctor +
"constructor.");
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."};
1111 const std::string message(
"Member variable '$symbol' is not initialized.");
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");
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;
1141 logChecker(
"CheckClass::initializationListUsage");
1160 if (!
Token::Match(tok,
"%var% =") || tok->strAt(-1) ==
"*" || tok->strAt(-1) ==
".")
1163 const Variable* var = tok->variable();
1176 bool localmember =
false;
1178 [&](
const Token *rhs) {
1179 if (rhs->str() ==
"." && rhs->astOperand1() && rhs->astOperand1()->variable() && rhs->astOperand1()->variable()->isLocal())
1181 return ChildrenToVisit::op1_and_op2;
1186 bool allowed =
true;
1188 [&](
const Token *tok2) {
1189 const Variable* var2 = tok2->variable();
1191 if (var2->scope() == owner && tok2->strAt(-1)!=
".") {
1193 return ChildrenToVisit::done;
1195 if (var2->isArray() && var2->isLocal()) {
1197 return ChildrenToVisit::done;
1199 }
else if (tok2->
str() ==
"this") {
1201 return ChildrenToVisit::done;
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);
1233 for (std::list<Function>::const_iterator func = scope->
functionList.cbegin(); func != scope->
functionList.cend(); ++func) {
1234 if (func->functionScope) {
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())
1239 if (ftok->str() ==
"(")
1240 ftok = ftok->link();
1243 for (
const Token *ftok = func->functionScope->classDef->
linkAt(1); ftok != func->functionScope->bodyEnd; ftok = ftok->
next()) {
1244 if (ftok->function() == privfunc)
1246 if (ftok->varId() == 0U && ftok->str() == privfunc->
name())
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;
1266 tok = tok->
tokAt(2);
1267 while (tok && tok->
str() !=
";") {
1291 std::list<const Function*> privateFuncs;
1295 privateFuncs.push_back(&func);
1301 for (std::list<const Function*>::iterator it = privateFuncs.begin(); it != privateFuncs.end();) {
1302 if ((*it)->isImplicitlyVirtual(
true))
1303 it = privateFuncs.erase(it);
1309 while (!privateFuncs.empty()) {
1310 const auto& pf = privateFuncs.front();
1311 if (pf->retDef && pf->retDef->isAttributeMaybeUnused()) {
1312 privateFuncs.pop_front();
1319 for (
int i = 0; i < friendList.size() && !used; i++) {
1320 if (friendList[i].type)
1329 privateFuncs.pop_front();
1369 const Token *typeTok =
nullptr;
1370 const Scope *type =
nullptr;
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);
1387 int numIndirToVariableType = 0;
1388 for (;; arg1 = arg1->
next()) {
1389 if (arg1->
str() ==
"&")
1390 ++numIndirToVariableType;
1391 else if (arg1->
str() ==
"*")
1392 --numIndirToVariableType;
1398 if (var && arg1->
strAt(1) ==
",") {
1402 ++numIndirToVariableType;
1408 numIndirToVariableType += int(var->
dimensions().size());
1410 if (numIndirToVariableType == 1)
1421 if (!typeTok && !type)
1424 if (typeTok && typeTok->
str() ==
"(")
1425 typeTok = typeTok->
next();
1427 if (!type && typeTok->
type())
1428 type = typeTok->
type()->classScope;
1431 const std::set<const Scope *> parsedTypes;
1434 }
else if (tok->variable() && tok->variable()->isPointer() && tok->variable()->typeScope() &&
Token::Match(tok,
"%var% = %name% (")) {
1440 const std::set<const Scope *> parsedTypes;
1441 checkMemsetType(scope, tok->tokAt(2), tok->variable()->typeScope(),
true, parsedTypes);
1443 if (printWarnings && tok->variable()->typeScope()->numConstructors > 0)
1453 if (parsedTypes.find(type) != parsedTypes.end())
1455 parsedTypes.insert(type);
1487 std::string typeName;
1489 const Token *typeTok = tok1;
1491 typeName += typeTok->
str() +
"::";
1492 typeTok = typeTok->
tokAt(2);
1494 typeName += typeTok->
str();
1506 else if (typeScope && typeScope != type)
1518 std::list<const Token *> toks = { tok, classTok };
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);
1528 std::list<const Token *> toks = { tok, classTok };
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);
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 "
1552 "$symbol:" + memfunc +
"\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);
1576 logChecker(
"CheckClass::operatorEqRetRefThis");
1579 for (std::list<Function>::const_iterator func = scope->
functionList.cbegin(); func != scope->
functionList.cend(); ++func) {
1582 if (func->retType == func->nestedIn->definedType && func->tokenDef->strAt(-1) ==
"&") {
1592 std::set<const Function*> analyzedFunctions;
1598 bool foundReturn =
false;
1600 const Token*
const startTok = tok;
1602 for (; tok && tok != last; tok = tok->
next()) {
1606 tok = lScope->
link();
1608 if (tok->
str() !=
"return")
1614 if (retExpr && retExpr->
str() ==
"=")
1619 std::string cast(
"( " + scope->
className +
" & )");
1621 tok = tok->
tokAt(4);
1624 if (tok->
strAt(2) ==
"(" &&
1627 for (std::list<Function>::const_iterator it = scope->
functionList.cbegin(); it != scope->
functionList.cend(); ++it) {
1630 it->token->str() == tok->
next()->
str()) {
1632 if (it->tokenDef->previous()->str() ==
"&" &&
1633 it->tokenDef->strAt(-2) == scope->
className) {
1635 if (!it->isConst()) {
1638 if (analyzedFunctions.find(&*it) == analyzedFunctions.end()) {
1639 analyzedFunctions.insert(&*it);
1640 checkReturnPtrThis(scope, &*it, it->arg->link()->next(), it->arg->link()->next()->link(),
1662 if (startTok->
next() == last) {
1663 const std::string tmp(
"( const " + scope->
className +
" &");
1733 while (typeTok->
str() ==
"const" || typeTok->
str() ==
"&" || typeTok->
str() ==
"*")
1742 const Token* out_ifStatementScopeStart =
nullptr;
1743 if (!
hasAssignSelf(&func, rhs, out_ifStatementScopeStart)) {
1746 }
else if (out_ifStatementScopeStart !=
nullptr) {
1759 if (ifStatementScopeStart->
str() ==
"{")
1760 end = ifStatementScopeStart->
link();
1763 return hasAllocation(func, scope, ifStatementScopeStart, end);
1775 for (
const Token *tok = start; tok && (tok != end); tok = tok->
next()) {
1776 if (((tok->isCpp() &&
Token::Match(tok,
"%var% = new")) ||
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%"))
1793 for (
const Token *tok1 = var->
next(); tok1 && (tok1 != end); tok1 = tok1->
next()) {
1820 for (
const Token *itr = tok; itr && itr->
str()!=
"("; itr=itr->astParent()) {
1854 return top->
link()->next();
1856 return top->
link()->next()->link();
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();
1881 return ChildrenToVisit::op1_and_op2;
1882 if (tok2 && tok2->isUnaryOp(
"&") && tok2->astOperand1()->str() == rhs->str())
1885 out_ifStatementScopeStart = getIfStmtBodyStart(tok2, rhs);
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 "
1918 std::list<const Function *> inconclusiveErrors;
1926 if (printInconclusive) {
1930 return func.hasVirtualSpecifier();
1932 inconclusiveErrors.push_back(destructor);
1944 if (!destructor || !destructor->
hasBody())
1953 const Token *derivedClass = derived->
next();
1961 if (!derivedFromScope)
1971 std::set<int> baseClassPointers;
1974 if (var && var->isPointer() && var->type() == derivedFrom)
1975 baseClassPointers.insert(var->declarationId());
1979 std::set<int> dontDelete;
1986 baseClassPointers.find(tok->next()->varId()) != baseClassPointers.end()) {
1988 const std::string tmp(
"new " + derivedClass->
str());
1990 dontDelete.insert(tok->next()->varId());
1996 dontDelete.find(tok->next()->varId()) != dontDelete.end()) {
2011 if (!baseDestructor) {
2029 const std::list<const Function *>::iterator found = find(inconclusiveErrors.begin(), inconclusiveErrors.end(), baseDestructor);
2030 if (found != inconclusiveErrors.end())
2031 inconclusiveErrors.erase(found);
2039 for (
const Function *func : inconclusiveErrors)
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);
2077 if (tok->
strAt(-1) !=
"*")
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() ==
"{")
2121 if (tok->str() ==
"<") {
2123 mSymbolDatabase->
debugMessage(tok,
"debug",
"CheckClass::checkConst found unlinked template argument list '" + tok->expressionString() +
"'.");
2124 inTemplArgList =
true;
2126 else if (tok->str() ==
">") {
2127 inTemplArgList =
false;
2128 isConstTemplArg =
false;
2130 else if (tok->str() ==
"const") {
2131 if (!inTemplArgList)
2133 isConstTemplArg =
true;
2141 const bool returnsPtrOrRef = isPointerOrReference(func.
retDef, func.
tokenDef);
2144 bool isTemplateArg =
false;
2146 if (tok2->isTemplateArg() && tok2->str() ==
"const") {
2147 isTemplateArg =
true;
2156 if (opName.compare(8, 5,
"const") != 0 && (
endsWith(opName,
'&') ||
endsWith(opName,
'*')))
2178 if ((returnsPtrOrRef || func.
isConst()) && !suggestStatic)
2181 std::string classname = scope->
className;
2184 classname = std::string(nest->
className +
"::" + classname);
2192 functionName +=
")";
2194 functionName +=
"]";
2208 tok = tok->
tokAt(2);
2210 tok = tok->
tokAt(2);
2222 if (tok->
str() ==
"this")
2227 tok = tok->
tokAt(-3);
2230 tok = tok->
tokAt(-2);
2235 }
else if (tok->
str() ==
"]") {
2236 tok = tok->
link()->previous();
2245 if (var.
name() == tok->
str()) {
2248 const Token* fqTok = tok;
2250 fqTok = fqTok->
tokAt(-2);
2251 if (fqTok->
strAt(-1) ==
"::")
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 +
" :: ");
2263 if (tok->
varId() == 0)
2293 if (func.
name() == tok->
str()) {
2295 int argsPassed = tok2->
str() ==
")" ? 0 : 1;
2303 if (argsPassed == func.
argCount() ||
2309 }
else if (tok->
function()->nestedIn == scope)
2310 return !tok->
function()->isStatic();
2334 if (tok->
function()->nestedIn == scope)
2345 if (derivedFrom && derivedFrom->
classScope) {
2362 auto getFuncTok = [](
const Token* tok) ->
const Token* {
2365 bool isReturn =
false;
2366 if ((
Token::Match(tok,
"%name% (|{") || (isReturn =
Token::simpleMatch(tok->astParent(),
"return {"))) && !tok->isStandardType() && !tok->isKeyword()) {
2374 auto checkFuncCall = [
this, &memberAccessed](
const Token* funcTok,
const Scope* scope,
const Function* func) {
2376 const bool isSelf = func == funcTok->
function();
2383 const std::vector<const Token*> args =
getArguments(funcTok);
2384 const auto argMax = std::min<nonneg int>(args.size(), f->argCount());
2386 for (
nonneg int argIndex = 0; argIndex < argMax; ++argIndex) {
2387 const Variable*
const argVar = f->getArgumentVar(argIndex);
2390 const Token* arg = args[argIndex];
2395 varTok = varTok->
next();
2409 lpar = lpar->
tokAt(2);
2410 for (
const Token* tok = lpar->
next(); tok && tok != funcTok->
next()->
link(); tok = tok->next()) {
2411 if (tok->str() ==
"(")
2413 else if ((tok->isName() &&
isMemberVar(scope, tok)) || (tok->isUnaryOp(
"&") && (tok = tok->astOperand1()) &&
isMemberVar(scope, tok))) {
2414 const Variable* var = tok->variable();
2427 const Variable* v = tok1->variable();
2431 if (tok1->str() ==
"this") {
2432 if (tok1->previous()->isAssignmentOp())
2434 if (
Token::Match(tok1->previous(),
"( this . * %var% )"))
2436 if (
Token::simpleMatch(tok1->astParent(),
"*") && tok1->astParent()->astParent() && tok1->astParent()->astParent()->isIncDecOp())
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))))
2447 if (lhs->
str() ==
"(" && tok1->astParent() && tok1->astParent()->astParent())
2453 if (lhs->
str() ==
"&") {
2465 if (lhs->
astParent()->strAt(1) !=
"const")
2475 const Token* jumpBackToken =
nullptr;
2476 const Token *lastVarTok = tok1;
2477 const Token *end = tok1;
2480 end = end->
tokAt(2);
2483 }
else if (end->
strAt(1) ==
"[") {
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);
2503 jumpBackToken = end->
next();
2505 }
else if (end->
strAt(1) ==
")")
2511 auto hasOverloadedMemberAccess = [](
const Token* end,
const Scope* scope) ->
bool {
2514 const std::string op =
"operator" + end->
astParent()->originalName();
2516 return f.isConst() && f.name() == op;
2518 if (it == scope->
functionList.end() || !it->retType || !it->retType->classScope)
2521 return func && func->
isConst();
2524 if (end->
strAt(1) ==
"(") {
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"))) ||
2534 && (tok1->previous()->isComparisonOp() ||
2535 (tok1->previous()->isAssignmentOp() && tok1->tokAt(-2)->variable() &&
Token::Match(tok1->tokAt(-2)->variable()->typeEndToken(),
"const_iterator|const_reverse_iterator"))))) {
2542 }
else if (hasOverloadedMemberAccess(end, var->
typeScope())) {
2555 else if (end->
strAt(1) ==
"<<" && tok1->strAt(-1) !=
"<<")
2565 const Token* start = tok1;
2566 while (tok1->strAt(-1) ==
")")
2569 if (start->
strAt(-1) ==
"delete")
2572 tok1 = jumpBackToken?jumpBackToken:end;
2573 if (tok1 == end &&
Token::Match(end->
previous(),
". %name% ( !!)") && !checkFuncCall(tok1, scope, func))
2580 const Variable* var = tok1->tokAt(-2)->variable();
2591 else if (
const Token* funcTok = getFuncTok(tok1)) {
2592 if (!checkFuncCall(funcTok, scope, func))
2594 }
else if (
Token::simpleMatch(tok1,
"> (") && (!tok1->link() || !
Token::Match(tok1->link()->previous(),
"static_cast|const_cast|dynamic_cast|reinterpret_cast"))) {
2609 std::list<const Token *> toks{ tok1 };
2611 toks.push_back(tok2);
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 "
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 "
2640 : var(_var), tok(_tok) {}
2644 std::vector<const Variable*> initArgs;
2660 logChecker(
"CheckClass::initializerListOrder");
2665 for (std::list<Function>::const_iterator func = scope->
functionList.cbegin(); func != scope->
functionList.cend(); ++func) {
2666 if (func->isConstructor() && func->hasBody()) {
2668 const Token *tok = func->arg->
link()->next();
2670 if (tok->
str() ==
":") {
2671 std::vector<VarInfo> vars;
2675 for (; tok && tok != func->functionScope->bodyStart; tok = tok->
next()) {
2680 vars.emplace_back(var, tok);
2684 for (; tok != end; tok = tok->
next()) {
2686 if (scope != argVar->scope())
2688 if (argVar->isStatic())
2698 vars.back().initArgs.emplace_back(argVar);
2704 for (
int j = 0; j < vars.size(); j++) {
2706 for (
const auto& arg : vars[j].initArgs)
2707 if (vars[j].var->index() < arg->index())
2714 if (vars[j].var->index() < vars[j - 1].var->index())
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.";
2730 "$symbol:" + classname +
"::" + varname +
'\n' +
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 "
2746 logChecker(
"CheckClass::checkSelfInitialization");
2750 if (!
function || !function->isConstructor())
2753 const Token* tok =
function->arg->
link()->next();
2754 if (tok->
str() !=
":")
2762 if (initTok->varId() == varTok->
varId())
2764 else if (initTok->isCast() && ((initTok->astOperand1() && initTok->astOperand1()->varId() == varTok->
varId()) || (initTok->astOperand2() && initTok->astOperand2()->varId() == varTok->
varId())))
2787 logChecker(
"CheckClass::checkVirtualFunctionCallInConstructor");
2788 std::map<const Function *, std::list<const Token *>> virtualFunctionCallsMap;
2796 for (
const Token *callToken : virtualFunctionCalls) {
2797 std::list<const Token *> callstack(1, callToken);
2799 if (callstack.empty())
2801 const Function*
const func = callstack.back()->function();
2814 std::map<
const Function *, std::list<const Token *>> & virtualFunctionCallsMap)
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;
2820 virtualFunctionCallsMap[&
function] = std::list<const Token *>();
2821 std::list<const Token *> & virtualFunctionCalls = virtualFunctionCallsMap.find(&
function)->second;
2823 if (!
function.hasBody() || !
function.functionScope)
2824 return virtualFunctionCalls;
2826 for (
const Token *tok =
function.arg->
link(); tok !=
function.functionScope->bodyEnd; tok = tok->next()) {
2834 tok = tok->linkAt(1);
2839 tok = tok->scope()->bodyEnd->next();
2841 const Function * callFunction = tok->function();
2842 if (!callFunction ||
2843 function.nestedIn != callFunction->
nestedIn ||
2845 !(tok->astParent() && (tok->astParent()->str() ==
"(" || (tok->astParent()->str() ==
"::" &&
Token::simpleMatch(tok->astParent()->astParent(),
"(")))))
2848 if (tok->previous() &&
2849 tok->previous()->str() ==
"(") {
2860 virtualFunctionCalls.push_back(tok);
2864 const std::list<const Token *> & virtualFunctionCallsOfTok =
getVirtualFunctionCalls(*callFunction, virtualFunctionCallsMap);
2865 if (!virtualFunctionCallsOfTok.empty())
2866 virtualFunctionCalls.push_back(tok);
2868 return virtualFunctionCalls;
2872 std::map<
const Function *, std::list<const Token *>> & virtualFunctionCallsMap,
2873 const Token * callToken,
2874 std::list<const Token *> & pureFuncStack)
2878 pureFuncStack.push_back(callFunction->
tokenDef);
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();
2886 const Token * firstCall = *found->second.cbegin();
2887 pureFuncStack.push_back(firstCall);
2893 const std::list<const Token *> & tokStack,
2894 const std::string &funcname)
2899 const char * scopeFunctionTypeName = scopeFunction ?
getFunctionTypeName(scopeFunction->
type) :
"constructor";
2902 std::transform(tokStack.cbegin(), tokStack.cend(), std::back_inserter(errorPath), [](
const Token* tok) {
2903 return ErrorPathItem(tok,
"Calling " + tok->str());
2906 if (!errorPath.empty()) {
2907 lineNumber = errorPath.front().first->linenr();
2908 errorPath.back().second = funcname +
" is a virtual function";
2911 std::string constructorName;
2912 if (scopeFunction) {
2914 if (scopeFunction->
type == Function::Type::eDestructor)
2915 constructorName =
"~";
2916 for (
const Token *tok = scopeFunction->
tokenDef; tok != endToken; tok = tok->
next()) {
2918 constructorName +=
' ';
2919 constructorName += tok->
str();
2920 if (tok->
str() ==
")")
2926 "Virtual function '" + funcname +
"' is called from " + scopeFunctionTypeName +
" '" + constructorName +
"' at line " + std::to_string(lineNumber) +
". Dynamic binding is not used.",
CWE(0U),
Certainty::normal);
2931 const std::list<const Token *> & tokStack,
2932 const std::string &purefuncname)
2934 const char * scopeFunctionTypeName = scopeFunction ?
getFunctionTypeName(scopeFunction->
type) :
"constructor";
2937 std::transform(tokStack.cbegin(), tokStack.cend(), std::back_inserter(errorPath), [](
const Token* tok) {
2938 return ErrorPathItem(tok,
"Calling " + tok->str());
2940 if (!errorPath.empty())
2941 errorPath.back().second = purefuncname +
" is a pure virtual function without body";
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);
2959 logChecker(
"CheckClass::checkDuplInheritedMembers");
2969 struct DuplMemberInfo {
2975 struct DuplMemberFuncInfo {
2985 std::vector<DuplMemberInfo> results;
2991 if (parentClassIt.
type == typeBase)
2996 if (classVarIt.
name() == parentClassVarIt.
name() && (!parentClassVarIt.
isPrivate() || !skipPrivate))
2997 results.emplace_back(&classVarIt, &parentClassVarIt, &parentClassIt);
3000 if (typeCurrent != parentClassIt.
type) {
3002 results.insert(results.end(), recursive.begin(), recursive.end());
3010 std::vector<DuplMemberFuncInfo> results;
3016 if (parentClassIt.
type == typeBase)
3022 if (classFuncIt.
name() == parentClassFuncIt.
name() &&
3028 results.emplace_back(&classFuncIt, &parentClassFuncIt, &parentClassIt);
3031 if (typeCurrent != parentClassIt.
type) {
3033 results.insert(results.end(), recursive.begin(), recursive.end());
3042 for (
const auto& r : resultsVar) {
3044 typeCurrent->
name(), r.parentClass->type->name(), r.classVar->name(),
3050 for (
const auto& r : resultsFunc) {
3052 typeCurrent->
name(), r.parentClass->type->name(), r.classFunc->name(),
3059 const std::string &derivedName,
const std::string &baseName,
3060 const std::string &memberName,
bool derivedIsStruct,
bool baseIsStruct,
bool isFunction)
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 +
"'");
3067 const std::string symbols =
"$symbol:" + derivedName +
"\n$symbol:" + memberName +
"\n$symbol:" + baseName;
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 +
"'.";
3098 const bool hasNonStaticVars = std::any_of(scope->
varlist.begin(), scope->
varlist.end(), [](
const Variable& var) {
3099 return !var.isStatic();
3101 if (!hasNonStaticVars)
3105 bool moveCtor =
false;
3141 const std::string message =
"$symbol:" + classname +
"\n"
3142 "The " + std::string(isStruct ?
"struct" :
"class") +
" '$symbol' has '" +
3155 logChecker(
"CheckClass::checkMissingOverride");
3171 const std::string functionName = funcInDerived ? ((funcInDerived->
isDestructor() ?
"~" :
"") + funcInDerived->
name()) :
"";
3172 const std::string funcType = (funcInDerived && funcInDerived->
isDestructor()) ?
"destructor" :
"function";
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");
3181 "$symbol:" + functionName +
"\n"
3182 "The " + funcType +
" '$symbol' overrides a " + funcType +
" in a base class but is not marked with a 'override' specifier.",
3189 const std::string functionName = funcInDerived ? ((funcInDerived->
isDestructor() ?
"~" :
"") + funcInDerived->
name()) :
"";
3190 const std::string funcType = (funcInDerived && funcInDerived->
isDestructor()) ?
"destructor" :
"function";
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");
3198 std::string errStr =
"\nThe " + funcType +
" '$symbol' overrides a " + funcType +
" in a base class but ";
3200 errStr +=
"is identical to the overridden function";
3203 errStr +=
"just delegates back to the base class.";
3205 "$symbol:" + functionName +
3216 const Token* ftok = start;
3217 if (ftok->
str() ==
"return")
3221 ftok = ftok->
next();
3229 const Token* tok1 = start1;
3230 const Token* tok2 = start2;
3232 while (tok1 && tok2) {
3235 if (tok1->
str() != tok2->
str())
3237 if (tok1->
str() ==
"this")
3241 if (tok1 == end1 && tok2 == end2) {
3245 tok1 = tok1->
next();
3246 tok2 = tok2->
next();
3256 logChecker(
"CheckClass::checkUselessOverride");
3272 return f.name() == func.name();
3295 if (call->function() != baseFunc)
3298 std::vector<const Token*> callArgs =
getArguments(call);
3299 if (funcArgs.size() != callArgs.size() ||
3300 !std::equal(funcArgs.begin(), funcArgs.end(), callArgs.begin(), [](
const Token* t1,
const Token* t2) {
3301 return t1->str() == t2->str();
3327 logChecker(
"CheckClass::checkReturnByReference");
3338 if (container->view)
3341 if (!var->valueType())
3343 if (var->isArgument())
3345 const bool isContainer = var->valueType()->type == ValueType::Type::CONTAINER && var->valueType()->container;
3346 const bool isView = isContainer && var->valueType()->container->view;
3347 bool warn = isContainer && !isView;
3348 if (!warn && !isView) {
3363 const std::string message =
"Function '" + (func ? func->
name() :
"func") +
"()' should return member '" + (var ? var->
name() :
"var") +
"' by const reference.";
3372 logChecker(
"CheckClass::checkThisUseAfterFree");
3386 bool hasAssign =
false;
3388 if (func.
type != Function::Type::eFunction || !func.
hasBody())
3405 if (func.
type != Function::Type::eFunction || !func.
hasBody())
3408 const Token * freeToken =
nullptr;
3409 std::set<const Function *> callstack;
3422 if (callstack.count(func))
3424 callstack.insert(func);
3428 for (
const Token *tok = bodyStart; tok != bodyEnd; tok = tok->
next()) {
3429 const bool isDestroyed = freeToken !=
nullptr && !func->
isStatic();
3430 if (
Token::Match(tok,
"delete %var% ;") && selfPointer == tok->next()->variable()) {
3432 tok = tok->
tokAt(2);
3433 }
else if (
Token::Match(tok,
"%var% . reset ( )") && selfPointer == tok->variable())
3435 else if (
Token::Match(tok->previous(),
"!!. %name% (") && tok->function() && tok->function()->nestedIn == classScope) {
3442 }
else if (isDestroyed &&
Token::Match(tok->previous(),
"!!. %name%") && tok->variable() && tok->variable()->scope() == classScope && !tok->variable()->isStatic() && !tok->variable()->isArgument()) {
3445 }
else if (freeToken &&
Token::Match(tok,
"return|throw")) {
3447 return tok->str() ==
"throw";
3448 }
else if (tok->str() ==
"{" && tok->scope()->type == Scope::ScopeType::eLambda) {
3457 std::string selfPointer =
self ?
self->str() :
"ptr";
3458 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") };
3459 const std::string usestr = use ? use->
str() :
"x";
3460 const std::string usemsg = use && use->
function() ? (
"Calling method '" + usestr +
"()'") : (
"Using member '" + usestr +
"'");
3462 "$symbol:" + selfPointer +
"\n" +
3463 usemsg +
" when 'this' might be invalid",
3471 logChecker(
"CheckClass::checkUnsafeClassRefMember");
3494 "$symbol:" + varname +
"\n"
3495 "Unsafe class: The const reference member '$symbol' is initialized by a const reference constructor argument. You need to be careful about lifetime issues.\n"
3496 "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.",
3503 inline namespace CheckClass_internal
3512 std::string className;
3513 std::string fileName;
3518 bool isSameLocation(
const NameLoc& other)
const {
3519 return fileName == other.fileName &&
3520 lineNumber == other.lineNumber &&
3521 column == other.column;
3524 std::vector<NameLoc> classDefinitions;
3527 std::string
toString()
const override
3530 for (
const NameLoc &nameLoc: classDefinitions) {
3533 "\" line=\"" + std::to_string(nameLoc.lineNumber) +
3534 "\" col=\"" + std::to_string(nameLoc.column) +
3535 "\" hash=\"" + std::to_string(nameLoc.hash) +
3545 if (!tokenizer.
isCPP())
3549 std::vector<MyFileInfo::NameLoc> classDefinitions;
3551 if (classScope->isAnonymous())
3554 if (classScope->classDef &&
Token::simpleMatch(classScope->classDef->previous(),
">"))
3558 const bool fullDefinition = std::all_of(classScope->functionList.cbegin(),
3559 classScope->functionList.cend(),
3563 if (!fullDefinition)
3567 const Scope *scope = classScope;
3580 if (scope->
type != Scope::ScopeType::eGlobal)
3583 MyFileInfo::NameLoc nameLoc;
3584 nameLoc.className = std::move(
name);
3585 nameLoc.fileName = tokenizer.
list.
file(classScope->classDef);
3586 nameLoc.lineNumber = classScope->classDef->linenr();
3587 nameLoc.column = classScope->classDef->column();
3591 for (
const Token *tok = classScope->classDef; tok != classScope->bodyEnd; tok = tok->
next())
3593 for (
const Function &f: classScope->functionList) {
3599 nameLoc.hash = std::hash<std::string> {}(def);
3601 classDefinitions.push_back(std::move(nameLoc));
3604 if (classDefinitions.empty())
3607 auto *fileInfo =
new MyFileInfo;
3608 fileInfo->classDefinitions.swap(classDefinitions);
3614 auto *fileInfo =
new MyFileInfo;
3615 for (
const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) {
3616 if (std::strcmp(e->Name(),
"class") != 0)
3618 const char *
name = e->Attribute(
"name");
3619 const char *file = e->Attribute(
"file");
3620 const char *line = e->Attribute(
"line");
3621 const char *col = e->Attribute(
"col");
3622 const char *hash = e->Attribute(
"hash");
3623 if (
name && file && line && col && hash) {
3624 MyFileInfo::NameLoc nameLoc;
3625 nameLoc.className =
name;
3626 nameLoc.fileName = file;
3627 nameLoc.lineNumber = strToInt<int>(line);
3628 nameLoc.column = strToInt<int>(col);
3629 nameLoc.hash = strToInt<std::size_t>(hash);
3630 fileInfo->classDefinitions.push_back(std::move(nameLoc));
3633 if (fileInfo->classDefinitions.empty()) {
3642 bool foundErrors =
false;
3646 std::unordered_map<std::string, MyFileInfo::NameLoc> all;
3648 CheckClass dummy(
nullptr, &settings, &errorLogger);
3650 logChecker(
"CheckClass::analyseWholeProgram");
3653 const MyFileInfo *fi =
dynamic_cast<const MyFileInfo*
>(fi1);
3656 for (
const MyFileInfo::NameLoc &nameLoc : fi->classDefinitions) {
3657 auto it = all.find(nameLoc.className);
3658 if (it == all.end()) {
3659 all[nameLoc.className] = nameLoc;
3662 if (it->second.hash == nameLoc.hash)
3665 if (it->second.isSameLocation(nameLoc))
3668 std::list<ErrorMessage::FileLocation> locationList;
3669 locationList.emplace_back(nameLoc.fileName, nameLoc.lineNumber, nameLoc.column);
3670 locationList.emplace_back(it->second.fileName, it->second.lineNumber, it->second.column);
3675 "$symbol:" + nameLoc.className +
3676 "\nThe one definition rule is violated, different classes/structs have the same name '$symbol'",
3677 "ctuOneDefinitionRuleViolation",
std::vector< const Token * > getArguments(const Token *ftok)
Get arguments (AST)
bool astIsRangeBasedForDecl(const Token *tok)
Is given token a range-declaration in a range-based for loop.
bool precedes(const Token *tok1, const Token *tok2)
If tok2 comes after tok1.
bool isLikelyStreamRead(const Token *op)
do we see a likely write of rhs through overloaded operator s >> x; a & x;
const Token * findLambdaEndToken(const Token *first)
find lambda function end token
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,...
const Token * previousBeforeAstLeftmostLeaf(const Token *tok)
void visitAstNodes(T *ast, const TFunc &visitor)
Visit AST nodes recursively.
#define WRONG_DATA(COND, TOK)
Use WRONG_DATA in checkers to mark conditions that check that data is correct.
static bool isVclTypeInit(const Type *type)
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)
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)
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)
static const CWE CWE404(404U)
static bool hasNonCopyableBase(const Scope *scope, bool *unknown)
static const char * getFunctionTypeName(Function::Type type)
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)
static bool isFalseKeyword(const Token *tok)
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
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
static bool canNotCopy(const Scope *scope)
void mallocOnClassWarning(const Token *tok, const std::string &memfunc, const Token *classTok)
void copyConstructorShallowCopyError(const Token *tok, const std::string &varname)
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.
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
static bool isBaseClassMutableMemberFunc(const Token *tok, const Scope *scope)
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)
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
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
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
void checkMemset()
Check that the memsets are valid.
void noDestructorError(const Scope *scope, bool isdefault, const Token *alloc)
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...
void memsetErrorFloat(const Token *tok, const std::string &type)
void noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive)
void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic)
void constructors()
Check that all class constructors are ok
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
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)
static const std::set< std::string > stl_containers_not_const
Set of the STL types whose operator[] is not const.
Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const override
void virtualDestructor()
The destructor in a base class should be virtual.
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.
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.
Interface class that cppcheck uses to communicate with the checks.
void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg)
report an error
const Settings *const mSettings
const Tokenizer *const mTokenizer
void logChecker(const char id[])
log checker
const std::string & name() const
class name, used to generate documentation
This is an interface, which the class responsible of error logging should implement.
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()
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
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)
Type type
constructor, destructor, ...
static bool returnsPointer(const Function *function, bool unknown=false)
static bool returnsReference(const Function *function, bool unknown=false, bool includeRValueRef=false)
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)
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
const Token * templateDef
points to 'template <' before function
std::list< Variable > argumentList
argument list, must remain list due to clangimport usage!
bool isDestructor() const
bool isConstructor() const
Yield getYield(const std::string &function) const
const Container * detectContainerOrIterator(const Token *typeStart, bool *isIterator=nullptr, bool withoutStd=false) const
bool isSmartPointer(const Token *tok) const
const AllocFunc * getAllocFuncInfo(const Token *tok) const
get allocation info for function
bool isScopeNoReturn(const Token *end, std::string *unknownFunc) const
TypeCheck getTypeCheck(std::string check, std::string typeName) const
const AllocFunc * getReallocFuncInfo(const Token *tok) const
get reallocation info for function
const PodType * podtype(const std::string &name) const
const AllocFunc * getDeallocFuncInfo(const Token *tok) const
get deallocation info for function
const Container * detectContainer(const Token *typeStart) const
bool isFunctionConst(const std::string &functionName, bool pure) const
bool ignorefunction(const std::string &functionName) const
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
const Function * getDestructor() const
Function * function
function info for this function
const Function * findFunction(const Token *tok, bool requireConst=false) const
find a function
const Token * classDef
class/struct/union/namespace token
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
nonneg int numConstructors
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...
bool hasLib(const std::string &lib) const
Is library specified?
bool isPremiumEnabled(const char id[]) const
Is checker id enabled by premiumArgs.
SimpleEnableGroup< Certainty > certainty
SimpleEnableGroup< Severity > severity
Standards standards
Struct contains standards settings.
bool isEnabled(T flag) const
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.
static bool Match(const Token *tok, const char pattern[], nonneg int varid=0)
Match given token (or list of tokens) to a pattern list.
static const Token * findmatch(const Token *const startTok, const char pattern[], const nonneg int varId=0)
bool hasKnownIntValue() const
MathLib::bigint getKnownIntValue() const
bool isExpandedMacro() const
bool isUpperCaseName() const
const ValueType * valueType() const
const std::string & strAt(int index) const
void astOperand1(Token *tok)
void function(const Function *f)
Associate this token with given function.
std::pair< const Token *, const Token * > findExpressionStartEndTokens() const
bool isUnaryOp(const std::string &s) const
static const Token * findsimplematch(const Token *const startTok, const char(&pattern)[count])
const Token * tokAt(int index) const
Token::Type tokType() const
void astOperand2(Token *tok)
void scope(const Scope *s)
Associate this token with given scope.
void link(Token *linkToToken)
Create link to given token.
const Token * linkAt(int index) const
void type(const ::Type *t)
Associate this token with given type.
bool isAssignmentOp() const
bool isStandardType() const
void variable(const Variable *v)
Associate this token with given variable.
const Token * nextArgument() const
static bool simpleMatch(const Token *tok, const char(&pattern)[count])
Match given token (or list of tokens) to a pattern list.
void astParent(Token *tok)
The main purpose is to tokenize the source code.
const Token * tokens() const
TokenList list
Token list: stores all tokens.
const SymbolDatabase * getSymbolDatabase() const
bool isCPP() const
Is the code CPP.
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::vector< FriendInfo > friendList
enum Type::NeedInitialization needInitialization
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)
static const std::string emptyString
std::pair< const Token *, std::string > ErrorPathItem
std::list< ErrorPathItem > ErrorPath
@ portability
Portability warning.
@ performance
Performance warning.
@ error
Programming error.
Whole program analysis (ctu=Cross Translation Unit)
size_t getSizeOf(const ValueType &vt, const Settings &settings, int maxRecursion=0)
static constexpr char CWE[]
Information about a member variable.
bool classes
Public interface of classes.
enum Standards::cppstd_t cpp
const Token * isLambdaCaptureList(const Token *tok)
bool endsWith(const std::string &str, char c)
#define bailout(tokenlist, errorLogger, tok, what)