Fixed #2363 (GUI: Using addons in the checking)

This commit is contained in:
Daniel Marjamäki 2017-08-03 12:30:28 +02:00
parent 3c7f6cf9c8
commit dfc48be70e
9 changed files with 277 additions and 4 deletions

View File

@ -18,6 +18,9 @@
#include <QString> #include <QString>
#include <QDebug> #include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QProcess>
#include "checkthread.h" #include "checkthread.h"
#include "threadresult.h" #include "threadresult.h"
#include "cppcheck.h" #include "cppcheck.h"
@ -69,10 +72,47 @@ void CheckThread::run()
return; return;
} }
QString addonPath;
if (QFileInfo(mDataDir + "/threadsafety.py").exists())
addonPath = mDataDir;
else if (QDir(mDataDir + "/addons").exists())
addonPath = mDataDir + "/addons";
else if (mDataDir.endsWith("/cfg")) {
if (QDir(mDataDir.mid(0,mDataDir.size()-3) + "addons").exists())
addonPath = mDataDir.mid(0,mDataDir.size()-3) + "addons";
}
bool needDump = mAddons.contains("y2038") || mAddons.contains("threadsafety") || mAddons.contains("cert") || mAddons.contains("misra");
QString file = mResult.getNextFile(); QString file = mResult.getNextFile();
while (!file.isEmpty() && mState == Running) { while (!file.isEmpty() && mState == Running) {
qDebug() << "Checking file" << file; qDebug() << "Checking file" << file;
mCppcheck.check(file.toStdString()); mCppcheck.check(file.toStdString());
if (!mAddons.isEmpty()) {
if (needDump) {
mCppcheck.settings().dump = true;
mCppcheck.check(file.toStdString());
mCppcheck.settings().dump = false;
}
foreach (const QString addon, mAddons) {
if (addon == "clang")
continue;
QProcess process;
QString a;
if (QFileInfo(addonPath + '/' + addon + ".py").exists())
a = addonPath + '/' + addon + ".py";
else if (QFileInfo(addonPath + '/' + addon + '/' + addon + ".py").exists())
a = addonPath + '/' + addon + '/' + addon + ".py";
else
continue;
QString dumpFile = QString::fromStdString(file + ".dump");
QString cmd = "python " + a + ' ' + dumpFile;
qDebug() << cmd;
process.start(cmd);
process.waitForFinished();
QString err(process.readAllStandardError());
parseErrors(err, addon);
}
}
emit fileChecked(file); emit fileChecked(file);
if (mState == Running) if (mState == Running)
@ -84,6 +124,45 @@ void CheckThread::run()
file = QString::fromStdString(fileSettings.filename); file = QString::fromStdString(fileSettings.filename);
qDebug() << "Checking file" << file; qDebug() << "Checking file" << file;
mCppcheck.check(fileSettings); mCppcheck.check(fileSettings);
if (!mAddons.isEmpty()) {
if (needDump) {
mCppcheck.settings().dump = true;
mCppcheck.check(fileSettings);
mCppcheck.settings().dump = false;
}
foreach (const QString addon, mAddons) {
QProcess process;
if (addon == "clang") {
QString cmd("clang --analyze");
for (std::list<std::string>::const_iterator I = fileSettings.includePaths.begin(); I != fileSettings.includePaths.end(); ++I)
cmd += " -I" + QString::fromStdString(*I);
foreach (QString D, QString::fromStdString(fileSettings.defines).split(";"))
cmd += " -D" + D;
QString fileName = QString::fromStdString(fileSettings.filename);
if (fileName.endsWith(".cpp"))
cmd += " -std=c++11";
cmd += ' ' + fileName;
qDebug() << cmd;
process.start(cmd);
process.waitForFinished(600*1000);
} else {
QString a;
if (QFileInfo(addonPath + '/' + addon + ".py").exists())
a = addonPath + '/' + addon + ".py";
else if (QFileInfo(addonPath + '/' + addon + '/' + addon + ".py").exists())
a = addonPath + '/' + addon + '/' + addon + ".py";
else
continue;
QString dumpFile = QString::fromStdString(fileSettings.filename + ".dump");
QString cmd = "python " + a + ' ' + dumpFile;
qDebug() << cmd;
process.start(cmd);
process.waitForFinished();
}
QString err(process.readAllStandardError());
parseErrors(err, addon);
}
}
emit fileChecked(file); emit fileChecked(file);
if (mState == Running) if (mState == Running)
@ -103,3 +182,46 @@ void CheckThread::stop()
mState = Stopping; mState = Stopping;
mCppcheck.terminate(); mCppcheck.terminate();
} }
void CheckThread::parseErrors(QString err, QString tool)
{
QTextStream in(&err, QIODevice::ReadOnly);
while (!in.atEnd()) {
QString line = in.readLine();
if (tool == "clang") {
QRegExp r("([^:]+):([0-9]+):[0-9]+: (warning|error): (.*)");
if (!r.exactMatch(line))
continue;
const std::string filename = r.cap(1).toStdString();
const int lineNumber = r.cap(2).toInt();
Severity::SeverityType severity = (r.cap(3) == "error") ? Severity::error : Severity::warning;
const std::string message = r.cap(4).toStdString();
const std::string id = tool.toStdString();
std::list<ErrorLogger::ErrorMessage::FileLocation> callstack;
callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(filename, lineNumber));
ErrorLogger::ErrorMessage errmsg(callstack, filename, severity, message, id, false);
mResult.reportErr(errmsg);
} else {
QRegExp r1("\\[([^:]+):([0-9]+)\\](.*)");
if (!r1.exactMatch(line))
continue;
const std::string &filename = r1.cap(1).toStdString();
const int lineNumber = r1.cap(2).toInt();
std::string message, id;
QRegExp r2("(.*)\\[([a-zA-Z0-9\\-\\._]+)\\]");
if (r2.exactMatch(r1.cap(3))) {
message = r2.cap(1).toStdString();
id = tool.toStdString() + '-' + r2.cap(2).toStdString();
} else {
message = r1.cap(3).toStdString();
id = tool.toStdString();
}
std::list<ErrorLogger::ErrorMessage::FileLocation> callstack;
callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(filename, lineNumber));
ErrorLogger::ErrorMessage errmsg(callstack, filename, Severity::style, message, id, false);
mResult.reportErr(errmsg);
}
}
}

View File

@ -52,6 +52,14 @@ public:
*/ */
void analyseWholeProgram(const QStringList &files); void analyseWholeProgram(const QStringList &files);
void setAddons(const QStringList &addons) {
mAddons = addons;
}
void setDataDir(const QString &dataDir) {
mDataDir = dataDir;
}
/** /**
* @brief method that is run in a thread * @brief method that is run in a thread
* *
@ -94,13 +102,16 @@ protected:
ThreadResult &mResult; ThreadResult &mResult;
/** /**
* @brief Cppcheck itself * @brief Cppcheck itself
*
*/ */
CppCheck mCppcheck; CppCheck mCppcheck;
private: private:
void parseErrors(QString err, QString tool);
QStringList mFiles; QStringList mFiles;
bool mAnalyseWholeProgram; bool mAnalyseWholeProgram;
QStringList mAddons;
QString mDataDir;
}; };
/// @} /// @}
#endif // CHECKTHREAD_H #endif // CHECKTHREAD_H

View File

@ -66,6 +66,7 @@ MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) :
{ {
mUI.setupUi(this); mUI.setupUi(this);
mThread = new ThreadHandler(this); mThread = new ThreadHandler(this);
mThread->setDataDir(mSettings->value("DATADIR", QString()).toString());
mUI.mResults->initialize(mSettings, mApplications, mThread); mUI.mResults->initialize(mSettings, mApplications, mThread);
// Filter timer to delay filtering results slightly while typing // Filter timer to delay filtering results slightly while typing
@ -411,6 +412,8 @@ void MainWindow::doAnalyzeProject(ImportProject p)
} }
//mThread->SetanalyzeProject(true); //mThread->SetanalyzeProject(true);
if (mProjectFile)
mThread->setAddons(mProjectFile->getAddons());
mThread->setProject(p); mThread->setProject(p);
mThread->check(checkSettings, true); mThread->check(checkSettings, true);
} }
@ -1364,6 +1367,8 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile)
QFileInfo inf(projectFile->getFilename()); QFileInfo inf(projectFile->getFilename());
const QString rootpath = projectFile->getRootPath(); const QString rootpath = projectFile->getRootPath();
mThread->setAddons(projectFile->getAddons());
// If the root path is not given or is not "current dir", use project // If the root path is not given or is not "current dir", use project
// file's location directory as root path // file's location directory as root path
if (rootpath.isEmpty() || rootpath == ".") if (rootpath.isEmpty() || rootpath == ".")

View File

@ -50,6 +50,8 @@ static const char LibrariesElementName[] = "libraries";
static const char LibraryElementName[] = "library"; static const char LibraryElementName[] = "library";
static const char SuppressionsElementName[] = "suppressions"; static const char SuppressionsElementName[] = "suppressions";
static const char SuppressionElementName[] = "suppression"; static const char SuppressionElementName[] = "suppression";
static const char AddonElementName[] = "addon";
static const char AddonsElementName[] = "addons";
ProjectFile::ProjectFile(QObject *parent) : ProjectFile::ProjectFile(QObject *parent) :
QObject(parent) QObject(parent)
@ -122,6 +124,10 @@ bool ProjectFile::read(const QString &filename)
if (insideProject && xmlReader.name() == SuppressionsElementName) if (insideProject && xmlReader.name() == SuppressionsElementName)
readStringList(mSuppressions, xmlReader,SuppressionElementName); readStringList(mSuppressions, xmlReader,SuppressionElementName);
// Addons
if (insideProject && xmlReader.name() == AddonsElementName)
readStringList(mAddons, xmlReader, AddonElementName);
break; break;
case QXmlStreamReader::EndElement: case QXmlStreamReader::EndElement:
@ -433,6 +439,11 @@ void ProjectFile::setSuppressions(const QStringList &suppressions)
mSuppressions = suppressions; mSuppressions = suppressions;
} }
void ProjectFile::setAddons(const QStringList &addons)
{
mAddons = addons;
}
bool ProjectFile::write(const QString &filename) bool ProjectFile::write(const QString &filename)
{ {
if (!filename.isEmpty()) if (!filename.isEmpty())
@ -516,6 +527,11 @@ bool ProjectFile::write(const QString &filename)
SuppressionsElementName, SuppressionsElementName,
SuppressionElementName); SuppressionElementName);
writeStringList(xmlWriter,
mAddons,
AddonsElementName,
AddonElementName);
xmlWriter.writeEndDocument(); xmlWriter.writeEndDocument();
file.close(); file.close();
return true; return true;

View File

@ -110,6 +110,14 @@ public:
return mSuppressions; return mSuppressions;
} }
/**
* @brief Get list addons.
* @return list of addons.
*/
QStringList getAddons() const {
return mAddons;
}
/** /**
* @brief Get filename for the project file. * @brief Get filename for the project file.
* @return file name. * @return file name.
@ -171,6 +179,12 @@ public:
*/ */
void setSuppressions(const QStringList &suppressions); void setSuppressions(const QStringList &suppressions);
/**
* @brief Set list of addons.
* @param addons List of addons.
*/
void setAddons(const QStringList &addons);
/** /**
* @brief Write project file (to disk). * @brief Write project file (to disk).
* @param filename Filename to use. * @param filename Filename to use.
@ -297,6 +311,11 @@ private:
* @brief List of suppressions. * @brief List of suppressions.
*/ */
QStringList mSuppressions; QStringList mSuppressions;
/**
* @brief List of addons.
*/
QStringList mAddons;
}; };
/// @} /// @}
#endif // PROJECT_FILE_H #endif // PROJECT_FILE_H

View File

@ -141,6 +141,11 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
setExcludedPaths(projectFile->getExcludedPaths()); setExcludedPaths(projectFile->getExcludedPaths());
setLibraries(projectFile->getLibraries()); setLibraries(projectFile->getLibraries());
setSuppressions(projectFile->getSuppressions()); setSuppressions(projectFile->getSuppressions());
mUI.mAddonThreadSafety->setChecked(projectFile->getAddons().contains("threadsafety"));
mUI.mAddonY2038->setChecked(projectFile->getAddons().contains("y2038"));
mUI.mAddonCert->setChecked(projectFile->getAddons().contains("cert"));
mUI.mAddonMisra->setChecked(projectFile->getAddons().contains("misra"));
mUI.mClang->setChecked(projectFile->getAddons().contains("clang"));
updatePathsAndDefines(); updatePathsAndDefines();
} }
@ -155,6 +160,18 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
projectFile->setExcludedPaths(getExcludedPaths()); projectFile->setExcludedPaths(getExcludedPaths());
projectFile->setLibraries(getLibraries()); projectFile->setLibraries(getLibraries());
projectFile->setSuppressions(getSuppressions()); projectFile->setSuppressions(getSuppressions());
QStringList list;
if (mUI.mAddonThreadSafety->isChecked())
list << "threadsafety";
if (mUI.mAddonY2038->isChecked())
list << "y2038";
if (mUI.mAddonCert->isChecked())
list << "cert";
if (mUI.mAddonMisra->isChecked())
list << "misra";
if (mUI.mClang->isChecked())
list << "clang";
projectFile->setAddons(list);
} }
void ProjectFileDialog::ok() void ProjectFileDialog::ok()
@ -224,7 +241,7 @@ void ProjectFileDialog::browseImportProject()
const QDir &dir = inf.absoluteDir(); const QDir &dir = inf.absoluteDir();
QString fileName = QFileDialog::getOpenFileName(this, tr("Import Project"), QString fileName = QFileDialog::getOpenFileName(this, tr("Import Project"),
dir.canonicalPath(), dir.canonicalPath(),
tr("Visual Studio (*.sln *.vcxproj);;Compile database (compile_database.json)")); tr("Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json)"));
if (!fileName.isEmpty()) { if (!fileName.isEmpty()) {
mUI.mEditImportProject->setText(dir.relativeFilePath(fileName)); mUI.mEditImportProject->setText(dir.relativeFilePath(fileName));
updatePathsAndDefines(); updatePathsAndDefines();

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>642</width> <width>642</width>
<height>498</height> <height>507</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -463,6 +463,75 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Addons</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="QCheckBox" name="mAddonY2038">
<property name="text">
<string>Y2038</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mAddonThreadSafety">
<property name="text">
<string>Thread safety</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Coding standards</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mAddonCert">
<property name="text">
<string>Cert</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mAddonMisra">
<property name="text">
<string>Misra</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Other</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mClang">
<property name="text">
<string>clang</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>368</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
<item> <item>

View File

@ -46,6 +46,7 @@ void ThreadHandler::clearFiles()
mLastFiles.clear(); mLastFiles.clear();
mResults.clearFiles(); mResults.clearFiles();
mAnalyseWholeProgram = false; mAnalyseWholeProgram = false;
mAddons.clear();
} }
void ThreadHandler::setFiles(const QStringList &files) void ThreadHandler::setFiles(const QStringList &files)
@ -91,6 +92,8 @@ void ThreadHandler::check(const Settings &settings, bool all)
} }
for (int i = 0; i < mRunningThreadCount; i++) { for (int i = 0; i < mRunningThreadCount; i++) {
mThreads[i]->setAddons(mAddons);
mThreads[i]->setDataDir(mDataDir);
mThreads[i]->check(settings); mThreads[i]->check(settings);
} }
@ -125,7 +128,6 @@ void ThreadHandler::setThreadCount(const int count)
connect(mThreads.last(), &CheckThread::fileChecked, connect(mThreads.last(), &CheckThread::fileChecked,
&mResults, &ThreadResult::fileChecked); &mResults, &ThreadResult::fileChecked);
} }
} }

View File

@ -71,6 +71,14 @@ public:
*/ */
void saveSettings(QSettings &settings) const; void saveSettings(QSettings &settings) const;
void setAddons(const QStringList &addons) {
mAddons = addons;
}
void setDataDir(const QString &dataDir) {
mDataDir = dataDir;
}
/** /**
* @brief Clear all files from cppcheck * @brief Clear all files from cppcheck
* *
@ -236,6 +244,10 @@ protected:
int mRunningThreadCount; int mRunningThreadCount;
bool mAnalyseWholeProgram; bool mAnalyseWholeProgram;
QStringList mAddons;
QString mDataDir;
private: private:
/** /**