diff --git a/gui/erroritem.cpp b/gui/erroritem.cpp index 2f4fe82ba..235667c8d 100644 --- a/gui/erroritem.cpp +++ b/gui/erroritem.cpp @@ -26,10 +26,16 @@ QErrorPathItem::QErrorPathItem(const ErrorLogger::ErrorMessage::FileLocation &lo { } +bool operator==(const QErrorPathItem &i1, const QErrorPathItem &i2) +{ + return i1.file == i2.file && i1.col == i2.col && i1.line == i2.line && i1.info == i2.info; +} + ErrorItem::ErrorItem() : severity(Severity::none) , inconclusive(false) , cwe(-1) + , tag(NONE) { } @@ -40,6 +46,7 @@ ErrorItem::ErrorItem(const ErrorLogger::ErrorMessage &errmsg) , summary(QString::fromStdString(errmsg.shortMessage())) , message(QString::fromStdString(errmsg.verboseMessage())) , cwe(errmsg._cwe.id) + , tag(NONE) { for (std::list::const_iterator loc = errmsg._callStack.begin(); loc != errmsg._callStack.end(); diff --git a/gui/erroritem.h b/gui/erroritem.h index 291d590aa..3066ed33a 100644 --- a/gui/erroritem.h +++ b/gui/erroritem.h @@ -59,6 +59,8 @@ public: QString info; }; +bool operator==(const QErrorPathItem &i1, const QErrorPathItem &i2); + /** * @brief A class containing error data for one error. * @@ -86,6 +88,10 @@ public: QString message; int cwe; QList errorPath; + + // Special GUI properties + QString sinceDate; + enum Tag { NONE, FP, IGNORE, BUG } tag; }; Q_DECLARE_METATYPE(ErrorItem); @@ -103,6 +109,8 @@ public: Severity::SeverityType severity; QString summary; QString message; + QString sinceDate; + ErrorItem::Tag tag; }; /// @} diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 8277645e7..e9ba42aa2 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -872,6 +872,7 @@ void MainWindow::analysisDone() const QString buildDir = prjpath + '/' + mProjectFile->getBuildDir(); if (QDir(buildDir).exists()) { mUI.mResults->saveStatistics(buildDir + "/statistics.txt"); + mUI.mResults->updateFromOldReport(buildDir + "/lastResults.xml"); mUI.mResults->save(buildDir + "/lastResults.xml", Report::XMLV2); } } @@ -1278,6 +1279,13 @@ void MainWindow::stopAnalysis() { mThread->stop(); mUI.mResults->disableProgressbar(); + if (mProjectFile && !mProjectFile->getBuildDir().isEmpty()) { + const QString prjpath = QFileInfo(mProjectFile->getFilename()).absolutePath(); + const QString buildDir = prjpath + '/' + mProjectFile->getBuildDir(); + if (QDir(buildDir).exists()) { + mUI.mResults->updateFromOldReport(buildDir + "/lastResults.xml"); + } + } } void MainWindow::openHelpContents() diff --git a/gui/resultstree.cpp b/gui/resultstree.cpp index 0a76cc1c6..347473f25 100644 --- a/gui/resultstree.cpp +++ b/gui/resultstree.cpp @@ -47,6 +47,11 @@ #include "showtypes.h" #include "threadhandler.h" #include "path.h" +#include "xmlreportv2.h" + +// These must match column headers given in ResultsTree::translate() +static const unsigned int COLUMN_SINCE_DATE = 6; +static const unsigned int COLUMN_TAG = 7; ResultsTree::ResultsTree(QWidget * parent) : QTreeView(parent), @@ -154,6 +159,8 @@ bool ResultsTree::addErrorItem(const ErrorItem &item) line.summary = item.summary; line.message = item.message; line.severity = item.severity; + line.sinceDate = item.sinceDate; + line.tag = item.tag; //Create the base item for the error and ensure it has a proper //file item as a parent QStandardItem* fileItem = ensureFileItem(item.errorPath.back().file, item.file0, hide); @@ -232,7 +239,22 @@ QStandardItem *ResultsTree::addBacktraceFiles(QStandardItem *parent, << createLineNumberItem(QString::number(item.line)) << createNormalItem(childOfMessage ? QString() : item.errorId) << (childOfMessage ? createNormalItem(QString()) : createCheckboxItem(item.inconclusive)) - << createNormalItem(item.summary); + << createNormalItem(item.summary) + << createNormalItem(item.sinceDate); + switch (item.tag) { + case ErrorItem::NONE: + list << createNormalItem(""); + break; + case ErrorItem::FP: + list << createNormalItem("fp"); + break; + case ErrorItem::IGNORE: + list << createNormalItem("ignore"); + break; + case ErrorItem::BUG: + list << createNormalItem("bug"); + break; + }; //TODO message has parameter names so we'll need changes to the core //cppcheck so we can get proper translations @@ -1003,12 +1025,86 @@ void ResultsTree::saveErrors(Report *report, QStandardItem *fileItem) const } } + +QList ResultsTree::getAllErrorItems() const +{ + QList ret; + for (int i = 0; i < mModel.rowCount(); i++) { + const QStandardItem *item = mModel.item(i,0); + for (int j = 0; j < item->rowCount(); j++) { + const QStandardItem *error = item->child(j,0); + ErrorItem errorItem; + readErrorItem(error, &errorItem); + ret << errorItem; + } + } + return ret; +} + +static int indexOf(const QList &list, const ErrorItem &item) +{ + for (int i = 0; i < list.size(); i++) { + if (list[i].errorId == item.errorId && + list[i].errorPath == item.errorPath && + list[i].file0 == item.file0 && + list[i].message == item.message && + list[i].inconclusive == item.inconclusive && + list[i].severity == item.severity) { + return i; + } + } + return -1; +} + +void ResultsTree::updateFromOldReport(const QString &filename) +{ + QList oldErrors; + XmlReportV2 oldReport(filename); + if (oldReport.open()) { + oldErrors = oldReport.read(); + oldReport.close(); + } + + // Read current results.. + for (int i = 0; i < mModel.rowCount(); i++) { + QStandardItem *fileItem = mModel.item(i,0); + for (int j = 0; j < fileItem->rowCount(); j++) { + QStandardItem *error = fileItem->child(j,0); + ErrorItem errorItem; + readErrorItem(error, &errorItem); + int oldErrorIndex = indexOf(oldErrors, errorItem); + QVariantMap data = error->data().toMap(); + + // New error .. set the "sinceDate" property + if (oldErrorIndex < 0 || data["sinceDate"].toString().isEmpty()) { + const QString sinceDate = QDate::currentDate().toString(Qt::SystemLocaleShortDate); + data["sinceDate"] = sinceDate; + error->setData(data); + fileItem->child(j, COLUMN_SINCE_DATE)->setText(sinceDate); + if (oldErrorIndex < 0) + continue; + } + + if (errorItem.tag != ErrorItem::NONE) + continue; + + const ErrorItem &oldErrorItem = oldErrors[oldErrorIndex]; + + if (oldErrorItem.tag == ErrorItem::FP) + data["tag"] = "fp"; + else if (oldErrorItem.tag == ErrorItem::IGNORE) + data["tag"] = "ignore"; + else if (oldErrorItem.tag == ErrorItem::BUG) + data["tag"] = "bug"; + error->setData(data); + } + } +} + void ResultsTree::readErrorItem(const QStandardItem *error, ErrorItem *item) const { - //Get error's user data - QVariant userdata = error->data(); - //Convert it to QVariantMap - QVariantMap data = userdata.toMap(); + // Get error's user data + QVariantMap data = error->data().toMap(); item->severity = ShowTypes::ShowTypeToSeverity(ShowTypes::VariantToShowType(data["severity"])); item->summary = data["summary"].toString(); @@ -1016,6 +1112,14 @@ void ResultsTree::readErrorItem(const QStandardItem *error, ErrorItem *item) con item->errorId = data["id"].toString(); item->inconclusive = data["inconclusive"].toBool(); item->file0 = data["file0"].toString(); + item->sinceDate = data["sinceDate"].toString(); + QString tag = data["tag"].toString(); + if (tag == "fp") + item->tag = ErrorItem::FP; + else if (tag == "ignore") + item->tag = ErrorItem::IGNORE; + else if (tag == "bug") + item->tag = ErrorItem::BUG; if (error->rowCount() == 0) { QErrorPathItem e; @@ -1161,7 +1265,7 @@ bool ResultsTree::hasResults() const void ResultsTree::translate() { QStringList labels; - labels << tr("File") << tr("Severity") << tr("Line") << tr("Id") << tr("Inconclusive") << tr("Summary"); + labels << tr("File") << tr("Severity") << tr("Line") << tr("Id") << tr("Inconclusive") << tr("Summary") << tr("Since date") << tr("Tag"); mModel.setHorizontalHeaderLabels(labels); //TODO go through all the errors in the tree and translate severity and message } diff --git a/gui/resultstree.h b/gui/resultstree.h index 7c165c8e5..fbec7a937 100644 --- a/gui/resultstree.h +++ b/gui/resultstree.h @@ -103,6 +103,16 @@ public: */ void saveResults(Report *report) const; + /** + * @brief Get all error items + */ + QList getAllErrorItems() const; + + /** + * @brief Update items from old report (tag, sinceDate) + */ + void updateFromOldReport(const QString &filename); + /** * @brief Update tree settings * diff --git a/gui/resultsview.cpp b/gui/resultsview.cpp index 635850ca3..b0fdaab02 100644 --- a/gui/resultsview.cpp +++ b/gui/resultsview.cpp @@ -149,6 +149,11 @@ void ResultsView::saveStatistics(const QString &filename) const ts << "portability:" << mStatistics->getCount(ShowTypes::ShowPortability) << '\n'; } +void ResultsView::updateFromOldReport(const QString &filename) const +{ + mUI.mTree->updateFromOldReport(filename); +} + void ResultsView::save(const QString &filename, Report::Type type) const { if (!hasResults()) { diff --git a/gui/resultsview.h b/gui/resultsview.h index 750017e14..9aa24a810 100644 --- a/gui/resultsview.h +++ b/gui/resultsview.h @@ -88,6 +88,11 @@ public: */ void save(const QString &filename, Report::Type type) const; + /** + * @brief Update results from old report (tag, sinceDate) + */ + void updateFromOldReport(const QString &filename) const; + /** * @brief Update tree settings * diff --git a/gui/xmlreportv2.cpp b/gui/xmlreportv2.cpp index d5e244091..2b80e9fbe 100644 --- a/gui/xmlreportv2.cpp +++ b/gui/xmlreportv2.cpp @@ -35,6 +35,8 @@ static const char ErrorsElementName[] = "errors"; static const char LocationElementName[] = "location"; static const char ColAttribute[] = "col"; static const char CWEAttribute[] = "cwe"; +static const char SinceDateAttribute[] = "sinceDate"; +static const char TagAttribute[] = "tag"; static const char FilenameAttribute[] = "file"; static const char IncludedFromFilenameAttribute[] = "file0"; static const char InconclusiveAttribute[] = "inconclusive"; @@ -120,6 +122,14 @@ void XmlReportV2::writeError(const ErrorItem &error) mXmlWriter->writeAttribute(InconclusiveAttribute, "true"); if (error.cwe > 0) mXmlWriter->writeAttribute(CWEAttribute, QString::number(error.cwe)); + if (!error.sinceDate.isEmpty()) + mXmlWriter->writeAttribute(SinceDateAttribute, error.sinceDate); + if (error.tag == ErrorItem::FP) + mXmlWriter->writeAttribute(TagAttribute, "fp"); + else if (error.tag == ErrorItem::IGNORE) + mXmlWriter->writeAttribute(TagAttribute, "ignore"); + else if (error.tag == ErrorItem::BUG) + mXmlWriter->writeAttribute(TagAttribute, "bug"); for (int i = error.errorPath.count() - 1; i >= 0; i--) { mXmlWriter->writeStartElement(LocationElementName); @@ -209,6 +219,17 @@ ErrorItem XmlReportV2::readError(QXmlStreamReader *reader) item.inconclusive = true; if (attribs.hasAttribute("", CWEAttribute)) item.cwe = attribs.value("", CWEAttribute).toString().toInt(); + if (attribs.hasAttribute("", SinceDateAttribute)) + item.sinceDate = attribs.value("", SinceDateAttribute).toString(); + if (attribs.hasAttribute("", TagAttribute)) { + const QString tag = attribs.value("", TagAttribute).toString(); + if (tag == "fp") + item.tag = ErrorItem::FP; + else if (tag == "ignore") + item.tag = ErrorItem::IGNORE; + else if (tag == "bug") + item.tag = ErrorItem::BUG; + } } bool errorRead = false; @@ -251,5 +272,9 @@ ErrorItem XmlReportV2::readError(QXmlStreamReader *reader) break; } } + + if (item.errorPath.size() == 1 && item.errorPath[0].info.isEmpty()) + item.errorPath[0].info = item.message; + return item; }