diff options
author | Ivan Donchevskii <[email protected]> | 2018-01-17 15:08:30 +0100 |
---|---|---|
committer | Ivan Donchevskii <[email protected]> | 2018-04-26 13:02:19 +0000 |
commit | 219e23332e5e21d3e4e1a9334bf3f5ef1d485b59 (patch) | |
tree | 2bfb49a89116180d3896503273e523b53bb989c7 /src/plugins/clangtools/clangtoolslogfilereader.cpp | |
parent | 4ec4f111cb3ce038a114769300647ee66875d658 (diff) |
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 <[email protected]>
Diffstat (limited to 'src/plugins/clangtools/clangtoolslogfilereader.cpp')
-rw-r--r-- | src/plugins/clangtools/clangtoolslogfilereader.cpp | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/src/plugins/clangtools/clangtoolslogfilereader.cpp b/src/plugins/clangtools/clangtoolslogfilereader.cpp index 22e4b6f1610..23bda8a8d5f 100644 --- a/src/plugins/clangtools/clangtoolslogfilereader.cpp +++ b/src/plugins/clangtools/clangtoolslogfilereader.cpp @@ -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 |