This commit is contained in:
Sébastien Debrard 2011-02-16 20:18:11 +01:00
commit 3b9b8784c8
25 changed files with 607 additions and 226 deletions

View File

@ -3,6 +3,7 @@ The cppcheck team, in alphabetical order:
Bill Egert
Daniel Marjamäki
Gianluca Scacco
Greg Hewgill
Hoang Tuan Su
Kimmo Varis
Leandro Penz

View File

@ -1,7 +1,7 @@
# This file is generated by tools/dmake, do not edit.
ifndef CXXFLAGS
CXXFLAGS=-DHAVE_DEPENDENCIES -Wall -Wextra -Wshadow -pedantic -Wno-long-long -Wfloat-equal -Wcast-qual -Wsign-conversion -g
CXXFLAGS=-DHAVE_RULES -Wall -Wextra -Wshadow -pedantic -Wno-long-long -Wfloat-equal -Wcast-qual -Wsign-conversion -g
endif
ifndef CXX
@ -95,6 +95,7 @@ TESTOBJ = test/options.o \
test/testsimplifytokens.o \
test/teststl.o \
test/testsuite.o \
test/testsuppressions.o \
test/testsymboldatabase.o \
test/testthreadexecutor.o \
test/testtoken.o \
@ -329,6 +330,9 @@ test/teststl.o: test/teststl.cpp lib/tokenize.h lib/checkstl.h lib/check.h lib/t
test/testsuite.o: test/testsuite.cpp test/testsuite.h lib/errorlogger.h test/redirect.h test/options.h
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testsuite.o test/testsuite.cpp
test/testsuppressions.o: test/testsuppressions.cpp test/testsuite.h lib/cppcheck.h lib/settings.h lib/errorlogger.h cli/threadexecutor.h
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testsuppressions.o test/testsuppressions.cpp
test/testsymboldatabase.o: test/testsymboldatabase.cpp test/testsuite.h lib/errorlogger.h test/redirect.h test/testutils.h lib/settings.h lib/tokenize.h lib/token.h lib/symboldatabase.h
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testsymboldatabase.o test/testsymboldatabase.cpp

View File

@ -5,7 +5,7 @@ INCLUDEPATH += . ../lib
OBJECTS_DIR = temp
CONFIG += warn_on
CONFIG -= qt app_bundle
DEFINES += HAVE_DEPENDENCIES
DEFINES += HAVE_RULES
BASEPATH = ../externals/tinyxml/
include($$PWD/../externals/tinyxml/tinyxml.pri)

View File

@ -28,7 +28,7 @@
#include "path.h"
#include "filelister.h"
#ifdef HAVE_DEPENDENCIES
#ifdef HAVE_RULES
// xml is used in rules
#include <tinyxml.h>
#endif
@ -505,7 +505,7 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[])
_settings->_showtime = SHOWTIME_NONE;
}
#ifdef HAVE_DEPENDENCIES
#ifdef HAVE_RULES
// Rule given at command line
else if (strncmp(argv[i], "--rule=", 7) == 0)
{

View File

@ -123,7 +123,7 @@ bool CppCheckExecutor::parseFromArgs(CppCheck *cppcheck, int argc, const char* c
int CppCheckExecutor::check(int argc, const char* const argv[])
{
CppCheck cppCheck(*this);
CppCheck cppCheck(*this, true);
if (!parseFromArgs(&cppCheck, argc, argv))
{
return EXIT_FAILURE;
@ -152,11 +152,13 @@ int CppCheckExecutor::check(int argc, const char* const argv[])
{
// Multiple processes
const std::vector<std::string> &filenames = cppCheck.filenames();
Settings settings = cppCheck.settings();
Settings &settings = cppCheck.settings();
ThreadExecutor executor(filenames, settings, *this);
returnValue = executor.check();
}
reportUnmatchedSuppressions(cppCheck.settings().nomsg.getUnmatchedGlobalSuppressions());
if (_settings._xml)
{
reportErr(ErrorLogger::ErrorMessage::getXMLFooter(_settings._xml_version));

View File

@ -31,7 +31,7 @@
#include <time.h>
#endif
ThreadExecutor::ThreadExecutor(const std::vector<std::string> &filenames, const Settings &settings, ErrorLogger &errorLogger)
ThreadExecutor::ThreadExecutor(const std::vector<std::string> &filenames, Settings &settings, ErrorLogger &errorLogger)
: _filenames(filenames), _settings(settings), _errorLogger(errorLogger), _fileCount(0)
{
#if (defined(__GNUC__) || defined(__sun)) && !defined(__MINGW32__)
@ -95,12 +95,23 @@ int ThreadExecutor::handleRead(unsigned int &result)
ErrorLogger::ErrorMessage msg;
msg.deserialize(buf);
// Alert only about unique errors
std::string errmsg = msg.toString(_settings._verbose);
if (std::find(_errorList.begin(), _errorList.end(), errmsg) == _errorList.end())
std::string file;
unsigned int line(0);
if (!msg._callStack.empty())
{
_errorList.push_back(errmsg);
_errorLogger.reportErr(msg);
file = msg._callStack.back().getfile(false);
line = msg._callStack.back().line;
}
if (!_settings.nomsg.isSuppressed(msg._id, file, line))
{
// Alert only about unique errors
std::string errmsg = msg.toString(_settings._verbose);
if (std::find(_errorList.begin(), _errorList.end(), errmsg) == _errorList.end())
{
_errorList.push_back(errmsg);
_errorLogger.reportErr(msg);
}
}
}
else if (type == '3')
@ -158,7 +169,7 @@ unsigned int ThreadExecutor::check()
}
else if (pid == 0)
{
CppCheck fileChecker(*this);
CppCheck fileChecker(*this, false);
fileChecker.settings(_settings);
if (_fileContents.size() > 0 && _fileContents.find(_filenames[i]) != _fileContents.end())

View File

@ -35,7 +35,7 @@
class ThreadExecutor : public ErrorLogger
{
public:
ThreadExecutor(const std::vector<std::string> &filenames, const Settings &settings, ErrorLogger &_errorLogger);
ThreadExecutor(const std::vector<std::string> &filenames, Settings &settings, ErrorLogger &_errorLogger);
virtual ~ThreadExecutor();
unsigned int check();
virtual void reportOut(const std::string &outmsg);
@ -52,7 +52,7 @@ public:
private:
const std::vector<std::string> &_filenames;
const Settings &_settings;
Settings &_settings;
ErrorLogger &_errorLogger;
unsigned int _fileCount;

View File

@ -30,15 +30,15 @@
#include <ctime>
#include "timer.h"
#ifdef HAVE_DEPENDENCIES
#ifdef HAVE_RULES
#define PCRE_STATIC
#include <pcre.h>
#endif
static TimerResults S_timerResults;
CppCheck::CppCheck(ErrorLogger &errorLogger)
: _errorLogger(errorLogger)
CppCheck::CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions)
: _useGlobalSuppressions(useGlobalSuppressions), _errorLogger(errorLogger)
{
exitcode = 0;
}
@ -197,6 +197,8 @@ unsigned int CppCheck::check()
_errorLogger.reportOut("Bailing out from checking " + fixedpath + ": " + e.what());
}
reportUnmatchedSuppressions(_settings.nomsg.getUnmatchedLocalSuppressions());
_errorLogger.reportStatus(c + 1, (unsigned int)_filenames.size());
}
@ -309,7 +311,7 @@ void CppCheck::checkFile(const std::string &code, const char FileName[])
(*it)->runSimplifiedChecks(&_tokenizer, &_settings, this);
}
#ifdef HAVE_DEPENDENCIES
#ifdef HAVE_RULES
// Are there extra rules?
if (!_settings.rules.empty())
{
@ -385,7 +387,7 @@ void CppCheck::checkFile(const std::string &code, const char FileName[])
#endif
}
Settings CppCheck::settings() const
Settings &CppCheck::settings()
{
return _settings;
}
@ -408,8 +410,16 @@ void CppCheck::reportErr(const ErrorLogger::ErrorMessage &msg)
line = msg._callStack.back().line;
}
if (_settings.nomsg.isSuppressed(msg._id, file, line))
return;
if (_useGlobalSuppressions)
{
if (_settings.nomsg.isSuppressed(msg._id, file, line))
return;
}
else
{
if (_settings.nomsg.isSuppressedLocal(msg._id, file, line))
return;
}
if (!_settings.nofail.isSuppressed(msg._id, file, line))
exitcode = 1;

View File

@ -43,7 +43,7 @@ public:
/**
* @brief Constructor.
*/
CppCheck(ErrorLogger &errorLogger);
CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions);
/**
* @brief Destructor.
@ -66,10 +66,10 @@ public:
void settings(const Settings &settings);
/**
* @brief Get copy of current settings.
* @return a copy of current settings
* @brief Get reference to current settings.
* @return a reference to current settings
*/
Settings settings() const;
Settings &settings();
/**
* @brief Add new file to be checked.
@ -147,6 +147,7 @@ private:
std::list<std::string> _errorList;
std::ostringstream _errout;
Settings _settings;
bool _useGlobalSuppressions;
std::vector<std::string> _filenames;
void reportProgress(const std::string &filename, const char stage[], const unsigned int value);

View File

@ -291,11 +291,26 @@ std::string ErrorLogger::ErrorMessage::toString(bool verbose, const std::string
}
}
void ErrorLogger::reportUnmatchedSuppressions(const std::list<Settings::Suppressions::SuppressionEntry> &unmatched)
{
for (std::list<Settings::Suppressions::SuppressionEntry>::const_iterator i = unmatched.begin(); i != unmatched.end(); ++i)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> callStack;
callStack.push_back(ErrorLogger::ErrorMessage::FileLocation(i->file, i->line));
reportErr(ErrorLogger::ErrorMessage(callStack, Severity::information, "Unmatched suppression: " + i->id, "unmatchedSuppression"));
}
}
std::string ErrorLogger::callStackToString(const std::list<ErrorLogger::ErrorMessage::FileLocation> &callStack)
{
std::ostringstream ostr;
for (std::list<ErrorLogger::ErrorMessage::FileLocation>::const_iterator tok = callStack.begin(); tok != callStack.end(); ++tok)
ostr << (tok == callStack.begin() ? "" : " -> ") << "[" << (*tok).getfile() << ":" << (*tok).line << "]";
{
ostr << (tok == callStack.begin() ? "" : " -> ") << "[" << (*tok).getfile();
if ((*tok).line != 0)
ostr << ":" << (*tok).line;
ostr << "]";
}
return ostr.str();
}

View File

@ -23,6 +23,8 @@
#include <list>
#include <string>
#include "settings.h"
class Token;
class Tokenizer;
@ -108,6 +110,11 @@ public:
line = 0;
}
FileLocation(const std::string &file, int aline)
: line(aline), _file(file)
{
}
/**
* Return the filename.
* @param convert If true convert path to native separators.
@ -230,6 +237,12 @@ public:
(void)value;
}
/**
* Report list of unmatched suppressions
* @param unmatched list of unmatched suppressions (from Settings::Suppressions::getUnmatched(Local|Global)Suppressions)
*/
void reportUnmatchedSuppressions(const std::list<Settings::Suppressions::SuppressionEntry> &unmatched);
static std::string callStackToString(const std::list<ErrorLogger::ErrorMessage::FileLocation> &callStack);
};

View File

@ -218,7 +218,7 @@ std::string Preprocessor::preprocessCleanupDirectives(const std::string &process
char prev = ' '; // hack to make it skip spaces between # and the directive
code << "#";
std::string::const_iterator i = line.begin();
i++;
++i;
// need space.. #if( => #if (
bool needSpace = true;
@ -247,7 +247,7 @@ std::string Preprocessor::preprocessCleanupDirectives(const std::string &process
// skip double whitespace between arguments
if (escapeStatus == ESC_NONE && prev == ' ' && *i == ' ')
{
i++;
++i;
continue;
}
// Convert #if( to "#if ("
@ -272,7 +272,7 @@ std::string Preprocessor::preprocessCleanupDirectives(const std::string &process
{
prev = *i;
}
i++;
++i;
}
if (escapeStatus != ESC_NONE)
{

View File

@ -70,46 +70,7 @@ std::string Settings::Suppressions::parseFile(std::istream &istr)
if (line.length() >= 2 && line[0] == '/' && line[1] == '/')
continue;
std::istringstream lineStream(line);
std::string id;
std::string file;
unsigned int lineNumber = 0;
if (std::getline(lineStream, id, ':'))
{
if (std::getline(lineStream, file))
{
// If there is not a dot after the last colon in "file" then
// the colon is a separator and the contents after the colon
// is a line number..
// Get position of last colon
const std::string::size_type pos = file.rfind(":");
// if a colon is found and there is no dot after it..
if (pos != std::string::npos &&
file.find(".", pos) == std::string::npos)
{
// Try to parse out the line number
try
{
std::istringstream istr1(file.substr(pos+1));
istr1 >> lineNumber;
}
catch (...)
{
lineNumber = 0;
}
if (lineNumber > 0)
{
file.erase(pos);
}
}
}
}
// We could perhaps check if the id is valid and return error if it is not
const std::string errmsg(addSuppression(id, file, lineNumber));
const std::string errmsg(addSuppressionLine(line));
if (!errmsg.empty())
return errmsg;
}
@ -117,6 +78,54 @@ std::string Settings::Suppressions::parseFile(std::istream &istr)
return "";
}
std::string Settings::Suppressions::addSuppressionLine(const std::string &line)
{
std::istringstream lineStream(line);
std::string id;
std::string file;
unsigned int lineNumber = 0;
if (std::getline(lineStream, id, ':'))
{
if (std::getline(lineStream, file))
{
// If there is not a dot after the last colon in "file" then
// the colon is a separator and the contents after the colon
// is a line number..
// Get position of last colon
const std::string::size_type pos = file.rfind(":");
// if a colon is found and there is no dot after it..
if (pos != std::string::npos &&
file.find(".", pos) == std::string::npos)
{
// Try to parse out the line number
try
{
std::istringstream istr1(file.substr(pos+1));
istr1 >> lineNumber;
}
catch (...)
{
lineNumber = 0;
}
if (lineNumber > 0)
{
file.erase(pos);
}
}
}
}
// We could perhaps check if the id is valid and return error if it is not
const std::string errmsg(addSuppression(id, file, lineNumber));
if (!errmsg.empty())
return errmsg;
return "";
}
bool Settings::Suppressions::FileMatcher::match(const std::string &pattern, const std::string &name)
{
const char *p = pattern.c_str();
@ -205,47 +214,64 @@ std::string Settings::Suppressions::FileMatcher::addFile(const std::string &name
}
}
}
_globs[name].insert(line);
_globs[name][line] = false;
}
else if (name.empty())
{
_globs["*"][0U] = false;
}
else
{
_files[name].insert(line);
_files[name][line] = false;
}
return "";
}
bool Settings::Suppressions::FileMatcher::isSuppressed(const std::string &file, unsigned int line)
{
// Check are all errors of this type filtered out
if (_files.find("") != _files.end())
if (isSuppressedLocal(file, line))
return true;
std::set<unsigned int> lineset;
std::map<std::string, std::set<unsigned int> >::const_iterator f = _files.find(file);
if (f != _files.end())
{
lineset.insert(f->second.begin(), f->second.end());
}
for (std::map<std::string, std::set<unsigned int> >::iterator g = _globs.begin(); g != _globs.end(); ++g)
for (std::map<std::string, std::map<unsigned int, bool> >::iterator g = _globs.begin(); g != _globs.end(); ++g)
{
if (match(g->first, file))
{
lineset.insert(g->second.begin(), g->second.end());
if (g->second.find(0U) != g->second.end())
{
g->second[0U] = true;
return true;
}
std::map<unsigned int, bool>::iterator l = g->second.find(line);
if (l != g->second.end())
{
l->second = true;
return true;
}
}
}
if (lineset.empty())
return false;
return false;
}
// Check should all errors in this file be filtered out
if (lineset.find(0U) != lineset.end())
return true;
bool Settings::Suppressions::FileMatcher::isSuppressedLocal(const std::string &file, unsigned int line)
{
std::map<std::string, std::map<unsigned int, bool> >::iterator f = _files.find(file);
if (f != _files.end())
{
if (f->second.find(0U) != f->second.end())
{
f->second[0U] = true;
return true;
}
std::map<unsigned int, bool>::iterator l = f->second.find(line);
if (l != f->second.end())
{
l->second = true;
return true;
}
}
if (lineset.find(line) == lineset.end())
return false;
return true;
return false;
}
std::string Settings::Suppressions::addSuppression(const std::string &errorId, const std::string &file, unsigned int line)
@ -278,6 +304,52 @@ bool Settings::Suppressions::isSuppressed(const std::string &errorId, const std:
return _suppressions[errorId].isSuppressed(file, line);
}
bool Settings::Suppressions::isSuppressedLocal(const std::string &errorId, const std::string &file, unsigned int line)
{
if (_suppressions.find(errorId) == _suppressions.end())
return false;
return _suppressions[errorId].isSuppressedLocal(file, line);
}
std::list<Settings::Suppressions::SuppressionEntry> Settings::Suppressions::getUnmatchedLocalSuppressions() const
{
std::list<SuppressionEntry> r;
for (std::map<std::string, FileMatcher>::const_iterator i = _suppressions.begin(); i != _suppressions.end(); ++i)
{
for (std::map<std::string, std::map<unsigned int, bool> >::const_iterator f = i->second._files.begin(); f != i->second._files.end(); ++f)
{
for (std::map<unsigned int, bool>::const_iterator l = f->second.begin(); l != f->second.end(); ++l)
{
if (!l->second)
{
r.push_back(SuppressionEntry(i->first, f->first, l->first));
}
}
}
}
return r;
}
std::list<Settings::Suppressions::SuppressionEntry> Settings::Suppressions::getUnmatchedGlobalSuppressions() const
{
std::list<SuppressionEntry> r;
for (std::map<std::string, FileMatcher>::const_iterator i = _suppressions.begin(); i != _suppressions.end(); ++i)
{
for (std::map<std::string, std::map<unsigned int, bool> >::const_iterator g = i->second._globs.begin(); g != i->second._globs.end(); ++g)
{
for (std::map<unsigned int, bool>::const_iterator l = g->second.begin(); l != g->second.end(); ++l)
{
if (!l->second)
{
r.push_back(SuppressionEntry(i->first, g->first, l->first));
}
}
}
}
return r;
}
std::string Settings::addEnabled(const std::string &str)
{
// Enable parameters may be comma separated...

View File

@ -138,11 +138,12 @@ public:
private:
class FileMatcher
{
friend class Suppressions;
private:
/** @brief List of filenames suppressed. */
std::map<std::string, std::set<unsigned int> > _files;
/** @brief List of globs suppressed. */
std::map<std::string, std::set<unsigned int> > _globs;
/** @brief List of filenames suppressed, bool flag indicates whether suppression matched. */
std::map<std::string, std::map<unsigned int, bool> > _files;
/** @brief List of globs suppressed, bool flag indicates whether suppression matched. */
std::map<std::string, std::map<unsigned int, bool> > _globs;
/**
* @brief Match a name against a glob pattern.
@ -168,6 +169,14 @@ public:
* @return true if this filename/line matches
*/
bool isSuppressed(const std::string &file, unsigned int line);
/**
* @brief Returns true if the file name matches a previously added file (only, not glob pattern).
* @param name File name to check
* @param line Line number
* @return true if this filename/line matches
*/
bool isSuppressedLocal(const std::string &file, unsigned int line);
};
/** @brief List of error which the user doesn't want to see. */
@ -180,6 +189,13 @@ public:
*/
std::string parseFile(std::istream &istr);
/**
* @brief Don't show the given error.
* @param str Description of error to suppress (in id:file:line format).
* @return error message. empty upon success
*/
std::string addSuppressionLine(const std::string &line);
/**
* @brief Don't show this error. If file and/or line are optional. In which case
* the errorId alone is used for filtering.
@ -198,6 +214,38 @@ public:
* @return true if this error is suppressed.
*/
bool isSuppressed(const std::string &errorId, const std::string &file, unsigned int line);
/**
* @brief Returns true if this message should not be shown to the user (explicit files only, not glob patterns).
* @param errorId the id for the error, e.g. "arrayIndexOutOfBounds"
* @param file File name with the path, e.g. "src/main.cpp"
* @param line number, e.g. "123"
* @return true if this error is suppressed.
*/
bool isSuppressedLocal(const std::string &errorId, const std::string &file, unsigned int line);
struct SuppressionEntry
{
SuppressionEntry(const std::string &aid, const std::string &afile, const unsigned int &aline)
: id(aid), file(afile), line(aline)
{ }
std::string id;
std::string file;
unsigned int line;
};
/**
* @brief Returns list of unmatched local (per-file) suppressions.
* @return list of unmatched suppressions
*/
std::list<SuppressionEntry> getUnmatchedLocalSuppressions() const;
/**
* @brief Returns list of unmatched global (glob pattern) suppressions.
* @return list of unmatched suppressions
*/
std::list<SuppressionEntry> getUnmatchedGlobalSuppressions() const;
};
/** @brief suppress message (--suppressions) */

View File

@ -906,12 +906,14 @@ const Token *SymbolDatabase::initBaseInfo(Scope *scope, const Token *tok)
base.name += tok2->str();
base.scope = 0;
// don't add unhandled templates
// add unhandled templates
if (tok2->next()->str() == "<")
{
int level1 = 1;
while (tok2->next())
{
base.name += tok2->next()->str();
if (tok2->next()->str() == ">")
{
level1--;
@ -926,10 +928,7 @@ const Token *SymbolDatabase::initBaseInfo(Scope *scope, const Token *tok)
}
// save pattern for base class name
else
{
scope->derivedFrom.push_back(base);
}
scope->derivedFrom.push_back(base);
}
tok2 = tok2->next();
}

View File

@ -869,6 +869,8 @@ static Token *processFunc(Token *tok2, bool inOperator)
tok2 = tok2->tokAt(5)->link();
else if (Token::Match(tok2->next(), "* ( * %type% ) ("))
tok2 = tok2->tokAt(6)->link();
else if (Token::Match(tok2->next(), "* ( * %type% ) ;"))
tok2 = tok2->tokAt(5);
else if (Token::Match(tok2->next(), "* ( %type% [") &&
Token::Match(tok2->tokAt(4)->link(), "] ) ;|="))
tok2 = tok2->tokAt(4)->link()->next();
@ -6357,7 +6359,7 @@ bool Tokenizer::simplifyKnownVariablesGetData(unsigned int varid, Token **_tok2,
bool Tokenizer::simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, unsigned int varid, const std::string &structname, std::string &value, unsigned int valueVarId, bool valueIsPointer, bool pointeralias, int indentlevel)
{
bool ret = false;;
bool ret = false;
Token* bailOutFromLoop = 0;
int indentlevel3 = indentlevel;
@ -6527,7 +6529,7 @@ bool Tokenizer::simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, unsign
// Using the variable in condition..
if (Token::Match(tok3->previous(), ("if ( " + structname + " %varid% ==|!=|<|<=|>|>=|)").c_str(), varid) ||
Token::Match(tok3, ("( " + structname + " %varid% ==|!=|<|<=|>|>=").c_str(), varid) ||
Token::Match(tok3, ("!|==|!=|<|<=|>|>= " + structname + " %varid% ==|!=|<|<=|>|>=|)").c_str(), varid) ||
Token::Match(tok3, ("!|==|!=|<|<=|>|>= " + structname + " %varid% ==|!=|<|<=|>|>=|)|;").c_str(), varid) ||
Token::Match(tok3->previous(), "strlen|free ( %varid% )", varid))
{
if (!structname.empty())
@ -6618,10 +6620,10 @@ bool Tokenizer::simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, unsign
// Variable is used in calculation..
if (((tok3->previous()->varId() > 0) && Token::Match(tok3, ("& " + structname + " %varid%").c_str(), varid)) ||
Token::Match(tok3, ("[=+-*/[] " + structname + " %varid% [=?+-*/;])]").c_str(), varid) ||
Token::Match(tok3, ("[(=+-*/[] " + structname + " %varid% <<|>>").c_str(), varid) ||
Token::Match(tok3, ("<<|>> " + structname + " %varid% [+-*/;])]").c_str(), varid) ||
Token::Match(tok3->previous(), ("[=+-*/[] ( " + structname + " %varid%").c_str(), varid))
Token::Match(tok3, ("[=+-*/%^|[] " + structname + " %varid% [=?+-*/%^|;])]").c_str(), varid) ||
Token::Match(tok3, ("[(=+-*/%^|[] " + structname + " %varid% <<|>>").c_str(), varid) ||
Token::Match(tok3, ("<<|>> " + structname + " %varid% [+-*/%^|;])]").c_str(), varid) ||
Token::Match(tok3->previous(), ("[=+-*/%^|[] ( " + structname + " %varid%").c_str(), varid))
{
if (!structname.empty())
{
@ -7069,10 +7071,11 @@ bool Tokenizer::simplifyCalculations()
{
// (1-2)
while (Token::Match(tok, "[[,(=<>+-*] %num% [+-*/] %num% [],);=<>+-*/]") ||
Token::Match(tok, "<< %num% [+-*/] %num% [],);=<>+-*/]") ||
Token::Match(tok, "[[,(=<>+-*] %num% [+-*/] %num% <<|>>") ||
Token::Match(tok, "<< %num% [+-*/] %num% <<"))
while (Token::Match(tok, "[[,(=<>+-*|&^] %num% [+-*/] %num% [],);=<>+-*/|&^]") ||
Token::Match(tok, "<< %num% [+-*/] %num% [],);=<>+-*/|&^]") ||
Token::Match(tok, "[[,(=<>+-*|&^] %num% [+-*/] %num% <<|>>") ||
Token::Match(tok, "<< %num% [+-*/] %num% <<") ||
Token::Match(tok, "[(,[] %num% [|&^] %num% [];,);]"))
{
tok = tok->next();
@ -7080,6 +7083,29 @@ bool Tokenizer::simplifyCalculations()
if (Token::simpleMatch(tok->next(), "/ 0"))
continue;
// & | ^
if (Token::Match(tok->next(), "[&|^]"))
{
std::string result;
const std::string first(tok->str());
const std::string second(tok->strAt(2));
const char op = tok->next()->str()[0];
if (op == '&')
result = MathLib::toString<MathLib::bigint>(MathLib::toLongNumber(first) & MathLib::toLongNumber(second));
else if (op == '|')
result = MathLib::toString<MathLib::bigint>(MathLib::toLongNumber(first) | MathLib::toLongNumber(second));
else if (op == '^')
result = MathLib::toString<MathLib::bigint>(MathLib::toLongNumber(first) ^ MathLib::toLongNumber(second));
if (!result.empty())
{
ret = true;
tok->str(result);
Token::eraseTokens(tok, tok->tokAt(3));
continue;
}
}
// + and - are calculated after * and /
if (Token::Match(tok->next(), "[+-/]"))
{

View File

@ -18,8 +18,8 @@ Compiling
To build the GUI, you need Qt.
To build the command line tool, PCRE is needed. More information about PCRE is found in
build.txt
To build the command line tool, no dependencies are required. However for
the handling of rules, PCRE is needed.
There are multiple compilation choices:
* qmake - cross platform build tool
@ -48,7 +48,7 @@ Compiling
g++ -o cppcheck -Ilib cli/*.cpp lib/*.cpp
If you want to use --rule and --rule-file then dependencies are needed:
g++ -o cppcheck -lpcre -DHAVE_DEPENDENCIES -Ilib -Iexternals cli/*.cpp lib/*.cpp externals/tinyxml/*.cpp
g++ -o cppcheck -lpcre -DHAVE_RULES -Ilib -Iexternals cli/*.cpp lib/*.cpp externals/tinyxml/*.cpp
mingw
=====
make LDFLAGS=-lshlwapi

View File

@ -868,7 +868,7 @@ private:
" a[256] = 0;\n" // 256 > CHAR_MAX
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[256]' index 256 out of bounds\n"
"[test.cpp:3]: (error) Array 'a[256]' index -1 out of bounds\n", errout.str());
"[test.cpp:3]: (error) Array index -1 out of bounds\n", errout.str());
}
check("void f(signed char n) {\n"
@ -1875,6 +1875,20 @@ private:
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("class A {\n"
" void foo();\n"
" bool b[7];\n"
"};\n"
"\n"
"void A::foo() {\n"
" for (int i=0; i<7; i++) {\n"
" b[i] = b[i+1];\n"
" }\n"
"}\n");
TODO_ASSERT_EQUALS("error", // wanted result
"", // current result
errout.str());
}
void buffer_overrun_bailoutIfSwitch()

View File

@ -86,7 +86,7 @@ private:
void getErrorMessages()
{
ErrorLogger2 errorLogger;
CppCheck cppCheck(errorLogger);
CppCheck cppCheck(errorLogger, true);
cppCheck.getErrorMessages();
ASSERT(!errorLogger.id.empty());

View File

@ -236,6 +236,7 @@ private:
TEST_CASE(simplifyTypedef76); // ticket #2453
TEST_CASE(simplifyTypedef77); // ticket #2554
TEST_CASE(simplifyTypedef78); // ticket #2568
TEST_CASE(simplifyTypedef79); // ticket #2348
TEST_CASE(simplifyTypedefFunction1);
TEST_CASE(simplifyTypedefFunction2); // ticket #1685
@ -2590,6 +2591,8 @@ private:
ASSERT_EQUALS("a [ 0 ]", tok(code));
}
ASSERT_EQUALS("a [ 4 ] ;", tok("a[1+3|4];"));
ASSERT_EQUALS("x = 1 + 2 * y ;", tok("x=1+2*y;"));
ASSERT_EQUALS("x = 7 ;", tok("x=1+2*3;"));
ASSERT_EQUALS("x = 47185 ;", tok("x=(65536*72/100);"));
@ -4858,6 +4861,21 @@ private:
ASSERT_EQUALS(expected, sizeof_(code));
}
void simplifyTypedef79() // ticket #2348
{
const char code[] = "typedef int (Tcl_ObjCmdProc) (int x);\n"
"typedef struct LangVtab\n"
"{\n"
" Tcl_ObjCmdProc * (*V_LangOptionCommand);\n"
"} LangVtab;\n";
const std::string expected = "; "
"struct LangVtab "
"{ "
"int ( * ( * V_LangOptionCommand ) ) ( int x ) ; "
"} ;";
ASSERT_EQUALS(expected, sizeof_(code));
}
void simplifyTypedefFunction1()
{
{

172
test/testsuppressions.cpp Normal file
View File

@ -0,0 +1,172 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "cppcheck.h"
#include "settings.h"
#include "testsuite.h"
#include "threadexecutor.h"
#include <sstream>
extern std::ostringstream errout;
class TestSuppressions : public TestFixture
{
public:
TestSuppressions() : TestFixture("TestSuppressions")
{ }
private:
void run()
{
TEST_CASE(suppressionsSettings);
}
// Check the suppression
void checkSuppression(const char code[], const std::string &suppression = "")
{
// Clear the error log
errout.str("");
Settings settings;
settings._inlineSuppressions = true;
if (!suppression.empty())
settings.nomsg.addSuppressionLine(suppression);
CppCheck cppCheck(*this, true);
cppCheck.settings(settings);
cppCheck.addFile("test.cpp", code);
cppCheck.check();
reportUnmatchedSuppressions(cppCheck.settings().nomsg.getUnmatchedGlobalSuppressions());
}
void checkSuppressionThreads(const char code[], const std::string &suppression = "")
{
errout.str("");
output.str("");
if (!ThreadExecutor::isEnabled())
{
// Skip this check on systems which don't use this feature
return;
}
std::vector<std::string> filenames;
filenames.push_back("test.cpp");
Settings settings;
settings._jobs = 1;
settings._inlineSuppressions = true;
if (!suppression.empty())
settings.nomsg.addSuppressionLine(suppression);
ThreadExecutor executor(filenames, settings, *this);
for (unsigned int i = 0; i < filenames.size(); ++i)
executor.addFileContent(filenames[i], code);
executor.check();
reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions());
}
void runChecks(void (TestSuppressions::*check)(const char[], const std::string &))
{
// check to make sure the appropriate error is present
(this->*check)("void f() {\n"
" int a;\n"
" a++;\n"
"}\n",
"");
ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str());
// suppress uninitvar globally
(this->*check)("void f() {\n"
" int a;\n"
" a++;\n"
"}\n",
"uninitvar");
ASSERT_EQUALS("", errout.str());
// suppress uninitvar globally, without error present
(this->*check)("void f() {\n"
" int a;\n"
" b++;\n"
"}\n",
"uninitvar");
ASSERT_EQUALS("[*]: (information) Unmatched suppression: uninitvar\n", errout.str());
// suppress uninitvar for this file only
(this->*check)("void f() {\n"
" int a;\n"
" a++;\n"
"}\n",
"uninitvar:test.cpp");
ASSERT_EQUALS("", errout.str());
// suppress uninitvar for this file only, without error present
(this->*check)("void f() {\n"
" int a;\n"
" b++;\n"
"}\n",
"uninitvar:test.cpp");
ASSERT_EQUALS("[test.cpp]: (information) Unmatched suppression: uninitvar\n", errout.str());
// suppress uninitvar for this file and line
(this->*check)("void f() {\n"
" int a;\n"
" a++;\n"
"}\n",
"uninitvar:test.cpp:3");
ASSERT_EQUALS("", errout.str());
// suppress uninitvar for this file and line, without error present
(this->*check)("void f() {\n"
" int a;\n"
" b++;\n"
"}\n",
"uninitvar:test.cpp:3");
ASSERT_EQUALS("[test.cpp:3]: (information) Unmatched suppression: uninitvar\n", errout.str());
// suppress uninitvar inline
(this->*check)("void f() {\n"
" int a;\n"
" // cppcheck-suppress uninitvar\n"
" a++;\n"
"}\n",
"");
ASSERT_EQUALS("", errout.str());
// suppress uninitvar inline, without error present
(this->*check)("void f() {\n"
" int a;\n"
" // cppcheck-suppress uninitvar\n"
" b++;\n"
"}\n",
"");
ASSERT_EQUALS("[test.cpp:4]: (information) Unmatched suppression: uninitvar\n", errout.str());
}
void suppressionsSettings()
{
runChecks(&TestSuppressions::checkSuppression);
runChecks(&TestSuppressions::checkSuppressionThreads);
}
};
REGISTER_TEST(TestSuppressions)

View File

@ -1637,21 +1637,14 @@ private:
" int i = v;\n"
" return h | i;\n"
"}\n";
const char wanted[] = "\n\n##file 0\n"
"1: int foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4:\n"
"5: return u@1 | v@2 ;\n"
"6: }\n";
const char current[] = "\n\n##file 0\n"
"1: int foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4: int i@4 ; i@4 = v@2 ;\n"
"5: return u@1 | i@4 ;\n"
"6: }\n";
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
const char expected[] = "\n\n##file 0\n"
"1: int foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4:\n"
"5: return u@1 | v@2 ;\n"
"6: }\n";
ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
}
{
@ -1661,21 +1654,14 @@ private:
" int i = v;\n"
" return h ^ i;\n"
"}\n";
const char wanted[] = "\n\n##file 0\n"
"1: int foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4:\n"
"5: return u@1 ^ v@2 ;\n"
"6: }\n";
const char current[] = "\n\n##file 0\n"
"1: int foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4: int i@4 ; i@4 = v@2 ;\n"
"5: return u@1 ^ i@4 ;\n"
"6: }\n";
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
const char expected[] = "\n\n##file 0\n"
"1: int foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4:\n"
"5: return u@1 ^ v@2 ;\n"
"6: }\n";
ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
}
{
@ -1685,21 +1671,14 @@ private:
" int i = v;\n"
" return h % i;\n"
"}\n";
const char wanted[] = "\n\n##file 0\n"
"1: int foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4:\n"
"5: return u@1 % v@2 ;\n"
"6: }\n";
const char current[] = "\n\n##file 0\n"
"1: int foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4: int i@4 ; i@4 = v@2 ;\n"
"5: return u@1 % i@4 ;\n"
"6: }\n";
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
const char expected[] = "\n\n##file 0\n"
"1: int foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4:\n"
"5: return u@1 % v@2 ;\n"
"6: }\n";
ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
}
{
@ -1743,22 +1722,14 @@ private:
" int i = v;\n"
" return h == i;\n"
"}\n";
const char wanted[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4: ;\n"
"5: return u@1 == v@2 ;\n"
"6: }\n";
const char current[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4: int i@4 ; i@4 = v@2 ;\n"
"5: return u@1 == i@4 ;\n"
"6: }\n";
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
const char expected[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4:\n"
"5: return u@1 == v@2 ;\n"
"6: }\n";
ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
}
{
@ -1768,21 +1739,14 @@ private:
" int i = v;\n"
" return h != i;\n"
"}\n";
const char wanted[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4: ;\n"
"5: return u@1 != v@2 ;\n"
"6: }\n";
const char current[] = "\n\n##file 0\n"
const char expected[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4: int i@4 ; i@4 = v@2 ;\n"
"5: return u@1 != i@4 ;\n"
"4:\n"
"5: return u@1 != v@2 ;\n"
"6: }\n";
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
}
{
@ -1792,15 +1756,14 @@ private:
" int i = v;\n"
" return h > i;\n"
"}\n";
const char wanted[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4: ;\n"
"5: return u@1 > v@2 ;\n"
"6: }\n";
const char current[] = "\n\n##file 0\n1: bool foo ( int u@1 , int v@2 )\n2: {\n3: ;\n4: int i@4 ; i@4 = v@2 ;\n5: return u@1 > i@4 ;\n6: }\n";
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
const char expected[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4:\n"
"5: return u@1 > v@2 ;\n"
"6: }\n";
ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
}
{
@ -1810,15 +1773,14 @@ private:
" int i = v;\n"
" return h >= i;\n"
"}\n";
const char wanted[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4: ;\n"
"5: return u@1 >= v@2 ;\n"
"6: }\n";
const char current[] = "\n\n##file 0\n1: bool foo ( int u@1 , int v@2 )\n2: {\n3: ;\n4: int i@4 ; i@4 = v@2 ;\n5: return u@1 >= i@4 ;\n6: }\n";
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
const char expected[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4:\n"
"5: return u@1 >= v@2 ;\n"
"6: }\n";
ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
}
{
@ -1828,15 +1790,14 @@ private:
" int i = v;\n"
" return h < i;\n"
"}\n";
const char wanted[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4: ;\n"
"5: return u@1 < v@2 ;\n"
"6: }\n";
const char current[] = "\n\n##file 0\n1: bool foo ( int u@1 , int v@2 )\n2: {\n3: ;\n4: int i@4 ; i@4 = v@2 ;\n5: return u@1 < i@4 ;\n6: }\n";
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
const char expected[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4:\n"
"5: return u@1 < v@2 ;\n"
"6: }\n";
ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
}
{
@ -1846,15 +1807,14 @@ private:
" int i = v;\n"
" return h <= i;\n"
"}\n";
const char wanted[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4: ;\n"
"5: return u@1 <= v@2 ;\n"
"6: }\n";
const char current[] = "\n\n##file 0\n1: bool foo ( int u@1 , int v@2 )\n2: {\n3: ;\n4: int i@4 ; i@4 = v@2 ;\n5: return u@1 <= i@4 ;\n6: }\n";
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
const char expected[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n"
"3: ;\n"
"4:\n"
"5: return u@1 <= v@2 ;\n"
"6: }\n";
ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
}
{

View File

@ -67,6 +67,7 @@ private:
TEST_CASE(testDoesNotIdentifyMethodAsLastFunctionArgument);
TEST_CASE(multiFile);
TEST_CASE(unknownBaseTemplate); // ticket #2580
}
@ -571,6 +572,20 @@ private:
ASSERT_EQUALS("", errout.str());
}
void unknownBaseTemplate() // ticket #2580
{
check("class Bla : public Base2<Base> {\n"
"public:\n"
" Bla() {}\n"
"private:\n"
" virtual void F() const;\n"
"};\n"
"void Bla::F() const { }");
ASSERT_EQUALS("", errout.str());
}
};
REGISTER_TEST(TestUnusedPrivateFunction)

View File

@ -225,7 +225,7 @@ int main(int argc, char **argv)
// Makefile settings..
if (release)
{
makeConditionalVariable(fout, "CXXFLAGS", "-O2 -DNDEBUG -DHAVE_DEPENDENCIES -Wall");
makeConditionalVariable(fout, "CXXFLAGS", "-O2 -DNDEBUG -DHAVE_RULES -Wall");
}
else
{
@ -235,7 +235,7 @@ int main(int argc, char **argv)
// The _GLIBCXX_DEBUG doesn't work in cygwin
makeConditionalVariable(fout, "CXXFLAGS",
"-DHAVE_DEPENDENCIES "
"-DHAVE_RULES "
"-Wall "
"-Wextra "
"-Wshadow "