2009-12-14 20:30:31 +01:00
|
|
|
/*
|
|
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
2010-04-13 21:23:17 +02:00
|
|
|
* Copyright (C) 2007-2010 Daniel Marjamäki and Cppcheck team.
|
2009-12-14 20:30:31 +01:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "executionpath.h"
|
|
|
|
#include "token.h"
|
|
|
|
#include <memory>
|
2010-04-25 11:55:57 +02:00
|
|
|
#include <set>
|
2010-04-25 12:10:50 +02:00
|
|
|
#include <iterator>
|
2010-04-28 21:33:11 +02:00
|
|
|
#include <iostream>
|
2009-12-14 20:30:31 +01:00
|
|
|
|
|
|
|
|
2009-12-25 19:45:21 +01:00
|
|
|
// default : bail out if the condition is has variable handling
|
2009-12-25 20:50:23 +01:00
|
|
|
bool ExecutionPath::parseCondition(const Token &tok, std::list<ExecutionPath *> & checks)
|
2009-12-25 19:45:21 +01:00
|
|
|
{
|
|
|
|
unsigned int parlevel = 0;
|
2010-04-02 07:30:58 +02:00
|
|
|
for (const Token *tok2 = &tok; tok2; tok2 = tok2->next())
|
2009-12-25 19:45:21 +01:00
|
|
|
{
|
2010-04-02 07:30:58 +02:00
|
|
|
if (tok2->str() == "(")
|
2009-12-25 19:45:21 +01:00
|
|
|
++parlevel;
|
2010-04-02 07:30:58 +02:00
|
|
|
else if (tok2->str() == ")")
|
2009-12-25 19:45:21 +01:00
|
|
|
{
|
2010-04-02 07:30:58 +02:00
|
|
|
if (parlevel == 0)
|
2009-12-25 19:45:21 +01:00
|
|
|
break;
|
|
|
|
--parlevel;
|
|
|
|
}
|
2010-04-02 07:30:58 +02:00
|
|
|
else if (Token::Match(tok2, ";{}"))
|
2009-12-25 19:45:21 +01:00
|
|
|
break;
|
2010-04-02 07:30:58 +02:00
|
|
|
if (tok2->varId() != 0)
|
2009-12-25 19:45:21 +01:00
|
|
|
{
|
2010-04-25 11:55:57 +02:00
|
|
|
bailOutVar(checks, tok2->varId());
|
|
|
|
for (std::list<ExecutionPath *>::iterator it = checks.begin(); it != checks.end();)
|
|
|
|
{
|
|
|
|
if ((*it)->varId > 0 && (*it)->numberOfIf >= 1)
|
|
|
|
{
|
|
|
|
delete *it;
|
|
|
|
checks.erase(it++);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
2009-12-25 19:45:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-28 21:33:11 +02:00
|
|
|
void ExecutionPath::print() const
|
|
|
|
{
|
|
|
|
std::cout << " varId=" << varId
|
|
|
|
<< " numberOfIf=" << numberOfIf
|
|
|
|
<< "\n";
|
|
|
|
}
|
|
|
|
|
2010-05-18 20:58:11 +02:00
|
|
|
// I use this function when debugging ExecutionPaths with GDB
|
|
|
|
#ifdef __GNUC__
|
2010-04-28 21:33:11 +02:00
|
|
|
static void printchecks(const std::list<ExecutionPath *> &checks)
|
|
|
|
{
|
|
|
|
for (std::list<ExecutionPath *>::const_iterator it = checks.begin(); it != checks.end(); ++it)
|
|
|
|
(*it)->print();
|
|
|
|
}
|
2010-05-18 19:19:15 +02:00
|
|
|
#endif
|
2010-04-28 21:33:11 +02:00
|
|
|
|
|
|
|
|
2010-04-04 11:24:52 +02:00
|
|
|
static void checkExecutionPaths_(const Token *tok, std::list<ExecutionPath *> &checks)
|
2009-12-14 20:30:31 +01:00
|
|
|
{
|
2010-04-02 07:30:58 +02:00
|
|
|
if (!tok || tok->str() == "}" || checks.empty())
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2010-01-08 21:54:24 +01:00
|
|
|
|
2009-12-14 20:30:31 +01:00
|
|
|
const std::auto_ptr<ExecutionPath> check(checks.front()->copy());
|
|
|
|
|
2010-04-02 07:30:58 +02:00
|
|
|
for (; tok; tok = tok->next())
|
2009-12-14 20:30:31 +01:00
|
|
|
{
|
2010-04-02 07:30:58 +02:00
|
|
|
if (tok->str() == "}")
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2009-12-14 20:30:31 +01:00
|
|
|
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::simpleMatch(tok, "while ("))
|
2010-01-03 13:30:20 +01:00
|
|
|
{
|
|
|
|
// parse condition
|
2010-04-02 07:30:58 +02:00
|
|
|
if (checks.size() > 10 || check->parseCondition(*tok->tokAt(2), checks))
|
2010-01-03 13:30:20 +01:00
|
|
|
{
|
|
|
|
ExecutionPath::bailOut(checks);
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2010-01-03 13:30:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// skip "while (fgets()!=NULL)"
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::simpleMatch(tok, "while ( fgets ("))
|
2010-01-03 13:30:20 +01:00
|
|
|
{
|
|
|
|
const Token *tok2 = tok->tokAt(3)->link();
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::simpleMatch(tok2, ") ) {"))
|
2010-01-03 13:30:20 +01:00
|
|
|
{
|
|
|
|
tok = tok2->tokAt(2)->link();
|
2010-04-02 07:30:58 +02:00
|
|
|
if (!tok)
|
2010-01-03 13:30:20 +01:00
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-02 08:35:05 +02:00
|
|
|
// goto => bailout
|
|
|
|
if (tok->str() == "goto")
|
|
|
|
{
|
|
|
|
ExecutionPath::bailOut(checks);
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2010-04-02 08:35:05 +02:00
|
|
|
}
|
|
|
|
|
2010-01-10 22:05:51 +01:00
|
|
|
// for/while/switch/do .. bail out
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::Match(tok, "for|while|switch|do"))
|
2009-12-14 20:30:31 +01:00
|
|
|
{
|
2010-01-10 22:05:51 +01:00
|
|
|
// goto {
|
|
|
|
const Token *tok2 = tok->next();
|
2010-04-02 07:30:58 +02:00
|
|
|
if (tok2 && tok2->str() == "(")
|
2010-01-10 22:05:51 +01:00
|
|
|
tok2 = tok2->link();
|
2010-04-02 07:30:58 +02:00
|
|
|
if (tok2 && tok2->str() == ")")
|
2010-01-10 22:05:51 +01:00
|
|
|
tok2 = tok2->next();
|
2010-04-02 07:30:58 +02:00
|
|
|
if (!tok2 || tok2->str() != "{")
|
2010-01-10 22:05:51 +01:00
|
|
|
{
|
|
|
|
ExecutionPath::bailOut(checks);
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2010-01-10 22:05:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// skip { .. }
|
|
|
|
tok2 = tok2->link();
|
|
|
|
|
|
|
|
// if "do { .. } while ( .." , goto end of while..
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::simpleMatch(tok, "do {") && Token::simpleMatch(tok2, "} while ("))
|
2010-01-10 22:05:51 +01:00
|
|
|
tok2 = tok2->tokAt(2)->link();
|
|
|
|
|
2010-02-17 18:10:50 +01:00
|
|
|
// bail out all variables if the scope contains a "return"
|
2010-01-10 22:05:51 +01:00
|
|
|
// bail out all variables used in this for/while/switch/do
|
2010-04-02 07:30:58 +02:00
|
|
|
for (; tok && tok != tok2; tok = tok->next())
|
2010-01-10 22:05:51 +01:00
|
|
|
{
|
2010-04-02 07:30:58 +02:00
|
|
|
if (tok->str() == "return")
|
2010-02-17 18:10:50 +01:00
|
|
|
ExecutionPath::bailOut(checks);
|
2010-04-02 07:30:58 +02:00
|
|
|
if (tok->varId())
|
2010-01-10 22:05:51 +01:00
|
|
|
ExecutionPath::bailOutVar(checks, tok->varId());
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
2009-12-14 20:30:31 +01:00
|
|
|
}
|
|
|
|
|
2010-05-30 08:26:44 +02:00
|
|
|
// bailout used variables in '; FOREACH ( .. ) { .. }'
|
2010-05-30 09:00:18 +02:00
|
|
|
else if (tok->str() != "if" && Token::Match(tok->previous(), "[;{}] %var% ("))
|
2010-05-30 08:26:44 +02:00
|
|
|
{
|
|
|
|
// goto {
|
2010-05-30 09:00:18 +02:00
|
|
|
const Token *tok2 = tok->next()->link();
|
2010-05-30 08:26:44 +02:00
|
|
|
if (tok2 && tok2->str() == ")")
|
|
|
|
{
|
2010-05-30 09:00:18 +02:00
|
|
|
tok2 = tok2->next();
|
|
|
|
if (tok2 && tok2->str() == "{")
|
2010-05-30 08:26:44 +02:00
|
|
|
{
|
2010-05-30 09:00:18 +02:00
|
|
|
// goto "}"
|
|
|
|
tok2 = tok2->link();
|
|
|
|
|
|
|
|
// bail out all variables used in "{ .. }"
|
|
|
|
for (; tok && tok != tok2; tok = tok->next())
|
|
|
|
{
|
|
|
|
if (tok->varId())
|
|
|
|
ExecutionPath::bailOutVar(checks, tok->varId());
|
|
|
|
}
|
2010-05-30 08:26:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-08 21:24:48 +01:00
|
|
|
// .. ) { ... } => bail out
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::simpleMatch(tok, ") {"))
|
2010-01-08 21:24:48 +01:00
|
|
|
{
|
|
|
|
ExecutionPath::bailOut(checks);
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2010-01-08 21:24:48 +01:00
|
|
|
}
|
|
|
|
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::Match(tok, "abort|exit ("))
|
2010-01-03 15:35:32 +01:00
|
|
|
{
|
|
|
|
ExecutionPath::bailOut(checks);
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2010-01-03 15:35:32 +01:00
|
|
|
}
|
|
|
|
|
2009-12-21 18:17:35 +01:00
|
|
|
// don't parse into "struct type { .."
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::Match(tok, "struct|union|class %type% {|:"))
|
2010-02-08 18:06:28 +01:00
|
|
|
{
|
2010-04-02 07:30:58 +02:00
|
|
|
while (tok && tok->str() != "{" && tok->str() != ";")
|
2010-02-08 18:06:28 +01:00
|
|
|
tok = tok->next();
|
|
|
|
tok = tok ? tok->link() : 0;
|
|
|
|
}
|
2009-12-21 18:17:35 +01:00
|
|
|
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::Match(tok, "= {"))
|
2009-12-14 20:30:31 +01:00
|
|
|
{
|
2010-03-28 10:42:37 +02:00
|
|
|
// GCC struct initialization.. bail out
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::Match(tok->tokAt(2), ". %var% ="))
|
2010-03-28 10:42:37 +02:00
|
|
|
{
|
|
|
|
ExecutionPath::bailOut(checks);
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2010-03-28 10:42:37 +02:00
|
|
|
}
|
|
|
|
|
2009-12-14 20:30:31 +01:00
|
|
|
tok = tok->next()->link();
|
2010-04-02 07:30:58 +02:00
|
|
|
if (!tok)
|
2009-12-14 20:30:31 +01:00
|
|
|
{
|
|
|
|
ExecutionPath::bailOut(checks);
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2009-12-14 20:30:31 +01:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-01-08 21:24:48 +01:00
|
|
|
// ; { ... }
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::Match(tok->previous(), "[;{}] {"))
|
2010-01-08 21:24:48 +01:00
|
|
|
{
|
2010-04-04 11:24:52 +02:00
|
|
|
checkExecutionPaths_(tok->next(), checks);
|
2010-01-08 21:24:48 +01:00
|
|
|
tok = tok->link();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-04-02 07:30:58 +02:00
|
|
|
if (tok->str() == "if")
|
2009-12-14 20:30:31 +01:00
|
|
|
{
|
2010-04-25 11:55:57 +02:00
|
|
|
// what variable ids should the numberOfIf be counted for?
|
|
|
|
std::set<unsigned int> countif;
|
|
|
|
|
2009-12-14 20:30:31 +01:00
|
|
|
std::list<ExecutionPath *> newchecks;
|
2010-04-02 07:30:58 +02:00
|
|
|
while (tok->str() == "if")
|
2009-12-14 20:30:31 +01:00
|
|
|
{
|
|
|
|
// goto "("
|
|
|
|
tok = tok->next();
|
|
|
|
|
2009-12-25 19:45:21 +01:00
|
|
|
// parse condition
|
2010-04-02 07:30:58 +02:00
|
|
|
if (checks.size() > 10 || check->parseCondition(*tok->next(), checks))
|
2009-12-14 20:30:31 +01:00
|
|
|
{
|
2009-12-29 09:17:07 +01:00
|
|
|
ExecutionPath::bailOut(checks);
|
|
|
|
ExecutionPath::bailOut(newchecks);
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2009-12-14 20:30:31 +01:00
|
|
|
}
|
|
|
|
|
2009-12-25 19:45:21 +01:00
|
|
|
// goto ")"
|
|
|
|
tok = tok ? tok->link() : 0;
|
|
|
|
|
2009-12-14 20:30:31 +01:00
|
|
|
// goto "{"
|
|
|
|
tok = tok ? tok->next() : 0;
|
|
|
|
|
2010-04-02 07:30:58 +02:00
|
|
|
if (!Token::simpleMatch(tok, "{"))
|
2009-12-28 19:48:30 +01:00
|
|
|
{
|
2009-12-29 09:17:07 +01:00
|
|
|
ExecutionPath::bailOut(checks);
|
|
|
|
ExecutionPath::bailOut(newchecks);
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2009-12-28 19:48:30 +01:00
|
|
|
}
|
2009-12-14 20:30:31 +01:00
|
|
|
|
|
|
|
// Recursively check into the if ..
|
|
|
|
{
|
2010-04-25 11:55:57 +02:00
|
|
|
std::set<unsigned int> countif2;
|
2009-12-14 20:30:31 +01:00
|
|
|
std::list<ExecutionPath *> c;
|
2010-04-28 21:33:11 +02:00
|
|
|
if (!checks.empty())
|
2010-04-25 11:55:57 +02:00
|
|
|
{
|
|
|
|
std::list<ExecutionPath *>::const_iterator it;
|
2010-04-28 21:33:11 +02:00
|
|
|
for (it = checks.begin(); it != checks.end(); ++it)
|
2010-04-25 11:55:57 +02:00
|
|
|
{
|
2010-05-27 19:00:52 +02:00
|
|
|
c.push_back((*it)->copy());
|
2010-04-28 21:33:11 +02:00
|
|
|
if ((*it)->varId != 0)
|
|
|
|
countif2.insert((*it)->varId);
|
2010-04-25 11:55:57 +02:00
|
|
|
}
|
|
|
|
}
|
2010-04-04 11:24:52 +02:00
|
|
|
checkExecutionPaths_(tok->next(), c);
|
2010-04-02 07:30:58 +02:00
|
|
|
while (!c.empty())
|
2009-12-14 20:30:31 +01:00
|
|
|
{
|
2010-05-27 19:00:52 +02:00
|
|
|
if (c.back()->varId == 0)
|
|
|
|
{
|
|
|
|
c.pop_back();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-04-25 11:55:57 +02:00
|
|
|
bool duplicate = false;
|
|
|
|
std::list<ExecutionPath *>::const_iterator it;
|
|
|
|
for (it = checks.begin(); it != checks.end(); ++it)
|
|
|
|
{
|
|
|
|
if (*(*it) == *c.back())
|
|
|
|
{
|
|
|
|
duplicate = true;
|
|
|
|
countif2.erase((*it)->varId);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!duplicate)
|
|
|
|
newchecks.push_back(c.back());
|
2009-12-14 20:30:31 +01:00
|
|
|
c.pop_back();
|
|
|
|
}
|
2010-04-25 11:55:57 +02:00
|
|
|
|
|
|
|
// Add countif2 ids to countif..
|
|
|
|
countif.insert(countif2.begin(), countif2.end());
|
2009-12-14 20:30:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// goto "}"
|
|
|
|
tok = tok->link();
|
|
|
|
|
|
|
|
// there is no else => break out
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::Match(tok, "} !!else"))
|
2009-12-14 20:30:31 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
// parse next "if"..
|
|
|
|
tok = tok->tokAt(2);
|
2010-04-02 07:30:58 +02:00
|
|
|
if (tok->str() == "if")
|
2009-12-14 20:30:31 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// there is no "if"..
|
2010-04-04 11:24:52 +02:00
|
|
|
checkExecutionPaths_(tok->next(), checks);
|
2009-12-14 20:30:31 +01:00
|
|
|
tok = tok->link();
|
2010-04-02 07:30:58 +02:00
|
|
|
if (!tok)
|
2009-12-28 19:48:30 +01:00
|
|
|
{
|
2009-12-29 09:17:07 +01:00
|
|
|
ExecutionPath::bailOut(newchecks);
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2009-12-28 19:48:30 +01:00
|
|
|
}
|
2009-12-14 20:30:31 +01:00
|
|
|
}
|
|
|
|
|
2010-04-25 11:55:57 +02:00
|
|
|
// Add newchecks to checks..
|
|
|
|
std::copy(newchecks.begin(), newchecks.end(), std::back_inserter(checks));
|
|
|
|
|
|
|
|
// Increase numberOfIf
|
2009-12-14 20:30:31 +01:00
|
|
|
std::list<ExecutionPath *>::iterator it;
|
2010-04-25 11:55:57 +02:00
|
|
|
for (it = checks.begin(); it != checks.end(); ++it)
|
|
|
|
{
|
|
|
|
if (countif.find((*it)->varId) != countif.end())
|
|
|
|
(*it)->numberOfIf++;
|
|
|
|
}
|
2009-12-14 20:30:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
{
|
2010-04-04 11:24:52 +02:00
|
|
|
tok = check->parse(*tok, checks);
|
2010-04-02 07:30:58 +02:00
|
|
|
if (checks.empty())
|
2010-04-04 11:24:52 +02:00
|
|
|
return;
|
2009-12-14 20:30:31 +01:00
|
|
|
}
|
|
|
|
|
2010-01-01 20:12:39 +01:00
|
|
|
// return/throw ends all execution paths
|
2010-04-02 07:30:58 +02:00
|
|
|
if (tok->str() == "return" || tok->str() == "throw")
|
2009-12-14 20:30:31 +01:00
|
|
|
{
|
|
|
|
ExecutionPath::bailOut(checks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-25 20:12:06 +01:00
|
|
|
void checkExecutionPaths(const Token *tok, ExecutionPath *c)
|
|
|
|
{
|
2010-04-02 07:30:58 +02:00
|
|
|
for (; tok; tok = tok->next())
|
2009-12-25 20:12:06 +01:00
|
|
|
{
|
2010-04-02 07:30:58 +02:00
|
|
|
if (tok->str() != ")")
|
2009-12-25 20:12:06 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// Start of implementation..
|
2010-04-02 07:30:58 +02:00
|
|
|
if (Token::Match(tok, ") const| {"))
|
2009-12-25 20:12:06 +01:00
|
|
|
{
|
|
|
|
// goto the "{"
|
|
|
|
tok = tok->next();
|
2010-04-02 07:30:58 +02:00
|
|
|
if (tok->str() == "const")
|
2009-12-25 20:12:06 +01:00
|
|
|
tok = tok->next();
|
|
|
|
|
|
|
|
std::list<ExecutionPath *> checks;
|
|
|
|
checks.push_back(c->copy());
|
|
|
|
checkExecutionPaths_(tok, checks);
|
|
|
|
|
|
|
|
c->end(checks, tok->link());
|
|
|
|
|
2010-04-02 07:30:58 +02:00
|
|
|
while (!checks.empty())
|
2009-12-25 20:12:06 +01:00
|
|
|
{
|
|
|
|
delete checks.back();
|
|
|
|
checks.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
// skip this scope - it has been checked
|
|
|
|
tok = tok->link();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|