forked from qt-creator/qt-creator
Import Clang Static Analyzer plugin
This plugin adds "Clang Static Analyzer" to the Analyze mode, which processes all implementation/source project files of the current project. For this, it will call the clang executable for each file. The found diagnostics will be displayed in a view similar to the one used in "Valgrind Memory Analyzer". The user can specify the clang executable to use and the number of concurrent processes to launch in Menu: Tools > Options > Analyzer > Clang Static Analyzer. Main TODOs: * Fiddle around the appropriate command line options, currently only defines and include paths are passed on. * Tests on Windows / OS X. * Remove dependency to clangcodemodel by moving the functions that create command line arguments to CppTools. Mostly they are not even specific to clang (but would also work with gcc). * Maybe limit to a range of tested clang versions. * How to deal with directory containing all the log files after the user starts a new run or Creator is shut down? (delete it? leave it there? make it configurable?). * Find out how to properly integrate the tests. Imaginable future additions: * Adding a button to load result/log files from a directory, e.g. if the user used the 'scan-build' approach. * Adding a button with a filter menu in order to display only diagnostics from certain categories, similar to "Valgrind Memory Analyzer". Change-Id: I6aeb5dfdbdfa239a06c03dd8759a983df71b77ea Reviewed-by: Eike Ziller <eike.ziller@theqtcompany.com>
This commit is contained in:
committed by
Eike Ziller
parent
b3e07a53db
commit
b9f9eb7ae5
@@ -0,0 +1,167 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc
|
||||
** All rights reserved.
|
||||
** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
|
||||
**
|
||||
** This file is part of the Qt Enterprise LicenseChecker Add-on.
|
||||
**
|
||||
** Licensees holding valid Qt Enterprise licenses may use this file in
|
||||
** accordance with the Qt Enterprise License Agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please use
|
||||
** contact form at http://qt.digia.com <http://qt.digia.com/>
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <clangstaticanalyzer/clangstaticanalyzerconstants.h>
|
||||
#include <clangstaticanalyzer/clangstaticanalyzerrunner.h>
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
using namespace ClangStaticAnalyzer::Internal;
|
||||
|
||||
class ClangStaticAnalyzerRunnerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ClangStaticAnalyzerRunnerTest() {}
|
||||
virtual ~ClangStaticAnalyzerRunnerTest() {}
|
||||
|
||||
private slots:
|
||||
void runWithTestCodeGeneratedOneIssue();
|
||||
void runWithNonExistentFileToAnalyze();
|
||||
};
|
||||
|
||||
static bool writeFile(const QString &filePath, const QByteArray &source)
|
||||
{
|
||||
Utils::FileSaver saver(filePath);
|
||||
return saver.write(source) && saver.finalize();
|
||||
}
|
||||
|
||||
class ClangStaticAnalyzerRunnerSignalTester
|
||||
{
|
||||
public:
|
||||
ClangStaticAnalyzerRunnerSignalTester(ClangStaticAnalyzerRunner *runner);
|
||||
|
||||
bool expectStartedSignal();
|
||||
bool expectFinishWithSuccessSignal();
|
||||
bool expectFinishWithFailureSignal(const QString &expectedErrorMessage = QString());
|
||||
|
||||
private:
|
||||
QSignalSpy m_spyStarted;
|
||||
QSignalSpy m_spyFinishedWithFailure;
|
||||
QSignalSpy m_spyFinishedWithSuccess;
|
||||
};
|
||||
|
||||
ClangStaticAnalyzerRunnerSignalTester::ClangStaticAnalyzerRunnerSignalTester(
|
||||
ClangStaticAnalyzerRunner *runner)
|
||||
: m_spyStarted(runner, SIGNAL(started()))
|
||||
, m_spyFinishedWithFailure(runner, SIGNAL(finishedWithFailure(QString,QString)))
|
||||
, m_spyFinishedWithSuccess(runner, SIGNAL(finishedWithSuccess(QString)))
|
||||
{
|
||||
}
|
||||
|
||||
bool ClangStaticAnalyzerRunnerSignalTester::expectStartedSignal()
|
||||
{
|
||||
if (m_spyStarted.wait()) {
|
||||
if (m_spyStarted.size() != 1) {
|
||||
qDebug() << "started() emitted more than once.";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "started() not emitted.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClangStaticAnalyzerRunnerSignalTester::expectFinishWithSuccessSignal()
|
||||
{
|
||||
if (m_spyFinishedWithSuccess.wait()) {
|
||||
if (m_spyFinishedWithSuccess.size() != 1) {
|
||||
qDebug() << "finishedWithSuccess() emitted more than once.";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "finishedWithSuccess() not emitted.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_spyFinishedWithFailure.size() != 0) {
|
||||
qDebug() << "finishedWithFailure() emitted.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClangStaticAnalyzerRunnerSignalTester::expectFinishWithFailureSignal(
|
||||
const QString &expectedErrorMessage)
|
||||
{
|
||||
if (m_spyFinishedWithFailure.wait()) {
|
||||
if (m_spyFinishedWithFailure.size() == 1) {
|
||||
const QList<QVariant> args = m_spyFinishedWithFailure.at(0);
|
||||
const QString errorMessage = args.at(0).toString();
|
||||
if (errorMessage == expectedErrorMessage) {
|
||||
if (m_spyFinishedWithSuccess.size() != 0) {
|
||||
qDebug() << "Got expected error, but finishedWithSuccess() was also emitted.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "Actual error message:" << errorMessage;
|
||||
qDebug() << "Expected error message:" << expectedErrorMessage;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Finished with more than one failure (expected only one).";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Not finished with failure, but expected.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ClangStaticAnalyzerRunnerTest::runWithTestCodeGeneratedOneIssue()
|
||||
{
|
||||
const QString testFilePath = QDir::tempPath() + QLatin1String("/testcode.cpp");
|
||||
const QByteArray source =
|
||||
"void f(int *p) {}\n"
|
||||
"void f2(int *p) {\n"
|
||||
" delete p;\n"
|
||||
" f(p); // warn: use after free\n"
|
||||
"}\n";
|
||||
QVERIFY(writeFile(testFilePath, source));
|
||||
|
||||
QTemporaryDir temporaryDir(QDir::tempPath() + QLatin1String("/qtc-clangstaticanalyzer-XXXXXX"));
|
||||
QVERIFY(temporaryDir.isValid());
|
||||
ClangStaticAnalyzerRunner runner(QLatin1String("clang"), temporaryDir.path());
|
||||
|
||||
ClangStaticAnalyzerRunnerSignalTester st(&runner);
|
||||
QVERIFY(runner.run(testFilePath));
|
||||
QVERIFY(st.expectStartedSignal());
|
||||
QVERIFY(st.expectFinishWithSuccessSignal());
|
||||
}
|
||||
|
||||
void ClangStaticAnalyzerRunnerTest::runWithNonExistentFileToAnalyze()
|
||||
{
|
||||
QTemporaryDir temporaryDir(QDir::tempPath() + QLatin1String("/qtc-clangstaticanalyzer-XXXXXX"));
|
||||
QVERIFY(temporaryDir.isValid());
|
||||
ClangStaticAnalyzerRunner runner(QLatin1String("clang"), temporaryDir.path());
|
||||
|
||||
ClangStaticAnalyzerRunnerSignalTester st(&runner);
|
||||
QVERIFY(runner.run(QLatin1String("not.existing.file.111")));
|
||||
|
||||
QVERIFY(st.expectStartedSignal());
|
||||
QVERIFY(st.expectFinishWithFailureSignal(finishedWithBadExitCode(1)));
|
||||
}
|
||||
|
||||
QTEST_MAIN(ClangStaticAnalyzerRunnerTest)
|
||||
|
||||
#include "tst_clangstaticanalyzerrunner.moc"
|
||||
Reference in New Issue
Block a user