GUI: Rework handling of tags. The allowed tags are now userdefined in the project file
This commit is contained in:
parent
94c5c3703c
commit
330ceccdc9
|
@ -35,7 +35,6 @@ ErrorItem::ErrorItem()
|
|||
: severity(Severity::none)
|
||||
, inconclusive(false)
|
||||
, cwe(-1)
|
||||
, tag(NONE)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -46,7 +45,6 @@ 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<ErrorLogger::ErrorMessage::FileLocation>::const_iterator loc = errmsg._callStack.begin();
|
||||
loc != errmsg._callStack.end();
|
||||
|
|
|
@ -92,7 +92,7 @@ public:
|
|||
|
||||
// Special GUI properties
|
||||
QString sinceDate;
|
||||
enum Tag { NONE, FP, IGNORE, BUG } tag;
|
||||
QString tags;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ErrorItem);
|
||||
|
@ -111,7 +111,7 @@ public:
|
|||
QString summary;
|
||||
QString message;
|
||||
QString sinceDate;
|
||||
ErrorItem::Tag tag;
|
||||
QString tags;
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
|
|
@ -1399,6 +1399,8 @@ QString MainWindow::getLastResults() const
|
|||
|
||||
bool MainWindow::loadLastResults()
|
||||
{
|
||||
if (mProjectFile)
|
||||
mUI.mResults->setTags(mProjectFile->getTags());
|
||||
const QString &lastResults = getLastResults();
|
||||
if (lastResults.isEmpty())
|
||||
return false;
|
||||
|
@ -1416,6 +1418,7 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile)
|
|||
const QString rootpath = projectFile->getRootPath();
|
||||
|
||||
mThread->setAddons(projectFile->getAddons());
|
||||
mUI.mResults->setTags(projectFile->getTags());
|
||||
|
||||
// If the root path is not given or is not "current dir", use project
|
||||
// file's location directory as root path
|
||||
|
@ -1508,6 +1511,7 @@ void MainWindow::closeProjectFile()
|
|||
delete mProjectFile;
|
||||
mProjectFile = nullptr;
|
||||
mUI.mResults->clear(true);
|
||||
mUI.mResults->setTags(QStringList());
|
||||
enableProjectActions(false);
|
||||
enableProjectOpenActions(true);
|
||||
formatAndSetTitle();
|
||||
|
|
|
@ -53,6 +53,8 @@ static const char SuppressionsElementName[] = "suppressions";
|
|||
static const char SuppressionElementName[] = "suppression";
|
||||
static const char AddonElementName[] = "addon";
|
||||
static const char AddonsElementName[] = "addons";
|
||||
static const char TagsElementName[] = "tags";
|
||||
static const char TagElementName[] = "tag";
|
||||
|
||||
ProjectFile::ProjectFile(QObject *parent) :
|
||||
QObject(parent)
|
||||
|
@ -151,6 +153,9 @@ bool ProjectFile::read(const QString &filename)
|
|||
if (insideProject && xmlReader.name() == AddonsElementName)
|
||||
readStringList(mAddons, xmlReader, AddonElementName);
|
||||
|
||||
if (insideProject && xmlReader.name() == TagsElementName)
|
||||
readStringList(mTags, xmlReader, TagElementName);
|
||||
|
||||
break;
|
||||
|
||||
case QXmlStreamReader::EndElement:
|
||||
|
@ -583,6 +588,8 @@ bool ProjectFile::write(const QString &filename)
|
|||
AddonsElementName,
|
||||
AddonElementName);
|
||||
|
||||
writeStringList(xmlWriter, mTags, TagsElementName, TagElementName);
|
||||
|
||||
xmlWriter.writeEndDocument();
|
||||
file.close();
|
||||
return true;
|
||||
|
|
|
@ -122,6 +122,10 @@ public:
|
|||
return mAddons;
|
||||
}
|
||||
|
||||
QStringList getTags() const {
|
||||
return mTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get filename for the project file.
|
||||
* @return file name.
|
||||
|
@ -138,7 +142,6 @@ public:
|
|||
mRootPath = rootpath;
|
||||
}
|
||||
|
||||
|
||||
void setBuildDir(const QString &buildDir) {
|
||||
mBuildDir = buildDir;
|
||||
}
|
||||
|
@ -193,6 +196,14 @@ public:
|
|||
*/
|
||||
void setAddons(const QStringList &addons);
|
||||
|
||||
/**
|
||||
* @brief Set tags.
|
||||
* @param tags tag list
|
||||
*/
|
||||
void setTags(const QStringList &tags) {
|
||||
mTags = tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write project file (to disk).
|
||||
* @param filename Filename to use.
|
||||
|
@ -335,6 +346,11 @@ private:
|
|||
* @brief List of addons.
|
||||
*/
|
||||
QStringList mAddons;
|
||||
|
||||
/**
|
||||
* @brief Warning tags
|
||||
*/
|
||||
QStringList mTags;
|
||||
};
|
||||
/// @}
|
||||
#endif // PROJECT_FILE_H
|
||||
|
|
|
@ -90,6 +90,8 @@ ProjectFileDialog::ProjectFileDialog(ProjectFile *projectFile, QWidget *parent)
|
|||
mLibraryCheckboxes << checkbox;
|
||||
}
|
||||
|
||||
mUI.mEditTags->setValidator(new QRegExpValidator(QRegExp("[a-zA-Z0-9 ;]*"),this));
|
||||
|
||||
connect(mUI.mButtons, &QDialogButtonBox::accepted, this, &ProjectFileDialog::ok);
|
||||
connect(mUI.mBtnBrowseBuildDir, &QPushButton::clicked, this, &ProjectFileDialog::browseBuildDir);
|
||||
connect(mUI.mBtnClearImportProject, &QPushButton::clicked, this, &ProjectFileDialog::clearImportProject);
|
||||
|
@ -146,6 +148,14 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
|
|||
mUI.mAddonThreadSafety->setChecked(projectFile->getAddons().contains("threadsafety"));
|
||||
mUI.mAddonY2038->setChecked(projectFile->getAddons().contains("y2038"));
|
||||
mUI.mAddonCert->setChecked(projectFile->getAddons().contains("cert"));
|
||||
QString tags;
|
||||
foreach (const QString tag, projectFile->getTags()) {
|
||||
if (tags.isEmpty())
|
||||
tags = tag;
|
||||
else
|
||||
tags += ';' + tag;
|
||||
}
|
||||
mUI.mEditTags->setText(tags);
|
||||
updatePathsAndDefines();
|
||||
}
|
||||
|
||||
|
@ -171,6 +181,9 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
|||
if (mUI.mAddonCert->isChecked())
|
||||
list << "cert";
|
||||
projectFile->setAddons(list);
|
||||
QStringList tags(mUI.mEditTags->text().split(";"));
|
||||
tags.removeAll(QString());
|
||||
projectFile->setTags(tags);
|
||||
}
|
||||
|
||||
void ProjectFileDialog::ok()
|
||||
|
|
|
@ -285,6 +285,18 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<property name="title">
|
||||
<string>Warning tags (separated by semicolon)</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="mEditTags"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
|
||||
// These must match column headers given in ResultsTree::translate()
|
||||
static const unsigned int COLUMN_SINCE_DATE = 6;
|
||||
static const unsigned int COLUMN_TAG = 7;
|
||||
static const unsigned int COLUMN_TAGS = 7;
|
||||
|
||||
ResultsTree::ResultsTree(QWidget * parent) :
|
||||
QTreeView(parent),
|
||||
|
@ -161,7 +161,7 @@ bool ResultsTree::addErrorItem(const ErrorItem &item)
|
|||
line.message = item.message;
|
||||
line.severity = item.severity;
|
||||
line.sinceDate = item.sinceDate;
|
||||
line.tag = item.tag;
|
||||
line.tags = item.tags;
|
||||
//Create the base item for the error and ensure it has a proper
|
||||
//file item as a parent
|
||||
QStandardItem* fileItem = ensureFileItem(loc.file, item.file0, hide);
|
||||
|
@ -187,7 +187,7 @@ bool ResultsTree::addErrorItem(const ErrorItem &item)
|
|||
data["inconclusive"] = item.inconclusive;
|
||||
data["file0"] = stripPath(item.file0, true);
|
||||
data["sinceDate"] = item.sinceDate;
|
||||
data["tag"] = item.tag;
|
||||
data["tags"] = item.tags;
|
||||
stditem->setData(QVariant(data));
|
||||
|
||||
//Add backtrace files as children
|
||||
|
@ -245,21 +245,9 @@ QStandardItem *ResultsTree::addBacktraceFiles(QStandardItem *parent,
|
|||
<< createNormalItem(childOfMessage ? QString() : item.errorId)
|
||||
<< (childOfMessage ? createNormalItem(QString()) : createCheckboxItem(item.inconclusive))
|
||||
<< createNormalItem(item.summary)
|
||||
<< createNormalItem(item.sinceDate);
|
||||
switch (item.tag) {
|
||||
case ErrorItem::NONE:
|
||||
list << createNormalItem("");
|
||||
break;
|
||||
case ErrorItem::FP:
|
||||
list << createNormalItem(tr("False positive"));
|
||||
break;
|
||||
case ErrorItem::IGNORE:
|
||||
list << createNormalItem(tr("Ignore"));
|
||||
break;
|
||||
case ErrorItem::BUG:
|
||||
list << createNormalItem(tr("bug"));
|
||||
break;
|
||||
};
|
||||
<< createNormalItem(item.sinceDate)
|
||||
<< createNormalItem(item.tags);
|
||||
|
||||
//TODO message has parameter names so we'll need changes to the core
|
||||
//cppcheck so we can get proper translations
|
||||
|
||||
|
@ -619,13 +607,16 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e)
|
|||
recheckSelectedFiles->setDisabled(false);
|
||||
|
||||
menu.addAction(recheckSelectedFiles);
|
||||
menu.addSeparator();
|
||||
menu.addAction(copyfilename);
|
||||
menu.addAction(copypath);
|
||||
menu.addAction(copymessage);
|
||||
menu.addAction(copymessageid);
|
||||
menu.addSeparator();
|
||||
menu.addAction(hide);
|
||||
menu.addAction(hideallid);
|
||||
menu.addAction(suppress);
|
||||
menu.addSeparator();
|
||||
menu.addAction(opencontainingfolder);
|
||||
|
||||
connect(recheckSelectedFiles, SIGNAL(triggered()), this, SLOT(recheckSelectedFiles()));
|
||||
|
@ -638,16 +629,21 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e)
|
|||
connect(suppress, SIGNAL(triggered()), this, SLOT(suppressSelectedIds()));
|
||||
connect(opencontainingfolder, SIGNAL(triggered()), this, SLOT(openContainingFolder()));
|
||||
|
||||
menu.addSeparator();
|
||||
QAction *fp = new QAction(tr("False positive"), &menu);
|
||||
QAction *ignore = new QAction(tr("Ignore"), &menu);
|
||||
QAction *bug = new QAction(tr("Bug"), &menu);
|
||||
menu.addAction(fp);
|
||||
menu.addAction(ignore);
|
||||
menu.addAction(bug);
|
||||
connect(fp, &QAction::triggered, this, &ResultsTree::tagFP);
|
||||
connect(ignore, &QAction::triggered, this, &ResultsTree::tagIgnore);
|
||||
connect(bug, &QAction::triggered, this, &ResultsTree::tagBug);
|
||||
if (!mTags.isEmpty()) {
|
||||
menu.addSeparator();
|
||||
QMenu *tagMenu = menu.addMenu(tr("Tag"));
|
||||
{
|
||||
QAction *action = new QAction(tr("No tag"), tagMenu);
|
||||
tagMenu->addAction(action);
|
||||
connect(action, &QAction::triggered, [=](){ tagSelectedItems(QString()); });
|
||||
}
|
||||
|
||||
foreach (const QString tagstr, mTags) {
|
||||
QAction *action = new QAction(tagstr, tagMenu);
|
||||
tagMenu->addAction(action);
|
||||
connect(action, &QAction::triggered, [=](){ tagSelectedItems(tagstr); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Start the menu
|
||||
|
@ -985,7 +981,7 @@ void ResultsTree::openContainingFolder()
|
|||
}
|
||||
}
|
||||
|
||||
void ResultsTree::tagSelectedItems(int tagNumber, const QString &tag)
|
||||
void ResultsTree::tagSelectedItems(const QString &tag)
|
||||
{
|
||||
if (!mSelectionModel)
|
||||
return;
|
||||
|
@ -993,10 +989,10 @@ void ResultsTree::tagSelectedItems(int tagNumber, const QString &tag)
|
|||
foreach (QModelIndex index, mSelectionModel->selectedRows()) {
|
||||
QStandardItem *item = mModel.itemFromIndex(index);
|
||||
QVariantMap data = item->data().toMap();
|
||||
if (data.contains("tag")) {
|
||||
data["tag"] = tagNumber;
|
||||
if (data.contains("tags")) {
|
||||
data["tags"] = tag;
|
||||
item->setData(QVariant(data));
|
||||
item->parent()->child(index.row(), COLUMN_TAG)->setText(tag);
|
||||
item->parent()->child(index.row(), COLUMN_TAGS)->setText(tag);
|
||||
isTagged = true;
|
||||
}
|
||||
}
|
||||
|
@ -1004,22 +1000,6 @@ void ResultsTree::tagSelectedItems(int tagNumber, const QString &tag)
|
|||
emit tagged();
|
||||
}
|
||||
|
||||
void ResultsTree::tagFP(bool)
|
||||
{
|
||||
tagSelectedItems(ErrorItem::FP, tr("False positive"));
|
||||
}
|
||||
|
||||
void ResultsTree::tagIgnore(bool)
|
||||
{
|
||||
tagSelectedItems(ErrorItem::IGNORE, tr("Ignore"));
|
||||
}
|
||||
|
||||
void ResultsTree::tagBug(bool)
|
||||
{
|
||||
tagSelectedItems(ErrorItem::BUG, tr("Bug"));
|
||||
}
|
||||
|
||||
|
||||
void ResultsTree::context(int application)
|
||||
{
|
||||
startApplication(mContextItem, application);
|
||||
|
@ -1164,17 +1144,11 @@ void ResultsTree::updateFromOldReport(const QString &filename)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (errorItem.tag != ErrorItem::NONE)
|
||||
if (!errorItem.tags.isEmpty())
|
||||
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";
|
||||
data["tags"] = oldErrorItem.tags;
|
||||
error->setData(data);
|
||||
}
|
||||
}
|
||||
|
@ -1192,7 +1166,7 @@ void ResultsTree::readErrorItem(const QStandardItem *error, ErrorItem *item) con
|
|||
item->inconclusive = data["inconclusive"].toBool();
|
||||
item->file0 = data["file0"].toString();
|
||||
item->sinceDate = data["sinceDate"].toString();
|
||||
item->tag = (ErrorItem::Tag)data["tag"].toInt();
|
||||
item->tags = data["tags"].toString();
|
||||
|
||||
if (error->rowCount() == 0) {
|
||||
QErrorPathItem e;
|
||||
|
|
|
@ -52,6 +52,10 @@ public:
|
|||
virtual ~ResultsTree();
|
||||
void initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler);
|
||||
|
||||
void setTags(const QStringList &tags) {
|
||||
mTags = tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a new item to the tree
|
||||
*
|
||||
|
@ -286,10 +290,6 @@ protected slots:
|
|||
*/
|
||||
virtual void currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
|
||||
void tagFP(bool);
|
||||
void tagIgnore(bool);
|
||||
void tagBug(bool);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
|
@ -519,11 +519,13 @@ protected:
|
|||
|
||||
private:
|
||||
/** tag selected items */
|
||||
void tagSelectedItems(int tagNumber, const QString &tag);
|
||||
void tagSelectedItems(const QString &tag);
|
||||
|
||||
/** @brief Convert GUI error item into data error item */
|
||||
void readErrorItem(const QStandardItem *error, ErrorItem *item) const;
|
||||
|
||||
QStringList mTags;
|
||||
|
||||
QItemSelectionModel *mSelectionModel;
|
||||
ThreadHandler *mThread;
|
||||
};
|
||||
|
|
|
@ -48,6 +48,10 @@ public:
|
|||
void initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler);
|
||||
virtual ~ResultsView();
|
||||
|
||||
void setTags(const QStringList &tags) {
|
||||
mUI.mTree->setTags(tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function to show/hide certain type of errors
|
||||
* Refreshes the tree.
|
||||
|
|
|
@ -36,7 +36,7 @@ static const QString LocationElementName = "location";
|
|||
static const QString ColAttribute = "col";
|
||||
static const QString CWEAttribute = "cwe";
|
||||
static const QString SinceDateAttribute = "sinceDate";
|
||||
static const QString TagAttribute = "tag";
|
||||
static const QString TagsAttribute = "tag";
|
||||
static const QString FilenameAttribute = "file";
|
||||
static const QString IncludedFromFilenameAttribute = "file0";
|
||||
static const QString InconclusiveAttribute = "inconclusive";
|
||||
|
@ -124,12 +124,8 @@ void XmlReportV2::writeError(const ErrorItem &error)
|
|||
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");
|
||||
if (!error.tags.isEmpty())
|
||||
mXmlWriter->writeAttribute(TagsAttribute, error.tags);
|
||||
|
||||
for (int i = error.errorPath.count() - 1; i >= 0; i--) {
|
||||
mXmlWriter->writeStartElement(LocationElementName);
|
||||
|
@ -221,15 +217,8 @@ ErrorItem XmlReportV2::readError(QXmlStreamReader *reader)
|
|||
item.cwe = attribs.value(QString(), CWEAttribute).toString().toInt();
|
||||
if (attribs.hasAttribute(QString(), SinceDateAttribute))
|
||||
item.sinceDate = attribs.value(QString(), SinceDateAttribute).toString();
|
||||
if (attribs.hasAttribute(QString(), TagAttribute)) {
|
||||
const QString tag = attribs.value(QString(), 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;
|
||||
}
|
||||
if (attribs.hasAttribute(QString(), TagsAttribute))
|
||||
item.tags = attribs.value(QString(), TagsAttribute).toString();
|
||||
}
|
||||
|
||||
bool errorRead = false;
|
||||
|
|
Loading…
Reference in New Issue