Cppcheck
compliancereportdialog.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 "compliancereportdialog.h"
20 
21 #include "ui_compliancereportdialog.h"
22 
23 #include "errortypes.h"
24 #include "filelist.h"
25 #include "filesettings.h"
26 #include "importproject.h"
27 #include "projectfile.h"
28 
29 #include <algorithm>
30 #include <iterator>
31 #include <list>
32 #include <string>
33 #include <utility>
34 #include <vector>
35 
36 #include <QByteArray>
37 #include <QCheckBox>
38 #include <QComboBox>
39 #include <QCoreApplication>
40 #include <QCryptographicHash>
41 #include <QDialogButtonBox>
42 #include <QDir>
43 #include <QFile>
44 #include <QFileDialog>
45 #include <QFileInfo>
46 #include <QIODevice>
47 #include <QLineEdit>
48 #include <QList>
49 #include <QMessageBox>
50 #include <QProcess>
51 #include <QRegularExpression>
52 #include <QSet>
53 #include <QStringList>
54 #include <QTemporaryFile>
55 #include <QTextStream>
56 
57 static void addHeaders(const QString& file1, QSet<QString> &allFiles) {
58  if (allFiles.contains(file1))
59  return;
60  QFile file(file1);
61  if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
62  return;
63  allFiles << file1;
64  const QRegularExpression re("^#include[ ]*\"([^\">]+)\".*");
65  QTextStream in(&file);
66  QString line = in.readLine();
67  while (!in.atEnd()) {
68  if (line.startsWith("#include")) {
69  const QRegularExpressionMatch match = re.match(line);
70  if (match.hasMatch()) {
71  QString hfile = match.captured(1);
72  if (file1.contains("/"))
73  hfile = file1.mid(0,file1.lastIndexOf("/") + 1) + hfile;
74  addHeaders(hfile, allFiles);
75  }
76  }
77  line = in.readLine();
78  }
79 }
80 
81 static std::vector<std::string> toStdStringList(const QStringList& from) {
82  std::vector<std::string> ret;
83  std::transform(from.cbegin(), from.cend(), std::back_inserter(ret), [](const QString& e) {
84  return e.toStdString();
85  });
86  return ret;
87 }
88 
89 ComplianceReportDialog::ComplianceReportDialog(ProjectFile* projectFile, QString resultsFile) :
90  QDialog(nullptr),
91  mUI(new Ui::ComplianceReportDialog),
92  mProjectFile(projectFile),
93  mResultsFile(std::move(resultsFile))
94 {
95  mUI->setupUi(this);
96  mUI->mEditProjectName->setText(projectFile->getProjectName());
97  connect(mUI->buttonBox, &QDialogButtonBox::clicked, this, &ComplianceReportDialog::buttonClicked);
98  mUI->mCodingStandard->clear();
99  if (!projectFile->getCodingStandards().contains("misra-c-2023") && projectFile->getAddons().contains("misra"))
100  mUI->mCodingStandard->addItem("Misra C 2012");
101  for (QString std: projectFile->getCodingStandards()) {
102  std[0] = std[0].toUpper();
103  std = std.replace("-", " ").replace(" c ", " C ").replace(" cpp ", " C++ ").replace(" c++ ", " C++ ");
104  mUI->mCodingStandard->addItem(std);
105  }
106 }
107 
109 {
110  delete mUI;
111 }
112 
113 void ComplianceReportDialog::buttonClicked(QAbstractButton* button)
114 {
115  switch (mUI->buttonBox->standardButton(button)) {
116  case QDialogButtonBox::StandardButton::Save:
117  save();
118  break;
119  case QDialogButtonBox::StandardButton::Close:
120  close();
121  break;
122  default:
123  break;
124  };
125 }
126 
128 {
129  const QString std(mUI->mCodingStandard->currentText().toLower().replace(" ", "-"));
130 
131  const QString outFile = QFileDialog::getSaveFileName(this,
132  tr("Compliance report"),
133  QDir::homePath() + "/" + std + "-compliance-report.html",
134  tr("HTML files (*.html)"));
135  if (outFile.isEmpty())
136  return;
137 
138  close();
139 
140  const QString& projectName = mUI->mEditProjectName->text();
141  const QString& projectVersion = mUI->mEditProjectVersion->text();
142  const bool files = mUI->mCheckFiles->isChecked();
143 
144  if (projectName != mProjectFile->getProjectName()) {
145  mProjectFile->setProjectName(projectName);
146  mProjectFile->write();
147  }
148 
149  QTemporaryFile tempFiles;
150  if (files && tempFiles.open()) {
151  QTextStream out(&tempFiles);
152  FileList fileList;
154  if (!mProjectFile->getImportProject().isEmpty()) {
155  QFileInfo inf(mProjectFile->getFilename());
156 
157  QString prjfile;
158  if (QFileInfo(mProjectFile->getImportProject()).isAbsolute())
159  prjfile = mProjectFile->getImportProject();
160  else
161  prjfile = inf.canonicalPath() + '/' + mProjectFile->getImportProject();
162 
163  ImportProject p;
164  try {
165  p.import(prjfile.toStdString());
166  } catch (InternalError &e) {
167  QMessageBox msg(QMessageBox::Critical,
168  tr("Save compliance report"),
169  tr("Failed to import '%1' (%2), can not show files in compliance report").arg(prjfile).arg(QString::fromStdString(e.errorMessage)),
170  QMessageBox::Ok,
171  this);
172  msg.exec();
173  return;
174  }
175 
177 
178  QDir dir(inf.absoluteDir());
179  for (const FileSettings& fs: p.fileSettings)
180  fileList.addFile(dir.relativeFilePath(QString::fromStdString(fs.filename())));
181  }
182 
183  QSet<QString> allFiles;
184  for (const QString &sourcefile: fileList.getFileList())
185  addHeaders(sourcefile, allFiles);
186  for (const QString& fileName: allFiles) {
187  QFile f(fileName);
188  if (f.open(QFile::ReadOnly)) {
189  QCryptographicHash hash(QCryptographicHash::Algorithm::Md5);
190  if (hash.addData(&f)) {
191  for (auto b: hash.result())
192  out << QString::number((unsigned char)b,16);
193  out << " " << fileName << "\n";
194  }
195  }
196  }
197  tempFiles.close();
198  }
199 
200  QStringList suppressions;
201  for (const auto& suppression: mProjectFile->getSuppressions()) {
202  if (!suppression.errorId.empty())
203  suppressions.append(QString::fromStdString(suppression.errorId));
204  }
205 
206  QStringList args{"--project-name=" + projectName,
207  "--project-version=" + projectVersion,
208  "--output-file=" + outFile};
209  if (!suppressions.isEmpty())
210  args << "--suppressions=" + suppressions.join(",");
211 
212  args << ("--" + std);
213 
214  if (files)
215  args << "--files=" + tempFiles.fileName();
216  args << mResultsFile;
217 
218  const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath();
219 
220  QProcess process;
221 #ifdef Q_OS_WIN
222  process.start(appPath + "/compliance-report.exe", args);
223 #else
224  process.start(appPath + "/compliance-report", args);
225 #endif
226  process.waitForFinished();
227  const QString output = process.readAll();
228  if (!output.isEmpty()) {
229  QMessageBox msg(QMessageBox::Critical,
230  tr("Save compliance report"),
231  output,
232  QMessageBox::Ok,
233  this);
234  msg.exec();
235  }
236 }
static bool match(const Token *tok, const std::string &rhs)
Definition: astutils.cpp:342
Ui::ComplianceReportDialog * mUI
ComplianceReportDialog(ProjectFile *projectFile, QString resultsFile)
void buttonClicked(QAbstractButton *button)
A class for listing files and directories to check.
Definition: filelist.h:39
void addFile(const QString &filepath)
Add filename to the list.
Definition: filelist.cpp:50
QStringList getFileList() const
Return list of filenames (to check).
Definition: filelist.cpp:93
void addPathList(const QStringList &paths)
Add list of filenames and directories to the list.
Definition: filelist.cpp:82
Importing project settings.
Definition: importproject.h:52
std::list< FileSettings > fileSettings
Definition: importproject.h:70
void ignorePaths(const std::vector< std::string > &ipaths)
Type import(const std::string &filename, Settings *settings=nullptr)
A class that reads and writes project files.
Definition: projectfile.h:46
const QStringList & getCodingStandards() const
Get list of coding standards (checked by Cppcheck Premium).
Definition: projectfile.h:370
const QStringList & getAddons() const
Get list addons.
Definition: projectfile.h:200
const QString & getFilename() const
Get filename for the project file.
Definition: projectfile.h:257
const QList< SuppressionList::Suppression > & getSuppressions() const
Get "raw" suppressions.
Definition: projectfile.h:186
const QString & getProjectName() const
Definition: projectfile.h:174
QStringList getCheckPaths() const
Get list of paths to check.
Definition: projectfile.h:138
void setProjectName(QString projectName)
Definition: projectfile.h:178
QStringList getExcludedPaths() const
Get list of paths to exclude from the check.
Definition: projectfile.h:146
bool write(const QString &filename=QString())
Write project file (to disk).
const QString & getImportProject() const
Definition: projectfile.h:86
static std::vector< std::string > toStdStringList(const QStringList &from)
static void addHeaders(const QString &file1, QSet< QString > &allFiles)
Definition: aboutdialog.h:27
File settings.
Definition: filesettings.h:57
const std::string & filename() const
Definition: filesettings.h:68
Simple container to be thrown when internal error is detected.
Definition: errortypes.h:36
std::string errorMessage
Definition: errortypes.h:43