ClangStaticAnalyzer: Warn about unsupported version

Change-Id: I4d8471b7d49c8f295d37add5a0c5b8a698e0f9d4
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Nikolai Kosjar
2016-07-12 18:50:47 +02:00
parent 6335fbf842
commit 36e12b75e0
6 changed files with 156 additions and 4 deletions

View File

@@ -47,7 +47,7 @@ ClangStaticAnalyzerConfigWidget::ClangStaticAnalyzerConfigWidget(
chooser->setExpectedKind(Utils::PathChooser::ExistingCommand); chooser->setExpectedKind(Utils::PathChooser::ExistingCommand);
chooser->setHistoryCompleter(QLatin1String("ClangStaticAnalyzer.ClangCommand.History")); chooser->setHistoryCompleter(QLatin1String("ClangStaticAnalyzer.ClangCommand.History"));
chooser->setPromptDialogTitle(tr("Clang Command")); chooser->setPromptDialogTitle(tr("Clang Command"));
const auto validator = [chooser](Utils::FancyLineEdit *edit, QString *errorMessage) { const auto validator = [chooser, this](Utils::FancyLineEdit *edit, QString *errorMessage) {
const QString currentFilePath = chooser->fileName().toString(); const QString currentFilePath = chooser->fileName().toString();
Utils::PathChooser pc; Utils::PathChooser pc;
Utils::PathChooser *helperPathChooser; Utils::PathChooser *helperPathChooser;
@@ -58,8 +58,17 @@ ClangStaticAnalyzerConfigWidget::ClangStaticAnalyzerConfigWidget(
} else { } else {
helperPathChooser = chooser; helperPathChooser = chooser;
} }
return chooser->defaultValidationFunction()(helperPathChooser->lineEdit(), errorMessage)
const bool isExecutableValid =
chooser->defaultValidationFunction()(helperPathChooser->lineEdit(), errorMessage)
&& isClangExecutableUsable(helperPathChooser->fileName().toString(), errorMessage); && isClangExecutableUsable(helperPathChooser->fileName().toString(), errorMessage);
const ClangExecutableVersion detectedVersion = isExecutableValid
? clangExecutableVersion(helperPathChooser->fileName().toString())
: ClangExecutableVersion();
updateDetectedVersionLabel(isExecutableValid, detectedVersion);
return isExecutableValid;
}; };
chooser->setValidationFunction(validator); chooser->setValidationFunction(validator);
bool clangExeIsSet; bool clangExeIsSet;
@@ -90,5 +99,29 @@ ClangStaticAnalyzerConfigWidget::~ClangStaticAnalyzerConfigWidget()
delete m_ui; delete m_ui;
} }
void ClangStaticAnalyzerConfigWidget::updateDetectedVersionLabel(
bool isExecutableValid,
const ClangExecutableVersion &providedVersion)
{
QLabel &label = *m_ui->detectedVersionLabel;
if (isExecutableValid) {
if (providedVersion.isValid()) {
if (providedVersion.isSupportedVersion()) {
label.setText(tr("Version: %1, supported.")
.arg(providedVersion.toString()));
} else {
label.setText(tr("Version: %1, unsupported (supported version is %2).")
.arg(providedVersion.toString())
.arg(ClangExecutableVersion::supportedVersionAsString()));
}
} else {
label.setText(tr("Version: Could not determine version."));
}
} else {
label.setText(tr("Version: Set valid executable first."));
}
}
} // namespace Internal } // namespace Internal
} // namespace ClangStaticAnalyzer } // namespace ClangStaticAnalyzer

View File

@@ -34,6 +34,8 @@ namespace Internal {
namespace Ui { class ClangStaticAnalyzerConfigWidget; } namespace Ui { class ClangStaticAnalyzerConfigWidget; }
class ClangExecutableVersion;
class ClangStaticAnalyzerConfigWidget : public QWidget class ClangStaticAnalyzerConfigWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
@@ -43,6 +45,9 @@ public:
QWidget *parent = 0); QWidget *parent = 0);
~ClangStaticAnalyzerConfigWidget(); ~ClangStaticAnalyzerConfigWidget();
void updateDetectedVersionLabel(bool executableIsValid,
const ClangExecutableVersion &providedVersion);
private: private:
Ui::ClangStaticAnalyzerConfigWidget *m_ui; Ui::ClangStaticAnalyzerConfigWidget *m_ui;
ClangStaticAnalyzerSettings *m_settings; ClangStaticAnalyzerSettings *m_settings;

View File

@@ -34,14 +34,21 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="1" column="0"> <item row="1" column="1">
<widget class="QLabel" name="detectedVersionLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
<string>Simultaneous processes:</string> <string>Simultaneous processes:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
<widget class="QSpinBox" name="simultaneousProccessesSpinBox"> <widget class="QSpinBox" name="simultaneousProccessesSpinBox">

View File

@@ -469,6 +469,28 @@ void ClangStaticAnalyzerRunControl::start()
emit finished(); emit finished();
return; return;
} }
// Check clang version
const ClangExecutableVersion version = clangExecutableVersion(executable);
if (!version.isValid()) {
const QString warningMessage
= tr("Clang Static Analyzer: Running with possibly unsupported version, "
"could not determine version from executable \"%1\".")
.arg(executable);
appendMessage(warningMessage + QLatin1Char('\n'), Utils::StdErrFormat);
TaskHub::addTask(Task::Warning, warningMessage, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::requestPopup();
} else if (!version.isSupportedVersion()) {
const QString warningMessage
= tr("Clang Static Analyzer: Running with unsupported version %1, "
"supported version is %2.")
.arg(version.toString())
.arg(ClangExecutableVersion::supportedVersionAsString());
appendMessage(warningMessage + QLatin1Char('\n'), Utils::StdErrFormat);
TaskHub::addTask(Task::Warning, warningMessage, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::requestPopup();
}
m_clangExecutable = executable; m_clangExecutable = executable;
// Create log dir // Create log dir

View File

@@ -32,9 +32,11 @@
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/synchronousprocess.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QFileInfo> #include <QFileInfo>
#include <QRegularExpression>
static bool isFileExecutable(const QString &executablePath) static bool isFileExecutable(const QString &executablePath)
{ {
@@ -108,5 +110,53 @@ bool isClangExecutableUsable(const QString &filePath, QString *errorMessage)
return true; return true;
} }
ClangExecutableVersion clangExecutableVersion(const QString &executable)
{
const ClangExecutableVersion invalidVersion;
// Sanity checks
const QFileInfo fileInfo(executable);
const bool isExecutableFile = fileInfo.isFile() && fileInfo.isExecutable();
if (!isExecutableFile)
return invalidVersion;
// Get version output
Utils::Environment environment = Utils::Environment::systemEnvironment();
Utils::Environment::setupEnglishOutput(&environment);
Utils::SynchronousProcess runner;
runner.setEnvironment(environment.toStringList());
runner.setTimeoutS(10);
// We would prefer "-dumpversion", but that one returns some old version number.
const QStringList arguments(QLatin1String(("--version")));
const Utils::SynchronousProcessResponse response = runner.runBlocking(executable, arguments);
if (response.result != Utils::SynchronousProcessResponse::Finished)
return invalidVersion;
const QString output = response.stdOut();
// Parse version output
const QRegularExpression re(QLatin1String("clang version (\\d+)\\.(\\d+)\\.(\\d+)"));
const QRegularExpressionMatch reMatch = re.match(output);
if (re.captureCount() != 3)
return invalidVersion;
const QString majorString = reMatch.captured(1);
bool convertedSuccessfully = false;
const int major = majorString.toInt(&convertedSuccessfully);
if (!convertedSuccessfully)
return invalidVersion;
const QString minorString = reMatch.captured(2);
const int minor = minorString.toInt(&convertedSuccessfully);
if (!convertedSuccessfully)
return invalidVersion;
const QString patchString = reMatch.captured(3);
const int patch = patchString.toInt(&convertedSuccessfully);
if (!convertedSuccessfully)
return invalidVersion;
return ClangExecutableVersion(major, minor, patch);
}
} // namespace Internal } // namespace Internal
} // namespace ClangStaticAnalyzer } // namespace ClangStaticAnalyzer

View File

@@ -44,5 +44,40 @@ QString clangExecutableFromSettings(Core::Id toolchainType, bool *isValid);
QString createFullLocationString(const Debugger::DiagnosticLocation &location); QString createFullLocationString(const Debugger::DiagnosticLocation &location);
// TODO: Use QVersionNumber once we can use >= Qt 5.6.0
class ClangExecutableVersion {
public:
ClangExecutableVersion() : majorNumber(-1) , minorNumber(-1) , patchNumber(-1) {}
ClangExecutableVersion(int major, int minor, int patch)
: majorNumber(major) , minorNumber(minor) , patchNumber(patch) {}
bool isValid() const
{
return majorNumber >= 0 && minorNumber >= 0 && patchNumber >= 0;
}
bool isSupportedVersion() const
{
return majorNumber == 3 && minorNumber == 8;
}
static QString supportedVersionAsString()
{
return QLatin1String("3.8");
}
QString toString() const
{
return QString::fromLatin1("%1.%2.%3").arg(majorNumber).arg(minorNumber).arg(patchNumber);
}
public:
int majorNumber;
int minorNumber;
int patchNumber;
};
ClangExecutableVersion clangExecutableVersion(const QString &absolutePath);
} // namespace Internal } // namespace Internal
} // namespace ClangStaticAnalyzer } // namespace ClangStaticAnalyzer