diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index ec52843ff..55ef4ed6a 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -946,6 +946,20 @@ Settings MainWindow::getCppcheckSettings() json += " }"; result.addons.push_back(json.toStdString()); } + + if (isCppcheckPremium()) { + QString premiumArgs; + if (mProjectFile->getBughunting()) + premiumArgs += " --bughunting"; + if (mProjectFile->getCertIntPrecision() > 0) + premiumArgs += " --cert-c-int-precision=" + QString::number(mProjectFile->getCertIntPrecision()); + if (mProjectFile->getAddons().contains("misra")) + premiumArgs += " --misra-c-2012"; + for (const QString& c: mProjectFile->getCodingStandards()) { + premiumArgs += " --" + c; + } + result.premiumArgs = premiumArgs.mid(1).toStdString(); + } } // Include directories (and files) are searched in listed order. @@ -1664,7 +1678,7 @@ void MainWindow::newProjectFile() mProjectFile->setFilename(filepath); mProjectFile->setBuildDir(filename.left(filename.indexOf(".")) + "-cppcheck-build-dir"); - ProjectFileDialog dlg(mProjectFile, this); + ProjectFileDialog dlg(mProjectFile, isCppcheckPremium(), this); if (dlg.exec() == QDialog::Accepted) { addProjectMRU(filepath); analyzeProject(mProjectFile); @@ -1695,7 +1709,7 @@ void MainWindow::editProjectFile() return; } - ProjectFileDialog dlg(mProjectFile, this); + ProjectFileDialog dlg(mProjectFile, isCppcheckPremium(), this); if (dlg.exec() == QDialog::Accepted) { mProjectFile->write(); analyzeProject(mProjectFile); @@ -1864,3 +1878,7 @@ void MainWindow::suppressIds(QStringList ids) mProjectFile->setSuppressions(suppressions); mProjectFile->write(); } + +bool MainWindow::isCppcheckPremium() const { + return mCppcheckCfgProductName.startsWith("Cppcheck Premium "); +} diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 5621ca12e..564674079 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -230,6 +230,8 @@ protected slots: private: + bool isCppcheckPremium() const; + /** Get filename for last results */ QString getLastResults() const; diff --git a/gui/projectfile.cpp b/gui/projectfile.cpp index 3eb49ca73..cb7d2e41b 100644 --- a/gui/projectfile.cpp +++ b/gui/projectfile.cpp @@ -74,6 +74,11 @@ void ProjectFile::clear() mVsConfigurations.clear(); mTags.clear(); mWarningTags.clear(); + + // Premium + mBughunting = false; + mCertIntPrecision = 0; + mCodingStandards.clear(); } bool ProjectFile::read(const QString &filename) @@ -195,6 +200,15 @@ bool ProjectFile::read(const QString &filename) // VSConfiguration if (xmlReader.name() == QString(CppcheckXml::VSConfigurationElementName)) readVsConfigurations(xmlReader); + + // Cppcheck Premium + if (xmlReader.name() == QString(CppcheckXml::BughuntingElementName)) + mBughunting = true; + if (xmlReader.name() == QString(CppcheckXml::CodingStandardsElementName)) + readStringList(mAddons, xmlReader, CppcheckXml::CodingStandardElementName); + if (xmlReader.name() == QString(CppcheckXml::CertIntPrecisionElementName)) + mCertIntPrecision = readInt(xmlReader, 0); + break; case QXmlStreamReader::EndElement: @@ -928,6 +942,23 @@ bool ProjectFile::write(const QString &filename) } } + // Cppcheck Premium + if (mBughunting) { + xmlWriter.writeStartElement(CppcheckXml::BughuntingElementName); + xmlWriter.writeEndElement(); + } + + writeStringList(xmlWriter, + mCodingStandards, + CppcheckXml::CodingStandardsElementName, + CppcheckXml::CodingStandardElementName); + + if (mCertIntPrecision > 0) { + xmlWriter.writeStartElement(CppcheckXml::CertIntPrecisionElementName); + xmlWriter.writeCharacters(QString::number(mCertIntPrecision)); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndDocument(); file.close(); return true; diff --git a/gui/projectfile.h b/gui/projectfile.h index 8951a4f94..92abca02a 100644 --- a/gui/projectfile.h +++ b/gui/projectfile.h @@ -333,6 +333,36 @@ public: /** Get tags for a warning */ QString getWarningTags(std::size_t hash) const; + /** Bughunting (Cppcheck Premium) */ + void setBughunting(bool bughunting) { + mBughunting = bughunting; + } + bool getBughunting() const { + return mBughunting; + } + + /** @brief Get list of coding standards (checked by Cppcheck Premium). */ + QStringList getCodingStandards() const { + return mCodingStandards; + } + + /** + * @brief Set list of coding standards (checked by Cppcheck Premium). + * @param codingStandards List of coding standards. + */ + void setCodingStandards(QStringList codingStandards) { + mCodingStandards = std::move(codingStandards); + } + + /** Cert C: int precision */ + void setCertIntPrecision(int p) { + mCertIntPrecision = p; + } + int getCertIntPrecision() const { + return mCertIntPrecision; + } + + /** * @brief Write project file (to disk). * @param filename Filename to use. @@ -543,6 +573,15 @@ private: */ QStringList mAddons; + bool mBughunting; + + /** + * @brief List of coding standards, checked by Cppcheck Premium. + */ + QStringList mCodingStandards; + + int mCertIntPrecision; + /** @brief Execute clang analyzer? */ bool mClangAnalyzer; diff --git a/gui/projectfile.ui b/gui/projectfile.ui index f35f54daa..96384385b 100644 --- a/gui/projectfile.ui +++ b/gui/projectfile.ui @@ -773,24 +773,26 @@ + + + + + + + Coding standards + + - + - Coding standards + Misra C 2012 - - - MISRA C 2012 - - - - - + - + MISRA rule texts @@ -812,6 +814,57 @@ + + + + Cert C + + + + + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + + + + + + + + Misra C++ 2008 + + + + + + + Autosar + + + + + + + + + + Bug hunting + + + + + + Bug hunting + + + diff --git a/gui/projectfiledialog.cpp b/gui/projectfiledialog.cpp index 33cc88aeb..d3d242460 100644 --- a/gui/projectfiledialog.cpp +++ b/gui/projectfiledialog.cpp @@ -40,6 +40,11 @@ #include #include +static const char ADDON_MISRA[] = "misra"; +static const char CODING_STANDARD_MISRA_CPP_2008[] = "misra-cpp-2008"; +static const char CODING_STANDARD_CERT_C[] = "cert-c-2016"; +static const char CODING_STANDARD_AUTOSAR[] = "autosar"; + class QModelIndex; /** Return paths from QListWidget */ @@ -79,10 +84,11 @@ QStringList ProjectFileDialog::getProjectConfigs(const QString &fileName) return ret; } -ProjectFileDialog::ProjectFileDialog(ProjectFile *projectFile, QWidget *parent) +ProjectFileDialog::ProjectFileDialog(ProjectFile *projectFile, bool premium, QWidget *parent) : QDialog(parent) , mUI(new Ui::ProjectFile) , mProjectFile(projectFile) + , mPremium(premium) { mUI->setupUi(this); @@ -191,6 +197,9 @@ ProjectFileDialog::ProjectFileDialog(ProjectFile *projectFile, QWidget *parent) platformFiles.sort(); mUI->mComboBoxPlatform->addItems(platformFiles); + // integer. allow empty. + mUI->mEditCertIntPrecision->setValidator(new QRegularExpressionValidator(QRegularExpression("[0-9]*"),this)); + mUI->mEditTags->setValidator(new QRegularExpressionValidator(QRegularExpression("[a-zA-Z0-9 ;]*"),this)); const QRegularExpression undefRegExp("\\s*([a-zA-Z_][a-zA-Z0-9_]*[; ]*)*"); @@ -340,15 +349,36 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile) const QString dataDir = getDataDir(); updateAddonCheckBox(mUI->mAddonThreadSafety, projectFile, dataDir, "threadsafety"); updateAddonCheckBox(mUI->mAddonY2038, projectFile, dataDir, "y2038"); - updateAddonCheckBox(mUI->mAddonMisra, projectFile, dataDir, "misra"); + updateAddonCheckBox(mUI->mMisraC2012, projectFile, dataDir, ADDON_MISRA); const QString &misraFile = settings.value(SETTINGS_MISRA_FILE, QString()).toString(); mUI->mEditMisraFile->setText(misraFile); - if (!mUI->mAddonMisra->isEnabled()) { + if (mPremium) { + mUI->mLabelMisraFile->setVisible(false); + mUI->mEditMisraFile->setVisible(false); + mUI->mBtnBrowseMisraFile->setVisible(false); + } else if (!mUI->mMisraC2012->isEnabled()) { mUI->mEditMisraFile->setEnabled(false); mUI->mBtnBrowseMisraFile->setEnabled(false); } + mUI->mPremiumCertC->setChecked(projectFile->getCodingStandards().contains(CODING_STANDARD_CERT_C)); + mUI->mMisraCpp2008->setChecked(projectFile->getCodingStandards().contains(CODING_STANDARD_MISRA_CPP_2008)); + mUI->mAutosar->setChecked(projectFile->getCodingStandards().contains(CODING_STANDARD_AUTOSAR)); + + if (projectFile->getCertIntPrecision() <= 0) + mUI->mEditCertIntPrecision->setText(QString()); + else + mUI->mEditCertIntPrecision->setText(QString::number(projectFile->getCertIntPrecision())); + + mUI->mPremiumCertC->setVisible(mPremium); + mUI->mMisraCpp2008->setVisible(mPremium); + mUI->mAutosar->setVisible(mPremium); + mUI->mLabelCertIntPrecision->setVisible(mPremium); + mUI->mEditCertIntPrecision->setVisible(mPremium); + mUI->mBughunting->setChecked(projectFile->getBughunting()); + mUI->mGroupboxBughunting->setVisible(mPremium); + mUI->mToolClangAnalyzer->setChecked(projectFile->getClangAnalyzer()); mUI->mToolClangTidy->setChecked(projectFile->getClangTidy()); if (CheckThread::clangTidyCmd().isEmpty()) { @@ -405,14 +435,23 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const projectFile->setSafeChecks(safeChecks); */ // Addons - QStringList list; + QStringList addons; if (mUI->mAddonThreadSafety->isChecked()) - list << "threadsafety"; + addons << "threadsafety"; if (mUI->mAddonY2038->isChecked()) - list << "y2038"; - if (mUI->mAddonMisra->isChecked()) - list << "misra"; - projectFile->setAddons(list); + addons << "y2038"; + if (mUI->mMisraC2012->isChecked()) + addons << ADDON_MISRA; + projectFile->setAddons(addons); + QStringList codingStandards; + if (mUI->mPremiumCertC->isChecked()) + codingStandards << CODING_STANDARD_CERT_C; + if (mUI->mMisraCpp2008->isChecked()) + codingStandards << CODING_STANDARD_MISRA_CPP_2008; + if (mUI->mAutosar->isChecked()) + codingStandards << CODING_STANDARD_AUTOSAR; + projectFile->setCodingStandards(codingStandards); + projectFile->setCertIntPrecision(mUI->mEditCertIntPrecision->text().toInt()); projectFile->setClangAnalyzer(mUI->mToolClangAnalyzer->isChecked()); projectFile->setClangTidy(mUI->mToolClangTidy->isChecked()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) @@ -874,8 +913,8 @@ void ProjectFileDialog::browseMisraFile() mUI->mEditMisraFile->setText(fileName); settings.setValue(SETTINGS_MISRA_FILE, fileName); - mUI->mAddonMisra->setText("MISRA C 2012"); - mUI->mAddonMisra->setEnabled(true); - updateAddonCheckBox(mUI->mAddonMisra, nullptr, getDataDir(), "misra"); + mUI->mMisraC2012->setText("MISRA C 2012"); + mUI->mMisraC2012->setEnabled(true); + updateAddonCheckBox(mUI->mMisraC2012, nullptr, getDataDir(), ADDON_MISRA); } } diff --git a/gui/projectfiledialog.h b/gui/projectfiledialog.h index 449d3c661..18c8deece 100644 --- a/gui/projectfiledialog.h +++ b/gui/projectfiledialog.h @@ -44,7 +44,7 @@ class ProjectFile; class ProjectFileDialog : public QDialog { Q_OBJECT public: - explicit ProjectFileDialog(ProjectFile *projectFile, QWidget *parent = nullptr); + explicit ProjectFileDialog(ProjectFile *projectFile, bool premium, QWidget *parent = nullptr); ~ProjectFileDialog() override; private: @@ -325,6 +325,9 @@ private: */ ProjectFile *mProjectFile; + /** Is this Cppcheck Premium? */ + bool mPremium; + QString getExistingDirectory(const QString &caption, bool trailingSlash); QList mSuppressions; diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 951acb563..949693cfe 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -89,7 +89,7 @@ namespace { std::string args; // special extra arguments std::string python; // script interpreter bool ctu = false; - std::string runScript{}; + std::string runScript; static std::string getFullPath(const std::string &fileName, const std::string &exename) { if (Path::fileExists(fileName)) @@ -275,6 +275,7 @@ static void createDumpFile(const Settings& settings, static std::string executeAddon(const AddonInfo &addonInfo, const std::string &defaultPythonExe, const std::string &file, + const std::string &premiumArgs, std::function,std::string,std::string*)> executeCommand) { const std::string redirect = "2>&1"; @@ -308,6 +309,8 @@ static std::string executeAddon(const AddonInfo &addonInfo, if (addonInfo.executable.empty()) args = cmdFileName(addonInfo.runScript) + " " + cmdFileName(addonInfo.scriptFile); args += std::string(args.empty() ? "" : " ") + "--cli" + addonInfo.args; + if (!premiumArgs.empty() && !addonInfo.executable.empty()) + args += " " + premiumArgs; const std::string fileArg = (endsWith(file, FILELIST, sizeof(FILELIST)-1) ? " --file-list " : " ") + cmdFileName(file); args += fileArg; @@ -1366,7 +1369,7 @@ void CppCheck::executeAddons(const std::vector& files) continue; const std::string results = - executeAddon(addonInfo, mSettings.addonPython, fileList.empty() ? files[0] : fileList, mExecuteCommand); + executeAddon(addonInfo, mSettings.addonPython, fileList.empty() ? files[0] : fileList, mSettings.premiumArgs, mExecuteCommand); std::istringstream istr(results); std::string line; diff --git a/lib/importproject.h b/lib/importproject.h index 5dc87b5d2..a6eb37ba5 100644 --- a/lib/importproject.h +++ b/lib/importproject.h @@ -178,6 +178,11 @@ namespace CppcheckXml { const char Name[] = "name"; const char VSConfigurationElementName[] = "vs-configurations"; const char VSConfigurationName[] = "config"; + // Cppcheck Premium + const char BughuntingElementName[] = "bug-hunting"; + const char CodingStandardsElementName[] = "coding-standards"; + const char CodingStandardElementName[] = "coding-standard"; + const char CertIntPrecisionElementName[] = "cert-c-int-precision"; } /// @} diff --git a/lib/settings.h b/lib/settings.h index 51fbb8dd5..ee8cf28f8 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -239,6 +239,9 @@ public: /** @brief plist output (--plist-output=<dir>) */ std::string plistOutput; + /** @brief Extra arguments for Cppcheck Premium addon */ + std::string premiumArgs; + /** @brief Using -E for debugging purposes */ bool preprocessOnly;