Added some multipass checking for the uninitialized variables. It is still experimental. You can activate it with the '--test-2-pass' switch. Some more refactorings are needed to make it truly usable, the main thing is to make it thread safe.

This commit is contained in:
Daniel Marjamäki 2010-05-21 19:35:18 +02:00
parent 4633979ff7
commit 75c9355e9a
7 changed files with 89 additions and 19 deletions

View File

@ -64,6 +64,20 @@ public:
return _instances; return _instances;
} }
/**
* analyse code - must be thread safe
* @param tokens The tokens to analyse
* @param result container where results are stored
*/
virtual void analyse(const Token * /*tokens*/, std::set<std::string> & /*result*/) const
{
}
/** Save analysis data - the caller ensures thread safety */
virtual void saveAnalysisData(const std::set<std::string> & /*data*/) const
{
}
/** run checks, the token list is not simplified */ /** run checks, the token list is not simplified */
virtual void runChecks(const Tokenizer *, const Settings *, ErrorLogger *) virtual void runChecks(const Tokenizer *, const Settings *, ErrorLogger *)
{ } { }

View File

@ -3208,7 +3208,7 @@ public:
/** Functions that don't handle uninitialized variables well */ /** Functions that don't handle uninitialized variables well */
static std::set<std::string> uvarFunctions; static std::set<std::string> uvarFunctions;
static void analyseFunctions(const Token * const tokens, std::set<std::string> &func, bool showAll) static void analyseFunctions(const Token * const tokens, std::set<std::string> &func)
{ {
for (const Token *tok = tokens; tok; tok = tok->next()) for (const Token *tok = tokens; tok; tok = tok->next())
{ {
@ -3262,18 +3262,6 @@ public:
r = true; r = true;
} }
// --all
else if (showAll)
{
if (!Token::simpleMatch(tok3->next(), "="))
r = true;
else
{
w = true;
break;
}
}
else else
{ {
w = true; w = true;
@ -3315,12 +3303,15 @@ std::set<std::string> CheckUninitVar::uvarFunctions;
/// @} /// @}
void CheckOther::analyseFunctions(const Token * const tokens, std::set<std::string> &func, bool showAll) void CheckOther::analyse(const Token * const tokens, std::set<std::string> &func) const
{ {
CheckUninitVar::analyseFunctions(tokens, func, showAll); CheckUninitVar::analyseFunctions(tokens, func);
} }
void CheckOther::saveAnalysisData(const std::set<std::string> &data) const
{
CheckUninitVar::uvarFunctions.insert(data.begin(), data.end());
}
void CheckOther::executionPaths() void CheckOther::executionPaths()
{ {
@ -3334,7 +3325,7 @@ void CheckOther::executionPaths()
{ {
// no writing if multiple threads are used (TODO: thread safe analysis?) // no writing if multiple threads are used (TODO: thread safe analysis?)
if (_settings->_jobs == 1) if (_settings->_jobs == 1)
CheckUninitVar::analyseFunctions(_tokenizer->tokens(), CheckUninitVar::uvarFunctions, _settings->inconclusive); CheckUninitVar::analyseFunctions(_tokenizer->tokens(), CheckUninitVar::uvarFunctions);
CheckUninitVar c(this); CheckUninitVar c(this);
checkExecutionPaths(_tokenizer->tokens(), &c); checkExecutionPaths(_tokenizer->tokens(), &c);

View File

@ -91,7 +91,10 @@ public:
* @param func [out] names of functions that don't handle uninitialized variables well. the function names are added to the set. No clearing is made. * @param func [out] names of functions that don't handle uninitialized variables well. the function names are added to the set. No clearing is made.
* @param showAll [in] enable --all checking * @param showAll [in] enable --all checking
*/ */
static void analyseFunctions(const Token * const tokens, std::set<std::string> &func, bool showAll); void analyse(const Token * const tokens, std::set<std::string> &func) const;
/** Save analysis results */
void saveAnalysisData(const std::set<std::string> &data) const;
/** @brief Are there C-style pointer casts in a c++ file? */ /** @brief Are there C-style pointer casts in a c++ file? */
void warningOldStylePointerCast(); void warningOldStylePointerCast();

View File

@ -503,6 +503,13 @@ bool CppCheck::parseFromArgs(int argc, const char* const argv[])
return true; return true;
} }
// --test-2-pass Experimental 2-pass checking of files
// This command line flag will be removed
else if (strcmp(argv[i], "--test-2-pass") == 0)
{
_settings.test_2_pass = true;
}
else if (strncmp(argv[i], "-", 1) == 0 || strncmp(argv[i], "--", 2) == 0) else if (strncmp(argv[i], "-", 1) == 0 || strncmp(argv[i], "--", 2) == 0)
{ {
reportOut("cppcheck: error: unrecognized command line option \"" + std::string(argv[i]) + "\""); reportOut("cppcheck: error: unrecognized command line option \"" + std::string(argv[i]) + "\"");
@ -518,6 +525,12 @@ bool CppCheck::parseFromArgs(int argc, const char* const argv[])
reportOut("unusedFunctions check can't be used with -j option, so it was disabled."); reportOut("unusedFunctions check can't be used with -j option, so it was disabled.");
} }
// FIXME: Make the _settings.test_2_pass thread safe
if (_settings.test_2_pass && _settings._jobs > 1)
{
reportOut("--test-2-pass doesn't work with -j option yet.");
}
if (!pathnames.empty()) if (!pathnames.empty())
{ {
// Execute recursiveAddFiles() to each given file parameter // Execute recursiveAddFiles() to each given file parameter
@ -613,6 +626,50 @@ unsigned int CppCheck::check()
_checkUnusedFunctions.setErrorLogger(this); _checkUnusedFunctions.setErrorLogger(this);
std::sort(_filenames.begin(), _filenames.end()); std::sort(_filenames.begin(), _filenames.end());
// TODO: Should this be moved out to its own function so all the files can be
// analysed before any files are checked?
if (_settings.test_2_pass && _settings._jobs == 1)
{
for (unsigned int c = 0; c < _filenames.size(); c++)
{
const std::string fname = _filenames[c];
if (_settings.terminated())
break;
reportOut("Analysing " + fname + "..");
// Preprocess file..
Preprocessor preprocessor(&_settings, this);
std::list<std::string> configurations;
std::string filedata = "";
std::ifstream fin(fname.c_str());
preprocessor.preprocess(fin, filedata, configurations, fname, _settings._includePaths);
const std::string code = Preprocessor::getcode(filedata, "", fname, &_errorLogger);
// Tokenize..
Tokenizer tokenizer(&_settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, fname.c_str(), "");
tokenizer.simplifyTokenList();
// Analyse the tokens..
std::set<std::string> data;
for (std::list<Check *>::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it)
{
(*it)->analyse(tokenizer.tokens(), data);
}
// Save analysis results..
// TODO: This loop should be protected by a mutex or something like that
// The saveAnalysisData must _not_ be called from many threads at the same time.
for (std::list<Check *>::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it)
{
(*it)->saveAnalysisData(data);
}
}
}
for (unsigned int c = 0; c < _filenames.size(); c++) for (unsigned int c = 0; c < _filenames.size(); c++)
{ {
_errout.str(""); _errout.str("");

View File

@ -38,6 +38,7 @@ Settings::Settings()
_append = ""; _append = "";
_terminate = false; _terminate = false;
inconclusive = false; inconclusive = false;
test_2_pass = false;
} }
bool Settings::Suppressions::parseFile(std::istream &istr) bool Settings::Suppressions::parseFile(std::istream &istr)

View File

@ -166,6 +166,9 @@ public:
/** @brief defines given by the user */ /** @brief defines given by the user */
std::string userDefines; std::string userDefines;
/** @brief Experimentat 2 pass checking of files */
bool test_2_pass;
}; };
/// @} /// @}

View File

@ -1940,7 +1940,8 @@ private:
tokenizer.tokenize(istr, "test.cpp"); tokenizer.tokenize(istr, "test.cpp");
std::set<std::string> f; std::set<std::string> f;
CheckOther::analyseFunctions(tokenizer.tokens(), f, true); const CheckOther checkOther((const Tokenizer *)0, (const Settings *)0, (ErrorLogger *)0);
checkOther.analyse(tokenizer.tokens(), f);
std::string ret; std::string ret;
for (std::set<std::string>::const_iterator it = f.begin(); it != f.end(); ++it) for (std::set<std::string>::const_iterator it = f.begin(); it != f.end(); ++it)