Index: .gitignore =================================================================== --- /dev/null +++ .gitignore @@ -0,0 +1,2 @@ +.kdev4 +build Index: CMakeLists.txt =================================================================== --- /dev/null +++ CMakeLists.txt @@ -0,0 +1,78 @@ +project(kdevverapp) + +set(VERSION_MAJOR 1) +set(VERSION_MINOR 90) +set(VERSION_PATCH 90) +# KDevplatform dependency version +set(KDEVPLATFORM_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") + +cmake_minimum_required(VERSION 2.8.12) +find_package (ECM 0.0.9 REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) + +include(ECMAddTests) +include(KDEInstallDirs) +include(KDECMakeSettings) +include(KDECompilerSettings) +include(FeatureSummary) + +find_package(Qt5 REQUIRED Core Widgets Test) +find_package(KF5 REQUIRED COMPONENTS ItemModels) +find_package(KDevPlatform ${KDEVPLATFORM_VERSION} REQUIRED) + +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic" ) + +include_directories( + ${kdevverapp_BINARY_DIR} + ${kdevverapp_SOURCE_DIR} + ${kdevverapp_SOURCE_DIR}/rules +) + +set(kdevverapp_SRCS + plugin.cpp + + config/globalconfigpage.cpp + config/projectconfigpage.cpp +) + +ki18n_wrap_ui(kdevverapp_UI_SRCS + config/globalconfigpage.ui + config/projectconfigpage.ui +) + +kconfig_add_kcfg_files(kdevverapp_CONFIG_SRCS + config/globalsettings.kcfgc + config/projectsettings.kcfgc +) + +add_library(kdevverapp_core STATIC + debug.cpp + job.cpp + parameters.cpp + + rules/rules.cpp + rules/rules_db.cpp + + ${kdevverapp_CONFIG_SRCS} +) + +target_link_libraries(kdevverapp_core + KDev::Project + KDev::Language + KDev::Shell +) + +kdevplatform_add_plugin(kdevverapp + JSON kdevverapp.json + SOURCES ${kdevverapp_SRCS} ${kdevverapp_UI_SRCS} +) + +target_link_libraries(kdevverapp + kdevverapp_core +) + +install(FILES kdevverapp.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/kdevverapp) + +add_subdirectory(tests) + +feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) Index: config/globalconfigpage.h =================================================================== --- /dev/null +++ config/globalconfigpage.h @@ -0,0 +1,46 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef VERAPP_GLOBAL_CONFIG_PAGE_H +#define VERAPP_GLOBAL_CONFIG_PAGE_H + +#include + +namespace verapp +{ + +class GlobalConfigPage: public KDevelop::ConfigPage +{ + Q_OBJECT + +public: + GlobalConfigPage(KDevelop::IPlugin* plugin, QWidget* parent); + ~GlobalConfigPage() override; + + KDevelop::ConfigPage::ConfigPageType configPageType() const override; + + QString name() const override; + QString fullName() const override; + QIcon icon() const override; +}; + +} + +#endif Index: config/globalconfigpage.cpp =================================================================== --- /dev/null +++ config/globalconfigpage.cpp @@ -0,0 +1,63 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "globalconfigpage.h" +#include "ui_globalconfigpage.h" + +#include "globalsettings.h" + +#include + +namespace verapp +{ + +GlobalConfigPage::GlobalConfigPage(KDevelop::IPlugin* plugin, QWidget* parent) + : ConfigPage(plugin, GlobalSettings::self(), parent) +{ + Ui::GlobalConfigPage ui; + ui.setupUi(this); +} + +GlobalConfigPage::~GlobalConfigPage() +{ +} + +KDevelop::ConfigPage::ConfigPageType GlobalConfigPage::configPageType() const +{ + return KDevelop::ConfigPage::AnalyzerConfigPage; +} + +QString GlobalConfigPage::name() const +{ + return i18n("Vera++"); +} + +QString GlobalConfigPage::fullName() const +{ + return i18n("Configure Vera++ settings"); +} + +QIcon GlobalConfigPage::icon() const +{ +// return QIcon::fromTheme(QStringLiteral("emblem-checked")); + return QIcon::fromTheme(QStringLiteral("kdevelop")); +} + +} Index: config/globalconfigpage.ui =================================================================== --- /dev/null +++ config/globalconfigpage.ui @@ -0,0 +1,83 @@ + + + verapp::GlobalConfigPage + + + + 0 + 0 + 757 + 397 + + + + Cppcheck Settings + + + + + + Paths + + + + + + &Vera++ executable + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + kcfg_executablePath + + + + + + + + + + + + + Output + + + + + + Hide output view during check + + + + + + + + + + Qt::Vertical + + + + 20 + 149 + + + + + + + + + KUrlRequester + QWidget +
kurlrequester.h
+ 1 +
+
+ + +
Index: config/globalsettings.kcfg =================================================================== --- /dev/null +++ config/globalsettings.kcfg @@ -0,0 +1,20 @@ + + + + "parameters.h" + + + + + defaults::executablePath + + + + defaults::hideOutputView + + + + Index: config/globalsettings.kcfgc =================================================================== --- /dev/null +++ config/globalsettings.kcfgc @@ -0,0 +1,4 @@ +File=globalsettings.kcfg +NameSpace=verapp +ClassName=GlobalSettings +Singleton=true Index: config/projectconfigpage.h =================================================================== --- /dev/null +++ config/projectconfigpage.h @@ -0,0 +1,73 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef VERAPP_PROJECT_CONFIG_PAGE_H +#define VERAPP_PROJECT_CONFIG_PAGE_H + +#include "rules.h" + +#include + +#include + +namespace KDevelop +{ +class IProject; +} + +namespace verapp +{ + +namespace Ui +{ +class ProjectConfigPage; +} + +class Parameters; + +class ProjectConfigPage : public KDevelop::ConfigPage +{ + Q_OBJECT + +public: + + ProjectConfigPage(KDevelop::IPlugin* plugin, KDevelop::IProject* project, QWidget* parent); + ~ProjectConfigPage() override; + + QIcon icon() const override; + QString name() const override; + +public slots: + void defaults() override; + void reset() override; + +private: + void updateCommandLine(); + void addRules(QWidget* tab, rules::Type first, rules::Type last); + + QScopedPointer ui; + QScopedPointer m_parameters; + + QHash m_rulesCheckBox; +}; + +} + +#endif Index: config/projectconfigpage.cpp =================================================================== --- /dev/null +++ config/projectconfigpage.cpp @@ -0,0 +1,136 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "projectconfigpage.h" +#include "ui_projectconfigpage.h" + +#include "parameters.h" +#include "projectsettings.h" +#include "rules.h" + +#include + +namespace verapp +{ + +ProjectConfigPage::ProjectConfigPage(KDevelop::IPlugin* plugin, KDevelop::IProject* project, QWidget* parent) + : ConfigPage(plugin, new ProjectSettings, parent) + , ui(new Ui::ProjectConfigPage) + , m_parameters(new Parameters(project)) +{ + configSkeleton()->setSharedConfig(project->projectConfiguration()); + configSkeleton()->load(); + + ui->setupUi(this); + + ui->commandLine->setFontFamily("Monospace"); + + connect(this, &ProjectConfigPage::changed, this, &ProjectConfigPage::updateCommandLine); + connect(ui->commandLineFilter, &QLineEdit::textChanged, this, &ProjectConfigPage::updateCommandLine); + connect(ui->commandLineBreaks, &QCheckBox::stateChanged, this, &ProjectConfigPage::updateCommandLine); + + addRules(ui->tabF, rules::F001, rules::F002); + addRules(ui->tabL, rules::L001, rules::L006); + addRules(ui->tabT1, rules::T001, rules::T007); + addRules(ui->tabT2, rules::T008, rules::T014); + addRules(ui->tabT3, rules::T015, rules::T019); +} + +ProjectConfigPage::~ProjectConfigPage() +{ +} + +QIcon ProjectConfigPage::icon() const +{ + return QIcon::fromTheme(QStringLiteral("dialog-ok")); +} + +void ProjectConfigPage::defaults() +{ + ConfigPage::defaults(); + updateCommandLine(); +} + +void ProjectConfigPage::reset() +{ + ConfigPage::reset(); + updateCommandLine(); +} + +QString ProjectConfigPage::name() const +{ + return i18n("Vera++"); +} + +void ProjectConfigPage::updateCommandLine() +{ + for (int intType = rules::Type::FIRST; intType <= rules::Type::LAST; ++intType) { + rules::Type ruleType = static_cast(intType); + + m_parameters->setRuleEnabled(ruleType, m_rulesCheckBox[ruleType]->isChecked()); + } + + m_parameters->extraParameters = ui->kcfg_extraParameters->text().trimmed(); + + QString text = m_parameters->commandLine().join(' '); + if (!ui->commandLineBreaks->isChecked()) { + ui->commandLine->setText(text); + return; + } + + text.replace(" -", "\n-"); + if (ui->commandLineFilter->text().isEmpty()) { + ui->commandLine->setText(text); + ui->commandLineBreaks->setEnabled(true); + return; + } + + QStringList lines = text.split('\n'); + QMutableStringListIterator i(lines); + + while (i.hasNext()) { + if (!i.next().contains(ui->commandLineFilter->text())) + i.remove(); + } + + ui->commandLine->setText(lines.join('\n')); + ui->commandLineBreaks->setEnabled(false); +} + +void ProjectConfigPage::addRules(QWidget* tab, rules::Type first, rules::Type last) +{ + QVBoxLayout* layout = new QVBoxLayout(tab); + + for (int intType = first; intType <= last; ++intType) { + rules::Type type = static_cast(intType); + QCheckBox* checkBox = new QCheckBox(tab); + + checkBox->setObjectName(QString("kcfg_rule%1").arg(intType)); + checkBox->setText(QString("%1: %2").arg(rules::name(type)).arg(rules::title(type))); + checkBox->setToolTip(rules::explanation(type)); + + layout->addWidget(checkBox); + + m_rulesCheckBox[type] = checkBox; + } + layout->addStretch(); +} + +} Index: config/projectconfigpage.ui =================================================================== --- /dev/null +++ config/projectconfigpage.ui @@ -0,0 +1,203 @@ + + + verapp::ProjectConfigPage + + + + 0 + 0 + 917 + 848 + + + + + + + + 0 + 0 + + + + Built-in Rules + + + + + + + 0 + 0 + + + + QTabWidget::North + + + 0 + + + + F + + + + + L + + + + + T1 + + + + + T2 + + + + + T3 + + + + + + + + + + + + + File Filter + + + + + + + + + + + + + + &Extra parameters: + + + kcfg_extraParameters + + + + + + + <html><head/><body><p>Defines additional parameters for vera++ (see documentation).</p><p>You can use the following placeholders:</p><p>%p - Gets replaced by the URL of the project's root directory.</p><p>%b - Gets replaced by the URL of the project's build directory.</p></body></html> + + + + + + + + + Command line + + + + + + + + &Filter: + + + commandLineFilter + + + + + + + false + + + + + + + Break lines + + + false + + + + + + + + + true + + + true + + + false + + + + + + + + + + tabWidget + kcfg_fileFilter + kcfg_extraParameters + commandLineFilter + commandLineBreaks + commandLine + + + + + commandLineBreaks + toggled(bool) + commandLineFilter + setEnabled(bool) + + + 901 + 513 + + + 772 + 518 + + + + + commandLineBreaks + toggled(bool) + commandLineFilterLabel + setEnabled(bool) + + + 901 + 513 + + + 54 + 518 + + + + + Index: config/projectsettings.kcfg =================================================================== --- /dev/null +++ config/projectsettings.kcfg @@ -0,0 +1,57 @@ + + + + "parameters.h" + + + + + + + F001 + F002 + + L001 + L002 + L003 + L004 + L005 + L006 + + T001 + T002 + T003 + T004 + T005 + T006 + T007 + T008 + T009 + T010 + T011 + T012 + T013 + T014 + T015 + T016 + T017 + T018 + T019 + + + defaults::isRuleEnabled((rules::Type)$(Idx)) + + + + ::defaults::fileFilter + + + + + + + + Index: config/projectsettings.kcfgc =================================================================== --- /dev/null +++ config/projectsettings.kcfgc @@ -0,0 +1,3 @@ +File=projectsettings.kcfg +NameSpace=verapp +ClassName=ProjectSettings Index: debug.h =================================================================== --- /dev/null +++ debug.h @@ -0,0 +1,28 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef VERAPP_DEBUG_H +#define VERAPP_DEBUG_H + +#include + +Q_DECLARE_LOGGING_CATEGORY(KDEV_VERAPP) + +#endif Index: debug.cpp =================================================================== --- /dev/null +++ debug.cpp @@ -0,0 +1,23 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "debug.h" + +Q_LOGGING_CATEGORY(KDEV_VERAPP, "kdev.verapp") Index: job.h =================================================================== --- /dev/null +++ job.h @@ -0,0 +1,63 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef VERAPP_JOB_H +#define VERAPP_JOB_H + + +#include "parameters.h" + +#include +#include + +class QElapsedTimer; + +namespace verapp +{ + +class Job : public KDevelop::OutputExecuteJob +{ + Q_OBJECT + +public: + explicit Job(const Parameters& params); + ~Job() override; + + void start() override; + +Q_SIGNALS: + void problemsDetected(const QVector& problems); + +protected slots: + void postProcessStdout(const QStringList& lines) override; + void postProcessStderr(const QStringList& lines) override; + + void childProcessExited(int exitCode, QProcess::ExitStatus exitStatus) override; + void childProcessError(QProcess::ProcessError processError) override; + +protected: + QScopedPointer m_timer; + + QStringList m_standardOutput; + QStringList m_stderrOutput; +}; + +} +#endif Index: job.cpp =================================================================== --- /dev/null +++ job.cpp @@ -0,0 +1,196 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "job.h" + +#include "debug.h" +#include "rules.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace verapp +{ + +Job::Job(const Parameters ¶ms) + : OutputExecuteJob(nullptr) + , m_timer(new QElapsedTimer) +{ + QString prettyName = KDevelop::ICore::self()->projectController()->prettyFileName( + params.checkPath, + KDevelop::IProjectController::FormatPlain); + setJobName(QString("Vera++ (%1)").arg(prettyName)); + + setCapabilities(KJob::Killable); + setStandardToolView(KDevelop::IOutputView::TestView); + setBehaviours(KDevelop::IOutputView::AutoScroll); + + setProperties(OutputExecuteJob::JobProperty::DisplayStdout); + setProperties(OutputExecuteJob::JobProperty::DisplayStderr); + setProperties(OutputExecuteJob::JobProperty::PostProcessOutput); + +#if defined(_WIN32) or defined(_WIN64) + *this << "cmd"; +#else + *this << "sh"; +#endif + + *this << params.buildRunScript(); + + qCDebug(KDEV_VERAPP) << "checking path" << params.checkPath; +} + +Job::~Job() +{ +} + +void Job::postProcessStdout(const QStringList& lines) +{ + static const auto errorRegex = QRegularExpression("(.+):(\\d+):\\s*([A-Z]\\d{3}):\\s*(.+)$"); + static const auto fileNameRegex = QRegularExpression("Checking ([^:]*)\\.{3}"); + static const auto percentRegex = QRegularExpression("(\\d+)% done"); + + QVector problems; + QRegularExpressionMatch match; + + for (const QString & line : lines) { + match = errorRegex.match(line); + if (match.hasMatch()) { + KDevelop::IProblem::Ptr problem(new KDevelop::DetectedProblem); + + problem->setSource(KDevelop::IProblem::Plugin); + problem->setSeverity(KDevelop::IProblem::Warning); + problem->setDescription(match.captured(3) + ": " + match.captured(4)); + problem->setExplanation(rules::explanation(match.captured(3))); + + KDevelop::DocumentRange range; + range.document = KDevelop::IndexedString(match.captured(1)); + range.setBothLines(match.captured(2).toInt() - 1); + range.setBothColumns(0); + problem->setFinalLocation(range); + + problems.append(problem); + continue; + } + + match = fileNameRegex.match(line); + if (match.hasMatch()) { + emit infoMessage(this, match.captured(1)); + continue; + } + + match = percentRegex.match(line); + if (match.hasMatch()) { + setPercent(match.captured(1).toULong()); + continue; + } + } + + if (problems.size()) + emit problemsDetected(problems); + + m_standardOutput << lines; + + if (status() == KDevelop::OutputExecuteJob::JobStatus::JobRunning) + OutputExecuteJob::postProcessStdout(lines); +} + +void Job::postProcessStderr(const QStringList& lines) +{ + m_stderrOutput << lines; + + if (status() == KDevelop::OutputExecuteJob::JobStatus::JobRunning) + OutputExecuteJob::postProcessStderr(lines); +} + +void Job::start() +{ + m_standardOutput.clear(); + m_stderrOutput.clear(); + + qCDebug(KDEV_VERAPP) << "executing:" << commandLine().join(' '); + + m_timer->restart(); + OutputExecuteJob::start(); +} + +void Job::childProcessError(QProcess::ProcessError e) +{ + QString message; + + switch (e) { + case QProcess::FailedToStart: + message = i18n("Failed to start Vera++ from \"%1\".", commandLine()[0]); + break; + + case QProcess::Crashed: + if (status() != KDevelop::OutputExecuteJob::JobStatus::JobCanceled) + message = i18n("Vera++ crashed."); + break; + + case QProcess::Timedout: + message = i18n("Vera++ process timed out."); + break; + + case QProcess::WriteError: + message = i18n("Write to Vera++ process failed."); + break; + + case QProcess::ReadError: + message = i18n("Read from Vera++ process failed."); + break; + + case QProcess::UnknownError: + // current vera++ errors will be displayed in the output view + // don't notify the user + break; + } + + if (!message.isEmpty()) + KMessageBox::error(qApp->activeWindow(), message, i18n("Vera++ Error")); + + KDevelop::OutputExecuteJob::childProcessError(e); +} + +void Job::childProcessExited(int exitCode, QProcess::ExitStatus exitStatus) +{ + qCDebug(KDEV_VERAPP) << "Process Finished, exitCode" << exitCode << "process exit status" << exitStatus; + + postProcessStdout({QString("Elapsed time: %1 s.").arg(m_timer->elapsed()/1000.0)}); + + if (exitCode != 0) { + qCDebug(KDEV_VERAPP) << "vera++ failed"; + qCDebug(KDEV_VERAPP) << "stdout output: "; + qCDebug(KDEV_VERAPP) << m_standardOutput.join('\n'); + qCDebug(KDEV_VERAPP) << "stderr output: "; + qCDebug(KDEV_VERAPP) << m_stderrOutput.join('\n'); + } + + KDevelop::OutputExecuteJob::childProcessExited(exitCode, exitStatus); +} + +} Index: kdev-verapp.kdev4 =================================================================== --- /dev/null +++ kdev-verapp.kdev4 @@ -0,0 +1,4 @@ +[Project] +CreatedFrom=CMakeLists.txt +Manager=KDevCMakeManager +Name=kdev-verapp Index: kdevverapp.json =================================================================== --- /dev/null +++ kdevverapp.json @@ -0,0 +1,33 @@ +{ + "GenericName": "Vera++ Support", + "GenericName[x-test]": "xxVera++ Supportxx", + + "KPlugin": { + "Authors": [ + { + "Name": "Anton Anikin", + "Name[x-test]": "xxAnton Anikinxx" + } + ], + + "Category": "Analyzers", + "Icon": "kdevelop", + "Id": "kdevverapp", + "License": "GPL", + "ServiceTypes": [ + "KDevelop/Plugin" + ], + + "Name": "Vera++ Support", + "Name[x-test]": "xxVera++ Supportxx", + + "Description": "This plugin integrates Vera++ to KDevelop", + "Description[x-test]": "xxThis plugin integrates Vera++ to KDevelopkxx" + }, + + "X-KDevelop-Category": "Global", + "X-KDevelop-Mode": "GUI", + "X-KDevelop-IRequired": [ + "org.kdevelop.IExecutePlugin" + ] +} Index: kdevverapp.rc =================================================================== --- /dev/null +++ kdevverapp.rc @@ -0,0 +1,11 @@ + + + + + Analyze + + + + + + Index: parameters.h =================================================================== --- /dev/null +++ parameters.h @@ -0,0 +1,82 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef VERAPP_PARAMETERS_H +#define VERAPP_PARAMETERS_H + +#include "rules.h" + +#include + +namespace KDevelop +{ +class IProject; +} + +namespace verapp +{ + +namespace defaults +{ + +static const QString executablePath = QStringLiteral("/usr/bin/vera++"); +static const bool hideOutputView = true; +static const QString fileFilter = QStringLiteral("*.h,*.hxx,*.hpp,*.hh,*.h++,*.H,*.tlh,*.cpp,*.cc,*.C,*.c++,*.cxx,*.ocl,*.inl,*.idl,*.c,*.m,*.mm,*.M,*.y,*.ypp,*.yxx,*.y++,*.l"); + +bool isRuleEnabled(rules::Type type); + +} + +class Parameters +{ + +public: + explicit Parameters(KDevelop::IProject* project = nullptr); + + QStringList commandLine() const; + + QString buildRunScript() const; + + // global settings + QString executablePath; + bool hideOutputView; + + // project settings + bool isRuleEnabled(rules::Type type) const; + void setRuleEnabled(rules::Type type, bool value); + + QString fileFilter; + QString extraParameters; + + QString checkPath; + +private: + QString applyPlaceholders(const QString& text) const; + + KDevelop::IProject* m_project; + + KDevelop::Path m_projectRootPath; + KDevelop::Path m_projectBuildPath; + + bool m_ruleEnabled[rules::Type::COUNT]; +}; + +} +#endif Index: parameters.cpp =================================================================== --- /dev/null +++ parameters.cpp @@ -0,0 +1,205 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "parameters.h" + +#include "globalsettings.h" +#include "projectsettings.h" +#include "rules.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace verapp +{ + +Parameters::Parameters(KDevelop::IProject* project) + : m_project(project) +{ + executablePath = KDevelop::Path(GlobalSettings::executablePath()).toLocalFile(); + hideOutputView = GlobalSettings::hideOutputView(); + + if (!project) { + for (int intType = rules::Type::FIRST; intType <= rules::Type::LAST; ++intType) { + rules::Type ruleType = static_cast(intType); + + m_ruleEnabled[ruleType] = defaults::isRuleEnabled(ruleType); + } + + fileFilter = defaults::fileFilter; + return; + } + + ProjectSettings projectSettings; + projectSettings.setSharedConfig(project->projectConfiguration()); + projectSettings.load(); + + for (int intType = rules::Type::FIRST; intType <= rules::Type::LAST; ++intType) { + rules::Type ruleType = static_cast(intType); + + m_ruleEnabled[ruleType] = projectSettings.rule(intType); + } + + extraParameters = projectSettings.extraParameters(); + fileFilter = projectSettings.fileFilter(); + + m_projectRootPath = m_project->path(); + m_projectBuildPath = m_project->buildSystemManager()->buildDirectory(m_project->projectItem()); +} + +QStringList Parameters::commandLine() const +{ + QStringList arguments; + + arguments << executablePath; + + arguments << QStringLiteral("--show-rule"); + arguments << QStringLiteral("--no-duplicate"); + + for (int intType = rules::Type::FIRST; intType <= rules::Type::LAST; ++intType) { + rules::Type ruleType = static_cast(intType); + + if (m_ruleEnabled[ruleType]) { + arguments << "-R"; + arguments << rules::name(ruleType); + } + } + + if (!extraParameters.isEmpty()) + arguments << KShell::splitArgs(applyPlaceholders(extraParameters)); + + return arguments; +} + +QString Parameters::buildRunScript() const +{ + QSet projectFiles; + QString scriptPath; + + if (!checkPath.isEmpty() && QFileInfo(checkPath).isFile()) + projectFiles.insert(KDevelop::IndexedString(checkPath)); + else if (m_project) { + projectFiles = m_project->fileSet(); + QStringList filters = fileFilter.split(',', QString::SkipEmptyParts); + + QMutableSetIterator i(projectFiles); + while (i.hasNext()) { + QString projectFile = i.next().str(); + + if (!projectFile.startsWith(checkPath)) { + i.remove(); + continue; + } + + if (!QDir::match(filters, projectFile)) + i.remove(); + } + } + + QString tempDir = QStandardPaths::standardLocations(QStandardPaths::TempLocation).first(); + scriptPath = tempDir + "/kdevverapp"; + + if (m_project) + scriptPath += "_" + m_project->name(); + +#ifdef Q_OS_WIN + scriptPath += ".bat"; +#else + scriptPath += ".sh"; +#endif + + QFile scriptFile(scriptPath); + if (!scriptFile.open(QIODevice::WriteOnly)) { + QMessageBox::critical( + nullptr, + i18n("Vera++ Error"), + i18n("Unable to open file '%1' to write", scriptPath)); + + return scriptPath; + } + + QTextStream scriptStream(&scriptFile); + QString veraCommand = commandLine().join(' '); + int fileNumber = 0; + int filesCount = projectFiles.size(); + foreach (const KDevelop::IndexedString& projectFile, projectFiles) { + scriptStream << QString("echo Checking %1 ...\n").arg(projectFile.str()); + + scriptStream << QString("%1 %2\n").arg(veraCommand).arg(projectFile.str()); + + int progress = (++fileNumber)/(double)filesCount * 100.0; + scriptStream << QString("echo %1/%2 files checked %3% done\n").arg(fileNumber).arg(filesCount).arg(progress); + } + + scriptFile.setPermissions( + QFileDevice::ReadOwner | + QFileDevice::WriteOwner | + QFileDevice::ExeOwner); + scriptFile.close(); + + return scriptPath; +} + +bool Parameters::isRuleEnabled(rules::Type type) const +{ + return m_ruleEnabled[type]; +} + +void Parameters::setRuleEnabled(rules::Type type, bool value) +{ + m_ruleEnabled[type] = value; +} + +QString Parameters::applyPlaceholders(const QString& text) const +{ + QString result(text); + + if (m_project) { + result.replace("%p", m_projectRootPath.toLocalFile()); + result.replace("%b", m_projectBuildPath.toLocalFile()); + } + + return result; +} + +namespace defaults +{ + +bool isRuleEnabled(rules::Type type) +{ + // default Vera++ profile + if (type == rules::F002 || type == rules::T014) + return false; + + return true; +} + +} + +} Index: plugin.h =================================================================== --- /dev/null +++ plugin.h @@ -0,0 +1,88 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef VERAPP_PLUGIN_H +#define VERAPP_PLUGIN_H + +#include "job.h" + +#include + +class KJob; +class QMenu; + +namespace KDevelop +{ + class IProject; + class ProblemModel; +} + +namespace verapp +{ + +class Plugin : public KDevelop::IPlugin +{ + Q_OBJECT + +public: + Plugin(QObject* parent, const QVariantList& = QVariantList()); + + ~Plugin() override; + + void unload() override; + + int configPages() const override { return 1; } + KDevelop::ConfigPage* configPage(int number, QWidget* parent) override; + + int perProjectConfigPages() const override { return 1; } + KDevelop::ConfigPage* perProjectConfigPage(int number, const KDevelop::ProjectConfigOptions& options, QWidget* parent) override; + + KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context) override; + +private: + bool isRunning(); + void killCppcheck(); + + void raiseProblemsView(); + void raiseOutputView(); + + void updateActions(); + void projectClosed(KDevelop::IProject* project); + + void runVerapp(); + void problemsDetected(const QVector& problems); + void result(KJob* job); + + Job* m_job; + + KDevelop::IProject* m_currentProject; + KDevelop::IProject* m_checkedProject; + + QAction* m_actionFile; + QAction* m_actionProject; + QAction* m_actionProjectItem; + + QScopedPointer m_model; + QVector m_problems; +}; + +} + +#endif Index: plugin.cpp =================================================================== --- /dev/null +++ plugin.cpp @@ -0,0 +1,282 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "plugin.h" + +#include "config/globalconfigpage.h" +#include "config/projectconfigpage.h" +#include "debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rules.h" + +K_PLUGIN_FACTORY_WITH_JSON(VerappFactory, "kdevverapp.json", registerPlugin();) + +namespace verapp +{ + +static const QString modelName("Vera++"); + +Plugin::Plugin(QObject* parent, const QVariantList&) + : IPlugin("kdevverapp", parent) + , m_job(nullptr) + , m_currentProject(nullptr) + , m_checkedProject(nullptr) + , m_model(new KDevelop::ProblemModel(this)) +{ + qCDebug(KDEV_VERAPP) << "setting kdevverapp.rc file"; + setXMLFile("kdevverapp.rc"); + + rules::init(); + + m_actionFile = new QAction(i18n("Vera++ (Current File)"), this); + m_actionProject = new QAction(i18n("Vera++ (Current Project)"), this); + m_actionProjectItem = new QAction("Vera++", this); + + connect(m_actionFile, &QAction::triggered, this, &Plugin::runVerapp); + connect(m_actionProject, &QAction::triggered, this, &Plugin::runVerapp); + connect(m_actionProjectItem, &QAction::triggered, this, &Plugin::runVerapp); + + actionCollection()->addAction("verapp_file", m_actionFile); + actionCollection()->addAction("verapp_project", m_actionProject); + + connect(core()->documentController(), &KDevelop::IDocumentController::documentClosed, + this, &Plugin::updateActions); + connect(core()->documentController(), &KDevelop::IDocumentController::documentActivated, + this, &Plugin::updateActions); + + connect(core()->projectController(), &KDevelop::IProjectController::projectOpened, + this, &Plugin::updateActions); + connect(core()->projectController(), &KDevelop::IProjectController::projectClosed, + this, &Plugin::projectClosed); + + KDevelop::ProblemModelSet* pms = core()->languageController()->problemModelSet(); + pms->addModel(modelName, m_model.data()); + + updateActions(); +} + +void Plugin::unload() +{ + KDevelop::ProblemModelSet* pms = core()->languageController()->problemModelSet(); + pms->removeModel(modelName); +} + +Plugin::~Plugin() +{ +} + +bool Plugin::isRunning() +{ + return m_job; +} + +void Plugin::killCppcheck() +{ + if (m_job) + m_job->kill(KJob::EmitResult); +} + +void Plugin::raiseProblemsView() +{ + core()->languageController()->problemModelSet()->showModel(modelName); +} + +void Plugin::raiseOutputView() +{ + core()->uiController()->findToolView( + i18ndc("kdevstandardoutputview", "@title:window", "Test"), + nullptr, + KDevelop::IUiController::FindFlags::Raise); +} + +void Plugin::updateActions() +{ + m_currentProject = nullptr; + + m_actionFile->setEnabled(false); + m_actionProject->setEnabled(false); + + if (isRunning()) + return; + + KDevelop::IDocument* activeDocument = core()->documentController()->activeDocument(); + if (!activeDocument) + return; + + QUrl url = activeDocument->url(); + + m_currentProject = core()->projectController()->findProjectForUrl(url); + if (!m_currentProject) + return; + + m_actionFile->setEnabled(true); + m_actionProject->setEnabled(true); +} + +void Plugin::projectClosed(KDevelop::IProject* project) +{ + if (project != m_checkedProject) + return; + + killCppcheck(); + m_problems.clear(); + m_model->clearProblems(); + m_checkedProject = nullptr; +} + +void Plugin::runVerapp() +{ + QAction* action = dynamic_cast(sender()); + + m_checkedProject = m_currentProject; + + Parameters params(m_checkedProject); + params.checkPath = action->data().toUrl().toLocalFile(); + + m_problems.clear(); + m_model->clearProblems(); + + m_job = new Job(params); + + connect(m_job, &Job::problemsDetected, this, &Plugin::problemsDetected); + connect(m_job, &Job::finished, this, &Plugin::result); + + core()->uiController()->registerStatus(new KDevelop::JobStatus(m_job, "vera++")); + core()->runController()->registerJob(m_job); + + if (params.hideOutputView) + raiseProblemsView(); + else + raiseOutputView(); + + updateActions(); +} + +void Plugin::problemsDetected(const QVector& problems) +{ + static int maxLength = 0; + + if (m_problems.isEmpty()) + maxLength = 0; + + m_problems.append(problems); + for (auto p : problems) { + m_model->addProblem(p); + + // this performs adjusing of columns width in the ProblemsView + if (maxLength < p->description().length()) { + maxLength = p->description().length(); + m_model->setProblems(m_problems); + } + } +} + +void Plugin::result(KJob*) +{ + if (!core()->projectController()->projects().contains(m_checkedProject)) { + m_problems.clear(); + m_model->clearProblems(); + } else { + m_model->setProblems(m_problems); + + if (m_job->status() == KDevelop::OutputExecuteJob::JobStatus::JobSucceeded || + m_job->status() == KDevelop::OutputExecuteJob::JobStatus::JobCanceled) + raiseProblemsView(); + else + raiseOutputView(); + } + + m_job = nullptr; // job is automatically deleted later + + updateActions(); +} + +KDevelop::ContextMenuExtension Plugin::contextMenuExtension(KDevelop::Context* context) +{ + KDevelop::ContextMenuExtension extension; + + if (context->hasType(KDevelop::Context::EditorContext) && + m_currentProject && !isRunning()) { + + extension.addAction(KDevelop::ContextMenuExtension::AnalyzeGroup, m_actionFile); + extension.addAction(KDevelop::ContextMenuExtension::AnalyzeGroup, m_actionProject); + } + + if (context->hasType(KDevelop::Context::ProjectItemContext) && !isRunning()) { + auto pContext = dynamic_cast(context); + if (pContext->items().size() != 1) + return extension; + + auto item = pContext->items().first(); + + switch (item->type()) { + case KDevelop::ProjectBaseItem::File: + case KDevelop::ProjectBaseItem::Folder: + case KDevelop::ProjectBaseItem::BuildFolder: + break; + + default: + return extension; + } + + m_currentProject = item->project(); + m_actionProjectItem->setData(item->path().toUrl()); + + extension.addAction(KDevelop::ContextMenuExtension::AnalyzeGroup, m_actionProjectItem); + } + + return extension; +} + +KDevelop::ConfigPage* Plugin::perProjectConfigPage(int number, const KDevelop::ProjectConfigOptions& options, QWidget* parent) +{ + if (number != 0) + return nullptr; + else + return new ProjectConfigPage(this, options.project, parent); +} + +KDevelop::ConfigPage* Plugin::configPage(int number, QWidget* parent) +{ + if (number != 0) + return nullptr; + else + return new GlobalConfigPage(this, parent); +} + +} + +#include "plugin.moc" Index: rules/Rules.md =================================================================== --- /dev/null +++ rules/Rules.md @@ -0,0 +1,570 @@ +Rules +===== + +F001 Source files should not use the '\\r' (CR) character +--------------------------------------------------------- + +As a commonly accepted practice, line breaks are denoted by a single +'\\n' (LF) character or by two characters "\\r\\n" (CRLF). A single +appearance of '\\r' (CR) is discouraged. + +**Compliance:** Boost + + +F002 File names should be well-formed +------------------------------------- + +The source file names should be well-formed in the sense of their +allowed maximum length and directory depth. Directory and file names +should start with alphabetic character or underscore. In addition, +directory names should not contain dots and file names can have only one +dot. + +**Recognized parameters:** + + Name Default Description + ----------------------- --------- ------------------------------------------------- + max-directory-depth 8 Maximum depth of the directory structure. + max-dirname-length 31 Maximum length of the directory path component. + max-filename-length 31 Maximum length of the leaf file name. + max-path-length 100 Maximum length of the full path. + +**Compliance:** Boost + + +L001 No trailing whitespace +--------------------------- + +*Trailing whitespace* is any whitespace character (space or tab) that is +placed at the end of the source line, after other characters or alone. + +The presence of *trailing whitespace* artificially influences some +source code metrics and is therefore discouraged. + +As a special case, the trailing whitespace in the otherwise empty lines +is allowed provided that the amount of whitespace is identical to the +indent in the previous line - this exception is more friendly with less +smart editors, but can be switched off by setting non-zero value for the +`strict-trailing-space` parameter. + +**Recognized parameters:** + + Name Default Description + ------------------------- --------- -------------------------------------- + strict-trailing-space 0 Strict mode for trailing whitespace. + +**Compliance:** Inspirel + + +L002 Don't use tab characters +----------------------------- + +*Horizontal tabs* are not consistently handled by editors and tools. +Avoiding them ensures that the intended formatting of the code is +preserved. + +**Compliance:** HICPP, JSF + + +L003 No leading and no trailing empty lines +------------------------------------------- + +*Leading and trailing empty lines* confuse users of various tools (like +`head` and `tail`) and artificially +influence some source code metrics. + +**Compliance:** Inspirel + + +L004 Line cannot be too long +---------------------------- + +The source code line should not exceed some *reasonable* length. + +**Recognized parameters:** + + Name Default Description + ------------------- --------- ------------------------------------- + max-line-length 100 Maximum length of source code line. + +**Compliance:** Inspirel + + +L005 There should not be too many consecutive empty lines +--------------------------------------------------------- + +The empty lines (if any) help to introduce more "light" in the source +code, but they should not be overdosed in the sense that too many +consecutive empty lines make the code harder to follow. + +Lines containing only whitespace are considered to be empty in this +context. + +**Recognized parameters:** + + Name Default Description + ------------------------------- --------- -------------------------------------------- + max-consecutive-empty-lines 2 Maximum number of consecutive empty lines. + +**Compliance:** Inspirel + + +L006 Source file should not be too long +--------------------------------------- + +The source file should not exceed a *reasonable* length. + +Long source files can indicate an opportunity for refactoring. + +**Recognized parameters:** + + Name Default Description + ------------------- --------- ------------------------------------ + max-file-length 2000 Maximum number of lines in a file. + +**Compliance:** Inspirel + + +T001 One-line comments should not have forced continuation +---------------------------------------------------------- + +The one-line comment is a comment that starts with `//`. + +The usual intent is to let the comment continue till the end of the +line, but the preprocessing rules of the language allow to actually +continue the comment in the next line if *line-splicing* is forced with +the backslash at the end of the line: + +~~~~ +void foo() +{ + // this comment is continued in the next line \ + exit(0); +} +~~~~ + +It is not immediately obvious what happens in this example. Moreover, +the line-splicing works only if the backslash is really the last +character in the line - which is error prone because any white +characters that might appear after the backslash will change the meaning +of the program without being visible in the code. + +**Compliance:** Inspirel + + +T002 Reserved names should not be used for preprocessor macros +-------------------------------------------------------------- + +The C++ Standard reserves some forms of names for language +implementations. One of the most frequent violations is a definition of +preprocessor macro that begins with underscore followed by a capital +letter or containing two consecutive underscores: + +~~~~ +#define _MY_MACRO something +#define MY__MACRO something +~~~~ + +Even though the majority of known compilers use more obscure names for +internal purposes and the above code is not likely to cause any +significant problems, all such names are *formally reserved* and +therefore should not be used. + +Apart from the use of underscore in macro names, preprocessor macros +should not be used to redefine language keywords: + +~~~~ +#define private public +#define const +~~~~ + +**Compliance:** ISO + + +T003 Some keywords should be followed by a single space +------------------------------------------------------- + +Keywords from the following list: + +- `case` +- `class` +- `delete` +- `enum` +- `explicit` +- `extern` +- `goto` +- `new` +- `struct` +- `union` +- `using` + +should be followed by a single space for better readability. + +**Compliance:** Inspirel + + +T004 Some keywords should be immediately followed by a colon +------------------------------------------------------------ + +Keywords from the following list: + +- `default` +- `private` +- `protected` +- `public` + +should be immediately followed by a colon, unless used in the list of +base classes: + +~~~~ +class A : public B, private C +{ +public: + A(); + ~A(); +protected: + // ... +private: + // ... +}; + +void fun(int a) +{ + switch (a) + { + // ... + default: + exit(0); + } +} +~~~~ + +**Compliance:** Inspirel + + +T005 Keywords break and continue should be immediately followed by a semicolon +------------------------------------------------------------------------------ + +The `break` and `continue` keywords should +be immediately followed by a semicolon, with no other tokens in between: + +~~~~ {.example} +while (...) +{ + if (...) + { + break; + } + if (...) + { + continue; + } + // ... +} +~~~~ + +**Compliance:** Inspirel + + +T006 Keywords return and throw should be immediately followed by a semicolon or a single space +---------------------------------------------------------------------------------------------- + +The `return` and `throw` keywords should be +immediately followed by a semicolon or a single space: + +~~~~ {.example} +void fun() +{ + if (...) + { + return; + } + // ... +} + +int add(int a, int b) +{ + return a + b; +} +~~~~ + +An exception to this rule is allowed for exeption specifications: + +~~~~ {.example} +void fun() throw(); +~~~~ + +**Compliance:** Inspirel + + +T007 Semicolons should not be isolated by spaces or comments from the rest of the code +-------------------------------------------------------------------------------------- + +The semicolon should not stand isolated by whitespace or comments from +the rest of the code. + +~~~~ {.example} +int a ; // bad +int b +; // bad +int c; // OK +~~~~ + +As an exception from this rule, semicolons surrounded by spaces are +allowed in `for` loops: + +~~~~ {.example} +for ( ; ; ) // OK as an exception +{ + // ... +} +~~~~ + +**Compliance:** Inspirel + + +T008 Keywords catch, for, if, switch and while should be followed by a single space +----------------------------------------------------------------------------------- + +Keywords `catch`, `for`, `if`, +`switch` and `while` should be followed by a +single space and then an opening left parenthesis: + +~~~~ {.example} +catch (...) +{ + for (int i = 0; i != 10; ++i) + { + if (foo(i)) + { + while (getline(cin, line)) + { + switch (i % 3) + { + case 0: + bar(line); + break; + // ... + } + } + } + } +} +~~~~ + +**Compliance:** Inspirel + + +T009 Comma should not be preceded by whitespace, but should be followed by one +------------------------------------------------------------------------------ + +A comma, whether used as operator or in various lists, should not be +preceded by whitespace on its left side, but should be followed by +whitespace on its right side: + +~~~~ {.example} +void fun(int x, int y, int z); +int a[] = {5, 6, 7}; +class A : public B, + public C +{ + // ... +}; +~~~~ + +An exception to this rule is allowed for `operator,`: + +~~~~ {.example} +struct A {}; +void operator,(const A &left, const A &right); +~~~~ + +**Compliance:** Inspirel + + +T010 Identifiers should not be composed of 'l' and 'O' characters only +---------------------------------------------------------------------- + +The characters 'l' (which is lowercase 'L') and 'O' (which is uppercase +'o') should not be the only characters used in the identifier, because +this would make them visually similar to numeric literals. + +**Compliance:** Inspirel + + +T011 Curly brackets from the same pair should be either in the same line or in the same column +---------------------------------------------------------------------------------------------- + +Corresponding curly brackets should be either in the same line or in +the same column. This promotes clarity by emphasising scopes, but +allows concise style of one-line definitions and empty blocks: + +~~~~ {.example} +class MyException {}; + +struct MyPair +{ + int a; + int b; +}; + +enum state { close, open }; + +enum colors +{ + black, + red, + green, + blue, + white +}; +~~~~ + +**Compliance:** Inspirel + + +T012 Negation operator should not be used in its short form +----------------------------------------------------------- + +The negation operator (exclamation mark) reduces readability of the code +due to its terseness. Prefer explicit logical comparisons or alternative +tokens for increased readability: + +~~~~ {.example} +if (!cond) // error-prone +if (cond == false) // better +if (not cond) // better (alternative keyword) +~~~~ + +**Compliance:** Inspirel + + +T013 Source files should contain the copyright notice +----------------------------------------------------- + +The copyright notice is required by man coding standards and guidelines. +In some countries every written artwork has some copyright, even if +implicit. Prefer explicit notice to avoid any later confusion. + +This rule verifies that at least one comment in the source file contains +the "copyright" word. + +**Compliance:** Boost + + +T014 Source files should refer the Boost Software License +--------------------------------------------------------- + +The Boost Software License should be referenced in the source code. + +This rule verifies that at least one comment in the source file contains +the "Boost Software License" phrase. + +Note that this rule is very specific to the Boost libraries and those +project that choose to use the Boost license. It is therefore not part +of the default profile. + +**Compliance:** Boost + + +T015 HTML links in comments and string literals should be correct +----------------------------------------------------------------- + +The links embedded in comments and string literals should have correct +form and should reference existing files. + +**Compliance:** Boost + + +T016 Calls to min/max should be protected against accidental macro substitution +------------------------------------------------------------------------------- + +The calls to min and max functions should be protected against +accidental macro substitution. + +~~~~ {.example} +x = max(y, z); // wrong, vulnerable to accidental macro substitution + +x = (max)(y, z); // OK + +x = max BOOST_PREVENT_MACRO_SUBSTITUTION (y, z); // OK +~~~~ + +**Compliance:** Boost + + +T017 Unnamed namespaces are not allowed in header files +------------------------------------------------------- + +Unnamed namespaces are not allowed in header files. + +The typical use of unnamed namespace is to hide module-internal names +from the outside world. Header files are physically concatenated in a +single translation unit, which logically merges all namespaces with the +same name. Unnamed namespaces are also merged in this process, which +effectively undermines their initial purpose. + +Use named namespaces in header files. Unnamed namespaces are allowed in +implementation files only. + +**Compliance:** Boost + + +T018 Using namespace is not allowed in header files +--------------------------------------------------- + +Using namespace directives are not allowed in header files. + +The using namespace directive imports names from the given namespace and +when used in a header file influences the global namespace of all the +files that directly or indirectly include this header file. + +It is imaginable to use the using namespace directive in a limited scope +in a header file (for example in a template or inline function +definition), but for the sake of consistency this is also discouraged. + +**Compliance:** C++ Coding Standards + + +T019 Control structures should have complete curly-braced block of code +----------------------------------------------------------------------- + +Control structures managed by for, if and while constructs can be +associated with a single instruction or with a complex block of code. +Standardizing on the curly-braced blocks in all cases allows one to +avoid common pitfalls and makes the code visually more uniform. + +~~~~ {.example} +if (x) foo(); // bad style +if (x) { foo(); } // OK + +if (x) + foo(); // again bad style + +if (x) +{ // OK + foo(); +} + +if (x) + while (y) // bad style + foo(); // bad style + +if (x) +{ // OK + while (y) + { // OK + foo(); + } +} + +for (int i = 0; i <= 10; ++i); // oops! + cout << "Hello\n"; + +for (int i = 0; i <= 10; ++i) // OK +{ + cout << "Hello\n"; +} +~~~~ + +**Compliance:** Inspirel Index: rules/rules.h =================================================================== --- /dev/null +++ rules/rules.h @@ -0,0 +1,82 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef VERAPP_RULES_H +#define VERAPP_RULES_H + +#include + +namespace verapp +{ + +namespace rules +{ + enum Type + { + F001, + F002, + + L001, + L002, + L003, + L004, + L005, + L006, + + T001, + T002, + T003, + T004, + T005, + T006, + T007, + T008, + T009, + T010, + T011, + T012, + T013, + T014, + T015, + T016, + T017, + T018, + T019, + + FIRST = F001, + LAST = T019, + COUNT + }; + + QString name(Type type); + Type type(const QString& name); + + QString title(Type type); + QString title(const QString& type); + + QString explanation(Type type); + QString explanation(const QString& type); + + void init(); +} + +} + +#endif Index: rules/rules.cpp =================================================================== --- /dev/null +++ rules/rules.cpp @@ -0,0 +1,110 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "rules.h" + +#include + +namespace verapp +{ + +namespace rules +{ + static QString names[Type::COUNT]; + static QString titles[Type::COUNT]; + static QString explanations[Type::COUNT]; + + static QHash types; + + void initDb(QString* titles, QString* explanations); + + void init() + { + names[F001] = QStringLiteral("F001"); + names[F002] = QStringLiteral("F002"); + + names[L001] = QStringLiteral("L001"); + names[L002] = QStringLiteral("L002"); + names[L003] = QStringLiteral("L003"); + names[L004] = QStringLiteral("L004"); + names[L005] = QStringLiteral("L005"); + names[L006] = QStringLiteral("L006"); + + names[T001] = QStringLiteral("T001"); + names[T002] = QStringLiteral("T002"); + names[T003] = QStringLiteral("T003"); + names[T004] = QStringLiteral("T004"); + names[T005] = QStringLiteral("T005"); + names[T006] = QStringLiteral("T006"); + names[T007] = QStringLiteral("T007"); + names[T008] = QStringLiteral("T008"); + names[T009] = QStringLiteral("T009"); + names[T010] = QStringLiteral("T010"); + names[T011] = QStringLiteral("T011"); + names[T012] = QStringLiteral("T012"); + names[T013] = QStringLiteral("T013"); + names[T014] = QStringLiteral("T014"); + names[T015] = QStringLiteral("T015"); + names[T016] = QStringLiteral("T016"); + names[T017] = QStringLiteral("T017"); + names[T018] = QStringLiteral("T018"); + names[T019] = QStringLiteral("T019"); + + for (int intType = FIRST; intType <= LAST; ++intType) { + Type ruleType = static_cast(intType); + + types[names[ruleType]] = ruleType; + } + + initDb(titles, explanations); + } + + Type type(const QString& name) + { + return types[name]; + } + + QString name(Type type) + { + return names[type]; + } + + QString title(Type type) + { + return titles[type]; + } + + QString title(const QString& name) + { + return titles[type(name)]; + } + + QString explanation(Type type) + { + return explanation(name(type)); + } + + QString explanation(const QString& name) + { + return explanations[type(name)]; + } +} + +} Index: rules/rules_db.cpp =================================================================== --- /dev/null +++ rules/rules_db.cpp @@ -0,0 +1,474 @@ +// This file is generated by 'rules_db.py' from 'Rules.md'. +// Please don't edit it + +#include "rules.h" + +#include + +namespace verapp +{ + +namespace rules +{ + +void initDb(QString* titles, QString* explanations) +{ + +titles[type("F001")] = "Source files should not use the '\\\\r' (CR) character"; +explanations[type("F001")] = +"" +"F001: Source files should not use the '\\\\r' (CR) character" +"
" +"As a commonly accepted practice, line breaks are denoted by a single '\\\\n' (LF) character or by two characters "\\\\r\\\\n" (CRLF). A single appearance of '\\\\r' (CR) is discouraged." +"

" +"Compliance: Boost" +""; + +titles[type("F002")] = "File names should be well-formed"; +explanations[type("F002")] = +"" +"F002: File names should be well-formed" +"
" +"The source file names should be well-formed in the sense of their allowed maximum length and directory depth. Directory and file names should start with alphabetic character or underscore. In addition, directory names should not contain dots and file names can have only one dot. Recognized parameters:" +"
"
+"    Name                    Default   Description\n"
+"    ----------------------- --------- -------------------------------------------------\n"
+"    max-directory-depth     8         Maximum depth of the directory structure.\n"
+"    max-dirname-length      31        Maximum length of the directory path component.\n"
+"    max-filename-length     31        Maximum length of the leaf file name.\n"
+"    max-path-length         100       Maximum length of the full path."
+"
" +"Compliance: Boost" +""; + +titles[type("L001")] = "No trailing whitespace"; +explanations[type("L001")] = +"" +"L001: No trailing whitespace" +"
" +"Trailing whitespace* is any whitespace character (space or tab) that is placed at the end of the source line, after other characters or alone. The presence of *trailing whitespace artificially influences some source code metrics and is therefore discouraged. As a special case, the trailing whitespace in the otherwise empty lines is allowed provided that the amount of whitespace is identical to the indent in the previous line - this exception is more friendly with less smart editors, but can be switched off by setting non-zero value for the `strict-trailing-space` parameter. Recognized parameters:" +"
"
+"    Name                      Default   Description\n"
+"    ------------------------- --------- --------------------------------------\n"
+"    strict-trailing-space     0         Strict mode for trailing whitespace."
+"
" +"Compliance: Inspirel" +""; + +titles[type("L002")] = "Don't use tab characters"; +explanations[type("L002")] = +"" +"L002: Don't use tab characters" +"
" +"Horizontal tabs are not consistently handled by editors and tools. Avoiding them ensures that the intended formatting of the code is preserved." +"

" +"Compliance: HICPP, JSF" +""; + +titles[type("L003")] = "No leading and no trailing empty lines"; +explanations[type("L003")] = +"" +"L003: No leading and no trailing empty lines" +"
" +"Leading and trailing empty lines confuse users of various tools (like `head` and `tail`) and artificially influence some source code metrics." +"

" +"Compliance: Inspirel" +""; + +titles[type("L004")] = "Line cannot be too long"; +explanations[type("L004")] = +"" +"L004: Line cannot be too long" +"
" +"The source code line should not exceed some reasonable length. Recognized parameters:" +"
"
+"    Name                Default   Description\n"
+"    ------------------- --------- -------------------------------------\n"
+"    max-line-length     100       Maximum length of source code line."
+"
" +"Compliance: Inspirel" +""; + +titles[type("L005")] = "There should not be too many consecutive empty lines"; +explanations[type("L005")] = +"" +"L005: There should not be too many consecutive empty lines" +"
" +"The empty lines (if any) help to introduce more "light" in the source code, but they should not be overdosed in the sense that too many consecutive empty lines make the code harder to follow. Lines containing only whitespace are considered to be empty in this context. Recognized parameters:" +"
"
+"    Name                            Default   Description\n"
+"    ------------------------------- --------- --------------------------------------------\n"
+"    max-consecutive-empty-lines     2         Maximum number of consecutive empty lines."
+"
" +"Compliance: Inspirel" +""; + +titles[type("L006")] = "Source file should not be too long"; +explanations[type("L006")] = +"" +"L006: Source file should not be too long" +"
" +"The source file should not exceed a reasonable length. Long source files can indicate an opportunity for refactoring. Recognized parameters:" +"
"
+"    Name                Default   Description\n"
+"    ------------------- --------- ------------------------------------\n"
+"    max-file-length     2000      Maximum number of lines in a file."
+"
" +"Compliance: Inspirel" +""; + +titles[type("T001")] = "One-line comments should not have forced continuation"; +explanations[type("T001")] = +"" +"T001: One-line comments should not have forced continuation" +"
" +"The one-line comment is a comment that starts with `//`. The usual intent is to let the comment continue till the end of the line, but the preprocessing rules of the language allow to actually continue the comment in the next line if line-splicing is forced with the backslash at the end of the line:" +"
"
+"void foo()\n"
+"{\n"
+"    // this comment is continued in the next line \\\n"
+"    exit(0);\n"
+"}"
+"
" +"It is not immediately obvious what happens in this example. Moreover, the line-splicing works only if the backslash is really the last character in the line - which is error prone because any white characters that might appear after the backslash will change the meaning of the program without being visible in the code." +"

" +"Compliance: Inspirel" +""; + +titles[type("T002")] = "Reserved names should not be used for preprocessor macros"; +explanations[type("T002")] = +"" +"T002: Reserved names should not be used for preprocessor macros" +"
" +"The C++ Standard reserves some forms of names for language implementations. One of the most frequent violations is a definition of preprocessor macro that begins with underscore followed by a capital letter or containing two consecutive underscores:" +"
"
+"#define _MY_MACRO something\n"
+"#define MY__MACRO something"
+"
" +"Even though the majority of known compilers use more obscure names for internal purposes and the above code is not likely to cause any significant problems, all such names are formally reserved and therefore should not be used. Apart from the use of underscore in macro names, preprocessor macros should not be used to redefine language keywords:" +"
"
+"#define private public\n"
+"#define const"
+"
" +"Compliance: ISO" +""; + +titles[type("T003")] = "Some keywords should be followed by a single space"; +explanations[type("T003")] = +"" +"T003: Some keywords should be followed by a single space" +"
" +"Keywords from the following list: - `case` - `class` - `delete` - `enum` - `explicit` - `extern` - `goto` - `new` - `struct` - `union` - `using` should be followed by a single space for better readability." +"

" +"Compliance: Inspirel" +""; + +titles[type("T004")] = "Some keywords should be immediately followed by a colon"; +explanations[type("T004")] = +"" +"T004: Some keywords should be immediately followed by a colon" +"
" +"Keywords from the following list: - `default` - `private` - `protected` - `public` should be immediately followed by a colon, unless used in the list of base classes:" +"
"
+"class A : public B, private C\n"
+"{\n"
+"public:\n"
+"     A();\n"
+"     ~A();\n"
+"protected:\n"
+"     // ...\n"
+"private:\n"
+"     // ...\n"
+"};\n"
+"void fun(int a)\n"
+"{\n"
+"     switch (a)\n"
+"     {\n"
+"     // ...\n"
+"     default:\n"
+"          exit(0);\n"
+"     }\n"
+"}"
+"
" +"Compliance: Inspirel" +""; + +titles[type("T005")] = "Keywords break and continue should be immediately followed by a semicolon"; +explanations[type("T005")] = +"" +"T005: Keywords break and continue should be immediately followed by a semicolon" +"
" +"The `break` and `continue` keywords should be immediately followed by a semicolon, with no other tokens in between:" +"
"
+"while (...)\n"
+"{\n"
+"     if (...)\n"
+"     {\n"
+"          break;\n"
+"     }\n"
+"     if (...)\n"
+"     {\n"
+"          continue;\n"
+"     }\n"
+"     // ...\n"
+"}"
+"
" +"Compliance: Inspirel" +""; + +titles[type("T006")] = "Keywords return and throw should be immediately followed by a semicolon or a single space"; +explanations[type("T006")] = +"" +"T006: Keywords return and throw should be immediately followed by a semicolon or a single space" +"
" +"The `return` and `throw` keywords should be immediately followed by a semicolon or a single space:" +"
"
+"void fun()\n"
+"{\n"
+"     if (...)\n"
+"     {\n"
+"          return;\n"
+"     }\n"
+"     // ...\n"
+"}\n"
+"int add(int a, int b)\n"
+"{\n"
+"     return a + b;\n"
+"}"
+"
" +"An exception to this rule is allowed for exeption specifications:" +"
"
+"void fun() throw();"
+"
" +"Compliance: Inspirel" +""; + +titles[type("T007")] = "Semicolons should not be isolated by spaces or comments from the rest of the code"; +explanations[type("T007")] = +"" +"T007: Semicolons should not be isolated by spaces or comments from the rest of the code" +"
" +"The semicolon should not stand isolated by whitespace or comments from the rest of the code." +"
"
+"int a ;     // bad\n"
+"int b\n"
+";           // bad\n"
+"int c;      // OK"
+"
" +"As an exception from this rule, semicolons surrounded by spaces are allowed in `for` loops:" +"
"
+"for ( ; ; ) // OK as an exception\n"
+"{\n"
+"    // ...\n"
+"}"
+"
" +"Compliance: Inspirel" +""; + +titles[type("T008")] = "Keywords catch, for, if, switch and while should be followed by a single space"; +explanations[type("T008")] = +"" +"T008: Keywords catch, for, if, switch and while should be followed by a single space" +"
" +"Keywords `catch`, `for`, `if`, `switch` and `while` should be followed by a single space and then an opening left parenthesis:" +"
"
+"catch (...)\n"
+"{\n"
+"     for (int i = 0; i != 10; ++i)\n"
+"     {\n"
+"          if (foo(i))\n"
+"          {\n"
+"               while (getline(cin, line))\n"
+"               {\n"
+"                    switch (i % 3)\n"
+"                    {\n"
+"                    case 0:\n"
+"                         bar(line);\n"
+"                         break;\n"
+"                    // ...\n"
+"                    }\n"
+"               }\n"
+"          }\n"
+"     }\n"
+"}"
+"
" +"Compliance: Inspirel" +""; + +titles[type("T009")] = "Comma should not be preceded by whitespace, but should be followed by one"; +explanations[type("T009")] = +"" +"T009: Comma should not be preceded by whitespace, but should be followed by one" +"
" +"A comma, whether used as operator or in various lists, should not be preceded by whitespace on its left side, but should be followed by whitespace on its right side:" +"
"
+"void fun(int x, int y, int z);\n"
+"int a[] = {5, 6, 7};\n"
+"class A : public B,\n"
+"          public C\n"
+"{\n"
+"     // ...\n"
+"};"
+"
" +"An exception to this rule is allowed for `operator,`:" +"
"
+"struct A {};\n"
+"void operator,(const A &left, const A &right);"
+"
" +"Compliance: Inspirel" +""; + +titles[type("T010")] = "Identifiers should not be composed of 'l' and 'O' characters only"; +explanations[type("T010")] = +"" +"T010: Identifiers should not be composed of 'l' and 'O' characters only" +"
" +"The characters 'l' (which is lowercase 'L') and 'O' (which is uppercase 'o') should not be the only characters used in the identifier, because this would make them visually similar to numeric literals." +"

" +"Compliance: Inspirel" +""; + +titles[type("T011")] = "Curly brackets from the same pair should be either in the same line or in the same column"; +explanations[type("T011")] = +"" +"T011: Curly brackets from the same pair should be either in the same line or in the same column" +"
" +"Corresponding curly brackets should be either in the same line or in the same column. This promotes clarity by emphasising scopes, but allows concise style of one-line definitions and empty blocks:" +"
"
+"class MyException {};\n"
+"struct MyPair\n"
+"{\n"
+"    int a;\n"
+"    int b;\n"
+"};\n"
+"enum state { close, open };\n"
+"enum colors\n"
+"{\n"
+"    black,\n"
+"    red,\n"
+"    green,\n"
+"    blue,\n"
+"    white\n"
+"};"
+"
" +"Compliance: Inspirel" +""; + +titles[type("T012")] = "Negation operator should not be used in its short form"; +explanations[type("T012")] = +"" +"T012: Negation operator should not be used in its short form" +"
" +"The negation operator (exclamation mark) reduces readability of the code due to its terseness. Prefer explicit logical comparisons or alternative tokens for increased readability:" +"
"
+"if (!cond)         // error-prone\n"
+"if (cond == false) // better\n"
+"if (not cond)      // better (alternative keyword)"
+"
" +"Compliance: Inspirel" +""; + +titles[type("T013")] = "Source files should contain the copyright notice"; +explanations[type("T013")] = +"" +"T013: Source files should contain the copyright notice" +"
" +"The copyright notice is required by man coding standards and guidelines. In some countries every written artwork has some copyright, even if implicit. Prefer explicit notice to avoid any later confusion. This rule verifies that at least one comment in the source file contains the "copyright" word." +"

" +"Compliance: Boost" +""; + +titles[type("T014")] = "Source files should refer the Boost Software License"; +explanations[type("T014")] = +"" +"T014: Source files should refer the Boost Software License" +"
" +"The Boost Software License should be referenced in the source code. This rule verifies that at least one comment in the source file contains the "Boost Software License" phrase. Note that this rule is very specific to the Boost libraries and those project that choose to use the Boost license. It is therefore not part of the default profile." +"

" +"Compliance: Boost" +""; + +titles[type("T015")] = "HTML links in comments and string literals should be correct"; +explanations[type("T015")] = +"" +"T015: HTML links in comments and string literals should be correct" +"
" +"The links embedded in comments and string literals should have correct form and should reference existing files." +"

" +"Compliance: Boost" +""; + +titles[type("T016")] = "Calls to min/max should be protected against accidental macro substitution"; +explanations[type("T016")] = +"" +"T016: Calls to min/max should be protected against accidental macro substitution" +"
" +"The calls to min and max functions should be protected against accidental macro substitution." +"
"
+"x = max(y, z); // wrong, vulnerable to accidental macro substitution\n"
+"x = (max)(y, z); // OK\n"
+"x = max BOOST_PREVENT_MACRO_SUBSTITUTION (y, z); // OK"
+"
" +"Compliance: Boost" +""; + +titles[type("T017")] = "Unnamed namespaces are not allowed in header files"; +explanations[type("T017")] = +"" +"T017: Unnamed namespaces are not allowed in header files" +"
" +"Unnamed namespaces are not allowed in header files. The typical use of unnamed namespace is to hide module-internal names from the outside world. Header files are physically concatenated in a single translation unit, which logically merges all namespaces with the same name. Unnamed namespaces are also merged in this process, which effectively undermines their initial purpose. Use named namespaces in header files. Unnamed namespaces are allowed in implementation files only." +"

" +"Compliance: Boost" +""; + +titles[type("T018")] = "Using namespace is not allowed in header files"; +explanations[type("T018")] = +"" +"T018: Using namespace is not allowed in header files" +"
" +"Using namespace directives are not allowed in header files. The using namespace directive imports names from the given namespace and when used in a header file influences the global namespace of all the files that directly or indirectly include this header file. It is imaginable to use the using namespace directive in a limited scope in a header file (for example in a template or inline function definition), but for the sake of consistency this is also discouraged." +"

" +"Compliance: C++ Coding Standards" +""; + +titles[type("T019")] = "Control structures should have complete curly-braced block of code"; +explanations[type("T019")] = +"" +"T019: Control structures should have complete curly-braced block of code" +"
" +"Control structures managed by for, if and while constructs can be associated with a single instruction or with a complex block of code. Standardizing on the curly-braced blocks in all cases allows one to avoid common pitfalls and makes the code visually more uniform." +"
"
+"if (x) foo();     // bad style\n"
+"if (x) { foo(); } // OK\n"
+"if (x)\n"
+"    foo();        // again bad style\n"
+"if (x)\n"
+"{                 // OK\n"
+"    foo();\n"
+"}\n"
+"if (x)\n"
+"    while (y)     // bad style\n"
+"        foo();    // bad style\n"
+"if (x)\n"
+"{                 // OK\n"
+"    while (y)\n"
+"    {             // OK\n"
+"        foo();\n"
+"    }\n"
+"}\n"
+"for (int i = 0; i <= 10; ++i);  // oops!\n"
+"    cout << "Hello\\n";\n"
+"for (int i = 0; i <= 10; ++i)   // OK\n"
+"{\n"
+"    cout << "Hello\\n";\n"
+"}"
+"
" +"Compliance: Inspirel" +""; + +} + +} + +} Index: rules/rules_db_create.py =================================================================== --- /dev/null +++ rules/rules_db_create.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# +# This file is part of KDevelop +# +# Copyright 2016 Anton Anikin +# +# 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 2 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; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +from xml.sax.saxutils import escape, unescape +import pyparsing as pp +import string +import re + +# escape() and unescape() takes care of &, < and >. +html_escape_table = { + '"': """, + "'": "'" + } + +html_unescape_table = {v:k for k, v in html_escape_table.items()} + +def html_escape(text): + return escape(text, html_escape_table) + +def html_unescape(text): + return unescape(text, html_unescape_table) + +def parseFile(fileName): + EOL = pp.LineEnd().suppress() + endTag = pp.Literal('**Compliance:**') + + ruleTitle = pp.Regex(r'[A-Z]\d{3}') + pp.White().suppress() + pp.restOfLine() + pp.ZeroOrMore(EOL) + ruleTitle.setParseAction( + lambda t: '\ntitles[type("%s")] = "%s";\nexplanations[type("%s")] =\n\n%s: %s' % + (t[0], html_unescape(t[1]), + t[0], t[0], t[1]) + ) + + ruleEnd = endTag + pp.restOfLine() + pp.ZeroOrMore(EOL) + ruleEnd.setParseAction( lambda t: '%s\n' % (t[0] + t[1]) ) + + hline = pp.Regex(r'-{3,}') + pp.ZeroOrMore(EOL) + hline.setParseAction( lambda t: '
' ) + + textLine = pp.LineStart() + pp.NotAny('~') + pp.NotAny(endTag) + pp.NotAny(EOL) + pp.restOfLine() + EOL + textBlock = pp.OneOrMore(textLine) + pp.ZeroOrMore(EOL) + textBlock.setParseAction( lambda t: '%s\n

' % ' '.join(t) ) + + codeTag = pp.Regex(r'~~~~.*') + EOL + codeLine = pp.LineStart().leaveWhitespace() + pp.NotAny(codeTag) + pp.restOfLine() + pp.ZeroOrMore(EOL) + codeBlock = codeTag.suppress() + pp.OneOrMore(codeLine) + codeTag.suppress() + pp.ZeroOrMore(EOL) + + parametersLine = pp.Literal(' ').leaveWhitespace() + pp.restOfLine() + EOL + parametersLine.setParseAction( lambda t: ''.join(t) ) + parametersBlock = pp.OneOrMore(parametersLine) + pp.ZeroOrMore(EOL) + + listLine = pp.Literal('-') + pp.restOfLine() + EOL + listLine.setParseAction( lambda t: ("•"+t[1]).replace('`', '') ) + listBlock = pp.OneOrMore(listLine) + pp.ZeroOrMore(EOL) + + preBlock = codeBlock ^ parametersBlock ^ listBlock + preBlock.setParseAction( lambda t: '
\n%s\n
' % '\\n\n'.join(t) ) + + rule = ruleTitle + hline + pp.OneOrMore(preBlock ^ textBlock) + ruleEnd + rule.setParseAction( lambda t: '\n'.join(t) ) + + rules = pp.Literal("Rules").suppress() + pp.Literal("=====").suppress() + EOL + pp.OneOrMore(rule) + rules.setParseAction( lambda t: '\n'.join(t) ) + + t = open(fileName, 'r').read() + + # escaping + t = html_escape(t) + t = re.sub(r'\\', r'\\\\', t) + + t = rules.parseString(t)[0] + + # add bold and italic + t = re.sub(r'\*\*(.+)\*\*', r'\1', t) + t = re.sub(r'\*(.+)\*', r'\1', t) + + # remove
before and after
 blocks
+    t = re.sub(r'

\n
', r'
', t)
+    t = re.sub(r'
\n

', r'
', t) + + # 'stringify' lines + t = re.sub(r'(.+)', r'"\1"', t) + + # fix begin ane end of code + t = re.sub(r'"', r'";', t) + t = re.sub(r'"(titles\[.*)"', r'\1', t) + t = re.sub(r'"(explanations\[.*)"', r'\1', t) + + #r = re.sub(r'^"', r' "', r, flags = re.MULTILINE) + + return t + +prefix=\ + '// This file is generated by \'rules_db.py\' from \'Rules.md\'.\n' \ + '// Please don\'t edit it\n\n' \ + '#include "rules.h"\n\n' \ + '#include \n\n' \ + 'namespace verapp\n' \ + '{\n\n' \ + 'namespace rules\n' \ + '{\n\n' \ + 'void initDb(QString* titles, QString* explanations)\n' \ + '{\n' + +suffix='\n\n}\n\n}\n\n}\n' + +f = open('rules_db.cpp', 'w') +f.write(prefix) +f.write(parseFile('Rules.md')) +f.write(suffix) Index: tests/CMakeLists.txt =================================================================== --- /dev/null +++ tests/CMakeLists.txt @@ -0,0 +1,6 @@ +ecm_add_test( + test_verappjob.cpp + + TEST_NAME test_verappjob + LINK_LIBRARIES kdevverapp_core Qt5::Test KDev::Tests +) Index: tests/test_verappjob.h =================================================================== --- /dev/null +++ tests/test_verappjob.h @@ -0,0 +1,36 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef TEST_VERAPP_JOB_H +#define TEST_VERAPP_JOB_H + +#include + +class TestVerappJob : public QObject +{ + Q_OBJECT +private slots: + void initTestCase(); + void cleanupTestCase(); + + void testJob(); +}; + +#endif Index: tests/test_verappjob.cpp =================================================================== --- /dev/null +++ tests/test_verappjob.cpp @@ -0,0 +1,126 @@ +/* This file is part of KDevelop + + Copyright 2016 Anton Anikin + + 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 2 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "test_verappjob.h" + +#include "job.h" + +#include +#include + +#include + +using namespace KDevelop; +using namespace verapp; + +class JobTester : public Job +{ + Q_OBJECT + +public: + explicit JobTester(const Parameters& params) : Job(params) + { + connect(this, &JobTester::problemsDetected, this, &JobTester::problemsDetectedSlot); + } + + using Job::postProcessStdout; + + QVector problems() + { + return m_problems; + } + +private: + void problemsDetectedSlot(const QVector& problems) + { + m_problems += problems; + } + + QVector m_problems; +}; + +void TestVerappJob::initTestCase() +{ + AutoTestShell::init({"kdevverapp"}); + TestCore::initialize(Core::NoUi); +} + +void TestVerappJob::cleanupTestCase() +{ + TestCore::shutdown(); +} + +void TestVerappJob::testJob() +{ + QStringList stdoutOutput1 = { + "source1.cpp:35: L004: line is longer than 100 characters", + "source1.cpp:37: T009: comma should not be preceded by whitespace", + "source1.cpp:42: T011: closing curly bracket not in the same line or column", + }; + + QStringList stdoutOutput2 = { + "source2.cpp:13: T012: negation operator used in its short form", + "source2.cpp:30: T019: full block {} expected in the control structure" + }; + + Parameters jobParams; + JobTester jobTester(jobParams); + + // test incremental parsing + jobTester.postProcessStdout(stdoutOutput1); + QCOMPARE(jobTester.problems().size(), 3); + + jobTester.postProcessStdout(stdoutOutput2); + QCOMPARE(jobTester.problems().size(), 5); + + // test common values + auto problems = jobTester.problems(); + foreach (auto problem, problems) { + QCOMPARE(problem->source(), KDevelop::IProblem::Plugin); + QCOMPARE(problem->severity(), KDevelop::IProblem::Warning); + } + + // test description + QCOMPARE(problems[0]->description(), QStringLiteral("L004: line is longer than 100 characters")); + QCOMPARE(problems[1]->description(), QStringLiteral("T009: comma should not be preceded by whitespace")); + QCOMPARE(problems[2]->description(), QStringLiteral("T011: closing curly bracket not in the same line or column")); + QCOMPARE(problems[3]->description(), QStringLiteral("T012: negation operator used in its short form")); + QCOMPARE(problems[4]->description(), QStringLiteral("T019: full block {} expected in the control structure")); + + // test problem location (file) + QCOMPARE(problems[0]->finalLocation().document.str(), QStringLiteral("source1.cpp")); + QCOMPARE(problems[1]->finalLocation().document.str(), QStringLiteral("source1.cpp")); + QCOMPARE(problems[2]->finalLocation().document.str(), QStringLiteral("source1.cpp")); + + QCOMPARE(problems[3]->finalLocation().document.str(), QStringLiteral("source2.cpp")); + QCOMPARE(problems[4]->finalLocation().document.str(), QStringLiteral("source2.cpp")); + + // test problem location (line) + QCOMPARE(problems[0]->finalLocation().start().line(), 34); + QCOMPARE(problems[1]->finalLocation().start().line(), 36); + QCOMPARE(problems[2]->finalLocation().start().line(), 41); + + QCOMPARE(problems[3]->finalLocation().start().line(), 12); + QCOMPARE(problems[4]->finalLocation().start().line(), 29); +} + +QTEST_GUILESS_MAIN(TestVerappJob) + +#include "test_verappjob.moc"