aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/clangtools/clangtoolslogfilereader.cpp
diff options
context:
space:
mode:
authorIvan Donchevskii <[email protected]>2018-01-17 15:08:30 +0100
committerIvan Donchevskii <[email protected]>2018-04-13 12:34:53 +0000
commite9c462391ed3526097ca8121cce5cf8c84df1435 (patch)
tree81e6742572d2cc59a9d07899c68a90e725141102 /src/plugins/clangtools/clangtoolslogfilereader.cpp
parent8936e5103367e300ac2b31ae5d4cf547425dc2a7 (diff)
ClangTools: Split generic part from static analyzer tool
To reuse it for other clang-based tools. Change-Id: I6c0d8e9eee543fa08faf3bf93c9fac33e43c6820 Reviewed-by: Nikolai Kosjar <[email protected]> Reviewed-by: Alessandro Portale <[email protected]>
Diffstat (limited to 'src/plugins/clangtools/clangtoolslogfilereader.cpp')
-rw-r--r--src/plugins/clangtools/clangtoolslogfilereader.cpp394
1 files changed, 394 insertions, 0 deletions
diff --git a/src/plugins/clangtools/clangtoolslogfilereader.cpp b/src/plugins/clangtools/clangtoolslogfilereader.cpp
new file mode 100644
index 00000000000..22e4b6f1610
--- /dev/null
+++ b/src/plugins/clangtools/clangtoolslogfilereader.cpp
@@ -0,0 +1,394 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "clangtoolslogfilereader.h"
+
+#include <QDebug>
+#include <QDir>
+#include <QObject>
+#include <QFile>
+#include <QFileInfo>
+#include <QRegularExpression>
+#include <QXmlStreamReader>
+
+#include <utils/qtcassert.h>
+
+namespace ClangTools {
+namespace Internal {
+
+class ClangStaticAnalyzerLogFileReader
+{
+ Q_DECLARE_TR_FUNCTIONS(ClangTools::Internal::ClangStaticAnalyzerLogFileReader)
+public:
+ ClangStaticAnalyzerLogFileReader(const QString &filePath);
+
+ QXmlStreamReader::Error read();
+
+ // Output
+ QString clangVersion() const;
+ QStringList files() const;
+ QList<Diagnostic> diagnostics() const;
+
+private:
+ void readPlist();
+ void readTopLevelDict();
+ void readDiagnosticsArray();
+ void readDiagnosticsDict();
+ QList<ExplainingStep> readPathArray();
+ ExplainingStep readPathDict();
+ Debugger::DiagnosticLocation readLocationDict(bool elementIsRead = false);
+ QList<Debugger::DiagnosticLocation> readRangesArray();
+
+ QString readString();
+ QStringList readStringArray();
+ int readInteger(bool *convertedSuccessfully);
+
+private:
+ QString m_filePath;
+ QXmlStreamReader m_xml;
+
+ QString m_clangVersion;
+ QStringList m_referencedFiles;
+ QList<Diagnostic> m_diagnostics;
+};
+
+static bool checkFilePath(const QString &filePath, QString *errorMessage)
+{
+ QFileInfo fi(filePath);
+ if (!fi.exists() || !fi.isReadable()) {
+ if (errorMessage) {
+ *errorMessage
+ = QString(QT_TRANSLATE_NOOP("LogFileReader",
+ "File \"%1\" does not exist or is not readable."))
+ .arg(filePath);
+ }
+ return false;
+ }
+ return true;
+}
+
+QList<Diagnostic> LogFileReader::readPlist(const QString &filePath, QString *errorMessage)
+{
+ if (!checkFilePath(filePath, errorMessage))
+ return QList<Diagnostic>();
+
+ // Read
+ ClangStaticAnalyzerLogFileReader reader(filePath);
+ const QXmlStreamReader::Error error = reader.read();
+
+ // Return diagnostics
+ switch (error) {
+ case QXmlStreamReader::NoError:
+ return reader.diagnostics();
+
+ // Handle errors
+ case QXmlStreamReader::UnexpectedElementError:
+ if (errorMessage) {
+ *errorMessage = tr("Could not read file \"%1\": UnexpectedElementError.")
+ .arg(filePath);
+ }
+ Q_FALLTHROUGH();
+ case QXmlStreamReader::CustomError:
+ if (errorMessage) {
+ *errorMessage = tr("Could not read file \"%1\": CustomError.")
+ .arg(filePath);
+ }
+ Q_FALLTHROUGH();
+ case QXmlStreamReader::NotWellFormedError:
+ if (errorMessage) {
+ *errorMessage = tr("Could not read file \"%1\": NotWellFormedError.")
+ .arg(filePath);
+ }
+ Q_FALLTHROUGH();
+ case QXmlStreamReader::PrematureEndOfDocumentError:
+ if (errorMessage) {
+ *errorMessage = tr("Could not read file \"%1\": PrematureEndOfDocumentError.")
+ .arg(filePath);
+ }
+ Q_FALLTHROUGH();
+ default:
+ return QList<Diagnostic>();
+ }
+}
+
+ClangStaticAnalyzerLogFileReader::ClangStaticAnalyzerLogFileReader(const QString &filePath)
+ : m_filePath(filePath)
+{
+}
+
+QXmlStreamReader::Error ClangStaticAnalyzerLogFileReader::read()
+{
+ QTC_ASSERT(!m_filePath.isEmpty(), return QXmlStreamReader::CustomError);
+ QFile file(m_filePath);
+ QTC_ASSERT(file.open(QIODevice::ReadOnly | QIODevice::Text),
+ return QXmlStreamReader::CustomError);
+
+ m_xml.setDevice(&file);
+ readPlist();
+
+ // If file is empty, m_xml.error() == QXmlStreamReader::PrematureEndOfDocumentError
+ return m_xml.error();
+}
+
+QString ClangStaticAnalyzerLogFileReader::clangVersion() const
+{
+ return m_clangVersion;
+}
+
+QStringList ClangStaticAnalyzerLogFileReader::files() const
+{
+ return m_referencedFiles;
+}
+
+QList<Diagnostic> ClangStaticAnalyzerLogFileReader::diagnostics() const
+{
+ return m_diagnostics;
+}
+
+void ClangStaticAnalyzerLogFileReader::readPlist()
+{
+ if (m_xml.readNextStartElement()) {
+ if (m_xml.name() == QLatin1String("plist")) {
+ if (m_xml.attributes().value(QLatin1String("version")) == QLatin1String("1.0"))
+ readTopLevelDict();
+ } else {
+ m_xml.raiseError(tr("File is not a plist version 1.0 file."));
+ }
+ }
+}
+
+void ClangStaticAnalyzerLogFileReader::readTopLevelDict()
+{
+ QTC_ASSERT(m_xml.isStartElement() && m_xml.name() == QLatin1String("plist"), return);
+ QTC_ASSERT(m_xml.readNextStartElement() && m_xml.name() == QLatin1String("dict"), return);
+
+ while (m_xml.readNextStartElement()) {
+ if (m_xml.name() == QLatin1String("key")) {
+ const QString key = m_xml.readElementText();
+ if (key == QLatin1String("clang_version"))
+ m_clangVersion = readString();
+ else if (key == QLatin1String("files"))
+ m_referencedFiles = readStringArray();
+ else if (key == QLatin1String("diagnostics"))
+ readDiagnosticsArray();
+ } else {
+ m_xml.skipCurrentElement();
+ }
+ }
+}
+
+void ClangStaticAnalyzerLogFileReader::readDiagnosticsArray()
+{
+ if (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("array")) {
+ while (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("dict"))
+ readDiagnosticsDict();
+ }
+}
+
+void ClangStaticAnalyzerLogFileReader::readDiagnosticsDict()
+{
+ QTC_ASSERT(m_xml.isStartElement() && m_xml.name() == QLatin1String("dict"), return);
+
+ Diagnostic diagnostic;
+
+ while (m_xml.readNextStartElement()) {
+ if (m_xml.name() == QLatin1String("key")) {
+ const QString key = m_xml.readElementText();
+ if (key == QLatin1String("path"))
+ diagnostic.explainingSteps = readPathArray();
+ else if (key == QLatin1String("description"))
+ diagnostic.description = readString();
+ else if (key == QLatin1String("category"))
+ diagnostic.category = readString();
+ else if (key == QLatin1String("type"))
+ diagnostic.type = readString();
+ else if (key == QLatin1String("issue_context_kind"))
+ diagnostic.issueContextKind = readString();
+ else if (key == QLatin1String("issue_context"))
+ diagnostic.issueContext = readString();
+ else if (key == QLatin1String("location"))
+ diagnostic.location = readLocationDict();
+ } else {
+ m_xml.skipCurrentElement();
+ }
+ }
+
+ m_diagnostics << diagnostic;
+}
+
+QList<ExplainingStep> ClangStaticAnalyzerLogFileReader::readPathArray()
+{
+ QList<ExplainingStep> result;
+
+ if (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("array")) {
+ while (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("dict")) {
+ const ExplainingStep step = readPathDict();
+ if (step.isValid())
+ result << step;
+ }
+ }
+
+ return result;
+}
+
+ExplainingStep ClangStaticAnalyzerLogFileReader::readPathDict()
+{
+ ExplainingStep explainingStep;
+
+ QTC_ASSERT(m_xml.isStartElement() && m_xml.name() == QLatin1String("dict"),
+ return explainingStep);
+
+ // We are interested only in dict entries an kind=event type
+ if (m_xml.readNextStartElement()) {
+ if (m_xml.name() == QLatin1String("key")) {
+ const QString key = m_xml.readElementText();
+ QTC_ASSERT(key == QLatin1String("kind"), return explainingStep);
+ const QString kind = readString();
+ if (kind != QLatin1String("event")) {
+ m_xml.skipCurrentElement();
+ return explainingStep;
+ }
+ }
+ }
+
+ bool depthOk = false;
+
+ while (m_xml.readNextStartElement()) {
+ if (m_xml.name() == QLatin1String("key")) {
+ const QString key = m_xml.readElementText();
+ if (key == QLatin1String("location"))
+ explainingStep.location = readLocationDict();
+ else if (key == QLatin1String("ranges"))
+ explainingStep.ranges = readRangesArray();
+ else if (key == QLatin1String("depth"))
+ explainingStep.depth = readInteger(&depthOk);
+ else if (key == QLatin1String("message"))
+ explainingStep.message = readString();
+ else if (key == QLatin1String("extended_message"))
+ explainingStep.extendedMessage = readString();
+ } else {
+ m_xml.skipCurrentElement();
+ }
+ }
+
+ QTC_CHECK(depthOk);
+ return explainingStep;
+}
+
+Debugger::DiagnosticLocation ClangStaticAnalyzerLogFileReader::readLocationDict(bool elementIsRead)
+{
+ Debugger::DiagnosticLocation location;
+ if (elementIsRead) {
+ QTC_ASSERT(m_xml.isStartElement() && m_xml.name() == QLatin1String("dict"),
+ return location);
+ } else {
+ QTC_ASSERT(m_xml.readNextStartElement() && m_xml.name() == QLatin1String("dict"),
+ return location);
+ }
+
+ int line = 0;
+ int column = 0;
+ int fileIndex = 0;
+ bool lineOk = false, columnOk = false, fileIndexOk = false;
+
+ // Collect values
+ while (m_xml.readNextStartElement()) {
+ if (m_xml.name() == QLatin1String("key")) {
+ const QString keyName = m_xml.readElementText();
+ if (keyName == QLatin1String("line"))
+ line = readInteger(&lineOk);
+ else if (keyName == QLatin1String("col"))
+ column = readInteger(&columnOk);
+ else if (keyName == QLatin1String("file"))
+ fileIndex = readInteger(&fileIndexOk);
+ } else {
+ m_xml.skipCurrentElement();
+ }
+ }
+
+ if (lineOk && columnOk && fileIndexOk) {
+ QTC_ASSERT(fileIndex < m_referencedFiles.size(), return location);
+ location = Debugger::DiagnosticLocation(m_referencedFiles.at(fileIndex), line, column);
+ }
+ return location;
+}
+
+QList<Debugger::DiagnosticLocation> ClangStaticAnalyzerLogFileReader::readRangesArray()
+{
+ QList<Debugger::DiagnosticLocation> result;
+
+ // It's an array of arrays...
+ QTC_ASSERT(m_xml.readNextStartElement() && m_xml.name() == QLatin1String("array"),
+ return result);
+ QTC_ASSERT(m_xml.readNextStartElement() && m_xml.name() == QLatin1String("array"),
+ return result);
+
+ while (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("dict"))
+ result << readLocationDict(true);
+
+ m_xml.skipCurrentElement(); // Laeve outer array
+ return result;
+}
+
+QString ClangStaticAnalyzerLogFileReader::readString()
+{
+ if (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("string"))
+ return m_xml.readElementText();
+
+ m_xml.raiseError(tr("Expected a string element."));
+ return QString();
+}
+
+QStringList ClangStaticAnalyzerLogFileReader::readStringArray()
+{
+ if (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("array")) {
+ QStringList result;
+ while (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("string")) {
+ const QString string = m_xml.readElementText();
+ if (!string.isEmpty())
+ result << string;
+ }
+ return result;
+ }
+
+ m_xml.raiseError(tr("Expected an array element."));
+ return QStringList();
+}
+
+int ClangStaticAnalyzerLogFileReader::readInteger(bool *convertedSuccessfully)
+{
+ if (m_xml.readNextStartElement() && m_xml.name() == QLatin1String("integer")) {
+ const QString contents = m_xml.readElementText();
+ return contents.toInt(convertedSuccessfully);
+ }
+
+ m_xml.raiseError(tr("Expected an integer element."));
+ if (convertedSuccessfully)
+ *convertedSuccessfully = false;
+ return -1;
+}
+
+} // namespace Internal
+} // namespace ClangTools