// Copyright (C) 2018 Sergey Morozov // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cppchecktool.h" #include "cppcheckdiagnostic.h" #include "cppcheckrunner.h" #include "cppchecksettings.h" #include "cppchecktextmarkmanager.h" #include "cppchecktr.h" #include #include #include #include #include #include #include #include #include using namespace Utils; namespace Cppcheck::Internal { CppcheckTool::CppcheckTool(CppcheckDiagnosticManager &manager, const Id &progressId) : m_manager(manager), m_progressRegexp("^.* checked (\\d+)% done$"), m_messageRegexp("^(.+),(\\d+),(\\w+),(\\w+),(.*)$"), m_progressId(progressId) { m_runner = std::make_unique(*this); QTC_ASSERT(m_progressRegexp.isValid(), return); QTC_ASSERT(m_messageRegexp.isValid(), return); } CppcheckTool::~CppcheckTool() = default; void CppcheckTool::updateOptions(const CppcheckSettings &settings) { m_filters.clear(); for (const QString &pattern : settings.ignoredPatterns().split(',')) { const QString trimmedPattern = pattern.trimmed(); if (trimmedPattern.isEmpty()) continue; const QRegularExpression re(Utils::wildcardToRegularExpression(trimmedPattern)); if (re.isValid()) m_filters.push_back(re); } updateArguments(settings); } void CppcheckTool::setProject(ProjectExplorer::Project *project) { m_project = project; updateArguments(settings()); } void CppcheckTool::updateArguments(const CppcheckSettings &settings) { if (!m_project) return; m_cachedAdditionalArguments.clear(); QStringList arguments; if (!settings.customArguments().isEmpty()) { Utils::MacroExpander *expander = Utils::globalMacroExpander(); const QString expanded = expander->expand(settings.customArguments()); arguments.push_back(expanded); } if (settings.warning()) arguments.push_back("--enable=warning"); if (settings.style()) arguments.push_back("--enable=style"); if (settings.performance()) arguments.push_back("--enable=performance"); if (settings.portability()) arguments.push_back("--enable=portability"); if (settings.information()) arguments.push_back("--enable=information"); if (settings.unusedFunction()) arguments.push_back("--enable=unusedFunction"); if (settings.missingInclude()) arguments.push_back("--enable=missingInclude"); if (settings.inconclusive()) arguments.push_back("--inconclusive"); if (settings.forceDefines()) arguments.push_back("--force"); if (!settings.unusedFunction() && !settings.customArguments().contains("-j ")) arguments.push_back("-j " + QString::number(QThread::idealThreadCount())); arguments.push_back("--template=\"{file},{line},{severity},{id},{message}\""); m_runner->reconfigure(settings.binary.effectiveBinary(), arguments.join(' ')); } QStringList CppcheckTool::additionalArguments(const CppEditor::ProjectPart &part) const { QStringList result; if (settings().addIncludePaths()) { for (const ProjectExplorer::HeaderPath &path : part.headerPaths) { const QString projectDir = m_project->projectDirectory().toString(); if (path.type == ProjectExplorer::HeaderPathType::User && path.path.startsWith(projectDir)) result.push_back("-I " + path.path); } } if (!settings().guessArguments()) return result; using Version = Utils::LanguageVersion; switch (part.languageVersion) { case Version::C89: result.push_back("--std=c89 --language=c"); break; case Version::C99: result.push_back("--std=c99 --language=c"); break; case Version::C11: result.push_back("--std=c11 --language=c"); break; case Version::C18: result.push_back("--language=c"); break; case Version::CXX03: result.push_back("--std=c++03 --language=c++"); break; case Version::CXX11: result.push_back("--std=c++11 --language=c++"); break; case Version::CXX14: result.push_back("--std=c++14 --language=c++"); break; case Version::CXX98: case Version::CXX17: case Version::CXX20: case Version::CXX2b: result.push_back("--language=c++"); break; case Version::None: break; } if (part.qtVersion != Utils::QtMajorVersion::None) result.push_back("--library=qt"); return result; } void CppcheckTool::check(const Utils::FilePaths &files) { QTC_ASSERT(m_project, return); Utils::FilePaths filtered; if (m_filters.isEmpty()) { filtered = files; } else { std::copy_if(files.cbegin(), files.cend(), std::back_inserter(filtered), [this](const Utils::FilePath &file) { const QString stringed = file.toString(); const auto filter = [stringed](const QRegularExpression &re) { return re.match(stringed).hasMatch(); }; return !Utils::contains(m_filters, filter); }); } if (filtered.isEmpty()) return; const CppEditor::ProjectInfo::ConstPtr info = CppEditor::CppModelManager::projectInfo(m_project); if (!info) return; const QVector parts = info->projectParts(); if (parts.size() == 1) { QTC_ASSERT(parts.first(), return); addToQueue(filtered, *parts.first()); return; } std::map groups; for (const FilePath &file : std::as_const(filtered)) { for (const CppEditor::ProjectPart::ConstPtr &part : parts) { using CppEditor::ProjectFile; QTC_ASSERT(part, continue); const auto match = [file](const ProjectFile &pFile){return pFile.path == file;}; if (Utils::contains(part->files, match)) groups[part].push_back(file); } } for (const auto &group : groups) addToQueue(group.second, *group.first); } void CppcheckTool::addToQueue(const Utils::FilePaths &files, const CppEditor::ProjectPart &part) { const QString key = part.id(); if (!m_cachedAdditionalArguments.contains(key)) m_cachedAdditionalArguments.insert(key, additionalArguments(part).join(' ')); m_runner->addToQueue(files, m_cachedAdditionalArguments[key]); } void CppcheckTool::stop(const Utils::FilePaths &files) { m_runner->removeFromQueue(files); m_runner->stop(files); } void CppcheckTool::startParsing() { if (settings().showOutput()) { const QString message = Tr::tr("Cppcheck started: \"%1\".").arg(m_runner->currentCommand()); Core::MessageManager::writeSilently(message); } m_progress = std::make_unique>(); const Core::FutureProgress *progress = Core::ProgressManager::addTask( m_progress->future(), Tr::tr("Cppcheck"), m_progressId); QObject::connect(progress, &Core::FutureProgress::canceled, this, [this]{stop({});}); m_progress->setProgressRange(0, 100); m_progress->reportStarted(); } void CppcheckTool::parseOutputLine(const QString &line) { if (line.isEmpty()) return; if (settings().showOutput()) Core::MessageManager::writeSilently(line); enum Matches { Percentage = 1 }; const QRegularExpressionMatch match = m_progressRegexp.match(line); if (!match.hasMatch()) return; QTC_ASSERT(m_progress, return); const int done = match.captured(Percentage).toInt(); m_progress->setProgressValue(done); } static Diagnostic::Severity toSeverity(const QString &text) { static const QMap values{ {"error", Diagnostic::Severity::Error}, {"warning", Diagnostic::Severity::Warning}, {"performance", Diagnostic::Severity::Performance}, {"portability", Diagnostic::Severity::Portability}, {"style", Diagnostic::Severity::Style}, {"information", Diagnostic::Severity::Information} }; return values.value(text, Diagnostic::Severity::Information); } void CppcheckTool::parseErrorLine(const QString &line) { if (line.isEmpty()) return; if (settings().showOutput()) Core::MessageManager::writeSilently(line); enum Matches { File = 1, Line, Severity, Id, Message }; const QRegularExpressionMatch match = m_messageRegexp.match(line); if (!match.hasMatch()) return; const Utils::FilePath fileName = Utils::FilePath::fromUserInput(match.captured(File)); if (!m_runner->currentFiles().contains(fileName)) return; Diagnostic diagnostic; diagnostic.fileName = fileName; diagnostic.lineNumber = std::max(match.captured(Line).toInt(), 1); diagnostic.severityText = match.captured(Severity); diagnostic.severity = toSeverity(diagnostic.severityText); diagnostic.checkId = match.captured(Id); diagnostic.message = match.captured(Message); if (diagnostic.isValid()) m_manager.add(diagnostic); } void CppcheckTool::finishParsing() { if (settings().showOutput()) Core::MessageManager::writeSilently(Tr::tr("Cppcheck finished.")); QTC_ASSERT(m_progress, return); m_progress->reportFinished(); } void CppcheckTool::finishWithFail(const QString &exitMessage) { if (!exitMessage.isEmpty()) Core::MessageManager::writeSilently(exitMessage); QTC_ASSERT(m_progress, return); m_progress->cancelAndFinish(); } } // Cppcheck::Internal