diff options
author | Kai Koehne <kai.koehne@theqtcompany.com> | 2014-11-18 15:08:43 +0100 |
---|---|---|
committer | Kai Koehne <kai.koehne@theqtcompany.com> | 2014-11-18 15:08:49 +0100 |
commit | 9328c87ba4325b88b981753ba46faa34e9532704 (patch) | |
tree | ec2265a70c91e1af1d5266b96cbd5d4eb0006f6b | |
parent | f6eb83490a3f4a793eea1dad3d0d23e4d2fc801c (diff) | |
parent | 37b8e7252e13a3b74c7e94a908f5818e60127c89 (diff) |
Merge branch '3.3'
Change-Id: Ibf7466649a400f8a652f284be8edd7e33a5f969a
14 files changed, 361 insertions, 94 deletions
diff --git a/plugins/clangstaticanalyzer/ClangStaticAnalyzer.json.in b/plugins/clangstaticanalyzer/ClangStaticAnalyzer.json.in index 77cc31b40d..13c2aa5b82 100644 --- a/plugins/clangstaticanalyzer/ClangStaticAnalyzer.json.in +++ b/plugins/clangstaticanalyzer/ClangStaticAnalyzer.json.in @@ -3,6 +3,7 @@ \"Version\" : \"$$QTCREATOR_VERSION\", \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", \"Experimental\" : true, + \"Platform\" : \"(Linux.*)|(OS X.*)\", \"Vendor\" : \"Digia Plc\", \"Copyright\" : \"(C) 2014 Digia Plc\", \"License\" : [ \"Commercial Usage\", diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs b/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs new file mode 100644 index 0000000000..6c2612a69c --- /dev/null +++ b/plugins/clangstaticanalyzer/clangstaticanalyzer.qbs @@ -0,0 +1,47 @@ +import qbs + +QtcPlugin { + name: "ClangStaticAnalyzer" + + Depends { name: "AnalyzerBase" } + Depends { name: "Core" } + Depends { name: "CppTools" } + Depends { name: "ExtensionSystem" } + Depends { name: "LicenseChecker" } + Depends { name: "ProjectExplorer" } + Depends { name: "QtcSsh" } // TODO: export + recursive dependencies broken in qbs + Depends { name: "Utils" } + + Depends { name: "Qt.widgets" } + Depends { name: "Qt.network" } // TODO: See above + + files: [ + "clangstaticanalyzerconfigwidget.cpp", + "clangstaticanalyzerconfigwidget.h", + "clangstaticanalyzerconfigwidget.ui", + "clangstaticanalyzerconstants.h", + "clangstaticanalyzerdiagnostic.cpp", + "clangstaticanalyzerdiagnostic.h", + "clangstaticanalyzerdiagnosticmodel.cpp", + "clangstaticanalyzerdiagnosticmodel.h", + "clangstaticanalyzerdiagnosticview.cpp", + "clangstaticanalyzerdiagnosticview.h", + "clangstaticanalyzerlogfilereader.cpp", + "clangstaticanalyzerlogfilereader.h", + "clangstaticanalyzerplugin.cpp", + "clangstaticanalyzerplugin.h", + "clangstaticanalyzerruncontrol.cpp", + "clangstaticanalyzerruncontrol.h", + "clangstaticanalyzerruncontrolfactory.cpp", + "clangstaticanalyzerruncontrolfactory.h", + "clangstaticanalyzerrunner.cpp", + "clangstaticanalyzerrunner.h", + "clangstaticanalyzersettings.cpp", + "clangstaticanalyzersettings.h", + "clangstaticanalyzertool.cpp", + "clangstaticanalyzertool.h", + "clangstaticanalyzerutils.cpp", + "clangstaticanalyzerutils.h", + "clangstaticanalyzer_global.h", + ] +} diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.cpp index e9152ffa47..09d810dfc9 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.cpp @@ -46,5 +46,10 @@ bool ExplainingStep::isValid() const return location.isValid() && !ranges.isEmpty() && !message.isEmpty(); } +bool Diagnostic::isValid() const +{ + return !description.isEmpty(); +} + } // namespace Internal } // namespace ClangStaticAnalyzer diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.h b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.h index 3348cfa416..75df6218a9 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.h +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnostic.h @@ -56,6 +56,8 @@ public: class Diagnostic { public: + bool isValid() const; + QString description; QString category; QString type; diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp index fd9e216ed7..6d00968c86 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticview.cpp @@ -168,6 +168,7 @@ DetailedErrorDelegate::SummaryLineInfo ClangStaticAnalyzerDiagnosticDelegate::su const QModelIndex &index) const { const Diagnostic diagnostic = index.data(Qt::UserRole).value<Diagnostic>(); + QTC_ASSERT(diagnostic.isValid(), return SummaryLineInfo()); DetailedErrorDelegate::SummaryLineInfo info; info.errorText = diagnostic.description; @@ -188,6 +189,8 @@ QWidget *ClangStaticAnalyzerDiagnosticDelegate::createDetailsWidget(const QFont QVBoxLayout *layout = new QVBoxLayout; const Diagnostic diagnostic = index.data(Qt::UserRole).value<Diagnostic>(); + if (!diagnostic.isValid()) + return widget; // Add summary label QLabel *summaryLineLabel = createSummaryLabel(diagnostic); diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp index 0a321d64e8..e635305b97 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerplugin.cpp @@ -121,10 +121,9 @@ bool ClangStaticAnalyzerPlugin::initializeEnterpriseFeatures(const QStringList & Q_UNUSED(arguments); Q_UNUSED(errorString); - addAutoReleasedObject(new ClangStaticAnalyzerOptionsPage); - addAutoReleasedObject(new ClangStaticAnalyzerRunControlFactory); - m_analyzerTool = new ClangStaticAnalyzerTool(this); + addAutoReleasedObject(new ClangStaticAnalyzerRunControlFactory(m_analyzerTool)); + addAutoReleasedObject(new ClangStaticAnalyzerOptionsPage); const QString toolTip = tr("Clang Static Analyzer uses the analyzer from the clang project " "to find bugs."); diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp index e0c7b11e71..f9b3506102 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp @@ -37,6 +37,8 @@ #include <projectexplorer/project.h> #include <projectexplorer/target.h> +#include <utils/algorithm.h> + #include <QLoggingCategory> #include <QTemporaryDir> @@ -49,48 +51,124 @@ namespace ClangStaticAnalyzer { namespace Internal { ClangStaticAnalyzerRunControl::ClangStaticAnalyzerRunControl( - const Analyzer::AnalyzerStartParameters &startParams, - ProjectExplorer::RunConfiguration *runConfiguration) + const Analyzer::AnalyzerStartParameters &startParams, + ProjectExplorer::RunConfiguration *runConfiguration, + const ProjectInfo &projectInfo) : AnalyzerRunControl(startParams, runConfiguration) + , m_projectInfo(projectInfo) , m_initialFilesToProcessSize(0) + , m_filesAnalyzed(0) + , m_filesNotAnalyzed(0) +{ +} + +// Removes (1) filePath (2) -o <somePath> +static QStringList tweakedArguments(const QString &filePath, const QStringList &arguments) { + QStringList newArguments; + + bool skip = false; + foreach (const QString &argument, arguments) { + if (skip) { + skip = false; + continue; + } else if (argument == QLatin1String("-o")) { + skip = true; + continue; + } else if (argument == filePath) { + continue; // TODO: Let it in? + } + + newArguments << argument; + } + QTC_CHECK(skip == false); + + return newArguments; } -static QList<ClangStaticAnalyzerRunControl::SourceFileConfiguration> calculateFilesToProcess( - Project *project) +static QStringList argumentsFromProjectPart(const CppTools::ProjectPart::Ptr &projectPart, + CppTools::ProjectFile::Kind fileKind) { - typedef ClangStaticAnalyzerRunControl::SourceFileConfiguration SourceFileConfiguration; - QTC_ASSERT(project, return QList<SourceFileConfiguration>()); - ProjectInfo projectInfo = CppModelManager::instance()->projectInfo(project); - QTC_ASSERT(projectInfo, return QList<SourceFileConfiguration>()); + QStringList result; + + const bool objcExt = projectPart->languageExtensions & ProjectPart::ObjectiveCExtensions; + result += CppTools::CompilerOptionsBuilder::createLanguageOption(fileKind, objcExt); + result += CppTools::CompilerOptionsBuilder::createOptionsForLanguage( + projectPart->languageVersion, + projectPart->languageExtensions); + result += CppTools::CompilerOptionsBuilder::createDefineOptions(projectPart->toolchainDefines); + result += CppTools::CompilerOptionsBuilder::createDefineOptions(projectPart->projectDefines); + result += CppTools::CompilerOptionsBuilder::createHeaderPathOptions(projectPart->headerPaths); + result += QLatin1String("-fPIC"); // TODO: Remove? + + return result; +} + +static QList<ClangStaticAnalyzerRunControl::AnalyzeUnit> unitsToAnalyzeFromCompilerCallData( + const ProjectInfo::CompilerCallData &compilerCallData) +{ + typedef ClangStaticAnalyzerRunControl::AnalyzeUnit AnalyzeUnit; + qCDebug(LOG) << "Taking arguments for analyzing from CompilerCallData."; + + QList<ClangStaticAnalyzerRunControl::AnalyzeUnit> unitsToAnalyze; + + QHashIterator<QString, QList<QStringList> > it(compilerCallData); + while (it.hasNext()) { + it.next(); + const QString file = it.key(); + const QList<QStringList> compilerCalls = it.value(); + foreach (const QStringList &options, compilerCalls) { + const QStringList arguments = tweakedArguments(file, options); + unitsToAnalyze << AnalyzeUnit(file, arguments); + } + } + + return unitsToAnalyze; +} + +static QList<ClangStaticAnalyzerRunControl::AnalyzeUnit> unitsToAnalyzeFromProjectParts( + const QList<ProjectPart::Ptr> projectParts) +{ + typedef ClangStaticAnalyzerRunControl::AnalyzeUnit AnalyzeUnit; + qCDebug(LOG) << "Taking arguments for analyzing from ProjectParts."; + + QList<ClangStaticAnalyzerRunControl::AnalyzeUnit> unitsToAnalyze; - QList<SourceFileConfiguration> files; - const QList<ProjectPart::Ptr> projectParts = projectInfo.projectParts(); foreach (const ProjectPart::Ptr &projectPart, projectParts) { + if (!projectPart->selectedForBuilding) + continue; + foreach (const ProjectFile &file, projectPart->files) { if (file.path == CppModelManager::configurationFileName()) continue; QTC_CHECK(file.kind != ProjectFile::Unclassified); - if (ProjectFile::isSource(file.kind)) - files << SourceFileConfiguration(file, projectPart); + if (ProjectFile::isSource(file.kind)) { + const QStringList arguments = argumentsFromProjectPart(projectPart, file.kind); + unitsToAnalyze << AnalyzeUnit(file.path, arguments); + } } } - return files; + return unitsToAnalyze; +} + +static QList<ClangStaticAnalyzerRunControl::AnalyzeUnit> unitsToAnalyze( + const CppTools::ProjectInfo &projectInfo) +{ + QTC_ASSERT(projectInfo.isValid(), return QList<ClangStaticAnalyzerRunControl::AnalyzeUnit>()); + + const ProjectInfo::CompilerCallData compilerCallData = projectInfo.compilerCallData(); + if (!compilerCallData.isEmpty()) + return unitsToAnalyzeFromCompilerCallData(compilerCallData); + return unitsToAnalyzeFromProjectParts(projectInfo.projectParts()); } bool ClangStaticAnalyzerRunControl::startEngine() { emit starting(this); - RunConfiguration *runConfig = runConfiguration(); - QTC_ASSERT(runConfig, emit finished(); return false); - Target *target = runConfig->target(); - QTC_ASSERT(target, emit finished(); return false); - Project *project = target->project(); - QTC_ASSERT(project, emit finished(); return false); - - const QString projectFile = project->projectFilePath().toString(); + QTC_ASSERT(m_projectInfo.isValid(), emit finished(); return false); + const QString projectFile = m_projectInfo.project()->projectFilePath().toString(); appendMessage(tr("Running Clang Static Analyzer on %1").arg(projectFile) + QLatin1Char('\n'), Utils::NormalMessageFormat); @@ -118,14 +196,18 @@ bool ClangStaticAnalyzerRunControl::startEngine() m_clangLogFileDir = temporaryDir.path(); // Collect files - const QList<SourceFileConfiguration> filesToProcess = calculateFilesToProcess(project); + QList<AnalyzeUnit> unitsToProcess = unitsToAnalyze(m_projectInfo); + Utils::sort(unitsToProcess, [](const AnalyzeUnit &a1, const AnalyzeUnit &a2) -> bool { + return a1.file < a2.file; + }); + qCDebug(LOG) << "Files to process:"; - foreach (const SourceFileConfiguration &fileConfig, filesToProcess) { - qCDebug(LOG) << fileConfig.file.path + QLatin1String(" [") - + fileConfig.projectPart->projectFile + QLatin1Char(']'); - } - m_filesToProcess = filesToProcess; - m_initialFilesToProcessSize = m_filesToProcess.count(); + foreach (const AnalyzeUnit &fileConfig, unitsToProcess) + qCDebug(LOG) << fileConfig.file; + m_unitsToProcess = unitsToProcess; + m_initialFilesToProcessSize = m_unitsToProcess.count(); + m_filesAnalyzed = 0; + m_filesNotAnalyzed = 0; // Set up progress information using namespace Core; @@ -141,7 +223,7 @@ bool ClangStaticAnalyzerRunControl::startEngine() m_runners.clear(); const int parallelRuns = ClangStaticAnalyzerSettings::instance()->simultaneousProcesses(); QTC_ASSERT(parallelRuns >= 1, emit finished(); return false); - while (m_runners.size() < parallelRuns && !m_filesToProcess.isEmpty()) + while (m_runners.size() < parallelRuns && !m_unitsToProcess.isEmpty()) analyzeNextFile(); return true; } @@ -155,8 +237,11 @@ void ClangStaticAnalyzerRunControl::stopEngine() delete runner; } m_runners.clear(); - m_filesToProcess.clear(); - analyzeNextFile(); // emits finished + m_unitsToProcess.clear(); + appendMessage(tr("Clang Static Analyzer stopped by user.") + QLatin1Char('\n'), + Utils::NormalMessageFormat); + m_progress.reportFinished(); + emit finished(); } void ClangStaticAnalyzerRunControl::analyzeNextFile() @@ -164,9 +249,13 @@ void ClangStaticAnalyzerRunControl::analyzeNextFile() if (m_progress.isFinished()) return; // The previous call already reported that we are finished. - if (m_filesToProcess.isEmpty()) { + if (m_unitsToProcess.isEmpty()) { if (m_runners.size() == 0) { - appendMessage(tr("Clang Static Analyzer finished.") + QLatin1Char('\n'), + appendMessage(tr("Clang Static Analyzer finished: " + "Processed %1 files successfully, %2 failed.") + .arg(m_filesAnalyzed) + .arg(m_filesNotAnalyzed) + + QLatin1Char('\n'), Utils::NormalMessageFormat); m_progress.reportFinished(); emit finished(); @@ -174,14 +263,15 @@ void ClangStaticAnalyzerRunControl::analyzeNextFile() return; } - const SourceFileConfiguration config = m_filesToProcess.takeFirst(); - const QString filePath = config.file.path; - const QStringList options = config.createClangOptions(); + const AnalyzeUnit unit = m_unitsToProcess.takeFirst(); + qCDebug(LOG) << "analyzeNextFile:" << unit.file; ClangStaticAnalyzerRunner *runner = createRunner(); m_runners.insert(runner); - qCDebug(LOG) << "analyzeNextFile:" << filePath; - QTC_ASSERT(runner->run(filePath, options), return); + QTC_ASSERT(runner->run(unit.file, unit.arguments), return); + + appendMessage(tr("Analyzing \"%1\".").arg(unit.file) + QLatin1Char('\n'), + Utils::StdOutFormat); } ClangStaticAnalyzerRunner *ClangStaticAnalyzerRunControl::createRunner() @@ -201,21 +291,36 @@ ClangStaticAnalyzerRunner *ClangStaticAnalyzerRunControl::createRunner() void ClangStaticAnalyzerRunControl::onRunnerFinishedWithSuccess(const QString &logFilePath) { qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << logFilePath; - handleFinished(); QString errorMessage; const QList<Diagnostic> diagnostics = LogFileReader::read(logFilePath, &errorMessage); - QTC_CHECK(errorMessage.isEmpty()); - if (!errorMessage.isEmpty()) + if (!errorMessage.isEmpty()) { qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage; - if (!diagnostics.isEmpty()) - emit newDiagnosticsAvailable(diagnostics); + const QString filePath = qobject_cast<ClangStaticAnalyzerRunner *>(sender())->filePath(); + appendMessage(tr("Failed to analyze \"%1\": %2").arg(filePath, errorMessage) + + QLatin1Char('\n') + , Utils::StdErrFormat); + } else { + ++m_filesAnalyzed; + if (!diagnostics.isEmpty()) + emit newDiagnosticsAvailable(diagnostics); + } + + handleFinished(); } void ClangStaticAnalyzerRunControl::onRunnerFinishedWithFailure(const QString &errorMessage, const QString &errorDetails) { qCDebug(LOG) << "onRunnerFinishedWithFailure:" << errorMessage << errorDetails; + + ++m_filesNotAnalyzed; + const QString filePath = qobject_cast<ClangStaticAnalyzerRunner *>(sender())->filePath(); + appendMessage(tr("Failed to analyze \"%1\": %2").arg(filePath, errorMessage) + + QLatin1Char('\n') + , Utils::StdErrFormat); + appendMessage(errorDetails, Utils::StdErrFormat); + handleFinished(); } @@ -236,24 +341,7 @@ void ClangStaticAnalyzerRunControl::onProgressCanceled() void ClangStaticAnalyzerRunControl::updateProgressValue() { - m_progress.setProgressValue(m_initialFilesToProcessSize - m_filesToProcess.size()); -} - -QStringList ClangStaticAnalyzerRunControl::SourceFileConfiguration::createClangOptions() const -{ - QStringList result; - - const bool objcExt = projectPart->languageExtensions & ProjectPart::ObjectiveCExtensions; - result += CppTools::CompilerOptionsBuilder::createLanguageOption(file.kind, objcExt); - result += CppTools::CompilerOptionsBuilder::createOptionsForLanguage( - projectPart->languageVersion, - projectPart->languageExtensions); - result += CppTools::CompilerOptionsBuilder::createDefineOptions(projectPart->toolchainDefines); - result += CppTools::CompilerOptionsBuilder::createDefineOptions(projectPart->projectDefines); - result += CppTools::CompilerOptionsBuilder::createHeaderPathOptions(projectPart->headerPaths); - result += QLatin1String("-fPIC"); // TODO: Remove? - - return result; + m_progress.setProgressValue(m_initialFilesToProcessSize - m_unitsToProcess.size()); } } // namespace Internal diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h index 661594bf11..cc56f107f6 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.h @@ -36,21 +36,18 @@ class ClangStaticAnalyzerRunControl : public Analyzer::AnalyzerRunControl Q_OBJECT public: - struct SourceFileConfiguration { - SourceFileConfiguration(const CppTools::ProjectFile &projectFile, - const CppTools::ProjectPart::Ptr &projectPart) - : file(projectFile) - , projectPart(projectPart) {} + struct AnalyzeUnit { + AnalyzeUnit(const QString &file, const QStringList &options) + : file(file), arguments(options) {} - QStringList createClangOptions() const; - - CppTools::ProjectFile file; - CppTools::ProjectPart::Ptr projectPart; + QString file; + QStringList arguments; // without file itself and "-o somePath" }; public: explicit ClangStaticAnalyzerRunControl(const Analyzer::AnalyzerStartParameters &startParams, - ProjectExplorer::RunConfiguration *runConfiguration); + ProjectExplorer::RunConfiguration *runConfiguration, + const CppTools::ProjectInfo &projectInfo); bool startEngine(); void stopEngine(); @@ -70,12 +67,16 @@ private: void updateProgressValue(); private: + const CppTools::ProjectInfo m_projectInfo; + QString m_clangExecutable; QString m_clangLogFileDir; QFutureInterface<void> m_progress; - QList<SourceFileConfiguration> m_filesToProcess; + QList<AnalyzeUnit> m_unitsToProcess; QSet<ClangStaticAnalyzerRunner *> m_runners; int m_initialFilesToProcessSize; + int m_filesAnalyzed; + int m_filesNotAnalyzed; }; } // namespace Internal diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.cpp index a8896b4565..94f7f27163 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.cpp @@ -22,21 +22,31 @@ #include <analyzerbase/analyzerruncontrol.h> #include <analyzerbase/analyzerstartparameters.h> +#include <cpptools/cppmodelmanager.h> +#include <cpptools/cppprojects.h> + #include <projectexplorer/gcctoolchain.h> #include <projectexplorer/kit.h> #include <projectexplorer/kitinformation.h> +#include <projectexplorer/session.h> #include <projectexplorer/target.h> #include <projectexplorer/toolchain.h> +#include <utils/qtcassert.h> + using namespace Analyzer; using namespace ProjectExplorer; namespace ClangStaticAnalyzer { namespace Internal { -ClangStaticAnalyzerRunControlFactory::ClangStaticAnalyzerRunControlFactory(QObject *parent) +ClangStaticAnalyzerRunControlFactory::ClangStaticAnalyzerRunControlFactory( + ClangStaticAnalyzerTool *tool, + QObject *parent) : IRunControlFactory(parent) + , m_tool(tool) { + QTC_CHECK(m_tool); } bool ClangStaticAnalyzerRunControlFactory::canRun(RunConfiguration *runConfiguration, @@ -58,9 +68,30 @@ RunControl *ClangStaticAnalyzerRunControlFactory::create(RunConfiguration *runCo RunMode runMode, QString *errorMessage) { - Q_UNUSED(errorMessage); Q_UNUSED(runMode); + using namespace CppTools; + const ProjectInfo projectInfoBeforeBuild = m_tool->projectInfoBeforeBuild(); + QTC_ASSERT(projectInfoBeforeBuild.isValid(), return 0); + + Project *project = SessionManager::startupProject(); + QTC_ASSERT(project, return 0); + const ProjectInfo projectInfoAfterBuild = CppModelManager::instance()->projectInfo(project); + + if (projectInfoAfterBuild.configurationOrFilesChanged(projectInfoBeforeBuild)) { + // If it's more than a release/debug build configuration change, e.g. + // a version control checkout, files might be not valid C++ anymore + // or even gone, so better stop here. + + m_tool->resetCursorAndProjectInfoBeforeBuild(); + if (errorMessage) { + *errorMessage = tr( + "The project configuration changed since the start of the Clang Static Analyzer. " + "Please re-run with current configuration."); + } + return 0; + } + AnalyzerStartParameters sp; sp.runMode = runMode; sp.startMode = StartLocal; diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.h b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.h index ec3b2d8c9c..a325691972 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.h +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrolfactory.h @@ -19,6 +19,8 @@ #ifndef CLANGSTATICANALYZERRUNCONTROLFACTORY_H #define CLANGSTATICANALYZERRUNCONTROLFACTORY_H +#include "clangstaticanalyzertool.h" + #include <projectexplorer/runconfiguration.h> namespace ClangStaticAnalyzer { @@ -29,7 +31,8 @@ class ClangStaticAnalyzerRunControlFactory : public ProjectExplorer::IRunControl Q_OBJECT public: - explicit ClangStaticAnalyzerRunControlFactory(QObject *parent = 0); + explicit ClangStaticAnalyzerRunControlFactory(ClangStaticAnalyzerTool *tool, + QObject *parent = 0); bool canRun(ProjectExplorer::RunConfiguration *runConfiguration, ProjectExplorer::RunMode runMode) const; @@ -37,6 +40,9 @@ public: ProjectExplorer::RunControl *create(ProjectExplorer::RunConfiguration *runConfiguration, ProjectExplorer::RunMode runMode, QString *errorMessage); + +private: + ClangStaticAnalyzerTool *m_tool; }; } // namespace Internal diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp index ed8efb93f8..5c469a07a2 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp @@ -20,6 +20,8 @@ #include "clangstaticanalyzerconstants.h" +#include <utils/synchronousprocess.h> + #include <QDebug> #include <QDir> #include <QFileInfo> @@ -83,14 +85,7 @@ ClangStaticAnalyzerRunner::ClangStaticAnalyzerRunner(const QString &clangExecuta ClangStaticAnalyzerRunner::~ClangStaticAnalyzerRunner() { - const QProcess::ProcessState processState = m_process.state(); - if (processState == QProcess::Starting || processState == QProcess::Running) { - m_process.terminate(); - if (!m_process.waitForFinished(500)) { - m_process.kill(); - m_process.waitForFinished(); - } - } + Utils::SynchronousProcess::stopProcess(m_process); } bool ClangStaticAnalyzerRunner::run(const QString &filePath, const QStringList &compilerOptions) @@ -99,6 +94,7 @@ bool ClangStaticAnalyzerRunner::run(const QString &filePath, const QStringList & QTC_CHECK(!compilerOptions.contains(QLatin1String("-o"))); QTC_CHECK(!compilerOptions.contains(filePath)); + m_filePath = filePath; m_processOutput.clear(); m_logFile = createLogFile(filePath); @@ -112,6 +108,11 @@ bool ClangStaticAnalyzerRunner::run(const QString &filePath, const QStringList & return true; } +QString ClangStaticAnalyzerRunner::filePath() const +{ + return m_filePath; +} + void ClangStaticAnalyzerRunner::onProcessStarted() { emit started(); @@ -161,8 +162,8 @@ QString ClangStaticAnalyzerRunner::createLogFile(const QString &filePath) const QString ClangStaticAnalyzerRunner::processCommandlineAndOutput() const { return QObject::tr("Command line: \"%1\"\n" - "Process Error: \"%2\"\n" - "Output:\n\"%3\"") + "Process Error: %2\n" + "Output:\n%3") .arg(m_commandLine, QString::number(m_process.error()), QString::fromLocal8Bit(m_processOutput)); diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.h b/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.h index b535ec1077..461f694e2e 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.h +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.h @@ -45,6 +45,8 @@ public: // (2) -o output-file bool run(const QString &filePath, const QStringList &compilerOptions = QStringList()); + QString filePath() const; + signals: void started(); void finishedWithSuccess(const QString &logFilePath); @@ -62,6 +64,7 @@ private: private: QString m_clangExecutable; QString m_clangLogFileDir; + QString m_filePath; QString m_logFile; QString m_commandLine; QProcess m_process; diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp index 143043d73f..5042267776 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzertool.cpp @@ -24,10 +24,15 @@ #include <analyzerbase/analyzermanager.h> #include <coreplugin/coreconstants.h> +#include <coreplugin/icore.h> +#include <cpptools/cppmodelmanager.h> +#include <projectexplorer/buildconfiguration.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/session.h> +#include <projectexplorer/target.h> +#include <utils/checkablemessagebox.h> #include <utils/fancymainwindow.h> #include <QDockWidget> @@ -129,7 +134,22 @@ AnalyzerRunControl *ClangStaticAnalyzerTool::createRunControl( const AnalyzerStartParameters &sp, ProjectExplorer::RunConfiguration *runConfiguration) { - ClangStaticAnalyzerRunControl *engine = new ClangStaticAnalyzerRunControl(sp, runConfiguration); + QTC_ASSERT(runConfiguration, return 0); + QTC_ASSERT(m_projectInfoBeforeBuild.isValid(), return 0); + + // Some projects provides CompilerCallData once a build is finished, + // so pass on the updated Project Info unless no configuration change + // (defines/includes/files) happened. + Project *project = SessionManager::startupProject(); + QTC_ASSERT(project, return 0); + const CppTools::ProjectInfo projectInfoAfterBuild + = CppTools::CppModelManager::instance()->projectInfo(project); + QTC_ASSERT(!projectInfoAfterBuild.configurationOrFilesChanged(m_projectInfoBeforeBuild), + return 0); + m_projectInfoBeforeBuild = CppTools::ProjectInfo(); + + ClangStaticAnalyzerRunControl *engine + = new ClangStaticAnalyzerRunControl(sp, runConfiguration, projectInfoAfterBuild); connect(engine, &ClangStaticAnalyzerRunControl::starting, this, &ClangStaticAnalyzerTool::onEngineIsStarting); connect(engine, &ClangStaticAnalyzerRunControl::newDiagnosticsAvailable, @@ -139,20 +159,73 @@ AnalyzerRunControl *ClangStaticAnalyzerTool::createRunControl( return engine; } +static bool dontStartAfterHintForDebugMode() +{ + const Project *project = SessionManager::startupProject(); + BuildConfiguration::BuildType buildType = BuildConfiguration::Unknown; + if (project) { + if (const Target *target = project->activeTarget()) { + if (const BuildConfiguration *buildConfig = target->activeBuildConfiguration()) + buildType = buildConfig->buildType(); + } + } + + if (buildType == BuildConfiguration::Release) { + const QString wrongMode = ClangStaticAnalyzerTool::tr("Release"); + const QString toolName = ClangStaticAnalyzerTool::tr("Clang Static Analyzer"); + const QString title = ClangStaticAnalyzerTool::tr("Run %1 in %2 Mode?").arg(toolName) + .arg(wrongMode); + const QString message = ClangStaticAnalyzerTool::tr( + "<html><head/><body>" + "<p>You are trying to run the tool \"%1\" on an application in %2 mode. The tool is " + "designed to be used in Debug mode since enabled assertions can reduce the number of " + "false positives.</p>" + "<p>Do you want to continue and run the tool in %2 mode?</p>" + "</body></html>") + .arg(toolName).arg(wrongMode); + if (Utils::CheckableMessageBox::doNotAskAgainQuestion(Core::ICore::mainWindow(), + title, message, Core::ICore::settings(), + QLatin1String("ClangStaticAnalyzerCorrectModeWarning"), + QDialogButtonBox::Yes|QDialogButtonBox::Cancel, + QDialogButtonBox::Cancel, QDialogButtonBox::Yes) != QDialogButtonBox::Yes) + return true; + } + + return false; +} + void ClangStaticAnalyzerTool::startTool(StartMode mode) { QTC_ASSERT(mode == Analyzer::StartLocal, return); AnalyzerManager::showMode(); - if (Project *pro = SessionManager::startupProject()) - ProjectExplorerPlugin::instance()->runProject(pro, runMode()); + + if (dontStartAfterHintForDebugMode()) + return; + + m_diagnosticModel->clear(); + setBusyCursor(true); + Project *project = SessionManager::startupProject(); + QTC_ASSERT(project, return); + m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo(project); + QTC_ASSERT(m_projectInfoBeforeBuild.isValid(), return); + ProjectExplorerPlugin::instance()->runProject(project, runMode()); +} + +CppTools::ProjectInfo ClangStaticAnalyzerTool::projectInfoBeforeBuild() const +{ + return m_projectInfoBeforeBuild; +} + +void ClangStaticAnalyzerTool::resetCursorAndProjectInfoBeforeBuild() +{ + setBusyCursor(false); + m_projectInfoBeforeBuild = CppTools::ProjectInfo(); } void ClangStaticAnalyzerTool::onEngineIsStarting() { QTC_ASSERT(m_diagnosticModel, return); - m_diagnosticModel->clear(); - setBusyCursor(true); } void ClangStaticAnalyzerTool::onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics) @@ -166,10 +239,12 @@ void ClangStaticAnalyzerTool::onEngineFinished() QTC_ASSERT(m_goBack, return); QTC_ASSERT(m_goNext, return); QTC_ASSERT(m_diagnosticModel, return); + + resetCursorAndProjectInfoBeforeBuild(); + const int issuesFound = m_diagnosticModel->rowCount(); m_goBack->setEnabled(issuesFound > 1); m_goNext->setEnabled(issuesFound > 1); - setBusyCursor(false); AnalyzerManager::showStatusMessage(issuesFound > 0 ? AnalyzerManager::tr("Clang Static Analyzer finished, %n issues were found.", 0, issuesFound) diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzertool.h b/plugins/clangstaticanalyzer/clangstaticanalyzertool.h index 2cb1393a7a..8f3bb1f196 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzertool.h +++ b/plugins/clangstaticanalyzer/clangstaticanalyzertool.h @@ -20,6 +20,7 @@ #define CLANGSTATICANALYZERTOOL_H #include <analyzerbase/ianalyzertool.h> +#include <cpptools/cppprojects.h> namespace Analyzer { class DetailedErrorView; } @@ -36,6 +37,8 @@ class ClangStaticAnalyzerTool : public Analyzer::IAnalyzerTool public: explicit ClangStaticAnalyzerTool(QObject *parent = 0); + CppTools::ProjectInfo projectInfoBeforeBuild() const; + void resetCursorAndProjectInfoBeforeBuild(); private: QWidget *createWidgets(); @@ -50,6 +53,8 @@ private: void setBusyCursor(bool busy); private: + CppTools::ProjectInfo m_projectInfoBeforeBuild; + ClangStaticAnalyzerDiagnosticModel *m_diagnosticModel; Analyzer::DetailedErrorView *m_diagnosticView; |