From 1bc605d05014026f7b3eebcbb9fb69962eca82eb Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Wed, 2 May 2018 14:51:05 +0200 Subject: [PATCH] ClangTools: Prompt for files to analyze If the run button is clicked, the user is prompted to select the files for analysis. Change-Id: I21e4ee6b7c14392a8c1a901ac7aa1c9c16e30f0d Reviewed-by: Ivan Donchevskii --- src/plugins/clangtools/clangfileinfo.h | 47 ++++ .../clangtools/clangselectablefilesdialog.cpp | 233 ++++++++++++++++++ .../clangtools/clangselectablefilesdialog.h | 65 +++++ .../clangtools/clangselectablefilesdialog.ui | 71 ++++++ ...taticanalyzerpreconfiguredsessiontests.cpp | 2 +- .../clangstaticanalyzerruncontrol.cpp | 6 +- .../clangstaticanalyzerruncontrol.h | 4 +- .../clangtools/clangstaticanalyzertool.cpp | 14 +- .../clangtools/clangstaticanalyzertool.h | 2 +- .../clangtools/clangtidyclazyruncontrol.cpp | 6 +- .../clangtools/clangtidyclazyruncontrol.h | 3 +- src/plugins/clangtools/clangtidyclazytool.cpp | 15 +- src/plugins/clangtools/clangtidyclazytool.h | 2 +- src/plugins/clangtools/clangtool.cpp | 48 ++++ src/plugins/clangtools/clangtool.h | 7 +- .../clangtools/clangtoolruncontrol.cpp | 52 ++-- src/plugins/clangtools/clangtoolruncontrol.h | 8 +- src/plugins/clangtools/clangtools.pro | 4 + src/plugins/clangtools/clangtools.qbs | 4 + .../clangtools/clangtoolsunittests.cpp | 2 +- .../projectexplorer/selectablefilesmodel.cpp | 6 - .../projectexplorer/selectablefilesmodel.h | 6 +- 22 files changed, 546 insertions(+), 61 deletions(-) create mode 100644 src/plugins/clangtools/clangfileinfo.h create mode 100644 src/plugins/clangtools/clangselectablefilesdialog.cpp create mode 100644 src/plugins/clangtools/clangselectablefilesdialog.h create mode 100644 src/plugins/clangtools/clangselectablefilesdialog.ui diff --git a/src/plugins/clangtools/clangfileinfo.h b/src/plugins/clangtools/clangfileinfo.h new file mode 100644 index 00000000000..a543ca1f274 --- /dev/null +++ b/src/plugins/clangtools/clangfileinfo.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace ClangTools { +namespace Internal { + +class FileInfo +{ +public: + Utils::FileName file; + CppTools::ProjectFile::Kind kind; + CppTools::ProjectPart::Ptr projectPart; +}; + +using FileInfos = QVector; + +} // namespace Internal +} // namespace ClangTools diff --git a/src/plugins/clangtools/clangselectablefilesdialog.cpp b/src/plugins/clangtools/clangselectablefilesdialog.cpp new file mode 100644 index 00000000000..7d001c29489 --- /dev/null +++ b/src/plugins/clangtools/clangselectablefilesdialog.cpp @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangselectablefilesdialog.h" +#include "ui_clangselectablefilesdialog.h" + +#include "clangtoolsutils.h" + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +using namespace CppTools; +using namespace Utils; +using namespace ProjectExplorer; + +namespace ClangTools { +namespace Internal { + +class TreeWithFileInfo : public Tree +{ +public: + FileInfo info; +}; + +static void linkDirNode(Tree *parentNode, Tree *childNode) +{ + parentNode->childDirectories.append(childNode); + childNode->parent = parentNode; +} + +static void linkFileNode(Tree *parentNode, Tree *childNode) +{ + childNode->parent = parentNode; + parentNode->files.append(childNode); + parentNode->visibleFiles.append(childNode); +} + +static Tree *createDirNode(const QString &name, const FileName &filePath = FileName()) +{ + auto node = new Tree; + node->name = name; + node->fullPath = filePath; + node->isDir = true; + + return node; +} + +static Tree *createFileNode(const FileInfo &fileInfo, bool displayFullPath = false) +{ + auto node = new TreeWithFileInfo; + node->name = displayFullPath ? fileInfo.file.toString() : fileInfo.file.fileName(); + node->fullPath = fileInfo.file; + node->info = fileInfo; + + return node; +} + +class SelectableFilesModel : public ProjectExplorer::SelectableFilesModel +{ + Q_OBJECT + +public: + SelectableFilesModel(const CppTools::ProjectInfo &projectInfo, const FileInfos &allFileInfos) + : ProjectExplorer::SelectableFilesModel(nullptr) + { + buildTree(projectInfo.project(), allFileInfos); + } + + void buildTree(ProjectExplorer::Project *project, const FileInfos &fileInfos) + { + m_root->fullPath = project->projectFilePath(); + m_root->name = project->projectFilePath().fileName(); + m_root->isDir = true; + + FileInfos outOfBaseDirFiles; + Tree *projectDirTree = buildProjectDirTree(project->projectDirectory(), + fileInfos, + outOfBaseDirFiles); + if (outOfBaseDirFiles.isEmpty()) { + // Showing the project file and beneath the project dir is pointless in this case, + // so get rid of the root node and modify the project dir node as the new root node. + projectDirTree->name = m_root->name; + projectDirTree->fullPath = m_root->fullPath; + projectDirTree->parent = m_root->parent; + + delete m_root; // OK, it has no files / child dirs. + + m_root = projectDirTree; + } else { + // Set up project dir node as sub node of the project file node + linkDirNode(m_root, projectDirTree); + + // Add files outside of the base directory to a separate node + Tree *externalFilesNode = createDirNode(SelectableFilesDialog::tr( + "Files outside of the base directory"), + FileName::fromString("/")); + linkDirNode(m_root, externalFilesNode); + for (const FileInfo &fileInfo : outOfBaseDirFiles) + linkFileNode(externalFilesNode, createFileNode(fileInfo, true)); + } + } + + static Tree *buildProjectDirTree(const FileName &projectDir, + const FileInfos &fileInfos, + FileInfos &outOfBaseDirFiles) + { + Tree *projectDirNode = createDirNode(projectDir.fileName(), projectDir); + + QHash dirsToNode; + dirsToNode.insert(projectDirNode->fullPath, projectDirNode); + + for (const FileInfo &fileInfo : fileInfos) { + if (!fileInfo.file.isChildOf(projectDirNode->fullPath)) { + outOfBaseDirFiles += fileInfo; + continue; // Handle these separately. + } + + // Find or create parent nodes + FileName parentDir = fileInfo.file.parentDir(); + Tree *parentNode = dirsToNode[parentDir]; + if (!parentNode) { + // Find nearest existing node + QStringList dirsToCreate; + while (!parentNode) { + dirsToCreate.prepend(parentDir.fileName()); + parentDir = parentDir.parentDir(); + parentNode = dirsToNode[parentDir]; + } + + // Create needed extra dir nodes + FileName currentDirPath = parentDir; + for (const QString &dirName : dirsToCreate) { + currentDirPath.appendPath(dirName); + + Tree *newDirNode = createDirNode(dirName, currentDirPath); + linkDirNode(parentNode, newDirNode); + + dirsToNode.insert(currentDirPath, newDirNode); + parentNode = newDirNode; + } + } + + // Create and link file node to dir node + linkFileNode(parentNode, createFileNode(fileInfo)); + } + + return projectDirNode; + } + + FileInfos selectedFileInfos() const + { + FileInfos result; + collectFileInfos(m_root, &result); + return result; + } + +private: + void collectFileInfos(ProjectExplorer::Tree *root, FileInfos *result) const + { + if (root->checked == Qt::Unchecked) + return; + for (Tree *t : root->childDirectories) + collectFileInfos(t, result); + for (Tree *t : root->visibleFiles) { + if (t->checked == Qt::Checked) + result->append(static_cast(t)->info); + } + } +}; + +SelectableFilesDialog::SelectableFilesDialog(const ProjectInfo &projectInfo, + const FileInfos &allFileInfos) + : QDialog(nullptr) + , m_ui(new Ui::SelectableFilesDialog) + , m_filesModel(new SelectableFilesModel(projectInfo, allFileInfos)) + , m_analyzeButton(new QPushButton(tr("Analyze"), this)) +{ + m_ui->setupUi(this); + + m_ui->filesView->setModel(m_filesModel.get()); + m_ui->filesView->expandToDepth(2); + + m_ui->buttons->setStandardButtons(QDialogButtonBox::Cancel); + m_ui->buttons->addButton(m_analyzeButton, QDialogButtonBox::AcceptRole); + + m_analyzeButton->setEnabled(m_filesModel->hasCheckedFiles()); + connect(m_filesModel.get(), &QAbstractItemModel::dataChanged, [this]() { + m_analyzeButton->setEnabled(m_filesModel->hasCheckedFiles()); + }); +} + +SelectableFilesDialog::~SelectableFilesDialog() {} + +FileInfos SelectableFilesDialog::filteredFileInfos() const +{ + return m_filesModel->selectedFileInfos(); +} + +} // namespace Internal +} // namespace ClangTools + +#include "clangselectablefilesdialog.moc" diff --git a/src/plugins/clangtools/clangselectablefilesdialog.h b/src/plugins/clangtools/clangselectablefilesdialog.h new file mode 100644 index 00000000000..58c93d08808 --- /dev/null +++ b/src/plugins/clangtools/clangselectablefilesdialog.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangfileinfo.h" + +#include + +#include + +QT_BEGIN_NAMESPACE +class QPushButton; +QT_END_NAMESPACE + +namespace CppTools { class ProjectInfo; } + +namespace ClangTools { +namespace Internal { + +namespace Ui { class SelectableFilesDialog; } +class SelectableFilesModel; + +class SelectableFilesDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SelectableFilesDialog(const CppTools::ProjectInfo &projectInfo, + const FileInfos &allFileInfos); + ~SelectableFilesDialog() override; + + FileInfos filteredFileInfos() const; + +private: + std::unique_ptr m_ui; + std::unique_ptr m_filesModel; + + QPushButton *m_analyzeButton = nullptr; +}; + +} // namespace Internal +} // namespace ClangTools diff --git a/src/plugins/clangtools/clangselectablefilesdialog.ui b/src/plugins/clangtools/clangselectablefilesdialog.ui new file mode 100644 index 00000000000..16d620cd7c3 --- /dev/null +++ b/src/plugins/clangtools/clangselectablefilesdialog.ui @@ -0,0 +1,71 @@ + + + ClangTools::Internal::SelectableFilesDialog + + + + 0 + 0 + 700 + 600 + + + + Select the Files to Analyze + + + + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttons + accepted() + ClangTools::Internal::SelectableFilesDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttons + rejected() + ClangTools::Internal::SelectableFilesDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/plugins/clangtools/clangstaticanalyzerpreconfiguredsessiontests.cpp b/src/plugins/clangtools/clangstaticanalyzerpreconfiguredsessiontests.cpp index 31d9efb518f..4e8cb876015 100644 --- a/src/plugins/clangtools/clangstaticanalyzerpreconfiguredsessiontests.cpp +++ b/src/plugins/clangtools/clangstaticanalyzerpreconfiguredsessiontests.cpp @@ -120,7 +120,7 @@ void ClangStaticAnalyzerPreconfiguredSessionTests::testPreconfiguredSession() QVERIFY(switchToProjectAndTarget(project, target)); - ClangStaticAnalyzerTool::instance()->startTool(); + ClangStaticAnalyzerTool::instance()->startTool(false); QSignalSpy waitUntilAnalyzerFinished(ClangStaticAnalyzerTool::instance(), SIGNAL(finished(bool))); QVERIFY(waitUntilAnalyzerFinished.wait(30000)); const QList arguments = waitUntilAnalyzerFinished.takeFirst(); diff --git a/src/plugins/clangtools/clangstaticanalyzerruncontrol.cpp b/src/plugins/clangtools/clangstaticanalyzerruncontrol.cpp index 8bacb4af88d..3e66f569184 100644 --- a/src/plugins/clangtools/clangstaticanalyzerruncontrol.cpp +++ b/src/plugins/clangtools/clangstaticanalyzerruncontrol.cpp @@ -71,8 +71,10 @@ using namespace ProjectExplorer; namespace ClangTools { namespace Internal { -ClangStaticAnalyzerRunControl::ClangStaticAnalyzerRunControl(RunControl *runControl, Target *target) - : ClangToolRunControl(runControl, target) +ClangStaticAnalyzerRunControl::ClangStaticAnalyzerRunControl(RunControl *runControl, + Target *target, + const FileInfos &fileInfos) + : ClangToolRunControl(runControl, target, fileInfos) { setDisplayName("ClangStaticAnalyzerRunner"); diff --git a/src/plugins/clangtools/clangstaticanalyzerruncontrol.h b/src/plugins/clangtools/clangstaticanalyzerruncontrol.h index 98bd4f18f1b..a5cad51e920 100644 --- a/src/plugins/clangtools/clangstaticanalyzerruncontrol.h +++ b/src/plugins/clangtools/clangstaticanalyzerruncontrol.h @@ -25,6 +25,7 @@ #pragma once +#include "clangfileinfo.h" #include "clangtoolruncontrol.h" namespace ClangTools { @@ -36,7 +37,8 @@ class ClangStaticAnalyzerRunControl final : public ClangToolRunControl public: ClangStaticAnalyzerRunControl(ProjectExplorer::RunControl *runControl, - ProjectExplorer::Target *target); + ProjectExplorer::Target *target, + const FileInfos &fileInfos); protected: ClangToolRunner *createRunner() final; diff --git a/src/plugins/clangtools/clangstaticanalyzertool.cpp b/src/plugins/clangtools/clangstaticanalyzertool.cpp index 651d794a27b..7f57272daaf 100644 --- a/src/plugins/clangtools/clangstaticanalyzertool.cpp +++ b/src/plugins/clangtools/clangstaticanalyzertool.cpp @@ -108,11 +108,11 @@ ClangStaticAnalyzerTool::ClangStaticAnalyzerTool() {{ClangStaticAnalyzerDockId, m_diagnosticView, {}, Perspective::SplitVertical}} )); - action = new QAction(tr("Clang Static Analyzer"), this); + action = new QAction(tr("Clang Static Analyzer..."), this); action->setToolTip(toolTip); menu->addAction(ActionManager::registerAction(action, "ClangStaticAnalyzer.Action"), Debugger::Constants::G_ANALYZER_TOOLS); - QObject::connect(action, &QAction::triggered, this, &ClangStaticAnalyzerTool::startTool); + QObject::connect(action, &QAction::triggered, this, [this]() { startTool(true); }); QObject::connect(m_startAction, &QAction::triggered, action, &QAction::triggered); QObject::connect(m_startAction, &QAction::changed, action, [action, this] { action->setEnabled(m_startAction->isEnabled()); @@ -136,7 +136,7 @@ ClangStaticAnalyzerTool *ClangStaticAnalyzerTool::instance() return s_instance; } -void ClangStaticAnalyzerTool::startTool() +void ClangStaticAnalyzerTool::startTool(bool askUserForFileSelection) { auto runControl = new RunControl(nullptr, Constants::CLANGSTATICANALYZER_RUN_MODE); runControl->setDisplayName(tr("Clang Static Analyzer")); @@ -146,7 +146,13 @@ void ClangStaticAnalyzerTool::startTool() QTC_ASSERT(project, return); QTC_ASSERT(project->activeTarget(), return); - auto clangTool = new ClangStaticAnalyzerRunControl(runControl, project->activeTarget()); + const FileInfos fileInfos = collectFileInfos(project, askUserForFileSelection); + if (fileInfos.isEmpty()) + return; + + auto clangTool = new ClangStaticAnalyzerRunControl(runControl, + project->activeTarget(), + fileInfos); m_stopAction->disconnect(); connect(m_stopAction, &QAction::triggered, runControl, [runControl] { diff --git a/src/plugins/clangtools/clangstaticanalyzertool.h b/src/plugins/clangtools/clangstaticanalyzertool.h index e44330eddc3..9f4433a78fa 100644 --- a/src/plugins/clangtools/clangstaticanalyzertool.h +++ b/src/plugins/clangtools/clangstaticanalyzertool.h @@ -47,7 +47,7 @@ public: static ClangStaticAnalyzerTool *instance(); - void startTool() final; + void startTool(bool askUserForFileSelection) final; QList read(const QString &, const QString &logFilePath, diff --git a/src/plugins/clangtools/clangtidyclazyruncontrol.cpp b/src/plugins/clangtools/clangtidyclazyruncontrol.cpp index a3ec062c221..44fcf746df7 100644 --- a/src/plugins/clangtools/clangtidyclazyruncontrol.cpp +++ b/src/plugins/clangtools/clangtidyclazyruncontrol.cpp @@ -33,8 +33,10 @@ using namespace ProjectExplorer; namespace ClangTools { namespace Internal { -ClangTidyClazyRunControl::ClangTidyClazyRunControl(RunControl *runControl, Target *target) - : ClangToolRunControl(runControl, target) +ClangTidyClazyRunControl::ClangTidyClazyRunControl(RunControl *runControl, + Target *target, + const FileInfos &fileInfos) + : ClangToolRunControl(runControl, target, fileInfos) { setDisplayName("ClangTidyClazyRunner"); init(); diff --git a/src/plugins/clangtools/clangtidyclazyruncontrol.h b/src/plugins/clangtools/clangtidyclazyruncontrol.h index 59073608fef..4cc2b39ddc5 100644 --- a/src/plugins/clangtools/clangtidyclazyruncontrol.h +++ b/src/plugins/clangtools/clangtidyclazyruncontrol.h @@ -36,7 +36,8 @@ class ClangTidyClazyRunControl final : public ClangToolRunControl public: ClangTidyClazyRunControl(ProjectExplorer::RunControl *runControl, - ProjectExplorer::Target *target); + ProjectExplorer::Target *target, + const FileInfos &fileInfos); protected: ClangToolRunner *createRunner() final; diff --git a/src/plugins/clangtools/clangtidyclazytool.cpp b/src/plugins/clangtools/clangtidyclazytool.cpp index ddd1cab9e2d..0442c5f293d 100644 --- a/src/plugins/clangtools/clangtidyclazytool.cpp +++ b/src/plugins/clangtools/clangtidyclazytool.cpp @@ -25,6 +25,7 @@ #include "clangtidyclazytool.h" +#include "clangselectablefilesdialog.h" #include "clangtoolsconstants.h" #include "clangtoolsdiagnosticmodel.h" #include "clangtoolslogfilereader.h" @@ -33,6 +34,8 @@ #include #include +#include + #include #include @@ -76,11 +79,11 @@ ClangTidyClazyTool::ClangTidyClazyTool() {{ClangTidyClazyDockId, m_diagnosticView, {}, Perspective::SplitVertical}} )); - auto *action = new QAction(tr("Clang-Tidy and Clazy"), this); + auto *action = new QAction(tr("Clang-Tidy and Clazy..."), this); action->setToolTip(toolTip); menu->addAction(ActionManager::registerAction(action, "ClangTidyClazy.Action"), Debugger::Constants::G_ANALYZER_TOOLS); - QObject::connect(action, &QAction::triggered, this, &ClangTidyClazyTool::startTool); + QObject::connect(action, &QAction::triggered, this, [this]() { startTool(true); }); QObject::connect(m_startAction, &QAction::triggered, action, &QAction::triggered); QObject::connect(m_startAction, &QAction::changed, action, [action, this] { action->setEnabled(m_startAction->isEnabled()); @@ -103,7 +106,7 @@ ClangTidyClazyTool *ClangTidyClazyTool::instance() return s_instance; } -void ClangTidyClazyTool::startTool() +void ClangTidyClazyTool::startTool(bool askUserForFileSelection) { auto runControl = new RunControl(nullptr, Constants::CLANGTIDYCLAZY_RUN_MODE); runControl->setDisplayName(tr("Clang-Tidy and Clazy")); @@ -112,7 +115,11 @@ void ClangTidyClazyTool::startTool() Project *project = SessionManager::startupProject(); QTC_ASSERT(project, return); - auto clangTool = new ClangTidyClazyRunControl(runControl, project->activeTarget()); + const FileInfos fileInfos = collectFileInfos(project, askUserForFileSelection); + if (fileInfos.isEmpty()) + return; + + auto clangTool = new ClangTidyClazyRunControl(runControl, project->activeTarget(), fileInfos); m_stopAction->disconnect(); connect(m_stopAction, &QAction::triggered, runControl, [runControl] { diff --git a/src/plugins/clangtools/clangtidyclazytool.h b/src/plugins/clangtools/clangtidyclazytool.h index e9e998b914c..0ec9e2e6084 100644 --- a/src/plugins/clangtools/clangtidyclazytool.h +++ b/src/plugins/clangtools/clangtidyclazytool.h @@ -42,7 +42,7 @@ public: static ClangTidyClazyTool *instance(); - void startTool() final; + void startTool(bool askUserForFileSelection) final; QList read(const QString &filePath, const QString &logFilePath, diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp index 7031cc98c56..7c63fa4ee47 100644 --- a/src/plugins/clangtools/clangtool.cpp +++ b/src/plugins/clangtools/clangtool.cpp @@ -25,15 +25,19 @@ #include "clangtool.h" +#include "clangselectablefilesdialog.h" #include "clangtoolsconstants.h" #include "clangtoolsdiagnostic.h" #include "clangtoolsdiagnosticmodel.h" +#include "clangtoolsutils.h" #include #include #include #include +#include + #include #include @@ -58,6 +62,33 @@ using namespace Utils; namespace ClangTools { namespace Internal { +static FileInfos sortedFileInfos(const QVector &projectParts) +{ + FileInfos fileInfos; + + for (CppTools::ProjectPart::Ptr projectPart : projectParts) { + QTC_ASSERT(projectPart, continue); + if (!projectPart->selectedForBuilding) + continue; + + for (const CppTools::ProjectFile &file : projectPart->files) { + QTC_ASSERT(file.kind != CppTools::ProjectFile::Unclassified, continue); + QTC_ASSERT(file.kind != CppTools::ProjectFile::Unsupported, continue); + if (file.path == CppTools::CppModelManager::configurationFileName()) + continue; + + if (CppTools::ProjectFile::isSource(file.kind)) { + const FileInfo info{Utils::FileName::fromString(file.path), file.kind, projectPart}; + fileInfos.append(info); + } + } + } + + Utils::sort(fileInfos, &FileInfo::file); + + return fileInfos; +} + ClangTool::ClangTool(const QString &name) : m_name(name) { @@ -67,6 +98,23 @@ ClangTool::ClangTool(const QString &name) m_stopAction = Debugger::createStopAction(); } +FileInfos ClangTool::collectFileInfos(Project *project, bool askUserForFileSelection) const +{ + auto projectInfo = CppTools::CppModelManager::instance()->projectInfo(project); + QTC_ASSERT(projectInfo.isValid(), return FileInfos()); + + const FileInfos allFileInfos = sortedFileInfos(projectInfo.projectParts()); + + if (askUserForFileSelection) { + SelectableFilesDialog dialog(projectInfo, allFileInfos); + if (dialog.exec() == QDialog::Rejected) + return FileInfos(); + return dialog.filteredFileInfos(); + } else { + return allFileInfos; + } +} + const QString &ClangTool::name() const { return m_name; diff --git a/src/plugins/clangtools/clangtool.h b/src/plugins/clangtools/clangtool.h index e7f11bb0ccf..6363cedb641 100644 --- a/src/plugins/clangtools/clangtool.h +++ b/src/plugins/clangtools/clangtool.h @@ -25,6 +25,8 @@ #pragma once +#include "clangfileinfo.h" + #include #include @@ -44,12 +46,15 @@ public: ClangTool(const QString &name); virtual ~ClangTool() = default; - virtual void startTool() = 0; + virtual void startTool(bool askUserForFileSelection) = 0; virtual QList read(const QString &filePath, const QString &logFilePath, QString *errorMessage) const = 0; + FileInfos collectFileInfos(ProjectExplorer::Project *project, + bool askUserForFileSelection) const; + // For testing. QList diagnostics() const; diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp index 6d8e674319d..c296b53d012 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.cpp +++ b/src/plugins/clangtools/clangtoolruncontrol.cpp @@ -185,48 +185,31 @@ private: bool m_success = false; }; -static AnalyzeUnits unitsToAnalyzeFromProjectParts(const QVector projectParts, - const QString &clangVersion, - const QString &clangResourceDirectory) +static AnalyzeUnits toAnalyzeUnits(const FileInfos &fileInfos, + const QString &clangVersion, + const QString &clangResourceDirectory) { - qCDebug(LOG) << "Taking arguments for analyzing from ProjectParts."; - AnalyzeUnits unitsToAnalyze; - - foreach (const ProjectPart::Ptr &projectPart, projectParts) { - if (!projectPart->selectedForBuilding || !projectPart.data()) - continue; - - foreach (const ProjectFile &file, projectPart->files) { - if (file.path == CppModelManager::configurationFileName()) - continue; - QTC_CHECK(file.kind != ProjectFile::Unclassified); - QTC_CHECK(file.kind != ProjectFile::Unsupported); - if (ProjectFile::isSource(file.kind)) { - const CompilerOptionsBuilder::PchUsage pchUsage = CppTools::getPchUsage(); - CompilerOptionsBuilder optionsBuilder(*projectPart, clangVersion, - clangResourceDirectory); - QStringList arguments = extraClangToolsPrependOptions(); - arguments.append(optionsBuilder.build(file.kind, pchUsage)); - arguments.append(extraClangToolsAppendOptions()); - unitsToAnalyze << AnalyzeUnit(file.path, arguments); - } - } + const CompilerOptionsBuilder::PchUsage pchUsage = CppTools::getPchUsage(); + for (const FileInfo &fileInfo : fileInfos) { + CompilerOptionsBuilder optionsBuilder(*fileInfo.projectPart, + clangVersion, + clangResourceDirectory); + QStringList arguments = extraClangToolsPrependOptions(); + arguments.append(optionsBuilder.build(fileInfo.kind, pchUsage)); + arguments.append(extraClangToolsAppendOptions()); + unitsToAnalyze << AnalyzeUnit(fileInfo.file.toString(), arguments); } return unitsToAnalyze; } -AnalyzeUnits ClangToolRunControl::sortedUnitsToAnalyze(const QString &clangVersion) +AnalyzeUnits ClangToolRunControl::unitsToAnalyze(const QString &clangVersion) { QTC_ASSERT(m_projectInfo.isValid(), return AnalyzeUnits()); const QString clangResourceDirectory = clangIncludeDirectory(m_clangExecutable, clangVersion); - AnalyzeUnits units = unitsToAnalyzeFromProjectParts(m_projectInfo.projectParts(), clangVersion, - clangResourceDirectory); - - Utils::sort(units, &AnalyzeUnit::file); - return units; + return toAnalyzeUnits(m_fileInfos, clangVersion, clangResourceDirectory); } static QDebug operator<<(QDebug debug, const Utils::Environment &environment) @@ -243,11 +226,14 @@ static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits) return debug; } -ClangToolRunControl::ClangToolRunControl(RunControl *runControl, Target *target) +ClangToolRunControl::ClangToolRunControl(RunControl *runControl, + Target *target, + const FileInfos &fileInfos) : RunWorker(runControl) , m_projectBuilder(new ProjectBuilder(runControl, target->project(), this)) , m_clangExecutable(CppTools::clangExecutable(CLANG_BINDIR)) , m_target(target) + , m_fileInfos(fileInfos) { addStartDependency(m_projectBuilder); @@ -327,7 +313,7 @@ void ClangToolRunControl::start() m_clangLogFileDir = temporaryDir.path(); // Collect files - const AnalyzeUnits unitsToProcess = sortedUnitsToAnalyze(CLANG_VERSION); + const AnalyzeUnits unitsToProcess = unitsToAnalyze(CLANG_VERSION); qCDebug(LOG) << "Files to process:" << unitsToProcess; m_unitsToProcess = unitsToProcess; m_initialFilesToProcessSize = m_unitsToProcess.count(); diff --git a/src/plugins/clangtools/clangtoolruncontrol.h b/src/plugins/clangtools/clangtoolruncontrol.h index 0d13bf9263c..dae3521bc1a 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.h +++ b/src/plugins/clangtools/clangtoolruncontrol.h @@ -25,6 +25,8 @@ #pragma once +#include "clangfileinfo.h" + #include #include #include @@ -55,7 +57,8 @@ class ClangToolRunControl : public ProjectExplorer::RunWorker public: ClangToolRunControl(ProjectExplorer::RunControl *runControl, - ProjectExplorer::Target *target); + ProjectExplorer::Target *target, + const FileInfos &fileInfos); bool success() const { return m_success; } // For testing. @@ -73,7 +76,7 @@ private: void start() final; void stop() final; - AnalyzeUnits sortedUnitsToAnalyze(const QString &clangVersion); + AnalyzeUnits unitsToAnalyze(const QString &clangVersion); void analyzeNextFile(); void handleFinished(); @@ -91,6 +94,7 @@ protected: private: QPointer m_target; + FileInfos m_fileInfos; CppTools::ProjectInfo m_projectInfoBeforeBuild; CppTools::ProjectInfo m_projectInfo; diff --git a/src/plugins/clangtools/clangtools.pro b/src/plugins/clangtools/clangtools.pro index f7271ee072c..fd96317a50f 100644 --- a/src/plugins/clangtools/clangtools.pro +++ b/src/plugins/clangtools/clangtools.pro @@ -9,6 +9,7 @@ LIBS += $$LIBCLANG_LIBS INCLUDEPATH += $$LLVM_INCLUDEPATH SOURCES += \ + clangselectablefilesdialog.cpp \ clangstaticanalyzerdiagnosticview.cpp \ clangstaticanalyzerprojectsettings.cpp \ clangstaticanalyzerprojectsettingsmanager.cpp \ @@ -31,6 +32,8 @@ SOURCES += \ clangtoolsconfigwidget.cpp HEADERS += \ + clangfileinfo.h \ + clangselectablefilesdialog.h \ clangstaticanalyzerdiagnosticview.h \ clangstaticanalyzerprojectsettings.h \ clangstaticanalyzerprojectsettingsmanager.h \ @@ -57,6 +60,7 @@ HEADERS += \ FORMS += \ clangstaticanalyzerprojectsettingswidget.ui \ clangtoolsconfigwidget.ui + clangselectablefilesdialog.ui equals(TEST, 1) { HEADERS += \ diff --git a/src/plugins/clangtools/clangtools.qbs b/src/plugins/clangtools/clangtools.qbs index cedbbaa31c8..ebb250ebb34 100644 --- a/src/plugins/clangtools/clangtools.qbs +++ b/src/plugins/clangtools/clangtools.qbs @@ -41,6 +41,10 @@ QtcPlugin { } files: [ + "clangfileinfo.h", + "clangselectablefilesdialog.cpp", + "clangselectablefilesdialog.h", + "clangselectablefilesdialog.ui", "clangstaticanalyzerdiagnosticview.cpp", "clangstaticanalyzerdiagnosticview.h", "clangstaticanalyzerprojectsettings.cpp", diff --git a/src/plugins/clangtools/clangtoolsunittests.cpp b/src/plugins/clangtools/clangtoolsunittests.cpp index 3c31d933700..95343fc59e1 100644 --- a/src/plugins/clangtools/clangtoolsunittests.cpp +++ b/src/plugins/clangtools/clangtoolsunittests.cpp @@ -131,7 +131,7 @@ void ClangToolsUnitTests::testProject() settings->setClangDiagnosticConfigId(clangTidyConfig.id()); } - tool->startTool(); + tool->startTool(false); QSignalSpy waiter(tool, SIGNAL(finished(bool))); QVERIFY(waiter.wait(30000)); diff --git a/src/plugins/projectexplorer/selectablefilesmodel.cpp b/src/plugins/projectexplorer/selectablefilesmodel.cpp index 81c3aaeb590..a8fce7aee46 100644 --- a/src/plugins/projectexplorer/selectablefilesmodel.cpp +++ b/src/plugins/projectexplorer/selectablefilesmodel.cpp @@ -47,12 +47,6 @@ const char HIDE_FILE_FILTER_DEFAULT[] = "Makefile*; *.o; *.lo; *.la; *.obj; *~; " *.config; *.creator; *.user*; *.includes; *.autosave"; const char SHOW_FILE_FILTER_DEFAULT[] = "*.c; *.cc; *.cpp; *.cp; *.cxx; *.c++; *.h; *.hh; *.hpp; *.hxx;"; -Tree::~Tree() -{ - qDeleteAll(childDirectories); - qDeleteAll(files); -} - SelectableFilesModel::SelectableFilesModel(QObject *parent) : QAbstractItemModel(parent) { m_root = new Tree; diff --git a/src/plugins/projectexplorer/selectablefilesmodel.h b/src/plugins/projectexplorer/selectablefilesmodel.h index f5b5869b509..c33d8a21106 100644 --- a/src/plugins/projectexplorer/selectablefilesmodel.h +++ b/src/plugins/projectexplorer/selectablefilesmodel.h @@ -47,7 +47,11 @@ namespace ProjectExplorer { class Tree { public: - ~Tree(); + virtual ~Tree() + { + qDeleteAll(childDirectories); + qDeleteAll(files); + } QString name; Qt::CheckState checked = Qt::Unchecked;