|
Cppcheck
|
00001 /* 00002 * Cppcheck - A tool for static C/C++ code analysis 00003 * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team. 00004 * 00005 * This program is free software: you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License as published by 00007 * the Free Software Foundation, either version 3 of the License, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 */ 00018 00019 00020 #include "executionpath.h" 00021 #include "token.h" 00022 #include "symboldatabase.h" 00023 #include <memory> 00024 #include <set> 00025 #include <iterator> 00026 #include <iostream> 00027 00028 00029 00030 // default : bail out if the condition is has variable handling 00031 bool ExecutionPath::parseCondition(const Token &tok, std::list<ExecutionPath *> & checks) 00032 { 00033 unsigned int parlevel = 0; 00034 for (const Token *tok2 = &tok; tok2; tok2 = tok2->next()) { 00035 if (tok2->str() == "(") 00036 ++parlevel; 00037 else if (tok2->str() == ")") { 00038 if (parlevel == 0) 00039 break; 00040 --parlevel; 00041 } else if (Token::Match(tok2, "[;{}]")) 00042 break; 00043 if (tok2->varId() != 0) { 00044 bailOutVar(checks, tok2->varId()); 00045 } 00046 } 00047 00048 for (std::list<ExecutionPath *>::iterator it = checks.begin(); it != checks.end();) { 00049 if ((*it)->varId > 0 && (*it)->numberOfIf >= 1) { 00050 delete *it; 00051 checks.erase(it++); 00052 } else { 00053 ++it; 00054 } 00055 } 00056 00057 00058 return false; 00059 } 00060 00061 00062 void ExecutionPath::print() const 00063 { 00064 std::cout << " varId=" << varId 00065 << " numberOfIf=" << numberOfIf 00066 << "\n"; 00067 } 00068 00069 // I use this function when debugging ExecutionPaths with GDB 00070 /* 00071 static void printchecks(const std::list<ExecutionPath *> &checks) 00072 { 00073 for (std::list<ExecutionPath *>::const_iterator it = checks.begin(); it != checks.end(); ++it) 00074 (*it)->print(); 00075 } 00076 */ 00077 00078 00079 00080 /** 00081 * @brief Parse If/Switch body recursively. 00082 * @param tok First token in body. 00083 * @param checks The current checks 00084 * @param newchecks new checks 00085 * @param countif The countif set - count number of if for each execution path 00086 */ 00087 static void parseIfSwitchBody(const Token * const tok, 00088 const std::list<ExecutionPath *> &checks, 00089 std::list<ExecutionPath *> &newchecks, 00090 std::set<unsigned int> &countif) 00091 { 00092 std::set<unsigned int> countif2; 00093 std::list<ExecutionPath *> c; 00094 if (!checks.empty()) { 00095 std::list<ExecutionPath *>::const_iterator it; 00096 for (it = checks.begin(); it != checks.end(); ++it) { 00097 if ((*it)->numberOfIf == 0) 00098 c.push_back((*it)->copy()); 00099 if ((*it)->varId != 0) 00100 countif2.insert((*it)->varId); 00101 } 00102 } 00103 ExecutionPath::checkScope(tok, c); 00104 while (!c.empty()) { 00105 if (c.back()->varId == 0) { 00106 delete c.back(); 00107 c.pop_back(); 00108 continue; 00109 } 00110 00111 bool duplicate = false; 00112 std::list<ExecutionPath *>::const_iterator it; 00113 for (it = checks.begin(); it != checks.end(); ++it) { 00114 if (*(*it) == *c.back() && (*it)->numberOfIf == c.back()->numberOfIf) { 00115 duplicate = true; 00116 countif2.erase((*it)->varId); 00117 break; 00118 } 00119 } 00120 if (!duplicate) 00121 newchecks.push_back(c.back()); 00122 else 00123 delete c.back(); 00124 c.pop_back(); 00125 } 00126 00127 // Add countif2 ids to countif.. countif. 00128 countif.insert(countif2.begin(), countif2.end()); 00129 } 00130 00131 00132 void ExecutionPath::checkScope(const Token *tok, std::list<ExecutionPath *> &checks) 00133 { 00134 if (!tok || tok->str() == "}" || checks.empty()) 00135 return; 00136 00137 const std::auto_ptr<ExecutionPath> check(checks.front()->copy()); 00138 00139 for (; tok; tok = tok->next()) { 00140 // might be a noreturn function.. 00141 if (Token::simpleMatch(tok->tokAt(-2), ") ; }") && 00142 Token::Match(tok->linkAt(-2)->tokAt(-2), "[;{}] %var% (") && 00143 tok->linkAt(-2)->previous()->varId() == 0) { 00144 ExecutionPath::bailOut(checks); 00145 return; 00146 } 00147 00148 if (Token::simpleMatch(tok, "union {")) { 00149 tok = tok->next()->link(); 00150 continue; 00151 } 00152 00153 if (tok->str() == "}") 00154 return; 00155 00156 if (tok->str() == "break") { 00157 ExecutionPath::bailOut(checks); 00158 return; 00159 } 00160 00161 if (Token::simpleMatch(tok, "while (")) { 00162 // parse condition 00163 if (checks.size() > 10 || check->parseCondition(*tok->tokAt(2), checks)) { 00164 ExecutionPath::bailOut(checks); 00165 return; 00166 } 00167 00168 // skip "while (fgets()!=NULL)" 00169 if (Token::simpleMatch(tok, "while ( fgets (")) { 00170 const Token *tok2 = tok->linkAt(3); 00171 if (Token::simpleMatch(tok2, ") ) {")) { 00172 tok = tok2->linkAt(2); 00173 if (!tok) 00174 break; 00175 continue; 00176 } 00177 } 00178 } 00179 00180 // goto/setjmp/longjmp => bailout 00181 else if (Token::Match(tok, "goto|setjmp|longjmp")) { 00182 ExecutionPath::bailOut(checks); 00183 return; 00184 } 00185 00186 // ?: => bailout 00187 if (tok->str() == "?") { 00188 for (const Token *tok2 = tok; tok2 && tok2->str() != ";"; tok2 = tok2->next()) { 00189 if (tok2->varId() > 0) 00190 ExecutionPath::bailOutVar(checks, tok2->varId()); 00191 } 00192 } 00193 00194 // for/while/switch/do .. bail out 00195 else if (Token::Match(tok, "for|while|switch|do")) { 00196 // goto { 00197 const Token *tok2 = tok->next(); 00198 if (tok2 && tok2->str() == "(") 00199 tok2 = tok2->link(); 00200 if (tok2 && tok2->str() == ")") 00201 tok2 = tok2->next(); 00202 if (!tok2 || tok2->str() != "{") { 00203 ExecutionPath::bailOut(checks); 00204 return; 00205 } 00206 00207 if (tok->str() == "switch") { 00208 // parse condition 00209 if (checks.size() > 10 || check->parseCondition(*tok->next(), checks)) { 00210 ExecutionPath::bailOut(checks); 00211 return; 00212 } 00213 00214 // what variable ids should the if be counted for? 00215 std::set<unsigned int> countif; 00216 00217 std::list<ExecutionPath *> newchecks; 00218 00219 for (const Token* tok3 = tok2->next(); tok3; tok3 = tok3->next()) { 00220 if (tok3->str() == "{") 00221 tok3 = tok3->link(); 00222 else if (tok3->str() == "}") 00223 break; 00224 else if (tok3->str() == "case" && 00225 !Token::Match(tok3, "case %num% : ; case")) { 00226 parseIfSwitchBody(tok3, checks, newchecks, countif); 00227 } 00228 } 00229 00230 // Add newchecks to checks.. 00231 std::copy(newchecks.begin(), newchecks.end(), std::back_inserter(checks)); 00232 00233 // Increase numberOfIf 00234 std::list<ExecutionPath *>::iterator it; 00235 for (it = checks.begin(); it != checks.end(); ++it) { 00236 if (countif.find((*it)->varId) != countif.end()) 00237 (*it)->numberOfIf++; 00238 } 00239 } 00240 // no switch 00241 else { 00242 for (const Token *tok3 = tok; tok3 && tok3 != tok2; tok3 = tok3->next()) { 00243 if (tok3->varId()) 00244 ExecutionPath::bailOutVar(checks, tok3->varId()); 00245 } 00246 00247 // it is not certain that a for/while will be executed: 00248 for (std::list<ExecutionPath *>::iterator it = checks.begin(); it != checks.end();) { 00249 if ((*it)->numberOfIf > 0) { 00250 delete *it; 00251 checks.erase(it++); 00252 } else 00253 ++it; 00254 } 00255 00256 // #2231 - loop body only contains a conditional initialization.. 00257 if (Token::simpleMatch(tok2->next(), "if (")) { 00258 // Start { for the if block 00259 const Token *tok3 = tok2->linkAt(2); 00260 if (Token::simpleMatch(tok3,") {")) { 00261 tok3 = tok3->next(); 00262 00263 // End } for the if block 00264 const Token *tok4 = tok3->link(); 00265 if (Token::Match(tok3, "{ %var% =") && 00266 Token::simpleMatch(tok4, "} }") && 00267 Token::simpleMatch(tok4->tokAt(-2), "break ;")) { 00268 // Is there a assignment and then a break? 00269 const Token *t = Token::findsimplematch(tok3, ";"); 00270 if (t && t->tokAt(3) == tok4) { 00271 for (std::list<ExecutionPath *>::iterator it = checks.begin(); it != checks.end(); ++it) { 00272 if ((*it)->varId == tok3->next()->varId()) { 00273 (*it)->numberOfIf++; 00274 break; 00275 } 00276 } 00277 tok = tok2->link(); 00278 continue; 00279 } 00280 } 00281 } 00282 } 00283 00284 // parse loop bodies 00285 check->parseLoopBody(tok2->next(), checks); 00286 } 00287 00288 // skip { .. } 00289 tok2 = tok2->link(); 00290 00291 // if "do { .. } while ( .." , goto end of while.. 00292 if (Token::simpleMatch(tok, "do {") && Token::simpleMatch(tok2, "} while (")) 00293 tok2 = tok2->linkAt(2); 00294 00295 // bail out all variables if the scope contains a "return" 00296 // bail out all variables used in this for/while/switch/do 00297 for (; tok && tok != tok2; tok = tok->next()) { 00298 if (tok->str() == "return") 00299 ExecutionPath::bailOut(checks); 00300 if (tok->varId()) 00301 ExecutionPath::bailOutVar(checks, tok->varId()); 00302 } 00303 00304 continue; 00305 } 00306 00307 // bailout used variables in '; FOREACH ( .. ) { .. }' 00308 else if (tok->str() != "if" && Token::Match(tok->previous(), "[;{}] %var% (")) { 00309 // goto { 00310 const Token *tok2 = tok->next()->link()->next(); 00311 if (tok2 && tok2->str() == "{") { 00312 // goto "}" 00313 tok2 = tok2->link(); 00314 00315 // bail out all variables used in "{ .. }" 00316 for (; tok && tok != tok2; tok = tok->next()) { 00317 if (tok->varId()) 00318 ExecutionPath::bailOutVar(checks, tok->varId()); 00319 } 00320 } 00321 } 00322 00323 // .. ) { ... } => bail out 00324 if (tok->str() == ")" && tok->next() && tok->next()->str() == "{") { 00325 ExecutionPath::bailOut(checks); 00326 return; 00327 } 00328 00329 if ((tok->str() == "abort" || tok->str() == "exit") && 00330 tok->next() && tok->next()->str() == "(") { 00331 ExecutionPath::bailOut(checks); 00332 return; 00333 } 00334 00335 // don't parse into "struct type { .." 00336 if (Token::Match(tok, "struct|union|class %type% {|:")) { 00337 while (tok && tok->str() != "{" && tok->str() != ";") 00338 tok = tok->next(); 00339 tok = tok ? tok->link() : 0; 00340 if (!tok) { 00341 ExecutionPath::bailOut(checks); 00342 return; 00343 } 00344 } 00345 00346 if (Token::simpleMatch(tok, "= {")) { 00347 // GCC struct initialization.. bail out 00348 if (Token::Match(tok->tokAt(2), ". %var% =")) { 00349 ExecutionPath::bailOut(checks); 00350 return; 00351 } 00352 00353 tok = tok->next()->link(); 00354 if (!tok) { 00355 ExecutionPath::bailOut(checks); 00356 return; 00357 } 00358 continue; 00359 } 00360 00361 // ; { ... } 00362 if (Token::Match(tok->previous(), "[;{}:] {")) { 00363 ExecutionPath::checkScope(tok->next(), checks); 00364 tok = tok->link(); 00365 continue; 00366 } 00367 00368 if (tok->str() == "if" && tok->next() && tok->next()->str() == "(") { 00369 // what variable ids should the numberOfIf be counted for? 00370 std::set<unsigned int> countif; 00371 00372 std::list<ExecutionPath *> newchecks; 00373 while (tok->str() == "if" && tok->next() && tok->next()->str() == "(") { 00374 // goto "(" 00375 tok = tok->next(); 00376 00377 // parse condition 00378 if (checks.size() > 10 || check->parseCondition(*tok->next(), checks)) { 00379 ExecutionPath::bailOut(checks); 00380 ExecutionPath::bailOut(newchecks); 00381 return; 00382 } 00383 00384 // goto ")" 00385 tok = tok->link(); 00386 00387 // goto "{" 00388 tok = tok->next(); 00389 00390 if (!tok || tok->str() != "{") { 00391 ExecutionPath::bailOut(checks); 00392 ExecutionPath::bailOut(newchecks); 00393 return; 00394 } 00395 00396 // Recursively check into the if .. 00397 parseIfSwitchBody(tok->next(), checks, newchecks, countif); 00398 00399 // goto "}" 00400 tok = tok->link(); 00401 00402 // there is no else => break out 00403 if (!tok->next() || tok->next()->str() != "else") 00404 break; 00405 00406 // parse next "if".. 00407 tok = tok->tokAt(2); 00408 if (tok && tok->str() == "if") 00409 continue; 00410 00411 if (!tok) { 00412 ExecutionPath::bailOut(newchecks); 00413 return; 00414 } 00415 00416 // there is no "if".. 00417 ExecutionPath::checkScope(tok->next(), checks); 00418 tok = tok->link(); 00419 if (!tok) { 00420 ExecutionPath::bailOut(newchecks); 00421 return; 00422 } 00423 } 00424 00425 // Add newchecks to checks.. 00426 std::copy(newchecks.begin(), newchecks.end(), std::back_inserter(checks)); 00427 00428 // Increase numberOfIf 00429 std::list<ExecutionPath *>::iterator it; 00430 for (it = checks.begin(); it != checks.end(); ++it) { 00431 if (countif.find((*it)->varId) != countif.end()) 00432 (*it)->numberOfIf++; 00433 } 00434 00435 // Delete checks that have numberOfIf >= 2 00436 for (it = checks.begin(); it != checks.end();) { 00437 if ((*it)->varId > 0 && (*it)->numberOfIf >= 2) { 00438 delete *it; 00439 checks.erase(it++); 00440 } else { 00441 ++it; 00442 } 00443 } 00444 } 00445 00446 00447 { 00448 tok = check->parse(*tok, checks); 00449 if (checks.empty()) 00450 return; 00451 } 00452 00453 if (!tok) 00454 break; 00455 00456 // return/throw ends all execution paths 00457 if (tok->str() == "return" || 00458 tok->str() == "throw" || 00459 tok->str() == "continue" || 00460 tok->str() == "break") { 00461 ExecutionPath::bailOut(checks); 00462 } 00463 } 00464 } 00465 00466 void checkExecutionPaths(const SymbolDatabase *symbolDatabase, ExecutionPath *c) 00467 { 00468 for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { 00469 if (i->type != Scope::eFunction || !i->classStart) 00470 continue; 00471 00472 // Check function 00473 std::list<ExecutionPath *> checks; 00474 checks.push_back(c->copy()); 00475 ExecutionPath::checkScope(i->classStart, checks); 00476 00477 c->end(checks, i->classEnd); 00478 00479 // Cleanup 00480 while (!checks.empty()) { 00481 delete checks.back(); 00482 checks.pop_back(); 00483 } 00484 } 00485 }
1.7.6.1