Cppcheck
resultstree.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2024 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "resultstree.h"
20 
21 #include "application.h"
22 #include "applicationlist.h"
23 #include "common.h"
24 #include "config.h"
25 #include "erroritem.h"
26 #include "errorlogger.h"
27 #include "errortypes.h"
28 #include "path.h"
29 #include "projectfile.h"
30 #include "report.h"
31 #include "showtypes.h"
32 #include "suppressions.h"
33 #include "threadhandler.h"
34 #include "xmlreportv2.h"
35 
36 #include <utility>
37 
38 #include <QAction>
39 #include <QApplication>
40 #include <QClipboard>
41 #include <QContextMenuEvent>
42 #include <QDebug>
43 #include <QDesktopServices>
44 #include <QDir>
45 #include <QFileInfo>
46 #include <QFileDialog>
47 #include <QIcon>
48 #include <QItemSelectionModel>
49 #include <QKeyEvent>
50 #include <QList>
51 #include <QLocale>
52 #include <QMap>
53 #include <QMenu>
54 #include <QMessageBox>
55 #include <QModelIndex>
56 #include <QProcess>
57 #include <QSet>
58 #include <QSettings>
59 #include <QSignalMapper>
60 #include <QStandardItem>
61 #include <QUrl>
62 #include <QVariant>
63 #include <QVariantMap>
64 #include <Qt>
65 
66 static constexpr char COLUMN[] = "column";
67 static constexpr char CWE[] = "cwe";
68 static constexpr char ERRORID[] = "id";
69 static constexpr char FILENAME[] = "file";
70 static constexpr char FILE0[] = "file0";
71 static constexpr char HASH[] = "hash";
72 static constexpr char HIDE[] = "hide";
73 static constexpr char INCONCLUSIVE[] = "inconclusive";
74 static constexpr char LINE[] = "line";
75 static constexpr char MESSAGE[] = "message";
76 static constexpr char SEVERITY[] = "severity";
77 static constexpr char SINCEDATE[] = "sinceDate";
78 static constexpr char SYMBOLNAMES[] = "symbolNames";
79 static constexpr char SUMMARY[] = "summary";
80 static constexpr char TAGS[] = "tags";
81 
82 // These must match column headers given in ResultsTree::translate()
83 static constexpr int COLUMN_SINCE_DATE = 6;
84 static constexpr int COLUMN_TAGS = 7;
85 
86 ResultsTree::ResultsTree(QWidget * parent) :
87  QTreeView(parent)
88 {
89  setModel(&mModel);
90  translate(); // Adds columns to grid
91  setExpandsOnDoubleClick(false);
92  setSortingEnabled(true);
93 
94  connect(this, &ResultsTree::doubleClicked, this, &ResultsTree::quickStartApplication);
95 }
96 
97 void ResultsTree::keyPressEvent(QKeyEvent *event)
98 {
99  if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
100  quickStartApplication(this->currentIndex());
101  }
102  QTreeView::keyPressEvent(event);
103 }
104 
105 void ResultsTree::initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler)
106 {
107  mSettings = settings;
108  mApplications = list;
109  mThread = checkThreadHandler;
110  loadSettings();
111 }
112 
113 
114 QStandardItem *ResultsTree::createNormalItem(const QString &name)
115 {
116  auto *item = new QStandardItem(name);
117  item->setData(name, Qt::ToolTipRole);
118  item->setEditable(false);
119  return item;
120 }
121 
122 QStandardItem *ResultsTree::createCheckboxItem(bool checked)
123 {
124  auto *item = new QStandardItem;
125  item->setCheckable(true);
126  item->setCheckState(checked ? Qt::Checked : Qt::Unchecked);
127  item->setEnabled(false);
128  return item;
129 }
130 
131 QStandardItem *ResultsTree::createLineNumberItem(const QString &linenumber)
132 {
133  auto *item = new QStandardItem();
134  item->setData(QVariant(linenumber.toInt()), Qt::DisplayRole);
135  item->setToolTip(linenumber);
136  item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
137  item->setEditable(false);
138  return item;
139 }
140 
142 {
143  if (item.errorPath.isEmpty()) {
144  return false;
145  }
146 
147  const QErrorPathItem &loc = item.errorId.startsWith("clang") ? item.errorPath.front() : item.errorPath.back();
148  QString realfile = stripPath(loc.file, false);
149 
150  if (realfile.isEmpty()) {
151  realfile = tr("Undefined file");
152  }
153 
154  bool hide = false;
155 
156  // Ids that are temporarily hidden..
157  if (mHiddenMessageId.contains(item.errorId))
158  hide = true;
159 
160  //If specified, filter on summary, message, filename, and id
161  if (!hide && !mFilter.isEmpty()) {
162  if (!item.summary.contains(mFilter, Qt::CaseInsensitive) &&
163  !item.message.contains(mFilter, Qt::CaseInsensitive) &&
164  !item.errorPath.back().file.contains(mFilter, Qt::CaseInsensitive) &&
165  !item.errorId.contains(mFilter, Qt::CaseInsensitive)) {
166  hide = true;
167  }
168  }
169 
170  //if there is at least one error that is not hidden, we have a visible error
171  if (!hide) {
172  mVisibleErrors = true;
173  }
174 
175  ErrorLine line;
176  line.file = realfile;
177  line.line = loc.line;
178  line.errorId = item.errorId;
179  line.cwe = item.cwe;
180  line.hash = item.hash;
181  line.inconclusive = item.inconclusive;
182  line.summary = item.summary;
183  line.message = item.message;
184  line.severity = item.severity;
185  line.sinceDate = item.sinceDate;
186  if (const ProjectFile *activeProject = ProjectFile::getActiveProject()) {
187  line.tags = activeProject->getWarningTags(item.hash);
188  }
189  //Create the base item for the error and ensure it has a proper
190  //file item as a parent
191  QStandardItem* fileItem = ensureFileItem(loc.file, item.file0, hide);
192  QStandardItem* stditem = addBacktraceFiles(fileItem,
193  line,
194  hide,
195  severityToIcon(line.severity),
196  false);
197 
198  if (!stditem)
199  return false;
200 
201  //Add user data to that item
202  QMap<QString, QVariant> data;
204  data[SUMMARY] = item.summary;
205  data[MESSAGE] = item.message;
206  data[FILENAME] = loc.file;
207  data[LINE] = loc.line;
208  data[COLUMN] = loc.column;
209  data[ERRORID] = item.errorId;
210  data[CWE] = item.cwe;
211  data[HASH] = item.hash;
212  data[INCONCLUSIVE] = item.inconclusive;
213  data[FILE0] = stripPath(item.file0, true);
214  data[SINCEDATE] = item.sinceDate;
215  data[SYMBOLNAMES] = item.symbolNames;
216  data[TAGS] = line.tags;
217  data[HIDE] = hide;
218  stditem->setData(QVariant(data));
219 
220  //Add backtrace files as children
221  if (item.errorPath.size() > 1) {
222  for (int i = 0; i < item.errorPath.size(); i++) {
223  const QErrorPathItem &e = item.errorPath[i];
224  line.file = e.file;
225  line.line = e.line;
226  line.message = line.summary = e.info;
227  QStandardItem *child_item = addBacktraceFiles(stditem,
228  line,
229  hide,
230  ":images/go-down.png",
231  true);
232  if (!child_item)
233  continue;
234 
235  // Add user data to that item
236  QMap<QString, QVariant> child_data;
237  child_data[SEVERITY] = ShowTypes::SeverityToShowType(line.severity);
238  child_data[SUMMARY] = line.summary;
239  child_data[MESSAGE] = line.message;
240  child_data[FILENAME] = e.file;
241  child_data[LINE] = e.line;
242  child_data[COLUMN] = e.column;
243  child_data[ERRORID] = line.errorId;
244  child_data[CWE] = line.cwe;
245  child_data[HASH] = line.hash;
246  child_data[INCONCLUSIVE] = line.inconclusive;
247  child_data[SYMBOLNAMES] = item.symbolNames;
248  child_item->setData(QVariant(child_data));
249  }
250  }
251 
252  // Partially refresh the tree: Unhide file item if necessary
253  if (!hide) {
254  setRowHidden(fileItem->row(), QModelIndex(), !mShowSeverities.isShown(item.severity));
255  }
256  return true;
257 }
258 
259 QStandardItem *ResultsTree::addBacktraceFiles(QStandardItem *parent,
260  const ErrorLine &item,
261  const bool hide,
262  const QString &icon,
263  bool childOfMessage)
264 {
265  if (!parent) {
266  return nullptr;
267  }
268 
269  QList<QStandardItem*> list;
270  // Ensure shown path is with native separators
271  list << createNormalItem(QDir::toNativeSeparators(item.file))
272  << createNormalItem(childOfMessage ? tr("note") : severityToTranslatedString(item.severity))
273  << createLineNumberItem(QString::number(item.line))
274  << createNormalItem(childOfMessage ? QString() : item.errorId)
275  << (childOfMessage ? createNormalItem(QString()) : createCheckboxItem(item.inconclusive))
276  << createNormalItem(item.summary)
277  << createNormalItem(item.sinceDate)
278  << createNormalItem(item.tags);
279 
280  //TODO message has parameter names so we'll need changes to the core
281  //cppcheck so we can get proper translations
282 
283  // Check for duplicate rows and don't add them if found
284  for (int i = 0; i < parent->rowCount(); i++) {
285  // The first column is the file name and is always the same
286 
287  // the third column is the line number so check it first
288  if (parent->child(i, 2)->text() == list[2]->text()) {
289  // the second column is the severity so check it next
290  if (parent->child(i, 1)->text() == list[1]->text()) {
291  // the sixth column is the summary so check it last
292  if (parent->child(i, 5)->text() == list[5]->text()) {
293  // this row matches so don't add it
294  return nullptr;
295  }
296  }
297  }
298  }
299 
300  parent->appendRow(list);
301 
302  setRowHidden(parent->rowCount() - 1, parent->index(), hide);
303 
304  if (!icon.isEmpty()) {
305  list[0]->setIcon(QIcon(icon));
306  }
307 
308  /* TODO: the list items leak memory
309  Indirect leak of 80624 byte(s) in 5039 object(s) allocated from:
310  #0 0xa15a2d in operator new(unsigned long) (/mnt/s/GitHub/cppcheck-fw/cmake-build-debug-wsl-kali-clang-asan-ubsan/bin/cppcheck-gui+0xa15a2d)
311  #1 0xdda276 in ResultsTree::createNormalItem(QString const&) /mnt/s/GitHub/cppcheck-fw/gui/resultstree.cpp:122:27
312  #2 0xde4290 in ResultsTree::addBacktraceFiles(QStandardItem*, ErrorLine const&, bool, QString const&, bool) /mnt/s/GitHub/cppcheck-fw/gui/resultstree.cpp:289:13
313  #3 0xddd754 in ResultsTree::addErrorItem(ErrorItem const&) /mnt/s/GitHub/cppcheck-fw/gui/resultstree.cpp:199:30
314  #4 0xe37046 in ResultsView::error(ErrorItem const&) /mnt/s/GitHub/cppcheck-fw/gui/resultsview.cpp:129:21
315  #5 0xd2448d in QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<ErrorItem const&>, void, void (ResultsView::*)(ErrorItem const&)>::call(void (ResultsView::*)(ErrorItem const&), ResultsView*, void**) /usr/include/x86_64-linux-gnu/qt5/QtCore/qobjectdefs_impl.h:152:13
316  #6 0xd2402c in void QtPrivate::FunctionPointer<void (ResultsView::*)(ErrorItem const&)>::call<QtPrivate::List<ErrorItem const&>, void>(void (ResultsView::*)(ErrorItem const&), ResultsView*, void**) /usr/include/x86_64-linux-gnu/qt5/QtCore/qobjectdefs_impl.h:185:13
317  #7 0xd23b45 in QtPrivate::QSlotObject<void (ResultsView::*)(ErrorItem const&), QtPrivate::List<ErrorItem const&>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) /usr/include/x86_64-linux-gnu/qt5/QtCore/qobjectdefs_impl.h:418:17
318  #8 0x7fd2536cc0dd in QObject::event(QEvent*) (/usr/lib/x86_64-linux-gnu/libQt5Core.so.5+0x2dc0dd)
319  #9 0x7fd2541836be in QApplicationPrivate::notify_helper(QObject*, QEvent*) (/usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5+0x1636be)
320  */
321 
322  return list[0];
323 }
324 
326 {
327  switch (severity) {
328  case Severity::style:
329  return tr("style");
330 
331  case Severity::error:
332  return tr("error");
333 
334  case Severity::warning:
335  return tr("warning");
336 
338  return tr("performance");
339 
341  return tr("portability");
342 
344  return tr("information");
345 
346  case Severity::debug:
347  return tr("debug");
348 
349  case Severity::internal:
350  return tr("internal");
351 
352  case Severity::none:
353  default:
354  return QString();
355  }
356 }
357 
358 QStandardItem *ResultsTree::findFileItem(const QString &name) const
359 {
360  // The first column contains the file name. In Windows we can get filenames
361  // "header.h" and "Header.h" and must compare them as identical.
362 
363  for (int i = 0; i < mModel.rowCount(); i++) {
364 #ifdef _WIN32
365  if (QString::compare(mModel.item(i, 0)->text(), name, Qt::CaseInsensitive) == 0)
366 #else
367  if (mModel.item(i, 0)->text() == name)
368 #endif
369  return mModel.item(i, 0);
370  }
371  return nullptr;
372 }
373 
375 {
376  mModel.removeRows(0, mModel.rowCount());
377 }
378 
379 void ResultsTree::clear(const QString &filename)
380 {
381  const QString stripped = stripPath(filename, false);
382 
383  for (int i = 0; i < mModel.rowCount(); ++i) {
384  const QStandardItem *fileItem = mModel.item(i, 0);
385  if (!fileItem)
386  continue;
387 
388  QVariantMap data = fileItem->data().toMap();
389  if (stripped == data[FILENAME].toString() ||
390  filename == data[FILE0].toString()) {
391  mModel.removeRow(i);
392  break;
393  }
394  }
395 }
396 
397 void ResultsTree::clearRecheckFile(const QString &filename)
398 {
399  for (int i = 0; i < mModel.rowCount(); ++i) {
400  const QStandardItem *fileItem = mModel.item(i, 0);
401  if (!fileItem)
402  continue;
403 
404  QString actualfile((!mCheckPath.isEmpty() && filename.startsWith(mCheckPath)) ? filename.mid(mCheckPath.length() + 1) : filename);
405  QVariantMap data = fileItem->data().toMap();
406  QString storedfile = data[FILENAME].toString();
407  storedfile = ((!mCheckPath.isEmpty() && storedfile.startsWith(mCheckPath)) ? storedfile.mid(mCheckPath.length() + 1) : storedfile);
408  if (actualfile == storedfile) {
409  mModel.removeRow(i);
410  break;
411  }
412  }
413 }
414 
415 
417 {
418  for (int i = 0; i < mModel.columnCount(); i++) {
419  QString temp = QString(SETTINGS_RESULT_COLUMN_WIDTH).arg(i);
420  setColumnWidth(i, qMax(20, mSettings->value(temp, 800 / mModel.columnCount()).toInt()));
421  }
422 
423  mSaveFullPath = mSettings->value(SETTINGS_SAVE_FULL_PATH, false).toBool();
424  mSaveAllErrors = mSettings->value(SETTINGS_SAVE_ALL_ERRORS, false).toBool();
425  mShowFullPath = mSettings->value(SETTINGS_SHOW_FULL_PATH, false).toBool();
426 
427  showIdColumn(mSettings->value(SETTINGS_SHOW_ERROR_ID, true).toBool());
429 }
430 
432 {
433  for (int i = 0; i < mModel.columnCount(); i++) {
434  QString temp = QString(SETTINGS_RESULT_COLUMN_WIDTH).arg(i);
435  mSettings->setValue(temp, columnWidth(i));
436  }
437 }
438 
440 {
441  if (type != ShowTypes::ShowNone && mShowSeverities.isShown(type) != show) {
442  mShowSeverities.show(type, show);
443  refreshTree();
444  }
445 }
446 
448 {
449  mShowCppcheck = show;
450  refreshTree();
451 }
452 
454 {
455  mShowClang = show;
456  refreshTree();
457 }
458 
459 void ResultsTree::filterResults(const QString& filter)
460 {
461  mFilter = filter;
462  refreshTree();
463 }
464 
466 {
467  //Clear the "hide" flag for each item
468  mHiddenMessageId.clear();
469  const int filecount = mModel.rowCount();
470  for (int i = 0; i < filecount; i++) {
471  QStandardItem *fileItem = mModel.item(i, 0);
472  if (!fileItem)
473  continue;
474 
475  QVariantMap data = fileItem->data().toMap();
476  data[HIDE] = false;
477  fileItem->setData(QVariant(data));
478 
479  const int errorcount = fileItem->rowCount();
480  for (int j = 0; j < errorcount; j++) {
481  QStandardItem *child = fileItem->child(j, 0);
482  if (child) {
483  data = child->data().toMap();
484  data[HIDE] = false;
485  child->setData(QVariant(data));
486  }
487  }
488  }
489  refreshTree();
490  emit resultsHidden(false);
491 }
492 
493 
495 {
496  mVisibleErrors = false;
497  //Get the amount of files in the tree
498  const int filecount = mModel.rowCount();
499 
500  for (int i = 0; i < filecount; i++) {
501  //Get file i
502  QStandardItem *fileItem = mModel.item(i, 0);
503  if (!fileItem) {
504  continue;
505  }
506 
507  //Get the amount of errors this file contains
508  const int errorcount = fileItem->rowCount();
509 
510  //By default it shouldn't be visible
511  bool show = false;
512 
513  for (int j = 0; j < errorcount; j++) {
514  //Get the error itself
515  QStandardItem *child = fileItem->child(j, 0);
516  if (!child) {
517  continue;
518  }
519 
520  //Get error's user data
521  QVariant userdata = child->data();
522  //Convert it to QVariantMap
523  QVariantMap data = userdata.toMap();
524 
525  //Check if this error should be hidden
526  bool hide = (data[HIDE].toBool() || !mShowSeverities.isShown(ShowTypes::VariantToShowType(data[SEVERITY])));
527 
528  //If specified, filter on summary, message, filename, and id
529  if (!hide && !mFilter.isEmpty()) {
530  if (!data[SUMMARY].toString().contains(mFilter, Qt::CaseInsensitive) &&
531  !data[MESSAGE].toString().contains(mFilter, Qt::CaseInsensitive) &&
532  !data[FILENAME].toString().contains(mFilter, Qt::CaseInsensitive) &&
533  !data[ERRORID].toString().contains(mFilter, Qt::CaseInsensitive)) {
534  hide = true;
535  }
536  }
537 
538  // Tool filter
539  if (!hide) {
540  if (data[ERRORID].toString().startsWith("clang"))
541  hide = !mShowClang;
542  else
543  hide = !mShowCppcheck;
544  }
545 
546  if (!hide) {
547  mVisibleErrors = true;
548  }
549 
550  //Hide/show accordingly
551  setRowHidden(j, fileItem->index(), hide);
552 
553  //If it was shown then the file itself has to be shown as well
554  if (!hide) {
555  show = true;
556  }
557  }
558 
559  //Hide the file if its "hide" attribute is set
560  if (fileItem->data().toMap()["hide"].toBool()) {
561  show = false;
562  }
563 
564  //Show the file if any of it's errors are visible
565  setRowHidden(i, QModelIndex(), !show);
566  }
567 }
568 
569 QStandardItem *ResultsTree::ensureFileItem(const QString &fullpath, const QString &file0, bool hide)
570 {
571  QString name = stripPath(fullpath, false);
572  // Since item has path with native separators we must use path with
573  // native separators to find it.
574  QStandardItem *item = findFileItem(QDir::toNativeSeparators(name));
575 
576  if (item) {
577  return item;
578  }
579 
580  // Ensure shown path is with native separators
581  name = QDir::toNativeSeparators(name);
582  item = createNormalItem(name);
583  item->setIcon(QIcon(":images/text-x-generic.png"));
584 
585  //Add user data to that item
586  QMap<QString, QVariant> data;
587  data[FILENAME] = fullpath;
588  data[FILE0] = file0;
589  item->setData(QVariant(data));
590  mModel.appendRow(item);
591 
592  setRowHidden(mModel.rowCount() - 1, QModelIndex(), hide);
593 
594  return item;
595 }
596 
597 void ResultsTree::contextMenuEvent(QContextMenuEvent * e)
598 {
599  QModelIndex index = indexAt(e->pos());
600  if (index.isValid()) {
601  bool multipleSelection = false;
602 
603  mSelectionModel = selectionModel();
604  if (mSelectionModel->selectedRows().count() > 1)
605  multipleSelection = true;
606 
607  mContextItem = mModel.itemFromIndex(index);
608 
609  //Create a new context menu
610  QMenu menu(this);
611 
612  //Store all applications in a list
613  QList<QAction*> actions;
614 
615  //Create a signal mapper so we don't have to store data to class
616  //member variables
617  auto *signalMapper = new QSignalMapper(this);
618 
619  if (mContextItem && mApplications->getApplicationCount() > 0 && mContextItem->parent()) {
620  //Create an action for the application
621  int defaultApplicationIndex = mApplications->getDefaultApplication();
622  if (defaultApplicationIndex < 0)
623  defaultApplicationIndex = 0;
624  const Application& app = mApplications->getApplication(defaultApplicationIndex);
625  auto *start = new QAction(app.getName(), &menu);
626  if (multipleSelection)
627  start->setDisabled(true);
628 
629  //Add it to our list so we can disconnect later on
630  actions << start;
631 
632  //Add it to context menu
633  menu.addAction(start);
634 
635  //Connect the signal to signal mapper
636  connect(start, SIGNAL(triggered()), signalMapper, SLOT(map()));
637 
638  //Add a new mapping
639  signalMapper->setMapping(start, defaultApplicationIndex);
640 
641  connect(signalMapper, SIGNAL(mapped(int)),
642  this, SLOT(context(int)));
643  }
644 
645  // Add popup menuitems
646  if (mContextItem) {
647  if (mApplications->getApplicationCount() > 0) {
648  menu.addSeparator();
649  }
650 
651  //Create an action for the application
652  auto *recheckAction = new QAction(tr("Recheck"), &menu);
653  auto *copyAction = new QAction(tr("Copy"), &menu);
654  auto *hide = new QAction(tr("Hide"), &menu);
655  auto *hideallid = new QAction(tr("Hide all with id"), &menu);
656  auto *opencontainingfolder = new QAction(tr("Open containing folder"), &menu);
657 
658  if (multipleSelection) {
659  hideallid->setDisabled(true);
660  opencontainingfolder->setDisabled(true);
661  }
662  if (mThread->isChecking())
663  recheckAction->setDisabled(true);
664  else
665  recheckAction->setDisabled(false);
666 
667  menu.addAction(recheckAction);
668  menu.addSeparator();
669  menu.addAction(copyAction);
670  menu.addSeparator();
671  menu.addAction(hide);
672  menu.addAction(hideallid);
673 
674  auto *suppress = new QAction(tr("Suppress selected id(s)"), &menu);
675  {
676  QVariantMap data = mContextItem->data().toMap();
677  const QString messageId = data[ERRORID].toString();
678  suppress->setEnabled(!ErrorLogger::isCriticalErrorId(messageId.toStdString()));
679  }
680  menu.addAction(suppress);
681  connect(suppress, &QAction::triggered, this, &ResultsTree::suppressSelectedIds);
682 
683  menu.addSeparator();
684  menu.addAction(opencontainingfolder);
685 
686  connect(recheckAction, SIGNAL(triggered()), this, SLOT(recheckAction()));
687  connect(copyAction, SIGNAL(triggered()), this, SLOT(copyAction()));
688  connect(hide, SIGNAL(triggered()), this, SLOT(hideResult()));
689  connect(hideallid, SIGNAL(triggered()), this, SLOT(hideAllIdResult()));
690  connect(opencontainingfolder, SIGNAL(triggered()), this, SLOT(openContainingFolder()));
691 
692  const ProjectFile *currentProject = ProjectFile::getActiveProject();
693  if (currentProject && !currentProject->getTags().isEmpty()) {
694  menu.addSeparator();
695  QMenu *tagMenu = menu.addMenu(tr("Tag"));
696  {
697  auto *action = new QAction(tr("No tag"), tagMenu);
698  tagMenu->addAction(action);
699  connect(action, &QAction::triggered, [=]() {
700  tagSelectedItems(QString());
701  });
702  }
703 
704  for (const QString& tagstr : currentProject->getTags()) {
705  auto *action = new QAction(tagstr, tagMenu);
706  tagMenu->addAction(action);
707  connect(action, &QAction::triggered, [=]() {
708  tagSelectedItems(tagstr);
709  });
710  }
711  }
712  }
713 
714  //Start the menu
715  menu.exec(e->globalPos());
716  index = indexAt(e->pos());
717  if (index.isValid()) {
718  mContextItem = mModel.itemFromIndex(index);
719  if (mContextItem && mApplications->getApplicationCount() > 0 && mContextItem->parent()) {
720  //Disconnect all signals
721  for (const QAction* action : actions) {
722  disconnect(action, SIGNAL(triggered()), signalMapper, SLOT(map()));
723  }
724 
725  disconnect(signalMapper, SIGNAL(mapped(int)),
726  this, SLOT(context(int)));
727  //And remove the signal mapper
728  delete signalMapper;
729  }
730  }
731  }
732 }
733 
734 void ResultsTree::startApplication(const QStandardItem *target, int application)
735 {
736  //If there are no applications specified, tell the user about it
737  if (mApplications->getApplicationCount() == 0) {
738  QMessageBox msg(QMessageBox::Critical,
739  tr("Cppcheck"),
740  tr("No editor application configured.\n\n"
741  "Configure the editor application for Cppcheck in preferences/Applications."),
742  QMessageBox::Ok,
743  this);
744  msg.exec();
745  return;
746  }
747 
748  if (application == -1)
749  application = mApplications->getDefaultApplication();
750 
751  if (application == -1) {
752  QMessageBox msg(QMessageBox::Critical,
753  tr("Cppcheck"),
754  tr("No default editor application selected.\n\n"
755  "Please select the default editor application in preferences/Applications."),
756  QMessageBox::Ok,
757  this);
758  msg.exec();
759  return;
760 
761  }
762 
763  if (target && application >= 0 && application < mApplications->getApplicationCount() && target->parent()) {
764  // Make sure we are working with the first column
765  if (target->column() != 0)
766  target = target->parent()->child(target->row(), 0);
767 
768  QVariantMap data = target->data().toMap();
769 
770  //Replace (file) with filename
771  QString file = data[FILENAME].toString();
772  file = QDir::toNativeSeparators(file);
773  qDebug() << "Opening file: " << file;
774 
775  QFileInfo info(file);
776  if (!info.exists()) {
777  if (info.isAbsolute()) {
778  QMessageBox msgbox(this);
779  msgbox.setWindowTitle("Cppcheck");
780  msgbox.setText(tr("Could not find the file!"));
781  msgbox.setIcon(QMessageBox::Critical);
782  msgbox.exec();
783  } else {
784  QDir checkdir(mCheckPath);
785  if (checkdir.isAbsolute() && checkdir.exists()) {
786  file = mCheckPath + "/" + file;
787  } else {
788  QString dir = askFileDir(file);
789  dir += '/';
790  file = dir + file;
791  }
792  }
793  }
794 
795  if (file.indexOf(" ") > -1) {
796  file.insert(0, "\"");
797  file.append("\"");
798  }
799 
800  const Application& app = mApplications->getApplication(application);
801  QString params = app.getParameters();
802  params.replace("(file)", file, Qt::CaseInsensitive);
803 
804  QVariant line = data[LINE];
805  params.replace("(line)", QString("%1").arg(line.toInt()), Qt::CaseInsensitive);
806 
807  params.replace("(message)", data[MESSAGE].toString(), Qt::CaseInsensitive);
808  params.replace("(severity)", data[SEVERITY].toString(), Qt::CaseInsensitive);
809 
810  QString program = app.getPath();
811 
812  // In Windows we must surround paths including spaces with quotation marks.
813 #ifdef Q_OS_WIN
814  if (program.indexOf(" ") > -1) {
815  if (!program.startsWith('"') && !program.endsWith('"')) {
816  program.insert(0, "\"");
817  program.append("\"");
818  }
819  }
820 #endif // Q_OS_WIN
821 
822  const QString cmdLine = QString("%1 %2").arg(program).arg(params);
823 
824  // this is reported as deprecated in Qt 5.15.2 but no longer in Qt 6
825 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
826  SUPPRESS_WARNING_CLANG_PUSH("-Wdeprecated")
827  SUPPRESS_WARNING_GCC_PUSH("-Wdeprecated-declarations")
828 #endif
829  const bool success = QProcess::startDetached(cmdLine);
830 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
833 #endif
834  if (!success) {
835  QString text = tr("Could not start %1\n\nPlease check the application path and parameters are correct.").arg(program);
836 
837  QMessageBox msgbox(this);
838  msgbox.setWindowTitle("Cppcheck");
839  msgbox.setText(text);
840  msgbox.setIcon(QMessageBox::Critical);
841 
842  msgbox.exec();
843  }
844  }
845 }
846 
847 QString ResultsTree::askFileDir(const QString &file)
848 {
849  QString text = tr("Could not find file:") + '\n' + file + '\n';
850  QString title;
851  if (file.indexOf('/')) {
852  QString folderName = file.mid(0, file.indexOf('/'));
853  text += tr("Please select the folder '%1'").arg(folderName);
854  title = tr("Select Directory '%1'").arg(folderName);
855  } else {
856  text += tr("Please select the directory where file is located.");
857  title = tr("Select Directory");
858  }
859 
860  QMessageBox msgbox(this);
861  msgbox.setWindowTitle("Cppcheck");
862  msgbox.setText(text);
863  msgbox.setIcon(QMessageBox::Warning);
864  msgbox.exec();
865 
866  QString dir = QFileDialog::getExistingDirectory(this, title,
868  QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
869 
870  if (dir.isEmpty())
871  return QString();
872 
873  // User selected root path
874  if (QFileInfo::exists(dir + '/' + file))
875  mCheckPath = dir;
876 
877  // user selected checked folder
878  else if (file.indexOf('/') > 0) {
879  dir += '/';
880  QString folderName = file.mid(0, file.indexOf('/'));
881  if (dir.indexOf('/' + folderName + '/'))
882  dir = dir.mid(0, dir.lastIndexOf('/' + folderName + '/'));
883  if (QFileInfo::exists(dir + '/' + file))
884  mCheckPath = dir;
885  }
886 
887  // Otherwise; return
888  else
889  return QString();
890 
892  return mCheckPath;
893 }
894 
896 {
897  if (!mSelectionModel)
898  return;
899 
900  QString text;
901  for (QModelIndex index : mSelectionModel->selectedRows()) {
902  QStandardItem *item = mModel.itemFromIndex(index);
903  if (!item->parent()) {
904  text += item->text() + '\n';
905  continue;
906  }
907  if (item->parent()->parent())
908  item = item->parent();
909  QVariantMap data = item->data().toMap();
910  if (!data.contains("id"))
911  continue;
912  QString inconclusive = data[INCONCLUSIVE].toBool() ? ",inconclusive" : "";
913  text += '[' + data[FILENAME].toString() + ':' + QString::number(data[LINE].toInt())
914  + "] ("
915  + QString::fromStdString(severityToString(ShowTypes::ShowTypeToSeverity((ShowTypes::ShowType)data[SEVERITY].toInt()))) + inconclusive
916  + ") "
917  + data[MESSAGE].toString()
918  + " ["
919  + data[ERRORID].toString()
920  + "]\n";
921  }
922 
923  QClipboard *clipboard = QApplication::clipboard();
924  clipboard->setText(text);
925 }
926 
928 {
929  if (!mSelectionModel)
930  return;
931 
932  for (QModelIndex index : mSelectionModel->selectedRows()) {
933  QStandardItem *item = mModel.itemFromIndex(index);
934  //Set the "hide" flag for this item
935  QVariantMap data = item->data().toMap();
936  data[HIDE] = true;
937  item->setData(QVariant(data));
938 
939  refreshTree();
940  emit resultsHidden(true);
941  }
942 }
943 
945 {
946  if (!mSelectionModel)
947  return;
948 
949  QStringList selectedItems;
950  for (QModelIndex index : mSelectionModel->selectedRows()) {
951  QStandardItem *item = mModel.itemFromIndex(index);
952  while (item->parent())
953  item = item->parent();
954  QVariantMap data = item->data().toMap();
955  QString currentFile = data[FILENAME].toString();
956  if (!currentFile.isEmpty()) {
957  QString fileNameWithCheckPath;
958  QFileInfo curfileInfo(currentFile);
959  if (!curfileInfo.exists() && !mCheckPath.isEmpty() && currentFile.indexOf(mCheckPath) != 0)
960  fileNameWithCheckPath = mCheckPath + "/" + currentFile;
961  else
962  fileNameWithCheckPath = currentFile;
963  const QFileInfo fileInfo(fileNameWithCheckPath);
964  if (!fileInfo.exists()) {
965  askFileDir(currentFile);
966  return;
967  }
968  if (Path::isHeader2(currentFile.toStdString())) {
969  if (!data[FILE0].toString().isEmpty() && !selectedItems.contains(data[FILE0].toString())) {
970  selectedItems<<((!mCheckPath.isEmpty() && (data[FILE0].toString().indexOf(mCheckPath) != 0)) ? (mCheckPath + "/" + data[FILE0].toString()) : data[FILE0].toString());
971  if (!selectedItems.contains(fileNameWithCheckPath))
972  selectedItems<<fileNameWithCheckPath;
973  }
974  } else if (!selectedItems.contains(fileNameWithCheckPath))
975  selectedItems<<fileNameWithCheckPath;
976  }
977  }
978  emit checkSelected(std::move(selectedItems));
979 }
980 
982 {
983  if (!mContextItem || !mContextItem->parent())
984  return;
985 
986  // Make sure we are working with the first column
987  if (mContextItem->column() != 0)
988  mContextItem = mContextItem->parent()->child(mContextItem->row(), 0);
989  QVariantMap data = mContextItem->data().toMap();
990 
991  QString messageId = data[ERRORID].toString();
992 
993  mHiddenMessageId.append(messageId);
994 
995  // hide all errors with that message Id
996  const int filecount = mModel.rowCount();
997  for (int i = 0; i < filecount; i++) {
998  //Get file i
999  QStandardItem *file = mModel.item(i, 0);
1000  if (!file) {
1001  continue;
1002  }
1003 
1004  //Get the amount of errors this file contains
1005  const int errorcount = file->rowCount();
1006 
1007  for (int j = 0; j < errorcount; j++) {
1008  //Get the error itself
1009  QStandardItem *child = file->child(j, 0);
1010  if (!child) {
1011  continue;
1012  }
1013 
1014  QVariantMap userdata = child->data().toMap();
1015  if (userdata[ERRORID].toString() == messageId) {
1016  userdata[HIDE] = true;
1017  child->setData(QVariant(userdata));
1018  }
1019  }
1020  }
1021 
1022  refreshTree();
1023  emit resultsHidden(true);
1024 }
1025 
1027 {
1028  if (!mSelectionModel)
1029  return;
1030 
1031  QSet<QString> selectedIds;
1032  for (QModelIndex index : mSelectionModel->selectedRows()) {
1033  QStandardItem *item = mModel.itemFromIndex(index);
1034  if (!item->parent())
1035  continue;
1036  if (item->parent()->parent())
1037  item = item->parent();
1038  QVariantMap data = item->data().toMap();
1039  if (!data.contains("id"))
1040  continue;
1041  selectedIds << data[ERRORID].toString();
1042  }
1043 
1044  // delete all errors with selected message Ids
1045  for (int i = 0; i < mModel.rowCount(); i++) {
1046  QStandardItem * const file = mModel.item(i, 0);
1047  for (int j = 0; j < file->rowCount();) {
1048  QStandardItem *errorItem = file->child(j, 0);
1049  QVariantMap userdata = errorItem->data().toMap();
1050  if (selectedIds.contains(userdata[ERRORID].toString())) {
1051  file->removeRow(j);
1052  } else {
1053  j++;
1054  }
1055  }
1056  if (file->rowCount() == 0)
1057  mModel.removeRow(file->row());
1058  }
1059 
1060 
1061  emit suppressIds(selectedIds.values());
1062 }
1063 
1065 {
1066  if (!mSelectionModel)
1067  return;
1068 
1069  // Extract selected warnings
1070  QSet<QStandardItem *> selectedWarnings;
1071  for (QModelIndex index : mSelectionModel->selectedRows()) {
1072  QStandardItem *item = mModel.itemFromIndex(index);
1073  if (!item->parent())
1074  continue;
1075  while (item->parent()->parent())
1076  item = item->parent();
1077  selectedWarnings.insert(item);
1078  }
1079 
1080  bool changed = false;
1081  ProjectFile *projectFile = ProjectFile::getActiveProject();
1082  for (QStandardItem *item: selectedWarnings) {
1083  QStandardItem *fileItem = item->parent();
1084  const QVariantMap data = item->data().toMap();
1085  if (projectFile && data.contains(HASH)) {
1086  SuppressionList::Suppression suppression;
1087  suppression.hash = data[HASH].toULongLong();
1088  suppression.errorId = data[ERRORID].toString().toStdString();
1089  suppression.fileName = data[FILENAME].toString().toStdString();
1090  suppression.lineNumber = data[LINE].toInt();
1091  projectFile->addSuppression(suppression);
1092  changed = true;
1093  }
1094  fileItem->removeRow(item->row());
1095  if (fileItem->rowCount() == 0)
1096  mModel.removeRow(fileItem->row());
1097  }
1098 
1099  if (changed)
1100  projectFile->write();
1101 }
1102 
1104 {
1105  QString filePath = getFilePath(mContextItem, true);
1106  if (!filePath.isEmpty()) {
1107  filePath = QFileInfo(filePath).absolutePath();
1108  QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
1109  }
1110 }
1111 
1112 void ResultsTree::tagSelectedItems(const QString &tag)
1113 {
1114  if (!mSelectionModel)
1115  return;
1116  bool isTagged = false;
1117  ProjectFile *currentProject = ProjectFile::getActiveProject();
1118  for (QModelIndex index : mSelectionModel->selectedRows()) {
1119  QStandardItem *item = mModel.itemFromIndex(index);
1120  QVariantMap data = item->data().toMap();
1121  if (data.contains("tags")) {
1122  data[TAGS] = tag;
1123  item->setData(QVariant(data));
1124  item->parent()->child(index.row(), COLUMN_TAGS)->setText(tag);
1125  if (currentProject && data.contains(HASH)) {
1126  isTagged = true;
1127  currentProject->setWarningTags(data[HASH].toULongLong(), tag);
1128  }
1129  }
1130  }
1131  if (isTagged)
1132  currentProject->write();
1133 }
1134 
1135 void ResultsTree::context(int application)
1136 {
1137  startApplication(mContextItem, application);
1138 }
1139 
1140 void ResultsTree::quickStartApplication(const QModelIndex &index)
1141 {
1142  startApplication(mModel.itemFromIndex(index));
1143 }
1144 
1145 QString ResultsTree::getFilePath(const QStandardItem *target, bool fullPath)
1146 {
1147  if (target) {
1148  // Make sure we are working with the first column
1149  if (target->column() != 0)
1150  target = target->parent()->child(target->row(), 0);
1151 
1152  QVariantMap data = target->data().toMap();
1153 
1154  //Replace (file) with filename
1155  QString file = data[FILENAME].toString();
1156  QString pathStr = QDir::toNativeSeparators(file);
1157  if (!fullPath) {
1158  QFileInfo fi(pathStr);
1159  pathStr = fi.fileName();
1160  }
1161 
1162  return pathStr;
1163  }
1164 
1165  return QString();
1166 }
1167 
1169 {
1170  switch (severity) {
1171  case Severity::error:
1172  return ":images/dialog-error.png";
1173  case Severity::style:
1174  return ":images/applications-development.png";
1175  case Severity::warning:
1176  return ":images/dialog-warning.png";
1177  case Severity::portability:
1178  return ":images/applications-system.png";
1179  case Severity::performance:
1180  return ":images/utilities-system-monitor.png";
1181  case Severity::information:
1182  return ":images/dialog-information.png";
1183  default:
1184  return QString();
1185  }
1186 }
1187 
1189 {
1190  report->writeHeader();
1191 
1192  for (int i = 0; i < mModel.rowCount(); i++) {
1193  if (mSaveAllErrors || !isRowHidden(i, QModelIndex()))
1194  saveErrors(report, mModel.item(i, 0));
1195  }
1196 
1197  report->writeFooter();
1198 }
1199 
1200 void ResultsTree::saveErrors(Report *report, const QStandardItem *fileItem) const
1201 {
1202  if (!fileItem) {
1203  return;
1204  }
1205 
1206  for (int i = 0; i < fileItem->rowCount(); i++) {
1207  const QStandardItem *error = fileItem->child(i, 0);
1208 
1209  if (!error) {
1210  continue;
1211  }
1212 
1213  if (isRowHidden(i, fileItem->index()) && !mSaveAllErrors) {
1214  continue;
1215  }
1216 
1217  ErrorItem item;
1218  readErrorItem(error, &item);
1219 
1220  report->writeError(item);
1221  }
1222 }
1223 
1224 static int indexOf(const QList<ErrorItem> &list, const ErrorItem &item)
1225 {
1226  for (int i = 0; i < list.size(); i++) {
1227  if (ErrorItem::sameCID(item, list[i])) {
1228  return i;
1229  }
1230  }
1231  return -1;
1232 }
1233 
1234 void ResultsTree::updateFromOldReport(const QString &filename)
1235 {
1236  QList<ErrorItem> oldErrors;
1237  XmlReportV2 oldReport(filename, QString());
1238  if (oldReport.open()) {
1239  oldErrors = oldReport.read();
1240  oldReport.close();
1241  }
1242 
1243  // Read current results..
1244  for (int i = 0; i < mModel.rowCount(); i++) {
1245  QStandardItem *fileItem = mModel.item(i,0);
1246  for (int j = 0; j < fileItem->rowCount(); j++) {
1247  QStandardItem *error = fileItem->child(j,0);
1248  ErrorItem errorItem;
1249  readErrorItem(error, &errorItem);
1250  const int oldErrorIndex = indexOf(oldErrors, errorItem);
1251  QVariantMap data = error->data().toMap();
1252 
1253  // New error .. set the "sinceDate" property
1254  if (oldErrorIndex >= 0 && !oldErrors[oldErrorIndex].sinceDate.isEmpty()) {
1255  data[SINCEDATE] = oldErrors[oldErrorIndex].sinceDate;
1256  error->setData(data);
1257  fileItem->child(j, COLUMN_SINCE_DATE)->setText(oldErrors[oldErrorIndex].sinceDate);
1258  } else if (oldErrorIndex < 0 || data[SINCEDATE].toString().isEmpty()) {
1259  const QString sinceDate = QLocale::system().toString(QDate::currentDate(), QLocale::ShortFormat);
1260  data[SINCEDATE] = sinceDate;
1261  error->setData(data);
1262  fileItem->child(j, COLUMN_SINCE_DATE)->setText(sinceDate);
1263  if (oldErrorIndex < 0)
1264  continue;
1265  }
1266 
1267  if (!errorItem.tags.isEmpty())
1268  continue;
1269 
1270  const ErrorItem &oldErrorItem = oldErrors[oldErrorIndex];
1271  data[TAGS] = oldErrorItem.tags;
1272  error->setData(data);
1273  }
1274  }
1275 }
1276 
1277 void ResultsTree::readErrorItem(const QStandardItem *error, ErrorItem *item) const
1278 {
1279  // Get error's user data
1280  QVariantMap data = error->data().toMap();
1281 
1283  item->summary = data[SUMMARY].toString();
1284  item->message = data[MESSAGE].toString();
1285  item->errorId = data[ERRORID].toString();
1286  item->cwe = data[CWE].toInt();
1287  item->hash = data[HASH].toULongLong();
1288  item->inconclusive = data[INCONCLUSIVE].toBool();
1289  item->file0 = data[FILE0].toString();
1290  item->sinceDate = data[SINCEDATE].toString();
1291  item->tags = data[TAGS].toString();
1292 
1293  if (error->rowCount() == 0) {
1294  QErrorPathItem e;
1295  e.file = stripPath(data[FILENAME].toString(), true);
1296  e.line = data[LINE].toInt();
1297  e.info = data[MESSAGE].toString();
1298  item->errorPath << e;
1299  }
1300 
1301  for (int j = 0; j < error->rowCount(); j++) {
1302  const QStandardItem *child_error = error->child(j, 0);
1303  //Get error's user data
1304  QVariant child_userdata = child_error->data();
1305  //Convert it to QVariantMap
1306  QVariantMap child_data = child_userdata.toMap();
1307 
1308  QErrorPathItem e;
1309  e.file = stripPath(child_data[FILENAME].toString(), true);
1310  e.line = child_data[LINE].toInt();
1311  e.info = child_data[MESSAGE].toString();
1312  item->errorPath << e;
1313  }
1314 }
1315 
1316 void ResultsTree::updateSettings(bool showFullPath,
1317  bool saveFullPath,
1318  bool saveAllErrors,
1319  bool showErrorId,
1320  bool showInconclusive)
1321 {
1322  if (mShowFullPath != showFullPath) {
1323  mShowFullPath = showFullPath;
1324  refreshFilePaths();
1325  }
1326 
1327  mSaveFullPath = saveFullPath;
1328  mSaveAllErrors = saveAllErrors;
1329 
1330  showIdColumn(showErrorId);
1331  showInconclusiveColumn(showInconclusive);
1332 }
1333 
1334 void ResultsTree::setCheckDirectory(const QString &dir)
1335 {
1336  mCheckPath = dir;
1337 }
1338 
1339 
1341 {
1342  return mCheckPath;
1343 }
1344 
1345 QString ResultsTree::stripPath(const QString &path, bool saving) const
1346 {
1347  if ((!saving && mShowFullPath) || (saving && mSaveFullPath)) {
1348  return QString(path);
1349  }
1350 
1351  QDir dir(mCheckPath);
1352  return dir.relativeFilePath(path);
1353 }
1354 
1355 void ResultsTree::refreshFilePaths(QStandardItem *item)
1356 {
1357  if (!item) {
1358  return;
1359  }
1360 
1361  //Mark that this file's path hasn't been updated yet
1362  bool updated = false;
1363 
1364  //Loop through all errors within this file
1365  for (int i = 0; i < item->rowCount(); i++) {
1366  //Get error i
1367  QStandardItem *error = item->child(i, 0);
1368 
1369  if (!error) {
1370  continue;
1371  }
1372 
1373  //Get error's user data
1374  QVariant userdata = error->data();
1375  //Convert it to QVariantMap
1376  QVariantMap data = userdata.toMap();
1377 
1378  //Get list of files
1379  QString file = data[FILENAME].toString();
1380 
1381  //Update this error's text
1382  error->setText(stripPath(file, false));
1383 
1384  //If this error has backtraces make sure the files list has enough filenames
1385  if (error->hasChildren()) {
1386  //Loop through all files within the error
1387  for (int j = 0; j < error->rowCount(); j++) {
1388  //Get file
1389  QStandardItem *child = error->child(j, 0);
1390  if (!child) {
1391  continue;
1392  }
1393  //Get child's user data
1394  QVariant child_userdata = child->data();
1395  //Convert it to QVariantMap
1396  QVariantMap child_data = child_userdata.toMap();
1397 
1398  //Get list of files
1399  QString child_files = child_data[FILENAME].toString();
1400  //Update file's path
1401  child->setText(stripPath(child_files, false));
1402  }
1403  }
1404 
1405  //if the main file hasn't been updated yet, update it now
1406  if (!updated) {
1407  updated = true;
1408  item->setText(error->text());
1409  }
1410 
1411  }
1412 }
1413 
1415 {
1416  qDebug("Refreshing file paths");
1417 
1418  //Go through all file items (these are parent items that contain the errors)
1419  for (int i = 0; i < mModel.rowCount(); i++) {
1420  refreshFilePaths(mModel.item(i, 0));
1421  }
1422 }
1423 
1425 {
1426  return mVisibleErrors;
1427 }
1428 
1430 {
1431  return mModel.rowCount() > 0;
1432 }
1433 
1435 {
1436  QStringList labels;
1437  labels << tr("File") << tr("Severity") << tr("Line") << tr("Id") << tr("Inconclusive") << tr("Summary") << tr("Since date") << tr("Tag");
1438  mModel.setHorizontalHeaderLabels(labels);
1439  //TODO go through all the errors in the tree and translate severity and message
1440 }
1441 
1443 {
1444  mShowErrorId = show;
1445  if (show)
1446  showColumn(3);
1447  else
1448  hideColumn(3);
1449 }
1450 
1452 {
1453  if (show)
1454  showColumn(4);
1455  else
1456  hideColumn(4);
1457 }
1458 
1459 void ResultsTree::currentChanged(const QModelIndex &current, const QModelIndex &previous)
1460 {
1461  QTreeView::currentChanged(current, previous);
1462  emit treeSelectionChanged(current);
1463 }
List of applications user has specified to open errors with.
int getApplicationCount() const
Get the amount of applications in the list.
const Application & getApplication(const int index) const
Get specific application's name.
int getDefaultApplication() const
Return the default application.
A class containing information of the application to execute.
Definition: application.h:43
const QString & getParameters() const
Get application command line parameters.
Definition: application.h:68
const QString & getPath() const
Get application path.
Definition: application.h:60
const QString & getName() const
Get application name.
Definition: application.h:52
A class containing error data for one error.
Definition: erroritem.h:72
bool inconclusive
Definition: erroritem.h:87
QString file0
Definition: erroritem.h:84
int cwe
Definition: erroritem.h:90
QString summary
Definition: erroritem.h:88
Severity severity
Definition: erroritem.h:86
QString symbolNames
Definition: erroritem.h:93
static bool sameCID(const ErrorItem &errorItem1, const ErrorItem &errorItem2)
Compare "CID".
Definition: erroritem.cpp:87
unsigned long long hash
Definition: erroritem.h:91
QString message
Definition: erroritem.h:89
QList< QErrorPathItem > errorPath
Definition: erroritem.h:92
QString sinceDate
Definition: erroritem.h:96
QString tags
Definition: erroritem.h:97
QString errorId
Definition: erroritem.h:85
A class containing error data for one shown error line.
Definition: erroritem.h:111
unsigned long long hash
Definition: erroritem.h:118
bool inconclusive
Definition: erroritem.h:119
int line
Definition: erroritem.h:114
QString errorId
Definition: erroritem.h:116
Severity severity
Definition: erroritem.h:120
QString tags
Definition: erroritem.h:124
QString summary
Definition: erroritem.h:121
QString sinceDate
Definition: erroritem.h:123
QString file
Definition: erroritem.h:113
QString message
Definition: erroritem.h:122
static bool isCriticalErrorId(const std::string &id)
Definition: errorlogger.h:264
static bool isHeader2(const std::string &path)
Is filename a header based on file extension.
Definition: path.cpp:277
A class that reads and writes project files.
Definition: projectfile.h:46
static ProjectFile * getActiveProject()
Definition: projectfile.h:61
void setWarningTags(std::size_t hash, const QString &tags)
Set tags for a warning.
const QStringList & getTags() const
Definition: projectfile.h:233
void addSuppression(const SuppressionList::Suppression &suppression)
Add suppression.
bool write(const QString &filename=QString())
Write project file (to disk).
A class containing data for one error path item.
Definition: erroritem.h:52
QString file
Definition: erroritem.h:56
QString info
Definition: erroritem.h:59
A base class for reports.
Definition: report.h:34
virtual void writeError(const ErrorItem &error)=0
Write error to report.
virtual void writeHeader()=0
Write report header.
virtual void writeFooter()=0
Write report footer.
void close()
Close the report (file).
Definition: report.cpp:54
void hideResult()
Slot for context menu item to hide the current error message.
bool mSaveFullPath
Should full path of files be saved.
Definition: resultstree.h:486
void updateFromOldReport(const QString &filename)
Update items from old report (tag, sinceDate)
bool mShowCppcheck
Definition: resultstree.h:524
void treeSelectionChanged(const QModelIndex &current)
Signal for selection change in result tree.
static QString severityToIcon(Severity severity)
Convert a severity string to a icon filename.
void hideAllIdResult()
Slot for context menu item to hide all messages with the current message Id.
QString stripPath(const QString &path, bool saving) const
Removes checking directory from given path if mShowFullPath is false.
QString mFilter
A string used to filter the results for display.
Definition: resultstree.h:462
void openContainingFolder()
Slot for context menu item to open the folder containing the current file.
void suppressSelectedIds()
Slot for context menu item to suppress all messages with the current message id.
QStandardItem * addBacktraceFiles(QStandardItem *parent, const ErrorLine &item, const bool hide, const QString &icon, bool childOfMessage)
Add a new error item beneath a file or a backtrace item beneath an error.
void tagSelectedItems(const QString &tag)
tag selected items
void showHiddenResults()
Function to show results that were previous hidden with HideResult()
void updateSettings(bool showFullPath, bool saveFullPath, bool saveAllErrors, bool showErrorId, bool showInconclusive)
Update tree settings.
void resultsHidden(bool hidden)
Signal that results have been hidden or shown.
void clearRecheckFile(const QString &filename)
Clear errors of a file selected for recheck.
void clear()
Clear all errors from the tree.
static QStandardItem * createCheckboxItem(bool checked)
Create new normal item.
void showClangResults(bool show)
Show/hide clang-tidy/clang-analyzer errors.
bool showIdColumn() const
Returns true if column "Id" is shown.
Definition: resultstree.h:176
ShowTypes mShowSeverities
GUI severities.
Definition: resultstree.h:183
static QStandardItem * createLineNumberItem(const QString &linenumber)
Create new line number item.
bool mVisibleErrors
Are there any visible errors.
Definition: resultstree.h:510
bool mShowClang
Definition: resultstree.h:525
void saveSettings() const
Save all settings Column widths.
static QString getFilePath(const QStandardItem *target, bool fullPath)
Helper function returning the filename/full path of the error tree item target.
bool hasVisibleResults() const
Check if there are any visible results in view.
void checkSelected(QStringList selectedItems)
Signal to perform selected files recheck.
void translate()
Change all visible texts language.
QSettings * mSettings
Program settings.
Definition: resultstree.h:456
void loadSettings()
Load all settings Column widths.
void refreshTree()
Refresh tree by checking which of the items should be shown and which should be hidden.
bool addErrorItem(const ErrorItem &item)
Add a new item to the tree.
QStandardItemModel mModel
Item model for tree.
Definition: resultstree.h:450
void filterResults(const QString &filter)
Function to filter the displayed list of errors.
void readErrorItem(const QStandardItem *error, ErrorItem *item) const
Convert GUI error item into data error item.
QString mCheckPath
Path we are currently checking.
Definition: resultstree.h:504
bool mShowErrorId
true if optional column "Id" is shown
Definition: resultstree.h:498
ApplicationList * mApplications
List of applications to open errors with.
Definition: resultstree.h:468
void refreshFilePaths()
Hides/shows full file path on all error file items according to mShowFullPath.
ResultsTree(QWidget *parent=nullptr)
Definition: resultstree.cpp:86
void suppressHash()
Slot for context menu item to suppress message with hash.
bool mShowFullPath
Should full path of files be shown (true) or relative (false)
Definition: resultstree.h:480
QStandardItem * findFileItem(const QString &name) const
Finds a file item.
void saveResults(Report *report) const
Save results to a text stream.
QStandardItem * ensureFileItem(const QString &fullpath, const QString &file0, bool hide)
Ensures there's a item in the model for the specified file.
bool mSaveAllErrors
Save all errors (true) or only visible (false)
Definition: resultstree.h:492
void recheckSelectedFiles()
Slot for rechecking selected files.
void suppressIds(QStringList ids)
Suppress Ids.
QStringList mHiddenMessageId
Definition: resultstree.h:519
bool hasResults() const
Do we have results from check?
void currentChanged(const QModelIndex &current, const QModelIndex &previous) override
Slot for selection change in the results tree.
void startApplication(const QStandardItem *target, int application=-1)
Helper function to open an error within target with application*.
void initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler)
QString askFileDir(const QString &file)
Ask directory where file is located.
void saveErrors(Report *report, const QStandardItem *fileItem) const
Save all errors under specified item.
ThreadHandler * mThread
Definition: resultstree.h:522
QItemSelectionModel * mSelectionModel
Definition: resultstree.h:521
void showResults(ShowTypes::ShowType type, bool show)
Function to show/hide certain type of errors Refreshes the tree.
QStandardItem * mContextItem
Right clicked item (used by context menu slots)
Definition: resultstree.h:474
static QStandardItem * createNormalItem(const QString &name)
Create new normal item.
void context(int application)
Slot for context menu item to open an error with specified application.
void keyPressEvent(QKeyEvent *event) override
Definition: resultstree.cpp:97
void copy()
Slot for context menu item to copy selection to clipboard.
static QString severityToTranslatedString(Severity severity)
Convert Severity to translated string for GUI.
void showInconclusiveColumn(bool show)
Show optional column "Inconclusve".
void showCppcheckResults(bool show)
Show/hide cppcheck errors.
void setCheckDirectory(const QString &dir)
Set the directory we are checking.
void quickStartApplication(const QModelIndex &index)
Slot to quickstart an error with default application.
const QString & getCheckDirectory()
Get the directory we are checking.
void contextMenuEvent(QContextMenuEvent *e) override
Context menu event (user right clicked on the tree)
static ShowTypes::ShowType SeverityToShowType(Severity severity)
Convert severity string to ShowTypes value.
Definition: showtypes.cpp:36
static ShowTypes::ShowType VariantToShowType(const QVariant &data)
Convert QVariant (that contains an int) to Showtypes value.
Definition: showtypes.cpp:86
ShowType
Show types we have (i.e.
Definition: showtypes.h:44
@ ShowNone
Definition: showtypes.h:51
static Severity ShowTypeToSeverity(ShowTypes::ShowType type)
Convert ShowType to severity string.
Definition: showtypes.cpp:59
void show(ShowTypes::ShowType category, bool showing)
Show/hide the showtype.
Definition: showtypes.cpp:127
bool isShown(ShowTypes::ShowType category) const
Is the showtype visible in the GUI?
Definition: showtypes.cpp:117
This class handles creating threadresult and starting threads.
Definition: threadhandler.h:50
bool isChecking() const
Is checking running?
XML file report version 2.
Definition: xmlreportv2.h:40
QList< ErrorItem > read() override
Read contents of the report file.
bool open() override
Open existing report file.
Definition: xmlreportv2.cpp:84
std::string toString(Color c)
Definition: color.cpp:54
#define SUPPRESS_WARNING_GCC_POP
Definition: config.h:186
#define SUPPRESS_WARNING_CLANG_POP
Definition: config.h:188
#define SUPPRESS_WARNING_GCC_PUSH(warning)
Definition: config.h:185
#define SUPPRESS_WARNING_CLANG_PUSH(warning)
Definition: config.h:187
Severity
enum class for severity.
Definition: errortypes.h:63
std::string severityToString(Severity severity)
Definition: errortypes.cpp:50
@ none
No severity (default value).
@ warning
Warning.
@ portability
Portability warning.
@ style
Style warning.
@ debug
Debug message.
@ information
Checking information.
@ performance
Performance warning.
@ error
Programming error.
@ internal
Internal message.
#define SETTINGS_LAST_SOURCE_PATH
Definition: common.h:101
#define SETTINGS_SHOW_FULL_PATH
Definition: common.h:69
QString getPath(const QString &type)
Obtains the path of specified type Returns the path of specified type if not empty.
Definition: common.cpp:32
#define SETTINGS_SAVE_FULL_PATH
Definition: common.h:73
#define SETTINGS_RESULT_COLUMN_WIDTH
Definition: common.h:46
#define SETTINGS_INCONCLUSIVE_ERRORS
Definition: common.h:85
#define SETTINGS_SHOW_ERROR_ID
Definition: common.h:87
void setPath(const QString &type, const QString &value)
Stores last used path of specified type Stores provided path as last used path for specified type.
Definition: common.cpp:46
#define SETTINGS_SAVE_ALL_ERRORS
Definition: common.h:72
static constexpr int COLUMN_SINCE_DATE
Definition: resultstree.cpp:83
static constexpr char SEVERITY[]
Definition: resultstree.cpp:76
static constexpr char FILENAME[]
Definition: resultstree.cpp:69
static constexpr char SINCEDATE[]
Definition: resultstree.cpp:77
static constexpr char TAGS[]
Definition: resultstree.cpp:80
static constexpr char HIDE[]
Definition: resultstree.cpp:72
static constexpr char ERRORID[]
Definition: resultstree.cpp:68
static constexpr char FILE0[]
Definition: resultstree.cpp:70
static constexpr int COLUMN_TAGS
Definition: resultstree.cpp:84
static int indexOf(const QList< ErrorItem > &list, const ErrorItem &item)
static constexpr char HASH[]
Definition: resultstree.cpp:71
static constexpr char SYMBOLNAMES[]
Definition: resultstree.cpp:78
static constexpr char CWE[]
Definition: resultstree.cpp:67
static constexpr char INCONCLUSIVE[]
Definition: resultstree.cpp:73
static constexpr char LINE[]
Definition: resultstree.cpp:74
static constexpr char COLUMN[]
Definition: resultstree.cpp:66
static constexpr char SUMMARY[]
Definition: resultstree.cpp:79
static constexpr char MESSAGE[]
Definition: resultstree.cpp:75