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:
parent
4633979ff7
commit
75c9355e9a
14
lib/check.h
14
lib/check.h
|
@ -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 *)
|
||||||
{ }
|
{ }
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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("");
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue