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 Bill Egert
Daniel Marjamäki Daniel Marjamäki
Gianluca Scacco Gianluca Scacco
Greg Hewgill
Hoang Tuan Su Hoang Tuan Su
Kimmo Varis Kimmo Varis
Leandro Penz Leandro Penz

View File

@ -1,7 +1,7 @@
# This file is generated by tools/dmake, do not edit. # This file is generated by tools/dmake, do not edit.
ifndef CXXFLAGS 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 endif
ifndef CXX ifndef CXX
@ -95,6 +95,7 @@ TESTOBJ = test/options.o \
test/testsimplifytokens.o \ test/testsimplifytokens.o \
test/teststl.o \ test/teststl.o \
test/testsuite.o \ test/testsuite.o \
test/testsuppressions.o \
test/testsymboldatabase.o \ test/testsymboldatabase.o \
test/testthreadexecutor.o \ test/testthreadexecutor.o \
test/testtoken.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 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 $(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 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 $(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 OBJECTS_DIR = temp
CONFIG += warn_on CONFIG += warn_on
CONFIG -= qt app_bundle CONFIG -= qt app_bundle
DEFINES += HAVE_DEPENDENCIES DEFINES += HAVE_RULES
BASEPATH = ../externals/tinyxml/ BASEPATH = ../externals/tinyxml/
include($$PWD/../externals/tinyxml/tinyxml.pri) include($$PWD/../externals/tinyxml/tinyxml.pri)

View File

@ -28,7 +28,7 @@
#include "path.h" #include "path.h"
#include "filelister.h" #include "filelister.h"
#ifdef HAVE_DEPENDENCIES #ifdef HAVE_RULES
// xml is used in rules // xml is used in rules
#include <tinyxml.h> #include <tinyxml.h>
#endif #endif
@ -505,7 +505,7 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[])
_settings->_showtime = SHOWTIME_NONE; _settings->_showtime = SHOWTIME_NONE;
} }
#ifdef HAVE_DEPENDENCIES #ifdef HAVE_RULES
// Rule given at command line // Rule given at command line
else if (strncmp(argv[i], "--rule=", 7) == 0) 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[]) int CppCheckExecutor::check(int argc, const char* const argv[])
{ {
CppCheck cppCheck(*this); CppCheck cppCheck(*this, true);
if (!parseFromArgs(&cppCheck, argc, argv)) if (!parseFromArgs(&cppCheck, argc, argv))
{ {
return EXIT_FAILURE; return EXIT_FAILURE;
@ -152,11 +152,13 @@ int CppCheckExecutor::check(int argc, const char* const argv[])
{ {
// Multiple processes // Multiple processes
const std::vector<std::string> &filenames = cppCheck.filenames(); const std::vector<std::string> &filenames = cppCheck.filenames();
Settings settings = cppCheck.settings(); Settings &settings = cppCheck.settings();
ThreadExecutor executor(filenames, settings, *this); ThreadExecutor executor(filenames, settings, *this);
returnValue = executor.check(); returnValue = executor.check();
} }
reportUnmatchedSuppressions(cppCheck.settings().nomsg.getUnmatchedGlobalSuppressions());
if (_settings._xml) if (_settings._xml)
{ {
reportErr(ErrorLogger::ErrorMessage::getXMLFooter(_settings._xml_version)); reportErr(ErrorLogger::ErrorMessage::getXMLFooter(_settings._xml_version));

View File

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

View File

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

View File

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

View File

@ -43,7 +43,7 @@ public:
/** /**
* @brief Constructor. * @brief Constructor.
*/ */
CppCheck(ErrorLogger &errorLogger); CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions);
/** /**
* @brief Destructor. * @brief Destructor.
@ -66,10 +66,10 @@ public:
void settings(const Settings &settings); void settings(const Settings &settings);
/** /**
* @brief Get copy of current settings. * @brief Get reference to current settings.
* @return a copy of current settings * @return a reference to current settings
*/ */
Settings settings() const; Settings &settings();
/** /**
* @brief Add new file to be checked. * @brief Add new file to be checked.
@ -147,6 +147,7 @@ private:
std::list<std::string> _errorList; std::list<std::string> _errorList;
std::ostringstream _errout; std::ostringstream _errout;
Settings _settings; Settings _settings;
bool _useGlobalSuppressions;
std::vector<std::string> _filenames; std::vector<std::string> _filenames;
void reportProgress(const std::string &filename, const char stage[], const unsigned int value); 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::string ErrorLogger::callStackToString(const std::list<ErrorLogger::ErrorMessage::FileLocation> &callStack)
{ {
std::ostringstream ostr; std::ostringstream ostr;
for (std::list<ErrorLogger::ErrorMessage::FileLocation>::const_iterator tok = callStack.begin(); tok != callStack.end(); ++tok) 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(); return ostr.str();
} }

View File

@ -23,6 +23,8 @@
#include <list> #include <list>
#include <string> #include <string>
#include "settings.h"
class Token; class Token;
class Tokenizer; class Tokenizer;
@ -108,6 +110,11 @@ public:
line = 0; line = 0;
} }
FileLocation(const std::string &file, int aline)
: line(aline), _file(file)
{
}
/** /**
* Return the filename. * Return the filename.
* @param convert If true convert path to native separators. * @param convert If true convert path to native separators.
@ -230,6 +237,12 @@ public:
(void)value; (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); 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 char prev = ' '; // hack to make it skip spaces between # and the directive
code << "#"; code << "#";
std::string::const_iterator i = line.begin(); std::string::const_iterator i = line.begin();
i++; ++i;
// need space.. #if( => #if ( // need space.. #if( => #if (
bool needSpace = true; bool needSpace = true;
@ -247,7 +247,7 @@ std::string Preprocessor::preprocessCleanupDirectives(const std::string &process
// skip double whitespace between arguments // skip double whitespace between arguments
if (escapeStatus == ESC_NONE && prev == ' ' && *i == ' ') if (escapeStatus == ESC_NONE && prev == ' ' && *i == ' ')
{ {
i++; ++i;
continue; continue;
} }
// Convert #if( to "#if (" // Convert #if( to "#if ("
@ -272,7 +272,7 @@ std::string Preprocessor::preprocessCleanupDirectives(const std::string &process
{ {
prev = *i; prev = *i;
} }
i++; ++i;
} }
if (escapeStatus != ESC_NONE) 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] == '/') if (line.length() >= 2 && line[0] == '/' && line[1] == '/')
continue; continue;
std::istringstream lineStream(line); const std::string errmsg(addSuppressionLine(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()) if (!errmsg.empty())
return errmsg; return errmsg;
} }
@ -117,6 +78,54 @@ std::string Settings::Suppressions::parseFile(std::istream &istr)
return ""; 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) bool Settings::Suppressions::FileMatcher::match(const std::string &pattern, const std::string &name)
{ {
const char *p = pattern.c_str(); 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 else
{ {
_files[name].insert(line); _files[name][line] = false;
} }
return ""; return "";
} }
bool Settings::Suppressions::FileMatcher::isSuppressed(const std::string &file, unsigned int line) bool Settings::Suppressions::FileMatcher::isSuppressed(const std::string &file, unsigned int line)
{ {
// Check are all errors of this type filtered out if (isSuppressedLocal(file, line))
if (_files.find("") != _files.end())
return true; return true;
std::set<unsigned int> lineset; for (std::map<std::string, std::map<unsigned int, bool> >::iterator g = _globs.begin(); g != _globs.end(); ++g)
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)
{ {
if (match(g->first, file)) 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 bool Settings::Suppressions::FileMatcher::isSuppressedLocal(const std::string &file, unsigned int line)
if (lineset.find(0U) != lineset.end()) {
return true; 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 false;
return true;
} }
std::string Settings::Suppressions::addSuppression(const std::string &errorId, const std::string &file, unsigned int line) 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); 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) std::string Settings::addEnabled(const std::string &str)
{ {
// Enable parameters may be comma separated... // Enable parameters may be comma separated...

View File

@ -138,11 +138,12 @@ public:
private: private:
class FileMatcher class FileMatcher
{ {
friend class Suppressions;
private: private:
/** @brief List of filenames suppressed. */ /** @brief List of filenames suppressed, bool flag indicates whether suppression matched. */
std::map<std::string, std::set<unsigned int> > _files; std::map<std::string, std::map<unsigned int, bool> > _files;
/** @brief List of globs suppressed. */ /** @brief List of globs suppressed, bool flag indicates whether suppression matched. */
std::map<std::string, std::set<unsigned int> > _globs; std::map<std::string, std::map<unsigned int, bool> > _globs;
/** /**
* @brief Match a name against a glob pattern. * @brief Match a name against a glob pattern.
@ -168,6 +169,14 @@ public:
* @return true if this filename/line matches * @return true if this filename/line matches
*/ */
bool isSuppressed(const std::string &file, unsigned int line); 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. */ /** @brief List of error which the user doesn't want to see. */
@ -180,6 +189,13 @@ public:
*/ */
std::string parseFile(std::istream &istr); 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 * @brief Don't show this error. If file and/or line are optional. In which case
* the errorId alone is used for filtering. * the errorId alone is used for filtering.
@ -198,6 +214,38 @@ public:
* @return true if this error is suppressed. * @return true if this error is suppressed.
*/ */
bool isSuppressed(const std::string &errorId, const std::string &file, unsigned int line); 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) */ /** @brief suppress message (--suppressions) */

View File

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

View File

@ -869,6 +869,8 @@ static Token *processFunc(Token *tok2, bool inOperator)
tok2 = tok2->tokAt(5)->link(); tok2 = tok2->tokAt(5)->link();
else if (Token::Match(tok2->next(), "* ( * %type% ) (")) else if (Token::Match(tok2->next(), "* ( * %type% ) ("))
tok2 = tok2->tokAt(6)->link(); tok2 = tok2->tokAt(6)->link();
else if (Token::Match(tok2->next(), "* ( * %type% ) ;"))
tok2 = tok2->tokAt(5);
else if (Token::Match(tok2->next(), "* ( %type% [") && else if (Token::Match(tok2->next(), "* ( %type% [") &&
Token::Match(tok2->tokAt(4)->link(), "] ) ;|=")) Token::Match(tok2->tokAt(4)->link(), "] ) ;|="))
tok2 = tok2->tokAt(4)->link()->next(); 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 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; Token* bailOutFromLoop = 0;
int indentlevel3 = indentlevel; int indentlevel3 = indentlevel;
@ -6527,7 +6529,7 @@ bool Tokenizer::simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, unsign
// Using the variable in condition.. // Using the variable in condition..
if (Token::Match(tok3->previous(), ("if ( " + structname + " %varid% ==|!=|<|<=|>|>=|)").c_str(), varid) || 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, ("!|==|!=|<|<=|>|>= " + structname + " %varid% ==|!=|<|<=|>|>=|)|;").c_str(), varid) ||
Token::Match(tok3->previous(), "strlen|free ( %varid% )", varid)) Token::Match(tok3->previous(), "strlen|free ( %varid% )", varid))
{ {
if (!structname.empty()) if (!structname.empty())
@ -6618,10 +6620,10 @@ bool Tokenizer::simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, unsign
// Variable is used in calculation.. // Variable is used in calculation..
if (((tok3->previous()->varId() > 0) && Token::Match(tok3, ("& " + structname + " %varid%").c_str(), varid)) || 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, ("[(=+-*/%^|[] " + 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->previous(), ("[=+-*/%^|[] ( " + structname + " %varid%").c_str(), varid))
{ {
if (!structname.empty()) if (!structname.empty())
{ {
@ -7069,10 +7071,11 @@ bool Tokenizer::simplifyCalculations()
{ {
// (1-2) // (1-2)
while (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% <<|>>") ||
Token::Match(tok, "<< %num% [+-*/] %num% <<")) Token::Match(tok, "<< %num% [+-*/] %num% <<") ||
Token::Match(tok, "[(,[] %num% [|&^] %num% [];,);]"))
{ {
tok = tok->next(); tok = tok->next();
@ -7080,6 +7083,29 @@ bool Tokenizer::simplifyCalculations()
if (Token::simpleMatch(tok->next(), "/ 0")) if (Token::simpleMatch(tok->next(), "/ 0"))
continue; 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 / // + and - are calculated after * and /
if (Token::Match(tok->next(), "[+-/]")) if (Token::Match(tok->next(), "[+-/]"))
{ {

View File

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

View File

@ -868,7 +868,7 @@ private:
" a[256] = 0;\n" // 256 > CHAR_MAX " a[256] = 0;\n" // 256 > CHAR_MAX
"}\n"); "}\n");
ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[256]' index 256 out of bounds\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" check("void f(signed char n) {\n"
@ -1875,6 +1875,20 @@ private:
" }\n" " }\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); 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() void buffer_overrun_bailoutIfSwitch()

View File

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

View File

@ -236,6 +236,7 @@ private:
TEST_CASE(simplifyTypedef76); // ticket #2453 TEST_CASE(simplifyTypedef76); // ticket #2453
TEST_CASE(simplifyTypedef77); // ticket #2554 TEST_CASE(simplifyTypedef77); // ticket #2554
TEST_CASE(simplifyTypedef78); // ticket #2568 TEST_CASE(simplifyTypedef78); // ticket #2568
TEST_CASE(simplifyTypedef79); // ticket #2348
TEST_CASE(simplifyTypedefFunction1); TEST_CASE(simplifyTypedefFunction1);
TEST_CASE(simplifyTypedefFunction2); // ticket #1685 TEST_CASE(simplifyTypedefFunction2); // ticket #1685
@ -2590,6 +2591,8 @@ private:
ASSERT_EQUALS("a [ 0 ]", tok(code)); 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 = 1 + 2 * y ;", tok("x=1+2*y;"));
ASSERT_EQUALS("x = 7 ;", tok("x=1+2*3;")); ASSERT_EQUALS("x = 7 ;", tok("x=1+2*3;"));
ASSERT_EQUALS("x = 47185 ;", tok("x=(65536*72/100);")); ASSERT_EQUALS("x = 47185 ;", tok("x=(65536*72/100);"));
@ -4858,6 +4861,21 @@ private:
ASSERT_EQUALS(expected, sizeof_(code)); 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() 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" " int i = v;\n"
" return h | i;\n" " return h | i;\n"
"}\n"; "}\n";
const char wanted[] = "\n\n##file 0\n" const char expected[] = "\n\n##file 0\n"
"1: int foo ( int u@1 , int v@2 )\n" "1: int foo ( int u@1 , int v@2 )\n"
"2: {\n" "2: {\n"
"3: ;\n" "3: ;\n"
"4:\n" "4:\n"
"5: return u@1 | v@2 ;\n" "5: return u@1 | v@2 ;\n"
"6: }\n"; "6: }\n";
const char current[] = "\n\n##file 0\n" ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
"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));
} }
{ {
@ -1661,21 +1654,14 @@ private:
" int i = v;\n" " int i = v;\n"
" return h ^ i;\n" " return h ^ i;\n"
"}\n"; "}\n";
const char wanted[] = "\n\n##file 0\n" const char expected[] = "\n\n##file 0\n"
"1: int foo ( int u@1 , int v@2 )\n" "1: int foo ( int u@1 , int v@2 )\n"
"2: {\n" "2: {\n"
"3: ;\n" "3: ;\n"
"4:\n" "4:\n"
"5: return u@1 ^ v@2 ;\n" "5: return u@1 ^ v@2 ;\n"
"6: }\n"; "6: }\n";
const char current[] = "\n\n##file 0\n" ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
"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));
} }
{ {
@ -1685,21 +1671,14 @@ private:
" int i = v;\n" " int i = v;\n"
" return h % i;\n" " return h % i;\n"
"}\n"; "}\n";
const char wanted[] = "\n\n##file 0\n" const char expected[] = "\n\n##file 0\n"
"1: int foo ( int u@1 , int v@2 )\n" "1: int foo ( int u@1 , int v@2 )\n"
"2: {\n" "2: {\n"
"3: ;\n" "3: ;\n"
"4:\n" "4:\n"
"5: return u@1 % v@2 ;\n" "5: return u@1 % v@2 ;\n"
"6: }\n"; "6: }\n";
const char current[] = "\n\n##file 0\n" ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
"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));
} }
{ {
@ -1743,22 +1722,14 @@ private:
" int i = v;\n" " int i = v;\n"
" return h == i;\n" " return h == i;\n"
"}\n"; "}\n";
const char wanted[] = "\n\n##file 0\n" const char expected[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n" "1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n" "2: {\n"
"3: ;\n" "3: ;\n"
"4: ;\n" "4:\n"
"5: return u@1 == v@2 ;\n" "5: return u@1 == v@2 ;\n"
"6: }\n"; "6: }\n";
const char current[] = "\n\n##file 0\n" ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
"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));
} }
{ {
@ -1768,21 +1739,14 @@ private:
" int i = v;\n" " int i = v;\n"
" return h != i;\n" " return h != i;\n"
"}\n"; "}\n";
const char wanted[] = "\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: ;\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" "1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n" "2: {\n"
"3: ;\n" "3: ;\n"
"4: int i@4 ; i@4 = v@2 ;\n" "4:\n"
"5: return u@1 != i@4 ;\n" "5: return u@1 != v@2 ;\n"
"6: }\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" " int i = v;\n"
" return h > i;\n" " return h > i;\n"
"}\n"; "}\n";
const char wanted[] = "\n\n##file 0\n" const char expected[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n" "1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n" "2: {\n"
"3: ;\n" "3: ;\n"
"4: ;\n" "4:\n"
"5: return u@1 > v@2 ;\n" "5: return u@1 > v@2 ;\n"
"6: }\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"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
} }
{ {
@ -1810,15 +1773,14 @@ private:
" int i = v;\n" " int i = v;\n"
" return h >= i;\n" " return h >= i;\n"
"}\n"; "}\n";
const char wanted[] = "\n\n##file 0\n" const char expected[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n" "1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n" "2: {\n"
"3: ;\n" "3: ;\n"
"4: ;\n" "4:\n"
"5: return u@1 >= v@2 ;\n" "5: return u@1 >= v@2 ;\n"
"6: }\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"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
} }
{ {
@ -1828,15 +1790,14 @@ private:
" int i = v;\n" " int i = v;\n"
" return h < i;\n" " return h < i;\n"
"}\n"; "}\n";
const char wanted[] = "\n\n##file 0\n" const char expected[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n" "1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n" "2: {\n"
"3: ;\n" "3: ;\n"
"4: ;\n" "4:\n"
"5: return u@1 < v@2 ;\n" "5: return u@1 < v@2 ;\n"
"6: }\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"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
} }
{ {
@ -1846,15 +1807,14 @@ private:
" int i = v;\n" " int i = v;\n"
" return h <= i;\n" " return h <= i;\n"
"}\n"; "}\n";
const char wanted[] = "\n\n##file 0\n" const char expected[] = "\n\n##file 0\n"
"1: bool foo ( int u@1 , int v@2 )\n" "1: bool foo ( int u@1 , int v@2 )\n"
"2: {\n" "2: {\n"
"3: ;\n" "3: ;\n"
"4: ;\n" "4:\n"
"5: return u@1 <= v@2 ;\n" "5: return u@1 <= v@2 ;\n"
"6: }\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"; ASSERT_EQUALS(expected, tokenizeDebugListing(code, true));
TODO_ASSERT_EQUALS(wanted, current, tokenizeDebugListing(code, true));
} }
{ {

View File

@ -67,6 +67,7 @@ private:
TEST_CASE(testDoesNotIdentifyMethodAsLastFunctionArgument); TEST_CASE(testDoesNotIdentifyMethodAsLastFunctionArgument);
TEST_CASE(multiFile); TEST_CASE(multiFile);
TEST_CASE(unknownBaseTemplate); // ticket #2580
} }
@ -571,6 +572,20 @@ private:
ASSERT_EQUALS("", errout.str()); 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) REGISTER_TEST(TestUnusedPrivateFunction)

View File

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