|
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 //--------------------------------------------------------------------------- 00021 #include "checkother.h" 00022 #include "mathlib.h" 00023 #include "symboldatabase.h" 00024 00025 #include <cmath> // fabs() 00026 #include <stack> 00027 #include <algorithm> // find_if() 00028 //--------------------------------------------------------------------------- 00029 00030 // Register this check class (by creating a static instance of it) 00031 namespace { 00032 CheckOther instance; 00033 } 00034 00035 //---------------------------------------------------------------------------------- 00036 // The return value of fgetc(), getc(), ungetc(), getchar() etc. is an integer value. 00037 // If this return value is stored in a character variable and then compared 00038 // to compared to EOF, which is an integer, the comparison maybe be false. 00039 // 00040 // Reference: 00041 // - Ticket #160 00042 // - http://www.cplusplus.com/reference/cstdio/fgetc/ 00043 // - http://www.cplusplus.com/reference/cstdio/getc/ 00044 // - http://www.cplusplus.com/reference/cstdio/getchar/ 00045 // - http://www.cplusplus.com/reference/cstdio/ungetc/ .... 00046 //---------------------------------------------------------------------------------- 00047 void CheckOther::checkCastIntToCharAndBack() 00048 { 00049 if (!_settings->isEnabled("warning")) 00050 return; 00051 00052 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 00053 const std::size_t functions = symbolDatabase->functionScopes.size(); 00054 for (std::size_t i = 0; i < functions; ++i) { 00055 const Scope * scope = symbolDatabase->functionScopes[i]; 00056 std::map<unsigned int, std::string> vars; 00057 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00058 if (Token::Match(tok, "%var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc (")) { 00059 const Variable *var = tok->variable(); 00060 if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { 00061 vars[tok->varId()] = tok->strAt(2); 00062 } 00063 } else if (Token::Match(tok, "EOF %comp% ( %var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc (")) { 00064 tok = tok->tokAt(3); 00065 if (tok && tok->varId()) { 00066 const Variable *var = tok->variable(); 00067 if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { 00068 checkCastIntToCharAndBackError(tok, tok->strAt(2)); 00069 } 00070 } 00071 } else if (Token::Match(tok, "EOF %comp% ( %var% = std :: cin . get (") || Token::Match(tok, "EOF %comp% ( %var% = cin . get (")) { 00072 tok = tok->tokAt(3); 00073 if (tok && tok->varId()) { 00074 const Variable *var = tok->variable(); 00075 if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { 00076 checkCastIntToCharAndBackError(tok, "cin.get"); 00077 } 00078 } 00079 } else if (Token::Match(tok, "%var% = std :: cin . get (") || Token::Match(tok, "%var% = cin . get (")) { 00080 const Variable *var = tok->variable(); 00081 if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { 00082 vars[tok->varId()] = "cin.get"; 00083 } 00084 } 00085 if (Token::Match(tok, "%var% %comp% EOF")) { 00086 if (vars.find(tok->varId()) != vars.end()) { 00087 checkCastIntToCharAndBackError(tok, vars[tok->varId()]); 00088 } 00089 } else if (Token::Match(tok, "EOF %comp% %var%")) { 00090 tok = tok->tokAt(2); 00091 if (vars.find(tok->varId()) != vars.end()) { 00092 checkCastIntToCharAndBackError(tok, vars[tok->varId()]); 00093 } 00094 } 00095 } 00096 } 00097 } 00098 00099 void CheckOther::checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName) 00100 { 00101 reportError( 00102 tok, 00103 Severity::warning, 00104 "checkCastIntToCharAndBack", 00105 "Storing "+ strFunctionName +"() return value in char variable and then comparing with EOF.\n" 00106 "When saving "+ strFunctionName +"() return value in char variable there is loss of precision. " 00107 " When "+ strFunctionName +"() returns EOF this value is truncated. Comparing the char " 00108 "variable with EOF can have unexpected results. For instance a loop \"while (EOF != (c = "+ strFunctionName +"());\" " 00109 "loops forever on some compilers/platforms and on other compilers/platforms it will stop " 00110 "when the file contains a matching character." 00111 ); 00112 } 00113 00114 //--------------------------------------------------------------------------- 00115 //--------------------------------------------------------------------------- 00116 void CheckOther::checkIncrementBoolean() 00117 { 00118 if (!_settings->isEnabled("style")) 00119 return; 00120 00121 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 00122 const std::size_t functions = symbolDatabase->functionScopes.size(); 00123 for (std::size_t i = 0; i < functions; ++i) { 00124 const Scope * scope = symbolDatabase->functionScopes[i]; 00125 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00126 if (Token::Match(tok, "%var% ++")) { 00127 if (tok->varId()) { 00128 const Variable *var = tok->variable(); 00129 00130 if (var && var->typeEndToken()->str() == "bool") 00131 incrementBooleanError(tok); 00132 } 00133 } 00134 } 00135 } 00136 } 00137 00138 void CheckOther::incrementBooleanError(const Token *tok) 00139 { 00140 reportError( 00141 tok, 00142 Severity::style, 00143 "incrementboolean", 00144 "Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n" 00145 "The operand of a postfix increment operator may be of type bool but it is deprecated by C++ Standard (Annex D-1) and the operand is always set to true. You should assign it the value 'true' instead." 00146 ); 00147 } 00148 00149 //--------------------------------------------------------------------------- 00150 //--------------------------------------------------------------------------- 00151 void CheckOther::clarifyCalculation() 00152 { 00153 if (!_settings->isEnabled("style")) 00154 return; 00155 00156 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 00157 const std::size_t functions = symbolDatabase->functionScopes.size(); 00158 for (std::size_t i = 0; i < functions; ++i) { 00159 const Scope * scope = symbolDatabase->functionScopes[i]; 00160 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00161 if (tok->str() == "?") { 00162 // condition 00163 const Token *cond = tok->previous(); 00164 if (cond->isName() || cond->isNumber()) 00165 cond = cond->previous(); 00166 else if (cond->str() == ")") 00167 cond = cond->link()->previous(); 00168 else 00169 continue; 00170 00171 if (cond && cond->str() == "!") 00172 cond = cond->previous(); 00173 00174 if (!cond) 00175 continue; 00176 00177 // calculation 00178 if (!cond->isArithmeticalOp()) 00179 continue; 00180 00181 const std::string &op = cond->str(); 00182 cond = cond->previous(); 00183 00184 // skip previous multiplications.. 00185 while (cond && cond->previous()) { 00186 if ((cond->isName() || cond->isNumber()) && cond->previous()->str() == "*") 00187 cond = cond->tokAt(-2); 00188 else if (cond->str() == ")") 00189 cond = cond->link()->previous(); 00190 else 00191 break; 00192 } 00193 00194 if (!cond) 00195 continue; 00196 00197 // first multiplication operand 00198 if (cond->str() == ")") { 00199 clarifyCalculationError(cond, op); 00200 } else if (cond->isName() || cond->isNumber()) { 00201 if (Token::Match(cond->previous(),"return|=|+|-|,|(") || cond->strAt(-1) == op) 00202 clarifyCalculationError(cond, op); 00203 } 00204 } 00205 } 00206 } 00207 } 00208 00209 void CheckOther::clarifyCalculationError(const Token *tok, const std::string &op) 00210 { 00211 // suspicious calculation 00212 const std::string calc("'a" + op + "b?c:d'"); 00213 00214 // recommended calculation #1 00215 const std::string s1("'(a" + op + "b)?c:d'"); 00216 00217 // recommended calculation #2 00218 const std::string s2("'a" + op + "(b?c:d)'"); 00219 00220 reportError(tok, 00221 Severity::style, 00222 "clarifyCalculation", 00223 "Clarify calculation precedence for '" + op + "' and '?'.\n" 00224 "Suspicious calculation. Please use parentheses to clarify the code. " 00225 "The code '" + calc + "' should be written as either '" + s1 + "' or '" + s2 + "'."); 00226 } 00227 00228 //--------------------------------------------------------------------------- 00229 // Clarify condition '(x = a < 0)' into '((x = a) < 0)' or '(x = (a < 0))' 00230 // Clarify condition '(a & b == c)' into '((a & b) == c)' or '(a & (b == c))' 00231 //--------------------------------------------------------------------------- 00232 void CheckOther::clarifyCondition() 00233 { 00234 if (!_settings->isEnabled("style")) 00235 return; 00236 00237 const bool isC = _tokenizer->isC(); 00238 00239 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 00240 const std::size_t functions = symbolDatabase->functionScopes.size(); 00241 for (std::size_t i = 0; i < functions; ++i) { 00242 const Scope * scope = symbolDatabase->functionScopes[i]; 00243 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00244 if (Token::Match(tok, "( %var% [=&|^]")) { 00245 for (const Token *tok2 = tok->tokAt(3); tok2; tok2 = tok2->next()) { 00246 if (tok2->str() == "(" || tok2->str() == "[") 00247 tok2 = tok2->link(); 00248 else if (tok2->type() == Token::eComparisonOp) { 00249 // This might be a template 00250 if (!isC && tok2->link()) 00251 break; 00252 00253 clarifyConditionError(tok, tok->strAt(2) == "=", false); 00254 break; 00255 } else if (!tok2->isName() && !tok2->isNumber() && tok2->str() != ".") 00256 break; 00257 } 00258 } 00259 } 00260 } 00261 00262 // using boolean result in bitwise operation ! x [&|^] 00263 for (std::size_t i = 0; i < functions; ++i) { 00264 const Scope * scope = symbolDatabase->functionScopes[i]; 00265 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00266 if (Token::Match(tok, "%comp%|!")) { 00267 if (tok->link()) // don't write false positives when templates are used 00268 continue; 00269 00270 const Token *tok2 = tok->next(); 00271 00272 // Todo: There are false positives if '(' if encountered. It 00273 // is assumed there is something like '(char *)&..' and therefore 00274 // it bails out. 00275 if (Token::Match(tok2, "(|&")) 00276 continue; 00277 00278 while (tok2 && (tok2->isName() || tok2->isNumber() || Token::Match(tok2,".|(|["))) { 00279 if (Token::Match(tok2, "(|[")) 00280 tok2 = tok2->link(); 00281 tok2 = tok2->next(); 00282 } 00283 00284 if (Token::Match(tok2, "[&|^]")) { 00285 // don't write false positives when templates are used 00286 if (Token::Match(tok2, "&|* ,|>") || Token::simpleMatch(tok2->previous(), "const &")) 00287 continue; 00288 00289 // #3609 - CWinTraits<WS_CHILD|WS_VISIBLE>::.. 00290 if (!isC && Token::Match(tok->previous(), "%var% <")) { 00291 const Token *tok3 = tok2; 00292 while (Token::Match(tok3, "[&|^] %var%")) 00293 tok3 = tok3->tokAt(2); 00294 if (Token::Match(tok3, ",|>")) 00295 continue; 00296 } 00297 00298 clarifyConditionError(tok,false,true); 00299 } 00300 } 00301 } 00302 } 00303 } 00304 00305 void CheckOther::clarifyConditionError(const Token *tok, bool assign, bool boolop) 00306 { 00307 std::string errmsg; 00308 00309 if (assign) 00310 errmsg = "Suspicious condition (assignment + comparison); Clarify expression with parentheses."; 00311 00312 else if (boolop) 00313 errmsg = "Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" 00314 "Suspicious expression. Boolean result is used in bitwise operation. The operator '!' " 00315 "and the comparison operators have higher precedence than bitwise operators. " 00316 "It is recommended that the expression is clarified with parentheses."; 00317 else 00318 errmsg = "Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" 00319 "Suspicious condition. Comparison operators have higher precedence than bitwise operators. " 00320 "Please clarify the condition with parentheses."; 00321 00322 reportError(tok, 00323 Severity::style, 00324 "clarifyCondition", 00325 errmsg); 00326 } 00327 00328 //--------------------------------------------------------------------------- 00329 // Clarify (meaningless) statements like *foo++; with parentheses. 00330 //--------------------------------------------------------------------------- 00331 void CheckOther::clarifyStatement() 00332 { 00333 if (!_settings->isEnabled("warning")) 00334 return; 00335 00336 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 00337 const std::size_t functions = symbolDatabase->functionScopes.size(); 00338 for (std::size_t i = 0; i < functions; ++i) { 00339 const Scope * scope = symbolDatabase->functionScopes[i]; 00340 for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { 00341 if (Token::Match(tok, "* %var%")) { 00342 const Token *tok2=tok->previous(); 00343 00344 while (tok2 && tok2->str() == "*") 00345 tok2=tok2->previous(); 00346 00347 if (Token::Match(tok2, "[{};]")) { 00348 tok = tok->tokAt(2); 00349 for (;;) { 00350 if (tok->str() == "[") 00351 tok = tok->link()->next(); 00352 00353 if (Token::Match(tok, ".|:: %var%")) { 00354 if (tok->strAt(2) == "(") 00355 tok = tok->linkAt(2)->next(); 00356 else 00357 tok = tok->tokAt(2); 00358 } else 00359 break; 00360 } 00361 if (Token::Match(tok, "++|-- [;,]")) 00362 clarifyStatementError(tok); 00363 } 00364 } 00365 } 00366 } 00367 } 00368 00369 void CheckOther::clarifyStatementError(const Token *tok) 00370 { 00371 reportError(tok, Severity::warning, "clarifyStatement", "Ineffective statement similar to '*A++;'. Did you intend to write '(*A)++;'?\n" 00372 "A statement like '*A++;' might not do what you intended. Postfix 'operator++' is executed before 'operator*'. " 00373 "Thus, the dereference is meaningless. Did you intend to write '(*A)++;'?"); 00374 } 00375 00376 //--------------------------------------------------------------------------- 00377 // if (bool & bool) -> if (bool && bool) 00378 // if (bool | bool) -> if (bool || bool) 00379 //--------------------------------------------------------------------------- 00380 void CheckOther::checkBitwiseOnBoolean() 00381 { 00382 if (!_settings->isEnabled("style")) 00383 return; 00384 00385 // danmar: this is inconclusive because I don't like that there are 00386 // warnings for calculations. Example: set_flag(a & b); 00387 if (!_settings->inconclusive) 00388 return; 00389 00390 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 00391 const std::size_t functions = symbolDatabase->functionScopes.size(); 00392 for (std::size_t i = 0; i < functions; ++i) { 00393 const Scope * scope = symbolDatabase->functionScopes[i]; 00394 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00395 if (Token::Match(tok, "(|.|return|&&|%oror%|throw|, %var% [&|]")) { 00396 const Variable *var = tok->next()->variable(); 00397 if (var && var->typeEndToken()->str() == "bool") { 00398 bitwiseOnBooleanError(tok->next(), var->name(), tok->strAt(2) == "&" ? "&&" : "||"); 00399 tok = tok->tokAt(2); 00400 } 00401 } else if (Token::Match(tok, "[&|] %var% )|.|return|&&|%oror%|throw|,") && (!tok->previous() || !tok->previous()->isExtendedOp() || tok->strAt(-1) == ")" || tok->strAt(-1) == "]")) { 00402 const Variable *var = tok->next()->variable(); 00403 if (var && var->typeEndToken()->str() == "bool") { 00404 bitwiseOnBooleanError(tok->next(), var->name(), tok->str() == "&" ? "&&" : "||"); 00405 tok = tok->tokAt(2); 00406 } 00407 } 00408 } 00409 } 00410 } 00411 00412 void CheckOther::bitwiseOnBooleanError(const Token *tok, const std::string &varname, const std::string &op) 00413 { 00414 reportError(tok, Severity::style, "bitwiseOnBoolean", 00415 "Boolean variable '" + varname + "' is used in bitwise operation. Did you mean '" + op + "'?", true); 00416 } 00417 00418 void CheckOther::checkSuspiciousSemicolon() 00419 { 00420 if (!_settings->inconclusive || !_settings->isEnabled("warning")) 00421 return; 00422 00423 const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); 00424 00425 // Look for "if(); {}", "for(); {}" or "while(); {}" 00426 for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { 00427 if (i->type == Scope::eIf || i->type == Scope::eElse || i->type == Scope::eElseIf || i->type == Scope::eWhile || i->type == Scope::eFor) { 00428 // Ensure the semicolon is at the same line number as the if/for/while statement 00429 // and the {..} block follows it without an extra empty line. 00430 if (Token::simpleMatch(i->classStart, "{ ; } {") && 00431 i->classStart->previous()->linenr() == i->classStart->tokAt(2)->linenr() 00432 && i->classStart->linenr()+1 >= i->classStart->tokAt(3)->linenr()) { 00433 SuspiciousSemicolonError(i->classDef); 00434 } 00435 } 00436 } 00437 } 00438 00439 void CheckOther::SuspiciousSemicolonError(const Token* tok) 00440 { 00441 reportError(tok, Severity::warning, "suspiciousSemicolon", 00442 "Suspicious use of ; at the end of '" + (tok ? tok->str() : std::string()) + "' statement.", true); 00443 } 00444 00445 00446 //--------------------------------------------------------------------------- 00447 //--------------------------------------------------------------------------- 00448 void CheckOther::warningOldStylePointerCast() 00449 { 00450 // Only valid on C++ code 00451 if (!_settings->isEnabled("style") || !_tokenizer->isCPP()) 00452 return; 00453 00454 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 00455 // Old style pointer casting.. 00456 if (!Token::Match(tok, "( const| %type% * ) (| %var%") && 00457 !Token::Match(tok, "( const| %type% * ) (| new")) 00458 continue; 00459 00460 if (tok->strAt(1) == "const") 00461 tok = tok->next(); 00462 00463 if (tok->strAt(4) == "const") 00464 continue; 00465 00466 // Is "type" a class? 00467 const std::string pattern("class|struct " + tok->strAt(1)); 00468 if (Token::findmatch(_tokenizer->tokens(), pattern.c_str(), tok)) 00469 cstyleCastError(tok); 00470 } 00471 } 00472 00473 void CheckOther::cstyleCastError(const Token *tok) 00474 { 00475 reportError(tok, Severity::style, "cstyleCast", "C-style pointer casting"); 00476 } 00477 00478 //--------------------------------------------------------------------------- 00479 // float* f; double* d = (double*)f; <-- Pointer cast to a type with an incompatible binary data representation 00480 //--------------------------------------------------------------------------- 00481 00482 static std::string analyzeType(const Token* tok) 00483 { 00484 if (tok->str() == "double") { 00485 if (tok->isLong()) 00486 return "long double"; 00487 else 00488 return "double"; 00489 } 00490 if (tok->str() == "float") 00491 return "float"; 00492 if (Token::Match(tok, "int|long|short|char|size_t")) 00493 return "integer"; 00494 return ""; 00495 } 00496 00497 void CheckOther::invalidPointerCast() 00498 { 00499 if (!_settings->isEnabled("warning") && !_settings->isEnabled("portability")) 00500 return; 00501 00502 const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); 00503 const std::size_t functions = symbolDatabase->functionScopes.size(); 00504 for (std::size_t i = 0; i < functions; ++i) { 00505 const Scope * scope = symbolDatabase->functionScopes[i]; 00506 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00507 const Token* toTok = 0; 00508 const Token* nextTok = 0; 00509 // Find cast 00510 if (Token::Match(tok, "( const| %type% const| * )") || 00511 Token::Match(tok, "( const| %type% %type% const| * )")) { 00512 toTok = tok->next(); 00513 nextTok = tok->link()->next(); 00514 if (nextTok && nextTok->str() == "(") 00515 nextTok = nextTok->next(); 00516 } else if (Token::Match(tok, "reinterpret_cast < const| %type% const| * > (") || 00517 Token::Match(tok, "reinterpret_cast < const| %type% %type% const| * > (")) { 00518 nextTok = tok->tokAt(5); 00519 while (nextTok->str() != "(") 00520 nextTok = nextTok->next(); 00521 nextTok = nextTok->next(); 00522 toTok = tok->tokAt(2); 00523 } 00524 if (toTok && toTok->str() == "const") 00525 toTok = toTok->next(); 00526 00527 if (!nextTok || !toTok || !toTok->isStandardType()) 00528 continue; 00529 00530 // Find casted variable 00531 const Variable *var = 0; 00532 bool allocation = false; 00533 bool ref = false; 00534 if (Token::Match(nextTok, "new %type%")) 00535 allocation = true; 00536 else if (Token::Match(nextTok, "%var% !![")) 00537 var = nextTok->variable(); 00538 else if (Token::Match(nextTok, "& %var%") && !Token::Match(nextTok->tokAt(2), "(|[")) { 00539 var = nextTok->next()->variable(); 00540 ref = true; 00541 } 00542 00543 const Token* fromTok = 0; 00544 00545 if (allocation) { 00546 fromTok = nextTok->next(); 00547 } else { 00548 if (!var || (!ref && !var->isPointer() && !var->isArray()) || (ref && (var->isPointer() || var->isArray()))) 00549 continue; 00550 fromTok = var->typeStartToken(); 00551 } 00552 00553 while (Token::Match(fromTok, "static|const")) 00554 fromTok = fromTok->next(); 00555 if (!fromTok->isStandardType()) 00556 continue; 00557 00558 std::string fromType = analyzeType(fromTok); 00559 std::string toType = analyzeType(toTok); 00560 if (fromType != toType && !fromType.empty() && !toType.empty() && (toType != "integer" || _settings->isEnabled("portability")) && (toTok->str() != "char" || _settings->inconclusive)) 00561 invalidPointerCastError(tok, fromType, toType, toTok->str() == "char"); 00562 } 00563 } 00564 } 00565 00566 void CheckOther::invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive) 00567 { 00568 if (to == "integer") { // If we cast something to int*, this can be useful to play with its binary data representation 00569 if (!inconclusive) 00570 reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + "* to integer* is not portable due to different binary data representations on different platforms."); 00571 else 00572 reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + "* to char* is not portable due to different binary data representations on different platforms.", true); 00573 } else 00574 reportError(tok, Severity::warning, "invalidPointerCast", "Casting between " + from + "* and " + to + "* which have an incompatible binary data representation."); 00575 } 00576 00577 //--------------------------------------------------------------------------- 00578 //--------------------------------------------------------------------------- 00579 void CheckOther::checkSizeofForNumericParameter() 00580 { 00581 if (!_settings->isEnabled("warning")) 00582 return; 00583 00584 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 00585 const std::size_t functions = symbolDatabase->functionScopes.size(); 00586 for (std::size_t i = 0; i < functions; ++i) { 00587 const Scope * scope = symbolDatabase->functionScopes[i]; 00588 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00589 if (Token::Match(tok, "sizeof ( %num% )") || 00590 Token::Match(tok, "sizeof %num%")) { 00591 sizeofForNumericParameterError(tok); 00592 } 00593 } 00594 } 00595 } 00596 00597 void CheckOther::sizeofForNumericParameterError(const Token *tok) 00598 { 00599 reportError(tok, Severity::warning, 00600 "sizeofwithnumericparameter", "Suspicious usage of 'sizeof' with a numeric constant as parameter.\n" 00601 "It is unusual to use a constant value with sizeof. For example, 'sizeof(10)'" 00602 " returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 10. 'sizeof('A')'" 00603 " and 'sizeof(char)' can return different results."); 00604 } 00605 00606 //--------------------------------------------------------------------------- 00607 // This check detects errors on POSIX systems, when a pipe command called 00608 // with a wrong dimensioned file descriptor array. The pipe command requires 00609 // exactly an integer array of dimension two as parameter. 00610 // 00611 // References: 00612 // - http://linux.die.net/man/2/pipe 00613 // - ticket #3521 00614 //--------------------------------------------------------------------------- 00615 void CheckOther::checkPipeParameterSize() 00616 { 00617 if (!_settings->standards.posix) 00618 return; 00619 00620 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 00621 const std::size_t functions = symbolDatabase->functionScopes.size(); 00622 for (std::size_t i = 0; i < functions; ++i) { 00623 const Scope * scope = symbolDatabase->functionScopes[i]; 00624 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00625 if (Token::Match(tok, "pipe ( %var% )") || 00626 Token::Match(tok, "pipe2 ( %var% ,")) { 00627 const Token * const varTok = tok->tokAt(2); 00628 00629 const Variable *var = varTok->variable(); 00630 MathLib::bigint dim; 00631 if (var && (var->isArray() || var->isPointer()) && ((dim=var->dimension(0U)) < 2)) { 00632 const std::string strDim = MathLib::longToString(dim); 00633 checkPipeParameterSizeError(varTok,varTok->str(), strDim); 00634 } 00635 } 00636 } 00637 } 00638 } 00639 00640 void CheckOther::checkPipeParameterSizeError(const Token *tok, const std::string &strVarName, const std::string &strDim) 00641 { 00642 reportError(tok, Severity::error, 00643 "wrongPipeParameterSize", "Buffer '" + strVarName + "' must have size of 2 integers if used as parameter of pipe().\n" 00644 "The pipe()/pipe2() system command takes an argument, which is an array of exactly two integers.\n" 00645 "The variable '" + strVarName + "' is an array of size " + strDim + ", which does not match."); 00646 } 00647 00648 //----------------------------------------------------------------------------- 00649 // check usleep(), which is allowed to be called with in a range of [0,999999] 00650 // 00651 // Reference: 00652 // - http://man7.org/linux/man-pages/man3/usleep.3.html 00653 //----------------------------------------------------------------------------- 00654 void CheckOther::checkSleepTimeInterval() 00655 { 00656 if (!_settings->standards.posix) 00657 return; 00658 00659 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 00660 const std::size_t functions = symbolDatabase->functionScopes.size(); 00661 for (std::size_t i = 0; i < functions; ++i) { 00662 const Scope * scope = symbolDatabase->functionScopes[i]; 00663 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00664 if (Token::Match(tok, "usleep ( %num% )")) { 00665 const Token * const numTok = tok->tokAt(2); 00666 MathLib::bigint value = MathLib::toLongNumber(numTok->str()); 00667 if (value > 999999) { // less than 1 million 00668 checkSleepTimeError(numTok, numTok->str()); 00669 } 00670 } 00671 } 00672 } 00673 } 00674 00675 void CheckOther::checkSleepTimeError(const Token *tok, const std::string &strDim) 00676 { 00677 reportError(tok, Severity::error, 00678 "tooBigSleepTime", "The argument of usleep must be less than 1000000.\n" 00679 "The argument of usleep must be less than 1000000, but " + strDim + " is provided."); 00680 } 00681 00682 //--------------------------------------------------------------------------- 00683 //--------------------------------------------------------------------------- 00684 void CheckOther::checkSizeofForArrayParameter() 00685 { 00686 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 00687 const std::size_t functions = symbolDatabase->functionScopes.size(); 00688 for (std::size_t i = 0; i < functions; ++i) { 00689 const Scope * scope = symbolDatabase->functionScopes[i]; 00690 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00691 if (Token::Match(tok, "sizeof ( %var% )") || 00692 Token::Match(tok, "sizeof %var% !![")) { 00693 const Token* varTok = tok->next(); 00694 if (varTok->str() == "(") { 00695 varTok = varTok->next(); 00696 } 00697 if (varTok->varId() > 0) { 00698 const Variable *var = varTok->variable(); 00699 if (var && var->isArray() && var->isArgument()) { 00700 sizeofForArrayParameterError(tok); 00701 } 00702 } 00703 } 00704 } 00705 } 00706 } 00707 00708 void CheckOther::sizeofForArrayParameterError(const Token *tok) 00709 { 00710 reportError(tok, Severity::error, 00711 "sizeofwithsilentarraypointer", "Using 'sizeof' on array given as function argument " 00712 "returns size of a pointer.\n" 00713 "Using 'sizeof' for array given as function argument returns the size of a pointer. " 00714 "It does not return the size of the whole array in bytes as might be " 00715 "expected. For example, this code:\n" 00716 " int f(char a[100]) {\n" 00717 " return sizeof(a);\n" 00718 " }\n" 00719 "returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 100 (the " 00720 "size of the array in bytes)." 00721 ); 00722 } 00723 00724 void CheckOther::checkSizeofForPointerSize() 00725 { 00726 if (!_settings->isEnabled("warning")) 00727 return; 00728 00729 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 00730 const std::size_t functions = symbolDatabase->functionScopes.size(); 00731 for (std::size_t i = 0; i < functions; ++i) { 00732 const Scope * scope = symbolDatabase->functionScopes[i]; 00733 for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { 00734 const Token *tokVar; 00735 const Token *variable; 00736 const Token *variable2 = 0; 00737 00738 // Find any function that may use sizeof on a pointer 00739 // Once leaving those tests, it is mandatory to have: 00740 // - variable matching the used pointer 00741 // - tokVar pointing on the argument where sizeof may be used 00742 if (Token::Match(tok, "[*;{}] %var% = malloc|alloca (")) { 00743 variable = tok->next(); 00744 tokVar = tok->tokAt(5); 00745 00746 } else if (Token::Match(tok, "[*;{}] %var% = calloc (")) { 00747 variable = tok->next(); 00748 tokVar = tok->tokAt(5)->nextArgument(); 00749 00750 } else if (Token::simpleMatch(tok, "memset (")) { 00751 variable = tok->tokAt(2); 00752 tokVar = variable->tokAt(2)->nextArgument(); 00753 00754 // The following tests can be inconclusive in case the variable in sizeof 00755 // is constant string by intention 00756 } else if (!_settings->inconclusive) { 00757 continue; 00758 00759 } else if (Token::Match(tok, "memcpy|memcmp|memmove|strncpy|strncmp|strncat (")) { 00760 variable = tok->tokAt(2); 00761 variable2 = variable->nextArgument(); 00762 tokVar = variable2->nextArgument(); 00763 00764 } else { 00765 continue; 00766 } 00767 00768 // Ensure the variables are in the symbol database 00769 // Also ensure the variables are pointers 00770 // Only keep variables which are pointers 00771 const Variable *var = variable->variable(); 00772 if (!var || !var->isPointer() || var->isArray()) { 00773 variable = 0; 00774 } 00775 00776 if (variable2) { 00777 var = variable2->variable(); 00778 if (!var || !var->isPointer() || var->isArray()) { 00779 variable2 = 0; 00780 } 00781 } 00782 00783 // If there are no pointer variable at this point, there is 00784 // no need to continue 00785 if (variable == 0 && variable2 == 0) { 00786 continue; 00787 } 00788 00789 // Jump to the next sizeof token in the function and in the parameter 00790 // This is to allow generic operations with sizeof 00791 for (; tokVar && tokVar->str() != ")" && tokVar->str() != "," && tokVar->str() != "sizeof"; tokVar = tokVar->next()) {} 00792 00793 // Now check for the sizeof usage. Once here, everything using sizeof(varid) or sizeof(&varid) 00794 // looks suspicious 00795 // Do it for first variable 00796 if (variable && (Token::Match(tokVar, "sizeof ( &| %varid% )", variable->varId()) || 00797 Token::Match(tokVar, "sizeof &| %varid%", variable->varId()))) { 00798 sizeofForPointerError(variable, variable->str()); 00799 } else if (variable2 && (Token::Match(tokVar, "sizeof ( &| %varid% )", variable2->varId()) || 00800 Token::Match(tokVar, "sizeof &| %varid%", variable2->varId()))) { 00801 sizeofForPointerError(variable2, variable2->str()); 00802 } 00803 } 00804 } 00805 } 00806 00807 void CheckOther::sizeofForPointerError(const Token *tok, const std::string &varname) 00808 { 00809 reportError(tok, Severity::warning, "pointerSize", 00810 "Size of pointer '" + varname + "' used instead of size of its data.\n" 00811 "Size of pointer '" + varname + "' used instead of size of its data. " 00812 "This is likely to lead to a buffer overflow. You probably intend to " 00813 "write 'sizeof(*" + varname + ")'.", true); 00814 } 00815 00816 //--------------------------------------------------------------------------- 00817 // Detect redundant assignments: x = 0; x = 4; 00818 //--------------------------------------------------------------------------- 00819 00820 static bool nonLocal(const Variable* var) 00821 { 00822 return !var || (!var->isLocal() && !var->isArgument()) || var->isStatic() || var->isReference(); 00823 } 00824 00825 static void eraseNotLocalArg(std::map<unsigned int, const Token*>& container, const SymbolDatabase* symbolDatabase) 00826 { 00827 for (std::map<unsigned int, const Token*>::iterator i = container.begin(); i != container.end();) { 00828 const Variable* var = symbolDatabase->getVariableFromVarId(i->first); 00829 if (!var || nonLocal(var)) { 00830 container.erase(i++); 00831 if (i == container.end()) 00832 break; 00833 } else 00834 ++i; 00835 } 00836 } 00837 00838 void CheckOther::checkRedundantAssignment() 00839 { 00840 bool performance = _settings->isEnabled("performance"); 00841 bool warning = _settings->isEnabled("warning"); 00842 if (!warning && !performance) 00843 return; 00844 00845 const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); 00846 00847 for (std::list<Scope>::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { 00848 if (!scope->isExecutable()) 00849 continue; 00850 00851 std::map<unsigned int, const Token*> varAssignments; 00852 std::map<unsigned int, const Token*> memAssignments; 00853 const Token* writtenArgumentsEnd = 0; 00854 00855 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 00856 if (tok == writtenArgumentsEnd) 00857 writtenArgumentsEnd = 0; 00858 00859 if (tok->str() == "{" && tok->strAt(-1) != "{" && tok->strAt(-1) != "=" && tok->strAt(-4) != "case" && tok->strAt(-3) != "default") { // conditional or non-executable inner scope: Skip it and reset status 00860 tok = tok->link(); 00861 varAssignments.clear(); 00862 memAssignments.clear(); 00863 } else if (Token::Match(tok, "for|if|while (")) { 00864 tok = tok->linkAt(1); 00865 } else if (Token::Match(tok, "break|return|continue|throw|goto")) { 00866 varAssignments.clear(); 00867 memAssignments.clear(); 00868 } else if (tok->type() == Token::eVariable) { 00869 std::map<unsigned int, const Token*>::iterator it = varAssignments.find(tok->varId()); 00870 if (tok->next()->isAssignmentOp() && Token::Match(tok->previous(), "[;{}]")) { // Assignment 00871 if (it != varAssignments.end()) { 00872 bool error = true; // Ensure that variable is not used on right side 00873 for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { 00874 if (tok2->str() == ";") 00875 break; 00876 else if (tok2->varId() == tok->varId()) 00877 error = false; 00878 else if (Token::Match(tok2, "%var% (") && nonLocal(tok->variable())) { // Called function might use the variable 00879 const Function* func = symbolDatabase->findFunction(tok2); 00880 const Variable* var = tok->variable(); 00881 if (!var || var->isGlobal() || var->isReference() || ((!func || func->nestedIn) && tok2->strAt(-1) != ".")) // Global variable, or member function 00882 error = false; 00883 } 00884 } 00885 if (error) { 00886 if (scope->type == Scope::eSwitch && Token::findmatch(it->second, "default|case", tok) && warning) 00887 redundantAssignmentInSwitchError(it->second, tok, tok->str()); 00888 else if (performance) 00889 redundantAssignmentError(it->second, tok, tok->str(), nonLocal(it->second->variable())); // Inconclusive for non-local variables 00890 } 00891 it->second = tok; 00892 } 00893 if (Token::simpleMatch(tok->tokAt(2), "0 ;")) 00894 varAssignments.erase(tok->varId()); 00895 else 00896 varAssignments[tok->varId()] = tok; 00897 memAssignments.erase(tok->varId()); 00898 } else if (tok->next()->type() == Token::eIncDecOp || (tok->previous()->type() == Token::eIncDecOp && !Token::Match(tok->next(), ".|[|("))) { // Variable incremented/decremented 00899 varAssignments[tok->varId()] = tok; 00900 memAssignments.erase(tok->varId()); 00901 } else if (!Token::simpleMatch(tok->tokAt(-2), "sizeof (")) { // Other usage of variable 00902 if (it != varAssignments.end()) 00903 varAssignments.erase(it); 00904 if (!writtenArgumentsEnd) // Indicates that we are in the first argument of strcpy/memcpy/... function 00905 memAssignments.erase(tok->varId()); 00906 } 00907 } else if (Token::Match(tok, "%var% (")) { // Function call. Global variables might be used. Reset their status 00908 bool memfunc = Token::Match(tok, "memcpy|memmove|memset|strcpy|strncpy|sprintf|snprintf|strcat|strncat|wcscpy|wcsncpy|swprintf|wcscat|wcsncat"); 00909 if (memfunc) { 00910 const Token* param1 = tok->tokAt(2); 00911 writtenArgumentsEnd = param1->next(); 00912 if (param1->varId() && param1->strAt(1) == "," && !Token::Match(tok, "strcat|strncat|wcscat|wcsncat")) { 00913 std::map<unsigned int, const Token*>::iterator it = memAssignments.find(param1->varId()); 00914 if (it == memAssignments.end()) 00915 memAssignments[param1->varId()] = tok; 00916 else { 00917 if (scope->type == Scope::eSwitch && Token::findmatch(it->second, "default|case", tok) && warning) 00918 redundantCopyInSwitchError(it->second, tok, param1->str()); 00919 else if (performance) 00920 redundantCopyError(it->second, tok, param1->str()); 00921 } 00922 } 00923 } else if (scope->type == Scope::eSwitch) { // Avoid false positives if noreturn function is called in switch 00924 const Function* func = tok->function(); 00925 if (!func || !func->hasBody) { 00926 varAssignments.clear(); 00927 memAssignments.clear(); 00928 continue; 00929 } 00930 const Token* funcEnd = func->functionScope->classEnd; 00931 bool noreturn; 00932 if (!_tokenizer->IsScopeNoReturn(funcEnd, &noreturn) && !noreturn) { 00933 eraseNotLocalArg(varAssignments, symbolDatabase); 00934 eraseNotLocalArg(memAssignments, symbolDatabase); 00935 } else { 00936 varAssignments.clear(); 00937 memAssignments.clear(); 00938 } 00939 } else { // Noreturn functions outside switch don't cause problems 00940 eraseNotLocalArg(varAssignments, symbolDatabase); 00941 eraseNotLocalArg(memAssignments, symbolDatabase); 00942 } 00943 } 00944 } 00945 } 00946 } 00947 00948 void CheckOther::redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var) 00949 { 00950 std::list<const Token*> callstack; 00951 callstack.push_back(tok1); 00952 callstack.push_back(tok2); 00953 reportError(callstack, Severity::performance, "redundantCopy", 00954 "Buffer '" + var + "' is being written before its old content has been used."); 00955 } 00956 00957 void CheckOther::redundantCopyInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) 00958 { 00959 std::list<const Token*> callstack; 00960 callstack.push_back(tok1); 00961 callstack.push_back(tok2); 00962 reportError(callstack, Severity::warning, "redundantCopyInSwitch", 00963 "Buffer '" + var + "' is being written before its old content has been used. 'break;' missing?"); 00964 } 00965 00966 void CheckOther::redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) 00967 { 00968 std::list<const Token*> callstack; 00969 callstack.push_back(tok1); 00970 callstack.push_back(tok2); 00971 if (inconclusive) 00972 reportError(callstack, Severity::performance, "redundantAssignment", 00973 "Variable '" + var + "' is reassigned a value before the old one has been used if variable is no semaphore variable.\n" 00974 "Variable '" + var + "' is reassigned a value before the old one has been used. Make sure that this variable is not used like a semaphore in a threading environment before simplifying this code.", true); 00975 else 00976 reportError(callstack, Severity::performance, "redundantAssignment", 00977 "Variable '" + var + "' is reassigned a value before the old one has been used."); 00978 } 00979 00980 void CheckOther::redundantAssignmentInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) 00981 { 00982 std::list<const Token*> callstack; 00983 callstack.push_back(tok1); 00984 callstack.push_back(tok2); 00985 reportError(callstack, Severity::warning, "redundantAssignInSwitch", 00986 "Variable '" + var + "' is reassigned a value before the old one has been used. 'break;' missing?"); 00987 } 00988 00989 00990 //--------------------------------------------------------------------------- 00991 // switch (x) 00992 // { 00993 // case 2: 00994 // y = a; // <- this assignment is redundant 00995 // case 3: 00996 // y = b; // <- case 2 falls through and sets y twice 00997 // } 00998 //--------------------------------------------------------------------------- 00999 static inline bool isFunctionOrBreakPattern(const Token *tok) 01000 { 01001 if (Token::Match(tok, "%var% (") || Token::Match(tok, "break|continue|return|exit|goto|throw")) 01002 return true; 01003 01004 return false; 01005 } 01006 01007 void CheckOther::checkRedundantAssignmentInSwitch() 01008 { 01009 if (!_settings->isEnabled("warning")) 01010 return; 01011 01012 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 01013 01014 // Find the beginning of a switch. E.g.: 01015 // switch (var) { ... 01016 for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { 01017 if (i->type != Scope::eSwitch || !i->classStart) 01018 continue; 01019 01020 // Check the contents of the switch statement 01021 std::map<unsigned int, const Token*> varsWithBitsSet; 01022 std::map<unsigned int, std::string> bitOperations; 01023 01024 for (const Token *tok2 = i->classStart->next(); tok2 != i->classEnd; tok2 = tok2->next()) { 01025 if (tok2->str() == "{") { 01026 // Inside a conditional or loop. Don't mark variable accesses as being redundant. E.g.: 01027 // case 3: b = 1; 01028 // case 4: if (a) { b = 2; } // Doesn't make the b=1 redundant because it's conditional 01029 if (Token::Match(tok2->previous(), ")|else {") && tok2->link()) { 01030 const Token* endOfConditional = tok2->link(); 01031 for (const Token* tok3 = tok2; tok3 != endOfConditional; tok3 = tok3->next()) { 01032 if (tok3->varId() != 0) { 01033 varsWithBitsSet.erase(tok3->varId()); 01034 bitOperations.erase(tok3->varId()); 01035 } else if (isFunctionOrBreakPattern(tok3)) { 01036 varsWithBitsSet.clear(); 01037 bitOperations.clear(); 01038 } 01039 } 01040 tok2 = endOfConditional; 01041 } 01042 } 01043 01044 // Variable assignment. Report an error if it's assigned to twice before a break. E.g.: 01045 // case 3: b = 1; // <== redundant 01046 // case 4: b = 2; 01047 01048 if (Token::Match(tok2->previous(), ";|{|}|: %var% = %any% ;") && tok2->varId() != 0) { 01049 varsWithBitsSet.erase(tok2->varId()); 01050 bitOperations.erase(tok2->varId()); 01051 } 01052 01053 // Bitwise operation. Report an error if it's performed twice before a break. E.g.: 01054 // case 3: b |= 1; // <== redundant 01055 // case 4: b |= 1; 01056 else if (Token::Match(tok2->previous(), ";|{|}|: %var% = %var% %or%|& %num% ;") && 01057 tok2->varId() != 0 && tok2->varId() == tok2->tokAt(2)->varId()) { 01058 std::string bitOp = tok2->strAt(3) + tok2->strAt(4); 01059 std::map<unsigned int, const Token*>::iterator i2 = varsWithBitsSet.find(tok2->varId()); 01060 01061 // This variable has not had a bit operation performed on it yet, so just make a note of it 01062 if (i2 == varsWithBitsSet.end()) { 01063 varsWithBitsSet[tok2->varId()] = tok2; 01064 bitOperations[tok2->varId()] = bitOp; 01065 } 01066 01067 // The same bit operation has been performed on the same variable twice, so report an error 01068 else if (bitOperations[tok2->varId()] == bitOp) 01069 redundantBitwiseOperationInSwitchError(i2->second, i2->second->str()); 01070 01071 // A different bit operation was performed on the variable, so clear it 01072 else { 01073 varsWithBitsSet.erase(tok2->varId()); 01074 bitOperations.erase(tok2->varId()); 01075 } 01076 } 01077 01078 // Not a simple assignment so there may be good reason if this variable is assigned to twice. E.g.: 01079 // case 3: b = 1; 01080 // case 4: b++; 01081 else if (tok2->varId() != 0 && tok2->strAt(1) != "|" && tok2->strAt(1) != "&") { 01082 varsWithBitsSet.erase(tok2->varId()); 01083 bitOperations.erase(tok2->varId()); 01084 } 01085 01086 // Reset our record of assignments if there is a break or function call. E.g.: 01087 // case 3: b = 1; break; 01088 if (isFunctionOrBreakPattern(tok2)) { 01089 varsWithBitsSet.clear(); 01090 bitOperations.clear(); 01091 } 01092 } 01093 } 01094 } 01095 01096 void CheckOther::redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname) 01097 { 01098 reportError(tok, Severity::warning, 01099 "redundantBitwiseOperationInSwitch", "Redundant bitwise operation on '" + varname + "' in 'switch' statement. 'break;' missing?"); 01100 } 01101 01102 01103 //--------------------------------------------------------------------------- 01104 //--------------------------------------------------------------------------- 01105 void CheckOther::checkSwitchCaseFallThrough() 01106 { 01107 if (!(_settings->isEnabled("style") && _settings->experimental)) 01108 return; 01109 01110 const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); 01111 01112 for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { 01113 if (i->type != Scope::eSwitch || !i->classStart) // Find the beginning of a switch 01114 continue; 01115 01116 // Check the contents of the switch statement 01117 std::stack<std::pair<Token *, bool> > ifnest; 01118 std::stack<Token *> loopnest; 01119 std::stack<Token *> scopenest; 01120 bool justbreak = true; 01121 bool firstcase = true; 01122 for (const Token *tok2 = i->classStart; tok2 != i->classEnd; tok2 = tok2->next()) { 01123 if (Token::simpleMatch(tok2, "if (")) { 01124 tok2 = tok2->next()->link()->next(); 01125 if (tok2->link() == NULL) { 01126 std::ostringstream errmsg; 01127 errmsg << "unmatched if in switch: " << tok2->linenr(); 01128 reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); 01129 break; 01130 } 01131 ifnest.push(std::make_pair(tok2->link(), false)); 01132 justbreak = false; 01133 } else if (Token::simpleMatch(tok2, "while (")) { 01134 tok2 = tok2->next()->link()->next(); 01135 // skip over "do { } while ( ) ;" case 01136 if (tok2->str() == "{") { 01137 if (tok2->link() == NULL) { 01138 std::ostringstream errmsg; 01139 errmsg << "unmatched while in switch: " << tok2->linenr(); 01140 reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); 01141 break; 01142 } 01143 loopnest.push(tok2->link()); 01144 } 01145 justbreak = false; 01146 } else if (Token::simpleMatch(tok2, "do {")) { 01147 tok2 = tok2->next(); 01148 if (tok2->link() == NULL) { 01149 std::ostringstream errmsg; 01150 errmsg << "unmatched do in switch: " << tok2->linenr(); 01151 reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); 01152 break; 01153 } 01154 loopnest.push(tok2->link()); 01155 justbreak = false; 01156 } else if (Token::simpleMatch(tok2, "for (")) { 01157 tok2 = tok2->next()->link()->next(); 01158 if (tok2->link() == NULL) { 01159 std::ostringstream errmsg; 01160 errmsg << "unmatched for in switch: " << tok2->linenr(); 01161 reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); 01162 break; 01163 } 01164 loopnest.push(tok2->link()); 01165 justbreak = false; 01166 } else if (Token::simpleMatch(tok2, "switch (")) { 01167 // skip over nested switch, we'll come to that soon 01168 tok2 = tok2->next()->link()->next()->link(); 01169 } else if (Token::Match(tok2, "break|continue|return|exit|goto|throw")) { 01170 if (loopnest.empty()) { 01171 justbreak = true; 01172 } 01173 tok2 = Token::findsimplematch(tok2, ";"); 01174 } else if (Token::Match(tok2, "case|default")) { 01175 if (!justbreak && !firstcase) { 01176 switchCaseFallThrough(tok2); 01177 } 01178 tok2 = Token::findsimplematch(tok2, ":"); 01179 justbreak = true; 01180 firstcase = false; 01181 } else if (tok2->str() == "{") { 01182 scopenest.push(tok2->link()); 01183 } else if (tok2->str() == "}") { 01184 if (!ifnest.empty() && tok2 == ifnest.top().first) { 01185 if (tok2->next()->str() == "else") { 01186 tok2 = tok2->tokAt(2); 01187 ifnest.pop(); 01188 if (tok2->link() == NULL) { 01189 std::ostringstream errmsg; 01190 errmsg << "unmatched if in switch: " << tok2->linenr(); 01191 reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); 01192 break; 01193 } 01194 ifnest.push(std::make_pair(tok2->link(), justbreak)); 01195 justbreak = false; 01196 } else { 01197 justbreak &= ifnest.top().second; 01198 ifnest.pop(); 01199 } 01200 } else if (!loopnest.empty() && tok2 == loopnest.top()) { 01201 loopnest.pop(); 01202 } else if (!scopenest.empty() && tok2 == scopenest.top()) { 01203 scopenest.pop(); 01204 } else { 01205 if (!ifnest.empty() || !loopnest.empty() || !scopenest.empty()) { 01206 std::ostringstream errmsg; 01207 errmsg << "unexpected end of switch: "; 01208 errmsg << "ifnest=" << ifnest.size(); 01209 if (!ifnest.empty()) 01210 errmsg << "," << ifnest.top().first->linenr(); 01211 errmsg << ", loopnest=" << loopnest.size(); 01212 if (!loopnest.empty()) 01213 errmsg << "," << loopnest.top()->linenr(); 01214 errmsg << ", scopenest=" << scopenest.size(); 01215 if (!scopenest.empty()) 01216 errmsg << "," << scopenest.top()->linenr(); 01217 reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); 01218 } 01219 // end of switch block 01220 break; 01221 } 01222 } else if (tok2->str() != ";") { 01223 justbreak = false; 01224 } 01225 01226 } 01227 } 01228 } 01229 01230 void CheckOther::switchCaseFallThrough(const Token *tok) 01231 { 01232 reportError(tok, Severity::style, 01233 "switchCaseFallThrough", "Switch falls through case without comment. 'break;' missing?"); 01234 } 01235 01236 01237 //--------------------------------------------------------------------------- 01238 // Check for statements like case A||B: in switch() 01239 //--------------------------------------------------------------------------- 01240 void CheckOther::checkSuspiciousCaseInSwitch() 01241 { 01242 if (!_settings->inconclusive || !_settings->isEnabled("warning")) 01243 return; 01244 01245 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 01246 01247 for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { 01248 if (i->type != Scope::eSwitch) 01249 continue; 01250 01251 for (const Token* tok = i->classStart->next(); tok != i->classEnd; tok = tok->next()) { 01252 if (tok->str() == "case") { 01253 const Token* end = 0; 01254 for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) { 01255 if (tok2->str() == ":") { 01256 end = tok2; 01257 break; 01258 } 01259 if (Token::Match(tok2, "[?;}{]")) { 01260 break; 01261 } 01262 } 01263 01264 if (end) { 01265 const Token* finding = Token::findmatch(tok->next(), "&&|%oror%", end); 01266 if (finding) 01267 suspiciousCaseInSwitchError(tok, finding->str()); 01268 } 01269 } 01270 } 01271 } 01272 } 01273 01274 void CheckOther::suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString) 01275 { 01276 reportError(tok, Severity::warning, "suspiciousCase", 01277 "Found suspicious case label in switch(). Operator '" + operatorString + "' probably doesn't work as intended.\n" 01278 "Using an operator like '" + operatorString + "' in a case label is suspicious. Did you intend to use a bitwise operator, multiple case labels or if/else instead?", true); 01279 } 01280 01281 //--------------------------------------------------------------------------- 01282 // if (x == 1) 01283 // x == 0; // <- suspicious equality comparison. 01284 //--------------------------------------------------------------------------- 01285 void CheckOther::checkSuspiciousEqualityComparison() 01286 { 01287 if (!_settings->isEnabled("warning")) 01288 return; 01289 01290 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 01291 01292 if (Token::simpleMatch(tok, "for (")) { 01293 const Token* const openParen = tok->next(); 01294 const Token* const closeParen = tok->linkAt(1); 01295 01296 // Search for any suspicious equality comparison in the initialization 01297 // or increment-decrement parts of the for() loop. 01298 // For example: 01299 // for (i == 2; i < 10; i++) 01300 // or 01301 // for (i = 0; i < 10; i == a) 01302 const Token* tok2 = Token::findmatch(openParen, "[;(] %var% == %any% [;)]", closeParen); 01303 if (tok2 && (tok2 == openParen || tok2->tokAt(4) == closeParen)) { 01304 suspiciousEqualityComparisonError(tok2->tokAt(2)); 01305 } 01306 01307 // Equality comparisons with 0 are simplified to negation. For instance, 01308 // (x == 0) is simplified to (!x), so also check for suspicious negation 01309 // in the initialization or increment-decrement parts of the for() loop. 01310 // For example: 01311 // for (!i; i < 10; i++) 01312 const Token* tok3 = Token::findmatch(openParen, "[;(] ! %var% [;)]", closeParen); 01313 if (tok3 && (tok3 == openParen || tok3->tokAt(3) == closeParen)) { 01314 suspiciousEqualityComparisonError(tok3->tokAt(2)); 01315 } 01316 01317 // Skip over for() loop conditions because "for (;running==1;)" 01318 // is a bit strange, but not necessarily incorrect. 01319 tok = closeParen; 01320 } else if (Token::Match(tok, "[;{}] *| %var% == %any% ;")) { 01321 01322 // Exclude compound statements surrounded by parentheses, such as 01323 // printf("%i\n", ({x==0;})); 01324 // because they may appear as an expression in GNU C/C++. 01325 // See http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html 01326 const Token* afterStatement = tok->strAt(1) == "*" ? tok->tokAt(6) : tok->tokAt(5); 01327 if (!Token::simpleMatch(afterStatement, "} )")) 01328 suspiciousEqualityComparisonError(tok->next()); 01329 } 01330 } 01331 } 01332 01333 void CheckOther::suspiciousEqualityComparisonError(const Token* tok) 01334 { 01335 reportError(tok, Severity::warning, "suspiciousEqualityComparison", 01336 "Found suspicious equality comparison. Did you intend to assign a value instead?", true); 01337 } 01338 01339 01340 //--------------------------------------------------------------------------- 01341 // int x = 1; 01342 // x = x; // <- redundant assignment to self 01343 // 01344 // int y = y; // <- redundant initialization to self 01345 //--------------------------------------------------------------------------- 01346 static bool isTypeWithoutSideEffects(const Tokenizer *tokenizer, const Variable* var) 01347 { 01348 return ((var && (!var->isClass() || var->isPointer() || Token::simpleMatch(var->typeStartToken(), "std ::"))) || !tokenizer->isCPP()); 01349 } 01350 01351 static inline const Token *findSelfAssignPattern(const Token *start) 01352 { 01353 return Token::findmatch(start, "%var% = %var% ;|=|)"); 01354 } 01355 01356 void CheckOther::checkSelfAssignment() 01357 { 01358 if (!_settings->isEnabled("warning")) 01359 return; 01360 01361 const Token *tok = findSelfAssignPattern(_tokenizer->tokens()); 01362 while (tok) { 01363 if (Token::Match(tok->previous(), "[;{}.]") && 01364 tok->varId() && tok->varId() == tok->tokAt(2)->varId() && 01365 isTypeWithoutSideEffects(_tokenizer, tok->variable())) { 01366 bool err = true; 01367 01368 // no false positive for 'x = x ? x : 1;' 01369 // it is simplified to 'if (x) { x=x; } else { x=1; }'. The simplification 01370 // always write all tokens on 1 line (even if the statement is several lines), so 01371 // check if the linenr is the same for all the tokens. 01372 if (Token::Match(tok->tokAt(-2), ") { %var% = %var% ; } else { %varid% =", tok->varId())) { 01373 // Find the 'if' token 01374 const Token *tokif = tok->linkAt(-2)->previous(); 01375 01376 // find the '}' that terminates the 'else'-block 01377 const Token *else_end = tok->linkAt(6); 01378 01379 if (tokif && else_end && tokif->linenr() == else_end->linenr()) 01380 err = false; 01381 } 01382 01383 if (err) 01384 selfAssignmentError(tok, tok->str()); 01385 } 01386 01387 tok = findSelfAssignPattern(tok->next()); 01388 } 01389 } 01390 01391 void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname) 01392 { 01393 reportError(tok, Severity::warning, 01394 "selfAssignment", "Redundant assignment of '" + varname + "' to itself."); 01395 } 01396 01397 //--------------------------------------------------------------------------- 01398 // int a = 1; 01399 // assert(a = 2); // <- assert should not have a side-effect 01400 //--------------------------------------------------------------------------- 01401 static inline const Token *findAssertPattern(const Token *start) 01402 { 01403 return Token::findmatch(start, "assert ( %any%"); 01404 } 01405 01406 void CheckOther::checkAssignmentInAssert() 01407 { 01408 if (!_settings->isEnabled("warning")) 01409 return; 01410 01411 const Token *tok = findAssertPattern(_tokenizer->tokens()); 01412 const Token *endTok = tok ? tok->next()->link() : NULL; 01413 01414 while (tok && endTok) { 01415 for (tok = tok->tokAt(2); tok != endTok; tok = tok->next()) { 01416 if (tok->isName() && (tok->next()->isAssignmentOp() || tok->next()->type() == Token::eIncDecOp)) 01417 assignmentInAssertError(tok, tok->str()); 01418 else if (Token::Match(tok, "--|++ %var%")) 01419 assignmentInAssertError(tok, tok->strAt(1)); 01420 } 01421 01422 tok = findAssertPattern(endTok->next()); 01423 endTok = tok ? tok->next()->link() : NULL; 01424 } 01425 } 01426 01427 void CheckOther::assignmentInAssertError(const Token *tok, const std::string &varname) 01428 { 01429 reportError(tok, Severity::warning, 01430 "assignmentInAssert", "Assert statement modifies '" + varname + "'.\n" 01431 "Variable '" + varname + "' is modified insert assert statement. " 01432 "Assert statements are removed from release builds so the code inside " 01433 "assert statement is not executed. If the code is needed also in release " 01434 "builds, this is a bug."); 01435 } 01436 01437 //--------------------------------------------------------------------------- 01438 // if ((x != 1) || (x != 3)) // expression always true 01439 // if ((x == 1) && (x == 3)) // expression always false 01440 // if ((x < 1) && (x > 3)) // expression always false 01441 // if ((x > 3) || (x < 10)) // expression always true 01442 // if ((x > 5) && (x != 1)) // second comparison always true 01443 // 01444 // Check for suspect logic for an expression consisting of 2 comparison 01445 // expressions with a shared variable and constants and a logical operator 01446 // between them. 01447 // 01448 // Suggest a different logical operator when the logical operator between 01449 // the comparisons is probably wrong. 01450 // 01451 // Inform that second comparison is always true when first comparison is true. 01452 //--------------------------------------------------------------------------- 01453 01454 enum Position { First, Second, NA }; 01455 enum Relation { Equal, NotEqual, Less, LessEqual, More, MoreEqual }; 01456 struct Condition { 01457 Position position; 01458 const char *opTokStr; 01459 }; 01460 01461 static std::string invertOperatorForOperandSwap(std::string s) 01462 { 01463 for (std::string::size_type i = 0; i < s.length(); i++) { 01464 if (s[i] == '>') 01465 s[i] = '<'; 01466 else if (s[i] == '<') 01467 s[i] = '>'; 01468 } 01469 return s; 01470 } 01471 01472 static bool analyzeLogicOperatorCondition(const Condition& c1, const Condition& c2, 01473 bool inv1, bool inv2, 01474 bool varFirst1, bool varFirst2, 01475 const std::string& firstConstant, const std::string& secondConstant, 01476 const Token* op1Tok, const Token* op3Tok, 01477 Relation relation) 01478 { 01479 if (!(c1.position == NA || (c1.position == First && varFirst1) || (c1.position == Second && !varFirst1))) 01480 return false; 01481 01482 if (!(c2.position == NA || (c2.position == First && varFirst2) || (c2.position == Second && !varFirst2))) 01483 return false; 01484 01485 if (!Token::Match(op1Tok, inv1?invertOperatorForOperandSwap(c1.opTokStr).c_str():c1.opTokStr)) 01486 return false; 01487 01488 if (!Token::Match(op3Tok, inv2?invertOperatorForOperandSwap(c2.opTokStr).c_str():c2.opTokStr)) 01489 return false; 01490 01491 return (relation == Equal && MathLib::isEqual(firstConstant, secondConstant)) || 01492 (relation == NotEqual && MathLib::isNotEqual(firstConstant, secondConstant)) || 01493 (relation == Less && MathLib::isLess(firstConstant, secondConstant)) || 01494 (relation == LessEqual && MathLib::isLessEqual(firstConstant, secondConstant)) || 01495 (relation == More && MathLib::isGreater(firstConstant, secondConstant)) || 01496 (relation == MoreEqual && MathLib::isGreaterEqual(firstConstant, secondConstant)); 01497 } 01498 01499 void CheckOther::checkIncorrectLogicOperator() 01500 { 01501 bool style = _settings->isEnabled("style"); 01502 bool warning = _settings->isEnabled("warning"); 01503 if (!style && !warning) 01504 return; 01505 01506 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 01507 const std::size_t functions = symbolDatabase->functionScopes.size(); 01508 for (std::size_t ii = 0; ii < functions; ++ii) { 01509 const Scope * scope = symbolDatabase->functionScopes[ii]; 01510 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 01511 // Find a pair of comparison expressions with or without parentheses 01512 // with a shared variable and constants and with a logical operator between them. 01513 // e.g. if (x != 3 || x != 4) 01514 const Token *term1Tok = NULL, *term2Tok = NULL; 01515 const Token *op1Tok = NULL, *op2Tok = NULL, *op3Tok = NULL, *nextTok = NULL; 01516 01517 if (Token::Match(tok, "( %any% %comp% %any% ) &&|%oror%")) { 01518 term1Tok = tok->next(); 01519 op1Tok = tok->tokAt(2); 01520 op2Tok = tok->tokAt(5); 01521 } else if (Token::Match(tok, "%any% %comp% %any% &&|%oror%")) { 01522 term1Tok = tok; 01523 op1Tok = tok->next(); 01524 op2Tok = tok->tokAt(3); 01525 } 01526 if (op2Tok) { 01527 if (Token::Match(op2Tok->next(), "( %any% %comp% %any% ) %any%")) { 01528 term2Tok = op2Tok->tokAt(2); 01529 op3Tok = op2Tok->tokAt(3); 01530 nextTok = op2Tok->tokAt(6); 01531 } else if (Token::Match(op2Tok->next(), "%any% %comp% %any% %any%")) { 01532 term2Tok = op2Tok->next(); 01533 op3Tok = op2Tok->tokAt(2); 01534 nextTok = op2Tok->tokAt(4); 01535 } 01536 } 01537 01538 if (nextTok) { 01539 // Find the common variable and the two different-valued constants 01540 std::string firstConstant, secondConstant; 01541 bool varFirst1, varFirst2; 01542 unsigned int varId; 01543 const Token *var1Tok = NULL, *var2Tok = NULL; 01544 if (Token::Match(term1Tok, "%var% %any% %num%")) { 01545 var1Tok = term1Tok; 01546 varId = var1Tok->varId(); 01547 if (!varId) { 01548 continue; 01549 } 01550 varFirst1 = true; 01551 firstConstant = term1Tok->strAt(2); 01552 } else if (Token::Match(term1Tok, "%num% %any% %var%")) { 01553 var1Tok = term1Tok->tokAt(2); 01554 varId = var1Tok->varId(); 01555 if (!varId) { 01556 continue; 01557 } 01558 varFirst1 = false; 01559 firstConstant = term1Tok->str(); 01560 } else { 01561 continue; 01562 } 01563 01564 if (Token::Match(term2Tok, "%var% %any% %num%")) { 01565 var2Tok = term2Tok; 01566 varFirst2 = true; 01567 secondConstant = term2Tok->strAt(2); 01568 } else if (Token::Match(term2Tok, "%num% %any% %var%")) { 01569 var2Tok = term2Tok->tokAt(2); 01570 varFirst2 = false; 01571 secondConstant = term2Tok->str(); 01572 } else { 01573 continue; 01574 } 01575 01576 if (varId != var2Tok->varId() || firstConstant.empty() || secondConstant.empty()) { 01577 continue; 01578 } 01579 01580 enum LogicError { AlwaysFalse, AlwaysTrue, FirstTrue, FirstFalse, SecondTrue, SecondFalse }; 01581 01582 static const struct LinkedConditions { 01583 const char *before; 01584 Condition c1; 01585 const char *op2TokStr; 01586 Condition c2; 01587 const char *after; 01588 Relation relation; 01589 LogicError error; 01590 } conditions[] = { 01591 { "!!&&", { NA, "!=" }, "%oror%", { NA, "!=" }, "!!&&", NotEqual, AlwaysTrue }, // (x != 1) || (x != 3) <- always true 01592 { 0, { NA, "==" }, "&&", { NA, "==" }, 0, NotEqual, AlwaysFalse }, // (x == 1) && (x == 3) <- always false 01593 { "!!&&", { First, ">" }, "%oror%", { First, "<" }, "!!&&", Less, AlwaysTrue }, // (x > 3) || (x < 10) <- always true 01594 { "!!&&", { First, ">=" }, "%oror%", { First, "<|<=" }, "!!&&", LessEqual, AlwaysTrue }, // (x >= 3) || (x < 10) <- always true 01595 { "!!&&", { First, ">" }, "%oror%", { First, "<=" }, "!!&&", LessEqual, AlwaysTrue }, // (x > 3) || (x <= 10) <- always true 01596 { 0, { First, "<" }, "&&", { First, ">" }, 0, LessEqual, AlwaysFalse }, // (x < 1) && (x > 3) <- always false 01597 { 0, { First, "<=" }, "&&", { First, ">|>=" }, 0, Less, AlwaysFalse }, // (x <= 1) && (x > 3) <- always false 01598 { 0, { First, "<" }, "&&", { First, ">=" }, 0, Less, AlwaysFalse }, // (x < 1) && (x >= 3) <- always false 01599 { 0, { First, ">" }, "&&", { NA, "==" }, 0, MoreEqual, AlwaysFalse }, // (x > 5) && (x == 1) <- always false 01600 { 0, { First, "<" }, "&&", { NA, "==" }, 0, LessEqual, AlwaysFalse }, // (x < 1) && (x == 3) <- always false 01601 { 0, { First, ">=" }, "&&", { NA, "==" }, 0, More, AlwaysFalse }, // (x >= 5) && (x == 1) <- always false 01602 { 0, { First, "<=" }, "&&", { NA, "==" }, 0, Less, AlwaysFalse }, // (x <= 1) && (x == 3) <- always false 01603 { "!!&&", { NA, "==" }, "%oror%", { First, ">" }, "!!&&", More, SecondTrue }, // (x == 4) || (x > 3) <- second expression always true 01604 { "!!&&", { NA, "==" }, "%oror%", { First, "<" }, "!!&&", Less, SecondTrue }, // (x == 4) || (x < 5) <- second expression always true 01605 { "!!&&", { NA, "==" }, "%oror%", { First, ">=" }, "!!&&", MoreEqual, SecondTrue }, // (x == 4) || (x >= 3) <- second expression always true 01606 { "!!&&", { NA, "==" }, "%oror%", { First, "<=" }, "!!&&", LessEqual, SecondTrue }, // (x == 4) || (x <= 5) <- second expression always true 01607 { "!!&&", { First, ">" }, "%oror%", { NA, "!=" }, "!!&&", MoreEqual, SecondTrue }, // (x > 5) || (x != 1) <- second expression always true 01608 { "!!&&", { First, "<" }, "%oror%", { NA, "!=" }, "!!&&", LessEqual, SecondTrue }, // (x < 1) || (x != 3) <- second expression always true 01609 { "!!&&", { First, ">=" }, "%oror%", { NA, "!=" }, "!!&&", More, SecondTrue }, // (x >= 5) || (x != 1) <- second expression always true 01610 { "!!&&", { First, "<=" }, "%oror%", { NA, "!=" }, "!!&&", Less, SecondTrue }, // (x <= 1) || (x != 3) <- second expression always true 01611 { 0, { First, ">" }, "&&", { NA, "!=" }, 0, MoreEqual, SecondTrue }, // (x > 5) && (x != 1) <- second expression always true 01612 { 0, { First, "<" }, "&&", { NA, "!=" }, 0, LessEqual, SecondTrue }, // (x < 1) && (x != 3) <- second expression always true 01613 { 0, { First, ">=" }, "&&", { NA, "!=" }, 0, More, SecondTrue }, // (x >= 5) && (x != 1) <- second expression always true 01614 { 0, { First, "<=" }, "&&", { NA, "!=" }, 0, Less, SecondTrue }, // (x <= 1) && (x != 3) <- second expression always true 01615 { "!!&&", { First, ">|>=" }, "%oror%", { First, ">|>=" }, "!!&&", LessEqual, SecondTrue }, // (x > 4) || (x > 5) <- second expression always true 01616 { "!!&&", { First, "<|<=" }, "%oror%", { First, "<|<=" }, "!!&&", MoreEqual, SecondTrue }, // (x < 5) || (x < 4) <- second expression always true 01617 { 0, { First, ">|>=" }, "&&", { First, ">|>=" }, 0, MoreEqual, SecondTrue }, // (x > 4) && (x > 5) <- second expression always true 01618 { 0, { First, "<|<=" }, "&&", { First, "<|<=" }, 0, MoreEqual, SecondTrue }, // (x < 5) && (x < 4) <- second expression always true 01619 { 0, { NA, "==" }, "&&", { NA, "!=" }, 0, NotEqual, SecondTrue }, // (x == 3) && (x != 4) <- second expression always true 01620 { "!!&&", { NA, "==" }, "%oror%", { NA, "!=" }, "!!&&", NotEqual, SecondTrue }, // (x == 3) || (x != 4) <- second expression always true 01621 { 0, { NA, "!=" }, "&&", { NA, "==" }, 0, Equal, AlwaysFalse }, // (x != 3) && (x == 3) <- expression always false 01622 { "!!&&", { NA, "!=" }, "%oror%", { NA, "==" }, "!!&&", Equal, AlwaysTrue }, // (x != 3) || (x == 3) <- expression always true 01623 }; 01624 01625 for (unsigned int i = 0; i < (sizeof(conditions) / sizeof(conditions[0])); i++) { 01626 if (!Token::Match(op2Tok, conditions[i].op2TokStr)) 01627 continue; 01628 01629 if (conditions[i].before != 0 && !Token::Match(tok->previous(), conditions[i].before)) 01630 continue; 01631 01632 if (conditions[i].after != 0 && !Token::Match(nextTok, conditions[i].after)) 01633 continue; 01634 01635 if (tok->previous()->isArithmeticalOp() || nextTok->isArithmeticalOp()) 01636 continue; 01637 01638 std::string cond1str = var1Tok->str() + " " + (varFirst1?op1Tok->str():invertOperatorForOperandSwap(op1Tok->str())) + " " + firstConstant; 01639 std::string cond2str = var2Tok->str() + " " + (varFirst2?op3Tok->str():invertOperatorForOperandSwap(op3Tok->str())) + " " + secondConstant; 01640 // cond1 op cond2 01641 bool error = analyzeLogicOperatorCondition(conditions[i].c1, conditions[i].c2, false, false, 01642 varFirst1, varFirst2, firstConstant, secondConstant, 01643 op1Tok, op3Tok, 01644 conditions[i].relation); 01645 // inv(cond1) op cond2 // invert first condition 01646 if (!error && conditions[i].c1.position != NA) 01647 error = analyzeLogicOperatorCondition(conditions[i].c1, conditions[i].c2, true, false, 01648 !varFirst1, varFirst2, firstConstant, secondConstant, 01649 op1Tok, op3Tok, 01650 conditions[i].relation); 01651 // cond1 op inv(cond2) // invert second condition 01652 if (!error && conditions[i].c2.position != NA) 01653 error = analyzeLogicOperatorCondition(conditions[i].c1, conditions[i].c2, false, true, 01654 varFirst1, !varFirst2, firstConstant, secondConstant, 01655 op1Tok, op3Tok, 01656 conditions[i].relation); 01657 // inv(cond1) op inv(cond2) // invert both conditions 01658 if (!error && conditions[i].c1.position != NA && conditions[i].c2.position != NA) 01659 error = analyzeLogicOperatorCondition(conditions[i].c1, conditions[i].c2, true, true, 01660 !varFirst1, !varFirst2, firstConstant, secondConstant, 01661 op1Tok, op3Tok, 01662 conditions[i].relation); 01663 if (!error) 01664 std::swap(cond1str, cond2str); 01665 // cond2 op cond1 // swap conditions 01666 if (!error) 01667 error = analyzeLogicOperatorCondition(conditions[i].c1, conditions[i].c2, false, false, 01668 varFirst2, varFirst1, secondConstant, firstConstant, 01669 op3Tok, op1Tok, 01670 conditions[i].relation); 01671 // cond2 op inv(cond1) // swap conditions; invert first condition 01672 if (!error && conditions[i].c1.position != NA) 01673 error = analyzeLogicOperatorCondition(conditions[i].c1, conditions[i].c2, true, false, 01674 !varFirst2, varFirst1, secondConstant, firstConstant, 01675 op3Tok, op1Tok, 01676 conditions[i].relation); 01677 // inv(cond2) op cond1 // swap conditions; invert second condition 01678 if (!error && conditions[i].c2.position != NA) 01679 error = analyzeLogicOperatorCondition(conditions[i].c1, conditions[i].c2, false, true, 01680 varFirst2, !varFirst1, secondConstant, firstConstant, 01681 op3Tok, op1Tok, 01682 conditions[i].relation); 01683 // inv(cond2) op inv(cond1) // swap conditions; invert both conditions 01684 if (!error && conditions[i].c1.position != NA && conditions[i].c2.position != NA) 01685 error = analyzeLogicOperatorCondition(conditions[i].c1, conditions[i].c2, true, true, 01686 !varFirst2, !varFirst1, secondConstant, firstConstant, 01687 op3Tok, op1Tok, 01688 conditions[i].relation); 01689 01690 if (error) { 01691 if (conditions[i].error == AlwaysFalse || conditions[i].error == AlwaysTrue) { 01692 if (warning) { 01693 const std::string text = cond1str + " " + op2Tok->str() + " " + cond2str; 01694 incorrectLogicOperatorError(term1Tok, text, conditions[i].error == AlwaysTrue); 01695 } 01696 } else { 01697 if (style) { 01698 const std::string text = "If " + cond1str + ", the comparison " + cond2str + 01699 " is always " + ((conditions[i].error == SecondTrue || conditions[i].error == AlwaysTrue) ? "true" : "false") + "."; 01700 redundantConditionError(term1Tok, text); 01701 } 01702 } 01703 break; 01704 } 01705 } 01706 } 01707 } 01708 } 01709 } 01710 01711 void CheckOther::incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always) 01712 { 01713 if (always) 01714 reportError(tok, Severity::warning, "incorrectLogicOperator", 01715 "Logical disjunction always evaluates to true: " + condition + ".\n" 01716 "Logical disjunction always evaluates to true: " + condition + ". " 01717 "Are these conditions necessary? Did you intend to use && instead? Are the numbers correct? Are you comparing the correct variables?"); 01718 else 01719 reportError(tok, Severity::warning, "incorrectLogicOperator", 01720 "Logical conjunction always evaluates to false: " + condition + ".\n" 01721 "Logical conjunction always evaluates to false: " + condition + ". " 01722 "Are these conditions necessary? Did you intend to use || instead? Are the numbers correct? Are you comparing the correct variables?"); 01723 } 01724 01725 void CheckOther::redundantConditionError(const Token *tok, const std::string &text) 01726 { 01727 reportError(tok, Severity::style, "redundantCondition", "Redundant condition: " + text); 01728 } 01729 01730 //--------------------------------------------------------------------------- 01731 // strtol(str, 0, radix) <- radix must be 0 or 2-36 01732 //--------------------------------------------------------------------------- 01733 void CheckOther::invalidFunctionUsage() 01734 { 01735 // strtol and strtoul.. 01736 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 01737 if (!Token::Match(tok, "strtol|strtoul|strtoll|strtoull|wcstol|wcstoul|wcstoll|wcstoull (")) 01738 continue; 01739 01740 const std::string& funcname = tok->str(); 01741 tok = tok->tokAt(2); 01742 // Locate the third parameter of the function call.. 01743 for (int i = 0; i < 2 && tok; i++) 01744 tok = tok->nextArgument(); 01745 01746 if (Token::Match(tok, "%num% )")) { 01747 const MathLib::bigint radix = MathLib::toLongNumber(tok->str()); 01748 if (!(radix == 0 || (radix >= 2 && radix <= 36))) { 01749 dangerousUsageStrtolError(tok, funcname); 01750 } 01751 } else 01752 break; 01753 } 01754 01755 // sprintf|snprintf overlapping data 01756 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 01757 // Get variable id of target buffer.. 01758 unsigned int varid = 0; 01759 01760 if (Token::Match(tok, "sprintf|snprintf|swprintf ( %var% ,")) 01761 varid = tok->tokAt(2)->varId(); 01762 01763 else if (Token::Match(tok, "sprintf|snprintf|swprintf ( %var% . %var% ,")) 01764 varid = tok->tokAt(4)->varId(); 01765 01766 if (varid == 0) 01767 continue; 01768 01769 // goto "," 01770 const Token *tok2 = tok->tokAt(3); 01771 while (tok2->str() != ",") 01772 tok2 = tok2->next(); 01773 01774 tok2 = tok2->next(); // Jump behind "," 01775 01776 if (tok->str() == "snprintf" || tok->str() == "swprintf") { // Jump over second parameter for snprintf and swprintf 01777 tok2 = tok2->nextArgument(); 01778 if (!tok2) 01779 continue; 01780 } 01781 01782 // is any source buffer overlapping the target buffer? 01783 do { 01784 if (Token::Match(tok2, "%varid% [,)]", varid)) { 01785 sprintfOverlappingDataError(tok2, tok2->str()); 01786 break; 01787 } 01788 } while (NULL != (tok2 = tok2->nextArgument())); 01789 } 01790 } 01791 01792 void CheckOther::dangerousUsageStrtolError(const Token *tok, const std::string& funcname) 01793 { 01794 reportError(tok, Severity::error, "dangerousUsageStrtol", "Invalid radix in call to " + funcname + "(). It must be 0 or 2-36."); 01795 } 01796 01797 void CheckOther::sprintfOverlappingDataError(const Token *tok, const std::string &varname) 01798 { 01799 reportError(tok, Severity::error, "sprintfOverlappingData", 01800 "Undefined behavior: Variable '" + varname + "' is used as parameter and destination in s[n]printf().\n" 01801 "The variable '" + varname + "' is used both as a parameter and as destination in " 01802 "s[n]printf(). The origin and destination buffers overlap. Quote from glibc (C-library) " 01803 "documentation (http://www.gnu.org/software/libc/manual/html_mono/libc.html#Formatted-Output-Functions): " 01804 "\"If copying takes place between objects that overlap as a result of a call " 01805 "to sprintf() or snprintf(), the results are undefined.\""); 01806 } 01807 01808 //--------------------------------------------------------------------------- 01809 // if (!x==3) <- Probably meant to be "x!=3" 01810 //--------------------------------------------------------------------------- 01811 01812 static bool isBool(const Variable* var) 01813 { 01814 return(var && var->typeEndToken()->str() == "bool"); 01815 } 01816 static bool isNonBoolStdType(const Variable* var) 01817 { 01818 return(var && var->typeEndToken()->isStandardType() && var->typeEndToken()->str() != "bool"); 01819 } 01820 void CheckOther::checkComparisonOfBoolWithInt() 01821 { 01822 if (!_settings->isEnabled("warning")) 01823 return; 01824 01825 const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); 01826 const std::size_t functions = symbolDatabase->functionScopes.size(); 01827 for (std::size_t i = 0; i < functions; ++i) { 01828 const Scope * scope = symbolDatabase->functionScopes[i]; 01829 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 01830 if (tok->next() && tok->next()->type() == Token::eComparisonOp && (!tok->previous() || !tok->previous()->isArithmeticalOp()) && (!tok->tokAt(3) || !tok->tokAt(3)->isArithmeticalOp())) { 01831 const Token* const right = tok->tokAt(2); 01832 if ((tok->varId() && right->isNumber()) || (tok->isNumber() && right->varId())) { // Comparing variable with number 01833 const Token* varTok = tok; 01834 const Token* numTok = right; 01835 if (tok->isNumber() && right->varId()) // num with var 01836 std::swap(varTok, numTok); 01837 if (isBool(varTok->variable()) && // Variable has to be a boolean 01838 ((tok->strAt(1) != "==" && tok->strAt(1) != "!=") || 01839 (MathLib::toLongNumber(numTok->str()) != 0 && MathLib::toLongNumber(numTok->str()) != 1))) { // == 0 and != 0 are allowed, for C also == 1 and != 1 01840 comparisonOfBoolWithIntError(varTok, numTok->str(), tok->strAt(1) == "==" || tok->strAt(1) == "!="); 01841 } 01842 } else if (tok->isBoolean() && right->varId()) { // Comparing boolean constant with variable 01843 if (isNonBoolStdType(right->variable())) { // Variable has to be of non-boolean standard type 01844 comparisonOfBoolWithIntError(right, tok->str(), false); 01845 } else if (tok->strAt(1) != "==" && tok->strAt(1) != "!=") { 01846 comparisonOfBoolWithInvalidComparator(right, tok->str()); 01847 } 01848 } else if (tok->varId() && right->isBoolean()) { // Comparing variable with boolean constant 01849 if (isNonBoolStdType(tok->variable())) { // Variable has to be of non-boolean standard type 01850 comparisonOfBoolWithIntError(tok, right->str(), false); 01851 } else if (tok->strAt(1) != "==" && tok->strAt(1) != "!=") { 01852 comparisonOfBoolWithInvalidComparator(right, tok->str()); 01853 } 01854 } else if (tok->isNumber() && right->isBoolean()) { // number constant with boolean constant 01855 comparisonOfBoolWithIntError(tok, right->str(), false); 01856 } else if (tok->isBoolean() && right->isNumber()) { // number constant with boolean constant 01857 comparisonOfBoolWithIntError(tok, tok->str(), false); 01858 } else if (tok->varId() && right->varId()) { // Comparing two variables, one of them boolean, one of them integer 01859 const Variable* var1 = right->variable(); 01860 const Variable* var2 = tok->variable(); 01861 if (isBool(var1) && isNonBoolStdType(var2)) // Comparing boolean with non-bool standard type 01862 comparisonOfBoolWithIntError(tok, var1->name(), false); 01863 else if (isNonBoolStdType(var1) && isBool(var2)) // Comparing non-bool standard type with boolean 01864 comparisonOfBoolWithIntError(tok, var2->name(), false); 01865 } 01866 } 01867 } 01868 } 01869 } 01870 01871 void CheckOther::comparisonOfBoolWithIntError(const Token *tok, const std::string &expression, bool n0o1) 01872 { 01873 if (n0o1) 01874 reportError(tok, Severity::warning, "comparisonOfBoolWithInt", 01875 "Comparison of a boolean with an integer that is neither 1 nor 0.\n" 01876 "The expression '" + expression + "' is of type 'bool' " 01877 "and it is compared against an integer value that is " 01878 "neither 1 nor 0."); 01879 else 01880 reportError(tok, Severity::warning, "comparisonOfBoolWithInt", 01881 "Comparison of a boolean with an integer.\n" 01882 "The expression '" + expression + "' is of type 'bool' " 01883 "and it is compared against an integer value."); 01884 } 01885 01886 void CheckOther::comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression) 01887 { 01888 reportError(tok, Severity::warning, "comparisonOfBoolWithInvalidComparator", 01889 "Comparison of a boolean value using relational operator (<, >, <= or >=).\n" 01890 "The result of the expression '" + expression + "' is of type 'bool'. " 01891 "Comparing 'bool' value using relational (<, >, <= or >=)" 01892 " operator could cause unexpected results."); 01893 } 01894 01895 //--------------------------------------------------------------------------- 01896 // Find consecutive return, break, continue, goto or throw statements. e.g.: 01897 // break; break; 01898 // Detect dead code, that follows such a statement. e.g.: 01899 // return(0); foo(); 01900 //--------------------------------------------------------------------------- 01901 void CheckOther::checkUnreachableCode() 01902 { 01903 if (!_settings->isEnabled("style")) 01904 return; 01905 01906 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 01907 const Token* secondBreak = 0; 01908 const Token* labelName = 0; 01909 if (tok->str() == "(") 01910 tok = tok->link(); 01911 else if (Token::Match(tok, "break|continue ;")) 01912 secondBreak = tok->tokAt(2); 01913 else if (Token::Match(tok, "[;{}:] return|throw")) { 01914 tok = tok->next(); // tok should point to return or throw 01915 for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) 01916 if (tok2->str() == ";") { 01917 secondBreak = tok2->next(); 01918 break; 01919 } 01920 } else if (Token::Match(tok, "goto %any% ;")) { 01921 secondBreak = tok->tokAt(3); 01922 labelName = tok->next(); 01923 } 01924 01925 // Statements follow directly, no line between them. (#3383) 01926 // TODO: Try to find a better way to avoid false positives due to preprocessor configurations. 01927 bool inconclusive = secondBreak && (secondBreak->linenr()-1 > secondBreak->previous()->linenr()); 01928 01929 if (secondBreak && (_settings->inconclusive || !inconclusive)) { 01930 if (Token::Match(secondBreak, "continue|goto|throw") || 01931 (secondBreak->str() == "return" && (tok->str() == "return" || secondBreak->strAt(1) == ";"))) { // return with value after statements like throw can be necessary to make a function compile 01932 duplicateBreakError(secondBreak, inconclusive); 01933 tok = Token::findmatch(secondBreak, "[}:]"); 01934 } else if (secondBreak->str() == "break") { // break inside switch as second break statement should not issue a warning 01935 if (tok->str() == "break") // If the previous was a break, too: Issue warning 01936 duplicateBreakError(secondBreak, inconclusive); 01937 else { 01938 if (tok->scope()->type != Scope::eSwitch) // Check, if the enclosing scope is a switch 01939 duplicateBreakError(secondBreak, inconclusive); 01940 } 01941 tok = Token::findmatch(secondBreak, "[}:]"); 01942 } else if (!Token::Match(secondBreak, "return|}|case|default") && secondBreak->strAt(1) != ":") { // TODO: No bailout for unconditional scopes 01943 // If the goto label is followed by a loop construct in which the label is defined it's quite likely 01944 // that the goto jump was intended to skip some code on the first loop iteration. 01945 bool labelInFollowingLoop = false; 01946 if (labelName && Token::Match(secondBreak, "while|do|for")) { 01947 const Token *scope = Token::findsimplematch(secondBreak, "{"); 01948 if (scope) { 01949 for (const Token *tokIter = scope; tokIter != scope->link() && tokIter; tokIter = tokIter->next()) { 01950 if (Token::Match(tokIter, "[;{}] %any% :") && labelName->str() == tokIter->strAt(1)) { 01951 labelInFollowingLoop = true; 01952 break; 01953 } 01954 } 01955 } 01956 } 01957 if (!labelInFollowingLoop) 01958 unreachableCodeError(secondBreak, inconclusive); 01959 tok = Token::findmatch(secondBreak, "[}:]"); 01960 } else 01961 tok = secondBreak; 01962 01963 if (!tok) 01964 break; 01965 } 01966 } 01967 } 01968 01969 void CheckOther::duplicateBreakError(const Token *tok, bool inconclusive) 01970 { 01971 reportError(tok, Severity::style, "duplicateBreak", 01972 "Consecutive return, break, continue, goto or throw statements are unnecessary.\n" 01973 "Consecutive return, break, continue, goto or throw statements are unnecessary. " 01974 "The second statement can never be executed, and so should be removed.", inconclusive); 01975 } 01976 01977 void CheckOther::unreachableCodeError(const Token *tok, bool inconclusive) 01978 { 01979 reportError(tok, Severity::style, "unreachableCode", 01980 "Statements following return, break, continue, goto or throw will never be executed.", inconclusive); 01981 } 01982 01983 //--------------------------------------------------------------------------- 01984 // Check for unsigned divisions 01985 //--------------------------------------------------------------------------- 01986 bool CheckOther::isUnsigned(const Variable* var) const 01987 { 01988 return(var && var->typeStartToken()->isUnsigned() && !var->isPointer() && !var->isArray() && _tokenizer->sizeOfType(var->typeStartToken()) >= _settings->sizeof_int); 01989 } 01990 bool CheckOther::isSigned(const Variable* var) 01991 { 01992 return(var && !var->typeStartToken()->isUnsigned() && Token::Match(var->typeEndToken(), "int|char|short|long") && !var->isPointer() && !var->isArray()); 01993 } 01994 01995 void CheckOther::checkUnsignedDivision() 01996 { 01997 bool warning = _settings->isEnabled("warning"); 01998 01999 const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); 02000 const std::size_t functions = symbolDatabase->functionScopes.size(); 02001 for (std::size_t i = 0; i < functions; ++i) { 02002 const Scope * scope = symbolDatabase->functionScopes[i]; 02003 const Token* ifTok = 0; 02004 // Check for "ivar / uvar" and "uvar / ivar" 02005 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 02006 02007 if (Token::Match(tok, "[).]")) // Don't check members or casted variables 02008 continue; 02009 02010 if (Token::Match(tok->next(), "%var% / %num%")) { 02011 if (tok->strAt(3)[0] == '-' && isUnsigned(tok->next()->variable())) { 02012 udivError(tok->next(), false); 02013 } 02014 } else if (Token::Match(tok->next(), "%num% / %var%")) { 02015 if (tok->strAt(1)[0] == '-' && isUnsigned(tok->tokAt(3)->variable())) { 02016 udivError(tok->next(), false); 02017 } 02018 } else if (Token::Match(tok->next(), "%var% / %var%") && _settings->inconclusive && warning && !ifTok) { 02019 const Variable* var1 = tok->next()->variable(); 02020 const Variable* var2 = tok->tokAt(3)->variable(); 02021 if ((isUnsigned(var1) && isSigned(var2)) || (isUnsigned(var2) && isSigned(var1))) { 02022 udivError(tok->next(), true); 02023 } 02024 } else if (!ifTok && Token::simpleMatch(tok, "if (")) 02025 ifTok = tok->next()->link()->next()->link(); 02026 else if (ifTok == tok) 02027 ifTok = 0; 02028 } 02029 } 02030 } 02031 02032 void CheckOther::udivError(const Token *tok, bool inconclusive) 02033 { 02034 if (inconclusive) 02035 reportError(tok, Severity::warning, "udivError", "Division with signed and unsigned operators. The result might be wrong.", true); 02036 else 02037 reportError(tok, Severity::error, "udivError", "Unsigned division. The result will be wrong."); 02038 } 02039 02040 //--------------------------------------------------------------------------- 02041 // memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted 02042 //--------------------------------------------------------------------------- 02043 void CheckOther::checkMemsetZeroBytes() 02044 { 02045 if (!_settings->isEnabled("warning")) 02046 return; 02047 02048 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 02049 const std::size_t functions = symbolDatabase->functionScopes.size(); 02050 for (std::size_t i = 0; i < functions; ++i) { 02051 const Scope * scope = symbolDatabase->functionScopes[i]; 02052 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 02053 if (Token::simpleMatch(tok, "memset (")) { 02054 const Token* lastParamTok = tok->next()->link()->previous(); 02055 if (lastParamTok->str() == "0") 02056 memsetZeroBytesError(tok, tok->strAt(2)); 02057 } 02058 } 02059 } 02060 } 02061 02062 void CheckOther::memsetZeroBytesError(const Token *tok, const std::string &varname) 02063 { 02064 const std::string summary("memset() called to fill 0 bytes of '" + varname + "'."); 02065 const std::string verbose(summary + " Second and third arguments might be inverted."); 02066 reportError(tok, Severity::warning, "memsetZeroBytes", summary + "\n" + verbose); 02067 } 02068 02069 //--------------------------------------------------------------------------- 02070 // Check scope of variables.. 02071 //--------------------------------------------------------------------------- 02072 void CheckOther::checkVariableScope() 02073 { 02074 if (!_settings->isEnabled("style")) 02075 return; 02076 02077 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 02078 02079 for (unsigned int i = 1; i < symbolDatabase->getVariableListSize(); i++) { 02080 const Variable* var = symbolDatabase->getVariableFromVarId(i); 02081 if (!var || !var->isLocal() || (!var->isPointer() && !var->typeStartToken()->isStandardType() && !var->typeStartToken()->next()->isStandardType())) 02082 continue; 02083 02084 bool forHead = false; // Don't check variables declared in header of a for loop 02085 for (const Token* tok = var->typeStartToken(); tok; tok = tok->previous()) { 02086 if (tok->str() == "(") { 02087 forHead = true; 02088 break; 02089 } else if (tok->str() == "{" || tok->str() == ";" || tok->str() == "}") 02090 break; 02091 } 02092 if (forHead) 02093 continue; 02094 02095 const Token* tok = var->nameToken()->next(); 02096 if (Token::Match(tok, "; %varid% = %any% ;", var->varId())) { 02097 tok = tok->tokAt(3); 02098 if (!tok->isNumber() && tok->type() != Token::eString && tok->type() != Token::eChar && !tok->isBoolean()) 02099 continue; 02100 } else if ((tok->str() == "=" || tok->str() == "(") && 02101 ((!tok->next()->isNumber() && tok->next()->type() != Token::eString && tok->next()->type() != Token::eChar && !tok->next()->isBoolean()) || tok->strAt(2) != ";")) 02102 continue; 02103 02104 bool reduce = true; 02105 bool used = false; // Don't warn about unused variables 02106 for (; tok != var->scope()->classEnd; tok = tok->next()) { 02107 if (tok->str() == "{" && tok->strAt(-1) != "=") { 02108 if (used) { 02109 bool used2 = false; 02110 if (!checkInnerScope(tok, var, used2) || used2) { 02111 reduce = false; 02112 break; 02113 } 02114 } else if (!checkInnerScope(tok, var, used)) { 02115 reduce = false; 02116 break; 02117 } 02118 02119 tok = tok->link(); 02120 } else if (tok->varId() == var->varId() || tok->str() == "goto") { 02121 reduce = false; 02122 break; 02123 } 02124 } 02125 02126 if (reduce && used) 02127 variableScopeError(var->nameToken(), var->name()); 02128 } 02129 } 02130 02131 bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& used) 02132 { 02133 const Scope* scope = tok->next()->scope(); 02134 bool loopVariable = scope->type == Scope::eFor || scope->type == Scope::eWhile || scope->type == Scope::eDo; 02135 bool noContinue = true; 02136 const Token* forHeadEnd = 0; 02137 const Token* end = tok->link(); 02138 if (scope->type == Scope::eUnconditional && (tok->strAt(-1) == ")" || tok->previous()->isName())) // Might be an unknown macro like BOOST_FOREACH 02139 loopVariable = true; 02140 02141 if (scope->type == Scope::eDo) { 02142 end = end->linkAt(2); 02143 } else if (loopVariable && tok->strAt(-1) == ")") { 02144 tok = tok->linkAt(-1); // Jump to opening ( of for/while statement 02145 } else if (scope->type == Scope::eSwitch) { 02146 for (std::list<Scope*>::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) { 02147 if (used) { 02148 bool used2 = false; 02149 if (!checkInnerScope((*i)->classStart, var, used2) || used2) { 02150 return false; 02151 } 02152 } else if (!checkInnerScope((*i)->classStart, var, used)) { 02153 return false; 02154 } 02155 } 02156 } 02157 02158 for (; tok != end; tok = tok->next()) { 02159 if (tok->str() == "goto") 02160 return false; 02161 if (tok->str() == "continue") 02162 noContinue = false; 02163 02164 if (Token::simpleMatch(tok, "for (")) 02165 forHeadEnd = tok->linkAt(1); 02166 if (tok == forHeadEnd) 02167 forHeadEnd = 0; 02168 02169 if (loopVariable && noContinue && tok->scope() == scope && !forHeadEnd && scope->type != Scope::eSwitch && Token::Match(tok, "%varid% =", var->varId())) { // Assigned in outer scope. 02170 loopVariable = false; 02171 unsigned int indent = 0; 02172 for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { // Ensure that variable isn't used on right side of =, too 02173 if (tok2->str() == "(") 02174 indent++; 02175 else if (tok2->str() == ")") { 02176 if (indent == 0) 02177 break; 02178 indent--; 02179 } else if (tok2->str() == ";") 02180 break; 02181 else if (tok2->varId() == var->varId()) { 02182 loopVariable = true; 02183 break; 02184 } 02185 } 02186 } 02187 02188 if (loopVariable && Token::Match(tok, "%varid% !!=", var->varId())) // Variable used in loop 02189 return false; 02190 02191 if (Token::Match(tok, "& %varid%", var->varId())) // Taking address of variable 02192 return false; 02193 02194 if (Token::Match(tok, "= %varid%", var->varId()) && (var->isArray() || var->isPointer())) // Create a copy of array/pointer. Bailout, because the memory it points to might be necessary in outer scope 02195 return false; 02196 02197 if (tok->varId() == var->varId()) { 02198 used = true; 02199 if (scope->type == Scope::eSwitch && scope == tok->scope()) 02200 return false; // Used in outer switch scope - unsafe or impossible to reduce scope 02201 } 02202 } 02203 02204 return true; 02205 } 02206 02207 void CheckOther::variableScopeError(const Token *tok, const std::string &varname) 02208 { 02209 reportError(tok, 02210 Severity::style, 02211 "variableScope", 02212 "The scope of the variable '" + varname + "' can be reduced.\n" 02213 "The scope of the variable '" + varname + "' can be reduced. Warning: Be careful " 02214 "when fixing this message, especially when there are inner loops. Here is an " 02215 "example where cppcheck will write that the scope for 'i' can be reduced:\n" 02216 "void f(int x)\n" 02217 "{\n" 02218 " int i = 0;\n" 02219 " if (x) {\n" 02220 " // it's safe to move 'int i = 0;' here\n" 02221 " for (int n = 0; n < 10; ++n) {\n" 02222 " // it is possible but not safe to move 'int i = 0;' here\n" 02223 " do_something(&i);\n" 02224 " }\n" 02225 " }\n" 02226 "}\n" 02227 "When you see this message it is always safe to reduce the variable scope 1 level."); 02228 } 02229 02230 //--------------------------------------------------------------------------- 02231 // Check for constant function parameters 02232 //--------------------------------------------------------------------------- 02233 void CheckOther::checkConstantFunctionParameter() 02234 { 02235 if (!_settings->isEnabled("performance") || _tokenizer->isC()) 02236 return; 02237 02238 const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); 02239 02240 for (unsigned int i = 1; i < symbolDatabase->getVariableListSize(); i++) { 02241 const Variable* var = symbolDatabase->getVariableFromVarId(i); 02242 if (!var || !var->isArgument() || !var->isClass() || !var->isConst() || var->isPointer() || var->isArray() || var->isReference()) 02243 continue; 02244 02245 const Token* const tok = var->typeStartToken(); 02246 // TODO: False negatives. This pattern only checks for string. 02247 // Investigate if there are other classes in the std 02248 // namespace and add them to the pattern. There are 02249 // streams for example (however it seems strange with 02250 // const stream parameter). 02251 if (Token::Match(tok, "std :: string|wstring")) { 02252 passedByValueError(tok, var->name()); 02253 } else if (Token::Match(tok, "std :: %type% <") && !Token::simpleMatch(tok->linkAt(3), "> ::")) { 02254 passedByValueError(tok, var->name()); 02255 } else if (var->type()) { // Check if type is a struct or class. 02256 passedByValueError(tok, var->name()); 02257 } 02258 } 02259 } 02260 02261 void CheckOther::passedByValueError(const Token *tok, const std::string &parname) 02262 { 02263 reportError(tok, Severity::performance, "passedByValue", 02264 "Function parameter '" + parname + "' should be passed by reference.\n" 02265 "Parameter '" + parname + "' is passed by value. It could be passed " 02266 "as a (const) reference which is usually faster and recommended in C++."); 02267 } 02268 02269 //--------------------------------------------------------------------------- 02270 // Check usage of char variables.. 02271 //--------------------------------------------------------------------------- 02272 static bool isChar(const Variable* var) 02273 { 02274 return(var && !var->isPointer() && !var->isArray() && var->typeStartToken()->str() == "char"); 02275 } 02276 02277 static bool isSignedChar(const Variable* var) 02278 { 02279 return(isChar(var) && !var->typeStartToken()->isUnsigned()); 02280 } 02281 02282 void CheckOther::checkCharVariable() 02283 { 02284 if (!_settings->isEnabled("warning")) 02285 return; 02286 02287 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 02288 const std::size_t functions = symbolDatabase->functionScopes.size(); 02289 for (std::size_t i = 0; i < functions; ++i) { 02290 const Scope * scope = symbolDatabase->functionScopes[i]; 02291 for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { 02292 if ((tok->str() != ".") && Token::Match(tok->next(), "%var% [ %var% ]")) { 02293 const Variable* arrayvar = tok->next()->variable(); 02294 const Variable* indexvar = tok->tokAt(3)->variable(); 02295 const MathLib::bigint arraysize = (arrayvar && arrayvar->isArray()) ? arrayvar->dimension(0U) : 0; 02296 if (isSignedChar(indexvar) && arraysize > 0x80) 02297 charArrayIndexError(tok->next()); 02298 } 02299 02300 else if (Token::Match(tok, "[;{}] %var% = %any% [&^|] %any% ;")) { 02301 // is a char variable used in the calculation? 02302 if (!isSignedChar(tok->tokAt(3)->variable()) && 02303 !isSignedChar(tok->tokAt(5)->variable())) 02304 continue; 02305 02306 // it's ok with a bitwise and where the other operand is 0xff or less.. 02307 if (tok->strAt(4) == "&") { 02308 if (tok->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok->strAt(3))) 02309 continue; 02310 if (tok->tokAt(5)->isNumber() && MathLib::isGreater("0x100", tok->strAt(5))) 02311 continue; 02312 } 02313 02314 // is the result stored in a short|int|long? 02315 const Variable *var = tok->next()->variable(); 02316 if (var && Token::Match(var->typeStartToken(), "short|int|long") && !var->isPointer() && !var->isArray()) 02317 charBitOpError(tok->tokAt(4)); // This is an error.. 02318 } 02319 02320 else if (Token::Match(tok, "[;{}] %var% = %any% [&^|] ( * %var% ) ;")) { 02321 const Variable* var = tok->tokAt(7)->variable(); 02322 if (!var || !var->isPointer() || var->typeStartToken()->str() != "char" || var->typeStartToken()->isUnsigned()) 02323 continue; 02324 // it's ok with a bitwise and where the other operand is 0xff or less.. 02325 if (tok->strAt(4) == "&" && tok->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok->strAt(3))) 02326 continue; 02327 02328 // is the result stored in a short|int|long? 02329 var = tok->next()->variable(); 02330 if (var && Token::Match(var->typeStartToken(), "short|int|long") && !var->isPointer() && !var->isArray()) 02331 charBitOpError(tok->tokAt(4)); // This is an error.. 02332 } 02333 } 02334 } 02335 } 02336 02337 void CheckOther::charArrayIndexError(const Token *tok) 02338 { 02339 reportError(tok, 02340 Severity::warning, 02341 "charArrayIndex", 02342 "Signed 'char' type used as array index.\n" 02343 "Signed 'char' type used as array index. If the value " 02344 "can be greater than 127 there will be a buffer underflow " 02345 "because of sign extension."); 02346 } 02347 02348 void CheckOther::charBitOpError(const Token *tok) 02349 { 02350 reportError(tok, 02351 Severity::warning, 02352 "charBitOp", 02353 "When using 'char' variables in bit operations, sign extension can generate unexpected results.\n" 02354 "When using 'char' variables in bit operations, sign extension can generate unexpected results. For example:\n" 02355 " char c = 0x80;\n" 02356 " int i = 0 | c;\n" 02357 " if (i & 0x8000)\n" 02358 " printf(\"not expected\");\n" 02359 "The \"not expected\" will be printed on the screen."); 02360 } 02361 02362 //--------------------------------------------------------------------------- 02363 // Incomplete statement.. 02364 //--------------------------------------------------------------------------- 02365 void CheckOther::checkIncompleteStatement() 02366 { 02367 if (!_settings->isEnabled("warning")) 02368 return; 02369 02370 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 02371 if (tok->str() == "(") { 02372 tok = tok->link(); 02373 if (Token::simpleMatch(tok, ") {") && Token::simpleMatch(tok->next()->link(), "} ;")) 02374 tok = tok->next()->link(); 02375 } 02376 02377 else if (Token::simpleMatch(tok, "= {")) 02378 tok = tok->next()->link(); 02379 02380 // C++11 struct/array initialization in initializer list 02381 else if (tok->str() == "{" && Token::Match(tok->tokAt(-2), ",|: %var%") && Token::Match(tok->link(), "} [,{]")) 02382 tok = tok->link(); 02383 02384 // C++11 vector initialization 02385 else if (Token::Match(tok,"> %var% {")) 02386 tok = tok->linkAt(2); 02387 02388 else if (Token::Match(tok, "[;{}] %str%") || Token::Match(tok, "[;{}] %num%")) { 02389 // No warning if numeric constant is followed by a "." or "," 02390 if (Token::Match(tok->next(), "%num% [,.]")) 02391 continue; 02392 02393 // bailout if there is a "? :" in this statement 02394 bool bailout = false; 02395 for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { 02396 if (tok2->str() == "?") 02397 bailout = true; 02398 else if (tok2->str() == ";") 02399 break; 02400 } 02401 if (bailout) 02402 continue; 02403 02404 constStatementError(tok->next(), tok->next()->isNumber() ? "numeric" : "string"); 02405 } 02406 } 02407 } 02408 02409 void CheckOther::constStatementError(const Token *tok, const std::string &type) 02410 { 02411 reportError(tok, Severity::warning, "constStatement", "Redundant code: Found a statement that begins with " + type + " constant."); 02412 } 02413 02414 //--------------------------------------------------------------------------- 02415 // str plus char 02416 //--------------------------------------------------------------------------- 02417 02418 void CheckOther::strPlusChar() 02419 { 02420 const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); 02421 const std::size_t functions = symbolDatabase->functionScopes.size(); 02422 for (std::size_t i = 0; i < functions; ++i) { 02423 const Scope * scope = symbolDatabase->functionScopes[i]; 02424 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 02425 if (Token::Match(tok, "[=(] %str% + %any%")) { 02426 // char constant.. 02427 if (tok->tokAt(3)->type() == Token::eChar) 02428 strPlusCharError(tok->next()); 02429 02430 // char variable.. 02431 if (isChar(tok->tokAt(3)->variable())) 02432 strPlusCharError(tok->next()); 02433 } 02434 } 02435 } 02436 } 02437 02438 void CheckOther::strPlusCharError(const Token *tok) 02439 { 02440 reportError(tok, Severity::error, "strPlusChar", "Unusual pointer arithmetic. A value of type 'char' is added to a string literal."); 02441 } 02442 02443 //--------------------------------------------------------------------------- 02444 //--------------------------------------------------------------------------- 02445 void CheckOther::checkZeroDivision() 02446 { 02447 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 02448 if (Token::Match(tok, "[/%] %num%") && 02449 MathLib::isInt(tok->next()->str()) && 02450 MathLib::toLongNumber(tok->next()->str()) == 0L) { 02451 zerodivError(tok); 02452 } else if (Token::Match(tok, "div|ldiv|lldiv|imaxdiv ( %num% , %num% )") && 02453 MathLib::isInt(tok->strAt(4)) && 02454 MathLib::toLongNumber(tok->strAt(4)) == 0L) { 02455 zerodivError(tok); 02456 } 02457 } 02458 } 02459 02460 void CheckOther::zerodivError(const Token *tok) 02461 { 02462 reportError(tok, Severity::error, "zerodiv", "Division by zero."); 02463 } 02464 02465 //--------------------------------------------------------------------------- 02466 //--------------------------------------------------------------------------- 02467 void CheckOther::checkMathFunctions() 02468 { 02469 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 02470 const std::size_t functions = symbolDatabase->functionScopes.size(); 02471 for (std::size_t i = 0; i < functions; ++i) { 02472 const Scope * scope = symbolDatabase->functionScopes[i]; 02473 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 02474 if (tok->varId()) 02475 continue; 02476 if (Token::Match(tok, "log|log10 ( %num% )")) { 02477 bool isNegative = MathLib::isNegative(tok->strAt(2)); 02478 bool isInt = MathLib::isInt(tok->strAt(2)); 02479 bool isFloat = MathLib::isFloat(tok->strAt(2)); 02480 if (isNegative && isInt && MathLib::toLongNumber(tok->strAt(2)) <= 0) { 02481 mathfunctionCallError(tok); // case log(-2) 02482 } else if (isNegative && isFloat && MathLib::toDoubleNumber(tok->strAt(2)) <= 0.) { 02483 mathfunctionCallError(tok); // case log(-2.0) 02484 } else if (!isNegative && isFloat && MathLib::toDoubleNumber(tok->strAt(2)) <= 0.) { 02485 mathfunctionCallError(tok); // case log(0.0) 02486 } else if (!isNegative && isInt && MathLib::toLongNumber(tok->strAt(2)) <= 0) { 02487 mathfunctionCallError(tok); // case log(0) 02488 } 02489 } 02490 02491 // acos( x ), asin( x ) where x is defined for interval [-1,+1], but not beyond 02492 else if (Token::Match(tok, "acos|asin ( %num% )") && 02493 std::fabs(MathLib::toDoubleNumber(tok->strAt(2))) > 1.0) { 02494 mathfunctionCallError(tok); 02495 } 02496 // sqrt( x ): if x is negative the result is undefined 02497 else if (Token::Match(tok, "sqrt|sqrtf|sqrtl ( %num% )") && 02498 MathLib::isNegative(tok->strAt(2))) { 02499 mathfunctionCallError(tok); 02500 } 02501 // atan2 ( x , y): x and y can not be zero, because this is mathematically not defined 02502 else if (Token::Match(tok, "atan2 ( %num% , %num% )") && 02503 MathLib::isNullValue(tok->strAt(2)) && 02504 MathLib::isNullValue(tok->strAt(4))) { 02505 mathfunctionCallError(tok, 2); 02506 } 02507 // fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined). 02508 else if (Token::Match(tok, "fmod ( %any%")) { 02509 const Token* nextArg = tok->tokAt(2)->nextArgument(); 02510 if (nextArg && nextArg->isNumber() && MathLib::isNullValue(nextArg->str())) 02511 mathfunctionCallError(tok, 2); 02512 } 02513 // pow ( x , y) If x is zero, and y is negative --> division by zero 02514 else if (Token::Match(tok, "pow ( %num% , %num% )") && 02515 MathLib::isNullValue(tok->strAt(2)) && 02516 MathLib::isNegative(tok->strAt(4))) { 02517 mathfunctionCallError(tok, 2); 02518 } 02519 } 02520 } 02521 } 02522 02523 void CheckOther::mathfunctionCallError(const Token *tok, const unsigned int numParam) 02524 { 02525 if (tok) { 02526 if (numParam == 1) 02527 reportError(tok, Severity::error, "wrongmathcall", "Passing value " + tok->strAt(2) + " to " + tok->str() + "() leads to undefined result."); 02528 else if (numParam == 2) 02529 reportError(tok, Severity::error, "wrongmathcall", "Passing values " + tok->strAt(2) + " and " + tok->strAt(4) + " to " + tok->str() + "() leads to undefined result."); 02530 } else 02531 reportError(tok, Severity::error, "wrongmathcall", "Passing value '#' to #() leads to undefined result."); 02532 } 02533 02534 //--------------------------------------------------------------------------- 02535 //--------------------------------------------------------------------------- 02536 void CheckOther::checkCCTypeFunctions() 02537 { 02538 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 02539 if (tok->varId() == 0 && 02540 Token::Match(tok, "isalnum|isalpha|iscntrl|isdigit|isgraph|islower|isprint|ispunct|isspace|isupper|isxdigit ( %num% ,|)") && 02541 MathLib::isNegative(tok->strAt(2))) { 02542 cctypefunctionCallError(tok, tok->str(), tok->strAt(2)); 02543 } 02544 } 02545 } 02546 void CheckOther::cctypefunctionCallError(const Token *tok, const std::string &functionName, const std::string &value) 02547 { 02548 reportError(tok, Severity::error, "wrongcctypecall", "Passing value " + value + " to " + functionName + "() causes undefined behavior which may lead to a crash."); 02549 } 02550 02551 //--------------------------------------------------------------------------- 02552 //--------------------------------------------------------------------------- 02553 void CheckOther::checkMisusedScopedObject() 02554 { 02555 // Skip this check for .c files 02556 if (_tokenizer->isC()) { 02557 return; 02558 } 02559 02560 const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); 02561 const std::size_t functions = symbolDatabase->functionScopes.size(); 02562 for (std::size_t i = 0; i < functions; ++i) { 02563 const Scope * scope = symbolDatabase->functionScopes[i]; 02564 for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { 02565 if (Token::Match(tok, "[;{}] %var% (") 02566 && Token::simpleMatch(tok->linkAt(2), ") ;") 02567 && symbolDatabase->isClassOrStruct(tok->next()->str()) 02568 && !tok->next()->function()) { 02569 tok = tok->next(); 02570 misusedScopeObjectError(tok, tok->str()); 02571 tok = tok->next(); 02572 } 02573 } 02574 } 02575 } 02576 02577 void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& varname) 02578 { 02579 reportError(tok, Severity::error, 02580 "unusedScopedObject", "Instance of '" + varname + "' object is destroyed immediately."); 02581 } 02582 02583 //------------------------------------------------------------------------------- 02584 // Comparing functions which are returning value of type bool 02585 //------------------------------------------------------------------------------- 02586 02587 void CheckOther::checkComparisonOfFuncReturningBool() 02588 { 02589 if (!_settings->isEnabled("style")) 02590 return; 02591 02592 if (!_tokenizer->isCPP()) 02593 return; 02594 02595 const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); 02596 02597 const std::size_t functions = symbolDatabase->functionScopes.size(); 02598 for (std::size_t i = 0; i < functions; ++i) { 02599 const Scope * scope = symbolDatabase->functionScopes[i]; 02600 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 02601 if (tok->type() != Token::eComparisonOp || tok->str() == "==" || tok->str() == "!=") 02602 continue; 02603 const Token *first_token; 02604 bool first_token_func_of_type_bool = false; 02605 if (tok->strAt(-1) == ")") { 02606 first_token = tok->previous()->link()->previous(); 02607 } else { 02608 first_token = tok->previous(); 02609 } 02610 if (Token::Match(first_token, "%var% (") && !Token::Match(first_token->previous(), "::|.")) { 02611 const Function* func = first_token->function(); 02612 if (func && func->tokenDef && func->tokenDef->strAt(-1) == "bool") { 02613 first_token_func_of_type_bool = true; 02614 } 02615 } 02616 02617 Token *second_token = tok->next(); 02618 bool second_token_func_of_type_bool = false; 02619 while (second_token->str()=="!") { 02620 second_token = second_token->next(); 02621 } 02622 if (Token::Match(second_token, "%var% (") && !Token::Match(second_token->previous(), "::|.")) { 02623 const Function* func = second_token->function(); 02624 if (func && func->tokenDef && func->tokenDef->strAt(-1) == "bool") { 02625 second_token_func_of_type_bool = true; 02626 } 02627 } 02628 02629 if ((first_token_func_of_type_bool == true) && (second_token_func_of_type_bool == true)) { 02630 comparisonOfTwoFuncsReturningBoolError(first_token->next(), first_token->str(), second_token->str()); 02631 } else if (first_token_func_of_type_bool == true) { 02632 comparisonOfFuncReturningBoolError(first_token->next(), first_token->str()); 02633 } else if (second_token_func_of_type_bool == true) { 02634 comparisonOfFuncReturningBoolError(second_token->previous(), second_token->str()); 02635 } 02636 } 02637 } 02638 } 02639 02640 void CheckOther::comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression) 02641 { 02642 reportError(tok, Severity::style, "comparisonOfFuncReturningBoolError", 02643 "Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n" 02644 "The return type of function '" + expression + "' is 'bool' " 02645 "and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)" 02646 " operator could cause unexpected results."); 02647 } 02648 02649 void CheckOther::comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2) 02650 { 02651 reportError(tok, Severity::style, "comparisonOfTwoFuncsReturningBoolError", 02652 "Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n" 02653 "The return type of function '" + expression1 + "' and function '" + expression2 + "' is 'bool' " 02654 "and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)" 02655 " operator could cause unexpected results."); 02656 } 02657 02658 //------------------------------------------------------------------------------- 02659 // Comparison of bool with bool 02660 //------------------------------------------------------------------------------- 02661 02662 void CheckOther::checkComparisonOfBoolWithBool() 02663 { 02664 // FIXME: This checking is "experimental" because of the false positives 02665 // when self checking lib/tokenize.cpp (#2617) 02666 if (!_settings->experimental) 02667 return; 02668 02669 if (!_settings->isEnabled("style")) 02670 return; 02671 02672 if (!_tokenizer->isCPP()) 02673 return; 02674 02675 const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); 02676 02677 const std::size_t functions = symbolDatabase->functionScopes.size(); 02678 for (std::size_t i = 0; i < functions; ++i) { 02679 const Scope * scope = symbolDatabase->functionScopes[i]; 02680 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 02681 if (tok->type() != Token::eComparisonOp || tok->str() == "==" || tok->str() == "!=") 02682 continue; 02683 bool first_token_bool = false; 02684 bool second_token_bool = false; 02685 02686 const Token *first_token = tok->previous(); 02687 if (first_token->varId()) { 02688 if (isBool(first_token->variable())) { 02689 first_token_bool = true; 02690 } 02691 } 02692 const Token *second_token = tok->next(); 02693 if (second_token->varId()) { 02694 if (isBool(second_token->variable())) { 02695 second_token_bool = true; 02696 } 02697 } 02698 if ((first_token_bool == true) && (second_token_bool == true)) { 02699 comparisonOfBoolWithBoolError(first_token->next(), first_token->str()); 02700 } 02701 } 02702 } 02703 } 02704 02705 void CheckOther::comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression) 02706 { 02707 reportError(tok, Severity::style, "comparisonOfBoolWithBoolError", 02708 "Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n" 02709 "The variable '" + expression + "' is of type 'bool' " 02710 "and comparing 'bool' value using relational (<, >, <= or >=)" 02711 " operator could cause unexpected results."); 02712 } 02713 02714 //--------------------------------------------------------------------------- 02715 //--------------------------------------------------------------------------- 02716 void CheckOther::checkIncorrectStringCompare() 02717 { 02718 if (!_settings->isEnabled("warning")) 02719 return; 02720 02721 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 02722 const std::size_t functions = symbolDatabase->functionScopes.size(); 02723 for (std::size_t i = 0; i < functions; ++i) { 02724 const Scope * scope = symbolDatabase->functionScopes[i]; 02725 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 02726 // skip "assert(str && ..)" and "assert(.. && str)" 02727 if (Token::Match(tok, "%var% (") && 02728 (Token::Match(tok->tokAt(2), "%str% &&") || Token::Match(tok->next()->link()->tokAt(-2), "&& %str% )")) && 02729 (tok->str().find("assert")+6U==tok->str().size() || tok->str().find("ASSERT")+6U==tok->str().size())) 02730 tok = tok->next()->link(); 02731 02732 if (Token::Match(tok, ". substr ( %any% , %num% ) ==|!= %str%")) { 02733 MathLib::bigint clen = MathLib::toLongNumber(tok->strAt(5)); 02734 std::size_t slen = Token::getStrLength(tok->tokAt(8)); 02735 if (clen != (int)slen) { 02736 incorrectStringCompareError(tok->next(), "substr", tok->strAt(8)); 02737 } 02738 } else if (Token::Match(tok, "%str% ==|!= %var% . substr ( %any% , %num% )")) { 02739 MathLib::bigint clen = MathLib::toLongNumber(tok->strAt(8)); 02740 std::size_t slen = Token::getStrLength(tok); 02741 if (clen != (int)slen) { 02742 incorrectStringCompareError(tok->next(), "substr", tok->str()); 02743 } 02744 } else if (Token::Match(tok, "&&|%oror%|( %str% &&|%oror%|)") && !Token::Match(tok, "( %str% )")) { 02745 incorrectStringBooleanError(tok->next(), tok->strAt(1)); 02746 } else if (Token::Match(tok, "if|while ( %str% )")) { 02747 incorrectStringBooleanError(tok->tokAt(2), tok->strAt(2)); 02748 } 02749 } 02750 } 02751 } 02752 02753 void CheckOther::incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string) 02754 { 02755 reportError(tok, Severity::warning, "incorrectStringCompare", "String literal " + string + " doesn't match length argument for " + func + "()."); 02756 } 02757 02758 void CheckOther::incorrectStringBooleanError(const Token *tok, const std::string& string) 02759 { 02760 reportError(tok, Severity::warning, "incorrectStringBooleanError", "Conversion of string literal " + string + " to bool always evaluates to true."); 02761 } 02762 02763 //----------------------------------------------------------------------------- 02764 // check for duplicate expressions in if statements 02765 // if (a) { } else if (a) { } 02766 //----------------------------------------------------------------------------- 02767 02768 static bool expressionHasSideEffects(const Token *first, const Token *last) 02769 { 02770 for (const Token *tok = first; tok != last->next(); tok = tok->next()) { 02771 // check for assignment 02772 if (tok->isAssignmentOp()) 02773 return true; 02774 02775 // check for inc/dec 02776 else if (tok->type() == Token::eIncDecOp) 02777 return true; 02778 02779 // check for function call 02780 else if (Token::Match(tok, "%var% (") && 02781 !(Token::Match(tok, "c_str|string") || tok->isStandardType())) 02782 return true; 02783 } 02784 02785 return false; 02786 } 02787 02788 void CheckOther::checkDuplicateIf() 02789 { 02790 if (!_settings->isEnabled("style")) 02791 return; 02792 02793 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 02794 02795 for (std::list<Scope>::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { 02796 const Token* const tok = scope->classDef; 02797 // only check if statements 02798 if (scope->type != Scope::eIf || !tok) 02799 continue; 02800 02801 std::map<std::string, const Token*> expressionMap; 02802 02803 // get the expression from the token stream 02804 std::string expression = tok->tokAt(2)->stringifyList(tok->next()->link()); 02805 02806 // save the expression and its location 02807 expressionMap.insert(std::make_pair(expression, tok)); 02808 02809 // find the next else if (...) statement 02810 const Token *tok1 = scope->classEnd; 02811 02812 // check all the else if (...) statements 02813 while ((Token::simpleMatch(tok1, "} else if (") && 02814 Token::simpleMatch(tok1->linkAt(3), ") {")) || 02815 (Token::simpleMatch(tok1, "} else { if (") && 02816 Token::simpleMatch(tok1->linkAt(4), ") {"))) { 02817 int conditionIndex=(tok1->strAt(3)=="(") ? 3 : 4; 02818 // get the expression from the token stream 02819 expression = tok1->tokAt(conditionIndex+1)->stringifyList(tok1->linkAt(conditionIndex)); 02820 02821 // try to look up the expression to check for duplicates 02822 std::map<std::string, const Token *>::iterator it = expressionMap.find(expression); 02823 02824 // found a duplicate 02825 if (it != expressionMap.end()) { 02826 // check for expressions that have side effects and ignore them 02827 if (!expressionHasSideEffects(tok1->tokAt(conditionIndex+1), tok1->linkAt(conditionIndex)->previous())) 02828 duplicateIfError(it->second, tok1->next()); 02829 } 02830 02831 // not a duplicate expression so save it and its location 02832 else 02833 expressionMap.insert(std::make_pair(expression, tok1->next())); 02834 02835 // find the next else if (...) statement 02836 tok1 = tok1->linkAt(conditionIndex)->next()->link(); 02837 } 02838 } 02839 } 02840 02841 void CheckOther::duplicateIfError(const Token *tok1, const Token *tok2) 02842 { 02843 std::list<const Token *> toks; 02844 toks.push_back(tok2); 02845 toks.push_back(tok1); 02846 02847 reportError(toks, Severity::style, "duplicateIf", "Duplicate conditions in 'if' and related 'else if'.\n" 02848 "Duplicate conditions in 'if' and related 'else if'. This is suspicious and might indicate " 02849 "a cut and paste or logic error. Please examine this code carefully to determine " 02850 "if it is correct."); 02851 } 02852 02853 //----------------------------------------------------------------------------- 02854 // check for duplicate code in if and else branches 02855 // if (a) { b = true; } else { b = true; } 02856 //----------------------------------------------------------------------------- 02857 void CheckOther::checkDuplicateBranch() 02858 { 02859 if (!_settings->isEnabled("style")) 02860 return; 02861 02862 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 02863 02864 std::list<Scope>::const_iterator scope; 02865 02866 for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { 02867 if (scope->type != Scope::eIf && scope->type != Scope::eElseIf) 02868 continue; 02869 02870 // check all the code in the function for if (..) else 02871 if (Token::simpleMatch(scope->classEnd, "} else {")) { 02872 // Make sure there are no macros (different macros might be expanded 02873 // to the same code) 02874 bool macro = false; 02875 for (const Token *tok = scope->classStart; tok != scope->classEnd->linkAt(2); tok = tok->next()) { 02876 if (tok->isExpandedMacro()) { 02877 macro = true; 02878 break; 02879 } 02880 } 02881 if (macro) 02882 continue; 02883 02884 // save if branch code 02885 std::string branch1 = scope->classStart->next()->stringifyList(scope->classEnd); 02886 02887 // save else branch code 02888 std::string branch2 = scope->classEnd->tokAt(3)->stringifyList(scope->classEnd->linkAt(2)); 02889 02890 // check for duplicates 02891 if (branch1 == branch2) 02892 duplicateBranchError(scope->classDef, scope->classEnd->next()); 02893 } 02894 } 02895 } 02896 02897 void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2) 02898 { 02899 std::list<const Token *> toks; 02900 toks.push_back(tok2); 02901 toks.push_back(tok1); 02902 02903 reportError(toks, Severity::style, "duplicateBranch", "Found duplicate branches for 'if' and 'else'.\n" 02904 "Finding the same code in an 'if' and related 'else' branch is suspicious and " 02905 "might indicate a cut and paste or logic error. Please examine this code " 02906 "carefully to determine if it is correct."); 02907 } 02908 02909 02910 //----------------------------------------------------------------------------- 02911 // Check for a free() of an invalid address 02912 // char* p = malloc(100); 02913 // free(p + 10); 02914 //----------------------------------------------------------------------------- 02915 void CheckOther::checkInvalidFree() 02916 { 02917 std::map<unsigned int, bool> allocatedVariables; 02918 for (const Token* tok = _tokenizer->tokens(); tok; tok = tok->next()) { 02919 02920 // Keep track of which variables were assigned addresses to newly-allocated memory 02921 if (Token::Match(tok, "%var% = malloc|g_malloc|new")) { 02922 allocatedVariables.insert(std::make_pair(tok->varId(), false)); 02923 } 02924 02925 // If a previously-allocated pointer is incremented or decremented, any subsequent 02926 // free involving pointer arithmetic may or may not be invalid, so we should only 02927 // report an inconclusive result. 02928 else if (Token::Match(tok, "%var% = %var% +|-") && 02929 tok->varId() == tok->tokAt(2)->varId() && 02930 allocatedVariables.find(tok->varId()) != allocatedVariables.end()) { 02931 if (_settings->inconclusive) 02932 allocatedVariables[tok->varId()] = true; 02933 else 02934 allocatedVariables.erase(tok->varId()); 02935 } 02936 02937 // If a previously-allocated pointer is assigned a completely new value, 02938 // we can't know if any subsequent free() on that pointer is valid or not. 02939 else if (Token::Match(tok, "%var% = ")) { 02940 allocatedVariables.erase(tok->varId()); 02941 } 02942 02943 // If a variable that was previously assigned a newly-allocated memory location is 02944 // added or subtracted from when used to free the memory, report an error. 02945 else if (Token::Match(tok, "free|g_free|delete ( %any% +|- %any%") || 02946 Token::Match(tok, "delete [ ] ( %any% +|- %any%") || 02947 Token::Match(tok, "delete %any% +|- %any%")) { 02948 02949 const int varIdx = tok->strAt(1) == "(" ? 2 : 02950 tok->strAt(3) == "(" ? 4 : 1; 02951 const unsigned int var1 = tok->tokAt(varIdx)->varId(); 02952 const unsigned int var2 = tok->tokAt(varIdx + 2)->varId(); 02953 const std::map<unsigned int, bool>::iterator alloc1 = allocatedVariables.find(var1); 02954 const std::map<unsigned int, bool>::iterator alloc2 = allocatedVariables.find(var2); 02955 if (alloc1 != allocatedVariables.end()) { 02956 invalidFreeError(tok, alloc1->second); 02957 } else if (alloc2 != allocatedVariables.end()) { 02958 invalidFreeError(tok, alloc2->second); 02959 } 02960 } 02961 02962 // If the previously-allocated variable is passed in to another function 02963 // as a parameter, it might be modified, so we shouldn't report an error 02964 // if it is later used to free memory 02965 else if (Token::Match(tok, "%var% (")) { 02966 const Token* tok2 = Token::findmatch(tok->next(), "%var%", tok->linkAt(1)); 02967 while (tok2 != NULL) { 02968 allocatedVariables.erase(tok2->varId()); 02969 tok2 = Token::findmatch(tok2->next(), "%var%", tok->linkAt(1)); 02970 } 02971 } 02972 } 02973 } 02974 02975 void CheckOther::invalidFreeError(const Token *tok, bool inconclusive) 02976 { 02977 reportError(tok, Severity::error, "invalidFree", "Invalid memory address freed.", inconclusive); 02978 } 02979 02980 02981 //----------------------------------------------------------------------------- 02982 // Check for double free 02983 // free(p); free(p); 02984 //----------------------------------------------------------------------------- 02985 void CheckOther::checkDoubleFree() 02986 { 02987 std::set<unsigned int> freedVariables; 02988 std::set<unsigned int> closeDirVariables; 02989 02990 for (const Token* tok = _tokenizer->tokens(); tok; tok = tok->next()) { 02991 // Keep track of any variables passed to "free()", "g_free()" or "closedir()", 02992 // and report an error if the same variable is passed twice. 02993 if (Token::Match(tok, "free|g_free|closedir ( %var% )")) { 02994 unsigned int var = tok->tokAt(2)->varId(); 02995 if (var) { 02996 if (Token::Match(tok, "free|g_free")) { 02997 if (freedVariables.find(var) != freedVariables.end()) 02998 doubleFreeError(tok, tok->strAt(2)); 02999 else 03000 freedVariables.insert(var); 03001 } else if (tok->str() == "closedir") { 03002 if (closeDirVariables.find(var) != closeDirVariables.end()) 03003 doubleCloseDirError(tok, tok->strAt(2)); 03004 else 03005 closeDirVariables.insert(var); 03006 } 03007 } 03008 } 03009 03010 // Keep track of any variables operated on by "delete" or "delete[]" 03011 // and report an error if the same variable is delete'd twice. 03012 else if (Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")) { 03013 int varIdx = (tok->strAt(1) == "[") ? 3 : 1; 03014 unsigned int var = tok->tokAt(varIdx)->varId(); 03015 if (var) { 03016 if (freedVariables.find(var) != freedVariables.end()) 03017 doubleFreeError(tok, tok->strAt(varIdx)); 03018 else 03019 freedVariables.insert(var); 03020 } 03021 } 03022 03023 // If this scope doesn't return, clear the set of previously freed variables 03024 else if (tok->str() == "}" && _tokenizer->IsScopeNoReturn(tok)) { 03025 freedVariables.clear(); 03026 closeDirVariables.clear(); 03027 } 03028 03029 // If this scope is a "for" or "while" loop that contains "break" or "continue", 03030 // give up on trying to figure out the flow of execution and just clear the set 03031 // of previously freed variables. 03032 // TODO: There are false negatives. This bailout is only needed when the 03033 // loop will exit without free()'ing the memory on the last iteration. 03034 else if (tok->str() == "}" && tok->link() && tok->link()->previous() && 03035 tok->link()->linkAt(-1) && 03036 Token::Match(tok->link()->linkAt(-1)->previous(), "while|for") && 03037 Token::findmatch(tok->link()->linkAt(-1), "break|continue ;", tok) != NULL) { 03038 freedVariables.clear(); 03039 closeDirVariables.clear(); 03040 } 03041 03042 // If a variable is passed to a function, remove it from the set of previously freed variables 03043 else if (Token::Match(tok, "%var% (") && !Token::Match(tok, "printf|sprintf|snprintf|fprintf|wprintf|swprintf|fwprintf")) { 03044 03045 // If this is a new function definition, clear all variables 03046 if (Token::simpleMatch(tok->next()->link(), ") {")) { 03047 freedVariables.clear(); 03048 closeDirVariables.clear(); 03049 } 03050 // If it is a function call, then clear those variables in its argument list 03051 else if (Token::simpleMatch(tok->next()->link(), ") ;")) { 03052 for (const Token* tok2 = tok->tokAt(2); tok2 != tok->linkAt(1); tok2 = tok2->next()) { 03053 if (tok2->varId()) { 03054 unsigned int var = tok2->varId(); 03055 freedVariables.erase(var); 03056 closeDirVariables.erase(var); 03057 } 03058 } 03059 } 03060 } 03061 03062 // If a pointer is assigned a new value, remove it from the set of previously freed variables 03063 else if (Token::Match(tok, "%var% =")) { 03064 unsigned int var = tok->varId(); 03065 if (var) { 03066 freedVariables.erase(var); 03067 closeDirVariables.erase(var); 03068 } 03069 } 03070 03071 // Any control statements in-between delete, free() or closedir() statements 03072 // makes it unclear whether any subsequent statements would be redundant. 03073 if (Token::Match(tok, "if|else|for|while|break|continue|goto|return|throw|switch")) { 03074 freedVariables.clear(); 03075 closeDirVariables.clear(); 03076 } 03077 } 03078 } 03079 03080 void CheckOther::doubleFreeError(const Token *tok, const std::string &varname) 03081 { 03082 reportError(tok, Severity::error, "doubleFree", "Memory pointed to by '" + varname +"' is freed twice."); 03083 } 03084 03085 void CheckOther::doubleCloseDirError(const Token *tok, const std::string &varname) 03086 { 03087 reportError(tok, Severity::error, "doubleCloseDir", "Directory handle '" + varname +"' closed twice."); 03088 } 03089 03090 namespace { 03091 struct ExpressionTokens { 03092 const Token *start; 03093 const Token *end; 03094 int count; 03095 bool inconclusiveFunction; 03096 ExpressionTokens(const Token *s, const Token *e): start(s), end(e), count(1), inconclusiveFunction(false) {} 03097 }; 03098 03099 struct FuncFilter { 03100 FuncFilter(const Scope *scope, const Token *tok): _scope(scope), _tok(tok) {} 03101 03102 bool operator()(const Function* func) const { 03103 bool matchingFunc = func->type == Function::eFunction && 03104 _tok->str() == func->token->str(); 03105 // either a class function, or a global function with the same name 03106 return (_scope && _scope == func->nestedIn && matchingFunc) || 03107 (!_scope && matchingFunc); 03108 } 03109 const Scope *_scope; 03110 const Token *_tok; 03111 }; 03112 03113 bool inconclusiveFunctionCall(const std::list<const Function*> &constFunctions, 03114 const ExpressionTokens &tokens) 03115 { 03116 const Token *start = tokens.start; 03117 const Token *end = tokens.end; 03118 // look for function calls between start and end... 03119 for (const Token *tok = start; tok && tok != end; tok = tok->next()) { 03120 if (tok != start && tok->str() == "(") { 03121 // go back to find the function call. 03122 const Token *prev = tok->previous(); 03123 if (!prev) 03124 continue; 03125 if (prev->str() == ">") { 03126 // ignore template functions like boo<double>() 03127 return true; 03128 } 03129 if (prev->isName()) { 03130 const Variable *v = 0; 03131 if (Token::Match(prev->tokAt(-2), "%var% .")) { 03132 const Token *scope = prev->tokAt(-2); 03133 v = scope->variable(); 03134 } 03135 // hard coded list of safe, no-side-effect functions 03136 if (v == 0 && Token::Match(prev, "strcmp|strncmp|strlen|wcscmp|wcsncmp|wcslen|memcmp|strcasecmp|strncasecmp")) 03137 return false; 03138 std::list<const Function*>::const_iterator it = std::find_if(constFunctions.begin(), 03139 constFunctions.end(), 03140 FuncFilter(v ? v->typeScope(): 0, prev)); 03141 if (it == constFunctions.end()) 03142 return true; 03143 } 03144 } 03145 } 03146 return false; 03147 } 03148 03149 class Expressions { 03150 public: 03151 Expressions(const std::list<const Function*> &constFunctions) 03152 : _start(0), 03153 _lastTokens(0), 03154 _constFunctions(constFunctions) { } 03155 03156 void endExpr(const Token *end) { 03157 const std::string &e = _expression.str(); 03158 if (!e.empty()) { 03159 std::map<std::string, ExpressionTokens>::iterator it = _expressions.find(e); 03160 bool lastInconclusive = _lastTokens && _lastTokens->inconclusiveFunction; 03161 if (it == _expressions.end()) { 03162 ExpressionTokens exprTokens(_start, end); 03163 exprTokens.inconclusiveFunction = lastInconclusive || inconclusiveFunctionCall( 03164 _constFunctions, exprTokens); 03165 _expressions.insert(std::make_pair(e, exprTokens)); 03166 _lastTokens = &_expressions.find(e)->second; 03167 } else { 03168 ExpressionTokens &expr = it->second; 03169 expr.count += 1; 03170 expr.inconclusiveFunction = expr.inconclusiveFunction || lastInconclusive; 03171 _lastTokens = &expr; 03172 } 03173 } 03174 _expression.str(""); 03175 _start = 0; 03176 } 03177 03178 void append(const Token *tok) { 03179 if (!_start) 03180 _start = tok; 03181 _expression << tok->str(); 03182 } 03183 03184 std::map<std::string,ExpressionTokens> &getMap() { 03185 return _expressions; 03186 } 03187 03188 private: 03189 std::map<std::string, ExpressionTokens> _expressions; 03190 std::ostringstream _expression; 03191 const Token *_start; 03192 ExpressionTokens *_lastTokens; 03193 const std::list<const Function*> &_constFunctions; 03194 }; 03195 03196 bool notconst(const Function* func) 03197 { 03198 return !func->isConst; 03199 } 03200 03201 void getConstFunctions(const SymbolDatabase *symbolDatabase, std::list<const Function*> &constFunctions) 03202 { 03203 std::list<Scope>::const_iterator scope; 03204 for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { 03205 std::list<Function>::const_iterator func; 03206 // only add const functions that do not have a non-const overloaded version 03207 // since it is pretty much impossible to tell which is being called. 03208 typedef std::map<std::string, std::list<const Function*> > StringFunctionMap; 03209 StringFunctionMap functionsByName; 03210 for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { 03211 functionsByName[func->tokenDef->str()].push_back(&*func); 03212 } 03213 for (StringFunctionMap::iterator it = functionsByName.begin(); 03214 it != functionsByName.end(); ++it) { 03215 std::list<const Function*>::const_iterator nc = std::find_if(it->second.begin(), it->second.end(), notconst); 03216 if (nc == it->second.end()) { 03217 // ok to add all of them 03218 constFunctions.splice(constFunctions.end(), it->second); 03219 } 03220 } 03221 } 03222 } 03223 03224 } 03225 03226 void CheckOther::checkExpressionRange(const std::list<const Function*> &constFunctions, 03227 const Token *start, 03228 const Token *end, 03229 const std::string &toCheck) 03230 { 03231 if (!start || !end) 03232 return; 03233 Expressions expressions(constFunctions); 03234 std::string opName; 03235 int level = 0; 03236 for (const Token *tok = start->next(); tok && tok != end; tok = tok->next()) { 03237 if (tok->str() == ")") 03238 level--; 03239 else if (tok->str() == "(") 03240 level++; 03241 03242 if (level == 0 && Token::Match(tok, toCheck.c_str())) { 03243 opName = tok->str(); 03244 expressions.endExpr(tok); 03245 } else { 03246 expressions.append(tok); 03247 } 03248 } 03249 expressions.endExpr(end); 03250 std::map<std::string,ExpressionTokens>::const_iterator it = expressions.getMap().begin(); 03251 for (; it != expressions.getMap().end(); ++it) { 03252 // check expression.. 03253 bool valid = true; 03254 unsigned int parentheses = 0; // () 03255 unsigned int brackets = 0; // [] 03256 03257 // taking address? 03258 if (Token::Match(it->second.end->previous(), "%op% &")) { 03259 continue; 03260 } 03261 03262 for (const Token *tok = it->second.start; tok && tok != it->second.end; tok = tok->next()) { 03263 if (tok->str() == "(") { 03264 ++parentheses; 03265 } else if (tok->str() == ")") { 03266 if (parentheses == 0) { 03267 valid = false; 03268 break; 03269 } 03270 --parentheses; 03271 } else if (tok->str() == "[") { 03272 ++brackets; 03273 } else if (tok->str() == "]") { 03274 if (brackets == 0) { 03275 valid = false; 03276 break; 03277 } 03278 --brackets; 03279 } else if (tok->type() == Token::eIncDecOp) { 03280 valid = false; 03281 break; 03282 } 03283 } 03284 03285 if (!valid || parentheses!=0 || brackets!=0) 03286 continue; 03287 03288 const ExpressionTokens &expr = it->second; 03289 if (expr.count > 1 && !expr.inconclusiveFunction) { 03290 duplicateExpressionError(expr.start, expr.start, opName); 03291 } 03292 } 03293 } 03294 03295 void CheckOther::complexDuplicateExpressionCheck(const std::list<const Function*> &constFunctions, 03296 const Token *classStart, 03297 const std::string &toCheck, 03298 const std::string &alt) 03299 { 03300 std::string statementStart(",|=|?|:|return"); 03301 if (!alt.empty()) 03302 statementStart += "|" + alt; 03303 std::string statementEnd(";|,|?|:"); 03304 if (!alt.empty()) 03305 statementEnd += "|" + alt; 03306 03307 for (const Token *tok = classStart; tok && tok != classStart->link(); tok = tok->next()) { 03308 if (!Token::Match(tok, toCheck.c_str())) 03309 continue; 03310 03311 // look backward for the start of the statement 03312 const Token *start = 0; 03313 int level = 0; 03314 for (const Token *tok1 = tok->previous(); tok1 && tok1 != classStart; tok1 = tok1->previous()) { 03315 if (tok1->str() == ")") 03316 level++; 03317 else if (tok1->str() == "(") 03318 level--; 03319 03320 if (level < 0 || (level == 0 && Token::Match(tok1, statementStart.c_str()))) { 03321 start = tok1; 03322 break; 03323 } 03324 } 03325 const Token *end = 0; 03326 level = 0; 03327 // look for the end of the statement 03328 for (const Token *tok1 = tok->next(); tok1 && tok1 != classStart->link(); tok1 = tok1->next()) { 03329 if (tok1->str() == ")") 03330 level--; 03331 else if (tok1->str() == "(") 03332 level++; 03333 03334 if (level < 0 || (level == 0 && Token::Match(tok1, statementEnd.c_str()))) { 03335 end = tok1; 03336 break; 03337 } 03338 } 03339 checkExpressionRange(constFunctions, start, end, toCheck); 03340 } 03341 } 03342 03343 //--------------------------------------------------------------------------- 03344 // check for the same expression on both sides of an operator 03345 // (x == x), (x && x), (x || x) 03346 // (x.y == x.y), (x.y && x.y), (x.y || x.y) 03347 //--------------------------------------------------------------------------- 03348 void CheckOther::checkDuplicateExpression() 03349 { 03350 if (!_settings->isEnabled("style")) 03351 return; 03352 03353 // Parse all executing scopes.. 03354 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 03355 03356 std::list<Scope>::const_iterator scope; 03357 std::list<const Function*> constFunctions; 03358 getConstFunctions(symbolDatabase, constFunctions); 03359 03360 for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { 03361 // only check functions 03362 if (scope->type != Scope::eFunction) 03363 continue; 03364 03365 complexDuplicateExpressionCheck(constFunctions, scope->classStart, "%or%", ""); 03366 complexDuplicateExpressionCheck(constFunctions, scope->classStart, "%oror%", ""); 03367 complexDuplicateExpressionCheck(constFunctions, scope->classStart, "&", "%oror%|%or%"); 03368 complexDuplicateExpressionCheck(constFunctions, scope->classStart, "&&", "%oror%|%or%"); 03369 03370 for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) { 03371 if (Token::Match(tok, ",|=|return|(|&&|%oror% %var% %comp%|- %var% )|&&|%oror%|;|,") && 03372 tok->strAt(1) == tok->strAt(3)) { 03373 // float == float and float != float are valid NaN checks 03374 // float - float is a valid Inf check 03375 if (Token::Match(tok->tokAt(2), "==|!=|-") && tok->next()->varId()) { 03376 const Variable *var = tok->next()->variable(); 03377 if (var && var->typeStartToken() == var->typeEndToken()) { 03378 if (Token::Match(var->typeStartToken(), "float|double")) 03379 continue; 03380 } 03381 } 03382 03383 // If either variable token is an expanded macro then 03384 // don't write the warning 03385 if (tok->next()->isExpandedMacro() || tok->tokAt(3)->isExpandedMacro()) 03386 continue; 03387 03388 duplicateExpressionError(tok->next(), tok->tokAt(3), tok->strAt(2)); 03389 } else if (Token::Match(tok, ",|=|return|(|&&|%oror% %var% . %var% %comp%|- %var% . %var% )|&&|%oror%|;|,") && 03390 tok->strAt(1) == tok->strAt(5) && tok->strAt(3) == tok->strAt(7)) { 03391 03392 // If either variable token is an expanded macro then 03393 // don't write the warning 03394 if (tok->next()->isExpandedMacro() || tok->tokAt(6)->isExpandedMacro()) 03395 continue; 03396 03397 duplicateExpressionError(tok->next(), tok->tokAt(6), tok->strAt(4)); 03398 } 03399 } 03400 } 03401 } 03402 03403 void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op) 03404 { 03405 std::list<const Token *> toks; 03406 toks.push_back(tok2); 03407 toks.push_back(tok1); 03408 03409 reportError(toks, Severity::style, "duplicateExpression", "Same expression on both sides of \'" + op + "\'.\n" 03410 "Finding the same expression on both sides of an operator is suspicious and might " 03411 "indicate a cut and paste or logic error. Please examine this code carefully to " 03412 "determine if it is correct."); 03413 } 03414 03415 //--------------------------------------------------------------------------- 03416 // Check for string comparison involving two static strings. 03417 // if(strcmp("00FF00","00FF00")==0) // <- statement is always true 03418 //--------------------------------------------------------------------------- 03419 void CheckOther::checkAlwaysTrueOrFalseStringCompare() 03420 { 03421 if (!_settings->isEnabled("warning")) 03422 return; 03423 03424 const Token *tok = _tokenizer->tokens(); 03425 while (tok && (tok = Token::findmatch(tok, "strncmp|strcmp|stricmp|strcmpi|strcasecmp|wcscmp|wcsncmp ( %str% , %str% ")) != NULL) { 03426 const std::string &str1 = tok->strAt(2); 03427 const std::string &str2 = tok->strAt(4); 03428 alwaysTrueFalseStringCompareError(tok, str1, str2); 03429 tok = tok->tokAt(5); 03430 } 03431 03432 tok = _tokenizer->tokens(); 03433 while (tok && (tok = Token::findmatch(tok, "QString :: compare ( %str% , %str% )")) != NULL) { 03434 const std::string &str1 = tok->strAt(4); 03435 const std::string &str2 = tok->strAt(6); 03436 alwaysTrueFalseStringCompareError(tok, str1, str2); 03437 tok = tok->tokAt(7); 03438 } 03439 03440 tok = _tokenizer->tokens(); 03441 while (tok && (tok = Token::findmatch(tok, "strncmp|strcmp|stricmp|strcmpi|strcasecmp|wcscmp|wcsncmp ( %var% , %var% ")) != NULL) { 03442 const std::string &str1 = tok->strAt(2); 03443 const std::string &str2 = tok->strAt(4); 03444 if (str1 == str2) 03445 alwaysTrueStringVariableCompareError(tok, str1, str2); 03446 tok = tok->tokAt(5); 03447 } 03448 03449 tok = _tokenizer->tokens(); 03450 while (tok && (tok = Token::findmatch(tok, "!!+ %str% ==|!= %str% !!+")) != NULL) { 03451 const std::string &str1 = tok->strAt(1); 03452 const std::string &str2 = tok->strAt(3); 03453 alwaysTrueFalseStringCompareError(tok, str1, str2); 03454 tok = tok->tokAt(5); 03455 } 03456 } 03457 03458 void CheckOther::alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2) 03459 { 03460 const std::size_t stringLen = 10; 03461 const std::string string1 = (str1.size() < stringLen) ? str1 : (str1.substr(0, stringLen-2) + ".."); 03462 const std::string string2 = (str2.size() < stringLen) ? str2 : (str2.substr(0, stringLen-2) + ".."); 03463 03464 reportError(tok, Severity::warning, "staticStringCompare", 03465 "Unnecessary comparison of static strings.\n" 03466 "The compared strings, '" + string1 + "' and '" + string2 + "', are always " + (str1==str2?"identical":"unequal") + ". " 03467 "Therefore the comparison is unnecessary and looks suspicious."); 03468 } 03469 03470 void CheckOther::alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2) 03471 { 03472 reportError(tok, Severity::warning, "stringCompare", 03473 "Comparison of identical string variables.\n" 03474 "The compared strings, '" + str1 + "' and '" + str2 + "', are identical. " 03475 "This could be a logic bug."); 03476 } 03477 03478 03479 //----------------------------------------------------------------------------- 03480 //----------------------------------------------------------------------------- 03481 void CheckOther::checkSuspiciousStringCompare() 03482 { 03483 if (!_settings->isEnabled("warning")) 03484 return; 03485 03486 const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); 03487 const std::size_t functions = symbolDatabase->functionScopes.size(); 03488 for (std::size_t i = 0; i < functions; ++i) { 03489 const Scope * scope = symbolDatabase->functionScopes[i]; 03490 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 03491 if (tok->next()->type() != Token::eComparisonOp) 03492 continue; 03493 03494 const Token* varTok = tok; 03495 const Token* litTok = tok->tokAt(2); 03496 03497 if (varTok->strAt(-1) == "+" || litTok->strAt(1) == "+") 03498 continue; 03499 03500 if ((varTok->type() == Token::eString || varTok->type() == Token::eVariable) && (litTok->type() == Token::eString || litTok->type() == Token::eVariable) && litTok->type() != varTok->type()) { 03501 if (varTok->type() == Token::eString) 03502 std::swap(varTok, litTok); 03503 03504 const Variable *var = varTok->variable(); 03505 if (var) { 03506 if (_tokenizer->isC() || 03507 (var->isPointer() && varTok->strAt(-1) != "*" && !Token::Match(varTok->next(), "[.([]"))) 03508 suspiciousStringCompareError(tok, var->name()); 03509 } 03510 } 03511 } 03512 } 03513 } 03514 03515 void CheckOther::suspiciousStringCompareError(const Token* tok, const std::string& var) 03516 { 03517 reportError(tok, Severity::warning, "literalWithCharPtrCompare", 03518 "String literal compared with variable '" + var + "'. Did you intend to use strcmp() instead?"); 03519 } 03520 03521 03522 //----------------------------------------------------------------------------- 03523 //----------------------------------------------------------------------------- 03524 void CheckOther::checkModuloAlwaysTrueFalse() 03525 { 03526 if (!_settings->isEnabled("warning")) 03527 return; 03528 03529 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 03530 const std::size_t functions = symbolDatabase->functionScopes.size(); 03531 for (std::size_t i = 0; i < functions; ++i) { 03532 const Scope * scope = symbolDatabase->functionScopes[i]; 03533 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 03534 if ((Token::Match(tok, "% %num% %comp% %num%")) && 03535 (!tok->tokAt(4) || !tok->tokAt(4)->isArithmeticalOp())) { 03536 if (MathLib::isLessEqual(tok->strAt(1), tok->strAt(3))) 03537 moduloAlwaysTrueFalseError(tok, tok->strAt(1)); 03538 } 03539 } 03540 } 03541 } 03542 03543 void CheckOther::moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal) 03544 { 03545 reportError(tok, Severity::warning, "moduloAlwaysTrueFalse", 03546 "Comparison of modulo result is predetermined, because it is always less than " + maxVal + "."); 03547 } 03548 03549 //----------------------------------------------------------------------------- 03550 //----------------------------------------------------------------------------- 03551 void CheckOther::sizeofsizeof() 03552 { 03553 if (!_settings->isEnabled("warning")) 03554 return; 03555 03556 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 03557 if (Token::Match(tok, "sizeof (| sizeof")) { 03558 sizeofsizeofError(tok); 03559 tok = tok->next(); 03560 } 03561 } 03562 } 03563 03564 void CheckOther::sizeofsizeofError(const Token *tok) 03565 { 03566 reportError(tok, Severity::warning, 03567 "sizeofsizeof", "Calling 'sizeof' on 'sizeof'.\n" 03568 "Calling sizeof for 'sizeof looks like a suspicious code and " 03569 "most likely there should be just one 'sizeof'. The current " 03570 "code is equivalent to 'sizeof(size_t)'"); 03571 } 03572 03573 //----------------------------------------------------------------------------- 03574 //----------------------------------------------------------------------------- 03575 void CheckOther::sizeofCalculation() 03576 { 03577 if (!_settings->isEnabled("warning")) 03578 return; 03579 03580 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 03581 if (Token::simpleMatch(tok, "sizeof (")) { 03582 const Token* const end = tok->linkAt(1); 03583 for (const Token *tok2 = tok->tokAt(2); tok2 != end; tok2 = tok2->next()) { 03584 if (tok2->isConstOp() && (!tok2->isExpandedMacro() || _settings->inconclusive) && !Token::Match(tok2, ">|<|&") && (Token::Match(tok2->previous(), "%var%") || tok2->str() != "*")) { 03585 if (!(Token::Match(tok2->previous(), "%type%") || Token::Match(tok2->next(), "%type%"))) { 03586 sizeofCalculationError(tok2, tok2->isExpandedMacro()); 03587 break; 03588 } 03589 } else if (tok2->type() == Token::eIncDecOp) 03590 sizeofCalculationError(tok2, tok2->isExpandedMacro()); 03591 } 03592 } 03593 } 03594 } 03595 03596 void CheckOther::sizeofCalculationError(const Token *tok, bool inconclusive) 03597 { 03598 reportError(tok, Severity::warning, 03599 "sizeofCalculation", "Found calculation inside sizeof().", inconclusive); 03600 } 03601 03602 //----------------------------------------------------------------------------- 03603 // Check for code like: 03604 // seteuid(geteuid()) or setuid(getuid()), which first gets and then sets the 03605 // (effective) user id to itself. Very often this indicates a copy and paste 03606 // error. 03607 //----------------------------------------------------------------------------- 03608 void CheckOther::redundantGetAndSetUserId() 03609 { 03610 if (_settings->isEnabled("warning") 03611 && _settings->standards.posix) { 03612 03613 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 03614 if (Token::simpleMatch(tok, "setuid ( getuid ( ) )") 03615 || Token::simpleMatch(tok, "seteuid ( geteuid ( ) )") 03616 || Token::simpleMatch(tok, "setgid ( getgid ( ) )") 03617 || Token::simpleMatch(tok, "setegid ( getegid ( ) )")) { 03618 redundantGetAndSetUserIdError(tok); 03619 } 03620 } 03621 } 03622 } 03623 void CheckOther::redundantGetAndSetUserIdError(const Token *tok) 03624 { 03625 reportError(tok, Severity::warning, 03626 "redundantGetAndSetUserId", "Redundant get and set of user id.\n" 03627 "Redundant statement without any effect. First the user id is retrieved" 03628 "by get(e)uid() and then set with set(e)uid().", false); 03629 } 03630 03631 //----------------------------------------------------------------------------- 03632 // Check for code like sizeof()*sizeof() or sizeof(ptr)/value 03633 //----------------------------------------------------------------------------- 03634 void CheckOther::suspiciousSizeofCalculation() 03635 { 03636 if (!_settings->isEnabled("warning") || !_settings->inconclusive) 03637 return; 03638 03639 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 03640 if (Token::simpleMatch(tok, "sizeof (")) { 03641 const Token* const end = tok->linkAt(1); 03642 const Variable* var = end->previous()->variable(); 03643 if (end->strAt(-1) == "*" || (var && var->isPointer() && !var->isArray())) { 03644 if (end->strAt(1) == "/") 03645 divideSizeofError(tok); 03646 } else if (Token::simpleMatch(end, ") * sizeof")) 03647 multiplySizeofError(tok); 03648 } 03649 } 03650 } 03651 03652 void CheckOther::multiplySizeofError(const Token *tok) 03653 { 03654 reportError(tok, Severity::warning, 03655 "multiplySizeof", "Multiplying sizeof() with sizeof() indicates a logic error.", true); 03656 } 03657 03658 void CheckOther::divideSizeofError(const Token *tok) 03659 { 03660 reportError(tok, Severity::warning, 03661 "divideSizeof", "Division of result of sizeof() on pointer type.\n" 03662 "Division of result of sizeof() on pointer type. sizeof() returns the size of the pointer, " 03663 "not the size of the memory area it points to.", true); 03664 } 03665 03666 //----------------------------------------------------------------------------- 03667 //----------------------------------------------------------------------------- 03668 void CheckOther::checkAssignBoolToPointer() 03669 { 03670 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 03671 const std::size_t functions = symbolDatabase->functionScopes.size(); 03672 for (std::size_t i = 0; i < functions; ++i) { 03673 const Scope * scope = symbolDatabase->functionScopes[i]; 03674 for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { 03675 if (Token::Match(tok, "!!* %var% = %bool% ;")) { 03676 const Variable *var1(tok->next()->variable()); 03677 03678 // Is variable a pointer? 03679 if (var1 && var1->isPointer()) 03680 assignBoolToPointerError(tok->next()); 03681 } 03682 } 03683 } 03684 } 03685 03686 void CheckOther::assignBoolToPointerError(const Token *tok) 03687 { 03688 reportError(tok, Severity::error, "assignBoolToPointer", 03689 "Boolean value assigned to pointer."); 03690 } 03691 03692 //----------------------------------------------------------------------------- 03693 //----------------------------------------------------------------------------- 03694 void CheckOther::checkComparisonOfBoolExpressionWithInt() 03695 { 03696 if (!_settings->isEnabled("warning")) 03697 return; 03698 03699 const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); 03700 03701 const std::size_t functions = symbolDatabase->functionScopes.size(); 03702 for (std::size_t i = 0; i < functions; ++i) { 03703 const Scope * scope = symbolDatabase->functionScopes[i]; 03704 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 03705 // Skip template parameters 03706 if (tok->str() == "<" && tok->link()) { 03707 tok = tok->link(); 03708 continue; 03709 } 03710 03711 const Token* numTok = 0; 03712 const Token* opTok = 0; 03713 char op = 0; 03714 if (Token::Match(tok, "&&|%oror% %any% ) %comp% %any%")) { 03715 numTok = tok->tokAt(4); 03716 opTok = tok->tokAt(3); 03717 if (Token::Match(opTok, "<|>")) 03718 op = opTok->str()[0]; 03719 } else if (Token::Match(tok, "%any% %comp% ( %any% &&|%oror%")) { 03720 numTok = tok; 03721 opTok = tok->next(); 03722 if (Token::Match(opTok, "<|>")) 03723 op = opTok->str()[0]=='>'?'<':'>'; 03724 } 03725 03726 else if (Token::Match(tok, "! %var% %comp% %any%")) { 03727 numTok = tok->tokAt(3); 03728 opTok = tok->tokAt(2); 03729 if (Token::Match(opTok, "<|>")) 03730 op = opTok->str()[0]; 03731 } else if (Token::Match(tok, "%any% %comp% ! %var%")) { 03732 numTok = tok; 03733 opTok = tok->next(); 03734 if (Token::Match(opTok, "<|>")) 03735 op = opTok->str()[0]=='>'?'<':'>'; 03736 } 03737 03738 if (numTok && opTok) { 03739 if (numTok->isNumber()) { 03740 if (((numTok->str() != "0" && numTok->str() != "1") || !Token::Match(opTok, "!=|==")) && !((op == '<' && numTok->str() == "1") || (op == '>' && numTok->str() == "0"))) 03741 comparisonOfBoolExpressionWithIntError(tok, true); 03742 } else if (isNonBoolStdType(numTok->variable())) 03743 comparisonOfBoolExpressionWithIntError(tok, false); 03744 } 03745 } 03746 } 03747 } 03748 03749 void CheckOther::comparisonOfBoolExpressionWithIntError(const Token *tok, bool n0o1) 03750 { 03751 if (n0o1) 03752 reportError(tok, Severity::warning, "compareBoolExpressionWithInt", 03753 "Comparison of a boolean expression with an integer other than 0 or 1."); 03754 else 03755 reportError(tok, Severity::warning, "compareBoolExpressionWithInt", 03756 "Comparison of a boolean expression with an integer."); 03757 } 03758 03759 03760 //--------------------------------------------------------------------------- 03761 // Check testing sign of unsigned variables and pointers. 03762 //--------------------------------------------------------------------------- 03763 void CheckOther::checkSignOfUnsignedVariable() 03764 { 03765 if (!_settings->isEnabled("style")) 03766 return; 03767 03768 const bool inconclusive = _tokenizer->codeWithTemplates(); 03769 if (inconclusive && !_settings->inconclusive) 03770 return; 03771 03772 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 03773 03774 const std::size_t functions = symbolDatabase->functionScopes.size(); 03775 for (std::size_t i = 0; i < functions; ++i) { 03776 const Scope * scope = symbolDatabase->functionScopes[i]; 03777 // check all the code in the function 03778 for (const Token *tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 03779 if (Token::Match(tok, "%var% <|<= 0") && tok->varId() && !Token::Match(tok->previous(), "++|--|)|+|-|*|/|~|<<|>>") && !Token::Match(tok->tokAt(3), "+|-")) { 03780 const Variable *var = tok->variable(); 03781 if (var && var->typeEndToken()->isUnsigned()) 03782 unsignedLessThanZeroError(tok, var->name(), inconclusive); 03783 else if (var && var->isPointer() && tok->strAt(-1) != "*") 03784 pointerLessThanZeroError(tok, inconclusive); 03785 } else if (Token::Match(tok, "0 >|>= %var%") && tok->tokAt(2)->varId() && !Token::Match(tok->tokAt(3), "+|-|*|/") && !Token::Match(tok->previous(), "+|-|<<|>>|~")) { 03786 const Variable *var = tok->tokAt(2)->variable(); 03787 if (var && var->typeEndToken()->isUnsigned()) 03788 unsignedLessThanZeroError(tok, var->name(), inconclusive); 03789 else if (var && var->isPointer() && !Token::Match(tok->tokAt(3), "[.[]")) 03790 pointerLessThanZeroError(tok, inconclusive); 03791 } else if (Token::Match(tok, "0 <= %var%") && tok->tokAt(2)->varId() && !Token::Match(tok->tokAt(3), "+|-|*|/") && !Token::Match(tok->previous(), "+|-|<<|>>|~")) { 03792 const Variable *var = tok->tokAt(2)->variable(); 03793 if (var && var->typeEndToken()->isUnsigned()) 03794 unsignedPositiveError(tok, var->name(), inconclusive); 03795 else if (var && var->isPointer() && !Token::Match(tok->tokAt(3), "[.[]")) 03796 pointerPositiveError(tok, inconclusive); 03797 } else if (Token::Match(tok, "%var% >= 0") && tok->varId() && !Token::Match(tok->previous(), "++|--|)|+|-|*|/|~|<<|>>") && !Token::Match(tok->tokAt(3), "+|-")) { 03798 const Variable *var = tok->variable(); 03799 if (var && var->typeEndToken()->isUnsigned()) 03800 unsignedPositiveError(tok, var->name(), inconclusive); 03801 else if (var && var->isPointer() && tok->strAt(-1) != "*") 03802 pointerPositiveError(tok, inconclusive); 03803 } 03804 } 03805 } 03806 } 03807 03808 void CheckOther::unsignedLessThanZeroError(const Token *tok, const std::string &varname, bool inconclusive) 03809 { 03810 if (inconclusive) { 03811 reportError(tok, Severity::style, "unsignedLessThanZero", 03812 "Checking if unsigned variable '" + varname + "' is less than zero. This might be a false warning.\n" 03813 "Checking if unsigned variable '" + varname + "' is less than zero. An unsigned " 03814 "variable will never be negative so it is either pointless or an error to check if it is. " 03815 "It's not known if the used constant is a template parameter or not and therefore " 03816 "this message might be a false warning.", true); 03817 } else { 03818 reportError(tok, Severity::style, "unsignedLessThanZero", 03819 "Checking if unsigned variable '" + varname + "' is less than zero.\n" 03820 "The unsigned variable '" + varname + "' will never be negative so it " 03821 "is either pointless or an error to check if it is."); 03822 } 03823 } 03824 03825 void CheckOther::pointerLessThanZeroError(const Token *tok, bool inconclusive) 03826 { 03827 reportError(tok, Severity::style, "pointerLessThanZero", 03828 "A pointer can not be negative so it is either pointless or an error to check if it is.", inconclusive); 03829 } 03830 03831 void CheckOther::unsignedPositiveError(const Token *tok, const std::string &varname, bool inconclusive) 03832 { 03833 if (inconclusive) { 03834 reportError(tok, Severity::style, "unsignedPositive", 03835 "Unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it.\n" 03836 "The unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it. " 03837 "It's not known if the used constant is a " 03838 "template parameter or not and therefore this message might be a false warning", true); 03839 } else { 03840 reportError(tok, Severity::style, "unsignedPositive", 03841 "Unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it."); 03842 } 03843 } 03844 03845 void CheckOther::pointerPositiveError(const Token *tok, bool inconclusive) 03846 { 03847 reportError(tok, Severity::style, "pointerPositive", 03848 "A pointer can not be negative so it is either pointless or an error to check if it is not.", inconclusive); 03849 } 03850 03851 /* 03852 This check rule works for checking the "const A a = getA()" usage when getA() returns "const A &" or "A &". 03853 In most scenarios, "const A & a = getA()" will be more efficient. 03854 */ 03855 void CheckOther::checkRedundantCopy() 03856 { 03857 if (!_settings->isEnabled("performance") || _tokenizer->isC()) 03858 return; 03859 03860 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 03861 03862 for (std::size_t i = 0; i < symbolDatabase->getVariableListSize(); i++) { 03863 const Variable* var = symbolDatabase->getVariableFromVarId(i); 03864 03865 if (!var || var->isReference() || !var->isConst() || var->isPointer() || !var->type()) // bailout if var is of standard type, if it is a pointer or non-const 03866 continue; 03867 03868 const Token* startTok = var->nameToken(); 03869 const Token* endToken; 03870 if (startTok->strAt(1) == "=") // %type% %var% = ... ; 03871 endToken = Token::findsimplematch(startTok->tokAt(2), ";"); 03872 else if (startTok->strAt(1) == "(") // %type% %var%(...) 03873 endToken = startTok->linkAt(1); 03874 else 03875 continue; 03876 03877 const Token* tok = startTok->tokAt(2); 03878 while (tok && Token::Match(tok, "%var% .|::")) 03879 tok = tok->tokAt(2); 03880 if (!Token::Match(tok, "%var% (")) 03881 continue; 03882 if (tok->linkAt(1)->next() != endToken) // bailout for usage like "const A a = getA()+3" 03883 continue; 03884 03885 const Function* func = tok->function(); 03886 if (func && func->tokenDef->strAt(-1) == "&") { 03887 redundantCopyError(startTok, startTok->str()); 03888 } 03889 } 03890 } 03891 void CheckOther::redundantCopyError(const Token *tok,const std::string& varname) 03892 { 03893 reportError(tok, Severity::performance, "redundantCopyLocalConst", 03894 "Use const reference for '" + varname + "' to avoid unnecessary data copying.\n" 03895 "The const variable '"+varname+"' is assigned a copy of the data. You can avoid " 03896 "the unnecessary data copying by converting '" + varname + "' to const reference."); 03897 } 03898 03899 //--------------------------------------------------------------------------- 03900 // Checking for shift by negative values 03901 //--------------------------------------------------------------------------- 03902 03903 void CheckOther::checkNegativeBitwiseShift() 03904 { 03905 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 03906 03907 const std::size_t functions = symbolDatabase->functionScopes.size(); 03908 for (std::size_t i = 0; i < functions; ++i) { 03909 const Scope * scope = symbolDatabase->functionScopes[i]; 03910 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 03911 03912 if ((Token::Match(tok,"%var% >>|<< %num%") || Token::Match(tok,"%num% >>|<< %num%")) && !Token::Match(tok->previous(),">>|<<")) { 03913 if (tok->isName()) { 03914 const Variable *var = tok->variable(); 03915 if (var && var->typeStartToken()->isStandardType() && (tok->strAt(2))[0] == '-') 03916 negativeBitwiseShiftError(tok); 03917 } else { 03918 if ((tok->strAt(2))[0] == '-') 03919 negativeBitwiseShiftError(tok); 03920 } 03921 } 03922 } 03923 } 03924 } 03925 03926 03927 void CheckOther::negativeBitwiseShiftError(const Token *tok) 03928 { 03929 reportError(tok, Severity::error, "shiftNegative", "Shifting by a negative value."); 03930 } 03931 03932 03933 //--------------------------------------------------------------------------- 03934 // Check for incompletely filled buffers. 03935 //--------------------------------------------------------------------------- 03936 void CheckOther::checkIncompleteArrayFill() 03937 { 03938 bool warning = _settings->isEnabled("warning"); 03939 bool portability = _settings->isEnabled("portability"); 03940 if (!_settings->inconclusive || (!portability && !warning)) 03941 return; 03942 03943 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 03944 03945 const std::size_t functions = symbolDatabase->functionScopes.size(); 03946 for (std::size_t i = 0; i < functions; ++i) { 03947 const Scope * scope = symbolDatabase->functionScopes[i]; 03948 for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { 03949 if (Token::Match(tok, "memset|memcpy|memmove ( %var% ,") && Token::Match(tok->linkAt(1)->tokAt(-2), ", %num% )")) { 03950 const Variable *var = tok->tokAt(2)->variable(); 03951 if (!var || !var->isArray() || var->dimensions().empty() || !var->dimension(0)) 03952 continue; 03953 03954 if (MathLib::toLongNumber(tok->linkAt(1)->strAt(-1)) == var->dimension(0)) { 03955 unsigned int size = _tokenizer->sizeOfType(var->typeStartToken()); 03956 if ((size != 1 && size != 100 && size != 0) || var->typeEndToken()->str() == "*") { 03957 if (warning) 03958 incompleteArrayFillError(tok, var->name(), tok->str(), false); 03959 } else if (var->typeStartToken()->str() == "bool" && portability) // sizeof(bool) is not 1 on all platforms 03960 incompleteArrayFillError(tok, var->name(), tok->str(), true); 03961 } 03962 } 03963 } 03964 } 03965 } 03966 03967 void CheckOther::incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean) 03968 { 03969 if (boolean) 03970 reportError(tok, Severity::portability, "incompleteArrayFill", 03971 "Array '" + buffer + "' might be filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n" 03972 "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but the type 'bool' is larger than 1 on some platforms. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", true); 03973 else 03974 reportError(tok, Severity::warning, "incompleteArrayFill", 03975 "Array '" + buffer + "' is filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n" 03976 "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but an element of the given array is larger than one byte. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", true); 03977 } 03978 03979 03980 void CheckOther::oppositeInnerCondition() 03981 { 03982 // FIXME: This check is experimental because of #4170 and #4186. Fix those tickets and remove the "experimental". 03983 if (!_settings->isEnabled("warning") || !_settings->inconclusive || !_settings->experimental) 03984 return; 03985 03986 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 03987 03988 for (std::list<Scope>::const_iterator scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { 03989 const Token* const toke = scope->classDef; 03990 03991 03992 if (scope->type == Scope::eIf && toke) { 03993 03994 const Token *op1Tok, *op2Tok; 03995 op1Tok = scope->classDef->tokAt(2); 03996 op2Tok = scope->classDef->tokAt(4); 03997 03998 if (scope->classDef->strAt(6) == "{") { 03999 04000 const char *oppositeCondition = NULL; 04001 04002 if (scope->classDef->strAt(3) == "==") 04003 oppositeCondition = "if ( %any% !=|<|>|<=|>= %any% )"; 04004 else if (scope->classDef->strAt(3) == "!=") 04005 oppositeCondition = "if ( %any% ==|>=|<= %any% )"; 04006 else if (scope->classDef->strAt(3) == "<") 04007 oppositeCondition = "if ( %any% >|>=|== %any% )"; 04008 else if (scope->classDef->strAt(3) == "<=") 04009 oppositeCondition = "if ( %any% > %any% )"; 04010 else if (scope->classDef->strAt(3) == ">") 04011 oppositeCondition = "if ( %any% <|<=|== %any% )"; 04012 else if (scope->classDef->strAt(3) == ">=") 04013 oppositeCondition = "if ( %any% < %any% )"; 04014 04015 if (oppositeCondition) { 04016 int flag = 0; 04017 04018 for (const Token* tok = scope->classStart; tok != scope->classEnd && flag == 0; tok = tok->next()) { 04019 if ((tok->str() == op1Tok->str() || tok->str() == op2Tok->str()) && tok->strAt(1) == "=") 04020 break; 04021 else if (Token::Match(tok, "%any% ( %any% )")) { 04022 if ((tok->strAt(2) == op1Tok->str() || tok->strAt(2) == op2Tok->str())) 04023 break; 04024 } else if (Token::Match(tok, "%any% ( %any% , %any%")) { 04025 for (const Token* tok2 = tok->next(); tok2 != tok->linkAt(1); tok2 = tok2->next()) { 04026 if (tok2->str() == op1Tok->str()) { 04027 flag = 1; 04028 break; 04029 } 04030 } 04031 } else if (Token::Match(tok, oppositeCondition)) { 04032 if ((tok->strAt(2) == op1Tok->str() && tok->strAt(4) == op2Tok->str()) || (tok->strAt(2) == op2Tok->str() && tok->strAt(4) == op1Tok->str())) 04033 oppositeInnerConditionError(toke); 04034 } 04035 } 04036 } 04037 } 04038 } 04039 } 04040 } 04041 04042 void CheckOther::oppositeInnerConditionError(const Token *tok) 04043 { 04044 reportError(tok, Severity::warning, "oppositeInnerCondition", "Opposite conditions in nested 'if' blocks lead to a dead code block.", true); 04045 } 04046 04047 04048 void CheckOther::checkVarFuncNullUB() 04049 { 04050 if (!_settings->isEnabled("portability")) 04051 return; 04052 04053 const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 04054 const std::size_t functions = symbolDatabase->functionScopes.size(); 04055 for (std::size_t i = 0; i < functions; ++i) { 04056 const Scope * scope = symbolDatabase->functionScopes[i]; 04057 for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { 04058 // Is NULL passed to a function? 04059 if (Token::Match(tok,"[(,] NULL [,)]")) { 04060 // Locate function name in this function call. 04061 const Token *ftok = tok; 04062 std::size_t argnr = 1; 04063 while (ftok && ftok->str() != "(") { 04064 if (ftok->str() == ")") 04065 ftok = ftok->link(); 04066 else if (ftok->str() == ",") 04067 ++argnr; 04068 ftok = ftok->previous(); 04069 } 04070 ftok = ftok ? ftok->previous() : NULL; 04071 if (ftok && ftok->isName()) { 04072 // If this is a variadic function then report error 04073 const Function *f = ftok->function(); 04074 if (f && f->argCount() <= argnr) { 04075 const Token *tok2 = f->argDef; 04076 tok2 = tok2 ? tok2->link() : NULL; // goto ')' 04077 if (Token::simpleMatch(tok2->tokAt(-3), ". . .")) 04078 varFuncNullUBError(tok); 04079 } 04080 } 04081 } 04082 } 04083 } 04084 } 04085 04086 void CheckOther::varFuncNullUBError(const Token *tok) 04087 { 04088 reportError(tok, 04089 Severity::portability, 04090 "varFuncNullUB", 04091 "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n" 04092 "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n" 04093 "The C99 standard, in section 7.15.1.1, states that if the type used by va_arg() is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined.\n" 04094 "The value of the NULL macro is an implementation-defined null pointer constant (7.17), which can be any integer constant expression with the value 0, or such an expression casted to (void*) (6.3.2.3). This includes values like 0, 0L, or even 0LL.\n" 04095 "In practice on common architectures, this will cause real crashes if sizeof(int) != sizeof(void*), and NULL is defined to 0 or any other null pointer constant that promotes to int.\n" 04096 "To reproduce you might be able to use this little code example on 64bit platforms. If the output includes \"ERROR\", the sentinel had only 4 out of 8 bytes initialized to zero and was not detected as the final argument to stop argument processing via va_arg(). Changing the 0 to (void*)0 or 0L will make the \"ERROR\" output go away.\n" 04097 "#include <stdarg.h>\n" 04098 "#include <stdio.h>\n" 04099 "\n" 04100 "void f(char *s, ...) {\n" 04101 " va_list ap;\n" 04102 " va_start(ap,s);\n" 04103 " for (;;) {\n" 04104 " char *p = va_arg(ap,char*);\n" 04105 " printf(\"%018p, %s\n\", p, (long)p & 255 ? p : \"\");\n" 04106 " if(!p) break;\n" 04107 " }\n" 04108 " va_end(ap);\n" 04109 "}\n" 04110 "\n" 04111 "void g() {\n" 04112 " char *s2 = \"x\";\n" 04113 " char *s3 = \"ERROR\";\n" 04114 "\n" 04115 " // changing 0 to 0L for the 7th argument (which is intended to act as sentinel) makes the error go away on x86_64\n" 04116 " f(\"first\", s2, s2, s2, s2, s2, 0, s3, (char*)0);\n" 04117 "}\n" 04118 "\n" 04119 "void h() {\n" 04120 " int i;\n" 04121 " volatile unsigned char a[1000];\n" 04122 " for (i = 0; i<sizeof(a); i++)\n" 04123 " a[i] = -1;\n" 04124 "}\n" 04125 "\n" 04126 "int main() {\n" 04127 " h();\n" 04128 " g();\n" 04129 " return 0;\n" 04130 "}"); 04131 }
1.7.6.1