Detect and purge duplicate configurations by calculating a checksum.

This commit is contained in:
PKEuS 2014-09-02 18:05:02 +02:00
parent a632f68345
commit bf2f76e70c
4 changed files with 79 additions and 15 deletions

View File

@ -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[]) bool CppCheck::findError(std::string code, const char FileName[])
{ {
std::set<unsigned long long> checksums;
// First make sure that error occurs with the original code // First make sure that error occurs with the original code
checkFile(code, FileName); checkFile(code, FileName, checksums);
if (_errorList.empty()) { if (_errorList.empty()) {
// Error does not occur with this code // Error does not occur with this code
return false; return false;
@ -102,7 +103,8 @@ bool CppCheck::findError(std::string code, const char FileName[])
// is still there. // is still there.
code = previousCode.substr(found+9); code = previousCode.substr(found+9);
_errorList.clear(); _errorList.clear();
checkFile(code, FileName); checksums.clear();
checkFile(code, FileName, checksums);
} }
if (_errorList.empty()) { if (_errorList.empty()) {
@ -199,6 +201,7 @@ unsigned int CppCheck::processFile(const std::string& filename, const std::strin
} }
} }
std::set<unsigned long long> checksums;
unsigned int checkCount = 0; unsigned int checkCount = 0;
for (std::list<std::string>::const_iterator it = configurations.begin(); it != configurations.end(); ++it) { for (std::list<std::string>::const_iterator it = configurations.begin(); it != configurations.end(); ++it) {
// Check only a few configurations (default 12), after that bail out, unless --force // 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; return exitcode;
} }
} else { } 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) { } 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 // CppCheck - A function that checks a specified file
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool CppCheck::checkFile(const std::string &code, const char FileName[], std::set<unsigned long long>& checksums)
void CppCheck::checkFile(const std::string &code, const char FileName[])
{ {
if (_settings.terminated() || _settings.checkConfiguration) if (_settings.terminated() || _settings.checkConfiguration)
return; return true;
Tokenizer _tokenizer(&_settings, this); Tokenizer _tokenizer(&_settings, this);
if (_settings._showtime != SHOWTIME_NONE) 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); Timer timer("Tokenizer::tokenize", _settings._showtime, &S_timerResults);
result = _tokenizer.tokenize(istr, FileName, cfg); result = _tokenizer.tokenize(istr, FileName, cfg);
timer.Stop(); timer.Stop();
unsigned long long checksum = _tokenizer.list.calculateChecksum();
if (checksums.find(checksum) != checksums.end())
return false;
checksums.insert(checksum);
if (!result) { if (!result) {
// File had syntax errors, abort // File had syntax errors, abort
return; return true;
} }
// dump // dump
@ -369,13 +380,13 @@ void CppCheck::checkFile(const std::string &code, const char FileName[])
_tokenizer.dump(fdump); _tokenizer.dump(fdump);
fdump << "</dump>" << std::endl; fdump << "</dump>" << std::endl;
} }
return; return true;
} }
// call all "runChecks" in all registered Check classes // call all "runChecks" in all registered Check classes
for (std::list<Check *>::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { for (std::list<Check *>::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) {
if (_settings.terminated()) if (_settings.terminated())
return; return true;
Timer timerRunChecks((*it)->name() + "::runChecks", _settings._showtime, &S_timerResults); Timer timerRunChecks((*it)->name() + "::runChecks", _settings._showtime, &S_timerResults);
(*it)->runChecks(&_tokenizer, &_settings, this); (*it)->runChecks(&_tokenizer, &_settings, this);
@ -387,30 +398,30 @@ void CppCheck::checkFile(const std::string &code, const char FileName[])
executeRules("normal", _tokenizer); executeRules("normal", _tokenizer);
if (!_simplify) if (!_simplify)
return; return true;
Timer timer3("Tokenizer::simplifyTokenList2", _settings._showtime, &S_timerResults); Timer timer3("Tokenizer::simplifyTokenList2", _settings._showtime, &S_timerResults);
result = _tokenizer.simplifyTokenList2(); result = _tokenizer.simplifyTokenList2();
timer3.Stop(); timer3.Stop();
if (!result) if (!result)
return; return true;
// call all "runSimplifiedChecks" in all registered Check classes // call all "runSimplifiedChecks" in all registered Check classes
for (std::list<Check *>::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { for (std::list<Check *>::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) {
if (_settings.terminated()) if (_settings.terminated())
return; return true;
Timer timerSimpleChecks((*it)->name() + "::runSimplifiedChecks", _settings._showtime, &S_timerResults); Timer timerSimpleChecks((*it)->name() + "::runSimplifiedChecks", _settings._showtime, &S_timerResults);
(*it)->runSimplifiedChecks(&_tokenizer, &_settings, this); (*it)->runSimplifiedChecks(&_tokenizer, &_settings, this);
} }
if (_settings.terminated()) if (_settings.terminated())
return; return true;
executeRules("simple", _tokenizer); executeRules("simple", _tokenizer);
if (_settings.terminated()) if (_settings.terminated())
return; return true;
} catch (const InternalError &e) { } catch (const InternalError &e) {
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList; std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc; ErrorLogger::ErrorMessage::FileLocation loc;
@ -433,6 +444,7 @@ void CppCheck::checkFile(const std::string &code, const char FileName[])
_errorLogger.reportErr(errmsg); _errorLogger.reportErr(errmsg);
} }
return true;
} }
void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &tokenizer) 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); reportErr(errmsg);
} }
void CppCheck::purgedConfigurationMessage(const std::string &file, const std::string& configuration)
{
tooManyConfigs = false;
if (_settings.isEnabled("information") && file.empty())
return;
std::list<ErrorLogger::ErrorMessage::FileLocation> 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) void CppCheck::reportErr(const ErrorLogger::ErrorMessage &msg)

View File

@ -128,6 +128,7 @@ public:
void analyseFile(std::istream &f, const std::string &filename); void analyseFile(std::istream &f, const std::string &filename);
void tooManyConfigsError(const std::string &file, const std::size_t numberOfConfigurations); void tooManyConfigsError(const std::string &file, const std::size_t numberOfConfigurations);
void purgedConfigurationMessage(const std::string &file, const std::string& configuration);
void dontSimplify() { void dontSimplify() {
_simplify = false; _simplify = false;
@ -147,7 +148,7 @@ private:
unsigned int processFile(const std::string& filename, const std::string& fileContent); unsigned int processFile(const std::string& filename, const std::string& fileContent);
/** @brief Check file */ /** @brief Check file */
void checkFile(const std::string &code, const char FileName[]); bool checkFile(const std::string &code, const char FileName[], std::set<unsigned long long>& checksums);
/** /**
* @brief Execute rules, if any * @brief Execute rules, if any

View File

@ -394,6 +394,27 @@ bool TokenList::createTokens(std::istream &code, const std::string& file0)
return true; 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<unsigned int>(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<unsigned long long>(subchecksum1) << 32) | subchecksum2);
}
return checksum;
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
struct AST_state { struct AST_state {

View File

@ -120,6 +120,12 @@ public:
*/ */
std::string fileLine(const Token *tok) const; 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(); void createAst();
private: private: