Fixed #4775 (Check for assert() with side effects)
This commit is contained in:
parent
67979f00be
commit
e23038c4de
8
Makefile
8
Makefile
|
@ -93,6 +93,7 @@ MAN_SOURCE=man/cppcheck.1.xml
|
||||||
###### Object Files
|
###### Object Files
|
||||||
|
|
||||||
LIBOBJ = $(SRCDIR)/check64bit.o \
|
LIBOBJ = $(SRCDIR)/check64bit.o \
|
||||||
|
$(SRCDIR)/checkassert.o \
|
||||||
$(SRCDIR)/checkassignif.o \
|
$(SRCDIR)/checkassignif.o \
|
||||||
$(SRCDIR)/checkautovariables.o \
|
$(SRCDIR)/checkautovariables.o \
|
||||||
$(SRCDIR)/checkbool.o \
|
$(SRCDIR)/checkbool.o \
|
||||||
|
@ -138,6 +139,7 @@ CLIOBJ = cli/cmdlineparser.o \
|
||||||
|
|
||||||
TESTOBJ = test/options.o \
|
TESTOBJ = test/options.o \
|
||||||
test/test64bit.o \
|
test/test64bit.o \
|
||||||
|
test/testassert.o \
|
||||||
test/testassignif.o \
|
test/testassignif.o \
|
||||||
test/testautovariables.o \
|
test/testautovariables.o \
|
||||||
test/testbool.o \
|
test/testbool.o \
|
||||||
|
@ -234,6 +236,9 @@ install: cppcheck
|
||||||
$(SRCDIR)/check64bit.o: lib/check64bit.cpp lib/check64bit.h lib/config.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
|
$(SRCDIR)/check64bit.o: lib/check64bit.cpp lib/check64bit.h lib/config.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 $(SRCDIR)/check64bit.o $(SRCDIR)/check64bit.cpp
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_LIB} -c -o $(SRCDIR)/check64bit.o $(SRCDIR)/check64bit.cpp
|
||||||
|
|
||||||
|
$(SRCDIR)/checkassert.o: lib/checkassert.cpp lib/checkassert.h lib/config.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 $(SRCDIR)/checkassert.o $(SRCDIR)/checkassert.cpp
|
||||||
|
|
||||||
$(SRCDIR)/checkassignif.o: lib/checkassignif.cpp lib/checkassignif.h lib/config.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/mathlib.h lib/symboldatabase.h
|
$(SRCDIR)/checkassignif.o: lib/checkassignif.cpp lib/checkassignif.h lib/config.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/mathlib.h lib/symboldatabase.h
|
||||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_LIB} -c -o $(SRCDIR)/checkassignif.o $(SRCDIR)/checkassignif.cpp
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_LIB} -c -o $(SRCDIR)/checkassignif.o $(SRCDIR)/checkassignif.cpp
|
||||||
|
|
||||||
|
@ -363,6 +368,9 @@ test/options.o: test/options.cpp test/options.h
|
||||||
test/test64bit.o: test/test64bit.cpp lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/check64bit.h lib/check.h lib/token.h lib/settings.h lib/standards.h test/testsuite.h test/redirect.h
|
test/test64bit.o: test/test64bit.cpp lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/check64bit.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/test64bit.o test/test64bit.cpp
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/test64bit.o test/test64bit.cpp
|
||||||
|
|
||||||
|
test/testassert.o: test/testassert.cpp lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkassert.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/testassert.o test/testassert.cpp
|
||||||
|
|
||||||
test/testassignif.o: test/testassignif.cpp lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkassignif.h lib/check.h lib/token.h lib/settings.h lib/standards.h lib/mathlib.h test/testsuite.h test/redirect.h
|
test/testassignif.o: test/testassignif.cpp lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkassignif.h lib/check.h lib/token.h lib/settings.h lib/standards.h lib/mathlib.h test/testsuite.h test/redirect.h
|
||||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testassignif.o test/testassignif.cpp
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testassignif.o test/testassignif.cpp
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* Cppcheck - A tool for static C/C++ code analysis
|
||||||
|
* Copyright (C) 2007-2013 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// You should not write statements with side effects in assert()
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "checkassert.h"
|
||||||
|
#include "symboldatabase.h"
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// Register this check class (by creating a static instance of it)
|
||||||
|
namespace {
|
||||||
|
CheckAssert instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckAssert::assertWithSideEffects()
|
||||||
|
{
|
||||||
|
if (!_settings->isEnabled("warning"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const Token *tok = findAssertPattern(_tokenizer->tokens());
|
||||||
|
const Token *endTok = tok ? tok->next()->link() : NULL;
|
||||||
|
|
||||||
|
while (tok && endTok) {
|
||||||
|
for (const Token* tmp = tok->tokAt(1); tmp != endTok; tmp = tmp->next()) {
|
||||||
|
checkVariableAssignment(tmp, true);
|
||||||
|
|
||||||
|
if (tmp->isName() && tmp->type() == Token::eFunction) {
|
||||||
|
const Function* f = tmp->function();
|
||||||
|
if (f->argCount() == 0 && f->isConst) continue;
|
||||||
|
|
||||||
|
// functions with non-const references
|
||||||
|
else if (f->argCount() != 0) {
|
||||||
|
for (std::list<Variable>::const_iterator it = f->argumentList.begin(); it != f->argumentList.end(); ++it) {
|
||||||
|
if (it->isConst() || it->isLocal()) continue;
|
||||||
|
else if (it->isReference()) {
|
||||||
|
const Token* next = it->nameToken()->next();
|
||||||
|
bool isAssigned = checkVariableAssignment(next, false);
|
||||||
|
if (isAssigned)
|
||||||
|
sideEffectInAssertError(tmp, f->name());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// variables in function scope
|
||||||
|
const Scope* scope = f->functionScope;
|
||||||
|
if (!scope) continue;
|
||||||
|
|
||||||
|
for (const Token *tok2 = scope->classStart; tok2 != scope->classEnd; tok2 = tok2->next()) {
|
||||||
|
if (tok2 && tok2->type() != Token::eAssignmentOp && tok2->type() != Token::eIncDecOp) continue;
|
||||||
|
const Variable* var = tok2->previous()->variable();
|
||||||
|
if (!var) continue;
|
||||||
|
|
||||||
|
std::vector<const Token*> returnTokens; // find all return statements
|
||||||
|
for (const Token *rt = scope->classStart; rt != scope->classEnd; rt = rt->next()) {
|
||||||
|
if (!Token::Match(rt, "return %any%")) continue;
|
||||||
|
returnTokens.push_back(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool noReturnInScope = true;
|
||||||
|
for (std::vector<const Token*>::iterator rt = returnTokens.begin(); rt != returnTokens.end(); ++rt) {
|
||||||
|
noReturnInScope &= !inSameScope(*rt, tok2);
|
||||||
|
}
|
||||||
|
if (noReturnInScope) continue;
|
||||||
|
bool isAssigned = checkVariableAssignment(tok2, false);
|
||||||
|
if (isAssigned)
|
||||||
|
sideEffectInAssertError(tmp, f->name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tok = findAssertPattern(endTok->next());
|
||||||
|
endTok = tok ? tok->next()->link() : NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
void CheckAssert::sideEffectInAssertError(const Token *tok, const std::string& functionName)
|
||||||
|
{
|
||||||
|
reportError(tok, Severity::warning,
|
||||||
|
"assertWithSideEffect", "Assert statement calls a function which may have desired side effects: '" + functionName + "'.\n"
|
||||||
|
"Non-pure function: '" + functionName + "' is called inside assert statement. "
|
||||||
|
"Assert statements are removed from release builds so the code inside "
|
||||||
|
"assert statement is not executed. If the code is needed also in release "
|
||||||
|
"builds, this is a bug.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckAssert::assignmentInAssertError(const Token *tok, const std::string& varname)
|
||||||
|
{
|
||||||
|
reportError(tok, Severity::warning,
|
||||||
|
"assignmentInAssert", "Assert statement modifies '" + varname + "'.\n"
|
||||||
|
"Variable '" + varname + "' is modified insert assert statement. "
|
||||||
|
"Assert statements are removed from release builds so the code inside "
|
||||||
|
"assert statement is not executed. If the code is needed also in release "
|
||||||
|
"builds, this is a bug.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if side effects happen on the variable prior to tmp
|
||||||
|
bool CheckAssert::checkVariableAssignment(const Token* assignTok, bool reportError /*= true*/)
|
||||||
|
{
|
||||||
|
const Variable* v = assignTok->previous()->variable();
|
||||||
|
if (!v) return false;
|
||||||
|
|
||||||
|
// assignment
|
||||||
|
if (assignTok->isAssignmentOp() || assignTok->type() == Token::eIncDecOp) {
|
||||||
|
|
||||||
|
if (v->isConst()) return false;
|
||||||
|
|
||||||
|
if (reportError) // report as variable assignment error
|
||||||
|
assignmentInAssertError(assignTok, v->name());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
// TODO: function calls on v
|
||||||
|
}
|
||||||
|
|
||||||
|
const Token* CheckAssert::findAssertPattern(const Token* start)
|
||||||
|
{
|
||||||
|
return Token::findmatch(start, "assert ( %any%");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckAssert::inSameScope(const Token* returnTok, const Token* assignTok)
|
||||||
|
{
|
||||||
|
// TODO: even if a return is in the same scope, the assignment might not affect it.
|
||||||
|
return returnTok->scope() == assignTok->scope();
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Cppcheck - A tool for static C/C++ code analysis
|
||||||
|
* Copyright (C) 2007-2013 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
#ifndef CheckAssertH
|
||||||
|
#define CheckAssertH
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "check.h"
|
||||||
|
|
||||||
|
/// @addtogroup Checks
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checking for side effects in assert statements
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CPPCHECKLIB CheckAssert : public Check {
|
||||||
|
public:
|
||||||
|
CheckAssert() : Check(myName())
|
||||||
|
{}
|
||||||
|
|
||||||
|
CheckAssert(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
|
||||||
|
: Check(myName(), tokenizer, settings, errorLogger)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) {
|
||||||
|
CheckAssert check(tokenizer, settings, errorLogger);
|
||||||
|
check.assertWithSideEffects();
|
||||||
|
}
|
||||||
|
|
||||||
|
void assertWithSideEffects();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool checkVariableAssignment(const Token* tmp, bool reportError = true);
|
||||||
|
bool inSameScope(const Token* returnTok, const Token* assignTok);
|
||||||
|
|
||||||
|
static const Token* findAssertPattern(const Token *start);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void sideEffectInAssertError(const Token *tok, const std::string& functionName);
|
||||||
|
void assignmentInAssertError(const Token *tok, const std::string &varname);
|
||||||
|
|
||||||
|
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
|
||||||
|
CheckAssert c(0, settings, errorLogger);
|
||||||
|
c.assertWithSideEffects();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string myName() {
|
||||||
|
return "Side Effects in asserts";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string classInfo() const {
|
||||||
|
return "Warn if side effects in assert statements \n";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/// @}
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
#endif
|
|
@ -1170,46 +1170,6 @@ void CheckOther::selfAssignmentError(const Token *tok, const std::string &varnam
|
||||||
"selfAssignment", "Redundant assignment of '" + varname + "' to itself.");
|
"selfAssignment", "Redundant assignment of '" + varname + "' to itself.");
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
// int a = 1;
|
|
||||||
// assert(a = 2); // <- assert should not have a side-effect
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
static inline const Token *findAssertPattern(const Token *start)
|
|
||||||
{
|
|
||||||
return Token::findmatch(start, "assert ( %any%");
|
|
||||||
}
|
|
||||||
|
|
||||||
void CheckOther::checkAssignmentInAssert()
|
|
||||||
{
|
|
||||||
if (!_settings->isEnabled("warning"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const Token *tok = findAssertPattern(_tokenizer->tokens());
|
|
||||||
const Token *endTok = tok ? tok->next()->link() : NULL;
|
|
||||||
|
|
||||||
while (tok && endTok) {
|
|
||||||
for (tok = tok->tokAt(2); tok != endTok; tok = tok->next()) {
|
|
||||||
if (tok->isName() && (tok->next()->isAssignmentOp() || tok->next()->type() == Token::eIncDecOp))
|
|
||||||
assignmentInAssertError(tok, tok->str());
|
|
||||||
else if (Token::Match(tok, "--|++ %var%"))
|
|
||||||
assignmentInAssertError(tok, tok->strAt(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
tok = findAssertPattern(endTok->next());
|
|
||||||
endTok = tok ? tok->next()->link() : NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CheckOther::assignmentInAssertError(const Token *tok, const std::string &varname)
|
|
||||||
{
|
|
||||||
reportError(tok, Severity::warning,
|
|
||||||
"assignmentInAssert", "Assert statement modifies '" + varname + "'.\n"
|
|
||||||
"Variable '" + varname + "' is modified insert assert statement. "
|
|
||||||
"Assert statements are removed from release builds so the code inside "
|
|
||||||
"assert statement is not executed. If the code is needed also in release "
|
|
||||||
"builds, this is a bug.");
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// if ((x != 1) || (x != 3)) // expression always true
|
// if ((x != 1) || (x != 3)) // expression always true
|
||||||
// if ((x == 1) && (x == 3)) // expression always false
|
// if ((x == 1) && (x == 3)) // expression always false
|
||||||
|
|
|
@ -60,7 +60,6 @@ public:
|
||||||
checkOther.checkRedundantAssignment();
|
checkOther.checkRedundantAssignment();
|
||||||
checkOther.checkRedundantAssignmentInSwitch();
|
checkOther.checkRedundantAssignmentInSwitch();
|
||||||
checkOther.checkSuspiciousCaseInSwitch();
|
checkOther.checkSuspiciousCaseInSwitch();
|
||||||
checkOther.checkAssignmentInAssert();
|
|
||||||
checkOther.checkSelfAssignment();
|
checkOther.checkSelfAssignment();
|
||||||
checkOther.checkDuplicateIf();
|
checkOther.checkDuplicateIf();
|
||||||
checkOther.checkDuplicateBranch();
|
checkOther.checkDuplicateBranch();
|
||||||
|
@ -186,9 +185,6 @@ public:
|
||||||
/** @brief %Check for assigning a variable to itself*/
|
/** @brief %Check for assigning a variable to itself*/
|
||||||
void checkSelfAssignment();
|
void checkSelfAssignment();
|
||||||
|
|
||||||
/** @brief %Check for assignment to a variable in an assert test*/
|
|
||||||
void checkAssignmentInAssert();
|
|
||||||
|
|
||||||
/** @brief %Check for testing for mutual exclusion over ||*/
|
/** @brief %Check for testing for mutual exclusion over ||*/
|
||||||
void checkIncorrectLogicOperator();
|
void checkIncorrectLogicOperator();
|
||||||
|
|
||||||
|
@ -293,7 +289,6 @@ private:
|
||||||
void suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString);
|
void suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString);
|
||||||
void suspiciousEqualityComparisonError(const Token* tok);
|
void suspiciousEqualityComparisonError(const Token* tok);
|
||||||
void selfAssignmentError(const Token *tok, const std::string &varname);
|
void selfAssignmentError(const Token *tok, const std::string &varname);
|
||||||
void assignmentInAssertError(const Token *tok, const std::string &varname);
|
|
||||||
void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always);
|
void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always);
|
||||||
void redundantConditionError(const Token *tok, const std::string &text);
|
void redundantConditionError(const Token *tok, const std::string &text);
|
||||||
void misusedScopeObjectError(const Token *tok, const std::string &varname);
|
void misusedScopeObjectError(const Token *tok, const std::string &varname);
|
||||||
|
@ -357,7 +352,6 @@ private:
|
||||||
c.suspiciousCaseInSwitchError(0, "||");
|
c.suspiciousCaseInSwitchError(0, "||");
|
||||||
c.suspiciousEqualityComparisonError(0);
|
c.suspiciousEqualityComparisonError(0);
|
||||||
c.selfAssignmentError(0, "varname");
|
c.selfAssignmentError(0, "varname");
|
||||||
c.assignmentInAssertError(0, "varname");
|
|
||||||
c.incorrectLogicOperatorError(0, "foo > 3 && foo < 4", true);
|
c.incorrectLogicOperatorError(0, "foo > 3 && foo < 4", true);
|
||||||
c.redundantConditionError(0, "If x > 10 the condition x > 11 is always true.");
|
c.redundantConditionError(0, "If x > 10 the condition x > 11 is always true.");
|
||||||
c.memsetZeroBytesError(0, "varname");
|
c.memsetZeroBytesError(0, "varname");
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="check64bit.cpp" />
|
<ClCompile Include="check64bit.cpp" />
|
||||||
|
<ClCompile Include="checkassert.cpp" />
|
||||||
<ClCompile Include="checkassignif.cpp" />
|
<ClCompile Include="checkassignif.cpp" />
|
||||||
<ClCompile Include="checkautovariables.cpp" />
|
<ClCompile Include="checkautovariables.cpp" />
|
||||||
<ClCompile Include="checkbool.cpp" />
|
<ClCompile Include="checkbool.cpp" />
|
||||||
|
@ -75,6 +76,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="check.h" />
|
<ClInclude Include="check.h" />
|
||||||
<ClInclude Include="check64bit.h" />
|
<ClInclude Include="check64bit.h" />
|
||||||
|
<ClInclude Include="checkassert.h" />
|
||||||
<ClInclude Include="checkassignif.h" />
|
<ClInclude Include="checkassignif.h" />
|
||||||
<ClInclude Include="checkautovariables.h" />
|
<ClInclude Include="checkautovariables.h" />
|
||||||
<ClInclude Include="checkbool.h" />
|
<ClInclude Include="checkbool.h" />
|
||||||
|
|
|
@ -119,6 +119,9 @@
|
||||||
<ClCompile Include="checksizeof.cpp">
|
<ClCompile Include="checksizeof.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="checkassert.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="checkbufferoverrun.h">
|
<ClInclude Include="checkbufferoverrun.h">
|
||||||
|
@ -238,6 +241,9 @@
|
||||||
<ClInclude Include="checksizeof.h">
|
<ClInclude Include="checksizeof.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="checkassert.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="version.rc" />
|
<ResourceCompile Include="version.rc" />
|
||||||
|
|
|
@ -4,6 +4,7 @@ include($$PWD/pcrerules.pri)
|
||||||
INCLUDEPATH += ../externals/tinyxml
|
INCLUDEPATH += ../externals/tinyxml
|
||||||
HEADERS += $${BASEPATH}check.h \
|
HEADERS += $${BASEPATH}check.h \
|
||||||
$${BASEPATH}check64bit.h \
|
$${BASEPATH}check64bit.h \
|
||||||
|
$${BASEPATH}checkassert.h \
|
||||||
$${BASEPATH}checkassignif.h \
|
$${BASEPATH}checkassignif.h \
|
||||||
$${BASEPATH}checkautovariables.h \
|
$${BASEPATH}checkautovariables.h \
|
||||||
$${BASEPATH}checkbool.h \
|
$${BASEPATH}checkbool.h \
|
||||||
|
@ -42,6 +43,7 @@ HEADERS += $${BASEPATH}check.h \
|
||||||
|
|
||||||
|
|
||||||
SOURCES += $${BASEPATH}check64bit.cpp \
|
SOURCES += $${BASEPATH}check64bit.cpp \
|
||||||
|
$${BASEPATH}checkassert.cpp \
|
||||||
$${BASEPATH}checkassignif.cpp \
|
$${BASEPATH}checkassignif.cpp \
|
||||||
$${BASEPATH}checkautovariables.cpp \
|
$${BASEPATH}checkautovariables.cpp \
|
||||||
$${BASEPATH}checkbool.cpp \
|
$${BASEPATH}checkbool.cpp \
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* Cppcheck - A tool for static C/C++ code analysis
|
||||||
|
* Copyright (C) 2007-2013 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tokenize.h"
|
||||||
|
#include "checkassert.h"
|
||||||
|
#include "testsuite.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
extern std::ostringstream errout;
|
||||||
|
|
||||||
|
class TestAssert : public TestFixture {
|
||||||
|
public:
|
||||||
|
TestAssert() : TestFixture("TestAsserts") {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void check(
|
||||||
|
const char code[],
|
||||||
|
const char *filename = NULL) {
|
||||||
|
// Clear the error buffer..
|
||||||
|
errout.str("");
|
||||||
|
|
||||||
|
Settings settings;
|
||||||
|
settings.addEnabled("style");
|
||||||
|
settings.addEnabled("warning");
|
||||||
|
settings.addEnabled("portability");
|
||||||
|
settings.addEnabled("performance");
|
||||||
|
|
||||||
|
// Tokenize..
|
||||||
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, filename ? filename : "test.cpp");
|
||||||
|
|
||||||
|
// Check..
|
||||||
|
CheckAssert checkAssert(&tokenizer, &settings, this);
|
||||||
|
checkAssert.runSimplifiedChecks(&tokenizer, &settings, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
TEST_CASE(assignmentInAssert);
|
||||||
|
TEST_CASE(functionCallInAssert);
|
||||||
|
TEST_CASE(safeFunctionCallInAssert);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void safeFunctionCallInAssert() {
|
||||||
|
check(
|
||||||
|
"int a;\n"
|
||||||
|
"bool b = false;\n"
|
||||||
|
"int foo() {\n"
|
||||||
|
" if (b) { a = 1+2 };\n"
|
||||||
|
" return a;\n"
|
||||||
|
"}\n"
|
||||||
|
"assert(foo() == 3); \n"
|
||||||
|
);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void functionCallInAssert() {
|
||||||
|
check(
|
||||||
|
"int a;\n"
|
||||||
|
"int foo() {\n"
|
||||||
|
" a = 1+2;\n"
|
||||||
|
" return a;\n"
|
||||||
|
"}\n"
|
||||||
|
"assert(foo() == 3); \n"
|
||||||
|
);
|
||||||
|
ASSERT_EQUALS("[test.cpp:6]: (warning) Assert statement calls a function which may have desired side effects: 'foo'.\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void assignmentInAssert() {
|
||||||
|
check("void f() {\n"
|
||||||
|
" int a; a = 0;\n"
|
||||||
|
" assert(a = 2);\n"
|
||||||
|
" return a;\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str());
|
||||||
|
|
||||||
|
check("void f(int a) {\n"
|
||||||
|
" assert(a == 2);\n"
|
||||||
|
" return a;\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void f(int a, int b) {\n"
|
||||||
|
" assert(a == 2 && b = 1);\n"
|
||||||
|
" return a;\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning) Assert statement modifies 'b'.\n", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" int a; a = 0;\n"
|
||||||
|
" assert(a += 2);\n"
|
||||||
|
" return a;\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" int a; a = 0;\n"
|
||||||
|
" assert(a *= 2);\n"
|
||||||
|
" return a;\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" int a; a = 0;\n"
|
||||||
|
" assert(a -= 2);\n"
|
||||||
|
" return a;\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" int a = 0;\n"
|
||||||
|
" assert(a--);\n"
|
||||||
|
" return a;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_TEST(TestAssert)
|
|
@ -4,6 +4,7 @@ INCLUDEPATH += ../externals/tinyxml
|
||||||
|
|
||||||
|
|
||||||
SOURCES += $${BASEPATH}/test64bit.cpp \
|
SOURCES += $${BASEPATH}/test64bit.cpp \
|
||||||
|
$${BASEPATH}/testassert.cpp \
|
||||||
$${BASEPATH}/testassignif.cpp \
|
$${BASEPATH}/testassignif.cpp \
|
||||||
$${BASEPATH}/testautovariables.cpp \
|
$${BASEPATH}/testautovariables.cpp \
|
||||||
$${BASEPATH}/testbool.cpp \
|
$${BASEPATH}/testbool.cpp \
|
||||||
|
|
|
@ -106,9 +106,6 @@ private:
|
||||||
TEST_CASE(trac2071);
|
TEST_CASE(trac2071);
|
||||||
TEST_CASE(trac2084);
|
TEST_CASE(trac2084);
|
||||||
TEST_CASE(trac3693);
|
TEST_CASE(trac3693);
|
||||||
|
|
||||||
TEST_CASE(assignmentInAssert);
|
|
||||||
|
|
||||||
TEST_CASE(modulo);
|
TEST_CASE(modulo);
|
||||||
|
|
||||||
TEST_CASE(incorrectLogicOperator1);
|
TEST_CASE(incorrectLogicOperator1);
|
||||||
|
@ -3235,62 +3232,6 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void assignmentInAssert() {
|
|
||||||
check("void f() {\n"
|
|
||||||
" int a; a = 0;\n"
|
|
||||||
" assert(a = 2);\n"
|
|
||||||
" return a;\n"
|
|
||||||
"}\n"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str());
|
|
||||||
|
|
||||||
check("void f(int a) {\n"
|
|
||||||
" assert(a == 2);\n"
|
|
||||||
" return a;\n"
|
|
||||||
"}\n"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("", errout.str());
|
|
||||||
|
|
||||||
check("void f(int a, int b) {\n"
|
|
||||||
" assert(a == 2 && b = 1);\n"
|
|
||||||
" return a;\n"
|
|
||||||
"}\n"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("[test.cpp:2]: (warning) Assert statement modifies 'b'.\n", errout.str());
|
|
||||||
|
|
||||||
check("void f() {\n"
|
|
||||||
" int a; a = 0;\n"
|
|
||||||
" assert(a += 2);\n"
|
|
||||||
" return a;\n"
|
|
||||||
"}\n"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str());
|
|
||||||
|
|
||||||
check("void f() {\n"
|
|
||||||
" int a; a = 0;\n"
|
|
||||||
" assert(a *= 2);\n"
|
|
||||||
" return a;\n"
|
|
||||||
"}\n"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str());
|
|
||||||
|
|
||||||
check("void f() {\n"
|
|
||||||
" int a; a = 0;\n"
|
|
||||||
" assert(a -= 2);\n"
|
|
||||||
" return a;\n"
|
|
||||||
"}\n"
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str());
|
|
||||||
|
|
||||||
check("void f() {\n"
|
|
||||||
" int a = 0;\n"
|
|
||||||
" assert(a--);\n" // don't simplify and verify
|
|
||||||
" return a;\n"
|
|
||||||
"}\n", 0, false, false, false, false
|
|
||||||
);
|
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void modulo() {
|
void modulo() {
|
||||||
check("bool f(bool& b1, bool& b2, bool& b3) {\n"
|
check("bool f(bool& b1, bool& b2, bool& b3) {\n"
|
||||||
" b1 = a % 5 == 4;\n"
|
" b1 = a % 5 == 4;\n"
|
||||||
|
@ -6224,4 +6165,4 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestOther)
|
REGISTER_TEST(TestOther)
|
|
@ -31,6 +31,7 @@
|
||||||
<ClCompile Include="..\cli\threadexecutor.cpp" />
|
<ClCompile Include="..\cli\threadexecutor.cpp" />
|
||||||
<ClCompile Include="options.cpp" />
|
<ClCompile Include="options.cpp" />
|
||||||
<ClCompile Include="test64bit.cpp" />
|
<ClCompile Include="test64bit.cpp" />
|
||||||
|
<ClCompile Include="testassert.cpp" />
|
||||||
<ClCompile Include="testassignif.cpp" />
|
<ClCompile Include="testassignif.cpp" />
|
||||||
<ClCompile Include="testautovariables.cpp" />
|
<ClCompile Include="testautovariables.cpp" />
|
||||||
<ClCompile Include="testbool.cpp" />
|
<ClCompile Include="testbool.cpp" />
|
||||||
|
|
|
@ -172,6 +172,9 @@
|
||||||
<ClCompile Include="testsizeof.cpp">
|
<ClCompile Include="testsizeof.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="testassert.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="options.h">
|
<ClInclude Include="options.h">
|
||||||
|
|
Loading…
Reference in New Issue