diff --git a/Makefile b/Makefile
index c67f08f57..d3f6ac8e1 100644
--- a/Makefile
+++ b/Makefile
@@ -88,6 +88,7 @@ LIBOBJ = lib/check64bit.o \
lib/checkexceptionsafety.o \
lib/checkinternal.o \
lib/checkio.o \
+ lib/checkleakautovar.o \
lib/checkmemoryleak.o \
lib/checknonreentrantfunctions.o \
lib/checknullpointer.o \
@@ -138,6 +139,7 @@ TESTOBJ = test/options.o \
test/testincompletestatement.o \
test/testinternal.o \
test/testio.o \
+ test/testleakautovar.o \
test/testmathlib.o \
test/testmemleak.o \
test/testnonreentrantfunctions.o \
@@ -238,6 +240,9 @@ lib/checkinternal.o: lib/checkinternal.cpp lib/checkinternal.h lib/check.h lib/t
lib/checkio.o: lib/checkio.cpp lib/checkio.h lib/check.h lib/token.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/standards.h lib/symboldatabase.h lib/mathlib.h
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_LIB} -c -o lib/checkio.o lib/checkio.cpp
+lib/checkleakautovar.o: lib/checkleakautovar.cpp lib/checkleakautovar.h lib/check.h lib/token.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/standards.h lib/symboldatabase.h lib/mathlib.h lib/checknullpointer.h
+ $(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_LIB} -c -o lib/checkleakautovar.o lib/checkleakautovar.cpp
+
lib/checkmemoryleak.o: lib/checkmemoryleak.cpp lib/checkmemoryleak.h lib/check.h lib/token.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/standards.h lib/symboldatabase.h lib/mathlib.h lib/checkuninitvar.h
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_LIB} -c -o lib/checkmemoryleak.o lib/checkmemoryleak.cpp
@@ -382,6 +387,9 @@ test/testinternal.o: test/testinternal.cpp lib/tokenize.h lib/errorlogger.h lib/
test/testio.o: test/testio.cpp lib/checkio.h lib/check.h lib/token.h lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/settings.h lib/standards.h test/testsuite.h test/redirect.h
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testio.o test/testio.cpp
+test/testleakautovar.o: test/testleakautovar.cpp lib/tokenize.h lib/errorlogger.h lib/suppressions.h lib/tokenlist.h lib/checkleakautovar.h lib/check.h lib/token.h lib/settings.h lib/standards.h test/testsuite.h test/redirect.h
+ $(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testleakautovar.o test/testleakautovar.cpp
+
test/testmathlib.o: test/testmathlib.cpp lib/mathlib.h test/testsuite.h lib/errorlogger.h lib/suppressions.h test/redirect.h
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testmathlib.o test/testmathlib.cpp
diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp
new file mode 100644
index 000000000..c1a0ea874
--- /dev/null
+++ b/lib/checkleakautovar.cpp
@@ -0,0 +1,456 @@
+/*
+ * Cppcheck - A tool for static C/C++ code analysis
+ * Copyright (C) 2007-2012 Daniel Marjamäki and Cppcheck team.
+ *
+ * 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 .
+ */
+
+//---------------------------------------------------------------------------
+// Leaks when using auto variables
+//---------------------------------------------------------------------------
+
+#include "checkleakautovar.h"
+
+#include "tokenize.h"
+#include "errorlogger.h"
+#include "symboldatabase.h"
+
+#include "checknullpointer.h" // <- isUpper
+
+#include
+
+//---------------------------------------------------------------------------
+
+// Register this check class (by creating a static instance of it)
+namespace {
+ CheckLeakAutoVar instance;
+}
+
+//---------------------------------------------------------------------------
+
+void VarInfo::print()
+{
+ std::cout << "size=" << alloctype.size() << std::endl;
+ std::map::const_iterator it;
+ for (it = alloctype.begin(); it != alloctype.end(); ++it) {
+ std::string strusage;
+ std::map::const_iterator use = possibleUsage.find(it->first);
+ if (use != possibleUsage.end())
+ strusage = use->second;
+
+ std::cout << "alloctype='" << it->second << "' "
+ << "possibleUsage='" << strusage << "'" << std::endl;
+ }
+}
+
+void VarInfo::possibleUsageAll(const std::string &functionName)
+{
+ possibleUsage.clear();
+ std::map::const_iterator it;
+ for (it = alloctype.begin(); it != alloctype.end(); ++it)
+ possibleUsage[it->first] = functionName;
+}
+
+
+void CheckLeakAutoVar::leakError(const Token *tok, const std::string &varname)
+{
+ reportError(tok, Severity::error, "newleak", "New memory leak: " + varname);
+}
+
+void CheckLeakAutoVar::mismatchError(const Token *tok, const std::string &varname)
+{
+ reportError(tok, Severity::error, "newmismatch", "New mismatching allocation and deallocation: " + varname);
+}
+
+void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varname)
+{
+ reportError(tok, Severity::error, "newdeallocuse", "Using deallocated pointer " + varname);
+}
+
+void CheckLeakAutoVar::doubleDeallocationError(const Token *tok, const std::string &varname)
+{
+ reportError(tok, Severity::error, "doubledeallocation", "Double deallocation: " + varname);
+}
+
+void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &functionName)
+{
+ if (_settings->experimental)
+ {
+ reportError(tok,
+ Severity::information,
+ "leakconfiguration",
+ functionName + " configuration is needed to establish if there is a leak or not");
+ }
+}
+
+void CheckLeakAutoVar::check()
+{
+ const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
+
+ // Check function scopes
+ for (std::list::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) {
+ if (i->type == Scope::eFunction) {
+ // Empty variable info
+ VarInfo varInfo;
+
+ // Local variables that are known to be non-zero.
+ const std::set notzero;
+
+ checkScope(i->classStart, &varInfo, notzero);
+
+ varInfo.conditionalAlloc.clear();
+
+ ret(i->classEnd, varInfo);
+ }
+ }
+}
+
+void CheckLeakAutoVar::checkScope(const Token * const startToken,
+ VarInfo *varInfo,
+ std::set notzero)
+{
+ std::map &alloctype = varInfo->alloctype;
+ std::map &possibleUsage = varInfo->possibleUsage;
+ const std::set conditionalAlloc(varInfo->conditionalAlloc);
+
+ // Allocation functions. key = function name, value = allocation type
+ std::map allocFunctions;
+ allocFunctions["malloc"] = "malloc";
+ allocFunctions["strdup"] = "malloc";
+ allocFunctions["fopen"] = "fopen";
+
+ // Deallocation functions. key = function name, value = allocation type
+ std::map deallocFunctions;
+ deallocFunctions["free"] = "malloc";
+ deallocFunctions["fclose"] = "fopen";
+
+ // 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) {
+ const std::map::iterator var = alloctype.find(tok->varId());
+ if (var != alloctype.end()) {
+ if (var->second == "dealloc") {
+ deallocUseError(tok, tok->str());
+ } else if (Token::simpleMatch(tok->tokAt(-2), "= &")) {
+ varInfo->erase(tok->varId());
+ } else if (Token::simpleMatch(tok->previous(), "=")) {
+ varInfo->erase(tok->varId());
+ }
+ }
+ }
+
+ if (tok->str() == "(" && tok->previous()->isName()) {
+ functionCall(tok->previous(), varInfo, "");
+ tok = tok->link();
+ continue;
+ }
+
+ // look for end of statement
+ if (!Token::Match(tok, "[;{}]") || Token::Match(tok->next(), "[;{}]"))
+ continue;
+ tok = tok->next();
+ if (tok == endToken)
+ break;
+
+ // parse statement
+
+ // assignment..
+ if (tok && tok->varId() && Token::Match(tok, "%var% =")) {
+ // 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
+ leakIfAllocated(tok, *varInfo);
+ varInfo->erase(tok->varId());
+
+ // not a local variable nor argument?
+ const Variable *var = _tokenizer->getSymbolDatabase()->getVariableFromVarId(tok->varId());
+ if (var && !var->isArgument() && !var->isLocal()) {
+ continue;
+ }
+
+ // allocation?
+ if (Token::Match(tok->tokAt(2), "%type% (")) {
+ const std::map::const_iterator it = allocFunctions.find(tok->strAt(2));
+ if (it != allocFunctions.end()) {
+ alloctype[tok->varId()] = it->second;
+ }
+ }
+
+ // 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());
+ else if (Token::Match(tok->tokAt(2), "- %type% ;") && CheckNullPointer::isUpper(tok->strAt(3)))
+ 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()) {
+ std::string dealloc;
+ {
+ const std::map::iterator func = deallocFunctions.find(tok->str());
+ if (func != deallocFunctions.end()) {
+ dealloc = func->second;
+ }
+ }
+
+ functionCall(innerTok->previous(), varInfo, dealloc);
+ innerTok = innerTok->link();
+ }
+ }
+
+ const Token *tok2 = tok->linkAt(1);
+ if (Token::simpleMatch(tok2, ") {")) {
+ VarInfo varInfo1(*varInfo);
+ VarInfo varInfo2(*varInfo);
+
+ if (Token::Match(tok->next(), "( %var% )")) {
+ varInfo2.erase(tok->tokAt(2)->varId());
+ if (notzero.find(tok->tokAt(2)->varId()) != notzero.end())
+ varInfo2.clear();
+ } else if (Token::Match(tok->next(), "( ! %var% )"))
+ varInfo1.erase(tok->tokAt(3)->varId());
+
+ 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
+ std::map::const_iterator it;
+ 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) {
+ if (it->second == "dealloc" && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
+ varInfo->conditionalAlloc.erase(it->first);
+ varInfo2.erase(it->first);
+ }
+ }
+ for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) {
+ if (it->second == "dealloc" && conditionalAlloc.find(it->first) != conditionalAlloc.end()) {
+ 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());
+ }
+ }
+
+ // unknown control..
+ else if (Token::Match(tok, "%type% (") && Token::simpleMatch(tok->linkAt(1), ") {")) {
+ varInfo->clear();
+ break;
+ }
+
+ // Function call..
+ else if (Token::Match(tok, "%type% (")) {
+ std::string dealloc;
+ {
+ const std::map::iterator func = deallocFunctions.find(tok->str());
+ if (func != deallocFunctions.end()) {
+ dealloc = func->second;
+ }
+ }
+
+ functionCall(tok, varInfo, dealloc);
+
+ tok = tok->next()->link();
+
+ // Handle scopes that might be noreturn
+ if (dealloc.empty() && Token::simpleMatch(tok, ") ; }")) {
+ bool unknown = false;
+ if (_tokenizer->IsScopeNoReturn(tok->tokAt(2), &unknown)) {
+ if (unknown) {
+ const std::string &functionName(tok->link()->previous()->str());
+ varInfo->possibleUsageAll(functionName);
+ } else {
+ varInfo->clear();
+ }
+ }
+ }
+
+ continue;
+ }
+
+ // return
+ else if (tok->str() == "return") {
+ ret(tok, *varInfo);
+ varInfo->clear();
+ }
+ }
+}
+
+void CheckLeakAutoVar::functionCall(const Token *tok, VarInfo *varInfo, const std::string &dealloc)
+{
+ std::map &alloctype = varInfo->alloctype;
+ std::map &possibleUsage = varInfo->possibleUsage;
+
+ for (const Token *arg = tok->tokAt(2); arg; arg = arg->nextArgument()) {
+ 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?
+ const std::map::iterator var = alloctype.find(arg->varId());
+ if (var != alloctype.end()) {
+ if (dealloc.empty()) {
+ // possible usage
+ possibleUsage[arg->varId()] = tok->str();
+ } else if (var->second == "dealloc") {
+ doubleDeallocationError(tok, arg->str());
+ } else if (var->second != dealloc) {
+ // mismatching allocation and deallocation
+ mismatchError(tok, arg->str());
+ varInfo->erase(arg->varId());
+ } else {
+ // deallocation
+ var->second = "dealloc";
+ }
+ } else if (!dealloc.empty()) {
+ alloctype[arg->varId()] = "dealloc";
+ }
+ }
+ }
+}
+
+
+void CheckLeakAutoVar::leakIfAllocated(const Token *vartok,
+ const VarInfo &varInfo)
+{
+ const std::map &alloctype = varInfo.alloctype;
+ const std::map &possibleUsage = varInfo.possibleUsage;
+
+ const std::map::const_iterator var = alloctype.find(vartok->varId());
+ if (var != alloctype.end() && var->second != "dealloc") {
+ const std::map::const_iterator use = possibleUsage.find(vartok->varId());
+ if (use == possibleUsage.end()) {
+ leakError(vartok, vartok->str());
+ } else {
+ configurationInfo(vartok, use->second);
+ }
+ }
+}
+
+void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo)
+{
+ const std::map &alloctype = varInfo.alloctype;
+ const std::map &possibleUsage = varInfo.possibleUsage;
+
+ const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
+ for (std::map::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) {
+ // has pointer been deallocated?
+ if (it->second == "dealloc")
+ continue;
+
+ // don't warn if variable is conditionally allocated
+ if (varInfo.conditionalAlloc.find(it->first) != varInfo.conditionalAlloc.end())
+ continue;
+
+ 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;
+ }
+ }
+ if (used)
+ continue;
+
+ const std::map::const_iterator use = possibleUsage.find(varid);
+ if (use == possibleUsage.end()) {
+ leakError(tok, var->name());
+ } else {
+ configurationInfo(tok, use->second);
+ }
+ }
+ }
+}
diff --git a/lib/checkleakautovar.h b/lib/checkleakautovar.h
new file mode 100644
index 000000000..c7698c5f5
--- /dev/null
+++ b/lib/checkleakautovar.h
@@ -0,0 +1,130 @@
+/*
+ * Cppcheck - A tool for static C/C++ code analysis
+ * Copyright (C) 2007-2012 Daniel Marjamäki and Cppcheck team.
+ *
+ * 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 .
+ */
+
+
+//---------------------------------------------------------------------------
+#ifndef checkleakautovarH
+#define checkleakautovarH
+//---------------------------------------------------------------------------
+
+#include "check.h"
+
+
+class VarInfo {
+public:
+ std::map alloctype;
+ std::map possibleUsage;
+ std::set conditionalAlloc;
+
+ void clear() {
+ alloctype.clear();
+ possibleUsage.clear();
+ conditionalAlloc.clear();
+ }
+
+ void erase(unsigned int varid) {
+ alloctype.erase(varid);
+ possibleUsage.erase(varid);
+ conditionalAlloc.erase(varid);
+ }
+
+ void swap(VarInfo &other) {
+ alloctype.swap(other.alloctype);
+ possibleUsage.swap(other.possibleUsage);
+ conditionalAlloc.swap(other.conditionalAlloc);
+ }
+
+ /** set possible usage for all variables */
+ void possibleUsageAll(const std::string &functionName);
+
+ void print();
+};
+
+
+/// @addtogroup Checks
+/// @{
+
+/**
+ * @brief Check for leaks
+ */
+
+class CheckLeakAutoVar : public Check {
+public:
+ /** This constructor is used when registering the CheckLeakAutoVar */
+ CheckLeakAutoVar() : Check(myName()) {
+ }
+
+ /** This constructor is used when running checks. */
+ CheckLeakAutoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
+ : Check(myName(), tokenizer, settings, errorLogger) {
+ }
+
+ /** @brief Run checks against the simplified token list */
+ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) {
+ CheckLeakAutoVar checkLeakAutoVar(tokenizer, settings, errorLogger);
+ checkLeakAutoVar.check();
+ }
+
+private:
+
+ /** check for leaks in all scopes */
+ void check();
+
+ /** check for leaks in a function scope */
+ void checkScope(const Token * const startToken,
+ VarInfo *varInfo,
+ std::set notzero);
+
+ /** parse function call */
+ void functionCall(const Token *tok, VarInfo *varInfo, const std::string &dealloc);
+
+ /** return. either "return" or end of variable scope is seen */
+ void ret(const Token *tok, const VarInfo &varInfo);
+
+ /** if variable is allocated then there is a leak */
+ void leakIfAllocated(const Token *vartok, const VarInfo &varInfo);
+
+ void leakError(const Token* tok, const std::string &varname);
+ void mismatchError(const Token* tok, const std::string &varname);
+ void deallocUseError(const Token *tok, const std::string &varname);
+ void doubleDeallocationError(const Token *tok, const std::string &varname);
+
+ /** message: user configuration is needed to complete analysis */
+ void configurationInfo(const Token* tok, const std::string &functionName);
+
+ void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
+ CheckLeakAutoVar c(0, settings, errorLogger);
+ c.leakError(NULL, "p");
+ c.mismatchError(NULL, "p");
+ c.deallocUseError(NULL, "p");
+ c.doubleDeallocationError(NULL, "p");
+ c.configurationInfo(0, "f"); // user configuration is needed to complete analysis
+ }
+
+ std::string myName() const {
+ return "Leaks in functions";
+ }
+
+ std::string classInfo() const {
+ return "";
+ }
+};
+/// @}
+//---------------------------------------------------------------------------
+#endif
+
diff --git a/lib/lib.pri b/lib/lib.pri
index 3fdf6082e..95deec5aa 100644
--- a/lib/lib.pri
+++ b/lib/lib.pri
@@ -12,6 +12,7 @@ HEADERS += $${BASEPATH}check.h \
$${BASEPATH}checkexceptionsafety.h \
$${BASEPATH}checkinternal.h \
$${BASEPATH}checkio.h \
+ $${BASEPATH}checkleakautovar.h \
$${BASEPATH}checkmemoryleak.h \
$${BASEPATH}checknonreentrantfunctions.h \
$${BASEPATH}checknullpointer.h \
@@ -46,6 +47,7 @@ SOURCES += $${BASEPATH}check64bit.cpp \
$${BASEPATH}checkexceptionsafety.cpp \
$${BASEPATH}checkinternal.cpp \
$${BASEPATH}checkio.cpp \
+ $${BASEPATH}checkleakautovar.cpp \
$${BASEPATH}checkmemoryleak.cpp \
$${BASEPATH}checknonreentrantfunctions.cpp \
$${BASEPATH}checknullpointer.cpp \
diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp
new file mode 100644
index 000000000..52ab1483e
--- /dev/null
+++ b/test/testleakautovar.cpp
@@ -0,0 +1,410 @@
+/*
+ * Cppcheck - A tool for static C/C++ code analysis
+ * Copyright (C) 2007-2012 Daniel Marjamäki and Cppcheck team.
+ *
+ * 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 .
+ */
+
+
+#include "tokenize.h"
+#include "checkleakautovar.h"
+#include "testsuite.h"
+#include
+
+extern std::ostringstream errout;
+
+class TestLeakAutoVar : public TestFixture {
+public:
+ TestLeakAutoVar() : TestFixture("TestLeakAutoVar")
+ { }
+
+private:
+
+ void run() {
+ // Assign
+ TEST_CASE(assign1);
+ TEST_CASE(assign2);
+ TEST_CASE(assign3);
+ TEST_CASE(assign4);
+ TEST_CASE(assign5);
+ TEST_CASE(assign6);
+ TEST_CASE(assign7);
+ TEST_CASE(assign8);
+ TEST_CASE(assign9);
+
+ TEST_CASE(deallocuse1);
+ TEST_CASE(deallocuse2);
+ TEST_CASE(deallocuse3);
+
+ TEST_CASE(doublefree);
+
+ // exit
+ TEST_CASE(exit1);
+ TEST_CASE(exit2);
+
+ // goto
+ TEST_CASE(goto1);
+
+ // if/else
+ TEST_CASE(ifelse1);
+ TEST_CASE(ifelse2);
+ TEST_CASE(ifelse3);
+ TEST_CASE(ifelse4);
+
+ // switch
+ TEST_CASE(switch1);
+
+ // loops
+ TEST_CASE(loop1);
+
+ // mismatching allocation/deallocation
+ TEST_CASE(mismatch_fopen_free);
+
+ // Execution reaches a 'return'
+ TEST_CASE(return1);
+ TEST_CASE(return2);
+ TEST_CASE(return3);
+
+ // Possible leak => Further configuration is needed for complete analysis
+ TEST_CASE(configuration1);
+ TEST_CASE(configuration2);
+ TEST_CASE(configuration3);
+ TEST_CASE(configuration4);
+ }
+
+ void check(const char code[]) {
+ // Clear the error buffer..
+ errout.str("");
+
+ // Tokenize..
+ Settings settings;
+ Tokenizer tokenizer(&settings, this);
+ std::istringstream istr(code);
+ tokenizer.tokenize(istr, "test.c");
+ tokenizer.simplifyTokenList();
+
+ // Check for leaks..
+ CheckLeakAutoVar c;
+ settings.experimental = true;
+ c.runSimplifiedChecks(&tokenizer, &settings, this);
+ }
+
+ void assign1() {
+ check("void f() {\n"
+ " char *p = malloc(10);\n"
+ " p = NULL;\n"
+ " free(p);\n"
+ "}\n");
+ ASSERT_EQUALS("[test.c:3]: (error) New memory leak: p\n", errout.str());
+ }
+
+ void assign2() {
+ check("void f() {\n"
+ " char *p = malloc(10);\n"
+ " char *q = p;\n"
+ " free(q);\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void assign3() {
+ check("void f() {\n"
+ " char *p = malloc(10);\n"
+ " char *q = p + 1;\n"
+ " free(q - 1);\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void assign4() {
+ check("void f() {\n"
+ " char *a = malloc(10);\n"
+ " a += 10;\n"
+ " free(a - 10);\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void assign5() {
+ check("void foo()\n"
+ "{\n"
+ " char *p = new char[100];\n"
+ " list += p;\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void assign6() { // #2806 - FP when there is redundant assignment
+ check("void foo() {\n"
+ " char *p = malloc(10);\n"
+ " p = strcpy(p,q);\n"
+ " free(p);\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void assign7() {
+ check("void foo(struct str *d) {\n"
+ " struct str *p = malloc(10);\n"
+ " d->p = p;\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void assign8() { // linux list
+ check("void foo(struct str *d) {\n"
+ " struct str *p = malloc(10);\n"
+ " d->p = &p->x;\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void assign9() {
+ check("void foo() {\n"
+ " char *p = x();\n"
+ " free(p);\n"
+ " p = NULL;\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void deallocuse1() {
+ check("void f(char *p) {\n"
+ " free(p);\n"
+ " *p = 0;\n"
+ "}");
+ ASSERT_EQUALS("[test.c:3]: (error) Using deallocated pointer p\n", errout.str());
+
+ check("void f(char *p) {\n"
+ " free(p);\n"
+ " char c = *p;\n"
+ "}");
+ ASSERT_EQUALS("[test.c:3]: (error) Using deallocated pointer p\n", errout.str());
+ }
+
+ void deallocuse2() {
+ check("void f(char *p) {\n"
+ " free(p);\n"
+ " strcpy(a, p);\n"
+ "}");
+ TODO_ASSERT_EQUALS("error", "", errout.str());
+
+ check("void f(char *p) {\n" // #3041 - assigning pointer when it's used
+ " free(p);\n"
+ " strcpy(a, p=b());\n"
+ "}");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void deallocuse3() {
+ check("void f(struct str *p) {\n"
+ " free(p);\n"
+ " p = p->next;\n"
+ "}");
+ ASSERT_EQUALS("[test.c:3]: (error) Using deallocated pointer p\n", errout.str());
+ }
+
+ void doublefree() {
+ check("void f(char *p) {\n"
+ " free(p);\n"
+ " free(p);\n"
+ "}");
+ ASSERT_EQUALS("[test.c:3]: (error) Double deallocation: p\n", errout.str());
+ }
+
+ void exit1() {
+ check("void f() {\n"
+ " char *p = malloc(10);\n"
+ " exit(0);\n"
+ "}");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void exit2() {
+ check("void f() {\n"
+ " char *p = malloc(10);\n"
+ " fatal_error();\n"
+ "}");
+ ASSERT_EQUALS("[test.c:4]: (information) fatal_error configuration is needed to establish if there is a leak or not\n", errout.str());
+ }
+
+ void goto1() {
+ check("static void f() {\n"
+ " int err = -ENOMEM;\n"
+ " char *reg = malloc(100);\n"
+ " if (err) {\n"
+ " free(reg);\n"
+ " }\n"
+ "}");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void ifelse1() {
+ check("int f() {\n"
+ " char *p = NULL;\n"
+ " if (x) { p = malloc(10); }\n"
+ " else { return 0; }\n"
+ " free(p);\n"
+ "}");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void ifelse2() {
+ check("int f() {\n"
+ " char *p = NULL;\n"
+ " if (x) { p = malloc(10); }\n"
+ " else { return 0; }\n"
+ "}");
+ ASSERT_EQUALS("[test.c:5]: (error) New memory leak: p\n", errout.str());
+ }
+
+ void ifelse3() {
+ check("void f() {\n"
+ " char *p = malloc(10);\n"
+ " if (!p) { return; }\n"
+ " free(p);\n"
+ "}");
+ ASSERT_EQUALS("", errout.str());
+
+ check("void f() {\n"
+ " char *p = malloc(10);\n"
+ " if (p) { } else { return; }\n"
+ " free(p);\n"
+ "}");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void ifelse4() {
+ check("void f(int x) {\n"
+ " char *p;\n"
+ " if (x) { p = malloc(10); }\n"
+ " if (x) { free(p); }\n"
+ "}");
+ ASSERT_EQUALS("", errout.str());
+
+ check("void f(int x) {\n"
+ " char *p;\n"
+ " if (x) { p = malloc(10); }\n"
+ " if (!x) { return; }\n"
+ " free(p);\n"
+ "}");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void switch1() {
+ check("void f() {\n"
+ " char *p = 0;\n"
+ " switch (x) {\n"
+ " case 123: p = malloc(100); break;\n"
+ " default: return;\n"
+ " }\n"
+ " free(p);\n"
+ "}");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void loop1() {
+ // test the handling of { }
+ check("void f() {\n"
+ " char *p;\n"
+ " for (i=0;i<5;i++) { }\n"
+ " if (x) { free(p) }\n"
+ " else { a = p; }\n"
+ "}\n");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void mismatch_fopen_free() {
+ check("void f() {\n"
+ " FILE*f=fopen(fname,a);\n"
+ " free(f);\n"
+ "}");
+ ASSERT_EQUALS("[test.c:3]: (error) New mismatching allocation and deallocation: f\n", errout.str());
+ }
+
+ void return1() {
+ check("int f() {\n"
+ " char *p = malloc(100);\n"
+ " return 123;\n"
+ "}");
+ ASSERT_EQUALS("[test.c:3]: (error) New memory leak: p\n", errout.str());
+ }
+
+ void return2() {
+ check("char *f() {\n"
+ " char *p = malloc(100);\n"
+ " return p;\n"
+ "}");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void return3() {
+ check("struct dev * f() {\n"
+ " struct ABC *abc = malloc(100);\n"
+ " return &abc->dev;\n"
+ "}");
+ ASSERT_EQUALS("", errout.str());
+ }
+
+ void configuration1() {
+ // Possible leak => configuration is required for complete analysis
+ // The user should be able to "white list" and "black list" functions.
+
+ // possible leak. If the function 'x' deallocates the pointer or
+ // takes the address, there is no leak.
+ check("void f() {\n"
+ " char *p = malloc(10);\n"
+ " x(p);\n"
+ "}");
+ ASSERT_EQUALS("[test.c:4]: (information) x configuration is needed to establish if there is a leak or not\n", errout.str());
+ }
+
+ void configuration2() {
+ // possible leak. If the function 'x' deallocates the pointer or
+ // takes the address, there is no leak.
+ check("void f() {\n"
+ " char *p = malloc(10);\n"
+ " x(&p);\n"
+ "}");
+ ASSERT_EQUALS("[test.c:4]: (information) x configuration is needed to establish if there is a leak or not\n", errout.str());
+ }
+
+ void configuration3() {
+ check("void f() {\n"
+ " char *p = malloc(10);\n"
+ " if (set_data(p)) { }\n"
+ "}");
+ ASSERT_EQUALS("[test.c:4]: (information) set_data configuration is needed to establish if there is a leak or not\n", errout.str());
+
+ check("void f() {\n"
+ " char *p = malloc(10);\n"
+ " if (set_data(p)) { return; }\n"
+ "}");
+ ASSERT_EQUALS("[test.c:3]: (information) set_data configuration is needed to establish if there is a leak or not\n"
+ "[test.c:4]: (information) set_data configuration is needed to establish if there is a leak or not\n"
+ , errout.str());
+ }
+
+ void configuration4() {
+ check("void f() {\n"
+ " char *p = malloc(10);\n"
+ " int ret = set_data(p);\n"
+ " return ret;\n"
+ "}");
+ ASSERT_EQUALS("[test.c:4]: (information) set_data configuration is needed to establish if there is a leak or not\n", errout.str());
+ }
+};
+
+REGISTER_TEST(TestLeakAutoVar)
+