|
Cppcheck
|
00001 /* 00002 * Cppcheck - A tool for static C/C++ code analysis 00003 * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team. 00004 * 00005 * This program is free software: you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License as published by 00007 * the Free Software Foundation, either version 3 of the License, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 */ 00018 00019 //--------------------------------------------------------------------------- 00020 #include "checkclass.h" 00021 00022 #include "tokenize.h" 00023 #include "token.h" 00024 #include "errorlogger.h" 00025 #include "symboldatabase.h" 00026 00027 #include <string> 00028 #include <algorithm> 00029 #include <cctype> 00030 00031 //--------------------------------------------------------------------------- 00032 00033 // Register CheckClass.. 00034 namespace { 00035 CheckClass instance; 00036 00037 const char * getFunctionTypeName( 00038 Function::Type type) 00039 { 00040 switch (type) { 00041 case Function::eConstructor: 00042 return "constructor"; 00043 case Function::eCopyConstructor: 00044 return "copy constructor"; 00045 case Function::eMoveConstructor: 00046 return "move constructor"; 00047 case Function::eDestructor: 00048 return "destructor"; 00049 case Function::eFunction: 00050 return "function"; 00051 case Function::eOperatorEqual: 00052 return "operator="; 00053 } 00054 return ""; 00055 } 00056 00057 inline bool isPureWithoutBody(Function const & func) 00058 { 00059 return func.isPure && !func.hasBody; 00060 } 00061 } 00062 00063 //--------------------------------------------------------------------------- 00064 00065 CheckClass::CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) 00066 : Check(myName(), tokenizer, settings, errorLogger), 00067 symbolDatabase(tokenizer?tokenizer->getSymbolDatabase():NULL) 00068 { 00069 00070 } 00071 00072 //--------------------------------------------------------------------------- 00073 // ClassCheck: Check that all class constructors are ok. 00074 //--------------------------------------------------------------------------- 00075 00076 void CheckClass::constructors() 00077 { 00078 bool style = _settings->isEnabled("style"); 00079 bool warnings = _settings->isEnabled("warning"); 00080 if (!style && !warnings) 00081 return; 00082 00083 const std::size_t classes = symbolDatabase->classAndStructScopes.size(); 00084 for (std::size_t i = 0; i < classes; ++i) { 00085 const Scope * scope = symbolDatabase->classAndStructScopes[i]; 00086 00087 // There are no constructors. 00088 if (scope->numConstructors == 0 && style) { 00089 // If there is a private variable, there should be a constructor.. 00090 std::list<Variable>::const_iterator var; 00091 for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { 00092 if (var->isPrivate() && !var->isStatic() && 00093 (!var->isClass() || (var->type() && var->type()->needInitialization == Type::True))) { 00094 noConstructorError(scope->classDef, scope->className, scope->classDef->str() == "struct"); 00095 break; 00096 } 00097 } 00098 } 00099 00100 if (!warnings) 00101 continue; 00102 00103 // #3196 => bailout if there are nested unions 00104 // TODO: handle union variables better 00105 { 00106 bool bailout = false; 00107 for (std::list<Scope *>::const_iterator it = scope->nestedList.begin(); it != scope->nestedList.end(); ++it) { 00108 const Scope * const nestedScope = *it; 00109 if (nestedScope->type == Scope::eUnion) { 00110 bailout = true; 00111 break; 00112 } 00113 } 00114 if (bailout) 00115 continue; 00116 } 00117 00118 00119 std::list<Function>::const_iterator func; 00120 std::vector<Usage> usage(scope->varlist.size()); 00121 00122 for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 00123 if (!func->hasBody || !(func->isConstructor() || 00124 func->type == Function::eOperatorEqual)) 00125 continue; 00126 00127 // Mark all variables not used 00128 clearAllVar(usage); 00129 00130 std::list<const Function *> callstack; 00131 initializeVarList(*func, callstack, &(*scope), usage); 00132 00133 // Check if any variables are uninitialized 00134 std::list<Variable>::const_iterator var; 00135 unsigned int count = 0; 00136 for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var, ++count) { 00137 // check for C++11 initializer 00138 if (var->hasDefault()) 00139 usage[count].init = true; 00140 00141 bool inconclusive = false; 00142 00143 if (usage[count].assign || usage[count].init || var->isStatic()) 00144 continue; 00145 00146 if (var->isConst() && func->isOperator) // We can't set const members in assignment operator 00147 continue; 00148 00149 // Check if this is a class constructor 00150 if (!var->isPointer() && var->isClass() && func->type == Function::eConstructor) { 00151 // Unknown type so assume it is initialized 00152 if (!var->type()) 00153 continue; 00154 00155 // Known type that doesn't need initialization or 00156 // known type that has member variables of an unknown type 00157 else if (var->type()->needInitialization != Type::True) 00158 continue; 00159 } 00160 00161 // Check if type can't be copied 00162 if (!var->isPointer() && var->typeScope()) { 00163 if (func->type == Function::eMoveConstructor) { 00164 if (canNotMove(var->typeScope())) 00165 continue; 00166 } else { 00167 if (canNotCopy(var->typeScope())) 00168 continue; 00169 } 00170 } 00171 00172 // Don't warn about unknown types in copy constructors since we 00173 // don't know if they can be copied or not.. 00174 if (!var->isPointer() && 00175 !(var->type() && var->type()->needInitialization != Type::True) && 00176 (func->type == Function::eCopyConstructor || func->type == Function::eOperatorEqual)) { 00177 bool stdtype = false; 00178 for (const Token *type = var->typeStartToken(); type && type->isName(); type = type->next()) 00179 stdtype |= type->isStandardType(); 00180 if (!stdtype) { 00181 if (_settings->inconclusive) 00182 inconclusive = true; 00183 else 00184 continue; 00185 } 00186 } 00187 00188 // It's non-static and it's not initialized => error 00189 if (func->type == Function::eOperatorEqual) { 00190 const Token *operStart = func->arg; 00191 00192 bool classNameUsed = false; 00193 for (const Token *operTok = operStart; operTok != operStart->link(); operTok = operTok->next()) { 00194 if (operTok->str() == scope->className) { 00195 classNameUsed = true; 00196 break; 00197 } 00198 } 00199 00200 if (classNameUsed) 00201 operatorEqVarError(func->token, scope->className, var->name(), inconclusive); 00202 } else if (func->access != Private) { 00203 const Scope *varType = var->typeScope(); 00204 if (!varType || varType->type != Scope::eUnion) { 00205 if (func->type == Function::eConstructor && 00206 func->nestedIn && (func->nestedIn->numConstructors - func->nestedIn->numCopyOrMoveConstructors) > 1 && 00207 func->argCount() == 0 && func->functionScope && 00208 func->arg && func->arg->link()->next() == func->functionScope->classStart && 00209 func->functionScope->classStart->link() == func->functionScope->classStart->next()) { 00210 // don't warn about user defined default constructor when there are other constructors 00211 if (_settings->inconclusive) 00212 uninitVarError(func->token, scope->className, var->name(), true); 00213 } else 00214 uninitVarError(func->token, scope->className, var->name(), inconclusive); 00215 } 00216 } 00217 } 00218 } 00219 } 00220 } 00221 00222 void CheckClass::copyconstructors() 00223 { 00224 if (!_settings->isEnabled("style")) 00225 return; 00226 00227 const std::size_t classes = symbolDatabase->classAndStructScopes.size(); 00228 for (std::size_t i = 0; i < classes; ++i) { 00229 const Scope * scope = symbolDatabase->classAndStructScopes[i]; 00230 std::map<unsigned int, const Token*> allocatedVars; 00231 00232 for (std::list<Function>::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 00233 if (func->type == Function::eConstructor && func->functionScope) { 00234 const Token* tok = func->functionScope->classDef->linkAt(1); 00235 for (const Token* const end = func->functionScope->classStart; tok != end; tok = tok->next()) { 00236 if (Token::Match(tok, "%var% ( new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc")) { 00237 const Variable* var = tok->variable(); 00238 if (var && var->isPointer() && var->scope() == &*scope) 00239 allocatedVars[tok->varId()] = tok; 00240 } 00241 } 00242 for (const Token* const end = func->functionScope->classEnd; tok != end; tok = tok->next()) { 00243 if (Token::Match(tok, "%var% = new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc")) { 00244 const Variable* var = tok->variable(); 00245 if (var && var->isPointer() && var->scope() == &*scope) 00246 allocatedVars[tok->varId()] = tok; 00247 } 00248 } 00249 } 00250 } 00251 00252 std::set<const Token*> copiedVars; 00253 const Token* copyCtor = 0; 00254 for (std::list<Function>::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 00255 if (func->type == Function::eCopyConstructor) { 00256 copyCtor = func->tokenDef; 00257 if (func->functionScope) { 00258 const Token* tok = func->tokenDef->linkAt(1)->next(); 00259 if (tok->str()==":") { 00260 tok=tok->next(); 00261 while (Token::Match(tok, "%var% (")) { 00262 if (allocatedVars.find(tok->varId()) != allocatedVars.end()) { 00263 if (tok->varId() && Token::Match(tok->tokAt(2), "%var% . %var% )")) 00264 copiedVars.insert(tok); 00265 else if (!Token::Match(tok->tokAt(2), "%any% )")) 00266 allocatedVars.erase(tok->varId()); // Assume memory is allocated 00267 } 00268 tok = tok->linkAt(1)->tokAt(2); 00269 } 00270 } 00271 for (tok=func->functionScope->classStart; tok!=func->functionScope->classEnd; tok=tok->next()) { 00272 if (Token::Match(tok, "%var% = new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc")) { 00273 allocatedVars.erase(tok->varId()); 00274 } else if (Token::Match(tok, "%var% = %var% . %var% ;") && allocatedVars.find(tok->varId()) != allocatedVars.end()) { 00275 copiedVars.insert(tok); 00276 } 00277 } 00278 } else // non-copyable or implementation not seen 00279 allocatedVars.clear(); 00280 break; 00281 } 00282 } 00283 if (!copyCtor) { 00284 if (!allocatedVars.empty() && scope->definedType->derivedFrom.empty()) // TODO: Check if base class is non-copyable 00285 noCopyConstructorError(scope->classDef, scope->className, scope->type == Scope::eStruct); 00286 } else { 00287 if (!copiedVars.empty()) { 00288 for (std::set<const Token*>::const_iterator it = copiedVars.begin(); it != copiedVars.end(); ++it) { 00289 copyConstructorShallowCopyError(*it, (*it)->str()); 00290 } 00291 } 00292 // throw error if count mismatch 00293 /* FIXME: This doesn't work. See #4154 00294 for (std::map<unsigned int, const Token*>::const_iterator i = allocatedVars.begin(); i != allocatedVars.end(); ++i) { 00295 copyConstructorMallocError(copyCtor, i->second, i->second->str()); 00296 } 00297 */ 00298 } 00299 } 00300 } 00301 00302 /* This doesn't work. See #4154 00303 void CheckClass::copyConstructorMallocError(const Token *cctor, const Token *alloc, const std::string& varname) 00304 { 00305 std::list<const Token*> callstack; 00306 callstack.push_back(cctor); 00307 callstack.push_back(alloc); 00308 reportError(callstack, Severity::warning, "copyCtorNoAllocation", "Copy constructor does not allocate memory for member '" + varname + "' although memory has been allocated in other constructors."); 00309 } 00310 */ 00311 00312 void CheckClass::copyConstructorShallowCopyError(const Token *tok, const std::string& varname) 00313 { 00314 reportError(tok, Severity::style, "copyCtorPointerCopying", "Value of pointer '" + varname + "', which points to allocated memory, is copied in copy constructor instead of allocating new memory."); 00315 } 00316 00317 void CheckClass::noCopyConstructorError(const Token *tok, const std::string &classname, bool isStruct) 00318 { 00319 // The constructor might be intentionally missing. Therefore this is not a "warning" 00320 reportError(tok, Severity::style, "noCopyConstructor", 00321 "'" + std::string(isStruct ? "struct" : "class") + " " + classname + 00322 "' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory."); 00323 } 00324 00325 bool CheckClass::canNotCopy(const Scope *scope) 00326 { 00327 std::list<Function>::const_iterator func; 00328 bool constructor = false; 00329 bool publicAssign = false; 00330 bool publicCopy = false; 00331 00332 for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 00333 if (func->isConstructor()) 00334 constructor = true; 00335 if ((func->type == Function::eCopyConstructor) && 00336 func->access == Public) 00337 publicCopy = true; 00338 else if (func->type == Function::eOperatorEqual && func->access == Public) 00339 publicAssign = true; 00340 } 00341 00342 return constructor && !(publicAssign || publicCopy); 00343 } 00344 00345 bool CheckClass::canNotMove(const Scope *scope) 00346 { 00347 std::list<Function>::const_iterator func; 00348 bool constructor = false; 00349 bool publicAssign = false; 00350 bool publicCopy = false; 00351 bool publicMove = false; 00352 00353 for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 00354 if (func->isConstructor()) 00355 constructor = true; 00356 if ((func->type == Function::eCopyConstructor) && 00357 func->access == Public) 00358 publicCopy = true; 00359 else if ((func->type == Function::eMoveConstructor) && 00360 func->access == Public) 00361 publicMove = true; 00362 else if (func->type == Function::eOperatorEqual && func->access == Public) 00363 publicAssign = true; 00364 } 00365 00366 return constructor && !(publicAssign || publicCopy || publicMove); 00367 } 00368 00369 void CheckClass::assignVar(const std::string &varname, const Scope *scope, std::vector<Usage> &usage) 00370 { 00371 std::list<Variable>::const_iterator var; 00372 unsigned int count = 0; 00373 00374 for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var, ++count) { 00375 if (var->name() == varname) { 00376 usage[count].assign = true; 00377 return; 00378 } 00379 } 00380 } 00381 00382 void CheckClass::initVar(const std::string &varname, const Scope *scope, std::vector<Usage> &usage) 00383 { 00384 std::list<Variable>::const_iterator var; 00385 unsigned int count = 0; 00386 00387 for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var, ++count) { 00388 if (var->name() == varname) { 00389 usage[count].init = true; 00390 return; 00391 } 00392 } 00393 } 00394 00395 void CheckClass::assignAllVar(std::vector<Usage> &usage) 00396 { 00397 for (std::size_t i = 0; i < usage.size(); ++i) 00398 usage[i].assign = true; 00399 } 00400 00401 void CheckClass::clearAllVar(std::vector<Usage> &usage) 00402 { 00403 for (std::size_t i = 0; i < usage.size(); ++i) { 00404 usage[i].assign = false; 00405 usage[i].init = false; 00406 } 00407 } 00408 00409 bool CheckClass::isBaseClassFunc(const Token *tok, const Scope *scope) 00410 { 00411 // Iterate through each base class... 00412 for (std::size_t i = 0; i < scope->definedType->derivedFrom.size(); ++i) { 00413 const Type *derivedFrom = scope->definedType->derivedFrom[i].type; 00414 00415 // Check if base class exists in database 00416 if (derivedFrom && derivedFrom->classScope) { 00417 std::list<Function>::const_iterator func; 00418 00419 for (func = derivedFrom->classScope->functionList.begin(); func != derivedFrom->classScope->functionList.end(); ++func) { 00420 if (func->tokenDef->str() == tok->str()) 00421 return true; 00422 } 00423 } 00424 00425 // Base class not found so assume it is in it. 00426 else 00427 return true; 00428 } 00429 00430 return false; 00431 } 00432 00433 void CheckClass::initializeVarList(const Function &func, std::list<const Function *> &callstack, const Scope *scope, std::vector<Usage> &usage) 00434 { 00435 bool initList = func.isConstructor(); 00436 const Token *ftok = func.arg->link()->next(); 00437 int level = 0; 00438 for (; ftok != func.functionScope->classEnd; ftok = ftok->next()) { 00439 // Class constructor.. initializing variables like this 00440 // clKalle::clKalle() : var(value) { } 00441 if (initList) { 00442 if (level == 0 && Token::Match(ftok, "%var% (")) { 00443 if (ftok->str() != func.name()) { 00444 initVar(ftok->str(), scope, usage); 00445 } else { // c++11 delegate constructor 00446 const Function *member = scope->findFunction(ftok); 00447 // member function found 00448 if (member) { 00449 // recursive call 00450 // assume that all variables are initialized 00451 if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) { 00452 /** @todo false negative: just bail */ 00453 assignAllVar(usage); 00454 return; 00455 } 00456 00457 // member function has implementation 00458 if (member->hasBody) { 00459 // initialize variable use list using member function 00460 callstack.push_back(member); 00461 initializeVarList(*member, callstack, scope, usage); 00462 callstack.pop_back(); 00463 } 00464 00465 // there is a called member function, but it has no implementation, so we assume it initializes everything 00466 else { 00467 assignAllVar(usage); 00468 } 00469 } 00470 } 00471 } else if (level == 0 && Token::Match(ftok, "%var% {") && ftok->str() != "const" && Token::Match(ftok->next()->link()->next(), ",|{|%type%")) { 00472 initVar(ftok->str(), scope, usage); 00473 ftok = ftok->linkAt(1); 00474 } else if (level != 0 && Token::Match(ftok, "%var% =")) // assignment in the initializer: var(value = x) 00475 assignVar(ftok->str(), scope, usage); 00476 00477 else if (ftok->str() == "(") 00478 level++; 00479 else if (ftok->str() == ")") 00480 level--; 00481 else if (ftok->str() == "{") { 00482 if (level == 0) 00483 initList = false; 00484 else 00485 ftok = ftok->link(); 00486 } 00487 } 00488 00489 if (initList) 00490 continue; 00491 00492 // Variable getting value from stream? 00493 if (Token::Match(ftok, ">> %var%")) { 00494 assignVar(ftok->strAt(1), scope, usage); 00495 } 00496 00497 // Before a new statement there is "[{};)=]" 00498 if (! Token::Match(ftok, "[{};()=]")) 00499 continue; 00500 00501 if (Token::simpleMatch(ftok, "( !")) 00502 ftok = ftok->next(); 00503 00504 // Using the operator= function to initialize all variables.. 00505 if (Token::Match(ftok->next(), "return| (| * this )| =")) { 00506 assignAllVar(usage); 00507 break; 00508 } 00509 00510 // Using swap to assign all variables.. 00511 if (func.type == Function::eOperatorEqual && Token::Match(ftok, "[;{}] %var% (") && Token::Match(ftok->linkAt(2), ") . %var% ( *| this ) ;")) { 00512 assignAllVar(usage); 00513 break; 00514 } 00515 00516 // Calling member variable function? 00517 if (Token::Match(ftok->next(), "%var% . %var% (")) { 00518 std::list<Variable>::const_iterator var; 00519 for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { 00520 if (var->varId() == ftok->next()->varId()) { 00521 /** @todo false negative: we assume function changes variable state */ 00522 assignVar(ftok->next()->str(), scope, usage); 00523 break; 00524 } 00525 } 00526 00527 ftok = ftok->tokAt(2); 00528 } 00529 00530 if (!Token::Match(ftok->next(), "::| %var%") && 00531 !Token::Match(ftok->next(), "this . %var%") && 00532 !Token::Match(ftok->next(), "* %var% =") && 00533 !Token::Match(ftok->next(), "( * this ) . %var%")) 00534 continue; 00535 00536 // Goto the first token in this statement.. 00537 ftok = ftok->next(); 00538 00539 // skip "return" 00540 if (ftok->str() == "return") 00541 ftok = ftok->next(); 00542 00543 // Skip "( * this )" 00544 if (Token::simpleMatch(ftok, "( * this ) .")) { 00545 ftok = ftok->tokAt(5); 00546 } 00547 00548 // Skip "this->" 00549 if (Token::simpleMatch(ftok, "this .")) 00550 ftok = ftok->tokAt(2); 00551 00552 // Skip "classname :: " 00553 if (Token::Match(ftok, "%var% ::")) 00554 ftok = ftok->tokAt(2); 00555 00556 // Clearing all variables.. 00557 if (Token::Match(ftok, "::| memset ( this ,")) { 00558 assignAllVar(usage); 00559 return; 00560 } 00561 00562 // Clearing array.. 00563 else if (Token::Match(ftok, "::| memset ( %var% ,")) { 00564 if (ftok->str() == "::") 00565 ftok = ftok->next(); 00566 assignVar(ftok->strAt(2), scope, usage); 00567 ftok = ftok->linkAt(1); 00568 continue; 00569 } 00570 00571 // Calling member function? 00572 else if (Token::simpleMatch(ftok, "operator= (") && 00573 ftok->previous()->str() != "::") { 00574 if (ftok->function() && ftok->function()->nestedIn == scope) { 00575 const Function *member = ftok->function(); 00576 // recursive call 00577 // assume that all variables are initialized 00578 if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) { 00579 /** @todo false negative: just bail */ 00580 assignAllVar(usage); 00581 return; 00582 } 00583 00584 // member function has implementation 00585 if (member->hasBody) { 00586 // initialize variable use list using member function 00587 callstack.push_back(member); 00588 initializeVarList(*member, callstack, scope, usage); 00589 callstack.pop_back(); 00590 } 00591 00592 // there is a called member function, but it has no implementation, so we assume it initializes everything 00593 else { 00594 assignAllVar(usage); 00595 } 00596 } 00597 00598 // using default operator =, assume everything initialized 00599 else { 00600 assignAllVar(usage); 00601 } 00602 } else if (Token::Match(ftok, "::| %var% (") && ftok->str() != "if") { 00603 if (ftok->str() == "::") 00604 ftok = ftok->next(); 00605 00606 // Passing "this" => assume that everything is initialized 00607 for (const Token *tok2 = ftok->next()->link(); tok2 && tok2 != ftok; tok2 = tok2->previous()) { 00608 if (tok2->str() == "this") { 00609 assignAllVar(usage); 00610 return; 00611 } 00612 } 00613 00614 // check if member function 00615 if (ftok->function() && ftok->function()->nestedIn == scope && 00616 !ftok->function()->isConstructor()) { 00617 const Function *member = ftok->function(); 00618 00619 // recursive call 00620 // assume that all variables are initialized 00621 if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) { 00622 assignAllVar(usage); 00623 return; 00624 } 00625 00626 // member function has implementation 00627 if (member->hasBody) { 00628 // initialize variable use list using member function 00629 callstack.push_back(member); 00630 initializeVarList(*member, callstack, scope, usage); 00631 callstack.pop_back(); 00632 00633 // Assume that variables that are passed to it are initialized.. 00634 for (const Token *tok2 = ftok; tok2; tok2 = tok2->next()) { 00635 if (Token::Match(tok2, "[;{}]")) 00636 break; 00637 if (Token::Match(tok2, "[(,] &| %var% [,)]")) { 00638 tok2 = tok2->next(); 00639 if (tok2->str() == "&") 00640 tok2 = tok2->next(); 00641 assignVar(tok2->str(), scope, usage); 00642 } 00643 } 00644 } 00645 00646 // there is a called member function, but it has no implementation, so we assume it initializes everything 00647 else { 00648 assignAllVar(usage); 00649 } 00650 } 00651 00652 // not member function 00653 else { 00654 // could be a base class virtual function, so we assume it initializes everything 00655 if (!func.isConstructor() && isBaseClassFunc(ftok, scope)) { 00656 /** @todo False Negative: we should look at the base class functions to see if they 00657 * call any derived class virtual functions that change the derived class state 00658 */ 00659 assignAllVar(usage); 00660 } 00661 00662 // has friends, so we assume it initializes everything 00663 if (!scope->definedType->friendList.empty()) 00664 assignAllVar(usage); 00665 00666 // the function is external and it's neither friend nor inherited virtual function. 00667 // assume all variables that are passed to it are initialized.. 00668 else { 00669 for (const Token *tok = ftok->tokAt(2); tok && tok != ftok->next()->link(); tok = tok->next()) { 00670 if (tok->isName()) { 00671 assignVar(tok->str(), scope, usage); 00672 } 00673 } 00674 } 00675 } 00676 } 00677 00678 // Assignment of member variable? 00679 else if (Token::Match(ftok, "%var% =")) { 00680 assignVar(ftok->str(), scope, usage); 00681 } 00682 00683 // Assignment of array item of member variable? 00684 else if (Token::Match(ftok, "%var% [|.")) { 00685 const Token *tok2 = ftok; 00686 while (tok2) { 00687 if (tok2->strAt(1) == "[") 00688 tok2 = tok2->next()->link(); 00689 else if (Token::Match(tok2->next(), ". %var%")) 00690 tok2 = tok2->tokAt(2); 00691 else 00692 break; 00693 } 00694 if (tok2 && tok2->strAt(1) == "=") 00695 assignVar(ftok->str(), scope, usage); 00696 } 00697 00698 // Assignment of array item of member variable? 00699 else if (Token::Match(ftok, "* %var% =")) { 00700 assignVar(ftok->next()->str(), scope, usage); 00701 } 00702 00703 // The functions 'clear' and 'Clear' are supposed to initialize variable. 00704 if (Token::Match(ftok, "%var% . clear|Clear (")) { 00705 assignVar(ftok->str(), scope, usage); 00706 } 00707 } 00708 } 00709 00710 void CheckClass::noConstructorError(const Token *tok, const std::string &classname, bool isStruct) 00711 { 00712 // For performance reasons the constructor might be intentionally missing. Therefore this is not a "warning" 00713 reportError(tok, Severity::style, "noConstructor", 00714 "The " + std::string(isStruct ? "struct" : "class") + " '" + classname + 00715 "' does not have a constructor.\n" 00716 "The " + std::string(isStruct ? "struct" : "class") + " '" + classname + 00717 "' does not have a constructor although it has private member variables. " 00718 "Member variables of builtin types are left uninitialized when the class is " 00719 "instanciated. That may cause bugs or undefined behavior."); 00720 } 00721 00722 void CheckClass::uninitVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive) 00723 { 00724 reportError(tok, Severity::warning, "uninitMemberVar", "Member variable '" + classname + "::" + varname + "' is not initialized in the constructor.", inconclusive); 00725 } 00726 00727 void CheckClass::operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive) 00728 { 00729 reportError(tok, Severity::warning, "operatorEqVarError", "Member variable '" + classname + "::" + varname + "' is not assigned a value in '" + classname + "::operator='.", inconclusive); 00730 } 00731 00732 //--------------------------------------------------------------------------- 00733 // ClassCheck: Use initialization list instead of assignment 00734 //--------------------------------------------------------------------------- 00735 00736 void CheckClass::initializationListUsage() 00737 { 00738 if (!_settings->isEnabled("performance")) 00739 return; 00740 00741 const std::size_t functions = symbolDatabase->functionScopes.size(); 00742 for (std::size_t i = 0; i < functions; ++i) { 00743 const Scope * scope = symbolDatabase->functionScopes[i]; 00744 00745 // Check every constructor 00746 if (!scope->function || (!scope->function->isConstructor())) 00747 continue; 00748 00749 const Scope* owner = scope->functionOf; 00750 for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { 00751 if (Token::Match(tok, "%var% (")) // Assignments might depend on this function call or if/for/while/switch statement from now on. 00752 break; 00753 if (Token::Match(tok, "try|do {")) 00754 break; 00755 if (tok->varId() && Token::Match(tok, "%var% = %any%")) { 00756 const Variable* var = tok->variable(); 00757 if (var && var->scope() == owner && !var->isStatic()) { 00758 bool allowed = true; 00759 for (const Token* tok2 = tok->tokAt(2); tok2->str() != ";"; tok2 = tok2->next()) { 00760 if (tok2->varId()) { 00761 const Variable* var2 = tok2->variable(); 00762 if (var2 && var2->scope() == owner && 00763 tok2->strAt(-1)!=".") { // Is there a dependency between two member variables? 00764 allowed = false; 00765 break; 00766 } 00767 } else if (tok2->str() == "this") { // 'this' instance is not completely constructed in initialization list 00768 allowed = false; 00769 break; 00770 } else if (Token::Match(tok2, "%var% (") && tok2->strAt(-1) != "." && isMemberFunc(owner, tok2)) { // Member function called? 00771 allowed = false; 00772 break; 00773 } 00774 } 00775 if (!allowed) 00776 continue; 00777 if (!var->isPointer() && (var->type() || Token::Match(var->typeStartToken(), "std :: string|wstring !!::") || (Token::Match(var->typeStartToken(), "std :: %type% <") && !Token::simpleMatch(var->typeStartToken()->linkAt(3), "> ::")))) 00778 suggestInitializationList(tok, tok->str()); 00779 } 00780 } 00781 } 00782 } 00783 } 00784 00785 void CheckClass::suggestInitializationList(const Token* tok, const std::string& varname) 00786 { 00787 reportError(tok, Severity::performance, "useInitializationList", "Variable '" + varname + "' is assigned in constructor body. Consider performing initialization in initialization list.\n" 00788 "When an object of a class is created, the constructors of all member variables are called consecutively " 00789 "in the order the variables are declared, even if you don't explicitly write them to the initialization list. You " 00790 "could avoid assigning '" + varname + "' a value by passing the value to the constructor in the initialization list."); 00791 } 00792 00793 //--------------------------------------------------------------------------- 00794 // ClassCheck: Unused private functions 00795 //--------------------------------------------------------------------------- 00796 00797 static bool checkFunctionUsage(const std::string& name, const Scope* scope) 00798 { 00799 if (!scope) 00800 return true; // Assume it is used, if scope is not seen 00801 00802 for (std::list<Function>::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 00803 if (func->functionScope) { 00804 for (const Token *ftok = func->functionScope->classDef->linkAt(1); ftok != func->functionScope->classEnd; ftok = ftok->next()) { 00805 if (ftok->str() == name) // Function used. TODO: Handle overloads 00806 return true; 00807 } 00808 } else if ((func->type != Function::eCopyConstructor && 00809 func->type != Function::eOperatorEqual) || 00810 func->access != Private) // Assume it is used, if a function implementation isn't seen, but empty private copy constructors and assignment operators are OK 00811 return true; 00812 } 00813 00814 for (std::list<Scope*>::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) { 00815 if ((*i)->isClassOrStruct()) 00816 if (checkFunctionUsage(name, *i)) // Check nested classes, which can access private functions of their base 00817 return true; 00818 } 00819 00820 return false; // Unused in this scope 00821 } 00822 00823 void CheckClass::privateFunctions() 00824 { 00825 if (!_settings->isEnabled("style")) 00826 return; 00827 00828 const std::size_t classes = symbolDatabase->classAndStructScopes.size(); 00829 for (std::size_t i = 0; i < classes; ++i) { 00830 const Scope * scope = symbolDatabase->classAndStructScopes[i]; 00831 00832 // dont check borland classes with properties.. 00833 if (Token::findsimplematch(scope->classStart, "; __property ;", scope->classEnd)) 00834 continue; 00835 00836 std::list<const Function*> FuncList; 00837 for (std::list<Function>::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 00838 // Get private functions.. 00839 if (func->type == Function::eFunction && func->access == Private) 00840 FuncList.push_back(&*func); 00841 } 00842 00843 // Bailout for overridden virtual functions of base classes 00844 if (!scope->definedType->derivedFrom.empty()) { 00845 // Check virtual functions 00846 for (std::list<const Function*>::iterator it = FuncList.begin(); it != FuncList.end();) { 00847 if ((*it)->isImplicitlyVirtual(true)) // Give true as default value to be returned if we don't see all base classes 00848 FuncList.erase(it++); 00849 else 00850 ++it; 00851 } 00852 } 00853 00854 while (!FuncList.empty()) { 00855 const std::string& funcName = FuncList.front()->tokenDef->str(); 00856 // Check that all private functions are used 00857 bool used = checkFunctionUsage(funcName, &*scope); // Usage in this class 00858 // Check in friend classes 00859 for (std::list<Type::FriendInfo>::const_iterator it = scope->definedType->friendList.begin(); !used && it != scope->definedType->friendList.end(); ++it) 00860 if (it->type) 00861 used = checkFunctionUsage(funcName, it->type->classScope); 00862 else 00863 used = true; // Assume, it is used if we do not see friend class 00864 00865 if (!used) 00866 unusedPrivateFunctionError(FuncList.front()->tokenDef, scope->className, funcName); 00867 00868 FuncList.pop_front(); 00869 } 00870 } 00871 } 00872 00873 void CheckClass::unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname) 00874 { 00875 reportError(tok, Severity::style, "unusedPrivateFunction", "Unused private function: '" + classname + "::" + funcname + "'"); 00876 } 00877 00878 //--------------------------------------------------------------------------- 00879 // ClassCheck: Check that memset is not used on classes 00880 //--------------------------------------------------------------------------- 00881 00882 static const Scope* findFunctionOf(const Scope* scope) 00883 { 00884 while (scope) { 00885 if (scope->type == Scope::eFunction) 00886 return scope->functionOf; 00887 scope = scope->nestedIn; 00888 } 00889 return 0; 00890 } 00891 00892 void CheckClass::noMemset() 00893 { 00894 const std::size_t functions = symbolDatabase->functionScopes.size(); 00895 for (std::size_t i = 0; i < functions; ++i) { 00896 const Scope * scope = symbolDatabase->functionScopes[i]; 00897 for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { 00898 if (Token::Match(tok, "memset|memcpy|memmove ( %any%")) { 00899 const Token* arg1 = tok->tokAt(2); 00900 const Token* arg3 = arg1; 00901 arg3 = arg3->nextArgument(); 00902 arg3 = (arg3 != NULL) ? arg3->nextArgument() : NULL; 00903 if (!arg3) 00904 // weird, shouldn't happen: memset etc should have 00905 // 3 arguments. 00906 continue; 00907 00908 const Token *typeTok = 0; 00909 const Scope *type = 0; 00910 if (Token::Match(arg3, "sizeof ( %type% ) )")) 00911 typeTok = arg3->tokAt(2); 00912 else if (Token::Match(arg3, "sizeof ( %type% :: %type% ) )")) 00913 typeTok = arg3->tokAt(4); 00914 else if (Token::Match(arg3, "sizeof ( struct %type% ) )")) 00915 typeTok = arg3->tokAt(3); 00916 else if (Token::simpleMatch(arg3, "sizeof ( * this ) )") || Token::simpleMatch(arg1, "this ,")) { 00917 type = findFunctionOf(arg3->scope()); 00918 } else if (Token::Match(arg1, "&|*|%var%")) { 00919 int derefs = 1; 00920 for (;; arg1 = arg1->next()) { 00921 if (arg1->str() == "&") 00922 derefs--; 00923 else if (arg1->str() == "*") 00924 derefs++; 00925 else 00926 break; 00927 } 00928 00929 const Variable *var = arg1->variable(); 00930 if (var && arg1->strAt(1) == ",") { 00931 if (var->isPointer()) 00932 derefs--; 00933 if (var->isArray()) 00934 derefs -= (int)var->dimensions().size(); 00935 00936 if (derefs == 0) 00937 type = var->typeScope(); 00938 } 00939 } 00940 00941 // No type defined => The tokens didn't match 00942 if (!typeTok && !type) 00943 continue; 00944 00945 if (typeTok && typeTok->str() == "(") 00946 typeTok = typeTok->next(); 00947 00948 if (!type) { 00949 const Type* t = symbolDatabase->findVariableType(&(*scope), typeTok); 00950 if (t) 00951 type = t->classScope; 00952 } 00953 00954 if (type) 00955 checkMemsetType(&(*scope), tok, type, false); 00956 } else if (tok->variable() && tok->variable()->typeScope() && Token::Match(tok, "%var% = calloc|malloc|realloc|g_malloc|g_try_malloc|g_realloc|g_try_realloc (")) { 00957 checkMemsetType(&(*scope), tok->tokAt(2), tok->variable()->typeScope(), true); 00958 00959 if (tok->variable()->typeScope()->numConstructors > 0 && _settings->isEnabled("warning")) 00960 mallocOnClassWarning(tok, tok->strAt(2), tok->variable()->typeScope()->classDef); 00961 } 00962 } 00963 } 00964 } 00965 00966 void CheckClass::checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation) 00967 { 00968 // recursively check all parent classes 00969 for (std::size_t i = 0; i < type->definedType->derivedFrom.size(); i++) { 00970 if (type->definedType->derivedFrom[i].type && type->definedType->derivedFrom[i].type->classScope) 00971 checkMemsetType(start, tok, type->definedType->derivedFrom[i].type->classScope, allocation); 00972 } 00973 00974 // Warn if type is a class that contains any virtual functions 00975 std::list<Function>::const_iterator func; 00976 00977 for (func = type->functionList.begin(); func != type->functionList.end(); ++func) { 00978 if (func->isVirtual) { 00979 if (allocation) 00980 mallocOnClassError(tok, tok->str(), type->classDef, "virtual method"); 00981 else 00982 memsetError(tok, tok->str(), "virtual method", type->classDef->str()); 00983 } 00984 } 00985 00986 // Warn if type is a class or struct that contains any std::* variables 00987 std::list<Variable>::const_iterator var; 00988 00989 for (var = type->varlist.begin(); var != type->varlist.end(); ++var) { 00990 // don't warn if variable static or const, pointer or reference 00991 if (!var->isStatic() && !var->isConst() && !var->isPointer() && !var->isReference()) { 00992 const Token *tok1 = var->typeStartToken(); 00993 00994 // check for std:: type 00995 if (Token::simpleMatch(tok1, "std ::")) 00996 if (allocation) 00997 mallocOnClassError(tok, tok->str(), type->classDef, "'std::" + tok1->strAt(2) + "'"); 00998 else 00999 memsetError(tok, tok->str(), "'std::" + tok1->strAt(2) + "'", type->classDef->str()); 01000 01001 // check for known type 01002 else if (var->typeScope()) 01003 checkMemsetType(start, tok, var->typeScope(), allocation); 01004 } 01005 } 01006 } 01007 01008 void CheckClass::mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok) 01009 { 01010 std::list<const Token *> toks; 01011 toks.push_back(tok); 01012 toks.push_back(classTok); 01013 reportError(toks, Severity::warning, "mallocOnClassWarning", 01014 "Memory for class instance allocated with " + memfunc + "(), but class provides constructors.\n" 01015 "Memory for class instance allocated with " + memfunc + "(), but class provides constructors. This is unsafe, " 01016 "since no constructor is called and class members remain uninitialized. Consider using 'new' instead."); 01017 } 01018 01019 void CheckClass::mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname) 01020 { 01021 std::list<const Token *> toks; 01022 toks.push_back(tok); 01023 toks.push_back(classTok); 01024 reportError(toks, Severity::error, "mallocOnClassError", 01025 "Memory for class instance allocated with " + memfunc + "(), but class contains a " + classname + ".\n" 01026 "Memory for class instance allocated with " + memfunc + "(), but class a " + classname + ". This is unsafe, " 01027 "since no constructor is called and class members remain uninitialized. Consider using 'new' instead."); 01028 } 01029 01030 void CheckClass::memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type) 01031 { 01032 reportError(tok, Severity::error, "memsetClass", "Using '" + memfunc + "' on " + type + " that contains a " + classname + "."); 01033 } 01034 01035 //--------------------------------------------------------------------------- 01036 // ClassCheck: "void operator=(" and "const type & operator=(" 01037 //--------------------------------------------------------------------------- 01038 01039 void CheckClass::operatorEq() 01040 { 01041 if (!_settings->isEnabled("style")) 01042 return; 01043 01044 const std::size_t classes = symbolDatabase->classAndStructScopes.size(); 01045 for (std::size_t i = 0; i < classes; ++i) { 01046 const Scope * scope = symbolDatabase->classAndStructScopes[i]; 01047 std::list<Function>::const_iterator func; 01048 01049 for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 01050 if (func->type == Function::eOperatorEqual && func->access != Private) { 01051 // use definition for check so we don't have to deal with qualification 01052 if (!(Token::Match(func->tokenDef->tokAt(-3), ";|}|{|public:|protected:|private:|virtual %type% &") && 01053 func->tokenDef->strAt(-2) == scope->className)) { 01054 // make sure we really have a copy assignment operator 01055 if (Token::Match(func->tokenDef->tokAt(2), "const| %var% &")) { 01056 if (func->tokenDef->strAt(2) == "const" && 01057 func->tokenDef->strAt(3) == scope->className) 01058 operatorEqReturnError(func->tokenDef->previous(), scope->className); 01059 else if (func->tokenDef->strAt(2) == scope->className) 01060 operatorEqReturnError(func->tokenDef->previous(), scope->className); 01061 } 01062 } 01063 } 01064 } 01065 } 01066 } 01067 01068 void CheckClass::operatorEqReturnError(const Token *tok, const std::string &className) 01069 { 01070 reportError(tok, Severity::style, "operatorEq", "'" + className + "::operator=' should return '" + className + " &'."); 01071 } 01072 01073 //--------------------------------------------------------------------------- 01074 // ClassCheck: "C& operator=(const C&) { ... return *this; }" 01075 // operator= should return a reference to *this 01076 //--------------------------------------------------------------------------- 01077 01078 void CheckClass::operatorEqRetRefThis() 01079 { 01080 if (!_settings->isEnabled("style")) 01081 return; 01082 01083 const std::size_t classes = symbolDatabase->classAndStructScopes.size(); 01084 for (std::size_t i = 0; i < classes; ++i) { 01085 const Scope * scope = symbolDatabase->classAndStructScopes[i]; 01086 std::list<Function>::const_iterator func; 01087 01088 for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 01089 if (func->type == Function::eOperatorEqual && func->hasBody) { 01090 // make sure return signature is correct 01091 if (Token::Match(func->tokenDef->tokAt(-3), ";|}|{|public:|protected:|private:|virtual %type% &") && 01092 func->tokenDef->strAt(-2) == scope->className) { 01093 01094 checkReturnPtrThis(&(*scope), &(*func), func->functionScope->classStart, func->functionScope->classEnd); 01095 } 01096 } 01097 } 01098 } 01099 } 01100 01101 void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last) 01102 { 01103 bool foundReturn = false; 01104 01105 for (; tok && tok != last; tok = tok->next()) { 01106 // check for return of reference to this 01107 if (tok->str() == "return") { 01108 foundReturn = true; 01109 std::string cast("( " + scope->className + " & )"); 01110 if (Token::simpleMatch(tok->next(), cast.c_str())) 01111 tok = tok->tokAt(4); 01112 01113 // check if a function is called 01114 if (tok->strAt(2) == "(" && 01115 tok->linkAt(2)->next()->str() == ";") { 01116 std::list<Function>::const_iterator it; 01117 01118 // check if it is a member function 01119 for (it = scope->functionList.begin(); it != scope->functionList.end(); ++it) { 01120 // check for a regular function with the same name and a body 01121 if (it->type == Function::eFunction && it->hasBody && 01122 it->token->str() == tok->next()->str()) { 01123 // check for the proper return type 01124 if (it->tokenDef->previous()->str() == "&" && 01125 it->tokenDef->strAt(-2) == scope->className) { 01126 // make sure it's not a const function 01127 if (!it->isConst) { 01128 /** @todo make sure argument types match */ 01129 // make sure it's not the same function 01130 if (&*it != func) 01131 checkReturnPtrThis(scope, &*it, it->arg->link()->next(), it->arg->link()->next()->link()); 01132 01133 // just bail for now 01134 else 01135 return; 01136 } 01137 } 01138 } 01139 } 01140 } 01141 01142 // check if *this is returned 01143 else if (!(Token::Match(tok->next(), "(| * this ;|=") || 01144 Token::simpleMatch(tok->next(), "operator= (") || 01145 Token::simpleMatch(tok->next(), "this . operator= (") || 01146 (Token::Match(tok->next(), "%type% :: operator= (") && 01147 tok->next()->str() == scope->className))) 01148 operatorEqRetRefThisError(func->token); 01149 } 01150 } 01151 if (!foundReturn) 01152 operatorEqRetRefThisError(func->token); 01153 } 01154 01155 void CheckClass::operatorEqRetRefThisError(const Token *tok) 01156 { 01157 reportError(tok, Severity::style, "operatorEqRetRefThis", "'operator=' should return reference to 'this' instance."); 01158 } 01159 01160 //--------------------------------------------------------------------------- 01161 // ClassCheck: "C& operator=(const C& rhs) { if (this == &rhs) ... }" 01162 // operator= should check for assignment to self 01163 // 01164 // For simple classes, an assignment to self check is only a potential optimization. 01165 // 01166 // For classes that allocate dynamic memory, assignment to self can be a real error 01167 // if it is deallocated and allocated again without being checked for. 01168 // 01169 // This check is not valid for classes with multiple inheritance because a 01170 // class can have multiple addresses so there is no trivial way to check for 01171 // assignment to self. 01172 //--------------------------------------------------------------------------- 01173 01174 void CheckClass::operatorEqToSelf() 01175 { 01176 if (!_settings->isEnabled("warning")) 01177 return; 01178 01179 const std::size_t classes = symbolDatabase->classAndStructScopes.size(); 01180 for (std::size_t i = 0; i < classes; ++i) { 01181 const Scope * scope = symbolDatabase->classAndStructScopes[i]; 01182 std::list<Function>::const_iterator func; 01183 01184 // skip classes with multiple inheritance 01185 if (scope->definedType->derivedFrom.size() > 1) 01186 continue; 01187 01188 for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 01189 if (func->type == Function::eOperatorEqual && func->hasBody) { 01190 // make sure that the operator takes an object of the same type as *this, otherwise we can't detect self-assignment checks 01191 if (func->argumentList.empty()) 01192 continue; 01193 const Token* typeTok = func->argumentList.front().typeEndToken(); 01194 while (typeTok->str() == "const" || typeTok->str() == "&" || typeTok->str() == "*") 01195 typeTok = typeTok->previous(); 01196 if (typeTok->str() != scope->className) 01197 continue; 01198 01199 // make sure return signature is correct 01200 if (Token::Match(func->tokenDef->tokAt(-3), ";|}|{|public:|protected:|private: %type% &") && 01201 func->tokenDef->strAt(-2) == scope->className) { 01202 // find the parameter name 01203 const Token *rhs = func->argumentList.begin()->nameToken(); 01204 01205 if (!hasAssignSelf(&(*func), rhs)) { 01206 if (hasAllocation(&(*func), &*scope)) 01207 operatorEqToSelfError(func->token); 01208 } 01209 } 01210 } 01211 } 01212 } 01213 } 01214 01215 bool CheckClass::hasAllocation(const Function *func, const Scope* scope) 01216 { 01217 // This function is called when no simple check was found for assignment 01218 // to self. We are currently looking for: 01219 // - deallocate member ; ... member = 01220 // - alloc member 01221 // That is not ideal because it can cause false negatives but its currently 01222 // necessary to prevent false positives. 01223 const Token *last = func->functionScope->classEnd; 01224 for (const Token *tok = func->functionScope->classStart; tok && (tok != last); tok = tok->next()) { 01225 if (Token::Match(tok, "%var% = malloc|realloc|calloc|new") && isMemberVar(scope, tok)) 01226 return true; 01227 01228 // check for deallocating memory 01229 const Token *var = 0; 01230 if (Token::Match(tok, "free ( %var%")) 01231 var = tok->tokAt(2); 01232 else if (Token::Match(tok, "delete [ ] %var%")) 01233 var = tok->tokAt(3); 01234 else if (Token::Match(tok, "delete %var%")) 01235 var = tok->next(); 01236 // Check for assignment to the deleted pointer (only if its a member of the class) 01237 if (var && isMemberVar(scope, var)) { 01238 for (const Token *tok1 = var->next(); tok1 && (tok1 != last); tok1 = tok1->next()) { 01239 if (Token::Match(tok1, "%var% =")) { 01240 if (tok1->str() == var->str()) 01241 return true; 01242 } 01243 } 01244 } 01245 } 01246 01247 return false; 01248 } 01249 01250 bool CheckClass::hasAssignSelf(const Function *func, const Token *rhs) 01251 { 01252 const Token *last = func->functionScope->classEnd; 01253 for (const Token *tok = func->functionScope->classStart; tok && tok != last; tok = tok->next()) { 01254 if (Token::simpleMatch(tok, "if (")) { 01255 const Token *tok1 = tok->tokAt(2); 01256 const Token *tok2 = tok->next()->link(); 01257 01258 if (tok1 && tok2) { 01259 for (; tok1 && tok1 != tok2; tok1 = tok1->next()) { 01260 if (Token::Match(tok1, "this ==|!= & %var%")) { 01261 if (tok1->strAt(3) == rhs->str()) 01262 return true; 01263 } else if (Token::Match(tok1, "& %var% ==|!= this")) { 01264 if (tok1->strAt(1) == rhs->str()) 01265 return true; 01266 } 01267 } 01268 } 01269 } 01270 } 01271 01272 return false; 01273 } 01274 01275 void CheckClass::operatorEqToSelfError(const Token *tok) 01276 { 01277 reportError(tok, Severity::warning, "operatorEqToSelf", 01278 "'operator=' should check for assignment to self to avoid problems with dynamic memory.\n" 01279 "'operator=' should check for assignment to self to ensure that each block of dynamically " 01280 "allocated memory is owned and managed by only one instance of the class."); 01281 } 01282 01283 //--------------------------------------------------------------------------- 01284 // A destructor in a base class should be virtual 01285 //--------------------------------------------------------------------------- 01286 01287 void CheckClass::virtualDestructor() 01288 { 01289 // This error should only be given if: 01290 // * base class doesn't have virtual destructor 01291 // * derived class has non-empty destructor 01292 // * base class is deleted 01293 01294 const std::size_t classes = symbolDatabase->classAndStructScopes.size(); 01295 for (std::size_t i = 0; i < classes; ++i) { 01296 const Scope * scope = symbolDatabase->classAndStructScopes[i]; 01297 01298 // Skip base classes 01299 if (scope->definedType->derivedFrom.empty()) 01300 continue; 01301 01302 // Find the destructor 01303 const Function *destructor = scope->getDestructor(); 01304 01305 // Check for destructor with implementation 01306 if (!destructor || !destructor->hasBody) 01307 continue; 01308 01309 // Empty destructor 01310 if (destructor->token->linkAt(3) == destructor->token->tokAt(4)) 01311 continue; 01312 01313 const Token *derived = scope->classDef; 01314 const Token *derivedClass = derived->next(); 01315 01316 // Iterate through each base class... 01317 for (unsigned int j = 0; j < scope->definedType->derivedFrom.size(); ++j) { 01318 // Check if base class is public and exists in database 01319 if (scope->definedType->derivedFrom[j].access != Private && scope->definedType->derivedFrom[j].type) { 01320 const Type *derivedFrom = scope->definedType->derivedFrom[j].type; 01321 const Scope *derivedFromScope = derivedFrom->classScope; 01322 if (!derivedFromScope) 01323 continue; 01324 01325 // Check for this pattern: 01326 // 1. Base class pointer is given the address of derived class instance 01327 // 2. Base class pointer is deleted 01328 // 01329 // If this pattern is not seen then bailout the checking of these base/derived classes 01330 { 01331 // pointer variables of type 'Base *' 01332 std::set<unsigned int> basepointer; 01333 01334 for (std::size_t k = 0; k < symbolDatabase->getVariableListSize(); k++) { 01335 const Variable* var = symbolDatabase->getVariableFromVarId(k); 01336 if (var && var->isPointer() && var->type() == derivedFrom) 01337 basepointer.insert(var->varId()); 01338 } 01339 01340 // pointer variables of type 'Base *' that should not be deleted 01341 std::set<unsigned int> dontDelete; 01342 01343 // No deletion of derived class instance through base class pointer found => the code is ok 01344 bool ok = true; 01345 01346 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 01347 if (Token::Match(tok, "[;{}] %var% =") && 01348 tok->next()->varId() > 0 && 01349 basepointer.find(tok->next()->varId()) != basepointer.end()) { 01350 // new derived class.. 01351 if (Token::simpleMatch(tok->tokAt(3), ("new " + derivedClass->str()).c_str())) { 01352 dontDelete.insert(tok->next()->varId()); 01353 } 01354 } 01355 01356 // Delete base class pointer that might point at derived class 01357 else if (Token::Match(tok, "delete %var% ;") && 01358 tok->next()->varId() && 01359 dontDelete.find(tok->next()->varId()) != dontDelete.end()) { 01360 ok = false; 01361 break; 01362 } 01363 } 01364 01365 // No base class pointer that points at a derived class is deleted 01366 if (ok) 01367 continue; 01368 } 01369 01370 // Find the destructor declaration for the base class. 01371 const Function *base_destructor = derivedFromScope->getDestructor(); 01372 const Token *base = 0; 01373 if (base_destructor) 01374 base = base_destructor->token; 01375 01376 // Check that there is a destructor.. 01377 if (!base_destructor) { 01378 if (derivedFrom->derivedFrom.empty()) 01379 virtualDestructorError(derivedFrom->classDef, derivedFrom->name(), derivedClass->str()); 01380 } else if (!base_destructor->isVirtual) { 01381 // TODO: This is just a temporary fix, better solution is needed. 01382 // Skip situations where base class has base classes of its own, because 01383 // some of the base classes might have virtual destructor. 01384 // Proper solution is to check all of the base classes. If base class is not 01385 // found or if one of the base classes has virtual destructor, error should not 01386 // be printed. See TODO test case "virtualDestructorInherited" 01387 if (derivedFrom->derivedFrom.empty()) { 01388 // Make sure that the destructor is public (protected or private 01389 // would not compile if inheritance is used in a way that would 01390 // cause the bug we are trying to find here.) 01391 if (base_destructor->access == Public) 01392 virtualDestructorError(base, derivedFrom->name(), derivedClass->str()); 01393 } 01394 } 01395 } 01396 } 01397 } 01398 } 01399 01400 void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived) 01401 { 01402 reportError(tok, Severity::error, "virtualDestructor", "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor.\n" 01403 "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor. " 01404 "If you destroy instances of the derived class by deleting a pointer that points to the base class, only " 01405 "the destructor of the base class is executed. Thus, dynamic memory that is managed by the derived class " 01406 "could leak. This can be avoided by adding a virtual destructor to the base class."); 01407 } 01408 01409 //--------------------------------------------------------------------------- 01410 // warn for "this-x". The indented code may be "this->x" 01411 //--------------------------------------------------------------------------- 01412 01413 void CheckClass::thisSubtraction() 01414 { 01415 if (!_settings->isEnabled("warning")) 01416 return; 01417 01418 const Token *tok = _tokenizer->tokens(); 01419 for (;;) { 01420 tok = Token::findmatch(tok, "this - %var%"); 01421 if (!tok) 01422 break; 01423 01424 if (tok->strAt(-1) != "*") 01425 thisSubtractionError(tok); 01426 01427 tok = tok->next(); 01428 } 01429 } 01430 01431 void CheckClass::thisSubtractionError(const Token *tok) 01432 { 01433 reportError(tok, Severity::warning, "thisSubtraction", "Suspicious pointer subtraction. Did you intend to write '->'?"); 01434 } 01435 01436 //--------------------------------------------------------------------------- 01437 // can member function be const? 01438 //--------------------------------------------------------------------------- 01439 01440 void CheckClass::checkConst() 01441 { 01442 // This is an inconclusive check. False positives: #3322. 01443 if (!_settings->inconclusive) 01444 return; 01445 01446 if (!_settings->isEnabled("style")) 01447 return; 01448 01449 const std::size_t classes = symbolDatabase->classAndStructScopes.size(); 01450 for (std::size_t i = 0; i < classes; ++i) { 01451 const Scope * scope = symbolDatabase->classAndStructScopes[i]; 01452 std::list<Function>::const_iterator func; 01453 01454 for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 01455 // does the function have a body? 01456 if (func->type == Function::eFunction && func->hasBody && !func->isFriend && !func->isStatic && !func->isVirtual) { 01457 // get last token of return type 01458 const Token *previous = func->tokenDef->previous(); 01459 01460 // does the function return a pointer or reference? 01461 if (Token::Match(previous, "*|&")) { 01462 const Token *temp = previous; 01463 01464 while (!Token::Match(temp->previous(), ";|}|{|public:|protected:|private:")) 01465 temp = temp->previous(); 01466 01467 if (temp->str() != "const") 01468 continue; 01469 } else if (Token::Match(previous->previous(), "*|& >")) { 01470 const Token *temp = previous; 01471 01472 while (!Token::Match(temp->previous(), ";|}|{|public:|protected:|private:")) { 01473 temp = temp->previous(); 01474 if (temp->str() == "const") 01475 break; 01476 } 01477 01478 if (temp->str() != "const") 01479 continue; 01480 } else if (func->isOperator && Token::Match(previous, ";|{|}|public:|private:|protected:")) { // Operator without return type: conversion operator 01481 const std::string& opName = func->tokenDef->str(); 01482 if (opName.compare(8, 5, "const") != 0 && opName[opName.size()-1] == '&') 01483 continue; 01484 } else { 01485 // don't warn for unknown types.. 01486 // LPVOID, HDC, etc 01487 if (previous->isUpperCaseName() && previous->str().size() > 2 && !symbolDatabase->isClassOrStruct(previous->str())) 01488 continue; 01489 } 01490 01491 // check if base class function is virtual 01492 if (!scope->definedType->derivedFrom.empty()) { 01493 if (func->isImplicitlyVirtual(true)) 01494 continue; 01495 } 01496 01497 bool memberAccessed = false; 01498 // if nothing non-const was found. write error.. 01499 if (checkConstFunc(&(*scope), &*func, memberAccessed)) { 01500 std::string classname = scope->className; 01501 const Scope *nest = scope->nestedIn; 01502 while (nest && nest->type != Scope::eGlobal) { 01503 classname = std::string(nest->className + "::" + classname); 01504 nest = nest->nestedIn; 01505 } 01506 01507 // get function name 01508 std::string functionName = (func->tokenDef->isName() ? "" : "operator") + func->tokenDef->str(); 01509 01510 if (func->tokenDef->str() == "(") 01511 functionName += ")"; 01512 else if (func->tokenDef->str() == "[") 01513 functionName += "]"; 01514 01515 if (!func->isConst || (!memberAccessed && !func->isOperator)) { 01516 if (func->isInline) 01517 checkConstError(func->token, classname, functionName, !memberAccessed && !func->isOperator); 01518 else // not inline 01519 checkConstError2(func->token, func->tokenDef, classname, functionName, !memberAccessed && !func->isOperator); 01520 } 01521 } 01522 } 01523 } 01524 } 01525 } 01526 01527 bool CheckClass::isMemberVar(const Scope *scope, const Token *tok) 01528 { 01529 bool again = false; 01530 01531 // try to find the member variable 01532 do { 01533 again = false; 01534 01535 if (tok->str() == "this") { 01536 return true; 01537 } else if (Token::simpleMatch(tok->tokAt(-3), "( * this )")) { 01538 return true; 01539 } else if (Token::Match(tok->tokAt(-2), "%var% . %var%")) { 01540 tok = tok->tokAt(-2); 01541 again = true; 01542 } else if (Token::Match(tok->tokAt(-2), "] . %var%")) { 01543 tok = tok->linkAt(-2)->previous(); 01544 again = true; 01545 } else if (tok->str() == "]") { 01546 tok = tok->link()->previous(); 01547 again = true; 01548 } 01549 } while (again); 01550 01551 std::list<Variable>::const_iterator var; 01552 for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { 01553 if (var->name() == tok->str()) { 01554 if (tok->varId() == 0) 01555 symbolDatabase->debugMessage(tok, "CheckClass::isMemberVar found used member variable \'" + tok->str() + "\' with varid 0"); 01556 01557 return !var->isStatic(); 01558 } 01559 } 01560 01561 // not found in this class 01562 if (!scope->definedType->derivedFrom.empty()) { 01563 // check each base class 01564 for (unsigned int i = 0; i < scope->definedType->derivedFrom.size(); ++i) { 01565 // find the base class 01566 const Type *derivedFrom = scope->definedType->derivedFrom[i].type; 01567 01568 // find the function in the base class 01569 if (derivedFrom && derivedFrom->classScope) { 01570 if (isMemberVar(derivedFrom->classScope, tok)) 01571 return true; 01572 } 01573 } 01574 } 01575 01576 return false; 01577 } 01578 01579 static unsigned int countParameters(const Token *tok) 01580 { 01581 tok = tok->tokAt(2); 01582 if (tok->str() == ")") 01583 return 0; 01584 01585 unsigned int numpar = 1; 01586 while (NULL != (tok = tok->nextArgument())) 01587 numpar++; 01588 01589 return numpar; 01590 } 01591 01592 static unsigned int countMinArgs(const Token* argList) 01593 { 01594 if (!argList) 01595 return 0; 01596 01597 argList = argList->next(); 01598 if (argList->str() == ")") 01599 return 0; 01600 01601 unsigned int count = 1; 01602 for (; argList; argList = argList->next()) { 01603 if (argList->link() && Token::Match(argList, "(|[|{|<")) 01604 argList = argList->link(); 01605 else if (argList->str() == ",") 01606 count++; 01607 else if (argList->str() == "=") 01608 return count-1; 01609 else if (argList->str() == ")") 01610 break; 01611 } 01612 return count; 01613 } 01614 01615 bool CheckClass::isMemberFunc(const Scope *scope, const Token *tok) 01616 { 01617 unsigned int args = countParameters(tok); 01618 01619 for (std::list<Function>::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 01620 /** @todo we need to look at the argument types when there are overloaded functions 01621 * with the same number of arguments */ 01622 if (func->tokenDef->str() == tok->str() && (func->argCount() == args || (func->argCount() > args && countMinArgs(func->argDef) <= args))) { 01623 return !func->isStatic; 01624 } 01625 } 01626 01627 // not found in this class 01628 if (!scope->definedType->derivedFrom.empty()) { 01629 // check each base class 01630 for (unsigned int i = 0; i < scope->definedType->derivedFrom.size(); ++i) { 01631 // find the base class 01632 const Type *derivedFrom = scope->definedType->derivedFrom[i].type; 01633 01634 // find the function in the base class 01635 if (derivedFrom && derivedFrom->classScope) { 01636 if (isMemberFunc(derivedFrom->classScope, tok)) 01637 return true; 01638 } 01639 } 01640 } 01641 01642 return false; 01643 } 01644 01645 bool CheckClass::isConstMemberFunc(const Scope *scope, const Token *tok) 01646 { 01647 unsigned int args = countParameters(tok); 01648 01649 std::list<Function>::const_iterator func; 01650 unsigned int matches = 0; 01651 unsigned int consts = 0; 01652 01653 for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 01654 /** @todo we need to look at the argument types when there are overloaded functions 01655 * with the same number of arguments */ 01656 if (func->tokenDef->str() == tok->str() && (func->argCount() == args || (func->argCount() > args && countMinArgs(func->argDef) <= args))) { 01657 matches++; 01658 if (func->isConst) 01659 consts++; 01660 } 01661 } 01662 01663 // if there are multiple matches that are all const, return const 01664 if (matches > 0 && matches == consts) 01665 return true; 01666 01667 // not found in this class 01668 if (!scope->definedType->derivedFrom.empty()) { 01669 // check each base class 01670 for (unsigned int i = 0; i < scope->definedType->derivedFrom.size(); ++i) { 01671 // find the base class 01672 const Type *derivedFrom = scope->definedType->derivedFrom[i].type; 01673 01674 // find the function in the base class 01675 if (derivedFrom && derivedFrom->classScope) { 01676 if (isConstMemberFunc(derivedFrom->classScope, tok)) 01677 return true; 01678 } 01679 } 01680 } 01681 01682 return false; 01683 } 01684 01685 bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool& memberAccessed) 01686 { 01687 // if the function doesn't have any assignment nor function call, 01688 // it can be a const function.. 01689 for (const Token *tok1 = func->functionScope->classStart; tok1 && tok1 != func->functionScope->classEnd; tok1 = tok1->next()) { 01690 if (tok1->isName() && isMemberVar(scope, tok1)) { 01691 memberAccessed = true; 01692 const Variable* v = tok1->variable(); 01693 if (v && v->isMutable()) 01694 continue; 01695 01696 if (tok1->str() == "this" && tok1->previous()->isAssignmentOp()) 01697 return(false); 01698 01699 const Token* jumpBackToken = 0; 01700 const Token *lastVarTok = tok1; 01701 const Token *end = tok1; 01702 for (;;) { 01703 if (Token::Match(end->next(), ". %var%")) { 01704 end = end->tokAt(2); 01705 if (end->varId()) 01706 lastVarTok = end; 01707 } else if (end->strAt(1) == "[") { 01708 if (end->varId()) { 01709 const Variable *var = end->variable(); 01710 01711 if (var && Token::simpleMatch(var->typeStartToken(), "std :: map")) // operator[] changes a map 01712 return(false); 01713 } 01714 if (!jumpBackToken) 01715 jumpBackToken = end->next(); // Check inside the [] brackets 01716 end = end->linkAt(1); 01717 } else if (end->strAt(1) == ")") 01718 end = end->next(); 01719 else 01720 break; 01721 } 01722 01723 if (end->strAt(1) == "(") { 01724 const Variable *var = lastVarTok->variable(); 01725 if (!var) 01726 return(false); 01727 if (Token::simpleMatch(var->typeStartToken(), "std ::") // assume all std::*::size() and std::*::empty() are const 01728 && (Token::Match(end, "size|empty|cend|crend|cbegin|crbegin|max_size|length|count|capacity|get_allocator|c_str|str ( )") || Token::Match(end, "rfind|copy"))) 01729 ; 01730 else if (!var->typeScope() || !isConstMemberFunc(var->typeScope(), end)) 01731 return(false); 01732 } 01733 01734 // Assignment 01735 else if (end->next()->type() == Token::eAssignmentOp) 01736 return(false); 01737 01738 // Streaming 01739 else if (end->strAt(1) == "<<" && tok1->strAt(-1) != "<<") 01740 return(false); 01741 else if (tok1->strAt(-1) == ">>") 01742 return(false); 01743 01744 // ++/-- 01745 else if (end->next()->type() == Token::eIncDecOp || tok1->previous()->type() == Token::eIncDecOp) 01746 return(false); 01747 01748 01749 const Token* start = tok1; 01750 while (tok1->strAt(-1) == ")") 01751 tok1 = tok1->linkAt(-1); 01752 01753 if (start->strAt(-1) == "delete") 01754 return(false); 01755 01756 tok1 = jumpBackToken?jumpBackToken:end; // Jump back to first [ to check inside, or jump to end of expression 01757 } 01758 01759 // streaming: << 01760 else if (Token::simpleMatch(tok1->previous(), ") <<") && 01761 isMemberVar(scope, tok1->tokAt(-2))) { 01762 const Variable* var = tok1->tokAt(-2)->variable(); 01763 if (!var || !var->isMutable()) 01764 return(false); 01765 } 01766 01767 01768 // function call.. 01769 else if (Token::Match(tok1, "%var% (") && !tok1->isStandardType() && 01770 !Token::Match(tok1, "return|if|string|switch|while|catch|for")) { 01771 if (isMemberFunc(scope, tok1) && tok1->strAt(-1) != ".") { 01772 if (!isConstMemberFunc(scope, tok1)) 01773 return(false); 01774 memberAccessed = true; 01775 } 01776 // Member variable given as parameter 01777 for (const Token* tok2 = tok1->tokAt(2); tok2 && tok2 != tok1->next()->link(); tok2 = tok2->next()) { 01778 if (tok2->str() == "(") 01779 tok2 = tok2->link(); 01780 else if (tok2->isName() && isMemberVar(scope, tok2)) { 01781 const Variable* var = tok2->variable(); 01782 if (!var || !var->isMutable()) 01783 return(false); // TODO: Only bailout if function takes argument as non-const reference 01784 } 01785 } 01786 } else if (Token::simpleMatch(tok1, "> (") && (!tok1->link() || !Token::Match(tok1->link()->previous(), "static_cast|const_cast|dynamic_cast|reinterpret_cast"))) { 01787 return(false); 01788 } 01789 } 01790 01791 return(true); 01792 } 01793 01794 void CheckClass::checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic) 01795 { 01796 checkConstError2(tok, 0, classname, funcname, suggestStatic); 01797 } 01798 01799 void CheckClass::checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic) 01800 { 01801 std::list<const Token *> toks; 01802 toks.push_back(tok1); 01803 if (tok2) 01804 toks.push_back(tok2); 01805 if (!suggestStatic) 01806 reportError(toks, Severity::style, "functionConst", 01807 "Technically the member function '" + classname + "::" + funcname + "' can be const.\n" 01808 "The member function '" + classname + "::" + funcname + "' can be made a const " 01809 "function. Making this function 'const' should not cause compiler errors. " 01810 "Even though the function can be made const function technically it may not make " 01811 "sense conceptually. Think about your design and the task of the function first - is " 01812 "it a function that must not change object internal state?", true); 01813 else 01814 reportError(toks, Severity::performance, "functionStatic", 01815 "Technically the member function '" + classname + "::" + funcname + "' can be static.\n" 01816 "The member function '" + classname + "::" + funcname + "' can be made a static " 01817 "function. Making a function static can bring a performance benefit since no 'this' instance is " 01818 "passed to the function. This change should not cause compiler errors but it does not " 01819 "necessarily make sense conceptually. Think about your design and the task of the function first - " 01820 "is it a function that must not access members of class instances?", true); 01821 } 01822 01823 //--------------------------------------------------------------------------- 01824 // ClassCheck: Check that initializer list is in declared order. 01825 //--------------------------------------------------------------------------- 01826 01827 struct VarInfo { 01828 VarInfo(const Variable *_var, const Token *_tok) 01829 : var(_var), tok(_tok) { } 01830 01831 const Variable *var; 01832 const Token *tok; 01833 }; 01834 01835 void CheckClass::initializerListOrder() 01836 { 01837 if (!_settings->isEnabled("style")) 01838 return; 01839 01840 // This check is not inconclusive. However it only determines if the initialization 01841 // order is incorrect. It does not determine if being out of order causes 01842 // a real error. Out of order is not necessarily an error but you can never 01843 // have an error if the list is in order so this enforces defensive programming. 01844 if (!_settings->inconclusive) 01845 return; 01846 01847 const std::size_t classes = symbolDatabase->classAndStructScopes.size(); 01848 for (std::size_t i = 0; i < classes; ++i) { 01849 const Scope * info = symbolDatabase->classAndStructScopes[i]; 01850 std::list<Function>::const_iterator func; 01851 01852 // iterate through all member functions looking for constructors 01853 for (func = info->functionList.begin(); func != info->functionList.end(); ++func) { 01854 if ((func->isConstructor()) && func->hasBody) { 01855 // check for initializer list 01856 const Token *tok = func->arg->link()->next(); 01857 01858 if (tok->str() == ":") { 01859 std::vector<VarInfo> vars; 01860 tok = tok->next(); 01861 01862 // find all variable initializations in list 01863 while (tok && tok->str() != "{") { 01864 if (Token::Match(tok, "%var% (")) { 01865 const Variable *var = info->getVariable(tok->str()); 01866 01867 if (var) 01868 vars.push_back(VarInfo(var, tok)); 01869 01870 if (Token::Match(tok->tokAt(2), "%var% =")) { 01871 var = info->getVariable(tok->strAt(2)); 01872 01873 if (var) 01874 vars.push_back(VarInfo(var, tok->tokAt(2))); 01875 } 01876 tok = tok->next()->link()->next(); 01877 } else 01878 tok = tok->next(); 01879 } 01880 01881 // need at least 2 members to have out of order initialization 01882 for (std::size_t j = 1; j < vars.size(); j++) { 01883 // check for out of order initialization 01884 if (vars[j].var->index() < vars[j - 1].var->index()) 01885 initializerListError(vars[j].tok,vars[j].var->nameToken(), info->className, vars[j].var->name()); 01886 } 01887 } 01888 } 01889 } 01890 } 01891 } 01892 01893 void CheckClass::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname) 01894 { 01895 std::list<const Token *> toks; 01896 toks.push_back(tok1); 01897 toks.push_back(tok2); 01898 reportError(toks, Severity::style, "initializerList", 01899 "Member variable '" + classname + "::" + 01900 varname + "' is in the wrong place in the initializer list.\n" 01901 "Member variable '" + classname + "::" + 01902 varname + "' is in the wrong place in the initializer list. " 01903 "Members are initialized in the order they are declared, not in the " 01904 "order they are in the initializer list. Keeping the initializer list " 01905 "in the same order that the members were declared prevents order dependent " 01906 "initialization errors.", true); 01907 } 01908 01909 void CheckClass::checkPureVirtualFunctionCall() 01910 { 01911 const std::size_t functions = symbolDatabase->functionScopes.size(); 01912 std::map<const Function *, std::list<const Token *> > callsPureVirtualFunctionMap; 01913 for (std::size_t i = 0; i < functions; ++i) { 01914 const Scope * scope = symbolDatabase->functionScopes[i]; 01915 if (scope->function == 0 || !scope->function->hasBody || 01916 !(scope->function->isConstructor() || 01917 scope->function->isDestructor())) 01918 continue; 01919 01920 const std::list<const Token *> & pureVirtualFunctionCalls=callsPureVirtualFunction(*scope->function,callsPureVirtualFunctionMap); 01921 for (std::list<const Token *>::const_iterator pureCallIter=pureVirtualFunctionCalls.begin(); 01922 pureCallIter!=pureVirtualFunctionCalls.end(); 01923 ++pureCallIter) { 01924 const Token & pureCall=**pureCallIter; 01925 std::list<const Token *> pureFuncStack; 01926 pureFuncStack.push_back(&pureCall); 01927 getFirstPureVirtualFunctionCallStack(callsPureVirtualFunctionMap, pureCall, pureFuncStack); 01928 if (!pureFuncStack.empty()) 01929 callsPureVirtualFunctionError(*scope->function, pureFuncStack, pureFuncStack.back()->str()); 01930 } 01931 } 01932 } 01933 01934 const std::list<const Token *> & CheckClass::callsPureVirtualFunction(const Function & function, 01935 std::map<const Function *, std::list<const Token *> > & callsPureVirtualFunctionMap) 01936 { 01937 std::pair<std::map<const Function *, std::list<const Token *> >::iterator , bool > found= 01938 callsPureVirtualFunctionMap.insert(std::pair<const Function *, std::list< const Token *> >(&function,std::list<const Token *>())); 01939 std::list<const Token *> & pureFunctionCalls=found.first->second; 01940 if (found.second) { 01941 if (function.hasBody) { 01942 for (const Token *tok = function.arg->link(); 01943 tok != function.functionScope->classEnd; 01944 tok = tok->next()) { 01945 const Function * callFunction=tok->function(); 01946 if (!callFunction || 01947 function.nestedIn != callFunction->nestedIn || 01948 (tok->previous() && tok->previous()->str()==".")) 01949 continue; 01950 01951 if (isPureWithoutBody(*callFunction)) { 01952 pureFunctionCalls.push_back(tok); 01953 continue; 01954 } 01955 01956 const std::list<const Token *> & pureFunctionCallsOfTok=callsPureVirtualFunction(*callFunction, 01957 callsPureVirtualFunctionMap); 01958 if (!pureFunctionCallsOfTok.empty()) { 01959 pureFunctionCalls.push_back(tok); 01960 continue; 01961 } 01962 } 01963 } 01964 } 01965 return pureFunctionCalls; 01966 } 01967 01968 void CheckClass::getFirstPureVirtualFunctionCallStack( 01969 std::map<const Function *, std::list<const Token *> > & callsPureVirtualFunctionMap, 01970 const Token & pureCall, 01971 std::list<const Token *> & pureFuncStack) 01972 { 01973 if (isPureWithoutBody(*pureCall.function())) { 01974 pureFuncStack.push_back(pureCall.function()->token); 01975 return; 01976 } 01977 std::map<const Function *, std::list<const Token *> >::const_iterator found=callsPureVirtualFunctionMap.find(pureCall.function()); 01978 if (found==callsPureVirtualFunctionMap.end() || 01979 found->second.empty()) { 01980 pureFuncStack.clear(); 01981 return; 01982 } 01983 const Token & firstPureCall=**found->second.begin(); 01984 pureFuncStack.push_back(&firstPureCall); 01985 getFirstPureVirtualFunctionCallStack(callsPureVirtualFunctionMap, firstPureCall, pureFuncStack); 01986 } 01987 01988 void CheckClass::callsPureVirtualFunctionError( 01989 const Function & scopeFunction, 01990 const std::list<const Token *> & tokStack, 01991 const std::string &purefuncname) 01992 { 01993 const char * scopeFunctionTypeName=getFunctionTypeName(scopeFunction.type); 01994 reportError(tokStack, Severity::warning, "pureVirtualCall", "Call of pure virtual function '" + purefuncname + "' in " + scopeFunctionTypeName + ".\n" 01995 "Call of pure virtual function '" + purefuncname + "' in " + scopeFunctionTypeName + ". The call will fail during runtime."); 01996 } 01997
1.7.6.1