forked from qt-creator/qt-creator
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user