cppcheck/gui/statsdialog.cpp
Scott Furry a195477470 Correct Zero/Null as pointer constant (#1938)
Building with enhanced clang warnings indicated a large number of
instances with the warning:

`warning: zero as null pointer constant`

Recommended practice in C++11 is to use `nullptr` as value for
a NULL or empty pointer value. All instances where this warning
was encountered were corrected in this commit.

Where warning was encountered in dependency code (i.e. external library)
no chnages were made. Patching will be offered upstream.
2019-06-30 21:39:22 +02:00

408 lines
17 KiB
C++

/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2018 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QPrinter>
#include <QDate>
#include <QFileDialog>
#include <QFileInfo>
#include <QTextDocument>
#include <QWidget>
#include <QString>
#include <QClipboard>
#include <QMimeData>
#include "projectfile.h"
#include "statsdialog.h"
#include "checkstatistics.h"
#include "common.h"
static const QString CPPCHECK("cppcheck");
StatsDialog::StatsDialog(QWidget *parent)
: QDialog(parent),
mStatistics(nullptr)
{
mUI.setupUi(this);
setWindowFlags(Qt::Window);
connect(mUI.mCopyToClipboard, &QPushButton::pressed, this, &StatsDialog::copyToClipboard);
connect(mUI.mPDFexport, &QPushButton::pressed, this, &StatsDialog::pdfExport);
}
void StatsDialog::setProject(const ProjectFile* projectFile)
{
if (projectFile) {
mUI.mProject->setText(projectFile->getRootPath());
mUI.mPaths->setText(projectFile->getCheckPaths().join(";"));
mUI.mIncludePaths->setText(projectFile->getIncludeDirs().join(";"));
mUI.mDefines->setText(projectFile->getDefines().join(";"));
mUI.mUndefines->setText(projectFile->getUndefines().join(";"));
#ifndef HAVE_QCHART
mUI.mTabHistory->setVisible(false);
#else
QString statsFile;
if (!projectFile->getBuildDir().isEmpty()) {
const QString prjpath = QFileInfo(projectFile->getFilename()).absolutePath();
const QString buildDir = prjpath + '/' + projectFile->getBuildDir();
if (QDir(buildDir).exists()) {
statsFile = buildDir + "/statistics.txt";
}
}
mUI.mLblHistoryFile->setText(tr("File: ") + (statsFile.isEmpty() ? tr("No cppcheck build dir") : statsFile));
if (!statsFile.isEmpty()) {
QChartView *chartView;
chartView = createChart(statsFile, "cppcheck");
mUI.mTabHistory->layout()->addWidget(chartView);
if (projectFile->getClangAnalyzer()) {
chartView = createChart(statsFile, CLANG_ANALYZER);
mUI.mTabHistory->layout()->addWidget(chartView);
}
if (projectFile->getClangTidy()) {
chartView = createChart(statsFile, CLANG_TIDY);
mUI.mTabHistory->layout()->addWidget(chartView);
}
}
#endif
} else {
mUI.mProject->setText(QString());
mUI.mPaths->setText(QString());
mUI.mIncludePaths->setText(QString());
mUI.mDefines->setText(QString());
mUI.mUndefines->setText(QString());
}
}
void StatsDialog::setPathSelected(const QString& path)
{
mUI.mPath->setText(path);
}
void StatsDialog::setNumberOfFilesScanned(int num)
{
mUI.mNumberOfFilesScanned->setText(QString::number(num));
}
void StatsDialog::setScanDuration(double seconds)
{
// Factor the duration into units (days/hours/minutes/seconds)
int secs = seconds;
int days = secs / (24 * 60 * 60);
secs -= days * (24 * 60 * 60);
int hours = secs / (60 * 60);
secs -= hours * (60 * 60);
int mins = secs / 60;
secs -= mins * 60;
// Concatenate the two most significant units (e.g. "1 day and 3 hours")
QStringList parts;
if (days)
parts << ((days == 1) ? tr("1 day") : tr("%1 days").arg(days));
if (hours)
parts << ((hours == 1) ? tr("1 hour") : tr("%1 hours").arg(hours));
if (mins && parts.size() < 2)
parts << ((mins == 1) ? tr("1 minute") : tr("%1 minutes").arg(mins));
if (secs && parts.size() < 2)
parts << ((secs == 1) ? tr("1 second") : tr("%1 seconds").arg(secs));
// For durations < 1s, show the fraction of a second (e.g. "0.7 seconds")
if (parts.isEmpty())
parts << tr("0.%1 seconds").arg(int(10.0 *(seconds - secs)));
mUI.mScanDuration->setText(parts.join(tr(" and ")));
}
void StatsDialog::pdfExport()
{
const QString Stat = QString(
"<center><h1>%1 %2</h1></center>\n"
"<font color=\"red\"><h3>%3 : %4</h3></font>\n"
"<font color=\"green\"><h3>%5 : %6</h3></font>\n"
"<font color=\"orange\"><h3>%7 : %8</h3></font>\n"
"<font color=\"blue\"><h3>%9 : %10</h3></font>\n"
"<font color=\"blue\"><h3>%11 : %12</h3></font>\n"
"<font color=\"purple\"><h3>%13 : %14</h3></font>\n")
.arg(tr("Statistics"))
.arg(QDate::currentDate().toString("dd.MM.yyyy"))
.arg(tr("Errors"))
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowErrors))
.arg(tr("Warnings"))
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowWarnings))
.arg(tr("Style warnings"))
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowStyle))
.arg(tr("Portability warnings"))
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPortability))
.arg(tr("Performance warnings"))
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPerformance))
.arg(tr("Information messages"))
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowInformation));
QString fileName = QFileDialog::getSaveFileName((QWidget*)nullptr, tr("Export PDF"), QString(), "*.pdf");
if (QFileInfo(fileName).suffix().isEmpty()) {
fileName.append(".pdf");
}
QPrinter printer(QPrinter::PrinterResolution);
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setPaperSize(QPrinter::A4);
printer.setOutputFileName(fileName);
QTextDocument doc;
doc.setHtml(Stat);
// doc.setPageSize(printer.pageRect().size());
doc.print(&printer);
}
void StatsDialog::copyToClipboard()
{
QClipboard *clipboard = QApplication::clipboard();
if (!clipboard)
return;
const QString projSettings(tr("Project Settings"));
const QString project(tr("Project"));
const QString paths(tr("Paths"));
const QString incPaths(tr("Include paths"));
const QString defines(tr("Defines"));
const QString undefines(tr("Undefines"));
const QString prevScan(tr("Previous Scan"));
const QString selPath(tr("Path selected"));
const QString numFiles(tr("Number of files scanned"));
const QString duration(tr("Scan duration"));
const QString stats(tr("Statistics"));
const QString errors(tr("Errors"));
const QString warnings(tr("Warnings"));
const QString style(tr("Style warnings"));
const QString portability(tr("Portability warnings"));
const QString performance(tr("Performance warnings"));
const QString information(tr("Information messages"));
// Plain text summary
const QString settings = QString(
"%1\n"
"\t%2:\t%3\n"
"\t%4:\t%5\n"
"\t%6:\t%7\n"
"\t%8:\t%9\n"
"\t%10:\t%11\n"
)
.arg(projSettings)
.arg(project)
.arg(mUI.mProject->text())
.arg(paths)
.arg(mUI.mPaths->text())
.arg(incPaths)
.arg(mUI.mIncludePaths->text())
.arg(defines)
.arg(mUI.mDefines->text())
.arg(undefines)
.arg(mUI.mUndefines->text());
const QString previous = QString(
"%1\n"
"\t%2:\t%3\n"
"\t%4:\t%5\n"
"\t%6:\t%7\n"
)
.arg(prevScan)
.arg(selPath)
.arg(mUI.mPath->text())
.arg(numFiles)
.arg(mUI.mNumberOfFilesScanned->text())
.arg(duration)
.arg(mUI.mScanDuration->text());
const QString statistics = QString(
"%1\n"
"\t%2:\t%3\n"
"\t%4:\t%5\n"
"\t%6:\t%7\n"
"\t%8:\t%9\n"
"\t%10:\t%11\n"
"\t%12:\t%13\n"
)
.arg(stats)
.arg(errors)
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowErrors))
.arg(warnings)
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowWarnings))
.arg(style)
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowStyle))
.arg(portability)
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPortability))
.arg(performance)
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPerformance))
.arg(information)
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowInformation));
const QString textSummary = settings + previous + statistics;
// HTML summary
const QString htmlSettings = QString(
"<h3>%1<h3>\n"
"<table>\n"
" <tr><th>%2:</th><td>%3</td></tr>\n"
" <tr><th>%4:</th><td>%5</td></tr>\n"
" <tr><th>%6:</th><td>%7</td></tr>\n"
" <tr><th>%8:</th><td>%9</td></tr>\n"
" <tr><th>%10:</th><td>%11</td></tr>\n"
"</table>\n"
)
.arg(projSettings)
.arg(project)
.arg(mUI.mProject->text())
.arg(paths)
.arg(mUI.mPaths->text())
.arg(incPaths)
.arg(mUI.mIncludePaths->text())
.arg(defines)
.arg(mUI.mDefines->text())
.arg(undefines)
.arg(mUI.mUndefines->text());
const QString htmlPrevious = QString(
"<h3>%1</h3>\n"
"<table>\n"
" <tr><th>%2:</th><td>%3</td></tr>\n"
" <tr><th>%4:</th><td>%5</td></tr>\n"
" <tr><th>%6:</th><td>%7</td></tr>\n"
"</table>\n"
)
.arg(prevScan)
.arg(selPath)
.arg(mUI.mPath->text())
.arg(numFiles)
.arg(mUI.mNumberOfFilesScanned->text())
.arg(duration)
.arg(mUI.mScanDuration->text());
const QString htmlStatistics = QString(
"<h3>%1</h3>\n"
" <tr><th>%2:</th><td>%3</td></tr>\n"
" <tr><th>%4:</th><td>%5</td></tr>\n"
" <tr><th>%6:</th><td>%7</td></tr>\n"
" <tr><th>%8:</th><td>%9</td></tr>\n"
" <tr><th>%10:</th><td>%11</td></tr>\n"
" <tr><th>%12:</th><td>%13</td></tr>\n"
"</table>\n"
)
.arg(stats)
.arg(errors)
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowErrors))
.arg(warnings)
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowWarnings))
.arg(style)
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowStyle))
.arg(portability)
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPortability))
.arg(performance)
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPerformance))
.arg(information)
.arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowInformation));
const QString htmlSummary = htmlSettings + htmlPrevious + htmlStatistics;
QMimeData *mimeData = new QMimeData();
mimeData->setText(textSummary);
mimeData->setHtml(htmlSummary);
clipboard->setMimeData(mimeData);
}
void StatsDialog::setStatistics(const CheckStatistics *stats)
{
mStatistics = stats;
mUI.mLblErrors->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowErrors)));
mUI.mLblWarnings->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowWarnings)));
mUI.mLblStyle->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowStyle)));
mUI.mLblPortability->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowPortability)));
mUI.mLblPerformance->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowPerformance)));
mUI.mLblInformation->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowInformation)));
}
#ifdef HAVE_QCHART
QChartView *StatsDialog::createChart(const QString &statsFile, const QString &tool)
{
QChart *chart = new QChart;
chart->addSeries(numberOfReports(statsFile, tool + "-error"));
chart->addSeries(numberOfReports(statsFile, tool + "-warning"));
chart->addSeries(numberOfReports(statsFile, tool + "-style"));
chart->addSeries(numberOfReports(statsFile, tool + "-performance"));
chart->addSeries(numberOfReports(statsFile, tool + "-portability"));
QDateTimeAxis *axisX = new QDateTimeAxis;
axisX->setTitleText("Date");
chart->addAxis(axisX, Qt::AlignBottom);
foreach (QAbstractSeries *s, chart->series()) {
s->attachAxis(axisX);
}
QValueAxis *axisY = new QValueAxis;
axisY->setLabelFormat("%i");
axisY->setTitleText("Count");
chart->addAxis(axisY, Qt::AlignLeft);
qreal maxY = 0;
foreach (QAbstractSeries *s, chart->series()) {
s->attachAxis(axisY);
if (QLineSeries *ls = dynamic_cast<QLineSeries*>(s)) {
foreach (QPointF p, ls->points()) {
if (p.y() > maxY)
maxY = p.y();
}
}
}
axisY->setMax(maxY);
//chart->createDefaultAxes();
chart->setTitle(tool);
QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
return chartView;
}
QLineSeries *StatsDialog::numberOfReports(const QString &fileName, const QString &severity) const
{
QLineSeries *series = new QLineSeries();
series->setName(severity);
QFile f(fileName);
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
quint64 t = 0;
QTextStream in(&f);
while (!in.atEnd()) {
QString line = in.readLine();
QRegExp rxdate("\\[(\\d\\d)\\.(\\d\\d)\\.(\\d\\d\\d\\d)\\]");
if (rxdate.exactMatch(line)) {
int y = rxdate.cap(3).toInt();
int m = rxdate.cap(2).toInt();
int d = rxdate.cap(1).toInt();
QDateTime dt;
dt.setDate(QDate(y,m,d));
if (t == dt.toMSecsSinceEpoch())
t += 1000;
else
t = dt.toMSecsSinceEpoch();
}
if (line.startsWith(severity + ':')) {
int y = line.mid(1+severity.length()).toInt();
series->append(t, y);
}
}
}
return series;
}
#endif