From bf2f76e70c5dc7a35487f50418761dc5f0dcbb95 Mon Sep 17 00:00:00 2001 From: PKEuS Date: Tue, 2 Sep 2014 18:05:02 +0200 Subject: [PATCH] Detect and purge duplicate configurations by calculating a checksum. --- lib/cppcheck.cpp | 64 ++++++++++++++++++++++++++++++++++++----------- lib/cppcheck.h | 3 ++- lib/tokenlist.cpp | 21 ++++++++++++++++ lib/tokenlist.h | 6 +++++ 4 files changed, 79 insertions(+), 15 deletions(-) diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index c4fd3d442..d82ad435e 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -82,8 +82,9 @@ void CppCheck::replaceAll(std::string& code, const std::string &from, const std: bool CppCheck::findError(std::string code, const char FileName[]) { + std::set checksums; // First make sure that error occurs with the original code - checkFile(code, FileName); + checkFile(code, FileName, checksums); if (_errorList.empty()) { // Error does not occur with this code return false; @@ -102,7 +103,8 @@ bool CppCheck::findError(std::string code, const char FileName[]) // is still there. code = previousCode.substr(found+9); _errorList.clear(); - checkFile(code, FileName); + checksums.clear(); + checkFile(code, FileName, checksums); } if (_errorList.empty()) { @@ -199,6 +201,7 @@ unsigned int CppCheck::processFile(const std::string& filename, const std::strin } } + std::set checksums; unsigned int checkCount = 0; for (std::list::const_iterator it = configurations.begin(); it != configurations.end(); ++it) { // Check only a few configurations (default 12), after that bail out, unless --force @@ -232,7 +235,10 @@ unsigned int CppCheck::processFile(const std::string& filename, const std::strin return exitcode; } } else { - checkFile(codeWithoutCfg + appendCode, filename.c_str()); + if (!checkFile(codeWithoutCfg + appendCode, filename.c_str(), checksums)) { + if (_settings.isEnabled("information") && (_settings.debug || _settings._verbose)) + purgedConfigurationMessage(filename, cfg); + } } } } catch (const std::runtime_error &e) { @@ -325,11 +331,10 @@ void CppCheck::analyseFile(std::istream &fin, const std::string &filename) //--------------------------------------------------------------------------- // CppCheck - A function that checks a specified file //--------------------------------------------------------------------------- - -void CppCheck::checkFile(const std::string &code, const char FileName[]) +bool CppCheck::checkFile(const std::string &code, const char FileName[], std::set& checksums) { if (_settings.terminated() || _settings.checkConfiguration) - return; + return true; Tokenizer _tokenizer(&_settings, this); if (_settings._showtime != SHOWTIME_NONE) @@ -354,9 +359,15 @@ void CppCheck::checkFile(const std::string &code, const char FileName[]) Timer timer("Tokenizer::tokenize", _settings._showtime, &S_timerResults); result = _tokenizer.tokenize(istr, FileName, cfg); timer.Stop(); + + unsigned long long checksum = _tokenizer.list.calculateChecksum(); + if (checksums.find(checksum) != checksums.end()) + return false; + checksums.insert(checksum); + if (!result) { // File had syntax errors, abort - return; + return true; } // dump @@ -369,13 +380,13 @@ void CppCheck::checkFile(const std::string &code, const char FileName[]) _tokenizer.dump(fdump); fdump << "" << std::endl; } - return; + return true; } // call all "runChecks" in all registered Check classes for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { if (_settings.terminated()) - return; + return true; Timer timerRunChecks((*it)->name() + "::runChecks", _settings._showtime, &S_timerResults); (*it)->runChecks(&_tokenizer, &_settings, this); @@ -387,30 +398,30 @@ void CppCheck::checkFile(const std::string &code, const char FileName[]) executeRules("normal", _tokenizer); if (!_simplify) - return; + return true; Timer timer3("Tokenizer::simplifyTokenList2", _settings._showtime, &S_timerResults); result = _tokenizer.simplifyTokenList2(); timer3.Stop(); if (!result) - return; + return true; // call all "runSimplifiedChecks" in all registered Check classes for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { if (_settings.terminated()) - return; + return true; Timer timerSimpleChecks((*it)->name() + "::runSimplifiedChecks", _settings._showtime, &S_timerResults); (*it)->runSimplifiedChecks(&_tokenizer, &_settings, this); } if (_settings.terminated()) - return; + return true; executeRules("simple", _tokenizer); if (_settings.terminated()) - return; + return true; } catch (const InternalError &e) { std::list locationList; ErrorLogger::ErrorMessage::FileLocation loc; @@ -433,6 +444,7 @@ void CppCheck::checkFile(const std::string &code, const char FileName[]) _errorLogger.reportErr(errmsg); } + return true; } void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &tokenizer) @@ -567,6 +579,30 @@ void CppCheck::tooManyConfigsError(const std::string &file, const std::size_t nu reportErr(errmsg); } +void CppCheck::purgedConfigurationMessage(const std::string &file, const std::string& configuration) +{ + + tooManyConfigs = false; + + if (_settings.isEnabled("information") && file.empty()) + return; + + std::list loclist; + if (!file.empty()) { + ErrorLogger::ErrorMessage::FileLocation location; + location.setfile(file); + loclist.push_back(location); + } + + ErrorLogger::ErrorMessage errmsg(loclist, + Severity::information, + "The configuration '" + configuration + "' was not checked because its code equals another one.", + "toomanyconfigs", + false); + + reportErr(errmsg); +} + //--------------------------------------------------------------------------- void CppCheck::reportErr(const ErrorLogger::ErrorMessage &msg) diff --git a/lib/cppcheck.h b/lib/cppcheck.h index f833423ee..43ea22722 100644 --- a/lib/cppcheck.h +++ b/lib/cppcheck.h @@ -128,6 +128,7 @@ public: void analyseFile(std::istream &f, const std::string &filename); void tooManyConfigsError(const std::string &file, const std::size_t numberOfConfigurations); + void purgedConfigurationMessage(const std::string &file, const std::string& configuration); void dontSimplify() { _simplify = false; @@ -147,7 +148,7 @@ private: unsigned int processFile(const std::string& filename, const std::string& fileContent); /** @brief Check file */ - void checkFile(const std::string &code, const char FileName[]); + bool checkFile(const std::string &code, const char FileName[], std::set& checksums); /** * @brief Execute rules, if any diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 790ba7477..b18c6a9ff 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -394,6 +394,27 @@ bool TokenList::createTokens(std::istream &code, const std::string& file0) return true; } +//--------------------------------------------------------------------------- + +unsigned long long TokenList::calculateChecksum() const +{ + unsigned long long checksum = 0; + for (const Token* tok = front(); tok; tok = tok->next()) { + unsigned int subchecksum1 = tok->flags() + tok->varId() + static_cast(tok->type()); + unsigned int subchecksum2 = 0; + for (std::size_t i = 0; i < tok->str().size(); i++) + subchecksum2 += (unsigned int)tok->str()[i]; + if (!tok->originalName().empty()) { + for (std::size_t i = 0; i < tok->originalName().size(); i++) + subchecksum2 += (unsigned int) tok->originalName()[i]; + } + + checksum ^= ((static_cast(subchecksum1) << 32) | subchecksum2); + } + return checksum; +} + + //--------------------------------------------------------------------------- struct AST_state { diff --git a/lib/tokenlist.h b/lib/tokenlist.h index 395e6f9b1..944e5d586 100644 --- a/lib/tokenlist.h +++ b/lib/tokenlist.h @@ -120,6 +120,12 @@ public: */ std::string fileLine(const Token *tok) const; + /** + * Calculates a 64-bit checksum of the token list used to compare + * multiple token lists with each other as quickly as possible. + */ + unsigned long long calculateChecksum() const; + void createAst(); private: