diff --git a/analyzers/cppcheck/CMakeLists.txt b/analyzers/cppcheck/CMakeLists.txt index f85d23b419..f51cb5b826 100644 --- a/analyzers/cppcheck/CMakeLists.txt +++ b/analyzers/cppcheck/CMakeLists.txt @@ -1,50 +1,49 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kdevcppcheck\") include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) set(kdevcppcheck_SRCS plugin.cpp problemmodel.cpp config/globalconfigpage.cpp config/projectconfigpage.cpp ) ki18n_wrap_ui(kdevcppcheck_UI_SRCS config/globalconfigpage.ui config/projectconfigpage.ui ) kconfig_add_kcfg_files(kdevcppcheck_CONFIG_SRCS config/globalsettings.kcfgc config/projectsettings.kcfgc ) add_library(kdevcppcheck_core STATIC debug.cpp parser.cpp job.cpp parameters.cpp - problem.cpp utils.cpp ${kdevcppcheck_CONFIG_SRCS} ) target_link_libraries(kdevcppcheck_core KDev::Language KDev::Project KDev::Shell ) kdevplatform_add_plugin(kdevcppcheck JSON kdevcppcheck.json SOURCES ${kdevcppcheck_SRCS} ${kdevcppcheck_UI_SRCS} ) target_link_libraries(kdevcppcheck kdevcppcheck_core KF5::ItemViews ) install(FILES kdevcppcheck.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/kdevcppcheck) ecm_install_icons(ICONS icons/128-apps-cppcheck.png DESTINATION ${KDE_INSTALL_ICONDIR} THEME hicolor) add_subdirectory(tests) diff --git a/analyzers/cppcheck/job.cpp b/analyzers/cppcheck/job.cpp index 3a2cddcd07..b6a931164f 100644 --- a/analyzers/cppcheck/job.cpp +++ b/analyzers/cppcheck/job.cpp @@ -1,217 +1,217 @@ /* This file is part of KDevelop Copyright 2011 Mathieu Lornac Copyright 2011 Damien Coppel Copyright 2011 Lionel Duc Copyright 2011 Sebastien Rannou Copyright 2011 Lucas Sarie Copyright 2006-2008 Hamish Rodda Copyright 2002 Harald Fernengel Copyright 2013 Christoph Thielecke Copyright 2016-2017 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 "parser.h" -#include "problem.h" #include "utils.h" #include #include +#include #include #include #include namespace cppcheck { Job::Job(const Parameters& params, QObject* parent) : KDevelop::OutputExecuteJob(parent) , m_timer(new QElapsedTimer) , m_parser(new CppcheckParser) , m_showXmlOutput(params.showXmlOutput) , m_projectRootPath(params.projectRootPath()) { setJobName(i18n("Cppcheck Analysis (%1)", prettyPathName(params.checkPath))); setCapabilities(KJob::Killable); setStandardToolView(KDevelop::IOutputView::TestView); setBehaviours(KDevelop::IOutputView::AutoScroll); setProperties(KDevelop::OutputExecuteJob::JobProperty::DisplayStdout); setProperties(KDevelop::OutputExecuteJob::JobProperty::DisplayStderr); setProperties(KDevelop::OutputExecuteJob::JobProperty::PostProcessOutput); *this << params.commandLine(); qCDebug(KDEV_CPPCHECK) << "checking path" << params.checkPath; } Job::~Job() { doKill(); } void Job::postProcessStdout(const QStringList& lines) { static const auto fileNameRegex = QRegularExpression("Checking ([^:]*)\\.{3}"); static const auto percentRegex = QRegularExpression("(\\d+)% done"); QRegularExpressionMatch match; foreach (const QString & line, lines) { 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; } } m_standardOutput << lines; if (status() == KDevelop::OutputExecuteJob::JobStatus::JobRunning) { KDevelop::OutputExecuteJob::postProcessStdout(lines); } } void Job::postProcessStderr(const QStringList& lines) { static const auto xmlStartRegex = QRegularExpression("\\s*<"); for (const QString & line : lines) { // unfortunately sometime cppcheck send non-XML messages to stderr. // For example, if we pass '-I /missing_include_dir' to the argument list, // then stderr output will contains such line (tested on cppcheck 1.72): // // (information) Couldn't find path given by -I '/missing_include_dir' // // Therefore we must 'move' such messages to m_standardOutput. if (line.indexOf(xmlStartRegex) != -1) { // the line contains XML m_xmlOutput << line; m_parser->addData(line); m_problems = m_parser->parse(); emitProblems(); } else { - KDevelop::IProblem::Ptr problem(new CppcheckProblem); + KDevelop::IProblem::Ptr problem(new KDevelop::DetectedProblem); problem->setSeverity(KDevelop::IProblem::Error); problem->setDescription(line); problem->setExplanation("Check your cppcheck settings"); m_problems = {problem}; emitProblems(); if (m_showXmlOutput) { m_standardOutput << line; } else { postProcessStdout({line}); } } } if (status() == KDevelop::OutputExecuteJob::JobStatus::JobRunning && m_showXmlOutput) { KDevelop::OutputExecuteJob::postProcessStderr(lines); } } void Job::start() { m_standardOutput.clear(); m_xmlOutput.clear(); qCDebug(KDEV_CPPCHECK) << "executing:" << commandLine().join(' '); m_timer->restart(); KDevelop::OutputExecuteJob::start(); } void Job::childProcessError(QProcess::ProcessError e) { QString message; switch (e) { case QProcess::FailedToStart: message = i18n("Failed to start Cppcheck from \"%1\".", commandLine()[0]); break; case QProcess::Crashed: if (status() != KDevelop::OutputExecuteJob::JobStatus::JobCanceled) { message = i18n("Cppcheck crashed."); } break; case QProcess::Timedout: message = i18n("Cppcheck process timed out."); break; case QProcess::WriteError: message = i18n("Write to Cppcheck process failed."); break; case QProcess::ReadError: message = i18n("Read from Cppcheck process failed."); break; case QProcess::UnknownError: // current cppcheck errors will be displayed in the output view // don't notify the user break; } if (!message.isEmpty()) { KMessageBox::error(qApp->activeWindow(), message, i18n("Cppcheck Error")); } KDevelop::OutputExecuteJob::childProcessError(e); } void Job::childProcessExited(int exitCode, QProcess::ExitStatus exitStatus) { qCDebug(KDEV_CPPCHECK) << "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_CPPCHECK) << "cppcheck failed, standard output: "; qCDebug(KDEV_CPPCHECK) << m_standardOutput.join('\n'); qCDebug(KDEV_CPPCHECK) << "cppcheck failed, XML output: "; qCDebug(KDEV_CPPCHECK) << m_xmlOutput.join('\n'); } KDevelop::OutputExecuteJob::childProcessExited(exitCode, exitStatus); } void Job::emitProblems() { if (!m_problems.isEmpty()) { emit problemsDetected(m_problems); } } } diff --git a/analyzers/cppcheck/parser.cpp b/analyzers/cppcheck/parser.cpp index d2505cf9f3..90d46164f4 100644 --- a/analyzers/cppcheck/parser.cpp +++ b/analyzers/cppcheck/parser.cpp @@ -1,294 +1,294 @@ /* This file is part of KDevelop Copyright 2013 Christoph Thielecke 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 "parser.h" #include "debug.h" -#include "problem.h" #include #include +#include #include namespace cppcheck { /** * Convert the value of \ attribute of \ element from cppcheck's * XML-output to 'good-looking' HTML-version. This is necessary because the * displaying of the original message is performed without line breaks - such * tooltips are uncomfortable to read, and large messages will not fit into the * screen. * * This function put the original message into \ tag that automatically * provides line wrapping by builtin capabilities of Qt library. The source text * also can contain tokens '\012' (line break) - they are present in the case of * source code examples. In such cases, the entire text between the first and * last tokens (i.e. source code) is placed into \ tag. * * @param[in] input the original value of \ attribute * @return HTML version for displaying in problem's tooltip */ QString verboseMessageToHtml( const QString & input ) { QString output(QString("%1").arg(input.toHtmlEscaped())); output.replace("\\012", "\n"); if (output.count('\n') >= 2) { output.replace(output.indexOf('\n'), 1, "
" );
         output.replace(output.lastIndexOf('\n'), 1, "

" ); } return output; } CppcheckParser::CppcheckParser() : m_errorInconclusive(false) { } CppcheckParser::~CppcheckParser() { } void CppcheckParser::clear() { m_stateStack.clear(); } bool CppcheckParser::startElement() { State newState = Unknown; qCDebug(KDEV_CPPCHECK) << "CppcheckParser::startElement: elem: " << qPrintable(name().toString()); if (name() == "results") { newState = Results; } else if (name() == "cppcheck") { newState = CppCheck; } else if (name() == "errors") { newState = Errors; } else if (name() == "location") { newState = Location; if (attributes().hasAttribute("file") && attributes().hasAttribute("line")) { m_errorFiles += attributes().value("file").toString(); m_errorLines += attributes().value("line").toString().toInt(); } } else if (name() == "error") { newState = Error; m_errorSeverity = "unknown"; m_errorInconclusive = false; m_errorFiles.clear(); m_errorLines.clear(); m_errorMessage.clear(); m_errorVerboseMessage.clear(); if (attributes().hasAttribute("msg")) { m_errorMessage = attributes().value("msg").toString(); } if (attributes().hasAttribute("verbose")) { m_errorVerboseMessage = verboseMessageToHtml(attributes().value("verbose").toString()); } if (attributes().hasAttribute("severity")) { m_errorSeverity = attributes().value("severity").toString(); } if (attributes().hasAttribute("inconclusive")) { m_errorInconclusive = true; } } else { m_stateStack.push(m_stateStack.top()); return true; } m_stateStack.push(newState); return true; } bool CppcheckParser::endElement(QVector& problems) { qCDebug(KDEV_CPPCHECK) << "CppcheckParser::endElement: elem: " << qPrintable(name().toString()); State state = m_stateStack.pop(); switch (state) { case CppCheck: if (attributes().hasAttribute("version")) { qCDebug(KDEV_CPPCHECK) << "Cppcheck report version: " << attributes().value("version"); } break; case Errors: // errors finished break; case Error: qCDebug(KDEV_CPPCHECK) << "CppcheckParser::endElement: new error elem: line: " << (m_errorLines.isEmpty() ? "?" : QString::number(m_errorLines.first())) << " at " << (m_errorFiles.isEmpty() ? "?" : m_errorFiles.first()) << ", msg: " << m_errorMessage; storeError(problems); break; case Results: // results finished break; case Location: break; default: break; } return true; } QVector CppcheckParser::parse() { QVector problems; qCDebug(KDEV_CPPCHECK) << "CppcheckParser::parse!"; while (!atEnd()) { int readNextVal = readNext(); switch (readNextVal) { case StartDocument: clear(); break; case StartElement: startElement(); break; case EndElement: endElement(problems); break; case Characters: break; default: qCDebug(KDEV_CPPCHECK) << "CppcheckParser::startElement: case: " << readNextVal; break; } } qCDebug(KDEV_CPPCHECK) << "CppcheckParser::parse: end"; if (hasError()) { switch (error()) { case CustomError: case UnexpectedElementError: case NotWellFormedError: KMessageBox::error( qApp->activeWindow(), i18n("Cppcheck XML Parsing: error at line %1, column %2: %3", lineNumber(), columnNumber(), errorString()), i18n("Cppcheck Error")); break; case NoError: case PrematureEndOfDocumentError: break; } } return problems; } void CppcheckParser::storeError(QVector& problems) { // Construct problem with using first location element KDevelop::IProblem::Ptr problem = getProblem(); // Adds other elements as diagnostics. // This allows the user to track the problem. for (int locationIdx = 1; locationIdx < m_errorFiles.size(); ++locationIdx) { problem->addDiagnostic(getProblem(locationIdx)); } problems.push_back(problem); } KDevelop::IProblem::Ptr CppcheckParser::getProblem(int locationIdx) const { - KDevelop::IProblem::Ptr problem(new CppcheckProblem); + KDevelop::IProblem::Ptr problem(new KDevelop::DetectedProblem); QStringList messagePrefix; QString errorMessage(m_errorMessage); if (m_errorSeverity == "error") { problem->setSeverity(KDevelop::IProblem::Error); } else if (m_errorSeverity == "warning") { problem->setSeverity(KDevelop::IProblem::Warning); } else { problem->setSeverity(KDevelop::IProblem::Hint); messagePrefix.push_back(m_errorSeverity); } if (m_errorInconclusive) { messagePrefix.push_back("inconclusive"); } if (!messagePrefix.isEmpty()) { errorMessage = QString("(%1) %2").arg(messagePrefix.join(", ")).arg(m_errorMessage); } problem->setDescription(errorMessage); problem->setExplanation(m_errorVerboseMessage); KDevelop::DocumentRange range; if (locationIdx < 0 || locationIdx >= m_errorFiles.size()) { range = KDevelop::DocumentRange::invalid(); } else { range.document = KDevelop::IndexedString(m_errorFiles.at(locationIdx)); range.setBothLines(m_errorLines.at(locationIdx) - 1); range.setBothColumns(0); } problem->setFinalLocation(range); problem->setFinalLocationMode(KDevelop::IProblem::TrimmedLine); return problem; } } diff --git a/analyzers/cppcheck/problem.cpp b/analyzers/cppcheck/problem.cpp deleted file mode 100644 index f83ea71231..0000000000 --- a/analyzers/cppcheck/problem.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* This file is part of KDevelop - - Copyright 2017 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 "problem.h" - -namespace cppcheck -{ - -CppcheckProblem::CppcheckProblem() -{ -} - -CppcheckProblem::~CppcheckProblem() -{ -} - -CppcheckProblem::Source CppcheckProblem::source() const -{ - return Plugin; -}; - -QString CppcheckProblem::sourceString() const -{ - return QStringLiteral("Cppcheck"); -}; - -} diff --git a/analyzers/cppcheck/problem.h b/analyzers/cppcheck/problem.h deleted file mode 100644 index 339ddbab04..0000000000 --- a/analyzers/cppcheck/problem.h +++ /dev/null @@ -1,39 +0,0 @@ -/* This file is part of KDevelop - - Copyright 2017 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. -*/ - -#pragma once - -#include - -namespace cppcheck -{ - -class CppcheckProblem - : public KDevelop::DetectedProblem -{ -public: - CppcheckProblem(); - ~CppcheckProblem() override; - - Source source() const override; - QString sourceString() const override; -}; - -}