diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 0a78fc653..e721b605c 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -341,6 +341,7 @@ void MainWindow::loadSettings() if (inf.exists() && inf.isReadable()) { setPath(SETTINGS_LAST_PROJECT_PATH, projectFile); mProjectFile = new ProjectFile(this); + mProjectFile->setActiveProject(); mProjectFile->read(projectFile); loadLastResults(); QDir::setCurrent(inf.absolutePath()); @@ -1508,6 +1509,7 @@ void MainWindow::loadProjectFile(const QString &filePath) mUI.mActionEditProjectFile->setEnabled(true); delete mProjectFile; mProjectFile = new ProjectFile(filePath, this); + mProjectFile->setActiveProject(); updateContractsTab(); if (!loadLastResults()) analyzeProject(mProjectFile); @@ -1630,6 +1632,7 @@ void MainWindow::newProjectFile() delete mProjectFile; mProjectFile = new ProjectFile(this); + mProjectFile->setActiveProject(); mProjectFile->setFilename(filepath); mProjectFile->setBuildDir(filename.left(filename.indexOf(".")) + "-cppcheck-build-dir"); diff --git a/gui/projectfile.cpp b/gui/projectfile.cpp index 01bdd28fa..fce386ee8 100644 --- a/gui/projectfile.cpp +++ b/gui/projectfile.cpp @@ -28,6 +28,8 @@ #include "settings.h" +ProjectFile *ProjectFile::mActiveProject; + ProjectFile::ProjectFile(QObject *parent) : QObject(parent) { @@ -603,6 +605,8 @@ void ProjectFile::readSuppressions(QXmlStreamReader &reader) suppression.lineNumber = reader.attributes().value(QString(),"lineNumber").toInt(); if (reader.attributes().hasAttribute(QString(),"symbolName")) suppression.symbolName = reader.attributes().value(QString(),"symbolName").toString().toStdString(); + if (reader.attributes().hasAttribute(QString(),"cppcheck-id")) + suppression.cppcheckId = reader.attributes().value(QString(),"cppcheck-id").toULongLong(); type = reader.readNext(); if (type == QXmlStreamReader::Characters) { suppression.errorId = reader.text().toString().toStdString(); @@ -873,6 +877,8 @@ bool ProjectFile::write(const QString &filename) xmlWriter.writeAttribute("lineNumber", QString::number(suppression.lineNumber)); if (!suppression.symbolName.empty()) xmlWriter.writeAttribute("symbolName", QString::fromStdString(suppression.symbolName)); + if (suppression.cppcheckId > 0) + xmlWriter.writeAttribute("cppcheck-id", QString::number(suppression.cppcheckId)); if (!suppression.errorId.empty()) xmlWriter.writeCharacters(QString::fromStdString(suppression.errorId)); xmlWriter.writeEndElement(); @@ -1022,3 +1028,12 @@ QString ProjectFile::getAddonFilePath(QString filesDir, const QString &addon) return QString(); } +void ProjectFile::suppressCppcheckId(std::size_t cppcheckId) +{ + if (cppcheckId > 0) { + Suppressions::Suppression s; + s.cppcheckId = cppcheckId; + mSuppressions.append(s); + write(); + } +} diff --git a/gui/projectfile.h b/gui/projectfile.h index 18772dcd8..fc1effc00 100644 --- a/gui/projectfile.h +++ b/gui/projectfile.h @@ -44,6 +44,19 @@ class ProjectFile : public QObject { public: explicit ProjectFile(QObject *parent = nullptr); explicit ProjectFile(const QString &filename, QObject *parent = nullptr); + ~ProjectFile() { + if (this == mActiveProject) mActiveProject = nullptr; + } + + static ProjectFile* getActiveProject() { + return mActiveProject; + } + void setActiveProject() { + mActiveProject = this; + } + + /** Suppress warning with Cppcheck-ID */ + void suppressCppcheckId(std::size_t cppcheckId); /** * @brief Read the project file. @@ -551,6 +564,7 @@ private: QStringList mCheckUnknownFunctionReturn; + static ProjectFile *mActiveProject; }; /// @} #endif // PROJECT_FILE_H diff --git a/gui/resultstree.cpp b/gui/resultstree.cpp index cbb1e3b93..7233a4d15 100644 --- a/gui/resultstree.cpp +++ b/gui/resultstree.cpp @@ -43,6 +43,7 @@ #include "resultstree.h" #include "report.h" #include "application.h" +#include "projectfile.h" #include "showtypes.h" #include "threadhandler.h" #include "path.h" @@ -638,6 +639,7 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e) QAction *copy = new QAction(tr("Copy"), &menu); QAction *hide = new QAction(tr("Hide"), &menu); QAction *hideallid = new QAction(tr("Hide all with id"), &menu); + QAction *suppressCppcheckID = new QAction(tr("Suppress cppcheck-id"), &menu); QAction *opencontainingfolder = new QAction(tr("Open containing folder"), &menu); if (multipleSelection) { @@ -655,6 +657,7 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e) menu.addSeparator(); menu.addAction(hide); menu.addAction(hideallid); + menu.addAction(suppressCppcheckID); if (!bughunting) { QAction *suppress = new QAction(tr("Suppress selected id(s)"), &menu); menu.addAction(suppress); @@ -667,6 +670,7 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e) connect(copy, SIGNAL(triggered()), this, SLOT(copy())); connect(hide, SIGNAL(triggered()), this, SLOT(hideResult())); connect(hideallid, SIGNAL(triggered()), this, SLOT(hideAllIdResult())); + connect(suppressCppcheckID, &QAction::triggered, this, &ResultsTree::suppressCppcheckID); connect(opencontainingfolder, SIGNAL(triggered()), this, SLOT(openContainingFolder())); if (!mTags.isEmpty()) { @@ -1031,12 +1035,35 @@ void ResultsTree::suppressSelectedIds() j++; } } + if (file->rowCount() == 0) + mModel.removeRow(file->row()); } emit suppressIds(selectedIds.toList()); } +void ResultsTree::suppressCppcheckID() +{ + if (!mSelectionModel) + return; + ProjectFile *projectFile = ProjectFile::getActiveProject(); + foreach (QModelIndex index, mSelectionModel->selectedRows()) { + QStandardItem *item = mModel.itemFromIndex(index); + if (!item->parent()) + continue; + while (item->parent()->parent()) + item = item->parent(); + const QVariantMap data = item->data().toMap(); + if (projectFile && data.contains("cppcheckId")) + projectFile->suppressCppcheckId(data["cppcheckId"].toULongLong()); + QStandardItem *fileItem = item->parent(); + fileItem->removeRow(item->row()); + if (fileItem->rowCount() == 0) + mModel.removeRow(fileItem->row()); + } +} + void ResultsTree::openContainingFolder() { QString filePath = getFilePath(mContextItem, true); diff --git a/gui/resultstree.h b/gui/resultstree.h index 55b5b709e..349ea855f 100644 --- a/gui/resultstree.h +++ b/gui/resultstree.h @@ -285,6 +285,9 @@ protected slots: /** Slot for context menu item to suppress all messages with the current message id */ void suppressSelectedIds(); + /** Slot for context menu item to suppress message with cppcheck ID */ + void suppressCppcheckID(); + /** * @brief Slot for context menu item to open the folder containing the current file. */ diff --git a/gui/xmlreportv2.cpp b/gui/xmlreportv2.cpp index 5fac01c9e..00fd096e8 100644 --- a/gui/xmlreportv2.cpp +++ b/gui/xmlreportv2.cpp @@ -219,7 +219,7 @@ ErrorItem XmlReportV2::readError(QXmlStreamReader *reader) if (attribs.hasAttribute(QString(), CWEAttribute)) item.cwe = attribs.value(QString(), CWEAttribute).toInt(); if (attribs.hasAttribute(QString(), CppcheckIdAttribute)) - item.cppcheckId = attribs.value(QString(), CWEAttribute).toULongLong(); + item.cppcheckId = attribs.value(QString(), CppcheckIdAttribute).toULongLong(); if (attribs.hasAttribute(QString(), SinceDateAttribute)) item.sinceDate = attribs.value(QString(), SinceDateAttribute).toString(); if (attribs.hasAttribute(QString(), TagsAttribute)) diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index b3d25952b..06bfbde76 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -235,6 +235,7 @@ void ErrorMessage::setmsg(const std::string &msg) Suppressions::ErrorMessage ErrorMessage::toSuppressionsErrorMessage() const { Suppressions::ErrorMessage ret; + ret.cppcheckId = cppcheckId; ret.errorId = id; if (!callStack.empty()) { ret.setFileName(callStack.back().getfile(false)); diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index 0f307b834..93bc71531 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -204,9 +204,9 @@ std::string Suppressions::addSuppressionLine(const std::string &line) std::string Suppressions::addSuppression(const Suppressions::Suppression &suppression) { // Check that errorId is valid.. - if (suppression.errorId.empty()) { + if (suppression.errorId.empty() && suppression.cppcheckId == 0) 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 || !isAcceptedErrorIdChar(suppression.errorId[pos])) { @@ -271,6 +271,8 @@ bool Suppressions::Suppression::parseComment(std::string comment, std::string *e bool Suppressions::Suppression::isSuppressed(const Suppressions::ErrorMessage &errmsg) const { + if (cppcheckId > 0 && cppcheckId != errmsg.cppcheckId) + return false; if (!errorId.empty() && !matchglob(errorId, errmsg.errorId)) return false; if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName())) @@ -371,6 +373,8 @@ std::list Suppressions::getUnmatchedLocalSuppressions for (const Suppression &s : mSuppressions) { if (s.matched) continue; + if (s.cppcheckId > 0) + continue; if (!unusedFunctionChecking && s.errorId == "unusedFunction") continue; if (file.empty() || !s.isLocal() || s.fileName != file) @@ -386,6 +390,8 @@ std::list Suppressions::getUnmatchedGlobalSuppression for (const Suppression &s : mSuppressions) { if (s.matched) continue; + if (s.cppcheckId > 0) + continue; if (!unusedFunctionChecking && s.errorId == "unusedFunction") continue; if (s.isLocal()) diff --git a/lib/suppressions.h b/lib/suppressions.h index 014e897df..5c9873e37 100644 --- a/lib/suppressions.h +++ b/lib/suppressions.h @@ -35,6 +35,7 @@ class CPPCHECKLIB Suppressions { public: struct CPPCHECKLIB ErrorMessage { + std::size_t cppcheckId; std::string errorId; void setFileName(const std::string &s); const std::string &getFileName() const { @@ -48,17 +49,18 @@ public: }; struct CPPCHECKLIB Suppression { - Suppression() : lineNumber(NO_LINE), matched(false) {} + Suppression() : lineNumber(NO_LINE), cppcheckId(0), 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(const std::string &id, const std::string &file, int line=NO_LINE) : errorId(id), fileName(file), lineNumber(line), cppcheckId(0), matched(false) {} Suppression & operator=(const Suppression &other) { errorId = other.errorId; fileName = other.fileName; lineNumber = other.lineNumber; symbolName = other.symbolName; + cppcheckId = other.cppcheckId; matched = other.matched; return *this; } @@ -72,6 +74,8 @@ public: return fileName < other.fileName; if (symbolName != other.symbolName) return symbolName < other.symbolName; + if (cppcheckId != other.cppcheckId) + return cppcheckId < other.cppcheckId; return false; } @@ -96,6 +100,7 @@ public: std::string fileName; int lineNumber; std::string symbolName; + std::size_t cppcheckId; bool matched; enum { NO_LINE = -1 };