Suppressions: New extensible Suppressions xml format that allow more attributes. To start with it also allows symbolName.
This commit is contained in:
parent
148af12bec
commit
a0906140a6
|
@ -10,10 +10,10 @@ knownConditionTrueFalse:build/*
|
|||
nullPointer:lib/checkother.cpp
|
||||
nullPointer:build/checkother.cpp
|
||||
|
||||
*:gui/test*
|
||||
*:gui/test/*
|
||||
*:test/test.cxx
|
||||
*:test/cfg*
|
||||
*:test/cfg/*
|
||||
|
||||
*:externals*
|
||||
*:htmlreport*
|
||||
*:samples*
|
||||
*:externals/*/*
|
||||
*:htmlreport/*
|
||||
*:samples/*/bad.c*
|
||||
|
|
|
@ -228,6 +228,15 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[])
|
|||
}
|
||||
}
|
||||
|
||||
else if (std::strncmp(argv[i], "--suppress-xml=", 15) == 0) {
|
||||
const char * filename = argv[i] + 15;
|
||||
const std::string errmsg(_settings->nomsg.parseXmlFile(filename));
|
||||
if (!errmsg.empty()) {
|
||||
PrintMessage(errmsg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
else if (std::strncmp(argv[i], "--suppress=", 11) == 0) {
|
||||
const std::string suppression = argv[i]+11;
|
||||
const std::string errmsg(_settings->nomsg.addSuppressionLine(suppression));
|
||||
|
|
|
@ -117,14 +117,7 @@ int ThreadExecutor::handleRead(int rpipe, unsigned int &result)
|
|||
ErrorLogger::ErrorMessage msg;
|
||||
msg.deserialize(buf);
|
||||
|
||||
std::string file;
|
||||
unsigned int line(0);
|
||||
if (!msg._callStack.empty()) {
|
||||
file = msg._callStack.back().getfile(false);
|
||||
line = msg._callStack.back().line;
|
||||
}
|
||||
|
||||
if (!_settings.nomsg.isSuppressed(msg._id, file, line)) {
|
||||
if (!_settings.nomsg.isSuppressed(msg.toSuppressionsErrorMessage())) {
|
||||
// Alert only about unique errors
|
||||
std::string errmsg = msg.toString(_settings.verbose);
|
||||
if (std::find(_errorList.begin(), _errorList.end(), errmsg) == _errorList.end()) {
|
||||
|
@ -309,7 +302,7 @@ unsigned int ThreadExecutor::check()
|
|||
"cppcheckError",
|
||||
false);
|
||||
|
||||
if (!_settings.nomsg.isSuppressed(errmsg._id, childname, 0))
|
||||
if (!_settings.nomsg.isSuppressed(errmsg.toSuppressionsErrorMessage()))
|
||||
_errorLogger.reportErr(errmsg);
|
||||
}
|
||||
}
|
||||
|
@ -502,14 +495,7 @@ void ThreadExecutor::reportInfo(const ErrorLogger::ErrorMessage &msg)
|
|||
|
||||
void ThreadExecutor::report(const ErrorLogger::ErrorMessage &msg, MessageType msgType)
|
||||
{
|
||||
std::string file;
|
||||
unsigned int line(0);
|
||||
if (!msg._callStack.empty()) {
|
||||
file = msg._callStack.back().getfile(false);
|
||||
line = msg._callStack.back().line;
|
||||
}
|
||||
|
||||
if (_settings.nomsg.isSuppressed(msg._id, file, line))
|
||||
if (_settings.nomsg.isSuppressed(msg.toSuppressionsErrorMessage()))
|
||||
return;
|
||||
|
||||
// Alert only about unique errors
|
||||
|
|
|
@ -419,7 +419,20 @@ void CheckThread::parseClangErrors(const QString &tool, const QString &file0, QS
|
|||
foreach (const ErrorItem &e, errorItems) {
|
||||
if (e.errorPath.isEmpty())
|
||||
continue;
|
||||
if (mSuppressions.contains(e.errorId))
|
||||
Suppressions::ErrorMessage errorMessage;
|
||||
errorMessage.setFileName(e.errorPath.back().file.toStdString());
|
||||
errorMessage.lineNumber = e.errorPath.back().line;
|
||||
errorMessage.errorId = e.errorId.toStdString();
|
||||
errorMessage.symbolNames = e.symbolNames.toStdString();
|
||||
|
||||
bool isSuppressed = false;
|
||||
foreach (const Suppressions::Suppression &suppression, mSuppressions) {
|
||||
if (suppression.isSuppressed(errorMessage)) {
|
||||
isSuppressed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isSuppressed)
|
||||
continue;
|
||||
std::list<ErrorLogger::ErrorMessage::FileLocation> callstack;
|
||||
foreach (const QErrorPathItem &path, e.errorPath) {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <QThread>
|
||||
#include "cppcheck.h"
|
||||
#include "threadresult.h"
|
||||
#include "suppressions.h"
|
||||
|
||||
class Settings;
|
||||
|
||||
|
@ -68,7 +69,7 @@ public:
|
|||
mClangIncludePaths = s;
|
||||
}
|
||||
|
||||
void setSuppressions(const QStringList s) {
|
||||
void setSuppressions(const QList<Suppressions::Suppression> &s) {
|
||||
mSuppressions = s;
|
||||
}
|
||||
|
||||
|
@ -151,7 +152,7 @@ private:
|
|||
QStringList mAddonsAndTools;
|
||||
QString mDataDir;
|
||||
QStringList mClangIncludePaths;
|
||||
QStringList mSuppressions;
|
||||
QList<Suppressions::Suppression> mSuppressions;
|
||||
QString mMisraFile;
|
||||
};
|
||||
/// @}
|
||||
|
|
|
@ -46,6 +46,7 @@ ErrorItem::ErrorItem(const ErrorLogger::ErrorMessage &errmsg)
|
|||
, summary(QString::fromStdString(errmsg.shortMessage()))
|
||||
, message(QString::fromStdString(errmsg.verboseMessage()))
|
||||
, cwe(errmsg._cwe.id)
|
||||
, symbolNames(QString::fromStdString(errmsg.symbolNames()))
|
||||
{
|
||||
for (std::list<ErrorLogger::ErrorMessage::FileLocation>::const_iterator loc = errmsg._callStack.begin();
|
||||
loc != errmsg._callStack.end();
|
||||
|
|
|
@ -89,6 +89,7 @@ public:
|
|||
QString message;
|
||||
int cwe;
|
||||
QList<QErrorPathItem> errorPath;
|
||||
QString symbolNames;
|
||||
|
||||
// Special GUI properties
|
||||
QString sinceDate;
|
||||
|
|
|
@ -59,7 +59,8 @@ FORMS = about.ui \
|
|||
stats.ui \
|
||||
librarydialog.ui \
|
||||
libraryaddfunctiondialog.ui \
|
||||
libraryeditargdialog.ui
|
||||
libraryeditargdialog.ui \
|
||||
newsuppressiondialog.ui
|
||||
|
||||
TRANSLATIONS = cppcheck_de.ts \
|
||||
cppcheck_es.ts \
|
||||
|
@ -116,7 +117,8 @@ HEADERS += aboutdialog.h \
|
|||
librarydialog.h \
|
||||
cppchecklibrarydata.h \
|
||||
libraryaddfunctiondialog.h \
|
||||
libraryeditargdialog.h
|
||||
libraryeditargdialog.h \
|
||||
newsuppressiondialog.h
|
||||
|
||||
SOURCES += aboutdialog.cpp \
|
||||
application.cpp \
|
||||
|
@ -152,7 +154,8 @@ SOURCES += aboutdialog.cpp \
|
|||
librarydialog.cpp \
|
||||
cppchecklibrarydata.cpp \
|
||||
libraryaddfunctiondialog.cpp \
|
||||
libraryeditargdialog.cpp
|
||||
libraryeditargdialog.cpp \
|
||||
newsuppressiondialog.cpp
|
||||
|
||||
win32 {
|
||||
RC_FILE = cppcheck-gui.rc
|
||||
|
|
|
@ -832,9 +832,9 @@ Settings MainWindow::getCppcheckSettings()
|
|||
tryLoadLibrary(&result.library, filename);
|
||||
}
|
||||
|
||||
const QStringList suppressions = mProjectFile->getSuppressions();
|
||||
foreach (QString suppression, suppressions) {
|
||||
result.nomsg.addSuppressionLine(suppression.toStdString());
|
||||
const QList<Suppressions::Suppression> &suppressions = mProjectFile->getSuppressions();
|
||||
foreach (const Suppressions::Suppression &suppression, suppressions) {
|
||||
result.nomsg.addSuppression(suppression);
|
||||
}
|
||||
|
||||
// Only check the given -D configuration
|
||||
|
@ -1729,13 +1729,26 @@ void MainWindow::tagged()
|
|||
|
||||
void MainWindow::suppressIds(QStringList ids)
|
||||
{
|
||||
if (mProjectFile) {
|
||||
QStringList suppressions = mProjectFile->getSuppressions();
|
||||
foreach (QString s, ids) {
|
||||
if (!suppressions.contains(s))
|
||||
suppressions << s;
|
||||
if (!mProjectFile)
|
||||
return;
|
||||
ids.removeDuplicates();
|
||||
|
||||
QList<Suppressions::Suppression> suppressions = mProjectFile->getSuppressions();
|
||||
foreach (QString id, ids) {
|
||||
// Remove all matching suppressions
|
||||
std::string id2 = id.toStdString();
|
||||
for (int i = 0; i < suppressions.size();) {
|
||||
if (suppressions[i].errorId == id2)
|
||||
suppressions.removeAt(i);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
|
||||
Suppressions::Suppression newSuppression;
|
||||
newSuppression.errorId = id2;
|
||||
suppressions << newSuppression;
|
||||
}
|
||||
|
||||
mProjectFile->setSuppressions(suppressions);
|
||||
mProjectFile->write();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
#include "newsuppressiondialog.h"
|
||||
#include "ui_newsuppressiondialog.h"
|
||||
|
||||
NewSuppressionDialog::NewSuppressionDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
mUI(new Ui::NewSuppressionDialog)
|
||||
{
|
||||
mUI->setupUi(this);
|
||||
}
|
||||
|
||||
NewSuppressionDialog::~NewSuppressionDialog()
|
||||
{
|
||||
delete mUI;
|
||||
}
|
||||
|
||||
void NewSuppressionDialog::setErrorIds(const QStringList &errorIds)
|
||||
{
|
||||
mUI->mComboErrorId->addItems(errorIds);
|
||||
mUI->mComboErrorId->setCurrentIndex(-1);
|
||||
mUI->mComboErrorId->setCurrentText("");
|
||||
}
|
||||
|
||||
Suppressions::Suppression NewSuppressionDialog::getSuppression() const
|
||||
{
|
||||
Suppressions::Suppression ret;
|
||||
ret.errorId = mUI->mComboErrorId->currentText().toStdString();
|
||||
ret.fileName = mUI->mTextFileName->text().toStdString();
|
||||
ret.lineNumber = mUI->mTextLineNumber->text().toInt();
|
||||
ret.symbolName = mUI->mTextSymbolName->text().toStdString();
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef NEWSUPPRESSIONDIALOG_H
|
||||
#define NEWSUPPRESSIONDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "suppressions.h"
|
||||
|
||||
namespace Ui {
|
||||
class NewSuppressionDialog;
|
||||
}
|
||||
|
||||
class NewSuppressionDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NewSuppressionDialog(QWidget *parent = 0);
|
||||
~NewSuppressionDialog();
|
||||
|
||||
void setErrorIds(const QStringList &errorIds);
|
||||
Suppressions::Suppression getSuppression() const;
|
||||
|
||||
private:
|
||||
Ui::NewSuppressionDialog *mUI;
|
||||
};
|
||||
|
||||
#endif // NEWSUPPRESSIONDIALOG_H
|
|
@ -0,0 +1,115 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>NewSuppressionDialog</class>
|
||||
<widget class="QDialog" name="NewSuppressionDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>334</width>
|
||||
<height>184</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>New suppression</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Error ID</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>File name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="mTextFileName"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Line number</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="mTextLineNumber"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Symbol name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="mTextSymbolName"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="mComboErrorId">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>NewSuppressionDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>NewSuppressionDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -156,7 +156,7 @@ bool ProjectFile::read(const QString &filename)
|
|||
|
||||
// Find suppressions list from inside project element
|
||||
if (insideProject && xmlReader.name() == SuppressionsElementName)
|
||||
readStringList(mSuppressions, xmlReader,SuppressionElementName);
|
||||
readSuppressions(xmlReader);
|
||||
|
||||
// Addons
|
||||
if (insideProject && xmlReader.name() == AddonsElementName)
|
||||
|
@ -464,6 +464,51 @@ void ProjectFile::readPlatform(QXmlStreamReader &reader)
|
|||
}
|
||||
|
||||
|
||||
void ProjectFile::readSuppressions(QXmlStreamReader &reader)
|
||||
{
|
||||
QXmlStreamReader::TokenType type;
|
||||
do {
|
||||
type = reader.readNext();
|
||||
switch (type) {
|
||||
case QXmlStreamReader::StartElement:
|
||||
// Read library-elements
|
||||
if (reader.name().toString() == SuppressionElementName) {
|
||||
Suppressions::Suppression suppression;
|
||||
if (reader.attributes().hasAttribute(QString(),"fileName"))
|
||||
suppression.fileName = reader.attributes().value(QString(),"fileName").toString().toStdString();
|
||||
if (reader.attributes().hasAttribute(QString(),"lineNumber"))
|
||||
suppression.lineNumber = reader.attributes().value(QString(),"lineNumber").toInt();
|
||||
if (reader.attributes().hasAttribute(QString(),"symbolName"))
|
||||
suppression.symbolName = reader.attributes().value(QString(),"symbolName").toString().toStdString();
|
||||
type = reader.readNext();
|
||||
if (type == QXmlStreamReader::Characters) {
|
||||
suppression.errorId = reader.text().toString().toStdString();
|
||||
}
|
||||
mSuppressions << suppression;
|
||||
}
|
||||
break;
|
||||
|
||||
case QXmlStreamReader::EndElement:
|
||||
if (reader.name().toString() != SuppressionElementName)
|
||||
return;
|
||||
break;
|
||||
|
||||
// Not handled
|
||||
case QXmlStreamReader::NoToken:
|
||||
case QXmlStreamReader::Invalid:
|
||||
case QXmlStreamReader::StartDocument:
|
||||
case QXmlStreamReader::EndDocument:
|
||||
case QXmlStreamReader::Characters:
|
||||
case QXmlStreamReader::Comment:
|
||||
case QXmlStreamReader::DTD:
|
||||
case QXmlStreamReader::EntityReference:
|
||||
case QXmlStreamReader::ProcessingInstruction:
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
|
||||
void ProjectFile::readStringList(QStringList &stringlist, QXmlStreamReader &reader, const char elementname[])
|
||||
{
|
||||
QXmlStreamReader::TokenType type;
|
||||
|
@ -532,7 +577,7 @@ void ProjectFile::setPlatform(const QString &platform)
|
|||
mPlatform = platform;
|
||||
}
|
||||
|
||||
void ProjectFile::setSuppressions(const QStringList &suppressions)
|
||||
void ProjectFile::setSuppressions(const QList<Suppressions::Suppression> &suppressions)
|
||||
{
|
||||
mSuppressions = suppressions;
|
||||
}
|
||||
|
@ -630,10 +675,22 @@ bool ProjectFile::write(const QString &filename)
|
|||
LibrariesElementName,
|
||||
LibraryElementName);
|
||||
|
||||
writeStringList(xmlWriter,
|
||||
mSuppressions,
|
||||
SuppressionsElementName,
|
||||
SuppressionElementName);
|
||||
if (!mSuppressions.isEmpty()) {
|
||||
xmlWriter.writeStartElement(SuppressionsElementName);
|
||||
foreach (const Suppressions::Suppression &suppression, mSuppressions) {
|
||||
xmlWriter.writeStartElement(SuppressionElementName);
|
||||
if (!suppression.fileName.empty())
|
||||
xmlWriter.writeAttribute("fileName", QString::fromStdString(suppression.fileName));
|
||||
if (suppression.lineNumber > 0)
|
||||
xmlWriter.writeAttribute("lineNumber", QString::number(suppression.lineNumber));
|
||||
if (!suppression.symbolName.empty())
|
||||
xmlWriter.writeAttribute("symbolName", QString::fromStdString(suppression.symbolName));
|
||||
if (!suppression.errorId.empty())
|
||||
xmlWriter.writeCharacters(QString::fromStdString(suppression.errorId));
|
||||
xmlWriter.writeEndElement();
|
||||
}
|
||||
xmlWriter.writeEndElement();
|
||||
}
|
||||
|
||||
writeStringList(xmlWriter,
|
||||
mAddons,
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include <QStringList>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include "suppressions.h"
|
||||
|
||||
/// @addtogroup GUI
|
||||
/// @{
|
||||
|
||||
|
@ -118,7 +120,7 @@ public:
|
|||
* @brief Get list suppressions.
|
||||
* @return list of suppressions.
|
||||
*/
|
||||
QStringList getSuppressions() const {
|
||||
QList<Suppressions::Suppression> getSuppressions() const {
|
||||
return mSuppressions;
|
||||
}
|
||||
|
||||
|
@ -224,7 +226,7 @@ public:
|
|||
* @brief Set list of suppressions.
|
||||
* @param suppressions List of suppressions.
|
||||
*/
|
||||
void setSuppressions(const QStringList &suppressions);
|
||||
void setSuppressions(const QList<Suppressions::Suppression> &suppressions);
|
||||
|
||||
/**
|
||||
* @brief Set list of addons.
|
||||
|
@ -302,6 +304,12 @@ protected:
|
|||
*/
|
||||
void readPlatform(QXmlStreamReader &reader);
|
||||
|
||||
/**
|
||||
* @brief Read suppressions.
|
||||
* @param reader XML stream reader.
|
||||
*/
|
||||
void readSuppressions(QXmlStreamReader &reader);
|
||||
|
||||
/**
|
||||
* @brief Read string list
|
||||
* @param stringlist destination string list
|
||||
|
@ -387,7 +395,7 @@ private:
|
|||
/**
|
||||
* @brief List of suppressions.
|
||||
*/
|
||||
QStringList mSuppressions;
|
||||
QList<Suppressions::Suppression> mSuppressions;
|
||||
|
||||
/**
|
||||
* @brief List of addons.
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <QSettings>
|
||||
#include <QProcess>
|
||||
#include "common.h"
|
||||
#include "newsuppressiondialog.h"
|
||||
#include "projectfiledialog.h"
|
||||
#include "checkthread.h"
|
||||
#include "projectfile.h"
|
||||
|
@ -484,17 +485,6 @@ QStringList ProjectFileDialog::getLibraries() const
|
|||
return libraries;
|
||||
}
|
||||
|
||||
QStringList ProjectFileDialog::getSuppressions() const
|
||||
{
|
||||
QStringList suppressions;
|
||||
const int count = mUI.mListSuppressions->count();
|
||||
for (int i = 0; i < count; i++) {
|
||||
QListWidgetItem *item = mUI.mListSuppressions->item(i);
|
||||
suppressions << item->text();
|
||||
}
|
||||
return suppressions;
|
||||
}
|
||||
|
||||
void ProjectFileDialog::setRootPath(const QString &root)
|
||||
{
|
||||
mUI.mEditProjectRoot->setText(QDir::toNativeSeparators(root));
|
||||
|
@ -553,10 +543,17 @@ void ProjectFileDialog::setLibraries(const QStringList &libraries)
|
|||
}
|
||||
}
|
||||
|
||||
void ProjectFileDialog::setSuppressions(const QStringList &suppressions)
|
||||
void ProjectFileDialog::setSuppressions(const QList<Suppressions::Suppression> &suppressions)
|
||||
{
|
||||
mSuppressions = suppressions;
|
||||
|
||||
QStringList s;
|
||||
foreach (const Suppressions::Suppression &suppression, mSuppressions) {
|
||||
s << QString::fromStdString(suppression.getText());
|
||||
}
|
||||
|
||||
mUI.mListSuppressions->clear();
|
||||
mUI.mListSuppressions->addItems(suppressions);
|
||||
mUI.mListSuppressions->addItems(s);
|
||||
mUI.mListSuppressions->sortItems();
|
||||
}
|
||||
|
||||
|
@ -655,12 +652,10 @@ void ProjectFileDialog::addSuppression()
|
|||
cppcheck.getErrorMessages();
|
||||
errorLogger.errorIds.sort();
|
||||
|
||||
bool ok;
|
||||
QString item = QInputDialog::getItem(this, tr("Add Suppression"),
|
||||
tr("Select error id suppress:"), errorLogger.errorIds, 0, false, &ok);
|
||||
if (ok && !item.isEmpty()) {
|
||||
mUI.mListSuppressions->addItem(item);
|
||||
mUI.mListSuppressions->sortItems();
|
||||
NewSuppressionDialog dlg;
|
||||
dlg.setErrorIds(errorLogger.errorIds);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
setSuppressions(mSuppressions << dlg.getSuppression());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -668,7 +663,14 @@ void ProjectFileDialog::removeSuppression()
|
|||
{
|
||||
const int row = mUI.mListSuppressions->currentRow();
|
||||
QListWidgetItem *item = mUI.mListSuppressions->takeItem(row);
|
||||
const std::string s = item->text().toStdString();
|
||||
delete item;
|
||||
for (int i = 0; i < mSuppressions.size(); ++i) {
|
||||
if (mSuppressions[i].getText() == s) {
|
||||
mSuppressions.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectFileDialog::browseMisraFile()
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include <QStringList>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include "suppressions.h"
|
||||
|
||||
#include "ui_projectfiledialog.h"
|
||||
|
||||
class QWidget;
|
||||
|
@ -95,7 +97,9 @@ private:
|
|||
* @brief Return suppressions from the dialog control.
|
||||
* @return List of suppressions.
|
||||
*/
|
||||
QStringList getSuppressions() const;
|
||||
QList<Suppressions::Suppression> getSuppressions() const {
|
||||
return mSuppressions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set project root path to dialog control.
|
||||
|
@ -142,7 +146,7 @@ private:
|
|||
* @brief Set suppressions to dialog control.
|
||||
* @param suppressions List of suppressions to set to dialog control.
|
||||
*/
|
||||
void setSuppressions(const QStringList &suppressions);
|
||||
void setSuppressions(const QList<Suppressions::Suppression> &suppressions);
|
||||
|
||||
protected slots:
|
||||
|
||||
|
@ -277,6 +281,8 @@ private:
|
|||
QList<QCheckBox*> mLibraryCheckboxes;
|
||||
|
||||
QString getExistingDirectory(const QString &caption, bool trailingSlash);
|
||||
|
||||
QList<Suppressions::Suppression> mSuppressions;
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <set>
|
||||
#include "threadresult.h"
|
||||
#include "importproject.h"
|
||||
#include "suppressions.h"
|
||||
|
||||
class ResultsView;
|
||||
class CheckThread;
|
||||
|
@ -76,7 +77,7 @@ public:
|
|||
mMisraFile = misraFile;
|
||||
}
|
||||
|
||||
void setSuppressions(const QStringList &s) {
|
||||
void setSuppressions(const QList<Suppressions::Suppression> &s) {
|
||||
mSuppressions = s;
|
||||
}
|
||||
|
||||
|
@ -255,7 +256,7 @@ protected:
|
|||
bool mAnalyseWholeProgram;
|
||||
|
||||
QStringList mAddonsAndTools;
|
||||
QStringList mSuppressions;
|
||||
QList<Suppressions::Suppression> mSuppressions;
|
||||
QStringList mClangIncludePaths;
|
||||
|
||||
QString mDataDir;
|
||||
|
|
|
@ -96,8 +96,10 @@ void CheckAssert::assertWithSideEffects()
|
|||
void CheckAssert::sideEffectInAssertError(const Token *tok, const std::string& functionName)
|
||||
{
|
||||
reportError(tok, Severity::warning,
|
||||
"assertWithSideEffect", "Assert statement calls a function which may have desired side effects: '" + functionName + "'.\n"
|
||||
"Non-pure function: '" + functionName + "' is called inside assert statement. "
|
||||
"assertWithSideEffect",
|
||||
"$symbol:" + functionName + "\n"
|
||||
"Assert statement calls a function which may have desired side effects: '$symbol'.\n"
|
||||
"Non-pure function: '$symbol' is called inside assert statement. "
|
||||
"Assert statements are removed from release builds so the code inside "
|
||||
"assert statement is not executed. If the code is needed also in release "
|
||||
"builds, this is a bug.", CWE398, false);
|
||||
|
@ -106,8 +108,10 @@ void CheckAssert::sideEffectInAssertError(const Token *tok, const std::string& f
|
|||
void CheckAssert::assignmentInAssertError(const Token *tok, const std::string& varname)
|
||||
{
|
||||
reportError(tok, Severity::warning,
|
||||
"assignmentInAssert", "Assert statement modifies '" + varname + "'.\n"
|
||||
"Variable '" + varname + "' is modified insert assert statement. "
|
||||
"assignmentInAssert",
|
||||
"$symbol:" + varname + "\n"
|
||||
"Assert statement modifies '$symbol'.\n"
|
||||
"Variable '$symbol' is modified insert assert statement. "
|
||||
"Assert statements are removed from release builds so the code inside "
|
||||
"assert statement is not executed. If the code is needed also in release "
|
||||
"builds, this is a bug.", CWE398, false);
|
||||
|
|
|
@ -392,7 +392,7 @@ void CheckAutoVariables::errorAssignAddressOfLocalArrayToGlobalPointer(const Tok
|
|||
const std::string pointerName = pointer ? pointer->str() : std::string("pointer");
|
||||
const std::string arrayName = array ? array->str() : std::string("array");
|
||||
reportError(pointer, Severity::warning, "autoVariablesAssignGlobalPointer",
|
||||
"Address of local array " + arrayName + " is assigned to global pointer " + pointerName +" and not reassigned before " + arrayName + " goes out of scope.", CWE562, false);
|
||||
"$symbol:" + arrayName + "\nAddress of local array $symbol is assigned to global pointer " + pointerName +" and not reassigned before $symbol goes out of scope.", CWE562, false);
|
||||
}
|
||||
|
||||
void CheckAutoVariables::errorAssignAddressOfLocalVariableToGlobalPointer(const Token *pointer, const Token *variable)
|
||||
|
@ -400,14 +400,15 @@ void CheckAutoVariables::errorAssignAddressOfLocalVariableToGlobalPointer(const
|
|||
const std::string pointerName = pointer ? pointer->str() : std::string("pointer");
|
||||
const std::string variableName = variable ? variable->str() : std::string("variable");
|
||||
reportError(pointer, Severity::warning, "autoVariablesAssignGlobalPointer",
|
||||
"Address of local variable " + variableName + " is assigned to global pointer " + pointerName +" and not reassigned before " + variableName + " goes out of scope.", CWE562, false);
|
||||
"$symbol:" + variableName + "\nAddress of local variable $symbol is assigned to global pointer " + pointerName +" and not reassigned before $symbol goes out of scope.", CWE562, false);
|
||||
}
|
||||
|
||||
void CheckAutoVariables::errorReturnAddressOfFunctionParameter(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::error, "returnAddressOfFunctionParameter",
|
||||
"Address of function parameter '" + varname + "' returned.\n"
|
||||
"Address of the function parameter '" + varname + "' becomes invalid after the function exits because "
|
||||
"$symbol:" + varname + "\n"
|
||||
"Address of function parameter '$symbol' returned.\n"
|
||||
"Address of the function parameter '$symbol' becomes invalid after the function exits because "
|
||||
"function parameters are stored on the stack which is freed when the function exits. Thus the returned "
|
||||
"value is invalid.", CWE562, false);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ static const CWE CWE788(788U); // Access of Memory Location After End of Buffer
|
|||
|
||||
static void makeArrayIndexOutOfBoundsError(std::ostream& oss, const CheckBufferOverrun::ArrayInfo &arrayInfo, const std::vector<MathLib::bigint> &index)
|
||||
{
|
||||
oss << "$symbol:" << arrayInfo.varname() << '\n';
|
||||
oss << "Array '" << arrayInfo.varname();
|
||||
for (std::size_t i = 0; i < arrayInfo.num().size(); ++i)
|
||||
oss << "[" << arrayInfo.num(i) << "]";
|
||||
|
@ -117,7 +118,8 @@ void CheckBufferOverrun::arrayIndexOutOfBoundsError(const Token *tok, const Arra
|
|||
return;
|
||||
|
||||
std::ostringstream errmsg;
|
||||
errmsg << ValueFlow::eitherTheConditionIsRedundant(condition) << " or the array '" << arrayInfo.varname();
|
||||
errmsg << "$symbol:" << arrayInfo.varname() << '\n';
|
||||
errmsg << ValueFlow::eitherTheConditionIsRedundant(condition) << " or the array '$symbol";
|
||||
for (std::size_t i = 0; i < arrayInfo.num().size(); ++i)
|
||||
errmsg << "[" << arrayInfo.num(i) << "]";
|
||||
if (index.size() == 1)
|
||||
|
@ -132,7 +134,8 @@ void CheckBufferOverrun::arrayIndexOutOfBoundsError(const Token *tok, const Arra
|
|||
reportError(errorPath, Severity::warning, "arrayIndexOutOfBoundsCond", errmsg.str(), CWE119, inconclusive);
|
||||
} else {
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "Array '" << arrayInfo.varname();
|
||||
errmsg << "$symbol:" << arrayInfo.varname() << '\n';
|
||||
errmsg << "Array '$symbol";
|
||||
for (std::size_t i = 0; i < arrayInfo.num().size(); ++i)
|
||||
errmsg << "[" << arrayInfo.num(i) << "]";
|
||||
if (index.size() == 1)
|
||||
|
@ -161,7 +164,7 @@ static std::string bufferOverrunMessage(std::string varnames)
|
|||
|
||||
std::string errmsg("Buffer is accessed out of bounds");
|
||||
if (!varnames.empty())
|
||||
errmsg += ": " + varnames;
|
||||
errmsg = "$symbol:" + varnames + '\n' + errmsg + ": " + varnames;
|
||||
else
|
||||
errmsg += ".";
|
||||
|
||||
|
@ -254,8 +257,11 @@ void CheckBufferOverrun::sizeArgumentAsCharError(const Token *tok)
|
|||
|
||||
void CheckBufferOverrun::terminateStrncpyError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
const std::string shortMessage = "The buffer '$symbol' may not be null-terminated after the call to strncpy().";
|
||||
reportError(tok, Severity::warning, "terminateStrncpy",
|
||||
"The buffer '" + varname + "' may not be null-terminated after the call to strncpy().\n"
|
||||
"$symbol:" + varname + '\n' +
|
||||
shortMessage + '\n' +
|
||||
shortMessage + ' ' +
|
||||
"If the source string's size fits or exceeds the given size, strncpy() does not add a "
|
||||
"zero at the end of the buffer. This causes bugs later in the code if the code "
|
||||
"assumes buffer is null-terminated.", CWE170, true);
|
||||
|
@ -268,7 +274,9 @@ void CheckBufferOverrun::cmdLineArgsError(const Token *tok)
|
|||
|
||||
void CheckBufferOverrun::bufferNotZeroTerminatedError(const Token *tok, const std::string &varname, const std::string &function)
|
||||
{
|
||||
const std::string errmsg = "The buffer '" + varname + "' is not null-terminated after the call to " + function + "().\n"
|
||||
const std::string errmsg = "$symbol:" + varname + '\n' +
|
||||
"$symbol:" + function + '\n' +
|
||||
"The buffer '" + varname + "' is not null-terminated after the call to " + function + "().\n"
|
||||
"The buffer '" + varname + "' is not null-terminated after the call to " + function + "(). "
|
||||
"This will cause bugs later in the code if the code assumes the buffer is null-terminated.";
|
||||
|
||||
|
@ -277,7 +285,10 @@ void CheckBufferOverrun::bufferNotZeroTerminatedError(const Token *tok, const st
|
|||
|
||||
void CheckBufferOverrun::argumentSizeError(const Token *tok, const std::string &functionName, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::warning, "argumentSize", "The array '" + varname + "' is too small, the function '" + functionName + "' expects a bigger one.", CWE398, false);
|
||||
reportError(tok, Severity::warning, "argumentSize",
|
||||
"$symbol:" + functionName + '\n' +
|
||||
"$symbol:" + varname + '\n' +
|
||||
"The array '" + varname + "' is too small, the function '" + functionName + "' expects a bigger one.", CWE398, false);
|
||||
}
|
||||
|
||||
void CheckBufferOverrun::negativeMemoryAllocationSizeError(const Token *tok)
|
||||
|
@ -1112,8 +1123,11 @@ void CheckBufferOverrun::negativeArraySize()
|
|||
|
||||
void CheckBufferOverrun::negativeArraySizeError(const Token *tok)
|
||||
{
|
||||
const std::string arrayName = tok ? tok->expressionString() : std::string();
|
||||
const std::string line1 = arrayName.empty() ? std::string() : ("$symbol:" + arrayName + '\n');
|
||||
reportError(tok, Severity::error, "negativeArraySize",
|
||||
"Declaration of array '" + (tok ? tok->str() : std::string()) + "' with negative size is undefined behaviour", CWE758, false);
|
||||
line1 +
|
||||
"Declaration of array '" + arrayName + "' with negative size is undefined behaviour", CWE758, false);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -1954,8 +1968,9 @@ void CheckBufferOverrun::arrayIndexThenCheck()
|
|||
void CheckBufferOverrun::arrayIndexThenCheckError(const Token *tok, const std::string &indexName)
|
||||
{
|
||||
reportError(tok, Severity::style, "arrayIndexThenCheck",
|
||||
"Array index '" + indexName + "' is used before limits check.\n"
|
||||
"Defensive programming: The variable '" + indexName + "' is used as an array index before it "
|
||||
"$symbol:" + indexName + "\n"
|
||||
"Array index '$symbol' is used before limits check.\n"
|
||||
"Defensive programming: The variable '$symbol' is used as an array index before it "
|
||||
"is checked that is within limits. This can mean that the array might be accessed out of bounds. "
|
||||
"Reorder conditions such as '(a[i] && i < 10)' to '(i < 10 && a[i])'. That way the array will "
|
||||
"not be accessed if the index is out of limits.", CWE398, false);
|
||||
|
|
|
@ -375,15 +375,15 @@ void CheckClass::copyConstructorMallocError(const Token *cctor, const Token *all
|
|||
void CheckClass::copyConstructorShallowCopyError(const Token *tok, const std::string& varname)
|
||||
{
|
||||
reportError(tok, Severity::style, "copyCtorPointerCopying",
|
||||
"Value of pointer '" + varname + "', which points to allocated memory, is copied in copy constructor instead of allocating new memory.", CWE398, false);
|
||||
"$symbol:" + varname + "\nValue of pointer '$symbol', which points to allocated memory, is copied in copy constructor instead of allocating new memory.", CWE398, false);
|
||||
}
|
||||
|
||||
void CheckClass::noCopyConstructorError(const Token *tok, const std::string &classname, bool isStruct)
|
||||
{
|
||||
// The constructor might be intentionally missing. Therefore this is not a "warning"
|
||||
reportError(tok, Severity::style, "noCopyConstructor",
|
||||
std::string(isStruct ? "struct" : "class") + " '" + classname +
|
||||
"' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.", CWE398, false);
|
||||
"$symbol:" + classname + "\n" +
|
||||
std::string(isStruct ? "struct" : "class") + " '$symbol' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory.", CWE398, false);
|
||||
}
|
||||
|
||||
bool CheckClass::canNotCopy(const Scope *scope)
|
||||
|
@ -802,29 +802,28 @@ void CheckClass::noConstructorError(const Token *tok, const std::string &classna
|
|||
{
|
||||
// For performance reasons the constructor might be intentionally missing. Therefore this is not a "warning"
|
||||
reportError(tok, Severity::style, "noConstructor",
|
||||
"The " + std::string(isStruct ? "struct" : "class") + " '" + classname +
|
||||
"' does not have a constructor.\n"
|
||||
"The " + std::string(isStruct ? "struct" : "class") + " '" + classname +
|
||||
"' does not have a constructor although it has private member variables. "
|
||||
"Member variables of builtin types are left uninitialized when the class is "
|
||||
"instantiated. That may cause bugs or undefined behavior.", CWE398, false);
|
||||
"$symbol:" + classname + "\n" +
|
||||
"The " + std::string(isStruct ? "struct" : "class") + " '$symbol' does not have a constructor.\n"
|
||||
"The " + std::string(isStruct ? "struct" : "class") + " '$symbol' does not have a constructor "
|
||||
"although it has private member variables. Member variables of builtin types are left "
|
||||
"uninitialized when the class is instantiated. That may cause bugs or undefined behavior.", CWE398, false);
|
||||
}
|
||||
|
||||
void CheckClass::noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct)
|
||||
{
|
||||
const std::string message(std::string(isStruct ? "Struct" : "Class") + " '" + classname + "' has a constructor with 1 argument that is not explicit.");
|
||||
const std::string message(std::string(isStruct ? "Struct" : "Class") + " '$symbol' has a constructor with 1 argument that is not explicit.");
|
||||
const std::string verbose(message + " Such constructors should in general be explicit for type safety reasons. Using the explicit keyword in the constructor means some mistakes when using the class can be avoided.");
|
||||
reportError(tok, Severity::style, "noExplicitConstructor", message + "\n" + verbose, CWE398, false);
|
||||
reportError(tok, Severity::style, "noExplicitConstructor", "$symbol:" + classname + '\n' + message + '\n' + verbose, CWE398, false);
|
||||
}
|
||||
|
||||
void CheckClass::uninitVarError(const Token *tok, bool isprivate, const std::string &classname, const std::string &varname, bool inconclusive)
|
||||
{
|
||||
reportError(tok, Severity::warning, isprivate ? "uninitMemberVarPrivate" : "uninitMemberVar", "Member variable '" + classname + "::" + varname + "' is not initialized in the constructor.", CWE398, inconclusive);
|
||||
reportError(tok, Severity::warning, isprivate ? "uninitMemberVarPrivate" : "uninitMemberVar", "$symbol:" + classname + "::" + varname + "\nMember variable '$symbol' is not initialized in the constructor.", CWE398, inconclusive);
|
||||
}
|
||||
|
||||
void CheckClass::operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive)
|
||||
{
|
||||
reportError(tok, Severity::warning, "operatorEqVarError", "Member variable '" + classname + "::" + varname + "' is not assigned a value in '" + classname + "::operator='.", CWE398, inconclusive);
|
||||
reportError(tok, Severity::warning, "operatorEqVarError", "$symbol:" + classname + "::" + varname + "\nMember variable '$symbol' is not assigned a value in '" + classname + "::operator='.", CWE398, inconclusive);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -887,10 +886,10 @@ void CheckClass::initializationListUsage()
|
|||
|
||||
void CheckClass::suggestInitializationList(const Token* tok, const std::string& varname)
|
||||
{
|
||||
reportError(tok, Severity::performance, "useInitializationList", "Variable '" + varname + "' is assigned in constructor body. Consider performing initialization in initialization list.\n"
|
||||
reportError(tok, Severity::performance, "useInitializationList", "$symbol:" + varname + "\nVariable '$symbol' is assigned in constructor body. Consider performing initialization in initialization list.\n"
|
||||
"When an object of a class is created, the constructors of all member variables are called consecutively "
|
||||
"in the order the variables are declared, even if you don't explicitly write them to the initialization list. You "
|
||||
"could avoid assigning '" + varname + "' a value by passing the value to the constructor in the initialization list.", CWE398, false);
|
||||
"could avoid assigning '$symbol' a value by passing the value to the constructor in the initialization list.", CWE398, false);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -1000,7 +999,7 @@ void CheckClass::privateFunctions()
|
|||
|
||||
void CheckClass::unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname)
|
||||
{
|
||||
reportError(tok, Severity::style, "unusedPrivateFunction", "Unused private function: '" + classname + "::" + funcname + "'", CWE398, false);
|
||||
reportError(tok, Severity::style, "unusedPrivateFunction", "$symbol:" + classname + "::" + funcname + "\nUnused private function: '$symbol'", CWE398, false);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -1164,8 +1163,9 @@ void CheckClass::mallocOnClassWarning(const Token* tok, const std::string &memfu
|
|||
toks.push_back(tok);
|
||||
toks.push_back(classTok);
|
||||
reportError(toks, Severity::warning, "mallocOnClassWarning",
|
||||
"Memory for class instance allocated with " + memfunc + "(), but class provides constructors.\n"
|
||||
"Memory for class instance allocated with " + memfunc + "(), but class provides constructors. This is unsafe, "
|
||||
"$symbol:" + memfunc +"\n"
|
||||
"Memory for class instance allocated with $symbol(), but class provides constructors.\n"
|
||||
"Memory for class instance allocated with $symbol(), but class provides constructors. This is unsafe, "
|
||||
"since no constructor is called and class members remain uninitialized. Consider using 'new' instead.", CWE762, false);
|
||||
}
|
||||
|
||||
|
@ -1175,6 +1175,8 @@ void CheckClass::mallocOnClassError(const Token* tok, const std::string &memfunc
|
|||
toks.push_back(tok);
|
||||
toks.push_back(classTok);
|
||||
reportError(toks, Severity::error, "mallocOnClassError",
|
||||
"$symbol:" + memfunc +"\n"
|
||||
"$symbol:" + classname +"\n"
|
||||
"Memory for class instance allocated with " + memfunc + "(), but class contains a " + classname + ".\n"
|
||||
"Memory for class instance allocated with " + memfunc + "(), but class a " + classname + ". This is unsafe, "
|
||||
"since no constructor is called and class members remain uninitialized. Consider using 'new' instead.", CWE665, false);
|
||||
|
@ -1183,6 +1185,8 @@ void CheckClass::mallocOnClassError(const Token* tok, const std::string &memfunc
|
|||
void CheckClass::memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type)
|
||||
{
|
||||
reportError(tok, Severity::error, "memsetClass",
|
||||
"$symbol:" + memfunc +"\n"
|
||||
"$symbol:" + classname +"\n"
|
||||
"Using '" + memfunc + "' on " + type + " that contains a " + classname + ".\n"
|
||||
"Using '" + memfunc + "' on " + type + " that contains a " + classname + " is unsafe, because constructor, destructor "
|
||||
"and copy operator calls are omitted. These are necessary for this non-POD type to ensure that a valid object "
|
||||
|
@ -1191,7 +1195,9 @@ void CheckClass::memsetError(const Token *tok, const std::string &memfunc, const
|
|||
|
||||
void CheckClass::memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type)
|
||||
{
|
||||
reportError(tok, Severity::error, "memsetClassReference", "Using '" + memfunc + "' on " + type + " that contains a reference.", CWE665, false);
|
||||
reportError(tok, Severity::error, "memsetClassReference",
|
||||
"$symbol:" + memfunc +"\n"
|
||||
"Using '" + memfunc + "' on " + type + " that contains a reference.", CWE665, false);
|
||||
}
|
||||
|
||||
void CheckClass::memsetErrorFloat(const Token *tok, const std::string &type)
|
||||
|
@ -1252,8 +1258,10 @@ void CheckClass::operatorEq()
|
|||
|
||||
void CheckClass::operatorEqReturnError(const Token *tok, const std::string &className)
|
||||
{
|
||||
reportError(tok, Severity::style, "operatorEq", "'" + className + "::operator=' should return '" + className + " &'.\n"
|
||||
"The "+className+"::operator= does not conform to standard C/C++ behaviour. To conform to standard C/C++ behaviour, return a reference to self (such as: '"+className+" &"+className+"::operator=(..) { .. return *this; }'. For safety reasons it might be better to not fix this message. If you think that safety is always more important than conformance then please ignore/suppress this message. For more details about this topic, see the book \"Effective C++\" by Scott Meyers."
|
||||
reportError(tok, Severity::style, "operatorEq",
|
||||
"$symbol:" + className +"\n"
|
||||
"'$symbol::operator=' should return '$symbol &'.\n"
|
||||
"The $symbol::operator= does not conform to standard C/C++ behaviour. To conform to standard C/C++ behaviour, return a reference to self (such as: '$symbol &$symbol::operator=(..) { .. return *this; }'. For safety reasons it might be better to not fix this message. If you think that safety is always more important than conformance then please ignore/suppress this message. For more details about this topic, see the book \"Effective C++\" by Scott Meyers."
|
||||
, CWE398, false);
|
||||
}
|
||||
|
||||
|
@ -1653,9 +1661,12 @@ void CheckClass::virtualDestructorError(const Token *tok, const std::string &Bas
|
|||
{
|
||||
if (inconclusive) {
|
||||
if (_settings->isEnabled(Settings::WARNING))
|
||||
reportError(tok, Severity::warning, "virtualDestructor", "Class '" + Base + "' which has virtual members does not have a virtual destructor.", CWE404, true);
|
||||
reportError(tok, Severity::warning, "virtualDestructor", "$symbol:" + Base + "\nClass '$symbol' which has virtual members does not have a virtual destructor.", CWE404, true);
|
||||
} else {
|
||||
reportError(tok, Severity::error, "virtualDestructor", "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor.\n"
|
||||
reportError(tok, Severity::error, "virtualDestructor",
|
||||
"$symbol:" + Base +"\n"
|
||||
"$symbol:" + Derived +"\n"
|
||||
"Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor.\n"
|
||||
"Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor. "
|
||||
"If you destroy instances of the derived class by deleting a pointer that points to the base class, only "
|
||||
"the destructor of the base class is executed. Thus, dynamic memory that is managed by the derived class "
|
||||
|
@ -2045,16 +2056,18 @@ void CheckClass::checkConstError2(const Token *tok1, const Token *tok2, const st
|
|||
toks.push_back(tok2);
|
||||
if (!suggestStatic)
|
||||
reportError(toks, Severity::style, "functionConst",
|
||||
"Technically the member function '" + classname + "::" + funcname + "' can be const.\n"
|
||||
"The member function '" + classname + "::" + funcname + "' can be made a const "
|
||||
"$symbol:" + classname + "::" + funcname +"\n"
|
||||
"Technically the member function '$symbol' can be const.\n"
|
||||
"The member function '$symbol' can be made a const "
|
||||
"function. Making this function 'const' should not cause compiler errors. "
|
||||
"Even though the function can be made const function technically it may not make "
|
||||
"sense conceptually. Think about your design and the task of the function first - is "
|
||||
"it a function that must not change object internal state?", CWE398, true);
|
||||
else
|
||||
reportError(toks, Severity::performance, "functionStatic",
|
||||
"Technically the member function '" + classname + "::" + funcname + "' can be static.\n"
|
||||
"The member function '" + classname + "::" + funcname + "' can be made a static "
|
||||
"$symbol:" + classname + "::" + funcname +"\n"
|
||||
"Technically the member function '$symbol' can be static.\n"
|
||||
"The member function '$symbol' can be made a static "
|
||||
"function. Making a function static can bring a performance benefit since no 'this' instance is "
|
||||
"passed to the function. This change should not cause compiler errors but it does not "
|
||||
"necessarily make sense conceptually. Think about your design and the task of the function first - "
|
||||
|
@ -2137,10 +2150,9 @@ void CheckClass::initializerListError(const Token *tok1, const Token *tok2, cons
|
|||
toks.push_back(tok1);
|
||||
toks.push_back(tok2);
|
||||
reportError(toks, Severity::style, "initializerList",
|
||||
"Member variable '" + classname + "::" +
|
||||
varname + "' is in the wrong place in the initializer list.\n"
|
||||
"Member variable '" + classname + "::" +
|
||||
varname + "' is in the wrong place in the initializer list. "
|
||||
"$symbol:" + classname + "::" + varname +"\n"
|
||||
"Member variable '$symbol' is in the wrong place in the initializer list.\n"
|
||||
"Member variable '$symbol' is in the wrong place in the initializer list. "
|
||||
"Members are initialized in the order they are declared, not in the "
|
||||
"order they are in the initializer list. Keeping the initializer list "
|
||||
"in the same order that the members were declared prevents order dependent "
|
||||
|
@ -2174,7 +2186,7 @@ void CheckClass::checkSelfInitialization()
|
|||
|
||||
void CheckClass::selfInitializationError(const Token* tok, const std::string& varname)
|
||||
{
|
||||
reportError(tok, Severity::error, "selfInitialization", "Member variable '" + varname + "' is initialized by itself.", CWE665, false);
|
||||
reportError(tok, Severity::error, "selfInitialization", "$symbol:" + varname + "\nMember variable '$symbol' is initialized by itself.", CWE665, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2336,8 +2348,10 @@ void CheckClass::pureVirtualFunctionCallInConstructorError(
|
|||
if (!errorPath.empty())
|
||||
errorPath.back().second = purefuncname + " is a pure virtual method without body";
|
||||
|
||||
reportError(tokStack, Severity::warning, "pureVirtualCall", "Call of pure virtual function '" + purefuncname + "' in " + scopeFunctionTypeName + ".\n"
|
||||
"Call of pure virtual function '" + purefuncname + "' in " + scopeFunctionTypeName + ". The call will fail during runtime.", CWE(0U), false);
|
||||
reportError(tokStack, Severity::warning, "pureVirtualCall",
|
||||
"$symbol:" + purefuncname +"\n"
|
||||
"Call of pure virtual function '$symbol' in " + scopeFunctionTypeName + ".\n"
|
||||
"Call of pure virtual function '$symbol' in " + scopeFunctionTypeName + ". The call will fail during runtime.", CWE(0U), false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2388,10 +2402,12 @@ void CheckClass::duplInheritedMembersError(const Token *tok1, const Token* tok2,
|
|||
toks.push_back(tok1);
|
||||
toks.push_back(tok2);
|
||||
|
||||
const std::string symbols = "$symbol:" + derivedname + "\n$symbol:" + variablename + "\n$symbol:" + basename;
|
||||
|
||||
const std::string message = "The " + std::string(derivedIsStruct ? "struct" : "class") + " '" + derivedname +
|
||||
"' defines member variable with name '" + variablename + "' also defined in its parent " +
|
||||
std::string(baseIsStruct ? "struct" : "class") + " '" + basename + "'.";
|
||||
reportError(toks, Severity::warning, "duplInheritedMember", message, CWE398, false);
|
||||
reportError(toks, Severity::warning, "duplInheritedMember", symbols + '\n' + message, CWE398, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2456,11 +2472,11 @@ void CheckClass::checkCopyCtorAndEqOperator()
|
|||
|
||||
void CheckClass::copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor)
|
||||
{
|
||||
const std::string message = "The " + std::string(isStruct ? "struct" : "class") + " '" + classname +
|
||||
"' has '" + getFunctionTypeName(hasCopyCtor ? Function::eCopyConstructor : Function::eOperatorEqual) +
|
||||
const std::string message = "$symbol:" + classname + "\n"
|
||||
"The " + std::string(isStruct ? "struct" : "class") + " '$symbol' has '" +
|
||||
getFunctionTypeName(hasCopyCtor ? Function::eCopyConstructor : Function::eOperatorEqual) +
|
||||
"' but lack of '" + getFunctionTypeName(hasCopyCtor ? Function::eOperatorEqual : Function::eCopyConstructor) +
|
||||
"'.";
|
||||
|
||||
reportError(tok, Severity::warning, "copyCtorAndEqOperator", message);
|
||||
}
|
||||
|
||||
|
@ -2506,6 +2522,7 @@ void CheckClass::checkUnsafeClassDivZero(bool test)
|
|||
|
||||
void CheckClass::unsafeClassDivZeroError(const Token *tok, const std::string &className, const std::string &methodName, const std::string &varName)
|
||||
{
|
||||
const std::string symbols = "$symbol:" + className + "\n$symbol:" + methodName + "\n$symbol:" + varName + '\n';
|
||||
const std::string s = className + "::" + methodName + "()";
|
||||
reportError(tok, Severity::style, "unsafeClassDivZero", "Public interface of " + className + " is not safe. When calling " + s + ", if parameter " + varName + " is 0 that leads to division by zero.");
|
||||
reportError(tok, Severity::style, "unsafeClassDivZero", symbols + "Public interface of " + className + " is not safe. When calling " + s + ", if parameter " + varName + " is 0 that leads to division by zero.");
|
||||
}
|
||||
|
|
|
@ -66,12 +66,14 @@ void CheckFunctions::checkProhibitedFunctions()
|
|||
if (_tokenizer->isC()) {
|
||||
if (_settings->standards.c > Standards::C89)
|
||||
reportError(tok, Severity::warning, "allocaCalled",
|
||||
"$symbol:alloca\n"
|
||||
"Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead.\n"
|
||||
"The obsolete function 'alloca' is called. In C99 and later it is recommended to use a variable length array or "
|
||||
"a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons "
|
||||
"(http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca).");
|
||||
} else
|
||||
reportError(tok, Severity::warning, "allocaCalled",
|
||||
"$symbol:alloca\n"
|
||||
"Obsolete function 'alloca' called.\n"
|
||||
"The obsolete function 'alloca' is called. In C++11 and later it is recommended to use std::array<> or "
|
||||
"a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons "
|
||||
|
@ -133,12 +135,12 @@ void CheckFunctions::invalidFunctionUsage()
|
|||
void CheckFunctions::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "$symbol:" << functionName << '\n';
|
||||
if (invalidValue && invalidValue->condition)
|
||||
errmsg << ValueFlow::eitherTheConditionIsRedundant(invalidValue->condition)
|
||||
<< " or " << functionName << "() argument nr " << argnr
|
||||
<< " can have invalid value.";
|
||||
<< " or $symbol() argument nr " << argnr << " can have invalid value.";
|
||||
else
|
||||
errmsg << "Invalid " << functionName << "() argument nr " << argnr << '.';
|
||||
errmsg << "Invalid $symbol() argument nr " << argnr << '.';
|
||||
if (invalidValue)
|
||||
errmsg << " The value is " << invalidValue->intvalue << " but the valid values are '" << validstr << "'.";
|
||||
else
|
||||
|
@ -162,7 +164,8 @@ void CheckFunctions::invalidFunctionArgError(const Token *tok, const std::string
|
|||
void CheckFunctions::invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "Invalid " << functionName << "() argument nr " << argnr << ". A non-boolean value is required.";
|
||||
errmsg << "$symbol:" << functionName << '\n';
|
||||
errmsg << "Invalid $symbol() argument nr " << argnr << ". A non-boolean value is required.";
|
||||
reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str(), CWE628, false);
|
||||
}
|
||||
|
||||
|
@ -205,7 +208,7 @@ void CheckFunctions::checkIgnoredReturnValue()
|
|||
void CheckFunctions::ignoredReturnValueError(const Token* tok, const std::string& function)
|
||||
{
|
||||
reportError(tok, Severity::warning, "ignoredReturnValue",
|
||||
"Return value of function " + function + "() is not used.", CWE252, false);
|
||||
"$symbol:" + function + "\nReturn value of function $symbol() is not used.", CWE252, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -289,9 +292,9 @@ void CheckFunctions::mathfunctionCallWarning(const Token *tok, const unsigned in
|
|||
{
|
||||
if (tok) {
|
||||
if (numParam == 1)
|
||||
reportError(tok, Severity::warning, "wrongmathcall", "Passing value " + tok->strAt(2) + " to " + tok->str() + "() leads to implementation-defined result.", CWE758, false);
|
||||
reportError(tok, Severity::warning, "wrongmathcall", "$symbol:" + tok->str() + "\nPassing value " + tok->strAt(2) + " to $symbol() leads to implementation-defined result.", CWE758, false);
|
||||
else if (numParam == 2)
|
||||
reportError(tok, Severity::warning, "wrongmathcall", "Passing values " + tok->strAt(2) + " and " + tok->strAt(4) + " to " + tok->str() + "() leads to implementation-defined result.", CWE758, false);
|
||||
reportError(tok, Severity::warning, "wrongmathcall", "$symbol:" + tok->str() + "\nPassing values " + tok->strAt(2) + " and " + tok->strAt(4) + " to $symbol() leads to implementation-defined result.", CWE758, false);
|
||||
} else
|
||||
reportError(tok, Severity::warning, "wrongmathcall", "Passing value '#' to #() leads to implementation-defined result.", CWE758, false);
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varn
|
|||
|
||||
void CheckLeakAutoVar::deallocReturnError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::error, "deallocret", "Returning/dereferencing '" + varname + "' after it is deallocated / released", CWE672, false);
|
||||
reportError(tok, Severity::error, "deallocret", "$symbol:" + varname + "\nReturning/dereferencing '$symbol' after it is deallocated / released", CWE672, false);
|
||||
}
|
||||
|
||||
void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &functionName)
|
||||
|
@ -133,9 +133,9 @@ void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &fu
|
|||
void CheckLeakAutoVar::doubleFreeError(const Token *tok, const std::string &varname, int type)
|
||||
{
|
||||
if (_settings->library.isresource(type))
|
||||
reportError(tok, Severity::error, "doubleFree", "Resource handle '" + varname + "' freed twice.", CWE415, false);
|
||||
reportError(tok, Severity::error, "doubleFree", "$symbol:" + varname + "\nResource handle '$symbol' freed twice.", CWE415, false);
|
||||
else
|
||||
reportError(tok, Severity::error, "doubleFree", "Memory pointed to by '" + varname + "' is freed twice.", CWE415, false);
|
||||
reportError(tok, Severity::error, "doubleFree", "$symbol:" + varname + "\nMemory pointed to by '$symbol' is freed twice.", CWE415, false);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -309,30 +309,30 @@ void CheckMemoryLeak::reportErr(const std::list<const Token *> &callstack, Sever
|
|||
|
||||
void CheckMemoryLeak::memleakError(const Token *tok, const std::string &varname) const
|
||||
{
|
||||
reportErr(tok, Severity::error, "memleak", "Memory leak: " + varname, CWE(401U));
|
||||
reportErr(tok, Severity::error, "memleak", "$symbol:" + varname + "\nMemory leak: $symbol", CWE(401U));
|
||||
}
|
||||
|
||||
void CheckMemoryLeak::memleakUponReallocFailureError(const Token *tok, const std::string &varname) const
|
||||
{
|
||||
reportErr(tok, Severity::error, "memleakOnRealloc", "Common realloc mistake: \'" + varname + "\' nulled but not freed upon failure", CWE(401U));
|
||||
reportErr(tok, Severity::error, "memleakOnRealloc", "$symbol:" + varname + "\nCommon realloc mistake: \'$symbol\' nulled but not freed upon failure", CWE(401U));
|
||||
}
|
||||
|
||||
void CheckMemoryLeak::resourceLeakError(const Token *tok, const std::string &varname) const
|
||||
{
|
||||
std::string errmsg("Resource leak");
|
||||
if (!varname.empty())
|
||||
errmsg += ": " + varname;
|
||||
errmsg = "$symbol:" + varname + '\n' + errmsg + ": $symbol";
|
||||
reportErr(tok, Severity::error, "resourceLeak", errmsg, CWE(775U));
|
||||
}
|
||||
|
||||
void CheckMemoryLeak::deallocDeallocError(const Token *tok, const std::string &varname) const
|
||||
{
|
||||
reportErr(tok, Severity::error, "deallocDealloc", "Deallocating a deallocated pointer: " + varname, CWE(415U));
|
||||
reportErr(tok, Severity::error, "deallocDealloc", "$symbol:" + varname + "\nDeallocating a deallocated pointer: $symbol", CWE(415U));
|
||||
}
|
||||
|
||||
void CheckMemoryLeak::deallocuseError(const Token *tok, const std::string &varname) const
|
||||
{
|
||||
reportErr(tok, Severity::error, "deallocuse", "Dereferencing '" + varname + "' after it is deallocated / released", CWE(416U));
|
||||
reportErr(tok, Severity::error, "deallocuse", "$symbol:" + varname + "\nDereferencing '$symbol' after it is deallocated / released", CWE(416U));
|
||||
}
|
||||
|
||||
void CheckMemoryLeak::mismatchSizeError(const Token *tok, const std::string &sz) const
|
||||
|
@ -342,7 +342,7 @@ void CheckMemoryLeak::mismatchSizeError(const Token *tok, const std::string &sz)
|
|||
|
||||
void CheckMemoryLeak::mismatchAllocDealloc(const std::list<const Token *> &callstack, const std::string &varname) const
|
||||
{
|
||||
reportErr(callstack, Severity::error, "mismatchAllocDealloc", "Mismatching allocation and deallocation: " + varname, CWE(762U));
|
||||
reportErr(callstack, Severity::error, "mismatchAllocDealloc", "$symbol:" + varname + "\nMismatching allocation and deallocation: $symbol", CWE(762U));
|
||||
}
|
||||
|
||||
CheckMemoryLeak::AllocType CheckMemoryLeak::functionReturnType(const Function* func, std::list<const Function*> *callstack) const
|
||||
|
@ -2386,6 +2386,8 @@ void CheckMemoryLeakInClass::unsafeClassError(const Token *tok, const std::strin
|
|||
return;
|
||||
|
||||
reportError(tok, Severity::style, "unsafeClassCanLeak",
|
||||
"$symbol:" + classname + "\n"
|
||||
"$symbol:" + varname + "\n"
|
||||
"Class '" + classname + "' is unsafe, '" + varname + "' can leak by wrong usage.\n"
|
||||
"The class '" + classname + "' is unsafe, wrong usage can cause memory/resource leaks for '" + varname + "'. This can for instance be fixed by adding proper cleanup in the destructor.", CWE398, false);
|
||||
}
|
||||
|
@ -2425,7 +2427,7 @@ void CheckMemoryLeakInClass::checkPublicFunctions(const Scope *scope, const Toke
|
|||
|
||||
void CheckMemoryLeakInClass::publicAllocationError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::warning, "publicAllocationError", "Possible leak in public function. The pointer '" + varname + "' is not deallocated before it is allocated.", CWE398, false);
|
||||
reportError(tok, Severity::warning, "publicAllocationError", "$symbol:" + varname + "\nPossible leak in public function. The pointer '$symbol' is not deallocated before it is allocated.", CWE398, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2774,14 +2776,15 @@ void CheckMemoryLeakNoVar::functionCallLeak(const Token *loc, const std::string
|
|||
|
||||
void CheckMemoryLeakNoVar::returnValueNotUsedError(const Token *tok, const std::string &alloc)
|
||||
{
|
||||
reportError(tok, Severity::error, "leakReturnValNotUsed", "Return value of allocation function '" + alloc + "' is not stored.", CWE771, false);
|
||||
reportError(tok, Severity::error, "leakReturnValNotUsed", "$symbol:" + alloc + "\nReturn value of allocation function '$symbol' is not stored.", CWE771, false);
|
||||
}
|
||||
|
||||
void CheckMemoryLeakNoVar::unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string& objType)
|
||||
{
|
||||
const std::string factoryFunc = ptrType == "shared_ptr" ? "make_shared" : "make_unique";
|
||||
reportError(tok, Severity::warning, "leakUnsafeArgAlloc",
|
||||
"Unsafe allocation. If " + funcName + "() throws, memory could be leaked. Use " + factoryFunc + "<" + objType + ">() instead.",
|
||||
"$symbol:" + funcName + "\n"
|
||||
"Unsafe allocation. If $symbol() throws, memory could be leaked. Use " + factoryFunc + "<" + objType + ">() instead.",
|
||||
CWE401,
|
||||
true); // Inconclusive because funcName may never throw
|
||||
}
|
||||
|
|
|
@ -480,8 +480,8 @@ void CheckNullPointer::nullConstantDereference()
|
|||
|
||||
void CheckNullPointer::nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive)
|
||||
{
|
||||
const std::string errmsgcond(ValueFlow::eitherTheConditionIsRedundant(value ? value->condition : nullptr) + " or there is possible null pointer dereference: " + varname + ".");
|
||||
const std::string errmsgdefarg("Possible null pointer dereference if the default parameter value is used: " + varname);
|
||||
const std::string errmsgcond("$symbol:" + varname + '\n' + ValueFlow::eitherTheConditionIsRedundant(value ? value->condition : nullptr) + " or there is possible null pointer dereference: $symbol.");
|
||||
const std::string errmsgdefarg("$symbol:" + varname + "\nPossible null pointer dereference if the default parameter value is used: $symbol");
|
||||
|
||||
if (!tok) {
|
||||
reportError(tok, Severity::error, "nullPointer", "Null pointer dereference", CWE476, false);
|
||||
|
@ -508,7 +508,7 @@ void CheckNullPointer::nullPointerError(const Token *tok, const std::string &var
|
|||
std::string errmsg;
|
||||
errmsg = std::string(value->isKnown() ? "Null" : "Possible null") + " pointer dereference";
|
||||
if (!varname.empty())
|
||||
errmsg += ": " + varname;
|
||||
errmsg = "$symbol:" + varname + '\n' + errmsg + ": $symbol";
|
||||
|
||||
reportError(errorPath,
|
||||
value->isKnown() ? Severity::error : Severity::warning,
|
||||
|
|
|
@ -134,10 +134,11 @@ void CheckOther::checkCastIntToCharAndBackError(const Token *tok, const std::str
|
|||
tok,
|
||||
Severity::warning,
|
||||
"checkCastIntToCharAndBack",
|
||||
"Storing "+ strFunctionName +"() return value in char variable and then comparing with EOF.\n"
|
||||
"When saving "+ strFunctionName +"() return value in char variable there is loss of precision. "
|
||||
" When "+ strFunctionName +"() returns EOF this value is truncated. Comparing the char "
|
||||
"variable with EOF can have unexpected results. For instance a loop \"while (EOF != (c = "+ strFunctionName +"());\" "
|
||||
"$symbol:" + strFunctionName + "\n"
|
||||
"Storing $symbol() return value in char variable and then comparing with EOF.\n"
|
||||
"When saving $symbol() return value in char variable there is loss of precision. "
|
||||
" When $symnol() returns EOF this value is truncated. Comparing the char "
|
||||
"variable with EOF can have unexpected results. For instance a loop \"while (EOF != (c = $symbol());\" "
|
||||
"loops forever on some compilers/platforms and on other compilers/platforms it will stop "
|
||||
"when the file contains a matching character.", CWE197, false
|
||||
);
|
||||
|
@ -411,9 +412,11 @@ void CheckOther::checkPipeParameterSize()
|
|||
void CheckOther::checkPipeParameterSizeError(const Token *tok, const std::string &strVarName, const std::string &strDim)
|
||||
{
|
||||
reportError(tok, Severity::error,
|
||||
"wrongPipeParameterSize", "Buffer '" + strVarName + "' must have size of 2 integers if used as parameter of pipe().\n"
|
||||
"wrongPipeParameterSize",
|
||||
"$symbol:" + strVarName + "\n"
|
||||
"Buffer '$symbol' must have size of 2 integers if used as parameter of pipe().\n"
|
||||
"The pipe()/pipe2() system command takes an argument, which is an array of exactly two integers.\n"
|
||||
"The variable '" + strVarName + "' is an array of size " + strDim + ", which does not match.", CWE686, false);
|
||||
"The variable '$symbol' is an array of size " + strDim + ", which does not match.", CWE686, false);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -711,14 +714,16 @@ void CheckOther::redundantCopyError(const Token *tok1, const Token* tok2, const
|
|||
{
|
||||
const std::list<const Token *> callstack = { tok1, tok2 };
|
||||
reportError(callstack, Severity::performance, "redundantCopy",
|
||||
"Buffer '" + var + "' is being written before its old content has been used.", CWE563, false);
|
||||
"$symbol:" + var + "\n"
|
||||
"Buffer '$symbol' is being written before its old content has been used.", CWE563, false);
|
||||
}
|
||||
|
||||
void CheckOther::redundantCopyInSwitchError(const Token *tok1, const Token* tok2, const std::string &var)
|
||||
{
|
||||
const std::list<const Token *> callstack = { tok1, tok2 };
|
||||
reportError(callstack, Severity::warning, "redundantCopyInSwitch",
|
||||
"Buffer '" + var + "' is being written before its old content has been used. 'break;' missing?", CWE563, false);
|
||||
"$symbol:" + var + "\n"
|
||||
"Buffer '$symbol' is being written before its old content has been used. 'break;' missing?", CWE563, false);
|
||||
}
|
||||
|
||||
void CheckOther::redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive)
|
||||
|
@ -726,18 +731,21 @@ void CheckOther::redundantAssignmentError(const Token *tok1, const Token* tok2,
|
|||
const std::list<const Token *> callstack = { tok1, tok2 };
|
||||
if (inconclusive)
|
||||
reportError(callstack, Severity::style, "redundantAssignment",
|
||||
"Variable '" + var + "' is reassigned a value before the old one has been used if variable is no semaphore variable.\n"
|
||||
"Variable '" + var + "' is reassigned a value before the old one has been used. Make sure that this variable is not used like a semaphore in a threading environment before simplifying this code.", CWE563, true);
|
||||
"$symbol:" + var + "\n"
|
||||
"Variable '$symbol' is reassigned a value before the old one has been used if variable is no semaphore variable.\n"
|
||||
"Variable '$symbol' is reassigned a value before the old one has been used. Make sure that this variable is not used like a semaphore in a threading environment before simplifying this code.", CWE563, true);
|
||||
else
|
||||
reportError(callstack, Severity::style, "redundantAssignment",
|
||||
"Variable '" + var + "' is reassigned a value before the old one has been used.", CWE563, false);
|
||||
"$symbol:" + var + "\n"
|
||||
"Variable '$symbol' is reassigned a value before the old one has been used.", CWE563, false);
|
||||
}
|
||||
|
||||
void CheckOther::redundantAssignmentInSwitchError(const Token *tok1, const Token* tok2, const std::string &var)
|
||||
{
|
||||
const std::list<const Token *> callstack = { tok1, tok2 };
|
||||
reportError(callstack, Severity::warning, "redundantAssignInSwitch",
|
||||
"Variable '" + var + "' is reassigned a value before the old one has been used. 'break;' missing?", CWE563, false);
|
||||
"$symbol:" + var + "\n"
|
||||
"Variable '$symbol' is reassigned a value before the old one has been used. 'break;' missing?", CWE563, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -876,7 +884,9 @@ void CheckOther::checkRedundantAssignmentInSwitch()
|
|||
void CheckOther::redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::warning,
|
||||
"redundantBitwiseOperationInSwitch", "Redundant bitwise operation on '" + varname + "' in 'switch' statement. 'break;' missing?");
|
||||
"redundantBitwiseOperationInSwitch",
|
||||
"$symbol:" + varname + "\n"
|
||||
"Redundant bitwise operation on '$symbol' in 'switch' statement. 'break;' missing?");
|
||||
}
|
||||
|
||||
|
||||
|
@ -1258,8 +1268,9 @@ void CheckOther::variableScopeError(const Token *tok, const std::string &varname
|
|||
reportError(tok,
|
||||
Severity::style,
|
||||
"variableScope",
|
||||
"The scope of the variable '" + varname + "' can be reduced.\n"
|
||||
"The scope of the variable '" + varname + "' can be reduced. Warning: Be careful "
|
||||
"$symbol:" + varname + "\n"
|
||||
"The scope of the variable '$symbol' can be reduced.\n"
|
||||
"The scope of the variable '$symbol' can be reduced. Warning: Be careful "
|
||||
"when fixing this message, especially when there are inner loops. Here is an "
|
||||
"example where cppcheck will write that the scope for 'i' can be reduced:\n"
|
||||
"void f(int x)\n"
|
||||
|
@ -1462,8 +1473,9 @@ void CheckOther::checkPassByReference()
|
|||
void CheckOther::passedByValueError(const Token *tok, const std::string &parname, bool inconclusive)
|
||||
{
|
||||
reportError(tok, Severity::performance, "passedByValue",
|
||||
"Function parameter '" + parname + "' should be passed by reference.\n"
|
||||
"Parameter '" + parname + "' is passed by value. It could be passed "
|
||||
"$symbol:" + parname + "\n"
|
||||
"Function parameter '$symbol' should be passed by reference.\n"
|
||||
"Parameter '$symbol' is passed by value. It could be passed "
|
||||
"as a (const) reference which is usually faster and recommended in C++.", CWE398, inconclusive);
|
||||
}
|
||||
|
||||
|
@ -1730,7 +1742,9 @@ void CheckOther::checkMisusedScopedObject()
|
|||
void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& varname)
|
||||
{
|
||||
reportError(tok, Severity::style,
|
||||
"unusedScopedObject", "Instance of '" + varname + "' object is destroyed immediately.", CWE563, false);
|
||||
"unusedScopedObject",
|
||||
"$symbol:" + varname + "\n"
|
||||
"Instance of '$symbol' object is destroyed immediately.", CWE563, false);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -2044,7 +2058,9 @@ void CheckOther::duplicateValueTernaryError(const Token *tok)
|
|||
void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::warning,
|
||||
"selfAssignment", "Redundant assignment of '" + varname + "' to itself.", CWE398, false);
|
||||
"selfAssignment",
|
||||
"$symbol:" + varname + "\n"
|
||||
"Redundant assignment of '$symbol' to itself.", CWE398, false);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -2092,8 +2108,9 @@ void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* to
|
|||
const struct CWE cweResult = result ? CWE571 : CWE570;
|
||||
|
||||
reportError(tok, Severity::warning, "comparisonFunctionIsAlwaysTrueOrFalse",
|
||||
"Comparison of two identical variables with " + functionName + "(" + varName + "," + varName + ") always evaluates to " + strResult + ".\n"
|
||||
"The function " + functionName + " is designed to compare two variables. Calling this function with one variable (" + varName + ") "
|
||||
"$symbol:" + functionName + "\n"
|
||||
"Comparison of two identical variables with $symbol(" + varName + "," + varName + ") always evaluates to " + strResult + ".\n"
|
||||
"The function $symbol is designed to compare two variables. Calling this function with one variable (" + varName + ") "
|
||||
"for both parameters leads to a statement which is always " + strResult + ".", cweResult, false);
|
||||
}
|
||||
|
||||
|
@ -2152,15 +2169,17 @@ void CheckOther::unsignedLessThanZeroError(const Token *tok, const std::string &
|
|||
{
|
||||
if (inconclusive) {
|
||||
reportError(tok, Severity::style, "unsignedLessThanZero",
|
||||
"Checking if unsigned variable '" + varname + "' is less than zero. This might be a false warning.\n"
|
||||
"Checking if unsigned variable '" + varname + "' is less than zero. An unsigned "
|
||||
"$symbol:" + varname + "\n"
|
||||
"Checking if unsigned variable '$symbol' is less than zero. This might be a false warning.\n"
|
||||
"Checking if unsigned variable '$symbol' is less than zero. An unsigned "
|
||||
"variable will never be negative so it is either pointless or an error to check if it is. "
|
||||
"It's not known if the used constant is a template parameter or not and therefore "
|
||||
"this message might be a false warning.", CWE570, true);
|
||||
} else {
|
||||
reportError(tok, Severity::style, "unsignedLessThanZero",
|
||||
"Checking if unsigned variable '" + varname + "' is less than zero.\n"
|
||||
"The unsigned variable '" + varname + "' will never be negative so it "
|
||||
"$symbol:" + varname + "\n"
|
||||
"Checking if unsigned variable '$symbol' is less than zero.\n"
|
||||
"The unsigned variable '$symbol' will never be negative so it "
|
||||
"is either pointless or an error to check if it is.", CWE570, false);
|
||||
}
|
||||
}
|
||||
|
@ -2175,13 +2194,15 @@ void CheckOther::unsignedPositiveError(const Token *tok, const std::string &varn
|
|||
{
|
||||
if (inconclusive) {
|
||||
reportError(tok, Severity::style, "unsignedPositive",
|
||||
"Unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it.\n"
|
||||
"The unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it. "
|
||||
"$symbol:" + varname + "\n"
|
||||
"Unsigned variable '$symbol' can't be negative so it is unnecessary to test it.\n"
|
||||
"The unsigned variable '$symbol' can't be negative so it is unnecessary to test it. "
|
||||
"It's not known if the used constant is a "
|
||||
"template parameter or not and therefore this message might be a false warning", CWE570, true);
|
||||
} else {
|
||||
reportError(tok, Severity::style, "unsignedPositive",
|
||||
"Unsigned variable '" + varname + "' can't be negative so it is unnecessary to test it.", CWE570, false);
|
||||
"$symbol:" + varname + "\n"
|
||||
"Unsigned variable '$symbol' can't be negative so it is unnecessary to test it.", CWE570, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2252,9 +2273,10 @@ void CheckOther::checkRedundantCopy()
|
|||
void CheckOther::redundantCopyError(const Token *tok,const std::string& varname)
|
||||
{
|
||||
reportError(tok, Severity::performance, "redundantCopyLocalConst",
|
||||
"Use const reference for '" + varname + "' to avoid unnecessary data copying.\n"
|
||||
"The const variable '"+varname+"' is assigned a copy of the data. You can avoid "
|
||||
"the unnecessary data copying by converting '" + varname + "' to const reference.",
|
||||
"$symbol:" + varname + "\n"
|
||||
"Use const reference for '$symbol' to avoid unnecessary data copying.\n"
|
||||
"The const variable '$symbol' is assigned a copy of the data. You can avoid "
|
||||
"the unnecessary data copying by converting '$symbol' to const reference.",
|
||||
CWE398,
|
||||
true); // since #5618 that check became inconclusive
|
||||
}
|
||||
|
@ -2359,10 +2381,14 @@ void CheckOther::incompleteArrayFillError(const Token* tok, const std::string& b
|
|||
{
|
||||
if (boolean)
|
||||
reportError(tok, Severity::portability, "incompleteArrayFill",
|
||||
"$symbol:" + buffer + "\n"
|
||||
"$symbol:" + function + "\n"
|
||||
"Array '" + buffer + "' might be filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n"
|
||||
"The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but the type 'bool' is larger than 1 on some platforms. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", CWE131, true);
|
||||
else
|
||||
reportError(tok, Severity::warning, "incompleteArrayFill",
|
||||
"$symbol:" + buffer + "\n"
|
||||
"$symbol:" + function + "\n"
|
||||
"Array '" + buffer + "' is filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n"
|
||||
"The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but an element of the given array is larger than one byte. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", CWE131, true);
|
||||
}
|
||||
|
@ -2490,7 +2516,8 @@ void CheckOther::checkRedundantPointerOp()
|
|||
void CheckOther::redundantPointerOpError(const Token* tok, const std::string &varname, bool inconclusive)
|
||||
{
|
||||
reportError(tok, Severity::style, "redundantPointerOp",
|
||||
"Redundant pointer operation on '" + varname + "' - it's already a pointer.", CWE398, inconclusive);
|
||||
"$symbol:" + varname + "\n"
|
||||
"Redundant pointer operation on '$symbol' - it's already a pointer.", CWE398, inconclusive);
|
||||
}
|
||||
|
||||
void CheckOther::checkInterlockedDecrement()
|
||||
|
@ -2564,11 +2591,13 @@ void CheckOther::unusedLabelError(const Token* tok, bool inSwitch)
|
|||
if (inSwitch) {
|
||||
if (!tok || _settings->isEnabled(Settings::WARNING))
|
||||
reportError(tok, Severity::warning, "unusedLabelSwitch",
|
||||
"Label '" + (tok ? tok->str() : emptyString) + "' is not used. Should this be a 'case' of the enclosing switch()?", CWE398, false);
|
||||
"$symbol:" + (tok ? tok->str() : emptyString) + "\n"
|
||||
"Label '$symbol' is not used. Should this be a 'case' of the enclosing switch()?", CWE398, false);
|
||||
} else {
|
||||
if (!tok || _settings->isEnabled(Settings::STYLE))
|
||||
reportError(tok, Severity::style, "unusedLabel",
|
||||
"Label '" + (tok ? tok->str() : emptyString) + "' is not used.", CWE398, false);
|
||||
"$symbol:" + (tok ? tok->str() : emptyString) + "\n"
|
||||
"Label '$symbol' is not used.", CWE398, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2740,7 +2769,7 @@ void CheckOther::accessMovedError(const Token *tok, const std::string &varname,
|
|||
default:
|
||||
return;
|
||||
}
|
||||
const std::string errmsg("Access of " + kindString + " variable '" + varname + "'.");
|
||||
const std::string errmsg("$symbol:" + varname + "\nAccess of " + kindString + " variable '$symbol'.");
|
||||
const ErrorPath errorPath = getErrorPath(tok, value, errmsg);
|
||||
reportError(errorPath, Severity::warning, errorId, errmsg, CWE672, inconclusive);
|
||||
}
|
||||
|
@ -2835,7 +2864,8 @@ void CheckOther::funcArgNamesDifferent(const std::string & functionName, size_t
|
|||
tokens.push_back(declaration);
|
||||
tokens.push_back(definition);
|
||||
reportError(tokens, Severity::style, "funcArgNamesDifferent",
|
||||
"Function '" + functionName + "' argument " + MathLib::toString(index + 1) + " names different: declaration '" +
|
||||
"$symbol:" + functionName + "\n"
|
||||
"Function '$symbol' argument " + MathLib::toString(index + 1) + " names different: declaration '" +
|
||||
(declaration ? declaration->str() : std::string("A")) + "' definition '" +
|
||||
(definition ? definition->str() : std::string("B")) + "'.", CWE628, true);
|
||||
}
|
||||
|
@ -2848,7 +2878,7 @@ void CheckOther::funcArgOrderDifferent(const std::string & functionName,
|
|||
std::list<const Token *> tokens;
|
||||
tokens.push_back(declarations.size() ? declarations[0] ? declarations[0] : declaration : nullptr);
|
||||
tokens.push_back(definitions.size() ? definitions[0] ? definitions[0] : definition : nullptr);
|
||||
std::string msg = "Function '" + functionName + "' argument order different: declaration '";
|
||||
std::string msg = "$symbol:" + functionName + "\nFunction '$symbol' argument order different: declaration '";
|
||||
for (std::size_t i = 0; i < declarations.size(); ++i) {
|
||||
if (i != 0)
|
||||
msg += ", ";
|
||||
|
|
|
@ -52,12 +52,15 @@ static const struct CWE CWE834(834U); // Excessive Iteration
|
|||
// Error message for bad iterator usage..
|
||||
void CheckStl::invalidIteratorError(const Token *tok, const std::string &iteratorName)
|
||||
{
|
||||
reportError(tok, Severity::error, "invalidIterator1", "Invalid iterator: " + iteratorName, CWE664, false);
|
||||
reportError(tok, Severity::error, "invalidIterator1", "$symbol:"+iteratorName+"\nInvalid iterator: $symbol", CWE664, false);
|
||||
}
|
||||
|
||||
void CheckStl::iteratorsError(const Token *tok, const std::string &container1, const std::string &container2)
|
||||
{
|
||||
reportError(tok, Severity::error, "iterators", "Same iterator is used with different containers '" + container1 + "' and '" + container2 + "'.", CWE664, false);
|
||||
reportError(tok, Severity::error, "iterators",
|
||||
"$symbol:" + container1 + "\n"
|
||||
"$symbol:" + container2 + "\n"
|
||||
"Same iterator is used with different containers '" + container1 + "' and '" + container2 + "'.", CWE664, false);
|
||||
}
|
||||
|
||||
// Error message used when dereferencing an iterator that has been erased..
|
||||
|
@ -68,13 +71,15 @@ void CheckStl::dereferenceErasedError(const Token *erased, const Token* deref, c
|
|||
callstack.push_back(deref);
|
||||
callstack.push_back(erased);
|
||||
reportError(callstack, Severity::error, "eraseDereference",
|
||||
"Iterator '" + itername + "' used after element has been erased.\n"
|
||||
"The iterator '" + itername + "' is invalid after the element it pointed to has been erased. "
|
||||
"$symbol:" + itername + "\n"
|
||||
"Iterator '$symbol' used after element has been erased.\n"
|
||||
"The iterator '$symbol' is invalid after the element it pointed to has been erased. "
|
||||
"Dereferencing or comparing it with another iterator is invalid operation.", CWE664, inconclusive);
|
||||
} else {
|
||||
reportError(deref, Severity::error, "eraseDereference",
|
||||
"Invalid iterator '" + itername + "' used.\n"
|
||||
"The iterator '" + itername + "' is invalid before being assigned. "
|
||||
"$symbol:" + itername + "\n"
|
||||
"Invalid iterator '$symbol' used.\n"
|
||||
"The iterator '$symbol' is invalid before being assigned. "
|
||||
"Dereferencing or comparing it with another iterator is invalid operation.", CWE664, inconclusive);
|
||||
}
|
||||
}
|
||||
|
@ -461,9 +466,9 @@ void CheckStl::stlOutOfBounds()
|
|||
void CheckStl::stlOutOfBoundsError(const Token *tok, const std::string &num, const std::string &var, bool at)
|
||||
{
|
||||
if (at)
|
||||
reportError(tok, Severity::error, "stlOutOfBounds", "When " + num + "==" + var + ".size(), " + var + ".at(" + num + ") is out of bounds.", CWE788, false);
|
||||
reportError(tok, Severity::error, "stlOutOfBounds", "$symbol:" + var + "\nWhen " + num + "==$symbol.size(), $symbol.at(" + num + ") is out of bounds.", CWE788, false);
|
||||
else
|
||||
reportError(tok, Severity::error, "stlOutOfBounds", "When " + num + "==" + var + ".size(), " + var + "[" + num + "] is out of bounds.", CWE788, false);
|
||||
reportError(tok, Severity::error, "stlOutOfBounds", "$symbol:" + var + "\nWhen " + num + "==$symbol.size(), $symbol[" + num + "] is out of bounds.", CWE788, false);
|
||||
}
|
||||
|
||||
void CheckStl::negativeIndex()
|
||||
|
@ -703,14 +708,20 @@ void CheckStl::pushback()
|
|||
// Error message for bad iterator usage..
|
||||
void CheckStl::invalidIteratorError(const Token *tok, const std::string &func, const std::string &iterator_name)
|
||||
{
|
||||
reportError(tok, Severity::error, "invalidIterator2", "After " + func + "(), the iterator '" + iterator_name + "' may be invalid.", CWE664, false);
|
||||
reportError(tok, Severity::error, "invalidIterator2",
|
||||
"$symbol:" + func + "\n"
|
||||
"$symbol:" + iterator_name + "\n"
|
||||
"After " + func + "(), the iterator '" + iterator_name + "' may be invalid.", CWE664, false);
|
||||
}
|
||||
|
||||
|
||||
// Error message for bad iterator usage..
|
||||
void CheckStl::invalidPointerError(const Token *tok, const std::string &func, const std::string &pointer_name)
|
||||
{
|
||||
reportError(tok, Severity::error, "invalidPointer", "Invalid pointer '" + pointer_name + "' after " + func + "().", CWE664, false);
|
||||
reportError(tok, Severity::error, "invalidPointer",
|
||||
"$symbol:" + func + "\n"
|
||||
"$symbol:" + pointer_name + "\n"
|
||||
"Invalid pointer '" + pointer_name + "' after " + func + "().", CWE664, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -909,10 +920,11 @@ void CheckStl::sizeError(const Token *tok)
|
|||
{
|
||||
const std::string varname = tok ? tok->str() : std::string("list");
|
||||
reportError(tok, Severity::performance, "stlSize",
|
||||
"Possible inefficient checking for '" + varname + "' emptiness.\n"
|
||||
"Checking for '" + varname + "' emptiness might be inefficient. "
|
||||
"Using " + varname + ".empty() instead of " + varname + ".size() can be faster. " +
|
||||
varname + ".size() can take linear time but " + varname + ".empty() is "
|
||||
"$symbol:" + varname + "\n"
|
||||
"Possible inefficient checking for '$symbol' emptiness.\n"
|
||||
"Checking for '$symbol' emptiness might be inefficient. "
|
||||
"Using $symbol.empty() instead of $symbol.size() can be faster. "
|
||||
"$symbol.size() can take linear time but $symbol.empty() is "
|
||||
"guaranteed to take constant time.", CWE398, false);
|
||||
}
|
||||
|
||||
|
@ -1373,9 +1385,9 @@ void CheckStl::autoPointerArrayError(const Token *tok)
|
|||
|
||||
void CheckStl::autoPointerMallocError(const Token *tok, const std::string& allocFunction)
|
||||
{
|
||||
const std::string summary = "Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with function '" + allocFunction + "'.";
|
||||
const std::string verbose = summary + " This means that you should only use 'auto_ptr' for pointers obtained with operator 'new'. This excludes use C library allocation functions (for example '" + allocFunction + "'), which must be deallocated by the appropriate C library function.";
|
||||
reportError(tok, Severity::error, "useAutoPointerMalloc", summary + "\n" + verbose, CWE762, false);
|
||||
const std::string summary = "Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with function '$symbol'.";
|
||||
const std::string verbose = summary + " This means that you should only use 'auto_ptr' for pointers obtained with operator 'new'. This excludes use C library allocation functions (for example '$symbol'), which must be deallocated by the appropriate C library function.";
|
||||
reportError(tok, Severity::error, "useAutoPointerMalloc", "$symbol:" + allocFunction + '\n' + summary + '\n' + verbose, CWE762, false);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -1435,6 +1447,8 @@ void CheckStl::uselessCalls()
|
|||
void CheckStl::uselessCallsReturnValueError(const Token *tok, const std::string &varname, const std::string &function)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "$symbol:" << varname << '\n';
|
||||
errmsg << "$symbol:" << function << '\n';
|
||||
errmsg << "It is inefficient to call '" << varname << "." << function << "(" << varname << ")' as it always returns 0.\n"
|
||||
<< "'std::string::" << function << "()' returns zero when given itself as parameter "
|
||||
<< "(" << varname << "." << function << "(" << varname << ")). As it is currently the "
|
||||
|
@ -1445,12 +1459,12 @@ void CheckStl::uselessCallsReturnValueError(const Token *tok, const std::string
|
|||
|
||||
void CheckStl::uselessCallsSwapError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
std::ostringstream errmsg;
|
||||
errmsg << "It is inefficient to swap a object with itself by calling '" << varname << ".swap(" << varname << ")'\n"
|
||||
<< "The 'swap()' function has no logical effect when given itself as parameter "
|
||||
<< "(" << varname << ".swap(" << varname << ")). As it is currently the "
|
||||
<< "code is inefficient. Is the object or the parameter wrong here?";
|
||||
reportError(tok, Severity::performance, "uselessCallsSwap", errmsg.str(), CWE628, false);
|
||||
reportError(tok, Severity::performance, "uselessCallsSwap",
|
||||
"$symbol:" + varname + "\n"
|
||||
"It is inefficient to swap a object with itself by calling '$symbol.swap($symbol)'\n"
|
||||
"The 'swap()' function has no logical effect when given itself as parameter "
|
||||
"($symbol.swap($symbol)). As it is currently the "
|
||||
"code is inefficient. Is the object or the parameter wrong here?", CWE628, false);
|
||||
}
|
||||
|
||||
void CheckStl::uselessCallsSubstrError(const Token *tok, bool empty)
|
||||
|
@ -1468,8 +1482,10 @@ void CheckStl::uselessCallsEmptyError(const Token *tok)
|
|||
|
||||
void CheckStl::uselessCallsRemoveError(const Token *tok, const std::string& function)
|
||||
{
|
||||
reportError(tok, Severity::warning, "uselessCallsRemove", "Return value of std::" + function + "() ignored. Elements remain in container.\n"
|
||||
"The return value of std::" + function + "() is ignored. This function returns an iterator to the end of the range containing those elements that should be kept. "
|
||||
reportError(tok, Severity::warning, "uselessCallsRemove",
|
||||
"$symbol:" + function + "\n"
|
||||
"Return value of std::$symbol() ignored. Elements remain in container.\n"
|
||||
"The return value of std::$symbol() is ignored. This function returns an iterator to the end of the range containing those elements that should be kept. "
|
||||
"Elements past new end remain valid but with unspecified values. Use the erase method of the container to delete them.", CWE762, false);
|
||||
}
|
||||
|
||||
|
@ -1540,8 +1556,10 @@ void CheckStl::checkDereferenceInvalidIterator()
|
|||
void CheckStl::dereferenceInvalidIteratorError(const Token* deref, const std::string &iterName)
|
||||
{
|
||||
reportError(deref, Severity::warning,
|
||||
"derefInvalidIterator", "Possible dereference of an invalid iterator: " + iterName + "\n" +
|
||||
"Make sure to check that the iterator is valid before dereferencing it - not after.", CWE825, false);
|
||||
"derefInvalidIterator",
|
||||
"$symbol:" + iterName + "\n"
|
||||
"Possible dereference of an invalid iterator: $symbol\n"
|
||||
"Possible dereference of an invalid iterator: $symbol. Make sure to check that the iterator is valid before dereferencing it - not after.", CWE825, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1668,5 +1686,6 @@ void CheckStl::readingEmptyStlContainer()
|
|||
|
||||
void CheckStl::readingEmptyStlContainerError(const Token *tok)
|
||||
{
|
||||
reportError(tok, Severity::style, "reademptycontainer", "Reading from empty STL container '" + (tok ? tok->str() : std::string("var")) + "'", CWE398, true);
|
||||
const std::string varname = tok ? tok->str() : std::string("var");
|
||||
reportError(tok, Severity::style, "reademptycontainer", "$symbol:" + varname +"\nReading from empty STL container '$symbol'", CWE398, true);
|
||||
}
|
||||
|
|
|
@ -228,13 +228,13 @@ void CheckString::checkSuspiciousStringCompare()
|
|||
void CheckString::suspiciousStringCompareError(const Token* tok, const std::string& var)
|
||||
{
|
||||
reportError(tok, Severity::warning, "literalWithCharPtrCompare",
|
||||
"String literal compared with variable '" + var + "'. Did you intend to use strcmp() instead?", CWE595, false);
|
||||
"$symbol:" + var + "\nString literal compared with variable '$symbol'. Did you intend to use strcmp() instead?", CWE595, false);
|
||||
}
|
||||
|
||||
void CheckString::suspiciousStringCompareError_char(const Token* tok, const std::string& var)
|
||||
{
|
||||
reportError(tok, Severity::warning, "charLiteralWithCharPtrCompare",
|
||||
"Char literal compared with pointer '" + var + "'. Did you intend to dereference it?", CWE595, false);
|
||||
"$symbol:" + var + "\nChar literal compared with pointer '$symbol'. Did you intend to dereference it?", CWE595, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -324,7 +324,7 @@ void CheckString::checkIncorrectStringCompare()
|
|||
|
||||
void CheckString::incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string)
|
||||
{
|
||||
reportError(tok, Severity::warning, "incorrectStringCompare", "String literal " + string + " doesn't match length argument for " + func + "().", CWE570, false);
|
||||
reportError(tok, Severity::warning, "incorrectStringCompare", "$symbol:" + func + "\nString literal " + string + " doesn't match length argument for $symbol().", CWE570, false);
|
||||
}
|
||||
|
||||
void CheckString::incorrectStringBooleanError(const Token *tok, const std::string& string)
|
||||
|
@ -453,8 +453,9 @@ void CheckString::sprintfOverlappingData()
|
|||
void CheckString::sprintfOverlappingDataError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::error, "sprintfOverlappingData",
|
||||
"Undefined behavior: Variable '" + varname + "' is used as parameter and destination in s[n]printf().\n"
|
||||
"The variable '" + varname + "' is used both as a parameter and as destination in "
|
||||
"$symbol:" + varname + "\n"
|
||||
"Undefined behavior: Variable '$symbol' is used as parameter and destination in s[n]printf().\n"
|
||||
"The variable '$symbol' is used both as a parameter and as destination in "
|
||||
"s[n]printf(). The origin and destination buffers overlap. Quote from glibc (C-library) "
|
||||
"documentation (http://www.gnu.org/software/libc/manual/html_mono/libc.html#Formatted-Output-Functions): "
|
||||
"\"If copying takes place between objects that overlap as a result of a call "
|
||||
|
|
|
@ -249,8 +249,8 @@ void CheckType::signConversionError(const Token *tok, const bool constvalue)
|
|||
Severity::warning,
|
||||
"signConversion",
|
||||
(constvalue) ?
|
||||
"Suspicious code: sign conversion of " + varname + " in calculation because '" + varname + "' has a negative value" :
|
||||
"Suspicious code: sign conversion of " + varname + " in calculation, even though " + varname + " can have a negative value", CWE195, false);
|
||||
"$symbol:" + varname + "\nSuspicious code: sign conversion of $symbol in calculation because '$symbol' has a negative value" :
|
||||
"$symbol:" + varname + "\nSuspicious code: sign conversion of $symbol in calculation, even though $symbol can have a negative value", CWE195, false);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1201,17 +1201,17 @@ bool CheckUninitVar::isMemberVariableUsage(const Token *tok, bool isPointer, All
|
|||
|
||||
void CheckUninitVar::uninitstringError(const Token *tok, const std::string &varname, bool strncpy_)
|
||||
{
|
||||
reportError(tok, Severity::error, "uninitstring", "Dangerous usage of '" + varname + "'" + (strncpy_ ? " (strncpy doesn't always null-terminate it)." : " (not null-terminated)."), CWE676, false);
|
||||
reportError(tok, Severity::error, "uninitstring", "$symbol:" + varname + "\nDangerous usage of '$symbol'" + (strncpy_ ? " (strncpy doesn't always null-terminate it)." : " (not null-terminated)."), CWE676, false);
|
||||
}
|
||||
|
||||
void CheckUninitVar::uninitdataError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::error, "uninitdata", "Memory is allocated but not initialized: " + varname, CWE908, false);
|
||||
reportError(tok, Severity::error, "uninitdata", "$symbol:" + varname + "\nMemory is allocated but not initialized: $symbol", CWE908, false);
|
||||
}
|
||||
|
||||
void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::error, "uninitvar", "Uninitialized variable: " + varname, CWE908, false);
|
||||
reportError(tok, Severity::error, "uninitvar", "$symbol:" + varname + "\nUninitialized variable: $symbol", CWE908, false);
|
||||
}
|
||||
|
||||
void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string &membername)
|
||||
|
@ -1219,7 +1219,7 @@ void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string
|
|||
reportError(tok,
|
||||
Severity::error,
|
||||
"uninitStructMember",
|
||||
"Uninitialized struct member: " + membername, CWE908, false);
|
||||
"$symbol:" + membername + "\nUninitialized struct member: $symbol", CWE908, false);
|
||||
}
|
||||
|
||||
void CheckUninitVar::valueFlowUninit()
|
||||
|
@ -1277,5 +1277,5 @@ void CheckUninitVar::deadPointerError(const Token *pointer, const Token *alias)
|
|||
reportError(pointer,
|
||||
Severity::error,
|
||||
"deadpointer",
|
||||
"Dead pointer usage. Pointer '" + strpointer + "' is dead if it has been assigned '" + stralias + "' at line " + MathLib::toString(alias ? alias->linenr() : 0U) + ".", CWE825, false);
|
||||
"$symbol:" + strpointer + "\nDead pointer usage. Pointer '$symbol' is dead if it has been assigned '" + stralias + "' at line " + MathLib::toString(alias ? alias->linenr() : 0U) + ".", CWE825, false);
|
||||
}
|
||||
|
|
|
@ -325,7 +325,7 @@ void CheckUnusedFunctions::unusedFunctionError(ErrorLogger * const errorLogger,
|
|||
locationList.push_back(fileLoc);
|
||||
}
|
||||
|
||||
const ErrorLogger::ErrorMessage errmsg(locationList, emptyString, Severity::style, "The function '" + funcname + "' is never used.", "unusedFunction", CWE561, false);
|
||||
const ErrorLogger::ErrorMessage errmsg(locationList, emptyString, Severity::style, "$symbol:" + funcname + "\nThe function '$symbol' is never used.", "unusedFunction", CWE561, false);
|
||||
if (errorLogger)
|
||||
errorLogger->reportErr(errmsg);
|
||||
else
|
||||
|
|
|
@ -1277,25 +1277,25 @@ void CheckUnusedVar::checkFunctionVariableUsage()
|
|||
|
||||
void CheckUnusedVar::unusedVariableError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::style, "unusedVariable", "Unused variable: " + varname, CWE563, false);
|
||||
reportError(tok, Severity::style, "unusedVariable", "$symbol:" + varname + "\nUnused variable: $symbol", CWE563, false);
|
||||
}
|
||||
|
||||
void CheckUnusedVar::allocatedButUnusedVariableError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::style, "unusedAllocatedMemory", "Variable '" + varname + "' is allocated memory that is never used.", CWE563, false);
|
||||
reportError(tok, Severity::style, "unusedAllocatedMemory", "$symbol:" + varname + "\nVariable '$symbol' is allocated memory that is never used.", CWE563, false);
|
||||
}
|
||||
|
||||
void CheckUnusedVar::unreadVariableError(const Token *tok, const std::string &varname, bool modified)
|
||||
{
|
||||
if (modified)
|
||||
reportError(tok, Severity::style, "unreadVariable", "Variable '" + varname + "' is modified but its new value is never used.", CWE563, false);
|
||||
reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is modified but its new value is never used.", CWE563, false);
|
||||
else
|
||||
reportError(tok, Severity::style, "unreadVariable", "Variable '" + varname + "' is assigned a value that is never used.", CWE563, false);
|
||||
reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is assigned a value that is never used.", CWE563, false);
|
||||
}
|
||||
|
||||
void CheckUnusedVar::unassignedVariableError(const Token *tok, const std::string &varname)
|
||||
{
|
||||
reportError(tok, Severity::style, "unassignedVariable", "Variable '" + varname + "' is not assigned a value.", CWE665, false);
|
||||
reportError(tok, Severity::style, "unassignedVariable", "$symbol:" + varname + "\nVariable '$symbol' is not assigned a value.", CWE665, false);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -1390,8 +1390,8 @@ void CheckUnusedVar::checkStructMemberUsage()
|
|||
|
||||
void CheckUnusedVar::unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname, bool isUnion)
|
||||
{
|
||||
const char* prefix = isUnion ? "union member '" : "struct member '";
|
||||
reportError(tok, Severity::style, "unusedStructMember", std::string(prefix) + structname + "::" + varname + "' is never used.", CWE563, false);
|
||||
const std::string prefix = isUnion ? "union member " : "struct member ";
|
||||
reportError(tok, Severity::style, "unusedStructMember", "$symbol:" + structname + "::" + varname + '\n' + prefix + "'$symbol' is never used.", CWE563, false);
|
||||
}
|
||||
|
||||
bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type)
|
||||
|
|
|
@ -728,22 +728,12 @@ void CppCheck::reportErr(const ErrorLogger::ErrorMessage &msg)
|
|||
if (std::find(_errorList.begin(), _errorList.end(), errmsg) != _errorList.end())
|
||||
return;
|
||||
|
||||
std::string file;
|
||||
unsigned int line(0);
|
||||
if (!msg._callStack.empty()) {
|
||||
file = msg._callStack.back().getfile(false);
|
||||
line = msg._callStack.back().line;
|
||||
}
|
||||
const Suppressions::ErrorMessage errorMessage = msg.toSuppressionsErrorMessage();
|
||||
|
||||
if (_useGlobalSuppressions) {
|
||||
if (_settings.nomsg.isSuppressed(msg._id, file, line))
|
||||
if (_settings.nomsg.isSuppressed(errorMessage))
|
||||
return;
|
||||
} else {
|
||||
if (_settings.nomsg.isSuppressedLocal(msg._id, file, line))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_settings.nofail.isSuppressed(msg._id, file, line) && !_settings.nomsg.isSuppressed(msg._id, file, line))
|
||||
if (!_settings.nofail.isSuppressed(errorMessage))
|
||||
exitcode = 1;
|
||||
|
||||
_errorList.push_back(errmsg);
|
||||
|
@ -767,21 +757,8 @@ void CppCheck::reportProgress(const std::string &filename, const char stage[], c
|
|||
|
||||
void CppCheck::reportInfo(const ErrorLogger::ErrorMessage &msg)
|
||||
{
|
||||
// Suppressing info message?
|
||||
std::string file;
|
||||
unsigned int line(0);
|
||||
if (!msg._callStack.empty()) {
|
||||
file = msg._callStack.back().getfile(false);
|
||||
line = msg._callStack.back().line;
|
||||
}
|
||||
if (_useGlobalSuppressions) {
|
||||
if (_settings.nomsg.isSuppressed(msg._id, file, line))
|
||||
return;
|
||||
} else {
|
||||
if (_settings.nomsg.isSuppressedLocal(msg._id, file, line))
|
||||
return;
|
||||
}
|
||||
|
||||
const Suppressions::ErrorMessage &errorMessage = msg.toSuppressionsErrorMessage();
|
||||
if (!_settings.nomsg.isSuppressed(errorMessage))
|
||||
_errorLogger.reportInfo(msg);
|
||||
}
|
||||
|
||||
|
|
|
@ -175,6 +175,16 @@ ErrorLogger::ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errms
|
|||
}
|
||||
}
|
||||
|
||||
static std::string replaceStr(std::string s, const std::string &from, const std::string &to)
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
while (std::string::npos != (pos = s.find(from,pos))) {
|
||||
s = s.substr(0, pos) + to + s.substr(pos + from.size());
|
||||
pos += to.size();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void ErrorLogger::ErrorMessage::setmsg(const std::string &msg)
|
||||
{
|
||||
// If a message ends to a '\n' and contains only a one '\n'
|
||||
|
@ -188,15 +198,33 @@ void ErrorLogger::ErrorMessage::setmsg(const std::string &msg)
|
|||
// If there is no newline then both the summary and verbose messages
|
||||
// are the given message
|
||||
const std::string::size_type pos = msg.find('\n');
|
||||
const std::string symbolName = _symbolNames.empty() ? std::string() : _symbolNames.substr(0, _symbolNames.find('\n'));
|
||||
if (pos == std::string::npos) {
|
||||
_shortMessage = msg;
|
||||
_verboseMessage = msg;
|
||||
_shortMessage = replaceStr(msg, "$symbol", symbolName);
|
||||
_verboseMessage = replaceStr(msg, "$symbol", symbolName);
|
||||
} else if (msg.compare(0,8,"$symbol:") == 0) {
|
||||
_symbolNames += msg.substr(8, pos-7);
|
||||
setmsg(msg.substr(pos + 1));
|
||||
} else {
|
||||
_shortMessage = msg.substr(0, pos);
|
||||
_verboseMessage = msg.substr(pos + 1);
|
||||
_shortMessage = replaceStr(msg.substr(0, pos), "$symbol", symbolName);
|
||||
_verboseMessage = replaceStr(msg.substr(pos + 1), "$symbol", symbolName);
|
||||
}
|
||||
}
|
||||
|
||||
Suppressions::ErrorMessage ErrorLogger::ErrorMessage::toSuppressionsErrorMessage() const
|
||||
{
|
||||
Suppressions::ErrorMessage ret;
|
||||
ret.errorId = _id;
|
||||
if (!_callStack.empty()) {
|
||||
ret.setFileName(_callStack.back().getfile(false));
|
||||
ret.lineNumber = _callStack.back().line;
|
||||
}
|
||||
ret.inconclusive = _inconclusive;
|
||||
ret.symbolNames = _symbolNames;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
std::string ErrorLogger::ErrorMessage::serialize() const
|
||||
{
|
||||
// Serialize this message into a simple string
|
||||
|
@ -378,6 +406,20 @@ std::string ErrorLogger::ErrorMessage::toXML() const
|
|||
printer.PushAttribute("info", it->getinfo().c_str());
|
||||
printer.CloseElement(false);
|
||||
}
|
||||
for (std::string::size_type pos = 0; pos < _symbolNames.size();) {
|
||||
const std::string::size_type pos2 = _symbolNames.find('\n', pos);
|
||||
std::string symbolName;
|
||||
if (pos2 == std::string::npos) {
|
||||
symbolName = _symbolNames.substr(pos);
|
||||
pos = pos2;
|
||||
} else {
|
||||
symbolName = _symbolNames.substr(pos, pos2-pos);
|
||||
pos = pos2 + 1;
|
||||
}
|
||||
printer.OpenElement("symbol", false);
|
||||
printer.PushText(symbolName.c_str());
|
||||
printer.CloseElement(false);
|
||||
}
|
||||
printer.CloseElement(false);
|
||||
return printer.CStr();
|
||||
}
|
||||
|
@ -470,20 +512,20 @@ std::string ErrorLogger::ErrorMessage::toString(bool verbose, const std::string
|
|||
}
|
||||
}
|
||||
|
||||
void ErrorLogger::reportUnmatchedSuppressions(const std::list<Suppressions::SuppressionEntry> &unmatched)
|
||||
void ErrorLogger::reportUnmatchedSuppressions(const std::list<Suppressions::Suppression> &unmatched)
|
||||
{
|
||||
// Report unmatched suppressions
|
||||
for (std::list<Suppressions::SuppressionEntry>::const_iterator i = unmatched.begin(); i != unmatched.end(); ++i) {
|
||||
for (std::list<Suppressions::Suppression>::const_iterator i = unmatched.begin(); i != unmatched.end(); ++i) {
|
||||
// don't report "unmatchedSuppression" as unmatched
|
||||
if (i->id == "unmatchedSuppression")
|
||||
if (i->errorId == "unmatchedSuppression")
|
||||
continue;
|
||||
|
||||
// check if this unmatched suppression is suppressed
|
||||
bool suppressed = false;
|
||||
for (std::list<Suppressions::SuppressionEntry>::const_iterator i2 = unmatched.begin(); i2 != unmatched.end(); ++i2) {
|
||||
if (i2->id == "unmatchedSuppression") {
|
||||
if ((i2->file == "*" || i2->file == i->file) &&
|
||||
(i2->line == 0 || i2->line == i->line)) {
|
||||
for (std::list<Suppressions::Suppression>::const_iterator i2 = unmatched.begin(); i2 != unmatched.end(); ++i2) {
|
||||
if (i2->errorId == "unmatchedSuppression") {
|
||||
if ((i2->fileName == "*" || i2->fileName == i->fileName) &&
|
||||
(i2->lineNumber == Suppressions::Suppression::NO_LINE || i2->lineNumber == i->lineNumber)) {
|
||||
suppressed = true;
|
||||
break;
|
||||
}
|
||||
|
@ -493,8 +535,10 @@ void ErrorLogger::reportUnmatchedSuppressions(const std::list<Suppressions::Supp
|
|||
if (suppressed)
|
||||
continue;
|
||||
|
||||
const std::list<ErrorLogger::ErrorMessage::FileLocation> callStack = { ErrorLogger::ErrorMessage::FileLocation(i->file, i->line) };
|
||||
reportErr(ErrorLogger::ErrorMessage(callStack, emptyString, Severity::information, "Unmatched suppression: " + i->id, "unmatchedSuppression", false));
|
||||
std::list<ErrorLogger::ErrorMessage::FileLocation> callStack;
|
||||
if (!i->fileName.empty())
|
||||
callStack.push_back(ErrorLogger::ErrorMessage::FileLocation(i->fileName, i->lineNumber));
|
||||
reportErr(ErrorLogger::ErrorMessage(callStack, emptyString, Severity::information, "Unmatched suppression: " + i->errorId, "unmatchedSuppression", false));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -288,6 +288,13 @@ public:
|
|||
return _verboseMessage;
|
||||
}
|
||||
|
||||
/** Symbol names */
|
||||
const std::string &symbolNames() const {
|
||||
return _symbolNames;
|
||||
}
|
||||
|
||||
Suppressions::ErrorMessage toSuppressionsErrorMessage() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Replace all occurrences of searchFor with replaceWith in the
|
||||
|
@ -305,6 +312,9 @@ public:
|
|||
|
||||
/** Verbose message */
|
||||
std::string _verboseMessage;
|
||||
|
||||
/** symbol names */
|
||||
std::string _symbolNames;
|
||||
};
|
||||
|
||||
ErrorLogger() { }
|
||||
|
@ -355,7 +365,7 @@ public:
|
|||
* Report list of unmatched suppressions
|
||||
* @param unmatched list of unmatched suppressions (from Settings::Suppressions::getUnmatched(Local|Global)Suppressions)
|
||||
*/
|
||||
void reportUnmatchedSuppressions(const std::list<Suppressions::SuppressionEntry> &unmatched);
|
||||
void reportUnmatchedSuppressions(const std::list<Suppressions::Suppression> &unmatched);
|
||||
|
||||
static std::string callStackToString(const std::list<ErrorLogger::ErrorMessage::FileLocation> &callStack);
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ static void inlineSuppressions(const simplecpp::TokenList &tokens, Settings &_se
|
|||
|
||||
// Add the suppressions.
|
||||
for (std::list<std::string>::const_iterator it = suppressionIDs.begin(); it != suppressionIDs.end(); ++it) {
|
||||
_settings.nomsg.addSuppression(*it, relativeFilename, tok->location.line);
|
||||
_settings.nomsg.addSuppression(Suppressions::Suppression(*it, relativeFilename, tok->location.line));
|
||||
}
|
||||
suppressionIDs.clear();
|
||||
}
|
||||
|
@ -713,9 +713,14 @@ void Preprocessor::error(const std::string &filename, unsigned int linenr, const
|
|||
void Preprocessor::missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType)
|
||||
{
|
||||
const std::string fname = Path::fromNativeSeparators(filename);
|
||||
if (_settings.nomsg.isSuppressed("missingInclude", fname, linenr))
|
||||
Suppressions::ErrorMessage errorMessage;
|
||||
errorMessage.errorId = "missingInclude";
|
||||
errorMessage.setFileName(fname);
|
||||
errorMessage.lineNumber = linenr;
|
||||
if (_settings.nomsg.isSuppressed(errorMessage))
|
||||
return;
|
||||
if (headerType == SystemHeader && _settings.nomsg.isSuppressed("missingIncludeSystem", fname, linenr))
|
||||
errorMessage.errorId = "missingIncludeSystem";
|
||||
if (headerType == SystemHeader && _settings.nomsg.isSuppressed(errorMessage))
|
||||
return;
|
||||
|
||||
if (headerType == SystemHeader)
|
||||
|
|
|
@ -18,14 +18,30 @@
|
|||
|
||||
#include "suppressions.h"
|
||||
|
||||
#include "mathlib.h"
|
||||
#include "path.h"
|
||||
|
||||
#include <tinyxml2.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype> // std::isdigit, std::isalnum, etc
|
||||
#include <stack>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
static bool isValidGlobPattern(const std::string &pattern)
|
||||
{
|
||||
for (std::string::const_iterator i = pattern.begin(); i != pattern.end(); ++i) {
|
||||
if (*i == '*' || *i == '?') {
|
||||
std::string::const_iterator j = i + 1;
|
||||
if (j != pattern.end() && (*j == '*' || *j == '?')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Suppressions::parseFile(std::istream &istr)
|
||||
{
|
||||
// Change '\r' to '\n' in the istr
|
||||
|
@ -54,48 +70,203 @@ std::string Suppressions::parseFile(std::istream &istr)
|
|||
return "";
|
||||
}
|
||||
|
||||
|
||||
std::string Suppressions::parseXmlFile(const char *filename)
|
||||
{
|
||||
tinyxml2::XMLDocument doc;
|
||||
tinyxml2::XMLError error = doc.LoadFile(filename);
|
||||
if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND)
|
||||
return "File not found";
|
||||
|
||||
const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
|
||||
for (const tinyxml2::XMLElement * e = rootnode->FirstChildElement(); e; e = e->NextSiblingElement()) {
|
||||
if (std::strcmp(e->Name(), "suppress") == 0) {
|
||||
Suppression s;
|
||||
for (const tinyxml2::XMLElement * e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) {
|
||||
const char *text = e2->GetText() ? e2->GetText() : "";
|
||||
if (std::strcmp(e2->Name(), "id") == 0)
|
||||
s.errorId = text;
|
||||
else if (std::strcmp(e2->Name(), "fileName") == 0)
|
||||
s.fileName = text;
|
||||
else if (std::strcmp(e2->Name(), "lineNumber") == 0)
|
||||
s.lineNumber = std::atoi(text);
|
||||
else if (std::strcmp(e2->Name(), "symbolName") == 0)
|
||||
s.symbolName = text;
|
||||
}
|
||||
const std::string err = addSuppression(s);
|
||||
if (!err.empty())
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string 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)) {
|
||||
Suppressions::Suppression suppression;
|
||||
if (std::getline(lineStream, suppression.errorId, ':')) {
|
||||
if (std::getline(lineStream, suppression.fileName)) {
|
||||
// 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(':');
|
||||
const std::string::size_type pos = suppression.fileName.rfind(':');
|
||||
|
||||
// if a colon is found and there is no dot after it..
|
||||
if (pos != std::string::npos &&
|
||||
file.find('.', pos) == std::string::npos) {
|
||||
suppression.fileName.find('.', pos) == std::string::npos) {
|
||||
// Try to parse out the line number
|
||||
try {
|
||||
std::istringstream istr1(file.substr(pos+1));
|
||||
istr1 >> lineNumber;
|
||||
std::istringstream istr1(suppression.fileName.substr(pos+1));
|
||||
istr1 >> suppression.lineNumber;
|
||||
} catch (...) {
|
||||
lineNumber = 0;
|
||||
suppression.lineNumber = 0;
|
||||
}
|
||||
|
||||
if (lineNumber > 0) {
|
||||
file.erase(pos);
|
||||
if (suppression.lineNumber > 0) {
|
||||
suppression.fileName.erase(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We could perhaps check if the id is valid and return error if it is not
|
||||
const std::string errmsg(addSuppression(id, Path::fromNativeSeparators(file), lineNumber));
|
||||
if (!errmsg.empty())
|
||||
return errmsg;
|
||||
suppression.fileName = Path::simplifyPath(suppression.fileName);
|
||||
|
||||
return addSuppression(suppression);
|
||||
}
|
||||
|
||||
std::string Suppressions::addSuppression(const Suppressions::Suppression &suppression)
|
||||
{
|
||||
// Check that errorId is valid..
|
||||
if (suppression.errorId.empty()) {
|
||||
return "Failed to add suppression. No id.";
|
||||
}
|
||||
if (suppression.errorId != "*") {
|
||||
for (std::string::size_type pos = 0; pos < suppression.errorId.length(); ++pos) {
|
||||
if (suppression.errorId[pos] < 0 || (!std::isalnum(suppression.errorId[pos]) && suppression.errorId[pos] != '_')) {
|
||||
return "Failed to add suppression. Invalid id \"" + suppression.errorId + "\"";
|
||||
}
|
||||
if (pos == 0 && std::isdigit(suppression.errorId[pos])) {
|
||||
return "Failed to add suppression. Invalid id \"" + suppression.errorId + "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValidGlobPattern(suppression.errorId))
|
||||
return "Failed to add suppression. Invalid glob pattern '" + suppression.errorId + "'.";
|
||||
if (!isValidGlobPattern(suppression.fileName))
|
||||
return "Failed to add suppression. Invalid glob pattern '" + suppression.fileName + "'.";
|
||||
|
||||
_suppressions.push_back(suppression);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool Suppressions::FileMatcher::match(const std::string &pattern, const std::string &name)
|
||||
void Suppressions::ErrorMessage::setFileName(const std::string &s)
|
||||
{
|
||||
_fileName = Path::simplifyPath(s);
|
||||
}
|
||||
|
||||
bool Suppressions::Suppression::isSuppressed(const Suppressions::ErrorMessage &errmsg) const
|
||||
{
|
||||
if (!errorId.empty() && !matchglob(errorId, errmsg.errorId))
|
||||
return false;
|
||||
if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName()))
|
||||
return false;
|
||||
if (lineNumber > 0 && lineNumber != errmsg.lineNumber)
|
||||
return false;
|
||||
if (!symbolName.empty()) {
|
||||
for (std::string::size_type pos = 0; pos < errmsg.symbolNames.size();) {
|
||||
const std::string::size_type pos2 = errmsg.symbolNames.find('\n',pos);
|
||||
std::string symname;
|
||||
if (pos2 == std::string::npos) {
|
||||
symname = errmsg.symbolNames.substr(pos);
|
||||
pos = pos2;
|
||||
} else {
|
||||
symname = errmsg.symbolNames.substr(pos,pos2-pos);
|
||||
pos = pos2+1;
|
||||
}
|
||||
if (matchglob(symbolName, symname))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Suppressions::Suppression::isMatch(const Suppressions::ErrorMessage &errmsg)
|
||||
{
|
||||
if (!isSuppressed(errmsg))
|
||||
return false;
|
||||
matched = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Suppressions::Suppression::getText() const
|
||||
{
|
||||
std::string ret;
|
||||
if (!errorId.empty())
|
||||
ret = errorId;
|
||||
if (!fileName.empty())
|
||||
ret += " fileName=" + fileName;
|
||||
if (lineNumber > 0)
|
||||
ret += " lineNumber=" + MathLib::toString(lineNumber);
|
||||
if (!symbolName.empty())
|
||||
ret += " symbolName=" + symbolName;
|
||||
if (ret.compare(0,1," ")==0)
|
||||
return ret.substr(1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Suppressions::isSuppressed(const Suppressions::ErrorMessage &errmsg)
|
||||
{
|
||||
const bool unmatchedSuppression(errmsg.errorId == "unmatchedSuppression");
|
||||
for (std::list<Suppression>::iterator it = _suppressions.begin(); it != _suppressions.end(); ++it) {
|
||||
Suppression &s = *it;
|
||||
if (unmatchedSuppression && s.errorId != errmsg.errorId)
|
||||
continue;
|
||||
if (s.isMatch(errmsg))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::list<Suppressions::Suppression> Suppressions::getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const
|
||||
{
|
||||
std::list<Suppression> result;
|
||||
for (std::list<Suppression>::const_iterator it = _suppressions.begin(); it != _suppressions.end(); ++it) {
|
||||
const Suppression &s = *it;
|
||||
if (s.matched)
|
||||
continue;
|
||||
if (!unusedFunctionChecking && s.errorId == "unusedFunction")
|
||||
continue;
|
||||
if (!file.empty() && !s.fileName.empty() && s.fileName != file)
|
||||
continue;
|
||||
result.push_back(s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::list<Suppressions::Suppression> Suppressions::getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const
|
||||
{
|
||||
std::list<Suppression> result;
|
||||
for (std::list<Suppression>::const_iterator it = _suppressions.begin(); it != _suppressions.end(); ++it) {
|
||||
const Suppression &s = *it;
|
||||
if (s.matched)
|
||||
continue;
|
||||
if (!unusedFunctionChecking && s.errorId == "unusedFunction")
|
||||
continue;
|
||||
if (s.fileName.find_first_of("?*") == std::string::npos)
|
||||
continue;
|
||||
result.push_back(s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Suppressions::matchglob(const std::string &pattern, const std::string &name)
|
||||
{
|
||||
const char *p = pattern.c_str();
|
||||
const char *n = name.c_str();
|
||||
|
@ -107,17 +278,17 @@ bool Suppressions::FileMatcher::match(const std::string &pattern, const std::str
|
|||
switch (*p) {
|
||||
case '*':
|
||||
// Step forward until we match the next character after *
|
||||
while (*n != '\0' && *n != p[1]) {
|
||||
while (*n != '\0' && *n != p[1] && *n != '\\' && *n != '/') {
|
||||
n++;
|
||||
}
|
||||
if (*n != '\0') {
|
||||
if (*n != '\0' && *n != '/') {
|
||||
// If this isn't the last possibility, save it for later
|
||||
backtrack.push(std::make_pair(p, n));
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
// Any character matches unless we're at the end of the name
|
||||
if (*n != '\0') {
|
||||
if (*n != '\0' && *n != '\\' && *n != '/') {
|
||||
n++;
|
||||
} else {
|
||||
matching = false;
|
||||
|
@ -127,6 +298,10 @@ bool Suppressions::FileMatcher::match(const std::string &pattern, const std::str
|
|||
// Non-wildcard characters match literally
|
||||
if (*n == *p) {
|
||||
n++;
|
||||
} else if (*n == '\\' && *p == '/') {
|
||||
n++;
|
||||
} else if (*n == '/' && *p == '\\') {
|
||||
n++;
|
||||
} else {
|
||||
matching = false;
|
||||
}
|
||||
|
@ -154,149 +329,3 @@ bool Suppressions::FileMatcher::match(const std::string &pattern, const std::str
|
|||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Suppressions::FileMatcher::addFile(const std::string &name, unsigned int line)
|
||||
{
|
||||
if (name.find_first_of("*?") != std::string::npos) {
|
||||
for (std::string::const_iterator i = name.begin(); i != name.end(); ++i) {
|
||||
if (*i == '*') {
|
||||
const std::string::const_iterator j = i + 1;
|
||||
if (j != name.end() && (*j == '*' || *j == '?')) {
|
||||
return "Failed to add suppression. Syntax error in glob.";
|
||||
}
|
||||
}
|
||||
}
|
||||
_globs[name][line] = false;
|
||||
} else if (name.empty()) {
|
||||
_globs["*"][0U] = false;
|
||||
} else {
|
||||
_files[Path::simplifyPath(name)][line] = false;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool Suppressions::FileMatcher::isSuppressed(const std::string &file, unsigned int line)
|
||||
{
|
||||
if (isSuppressedLocal(file, line))
|
||||
return true;
|
||||
|
||||
for (std::map<std::string, std::map<unsigned int, bool> >::iterator g = _globs.begin(); g != _globs.end(); ++g) {
|
||||
if (match(g->first, file)) {
|
||||
std::map<unsigned int, bool>::iterator l = g->second.find(0U);
|
||||
if (l != g->second.end()) {
|
||||
l->second = true;
|
||||
return true;
|
||||
}
|
||||
l = g->second.find(line);
|
||||
if (l != g->second.end()) {
|
||||
l->second = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Suppressions::FileMatcher::isSuppressedLocal(const std::string &file, unsigned int line)
|
||||
{
|
||||
std::map<std::string, std::map<unsigned int, bool> >::iterator f = _files.find(Path::fromNativeSeparators(file));
|
||||
if (f != _files.end()) {
|
||||
std::map<unsigned int, bool>::iterator l = f->second.find(0U);
|
||||
if (l != f->second.end()) {
|
||||
l->second = true;
|
||||
return true;
|
||||
}
|
||||
l = f->second.find(line);
|
||||
if (l != f->second.end()) {
|
||||
l->second = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Suppressions::addSuppression(const std::string &errorId, const std::string &file, unsigned int line)
|
||||
{
|
||||
// Check that errorId is valid..
|
||||
if (errorId.empty()) {
|
||||
return "Failed to add suppression. No id.";
|
||||
}
|
||||
if (errorId != "*") {
|
||||
for (std::string::size_type pos = 0; pos < errorId.length(); ++pos) {
|
||||
if (errorId[pos] < 0 || (!std::isalnum(errorId[pos]) && errorId[pos] != '_')) {
|
||||
return "Failed to add suppression. Invalid id \"" + errorId + "\"";
|
||||
}
|
||||
if (pos == 0 && std::isdigit(errorId[pos])) {
|
||||
return "Failed to add suppression. Invalid id \"" + errorId + "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _suppressions[errorId].addFile(file, line);
|
||||
}
|
||||
|
||||
bool Suppressions::isSuppressed(const std::string &errorId, const std::string &file, unsigned int line)
|
||||
{
|
||||
if (errorId != "unmatchedSuppression" && _suppressions.find("*") != _suppressions.end())
|
||||
if (_suppressions["*"].isSuppressed(file, line))
|
||||
return true;
|
||||
|
||||
std::map<std::string, FileMatcher>::iterator suppression = _suppressions.find(errorId);
|
||||
if (suppression == _suppressions.end())
|
||||
return false;
|
||||
|
||||
return suppression->second.isSuppressed(file, line);
|
||||
}
|
||||
|
||||
bool Suppressions::isSuppressedLocal(const std::string &errorId, const std::string &file, unsigned int line)
|
||||
{
|
||||
if (errorId != "unmatchedSuppression" && _suppressions.find("*") != _suppressions.end())
|
||||
if (_suppressions["*"].isSuppressedLocal(file, line))
|
||||
return true;
|
||||
|
||||
std::map<std::string, FileMatcher>::iterator suppression = _suppressions.find(errorId);
|
||||
if (suppression == _suppressions.end())
|
||||
return false;
|
||||
|
||||
return suppression->second.isSuppressedLocal(file, line);
|
||||
}
|
||||
|
||||
std::list<Suppressions::SuppressionEntry> Suppressions::getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const
|
||||
{
|
||||
std::list<SuppressionEntry> result;
|
||||
for (std::map<std::string, FileMatcher>::const_iterator i = _suppressions.begin(); i != _suppressions.end(); ++i) {
|
||||
if (!unusedFunctionChecking && i->first == "unusedFunction")
|
||||
continue;
|
||||
|
||||
const std::map<std::string, std::map<unsigned int, bool> >::const_iterator f = i->second._files.find(Path::fromNativeSeparators(file));
|
||||
if (f != i->second._files.end()) {
|
||||
for (std::map<unsigned int, bool>::const_iterator l = f->second.begin(); l != f->second.end(); ++l) {
|
||||
if (!l->second) {
|
||||
result.push_back(SuppressionEntry(i->first, f->first, l->first));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::list<Suppressions::SuppressionEntry> Suppressions::getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const
|
||||
{
|
||||
std::list<SuppressionEntry> result;
|
||||
for (std::map<std::string, FileMatcher>::const_iterator i = _suppressions.begin(); i != _suppressions.end(); ++i) {
|
||||
if (!unusedFunctionChecking && i->first == "unusedFunction")
|
||||
continue;
|
||||
|
||||
// global suppressions..
|
||||
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) {
|
||||
result.push_back(SuppressionEntry(i->first, g->first, l->first));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include <istream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
/// @addtogroup Core
|
||||
|
@ -32,52 +32,62 @@
|
|||
|
||||
/** @brief class for handling suppressions */
|
||||
class CPPCHECKLIB Suppressions {
|
||||
private:
|
||||
class CPPCHECKLIB FileMatcher {
|
||||
friend class Suppressions;
|
||||
public:
|
||||
|
||||
struct CPPCHECKLIB ErrorMessage {
|
||||
std::string errorId;
|
||||
void setFileName(const std::string &s);
|
||||
const std::string &getFileName() const {
|
||||
return _fileName;
|
||||
}
|
||||
int lineNumber;
|
||||
bool inconclusive;
|
||||
std::string symbolNames;
|
||||
private:
|
||||
/** @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.
|
||||
* @param pattern The glob pattern to match.
|
||||
* @param name The filename to match against the glob pattern.
|
||||
* @return match success
|
||||
*/
|
||||
static bool match(const std::string &pattern, const std::string &name);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Add a file or glob (and line number).
|
||||
* @param name File name or glob pattern
|
||||
* @param line Line number
|
||||
* @return error message. empty upon success
|
||||
*/
|
||||
std::string addFile(const std::string &name, unsigned int line);
|
||||
|
||||
/**
|
||||
* @brief Returns true if the file name matches a previously added file or glob pattern.
|
||||
* @param file File name to check
|
||||
* @param line Line number
|
||||
* @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 file File name to check
|
||||
* @param line Line number
|
||||
* @return true if this filename/line matches
|
||||
*/
|
||||
bool isSuppressedLocal(const std::string &file, unsigned int line);
|
||||
std::string _fileName;
|
||||
};
|
||||
|
||||
struct CPPCHECKLIB Suppression {
|
||||
Suppression() : lineNumber(NO_LINE), matched(false) {}
|
||||
Suppression(const Suppression &other) {
|
||||
*this = other;
|
||||
}
|
||||
Suppression(const std::string &id, const std::string &file, int line=NO_LINE) : errorId(id), fileName(file), lineNumber(line), matched(false) {}
|
||||
|
||||
Suppression & operator=(const Suppression &other) {
|
||||
errorId = other.errorId;
|
||||
fileName = other.fileName;
|
||||
lineNumber = other.lineNumber;
|
||||
symbolName = other.symbolName;
|
||||
matched = other.matched;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator<(const Suppression &other) const {
|
||||
if (errorId != other.errorId)
|
||||
return errorId < other.errorId;
|
||||
if (lineNumber < other.lineNumber)
|
||||
return lineNumber < other.lineNumber;
|
||||
if (fileName != other.fileName)
|
||||
return fileName < other.fileName;
|
||||
if (symbolName != other.symbolName)
|
||||
return symbolName < other.symbolName;
|
||||
};
|
||||
|
||||
bool isSuppressed(const ErrorMessage &errmsg) const;
|
||||
|
||||
bool isMatch(const ErrorMessage &errmsg);
|
||||
std::string getText() const;
|
||||
|
||||
std::string errorId;
|
||||
std::string fileName;
|
||||
int lineNumber;
|
||||
std::string symbolName;
|
||||
bool matched;
|
||||
|
||||
static const int NO_LINE = 0;
|
||||
};
|
||||
|
||||
/** @brief List of error which the user doesn't want to see. */
|
||||
std::map<std::string, FileMatcher> _suppressions;
|
||||
public:
|
||||
/**
|
||||
* @brief Don't show errors listed in the file.
|
||||
* @param istr Open file stream where errors can be read.
|
||||
|
@ -85,6 +95,13 @@ public:
|
|||
*/
|
||||
std::string parseFile(std::istream &istr);
|
||||
|
||||
/**
|
||||
* @brief Don't show errors listed in the file.
|
||||
* @param filename file name
|
||||
* @return error message. empty upon success
|
||||
*/
|
||||
std::string parseXmlFile(const char *filename);
|
||||
|
||||
/**
|
||||
* @brief Don't show the given error.
|
||||
* @param line Description of error to suppress (in id:file:line format).
|
||||
|
@ -100,47 +117,31 @@ public:
|
|||
* @param line number, e.g. "123"
|
||||
* @return error message. empty upon success
|
||||
*/
|
||||
std::string addSuppression(const std::string &errorId, const std::string &file = emptyString, unsigned int line = 0);
|
||||
std::string addSuppression(const Suppression &suppression);
|
||||
|
||||
/**
|
||||
* @brief Returns true if this message should not be shown to the user.
|
||||
* @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"
|
||||
* @param errmsg error message
|
||||
* @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, unsigned int aline)
|
||||
: id(aid), file(afile), line(aline) {
|
||||
}
|
||||
|
||||
std::string id;
|
||||
std::string file;
|
||||
unsigned int line;
|
||||
};
|
||||
bool isSuppressed(const ErrorMessage &errmsg);
|
||||
|
||||
/**
|
||||
* @brief Returns list of unmatched local (per-file) suppressions.
|
||||
* @return list of unmatched suppressions
|
||||
*/
|
||||
std::list<SuppressionEntry> getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const;
|
||||
std::list<Suppression> getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const;
|
||||
|
||||
/**
|
||||
* @brief Returns list of unmatched global (glob pattern) suppressions.
|
||||
* @return list of unmatched suppressions
|
||||
*/
|
||||
std::list<SuppressionEntry> getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const;
|
||||
std::list<Suppression> getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const;
|
||||
|
||||
static bool matchglob(const std::string &pattern, const std::string &name);
|
||||
private:
|
||||
/** @brief List of error which the user doesn't want to see. */
|
||||
std::list<Suppression> _suppressions;
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
|
|
@ -757,12 +757,20 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
static Suppressions::ErrorMessage errorMessage(const std::string &errorId, const std::string &fileName, int lineNumber) {
|
||||
Suppressions::ErrorMessage e;
|
||||
e.errorId = errorId;
|
||||
e.setFileName(fileName);
|
||||
e.lineNumber = lineNumber;
|
||||
return e;
|
||||
}
|
||||
|
||||
void suppressionSingle() {
|
||||
REDIRECT;
|
||||
const char *argv[] = {"cppcheck", "--suppress=uninitvar", "file.cpp"};
|
||||
settings = Settings();
|
||||
ASSERT(defParser.ParseFromArgs(3, argv));
|
||||
ASSERT_EQUALS(true, settings.nomsg.isSuppressed("uninitvar", "file.cpp", 1U));
|
||||
ASSERT_EQUALS(true, settings.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1)));
|
||||
}
|
||||
|
||||
void suppressionSingleFile() {
|
||||
|
@ -770,7 +778,7 @@ private:
|
|||
const char *argv[] = {"cppcheck", "--suppress=uninitvar:file.cpp", "file.cpp"};
|
||||
settings = Settings();
|
||||
ASSERT(defParser.ParseFromArgs(3, argv));
|
||||
ASSERT_EQUALS(true, settings.nomsg.isSuppressed("uninitvar", "file.cpp", 1U));
|
||||
ASSERT_EQUALS(true, settings.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1U)));
|
||||
}
|
||||
|
||||
void suppressionTwo() {
|
||||
|
@ -778,8 +786,8 @@ private:
|
|||
const char *argv[] = {"cppcheck", "--suppress=uninitvar,noConstructor", "file.cpp"};
|
||||
settings = Settings();
|
||||
TODO_ASSERT_EQUALS(true, false, defParser.ParseFromArgs(3, argv));
|
||||
TODO_ASSERT_EQUALS(true, false, settings.nomsg.isSuppressed("uninitvar", "file.cpp", 1U));
|
||||
TODO_ASSERT_EQUALS(true, false, settings.nomsg.isSuppressed("noConstructor", "file.cpp", 1U));
|
||||
TODO_ASSERT_EQUALS(true, false, settings.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1U)));
|
||||
TODO_ASSERT_EQUALS(true, false, settings.nomsg.isSuppressed(errorMessage("noConstructor", "file.cpp", 1U)));
|
||||
}
|
||||
|
||||
void suppressionTwoSeparate() {
|
||||
|
@ -787,8 +795,8 @@ private:
|
|||
const char *argv[] = {"cppcheck", "--suppress=uninitvar", "--suppress=noConstructor", "file.cpp"};
|
||||
settings = Settings();
|
||||
ASSERT(defParser.ParseFromArgs(4, argv));
|
||||
ASSERT_EQUALS(true, settings.nomsg.isSuppressed("uninitvar", "file.cpp", 1U));
|
||||
ASSERT_EQUALS(true, settings.nomsg.isSuppressed("noConstructor", "file.cpp", 1U));
|
||||
ASSERT_EQUALS(true, settings.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1U)));
|
||||
ASSERT_EQUALS(true, settings.nomsg.isSuppressed(errorMessage("noConstructor", "file.cpp", 1U)));
|
||||
}
|
||||
|
||||
void templates() {
|
||||
|
|
|
@ -297,7 +297,7 @@ private:
|
|||
}
|
||||
|
||||
void suppressUnmatchedSuppressions() {
|
||||
std::list<Suppressions::SuppressionEntry> suppressions;
|
||||
std::list<Suppressions::Suppression> suppressions;
|
||||
|
||||
// No unmatched suppression
|
||||
errout.str("");
|
||||
|
@ -308,40 +308,40 @@ private:
|
|||
// suppress all unmatchedSuppression
|
||||
errout.str("");
|
||||
suppressions.clear();
|
||||
suppressions.push_back(Suppressions::SuppressionEntry("abc", "a.c", 10U));
|
||||
suppressions.push_back(Suppressions::SuppressionEntry("unmatchedSuppression", "*", 0U));
|
||||
suppressions.push_back(Suppressions::Suppression("abc", "a.c", 10U));
|
||||
suppressions.push_back(Suppressions::Suppression("unmatchedSuppression", "*", 0U));
|
||||
reportUnmatchedSuppressions(suppressions);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
// suppress all unmatchedSuppression in a.c
|
||||
errout.str("");
|
||||
suppressions.clear();
|
||||
suppressions.push_back(Suppressions::SuppressionEntry("abc", "a.c", 10U));
|
||||
suppressions.push_back(Suppressions::SuppressionEntry("unmatchedSuppression", "a.c", 0U));
|
||||
suppressions.push_back(Suppressions::Suppression("abc", "a.c", 10U));
|
||||
suppressions.push_back(Suppressions::Suppression("unmatchedSuppression", "a.c", 0U));
|
||||
reportUnmatchedSuppressions(suppressions);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
// suppress unmatchedSuppression in a.c at line 10
|
||||
errout.str("");
|
||||
suppressions.clear();
|
||||
suppressions.push_back(Suppressions::SuppressionEntry("abc", "a.c", 10U));
|
||||
suppressions.push_back(Suppressions::SuppressionEntry("unmatchedSuppression", "a.c", 10U));
|
||||
suppressions.push_back(Suppressions::Suppression("abc", "a.c", 10U));
|
||||
suppressions.push_back(Suppressions::Suppression("unmatchedSuppression", "a.c", 10U));
|
||||
reportUnmatchedSuppressions(suppressions);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
// don't suppress unmatchedSuppression when file is mismatching
|
||||
errout.str("");
|
||||
suppressions.clear();
|
||||
suppressions.push_back(Suppressions::SuppressionEntry("abc", "a.c", 10U));
|
||||
suppressions.push_back(Suppressions::SuppressionEntry("unmatchedSuppression", "b.c", 0U));
|
||||
suppressions.push_back(Suppressions::Suppression("abc", "a.c", 10U));
|
||||
suppressions.push_back(Suppressions::Suppression("unmatchedSuppression", "b.c", 0U));
|
||||
reportUnmatchedSuppressions(suppressions);
|
||||
ASSERT_EQUALS("[a.c:10]: (information) Unmatched suppression: abc\n", errout.str());
|
||||
|
||||
// don't suppress unmatchedSuppression when line is mismatching
|
||||
errout.str("");
|
||||
suppressions.clear();
|
||||
suppressions.push_back(Suppressions::SuppressionEntry("abc", "a.c", 10U));
|
||||
suppressions.push_back(Suppressions::SuppressionEntry("unmatchedSuppression", "a.c", 1U));
|
||||
suppressions.push_back(Suppressions::Suppression("abc", "a.c", 10U));
|
||||
suppressions.push_back(Suppressions::Suppression("unmatchedSuppression", "a.c", 1U));
|
||||
reportUnmatchedSuppressions(suppressions);
|
||||
ASSERT_EQUALS("[a.c:10]: (information) Unmatched suppression: abc\n", errout.str());
|
||||
}
|
||||
|
|
|
@ -53,7 +53,11 @@ private:
|
|||
TEST_CASE(suppressingSyntaxErrors); // #7076
|
||||
TEST_CASE(suppressingSyntaxErrorsInline); // #5917
|
||||
|
||||
TEST_CASE(symbol);
|
||||
|
||||
TEST_CASE(unusedFunction);
|
||||
|
||||
TEST_CASE(matchglob);
|
||||
}
|
||||
|
||||
void suppressionsBadId1() const {
|
||||
|
@ -65,21 +69,35 @@ private:
|
|||
ASSERT_EQUALS("", suppressions.parseFile(s2));
|
||||
}
|
||||
|
||||
Suppressions::ErrorMessage errorMessage(const std::string &errorId) const {
|
||||
Suppressions::ErrorMessage ret;
|
||||
ret.errorId = errorId;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Suppressions::ErrorMessage errorMessage(const std::string &errorId, const std::string &file, int line) const {
|
||||
Suppressions::ErrorMessage ret;
|
||||
ret.errorId = errorId;
|
||||
ret.setFileName(file);
|
||||
ret.lineNumber = line;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void suppressionsDosFormat() const {
|
||||
Suppressions suppressions;
|
||||
std::istringstream s("abc\r\ndef\r\n");
|
||||
ASSERT_EQUALS("", suppressions.parseFile(s));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("abc", "test.cpp", 1));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("def", "test.cpp", 1));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("abc")));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("def")));
|
||||
}
|
||||
|
||||
void suppressionsFileNameWithColon() const {
|
||||
Suppressions suppressions;
|
||||
std::istringstream s("errorid:c:\\foo.cpp\nerrorid:c:\\bar.cpp:12");
|
||||
ASSERT_EQUALS("", suppressions.parseFile(s));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "c:/foo.cpp", 1111));
|
||||
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "c:/bar.cpp", 10));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "c:/bar.cpp", 12));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "c:/foo.cpp", 1111)));
|
||||
ASSERT_EQUALS(false, suppressions.isSuppressed(errorMessage("errorid", "c:/bar.cpp", 10)));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "c:/bar.cpp", 12)));
|
||||
}
|
||||
|
||||
void suppressionsGlob() const {
|
||||
|
@ -87,7 +105,7 @@ private:
|
|||
{
|
||||
Suppressions suppressions;
|
||||
std::istringstream s("errorid:**.cpp\n");
|
||||
ASSERT_EQUALS("Failed to add suppression. Syntax error in glob.", suppressions.parseFile(s));
|
||||
ASSERT_EQUALS("Failed to add suppression. Invalid glob pattern '**.cpp'.", suppressions.parseFile(s));
|
||||
}
|
||||
|
||||
// Check that globbing works
|
||||
|
@ -95,13 +113,13 @@ private:
|
|||
Suppressions suppressions;
|
||||
std::istringstream s("errorid:x*.cpp\nerrorid:y?.cpp\nerrorid:test.c*");
|
||||
ASSERT_EQUALS("", suppressions.parseFile(s));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 1));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp.cpp", 1));
|
||||
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "abc.cpp", 1));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "ya.cpp", 1));
|
||||
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "y.cpp", 1));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "test.c", 1));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "test.cpp", 1));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "xyz.cpp", 1)));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "xyz.cpp.cpp", 1)));
|
||||
ASSERT_EQUALS(false, suppressions.isSuppressed(errorMessage("errorid", "abc.cpp", 1)));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "ya.cpp", 1)));
|
||||
ASSERT_EQUALS(false, suppressions.isSuppressed(errorMessage("errorid", "y.cpp", 1)));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "test.c", 1)));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "test.cpp", 1)));
|
||||
}
|
||||
|
||||
// Check that both a filename match and a glob match apply
|
||||
|
@ -109,18 +127,19 @@ private:
|
|||
Suppressions suppressions;
|
||||
std::istringstream s("errorid:x*.cpp\nerrorid:xyz.cpp:1\nerrorid:a*.cpp:1\nerrorid:abc.cpp:2");
|
||||
ASSERT_EQUALS("", suppressions.parseFile(s));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 1));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 2));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "abc.cpp", 1));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "abc.cpp", 2));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "xyz.cpp", 1)));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "xyz.cpp", 2)));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "abc.cpp", 1)));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "abc.cpp", 2)));
|
||||
}
|
||||
}
|
||||
|
||||
void suppressionsFileNameWithExtraPath() const {
|
||||
// Ticket #2797
|
||||
Suppressions suppressions;
|
||||
suppressions.addSuppression("errorid", "./a.c", 123);
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "a.c", 123));
|
||||
suppressions.addSuppressionLine("errorid:./a.c:123");
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "a.c", 123)));
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed(errorMessage("errorid", "x/../a.c", 123)));
|
||||
}
|
||||
|
||||
void reportSuppressions(const Settings &settings, const std::map<std::string, std::string> &files) {
|
||||
|
@ -226,7 +245,7 @@ private:
|
|||
" b++;\n"
|
||||
"}\n",
|
||||
"uninitvar");
|
||||
ASSERT_EQUALS("[*]: (information) Unmatched suppression: uninitvar\n", errout.str());
|
||||
ASSERT_EQUALS("(information) Unmatched suppression: uninitvar\n", errout.str());
|
||||
|
||||
// suppress uninitvar for this file only
|
||||
(this->*check)("void f() {\n"
|
||||
|
@ -369,17 +388,16 @@ private:
|
|||
}
|
||||
|
||||
void suppressionsPathSeparator() const {
|
||||
Suppressions suppressions;
|
||||
suppressions.addSuppressionLine("*:test\\*");
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("someid", "test/foo/bar.cpp", 142));
|
||||
const Suppressions::Suppression s1("*", "test/foo/*");
|
||||
ASSERT_EQUALS(true, s1.isSuppressed(errorMessage("someid", "test/foo/bar.cpp", 142)));
|
||||
|
||||
suppressions.addSuppressionLine("abc:include/1.h");
|
||||
ASSERT_EQUALS(true, suppressions.isSuppressed("abc", "include\\1.h", 142));
|
||||
const Suppressions::Suppression s2("abc", "include/1.h");
|
||||
ASSERT_EQUALS(true, s2.isSuppressed(errorMessage("abc", "include/1.h", 142)));
|
||||
}
|
||||
|
||||
void inlinesuppress_unusedFunction() const { // #4210, #4946 - wrong report of "unmatchedSuppression" for "unusedFunction"
|
||||
Suppressions suppressions;
|
||||
suppressions.addSuppression("unusedFunction", "test.c", 3U);
|
||||
suppressions.addSuppression(Suppressions::Suppression("unusedFunction", "test.c", 3));
|
||||
ASSERT_EQUALS(true, !suppressions.getUnmatchedLocalSuppressions("test.c", true).empty());
|
||||
ASSERT_EQUALS(false, !suppressions.getUnmatchedGlobalSuppressions(true).empty());
|
||||
ASSERT_EQUALS(false, !suppressions.getUnmatchedLocalSuppressions("test.c", false).empty());
|
||||
|
@ -441,9 +459,48 @@ private:
|
|||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void symbol() {
|
||||
Suppressions::Suppression s;
|
||||
s.errorId = "foo";
|
||||
s.symbolName = "array*";
|
||||
|
||||
Suppressions::ErrorMessage errmsg;
|
||||
errmsg.errorId = "foo";
|
||||
errmsg.setFileName("test.cpp");
|
||||
errmsg.lineNumber = 123;
|
||||
errmsg.symbolNames = "";
|
||||
ASSERT_EQUALS(false, s.isSuppressed(errmsg));
|
||||
errmsg.symbolNames = "x\n";
|
||||
ASSERT_EQUALS(false, s.isSuppressed(errmsg));
|
||||
errmsg.symbolNames = "array1\n";
|
||||
ASSERT_EQUALS(true, s.isSuppressed(errmsg));
|
||||
errmsg.symbolNames = "x\narray2\n";
|
||||
ASSERT_EQUALS(true, s.isSuppressed(errmsg));
|
||||
errmsg.symbolNames = "array3\nx\n";
|
||||
ASSERT_EQUALS(true, s.isSuppressed(errmsg));
|
||||
|
||||
}
|
||||
|
||||
void unusedFunction() {
|
||||
ASSERT_EQUALS(0, checkSuppression("void f() {}", "unusedFunction"));
|
||||
}
|
||||
|
||||
void matchglob() {
|
||||
ASSERT_EQUALS(true, Suppressions::matchglob("*", "xyz"));
|
||||
ASSERT_EQUALS(true, Suppressions::matchglob("x*", "xyz"));
|
||||
ASSERT_EQUALS(true, Suppressions::matchglob("*z", "xyz"));
|
||||
ASSERT_EQUALS(true, Suppressions::matchglob("*y*", "xyz"));
|
||||
ASSERT_EQUALS(true, Suppressions::matchglob("*y*", "yz"));
|
||||
ASSERT_EQUALS(false, Suppressions::matchglob("*y*", "abc"));
|
||||
ASSERT_EQUALS(false, Suppressions::matchglob("*", "x/y/z"));
|
||||
ASSERT_EQUALS(true, Suppressions::matchglob("*/y/z", "x/y/z"));
|
||||
|
||||
ASSERT_EQUALS(false, Suppressions::matchglob("?", "xyz"));
|
||||
ASSERT_EQUALS(false, Suppressions::matchglob("x?", "xyz"));
|
||||
ASSERT_EQUALS(false, Suppressions::matchglob("?z", "xyz"));
|
||||
ASSERT_EQUALS(true, Suppressions::matchglob("?y?", "xyz"));
|
||||
ASSERT_EQUALS(true, Suppressions::matchglob("?/?/?", "x/y/z"));
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TEST(TestSuppressions)
|
||||
|
|
|
@ -54,7 +54,7 @@ public:
|
|||
_next->reportOut(outmsg);
|
||||
}
|
||||
virtual void reportErr(const ErrorLogger::ErrorMessage &msg) {
|
||||
if (!msg._callStack.empty() && !_settings.nomsg.isSuppressed(msg._id, msg._callStack.begin()->getfile(), msg._callStack.begin()->line))
|
||||
if (!msg._callStack.empty() && !_settings.nomsg.isSuppressed(msg.toSuppressionsErrorMessage()))
|
||||
_next->reportErr(msg);
|
||||
}
|
||||
private:
|
||||
|
|
Loading…
Reference in New Issue