diff --git a/Makefile b/Makefile index 194ebf58e..62bba1bb2 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,7 @@ MAN_SOURCE=man/cppcheck.1.xml ###### Object Files LIBOBJ = lib/check64bit.o \ + lib/checkassignif.o \ lib/checkautovariables.o \ lib/checkbufferoverrun.o \ lib/checkclass.o \ @@ -81,6 +82,7 @@ CLIOBJ = cli/cmdlineparser.o \ TESTOBJ = test/options.o \ test/test64bit.o \ + test/testassignif.o \ test/testautovariables.o \ test/testbufferoverrun.o \ test/testcharvar.o \ @@ -170,6 +172,9 @@ install: cppcheck lib/check64bit.o: lib/check64bit.cpp lib/check64bit.h lib/check.h lib/token.h lib/tokenize.h lib/settings.h lib/errorlogger.h lib/symboldatabase.h lib/mathlib.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_LIB} -c -o lib/check64bit.o lib/check64bit.cpp +lib/checkassignif.o: lib/checkassignif.cpp lib/checkassignif.h lib/check.h lib/token.h lib/tokenize.h lib/settings.h lib/errorlogger.h lib/symboldatabase.h lib/mathlib.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_LIB} -c -o lib/checkassignif.o lib/checkassignif.cpp + lib/checkautovariables.o: lib/checkautovariables.cpp lib/checkautovariables.h lib/check.h lib/token.h lib/tokenize.h lib/settings.h lib/errorlogger.h lib/symboldatabase.h lib/mathlib.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_LIB} -c -o lib/checkautovariables.o lib/checkautovariables.cpp @@ -266,6 +271,9 @@ test/options.o: test/options.cpp test/options.h test/test64bit.o: test/test64bit.cpp lib/tokenize.h lib/check64bit.h lib/check.h lib/token.h lib/settings.h lib/errorlogger.h test/testsuite.h test/redirect.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/test64bit.o test/test64bit.cpp +test/testassignif.o: test/testassignif.cpp lib/tokenize.h lib/checkassignif.h lib/check.h lib/token.h lib/settings.h lib/errorlogger.h test/testsuite.h test/redirect.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testassignif.o test/testassignif.cpp + test/testautovariables.o: test/testautovariables.cpp lib/tokenize.h lib/checkautovariables.h lib/check.h lib/token.h lib/settings.h lib/errorlogger.h test/testsuite.h test/redirect.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testautovariables.o test/testautovariables.cpp diff --git a/lib/checkassignif.cpp b/lib/checkassignif.cpp new file mode 100644 index 000000000..f84568041 --- /dev/null +++ b/lib/checkassignif.cpp @@ -0,0 +1,77 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2011 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 . + */ + +//--------------------------------------------------------------------------- +// Check for assignment / condition mismatches +//--------------------------------------------------------------------------- + +#include "checkassignif.h" +#include "symboldatabase.h" + +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace +{ +CheckAssignIf instance; +} + + +void CheckAssignIf::check() +{ + if (!_settings->_checkCodingStyle) + return; + + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (tok->str() != "=") + continue; + + if (Token::Match(tok->tokAt(-2), "[;{}] %var% = %var% & %num% ;")) + { + const unsigned int varid(tok->previous()->varId()); + if (varid == 0) + continue; + + const MathLib::bigint num = MathLib::toLongNumber(tok->strAt(3)); + if (num < 0) + continue; + + for (const Token *tok2 = tok->tokAt(4); tok2; tok2 = tok2->next()) + { + if (tok2->str() == "(" || tok2->str() == "}" || tok2->str() == "=") + break; + if (Token::Match(tok2,"if ( %varid% %any% %num% ) ", varid)) + { + const std::string op(tok2->strAt(3)); + const MathLib::bigint num2 = MathLib::toLongNumber(tok2->strAt(4)); + if (op == "==" && (num & num2) != num2) + mismatchError(tok2); + break; + } + } + } + } +} + +void CheckAssignIf::mismatchError(const Token *tok) +{ + reportError(tok, Severity::style, + "assignIfMismatchError", + "Mismatching assignment and condition, condition is always false"); +} diff --git a/lib/checkassignif.h b/lib/checkassignif.h new file mode 100644 index 000000000..66c87d260 --- /dev/null +++ b/lib/checkassignif.h @@ -0,0 +1,83 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2011 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 checkassignifH +#define checkassignifH +//--------------------------------------------------------------------------- + +#include "check.h" + + +/// @addtogroup Checks +/// @{ + +/** + * @brief Check for assignment / condition mismatches + */ + +class CheckAssignIf : public Check +{ +public: + /** This constructor is used when registering the CheckAssignIf */ + CheckAssignIf() : Check(myName()) + { + } + + /** This constructor is used when running checks. */ + CheckAssignIf(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) + { + CheckAssignIf checkAssignIf(tokenizer, settings, errorLogger); + checkAssignIf.check(); + } + + /** Check for obsolete functions */ + void check(); + +private: + + void mismatchError(const Token *tok); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) + { + CheckAssignIf c(0, settings, errorLogger); + c.mismatchError(0); + } + + std::string myName() const + { + return "match assignment / conditions"; + } + + std::string classInfo() const + { + return "Match assignments and conditions:\n" + " mismatching assignment and condition"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif + diff --git a/lib/lib.pri b/lib/lib.pri index 4ca1346fb..95f0ebe22 100644 --- a/lib/lib.pri +++ b/lib/lib.pri @@ -4,6 +4,7 @@ LIBS += -L../externals -lpcre INCLUDEPATH += ../externals ../externals/tinyxml HEADERS += $${BASEPATH}check.h \ $${BASEPATH}check64bit.h \ + $${BASEPATH}checkassignif.h \ $${BASEPATH}checkautovariables.h \ $${BASEPATH}checkbufferoverrun.h \ $${BASEPATH}checkclass.h \ @@ -30,6 +31,7 @@ HEADERS += $${BASEPATH}check.h \ $${BASEPATH}tokenize.h SOURCES += $${BASEPATH}check64bit.cpp \ + $${BASEPATH}checkassignif.cpp \ $${BASEPATH}checkautovariables.cpp \ $${BASEPATH}checkbufferoverrun.cpp \ $${BASEPATH}checkclass.cpp \ diff --git a/test/testassignif.cpp b/test/testassignif.cpp new file mode 100644 index 000000000..e3bd421bc --- /dev/null +++ b/test/testassignif.cpp @@ -0,0 +1,73 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2011 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 "checkassignif.h" +#include "testsuite.h" +#include + +extern std::ostringstream errout; + +class TestAssignIf : public TestFixture +{ +public: + TestAssignIf() : TestFixture("TestAssignIf") + { } + +private: + + + void run() + { + TEST_CASE(testand); + } + + void check(const char code[]) + { + // Clear the error buffer.. + errout.str(""); + + Settings settings; + settings._checkCodingStyle = true; + + // Tokenize.. + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + tokenizer.setVarId(); + tokenizer.simplifyTokenList(); + + // Check char variable usage.. + CheckAssignIf CheckAssignIf(&tokenizer, &settings, this); + CheckAssignIf.check(); + } + + void testand() + { + check("void foo(int x)\n" + "{\n" + " int y = x & 4;\n" + " if (y == 3);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Mismatching assignment and condition, condition is always false\n", errout.str()); + } +}; + +REGISTER_TEST(TestAssignIf) +