Cppcheck
executionpath.cpp
Go to the documentation of this file.
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 }