aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorMaximilian Goldstein <max.goldstein@qt.io>2021-06-01 11:03:12 +0200
committerMaximilian Goldstein <max.goldstein@qt.io>2021-06-01 17:41:45 +0200
commitd2bb3513b434047ee446156858247f7a516e95b0 (patch)
treeeef1ec23910d10bd36f0a8117b5b226cee5a4709 /tools
parentbebc6f5d6460941edb1241c09016629c5b550837 (diff)
qmllint: Add support for outputting warnings as JSON
Adds the ability to output warnings as JSON, one line per file. This makes it way easier to process qmllint warnings for use in CI tooling or IDEs. [ChangeLog][QtQml][qmllint] Add the ability to output warnings as JSON for use in tooling. Change-Id: Iecd48e123859aa939bd9753cb8f6e44d4d7e3667 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'tools')
-rw-r--r--tools/qmllint/main.cpp121
1 files changed, 112 insertions, 9 deletions
diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp
index 99294ca08a..7e3962fe0b 100644
--- a/tools/qmllint/main.cpp
+++ b/tools/qmllint/main.cpp
@@ -42,6 +42,10 @@
#include <QtCore/qfileinfo.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdiriterator.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qscopeguard.h>
#if QT_CONFIG(commandlineparser)
#include <QtCore/qcommandlineparser.h>
@@ -51,14 +55,78 @@
#include <QtCore/qlibraryinfo.h>
#endif
-static bool lint_file(const QString &filename, const bool silent, const QStringList &qmlImportPaths,
- const QStringList &qmltypesFiles, const QString &resourceFile,
+#include <cstdio>
+
+constexpr int JSON_LOGGING_FORMAT_REVISION = 1;
+
+static bool lint_file(const QString &filename, const bool silent, QJsonArray *json,
+ const QStringList &qmlImportPaths, const QStringList &qmltypesFiles,
+ const QString &resourceFile,
const QMap<QString, QQmlJSLogger::Option> &options, QQmlJSImporter &importer)
{
+ QJsonArray warnings;
+ QJsonObject result;
+
+ bool success = true;
+
+ QScopeGuard jsonOutput([&] {
+ if (!json)
+ return;
+
+ result[u"filename"_qs] = QFileInfo(filename).absoluteFilePath();
+ result[u"warnings"] = warnings;
+ result[u"success"] = success;
+
+ json->append(result);
+ });
+
+ auto addJsonWarning = [&](const QQmlJS::DiagnosticMessage &message) {
+ QJsonObject jsonMessage;
+
+ QString type;
+ switch (message.type) {
+ case QtDebugMsg:
+ type = "debug";
+ break;
+ case QtWarningMsg:
+ type = "warning";
+ break;
+ case QtCriticalMsg:
+ type = "critical";
+ break;
+ case QtFatalMsg:
+ type = "fatal";
+ break;
+ case QtInfoMsg:
+ type = "info";
+ break;
+ default:
+ type = "unknown";
+ break;
+ }
+
+ jsonMessage[u"type"_qs] = type;
+
+ if (message.loc.isValid()) {
+ jsonMessage[u"line"_qs] = static_cast<int>(message.loc.startLine);
+ jsonMessage[u"column"_qs] = static_cast<int>(message.loc.startColumn);
+ jsonMessage[u"charOffset"_qs] = static_cast<int>(message.loc.offset);
+ jsonMessage[u"length"_qs] = static_cast<int>(message.loc.length);
+ }
+
+ jsonMessage[u"message"_qs] = message.message;
+
+ warnings << jsonMessage;
+ };
+
QFile file(filename);
if (!file.open(QFile::ReadOnly)) {
- if (!silent)
+ if (json) {
+ result[u"openFailed"] = true;
+ success = false;
+ } else if (!silent) {
qWarning() << "Failed to open file" << filename << file.error();
+ }
return false;
}
@@ -76,14 +144,20 @@ static bool lint_file(const QString &filename, const bool silent, const QStringL
lexer.setCode(code, /*lineno = */ 1, /*qmlMode=*/ !isJavaScript);
QQmlJS::Parser parser(&engine);
- bool success = isJavaScript ? (isESModule ? parser.parseModule() : parser.parseProgram())
- : parser.parse();
+ success = isJavaScript ? (isESModule ? parser.parseModule() : parser.parseProgram())
+ : parser.parse();
if (!success && !silent) {
const auto diagnosticMessages = parser.diagnosticMessages();
for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
- qWarning().noquote() << QString::fromLatin1("%1:%2 : %3")
- .arg(filename).arg(m.loc.startLine).arg(m.message);
+ if (json) {
+ addJsonWarning(m);
+ } else {
+ qWarning().noquote() << QString::fromLatin1("%1:%2 : %3")
+ .arg(filename)
+ .arg(m.loc.startLine)
+ .arg(m.message);
+ }
}
}
@@ -94,7 +168,7 @@ static bool lint_file(const QString &filename, const bool silent, const QStringL
importer.setResourceFileMapper(mapper);
- FindWarningVisitor v { &importer, qmltypesFiles, code, filename, silent };
+ FindWarningVisitor v { &importer, qmltypesFiles, code, filename, silent || json };
for (auto it = options.cbegin(); it != options.cend(); ++it) {
v.logger().setCategoryDisabled(it.value().m_category, it.value().m_disabled);
@@ -103,6 +177,15 @@ static bool lint_file(const QString &filename, const bool silent, const QStringL
parser.rootNode()->accept(&v);
success = v.check();
+
+ if (json) {
+ for (const auto &error : v.logger().errors())
+ addJsonWarning(error);
+ for (const auto &warning : v.logger().warnings())
+ addJsonWarning(warning);
+ for (const auto &info : v.logger().infos())
+ addJsonWarning(info);
+ }
};
if (resourceFile.isEmpty()) {
@@ -141,6 +224,10 @@ All warnings can be set to three levels:
QLatin1String("Don't output syntax errors"));
parser.addOption(silentOption);
+ QCommandLineOption jsonOption(QStringList() << "json",
+ QLatin1String("Output linting errors as JSON"));
+ parser.addOption(jsonOption);
+
QCommandLineOption writeDefaultsOption(
QStringList() << "write-defaults",
QLatin1String("Writes defaults settings to .qmllint.ini and exits (Warning: This "
@@ -247,6 +334,7 @@ All warnings can be set to three levels:
}
bool silent = parser.isSet(silentOption);
+ bool useJson = parser.isSet(jsonOption);
// TODO: Remove after Qt 6.2
bool NoWarnUnqualified = parser.isSet(disableCheckUnqualified);
@@ -305,6 +393,7 @@ All warnings can be set to three levels:
#else
bool silent = false;
+ bool useJson = false;
bool warnUnqualified = true;
bool warnWithStatement = true;
bool warnInheritanceCycle = true;
@@ -314,6 +403,8 @@ All warnings can be set to three levels:
bool success = true;
QQmlJSImporter importer(qmlImportPaths, nullptr);
+ QJsonArray jsonFiles;
+
#if QT_CONFIG(commandlineparser)
for (const QString &filename : positionalArguments) {
if (!parser.isSet(ignoreSettings)) {
@@ -324,7 +415,19 @@ All warnings can be set to three levels:
const auto arguments = app.arguments();
for (const QString &filename : arguments) {
#endif
- success &= lint_file(filename, silent, qmlImportPaths, qmltypesFiles, resourceFile, options, importer);
+ success &= lint_file(filename, silent, useJson ? &jsonFiles : nullptr, qmlImportPaths,
+ qmltypesFiles, resourceFile, options, importer);
+ }
+
+ if (useJson) {
+ QJsonObject result;
+
+ result[u"revision"_qs] = JSON_LOGGING_FORMAT_REVISION;
+ result[u"files"_qs] = jsonFiles;
+
+ QTextStream(stdout) << QString::fromUtf8(
+ QJsonDocument(result).toJson(QJsonDocument::Compact));
}
+
return success ? 0 : -1;
}