ClangTools: Add tool that runs clang-tidy and clazy

... over the whole project.
Generate and read serialized files to get diagnostics.

Change-Id: Iafc25fc70443107a040a995efc038aed35102bbf
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
Ivan Donchevskii
2018-01-17 15:08:30 +01:00
parent 4ec4f111cb
commit 219e23332e
24 changed files with 765 additions and 17 deletions

View File

@@ -33,8 +33,13 @@
#include <QRegularExpression>
#include <QXmlStreamReader>
#include <utils/executeondestruction.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <clang-c/Index.h>
namespace ClangTools {
namespace Internal {
@@ -74,6 +79,12 @@ private:
QList<Diagnostic> m_diagnostics;
};
class ClangSerializedDiagnosticsReader
{
public:
QList<Diagnostic> read(const QString &filePath, const QString &logFilePath);
};
static bool checkFilePath(const QString &filePath, QString *errorMessage)
{
QFileInfo fi(filePath);
@@ -133,6 +144,16 @@ QList<Diagnostic> LogFileReader::readPlist(const QString &filePath, QString *err
}
}
QList<Diagnostic> LogFileReader::readSerialized(const QString &filePath, const QString &logFilePath,
QString *errorMessage)
{
if (!checkFilePath(filePath, errorMessage))
return QList<Diagnostic>();
ClangSerializedDiagnosticsReader reader;
return reader.read(filePath, logFilePath);
}
ClangStaticAnalyzerLogFileReader::ClangStaticAnalyzerLogFileReader(const QString &filePath)
: m_filePath(filePath)
{
@@ -390,5 +411,157 @@ int ClangStaticAnalyzerLogFileReader::readInteger(bool *convertedSuccessfully)
return -1;
}
static QString fromCXString(CXString &&cxString)
{
QString result = QString::fromUtf8(clang_getCString(cxString));
clang_disposeString(cxString);
return result;
}
static Debugger::DiagnosticLocation diagLocationFromSourceLocation(CXSourceLocation cxLocation)
{
CXFile file;
unsigned line;
unsigned column;
clang_getSpellingLocation(cxLocation, &file, &line, &column, nullptr);
Debugger::DiagnosticLocation location;
location.filePath = fromCXString(clang_getFileName(file));
location.line = line;
location.column = column;
return location;
}
static QString cxDiagnosticType(const CXDiagnostic cxDiagnostic)
{
const CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(cxDiagnostic);
switch (severity) {
case CXDiagnostic_Note:
return QString("note");
case CXDiagnostic_Warning:
return QString("warning");
case CXDiagnostic_Error:
return QString("error");
case CXDiagnostic_Fatal:
return QString("fatal");
case CXDiagnostic_Ignored:
return QString("ignored");
}
return QString("ignored");
}
static ExplainingStep buildChildDiagnostic(const CXDiagnostic cxDiagnostic)
{
ExplainingStep diagnosticStep;
QString type = cxDiagnosticType(cxDiagnostic);
if (type == QStringLiteral("ignored"))
return diagnosticStep;
const CXSourceLocation cxLocation = clang_getDiagnosticLocation(cxDiagnostic);
diagnosticStep.location = diagLocationFromSourceLocation(cxLocation);
diagnosticStep.message = type + ": " + fromCXString(clang_getDiagnosticSpelling(cxDiagnostic));
return diagnosticStep;
}
static bool isInvalidDiagnosticLocation(const Diagnostic &diagnostic, const ExplainingStep &child,
const QString &nativeFilePath)
{
// When main file is considered included by itself - this diagnostic has invalid location.
// This case usually happens when original diagnostic comes from system header but
// has main file name set in the source location instead (which is incorrect).
return child.message.indexOf(nativeFilePath) >= 0
&& child.message.indexOf("in file included from") >= 0
&& diagnostic.location.filePath == nativeFilePath;
}
static ExplainingStep buildFixIt(const CXDiagnostic cxDiagnostic, unsigned index)
{
ExplainingStep fixItStep;
CXSourceRange cxFixItRange;
fixItStep.message = "fix-it: " + fromCXString(clang_getDiagnosticFixIt(cxDiagnostic, index,
&cxFixItRange));
fixItStep.location = diagLocationFromSourceLocation(clang_getRangeStart(cxFixItRange));
fixItStep.ranges.push_back(fixItStep.location);
fixItStep.ranges.push_back(diagLocationFromSourceLocation(clang_getRangeEnd(cxFixItRange)));
return fixItStep;
}
static Diagnostic buildDiagnostic(const CXDiagnostic cxDiagnostic, const QString &nativeFilePath)
{
Diagnostic diagnostic;
diagnostic.type = cxDiagnosticType(cxDiagnostic);
if (diagnostic.type == QStringLiteral("ignored"))
return diagnostic;
const CXSourceLocation cxLocation = clang_getDiagnosticLocation(cxDiagnostic);
if (clang_Location_isInSystemHeader(cxLocation))
return diagnostic;
diagnostic.location = diagLocationFromSourceLocation(cxLocation);
if (diagnostic.location.filePath != nativeFilePath)
return diagnostic;
CXDiagnosticSet cxChildDiagnostics = clang_getChildDiagnostics(cxDiagnostic);
Utils::ExecuteOnDestruction onBuildExit([&]() {
clang_disposeDiagnosticSet(cxChildDiagnostics);
});
for (unsigned i = 0; i < clang_getNumDiagnosticsInSet(cxChildDiagnostics); ++i) {
CXDiagnostic cxDiagnostic = clang_getDiagnosticInSet(cxChildDiagnostics, i);
Utils::ExecuteOnDestruction cleanUpDiagnostic([&]() {
clang_disposeDiagnostic(cxDiagnostic);
});
const ExplainingStep diagnosticStep = buildChildDiagnostic(cxDiagnostic);
if (diagnosticStep.isValid())
continue;
if (isInvalidDiagnosticLocation(diagnostic, diagnosticStep, nativeFilePath))
return diagnostic;
diagnostic.explainingSteps.push_back(diagnosticStep);
}
for (unsigned i = 0; i < clang_getDiagnosticNumFixIts(cxDiagnostic); ++i)
diagnostic.explainingSteps.push_back(buildFixIt(cxDiagnostic, i));
diagnostic.description = fromCXString(clang_getDiagnosticSpelling(cxDiagnostic));
diagnostic.category = fromCXString(clang_getDiagnosticCategoryText(cxDiagnostic));
return diagnostic;
}
QList<Diagnostic> ClangSerializedDiagnosticsReader::read(const QString &filePath,
const QString &logFilePath)
{
QList<Diagnostic> list;
CXLoadDiag_Error error;
CXString errorString;
CXDiagnosticSet diagnostics = clang_loadDiagnostics(logFilePath.toStdString().c_str(),
&error,
&errorString);
if (error != CXLoadDiag_None || !diagnostics)
return list;
Utils::ExecuteOnDestruction onReadExit([&]() {
clang_disposeDiagnosticSet(diagnostics);
});
const QString nativeFilePath = QDir::toNativeSeparators(filePath);
for (unsigned i = 0; i < clang_getNumDiagnosticsInSet(diagnostics); ++i) {
CXDiagnostic cxDiagnostic = clang_getDiagnosticInSet(diagnostics, i);
Utils::ExecuteOnDestruction cleanUpDiagnostic([&]() {
clang_disposeDiagnostic(cxDiagnostic);
});
const Diagnostic diagnostic = buildDiagnostic(cxDiagnostic, nativeFilePath);
if (!diagnostic.isValid())
continue;
list.push_back(diagnostic);
}
return list;
}
} // namespace Internal
} // namespace ClangTools