2012-05-26 08:53:46 +02:00
|
|
|
/*
|
|
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
2015-01-03 12:14:58 +01:00
|
|
|
* Copyright (C) 2007-2015 Daniel Marjamäki and Cppcheck team.
|
2012-05-26 08:53:46 +02: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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Leaks when using auto variables
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#include "checkleakautovar.h"
|
2012-06-30 16:23:10 +02:00
|
|
|
#include "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak
|
2012-05-26 08:53:46 +02:00
|
|
|
#include "tokenize.h"
|
|
|
|
#include "symboldatabase.h"
|
|
|
|
|
2014-05-23 14:17:39 +02:00
|
|
|
#include <iostream>
|
2012-05-26 08:53:46 +02:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// Register this check class (by creating a static instance of it)
|
|
|
|
namespace {
|
|
|
|
CheckLeakAutoVar instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void VarInfo::print()
|
|
|
|
{
|
|
|
|
std::cout << "size=" << alloctype.size() << std::endl;
|
2015-01-28 13:45:40 +01:00
|
|
|
std::map<unsigned int, AllocInfo>::const_iterator it;
|
2012-05-26 08:53:46 +02:00
|
|
|
for (it = alloctype.begin(); it != alloctype.end(); ++it) {
|
|
|
|
std::string strusage;
|
|
|
|
std::map<unsigned int, std::string>::const_iterator use = possibleUsage.find(it->first);
|
|
|
|
if (use != possibleUsage.end())
|
|
|
|
strusage = use->second;
|
|
|
|
|
2015-01-28 13:45:40 +01:00
|
|
|
std::cout << "alloctype='" << it->second.type << "' "
|
2012-05-26 08:53:46 +02:00
|
|
|
<< "possibleUsage='" << strusage << "'" << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VarInfo::possibleUsageAll(const std::string &functionName)
|
|
|
|
{
|
|
|
|
possibleUsage.clear();
|
2015-01-28 13:45:40 +01:00
|
|
|
std::map<unsigned int, AllocInfo>::const_iterator it;
|
2012-05-26 08:53:46 +02:00
|
|
|
for (it = alloctype.begin(); it != alloctype.end(); ++it)
|
|
|
|
possibleUsage[it->first] = functionName;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-02 07:18:19 +02:00
|
|
|
void CheckLeakAutoVar::leakError(const Token *tok, const std::string &varname, int type)
|
2012-05-26 08:53:46 +02:00
|
|
|
{
|
2013-07-05 20:55:31 +02:00
|
|
|
const CheckMemoryLeak checkmemleak(_tokenizer, _errorLogger, _settings);
|
2013-07-08 18:26:18 +02:00
|
|
|
if (_settings->library.isresource(type))
|
2012-09-18 16:20:09 +02:00
|
|
|
checkmemleak.resourceLeakError(tok, varname);
|
|
|
|
else
|
|
|
|
checkmemleak.memleakError(tok, varname);
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckLeakAutoVar::mismatchError(const Token *tok, const std::string &varname)
|
|
|
|
{
|
2013-07-05 20:55:31 +02:00
|
|
|
const CheckMemoryLeak c(_tokenizer, _errorLogger, _settings);
|
2013-04-18 13:24:09 +02:00
|
|
|
std::list<const Token *> callstack(1, tok);
|
2012-06-30 16:23:10 +02:00
|
|
|
c.mismatchAllocDealloc(callstack, varname);
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varname)
|
|
|
|
{
|
2013-07-05 20:55:31 +02:00
|
|
|
const CheckMemoryLeak c(_tokenizer, _errorLogger, _settings);
|
2012-06-30 16:23:10 +02:00
|
|
|
c.deallocuseError(tok, varname);
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
|
|
|
|
2012-06-11 18:28:31 +02:00
|
|
|
void CheckLeakAutoVar::deallocReturnError(const Token *tok, const std::string &varname)
|
|
|
|
{
|
2012-06-30 16:23:10 +02:00
|
|
|
reportError(tok, Severity::error, "deallocret", "Returning/dereferencing '" + varname + "' after it is deallocated / released");
|
2012-06-11 18:28:31 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 08:53:46 +02:00
|
|
|
void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &functionName)
|
|
|
|
{
|
2013-07-16 18:50:25 +02:00
|
|
|
if (_settings->checkLibrary && _settings->isEnabled("information")) {
|
2012-05-26 08:53:46 +02:00
|
|
|
reportError(tok,
|
|
|
|
Severity::information,
|
2013-07-16 18:50:25 +02:00
|
|
|
"checkLibraryUseIgnore",
|
|
|
|
"--check-library: Function " + functionName + "() should have <use>/<ignore> configuration");
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-28 13:45:40 +01:00
|
|
|
void CheckLeakAutoVar::doubleFreeError(const Token *tok, const std::string &varname, int type)
|
|
|
|
{
|
|
|
|
if (_settings->library.isresource(type))
|
|
|
|
reportError(tok, Severity::error, "doubleFree", "Resource handle '" + varname + "' freed twice.");
|
|
|
|
else
|
|
|
|
reportError(tok, Severity::error, "doubleFree", "Memory pointed to by '" + varname + "' is freed twice.");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-26 08:53:46 +02:00
|
|
|
void CheckLeakAutoVar::check()
|
|
|
|
{
|
|
|
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
|
|
|
|
2015-01-28 13:45:40 +01:00
|
|
|
// Local variables that are known to be non-zero.
|
|
|
|
const std::set<unsigned int> notzero;
|
|
|
|
|
2012-05-26 08:53:46 +02:00
|
|
|
// Check function scopes
|
2012-10-21 09:05:01 +02:00
|
|
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
|
|
|
for (std::size_t i = 0; i < functions; ++i) {
|
|
|
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
|
|
|
// Empty variable info
|
|
|
|
VarInfo varInfo;
|
|
|
|
|
|
|
|
checkScope(scope->classStart, &varInfo, notzero);
|
|
|
|
|
|
|
|
varInfo.conditionalAlloc.clear();
|
|
|
|
|
|
|
|
// Clear reference arguments from varInfo..
|
2015-01-28 13:45:40 +01:00
|
|
|
std::map<unsigned int, VarInfo::AllocInfo>::iterator it = varInfo.alloctype.begin();
|
2012-10-21 09:05:01 +02:00
|
|
|
while (it != varInfo.alloctype.end()) {
|
|
|
|
const Variable *var = symbolDatabase->getVariableFromVarId(it->first);
|
|
|
|
if (!var ||
|
|
|
|
(var->isArgument() && var->isReference()) ||
|
|
|
|
(!var->isArgument() && !var->isLocal()))
|
|
|
|
varInfo.alloctype.erase(it++);
|
|
|
|
else
|
|
|
|
++it;
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
2012-10-21 09:05:01 +02:00
|
|
|
|
|
|
|
ret(scope->classEnd, varInfo);
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
|
|
|
VarInfo *varInfo,
|
|
|
|
std::set<unsigned int> notzero)
|
|
|
|
{
|
2015-01-28 13:45:40 +01:00
|
|
|
std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo->alloctype;
|
2012-05-26 08:53:46 +02:00
|
|
|
std::map<unsigned int, std::string> &possibleUsage = varInfo->possibleUsage;
|
|
|
|
const std::set<unsigned int> conditionalAlloc(varInfo->conditionalAlloc);
|
|
|
|
|
|
|
|
// Parse all tokens
|
|
|
|
const Token * const endToken = startToken->link();
|
|
|
|
for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) {
|
|
|
|
// Deallocation and then dereferencing pointer..
|
|
|
|
if (tok->varId() > 0) {
|
2015-01-28 13:45:40 +01:00
|
|
|
const std::map<unsigned int, VarInfo::AllocInfo>::iterator var = alloctype.find(tok->varId());
|
2012-05-26 08:53:46 +02:00
|
|
|
if (var != alloctype.end()) {
|
2015-01-28 13:45:40 +01:00
|
|
|
if (var->second.status == VarInfo::DEALLOC && !Token::Match(tok->previous(), "[;{},=] %var% =")) {
|
2012-05-26 08:53:46 +02:00
|
|
|
deallocUseError(tok, tok->str());
|
|
|
|
} else if (Token::simpleMatch(tok->tokAt(-2), "= &")) {
|
|
|
|
varInfo->erase(tok->varId());
|
2013-03-01 12:42:04 +01:00
|
|
|
} else if (tok->strAt(-1) == "=") {
|
2012-05-26 08:53:46 +02:00
|
|
|
varInfo->erase(tok->varId());
|
|
|
|
}
|
2012-10-27 16:36:14 +02:00
|
|
|
} else if (Token::Match(tok->previous(), "& %var% = %var% ;")) {
|
|
|
|
varInfo->referenced.insert(tok->tokAt(2)->varId());
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tok->str() == "(" && tok->previous()->isName()) {
|
2015-01-28 13:45:40 +01:00
|
|
|
VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC);
|
|
|
|
functionCall(tok->previous(), varInfo, allocation);
|
2012-05-26 08:53:46 +02:00
|
|
|
tok = tok->link();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// look for end of statement
|
|
|
|
if (!Token::Match(tok, "[;{}]") || Token::Match(tok->next(), "[;{}]"))
|
|
|
|
continue;
|
2015-01-28 13:45:40 +01:00
|
|
|
|
2012-05-26 08:53:46 +02:00
|
|
|
tok = tok->next();
|
2012-09-17 15:22:51 +02:00
|
|
|
if (!tok || tok == endToken)
|
2012-05-26 08:53:46 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
// parse statement
|
|
|
|
|
|
|
|
// assignment..
|
2012-09-17 15:22:51 +02:00
|
|
|
if (tok->varId() && Token::Match(tok, "%var% =")) {
|
2012-05-26 08:53:46 +02:00
|
|
|
// taking address of another variable..
|
|
|
|
if (Token::Match(tok->next(), "= %var% [+;]")) {
|
|
|
|
if (tok->tokAt(2)->varId() != tok->varId()) {
|
|
|
|
// If variable points at allocated memory => error
|
|
|
|
leakIfAllocated(tok, *varInfo);
|
|
|
|
|
|
|
|
// no multivariable checking currently => bail out for rhs variables
|
|
|
|
for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) {
|
|
|
|
if (tok2->str() == ";") {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (tok2->varId()) {
|
|
|
|
varInfo->erase(tok2->varId());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// is variable used in rhs?
|
|
|
|
bool used_in_rhs = false;
|
|
|
|
for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) {
|
|
|
|
if (tok2->str() == ";") {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (tok->varId() == tok2->varId()) {
|
|
|
|
used_in_rhs = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO: Better checking how the pointer is used in rhs?
|
|
|
|
if (used_in_rhs)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Variable has already been allocated => error
|
2012-05-26 21:28:35 +02:00
|
|
|
if (conditionalAlloc.find(tok->varId()) == conditionalAlloc.end())
|
|
|
|
leakIfAllocated(tok, *varInfo);
|
2012-05-26 08:53:46 +02:00
|
|
|
varInfo->erase(tok->varId());
|
|
|
|
|
|
|
|
// not a local variable nor argument?
|
2013-02-06 06:39:58 +01:00
|
|
|
const Variable *var = tok->variable();
|
2014-06-23 16:05:28 +02:00
|
|
|
if (var && !var->isArgument() && (!var->isLocal() || var->isStatic())) {
|
2012-05-26 08:53:46 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-10-14 16:40:30 +02:00
|
|
|
// non-pod variable
|
|
|
|
if (_tokenizer->isCPP() && (!var || !var->typeStartToken()->isStandardType()))
|
|
|
|
continue;
|
|
|
|
|
2012-07-17 16:28:34 +02:00
|
|
|
// Don't check reference variables
|
|
|
|
if (var && var->isReference())
|
|
|
|
continue;
|
|
|
|
|
2012-05-26 08:53:46 +02:00
|
|
|
// allocation?
|
|
|
|
if (Token::Match(tok->tokAt(2), "%type% (")) {
|
2014-04-20 14:59:16 +02:00
|
|
|
int i = _settings->library.alloc(tok->tokAt(2));
|
2013-07-02 07:18:19 +02:00
|
|
|
if (i > 0) {
|
2015-01-28 13:45:40 +01:00
|
|
|
alloctype[tok->varId()].type = i;
|
|
|
|
alloctype[tok->varId()].status = VarInfo::ALLOC;
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
2015-01-28 13:45:40 +01:00
|
|
|
} else if (_tokenizer->isCPP() && tok->strAt(2) == "new") {
|
|
|
|
alloctype[tok->varId()].type = -1;
|
|
|
|
alloctype[tok->varId()].status = VarInfo::ALLOC;
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Assigning non-zero value variable. It might be used to
|
|
|
|
// track the execution for a later if condition.
|
|
|
|
if (Token::Match(tok->tokAt(2), "%num% ;") && MathLib::toLongNumber(tok->strAt(2)) != 0)
|
|
|
|
notzero.insert(tok->varId());
|
2012-06-21 19:00:53 +02:00
|
|
|
else if (Token::Match(tok->tokAt(2), "- %type% ;") && tok->tokAt(3)->isUpperCaseName())
|
2012-05-26 08:53:46 +02:00
|
|
|
notzero.insert(tok->varId());
|
|
|
|
else
|
|
|
|
notzero.erase(tok->varId());
|
|
|
|
}
|
|
|
|
|
|
|
|
// if/else
|
|
|
|
else if (Token::simpleMatch(tok, "if (")) {
|
|
|
|
// Parse function calls inside the condition
|
|
|
|
for (const Token *innerTok = tok->tokAt(2); innerTok; innerTok = innerTok->next()) {
|
|
|
|
if (innerTok->str() == ")")
|
|
|
|
break;
|
|
|
|
if (innerTok->str() == "(" && innerTok->previous()->isName()) {
|
2015-01-28 13:45:40 +01:00
|
|
|
VarInfo::AllocInfo allocation(_settings->library.dealloc(tok), VarInfo::DEALLOC);
|
|
|
|
if (allocation.type == 0)
|
|
|
|
allocation.status = VarInfo::NOALLOC;
|
|
|
|
functionCall(innerTok->previous(), varInfo, allocation);
|
2012-05-26 08:53:46 +02:00
|
|
|
innerTok = innerTok->link();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Token *tok2 = tok->linkAt(1);
|
|
|
|
if (Token::simpleMatch(tok2, ") {")) {
|
2014-04-18 18:18:47 +02:00
|
|
|
VarInfo varInfo1(*varInfo); // VarInfo for if code
|
|
|
|
VarInfo varInfo2(*varInfo); // VarInfo for else code
|
2012-05-26 08:53:46 +02:00
|
|
|
|
|
|
|
if (Token::Match(tok->next(), "( %var% )")) {
|
|
|
|
varInfo2.erase(tok->tokAt(2)->varId());
|
|
|
|
if (notzero.find(tok->tokAt(2)->varId()) != notzero.end())
|
|
|
|
varInfo2.clear();
|
2012-06-17 07:54:24 +02:00
|
|
|
} else if (Token::Match(tok->next(), "( ! %var% )|&&")) {
|
2012-05-26 08:53:46 +02:00
|
|
|
varInfo1.erase(tok->tokAt(3)->varId());
|
2012-06-17 07:54:24 +02:00
|
|
|
} else if (Token::Match(tok->next(), "( %var% ( ! %var% ) )|&&")) {
|
|
|
|
varInfo1.erase(tok->tokAt(5)->varId());
|
2014-04-18 18:18:47 +02:00
|
|
|
} else if (Token::Match(tok->next(), "( %var% < 0 )|&&")) {
|
|
|
|
varInfo1.erase(tok->tokAt(2)->varId());
|
|
|
|
} else if (Token::Match(tok->next(), "( 0 > %var% )|&&")) {
|
|
|
|
varInfo1.erase(tok->tokAt(4)->varId());
|
|
|
|
} else if (Token::Match(tok->next(), "( %var% > 0 )|&&")) {
|
|
|
|
varInfo2.erase(tok->tokAt(2)->varId());
|
|
|
|
} else if (Token::Match(tok->next(), "( 0 < %var% )|&&")) {
|
|
|
|
varInfo2.erase(tok->tokAt(4)->varId());
|
2014-09-17 18:37:41 +02:00
|
|
|
} else if (Token::Match(tok->next(), "( %var% == -1 )|&&")) {
|
|
|
|
varInfo1.erase(tok->tokAt(2)->varId());
|
|
|
|
} else if (Token::Match(tok->next(), "( -1 == %var% )|&&")) {
|
|
|
|
varInfo1.erase(tok->tokAt(4)->varId());
|
2012-06-17 07:54:24 +02:00
|
|
|
}
|
2012-05-26 08:53:46 +02:00
|
|
|
|
|
|
|
checkScope(tok2->next(), &varInfo1, notzero);
|
|
|
|
tok2 = tok2->linkAt(1);
|
|
|
|
if (Token::simpleMatch(tok2, "} else {")) {
|
|
|
|
checkScope(tok2->tokAt(2), &varInfo2, notzero);
|
|
|
|
tok = tok2->linkAt(2)->previous();
|
|
|
|
} else {
|
|
|
|
tok = tok2->previous();
|
|
|
|
}
|
|
|
|
|
|
|
|
VarInfo old;
|
|
|
|
old.swap(*varInfo);
|
|
|
|
|
|
|
|
// Conditional allocation in varInfo1
|
2015-01-28 13:45:40 +01:00
|
|
|
std::map<unsigned int, VarInfo::AllocInfo>::const_iterator it;
|
2012-05-26 08:53:46 +02:00
|
|
|
for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) {
|
|
|
|
if (varInfo2.alloctype.find(it->first) == varInfo2.alloctype.end() &&
|
|
|
|
old.alloctype.find(it->first) == old.alloctype.end()) {
|
|
|
|
varInfo->conditionalAlloc.insert(it->first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Conditional allocation in varInfo2
|
|
|
|
for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) {
|
|
|
|
if (varInfo1.alloctype.find(it->first) == varInfo1.alloctype.end() &&
|
|
|
|
old.alloctype.find(it->first) == old.alloctype.end()) {
|
|
|
|
varInfo->conditionalAlloc.insert(it->first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Conditional allocation/deallocation
|
|
|
|
for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) {
|
2015-01-28 13:45:40 +01:00
|
|
|
if (it->second.status == VarInfo::DEALLOC && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
|
2012-05-26 08:53:46 +02:00
|
|
|
varInfo->conditionalAlloc.erase(it->first);
|
|
|
|
varInfo2.erase(it->first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) {
|
2015-01-28 13:45:40 +01:00
|
|
|
if (it->second.status == VarInfo::DEALLOC && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
|
2012-05-26 08:53:46 +02:00
|
|
|
varInfo->conditionalAlloc.erase(it->first);
|
|
|
|
varInfo1.erase(it->first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
alloctype.insert(varInfo1.alloctype.begin(), varInfo1.alloctype.end());
|
|
|
|
alloctype.insert(varInfo2.alloctype.begin(), varInfo2.alloctype.end());
|
|
|
|
|
|
|
|
possibleUsage.insert(varInfo1.possibleUsage.begin(), varInfo1.possibleUsage.end());
|
|
|
|
possibleUsage.insert(varInfo2.possibleUsage.begin(), varInfo2.possibleUsage.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-28 13:45:40 +01:00
|
|
|
// unknown control.. (TODO: handle loops)
|
2015-01-28 22:29:07 +01:00
|
|
|
else if ((Token::Match(tok, "%type% (") && Token::simpleMatch(tok->linkAt(1), ") {")) || Token::simpleMatch(tok, "do {")) {
|
2012-05-26 08:53:46 +02:00
|
|
|
varInfo->clear();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-01-28 19:44:59 +01:00
|
|
|
// return
|
|
|
|
else if (tok->str() == "return") {
|
|
|
|
ret(tok, *varInfo);
|
|
|
|
varInfo->clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// throw
|
|
|
|
else if (tok->str() == "throw") {
|
|
|
|
bool tryFound = false;
|
|
|
|
const Scope* scope = tok->scope();
|
|
|
|
while (scope && scope->isExecutable()) {
|
|
|
|
if (scope->type == Scope::eTry)
|
|
|
|
tryFound = true;
|
|
|
|
scope = scope->nestedIn;
|
|
|
|
}
|
|
|
|
// If the execution leaves the function then treat it as return
|
|
|
|
if (!tryFound)
|
|
|
|
ret(tok, *varInfo);
|
|
|
|
varInfo->clear();
|
|
|
|
}
|
|
|
|
|
2012-05-26 08:53:46 +02:00
|
|
|
// Function call..
|
2015-01-28 19:44:59 +01:00
|
|
|
else if (Token::Match(tok, "%type% (")) {
|
2015-01-28 13:45:40 +01:00
|
|
|
VarInfo::AllocInfo allocation(_settings->library.dealloc(tok), VarInfo::DEALLOC);
|
|
|
|
if (allocation.type == 0)
|
|
|
|
allocation.status = VarInfo::NOALLOC;
|
|
|
|
functionCall(tok, varInfo, allocation);
|
2012-05-26 08:53:46 +02:00
|
|
|
|
|
|
|
tok = tok->next()->link();
|
|
|
|
|
|
|
|
// Handle scopes that might be noreturn
|
2015-01-28 13:45:40 +01:00
|
|
|
if (allocation.status == VarInfo::NOALLOC && Token::simpleMatch(tok, ") ; }")) {
|
2012-06-24 15:38:37 +02:00
|
|
|
const std::string &functionName(tok->link()->previous()->str());
|
2012-05-26 08:53:46 +02:00
|
|
|
bool unknown = false;
|
2014-05-04 13:11:21 +02:00
|
|
|
if (_tokenizer->IsScopeNoReturn(tok->tokAt(2), &unknown)) {
|
|
|
|
if (!unknown)
|
2012-05-26 08:53:46 +02:00
|
|
|
varInfo->clear();
|
2014-05-04 13:11:21 +02:00
|
|
|
else if (_settings->library.leakignore.find(functionName) == _settings->library.leakignore.end() &&
|
|
|
|
_settings->library.use.find(functionName) == _settings->library.use.end())
|
|
|
|
varInfo->possibleUsageAll(functionName);
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-01-28 13:45:40 +01:00
|
|
|
// delete
|
|
|
|
else if (_tokenizer->isCPP() && tok->str() == "delete") {
|
|
|
|
if (tok->strAt(1) == "[")
|
|
|
|
tok = tok->tokAt(3);
|
|
|
|
else
|
|
|
|
tok = tok->next();
|
|
|
|
while (Token::Match(tok, "%var% ::|."))
|
|
|
|
tok = tok->tokAt(2);
|
|
|
|
if (tok->varId()) {
|
|
|
|
VarInfo::AllocInfo allocation(-1, VarInfo::DEALLOC);
|
|
|
|
changeAllocStatus(varInfo, allocation, tok, tok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-08 17:23:47 +02:00
|
|
|
// goto => weird execution path
|
|
|
|
else if (tok->str() == "goto") {
|
|
|
|
varInfo->clear();
|
|
|
|
}
|
2013-07-28 11:53:49 +02:00
|
|
|
|
2013-07-28 10:26:46 +02:00
|
|
|
// continue/break
|
|
|
|
else if (Token::Match(tok, "continue|break ;")) {
|
|
|
|
varInfo->clear();
|
|
|
|
}
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-28 13:45:40 +01:00
|
|
|
void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg)
|
|
|
|
{
|
|
|
|
std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo->alloctype;
|
|
|
|
std::map<unsigned int, std::string> &possibleUsage = varInfo->possibleUsage;
|
|
|
|
const std::map<unsigned int, VarInfo::AllocInfo>::iterator var = alloctype.find(arg->varId());
|
|
|
|
if (var != alloctype.end()) {
|
|
|
|
if (allocation.status == VarInfo::NOALLOC) {
|
|
|
|
// possible usage
|
|
|
|
possibleUsage[arg->varId()] = tok->str();
|
|
|
|
if (var->second.status == VarInfo::DEALLOC && arg->previous()->str() == "&")
|
|
|
|
varInfo->erase(arg->varId());
|
|
|
|
} else if (var->second.status == VarInfo::DEALLOC) {
|
|
|
|
doubleFreeError(tok, arg->str(), allocation.type);
|
|
|
|
} else if (var->second.type != allocation.type) {
|
|
|
|
// mismatching allocation and deallocation
|
|
|
|
mismatchError(tok, arg->str());
|
|
|
|
varInfo->erase(arg->varId());
|
|
|
|
} else {
|
|
|
|
// deallocation
|
|
|
|
var->second.status = VarInfo::DEALLOC;
|
|
|
|
var->second.type = allocation.type;
|
|
|
|
}
|
|
|
|
} else if (allocation.status != VarInfo::NOALLOC) {
|
|
|
|
alloctype[arg->varId()].status = VarInfo::DEALLOC;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const VarInfo::AllocInfo& allocation)
|
2012-05-26 08:53:46 +02:00
|
|
|
{
|
2012-06-24 15:38:37 +02:00
|
|
|
// Ignore function call?
|
2013-07-20 13:12:24 +02:00
|
|
|
const bool ignore = bool(_settings->library.leakignore.find(tok->str()) != _settings->library.leakignore.end());
|
2012-06-24 15:38:37 +02:00
|
|
|
if (ignore)
|
|
|
|
return;
|
|
|
|
|
2012-05-26 08:53:46 +02:00
|
|
|
for (const Token *arg = tok->tokAt(2); arg; arg = arg->nextArgument()) {
|
2014-09-30 13:02:02 +02:00
|
|
|
if (arg->str() == "new")
|
|
|
|
arg = arg->next();
|
|
|
|
|
2012-05-26 08:53:46 +02:00
|
|
|
if ((Token::Match(arg, "%var% [-,)]") && arg->varId() > 0) ||
|
|
|
|
(Token::Match(arg, "& %var%") && arg->next()->varId() > 0)) {
|
|
|
|
|
|
|
|
// goto variable
|
|
|
|
if (arg->str() == "&")
|
|
|
|
arg = arg->next();
|
|
|
|
|
|
|
|
// Is variable allocated?
|
2015-01-28 13:45:40 +01:00
|
|
|
changeAllocStatus(varInfo, allocation, tok, arg);
|
2012-08-21 17:02:11 +02:00
|
|
|
} else if (Token::Match(arg, "%var% (")) {
|
2015-01-28 13:45:40 +01:00
|
|
|
functionCall(arg, varInfo, allocation);
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CheckLeakAutoVar::leakIfAllocated(const Token *vartok,
|
|
|
|
const VarInfo &varInfo)
|
|
|
|
{
|
2015-01-28 13:45:40 +01:00
|
|
|
const std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
|
2012-05-26 08:53:46 +02:00
|
|
|
const std::map<unsigned int, std::string> &possibleUsage = varInfo.possibleUsage;
|
|
|
|
|
2015-01-28 13:45:40 +01:00
|
|
|
const std::map<unsigned int, VarInfo::AllocInfo>::const_iterator var = alloctype.find(vartok->varId());
|
|
|
|
if (var != alloctype.end() && var->second.status != VarInfo::DEALLOC) {
|
2012-05-26 08:53:46 +02:00
|
|
|
const std::map<unsigned int, std::string>::const_iterator use = possibleUsage.find(vartok->varId());
|
|
|
|
if (use == possibleUsage.end()) {
|
2015-01-28 13:45:40 +01:00
|
|
|
leakError(vartok, vartok->str(), var->second.type);
|
2012-05-26 08:53:46 +02:00
|
|
|
} else {
|
|
|
|
configurationInfo(vartok, use->second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo)
|
|
|
|
{
|
2015-01-28 13:45:40 +01:00
|
|
|
const std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo.alloctype;
|
2012-05-26 08:53:46 +02:00
|
|
|
const std::map<unsigned int, std::string> &possibleUsage = varInfo.possibleUsage;
|
|
|
|
|
|
|
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
2015-01-28 13:45:40 +01:00
|
|
|
for (std::map<unsigned int, VarInfo::AllocInfo>::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) {
|
2012-05-26 08:53:46 +02:00
|
|
|
// don't warn if variable is conditionally allocated
|
2015-01-28 13:45:40 +01:00
|
|
|
if (it->second.status != VarInfo::DEALLOC && varInfo.conditionalAlloc.find(it->first) != varInfo.conditionalAlloc.end())
|
2012-05-26 08:53:46 +02:00
|
|
|
continue;
|
|
|
|
|
2012-10-27 16:36:14 +02:00
|
|
|
// don't warn if there is a reference of the variable
|
|
|
|
if (varInfo.referenced.find(it->first) != varInfo.referenced.end())
|
|
|
|
continue;
|
|
|
|
|
2012-05-26 08:53:46 +02:00
|
|
|
const unsigned int varid = it->first;
|
|
|
|
const Variable *var = symbolDatabase->getVariableFromVarId(varid);
|
|
|
|
if (var) {
|
|
|
|
bool used = false;
|
|
|
|
for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) {
|
|
|
|
if (tok2->str() == ";")
|
|
|
|
break;
|
|
|
|
if (Token::Match(tok2, "return|(|, %varid% [);,]", varid)) {
|
|
|
|
used = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (Token::Match(tok2, "return|(|, & %varid% . %var% [);,]", varid)) {
|
|
|
|
used = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-11 18:28:31 +02:00
|
|
|
// return deallocated pointer
|
2015-01-28 13:45:40 +01:00
|
|
|
if (used && it->second.status == VarInfo::DEALLOC)
|
2012-06-11 18:28:31 +02:00
|
|
|
deallocReturnError(tok, var->name());
|
|
|
|
|
2015-01-28 13:45:40 +01:00
|
|
|
else if (!used && it->second.status != VarInfo::DEALLOC) {
|
2012-06-11 18:28:31 +02:00
|
|
|
const std::map<unsigned int, std::string>::const_iterator use = possibleUsage.find(varid);
|
|
|
|
if (use == possibleUsage.end()) {
|
2015-01-28 13:45:40 +01:00
|
|
|
leakError(tok, var->name(), it->second.type);
|
2012-06-11 18:28:31 +02:00
|
|
|
} else {
|
|
|
|
configurationInfo(tok, use->second);
|
|
|
|
}
|
2012-05-26 08:53:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|