Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmljslogger.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include <QtCore/qcompilerdetection.h>
5// GCC 11 thinks diagMsg.fixSuggestion.fixes.d.ptr is somehow uninitialized in
6// QList::emplaceBack(), probably called from QQmlJsLogger::log()
7// Ditto for GCC 12, but it emits a different warning
9QT_WARNING_DISABLE_GCC("-Wuninitialized")
10QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
11#include <QtCore/qlist.h>
13
14#include <private/qqmljslogger_p.h>
15#include <private/qqmlsa_p.h>
16
17#include <QtQmlCompiler/qqmljsloggingutils.h>
18
19#include <QtCore/qglobal.h>
20#include <QtCore/qfile.h>
21#include <QtCore/qfileinfo.h>
22
23
25
26using namespace Qt::StringLiterals;
27
30const QQmlSA::LoggerWarningId qmlUnresolvedAlias{ "unresolved-alias" };
32const QQmlSA::LoggerWarningId qmlRecursionDepthErrors{ "recursion-depth-errors" };
34const QQmlSA::LoggerWarningId qmlInheritanceCycle{ "inheritance-cycle" };
36const QQmlSA::LoggerWarningId qmlSignalParameters{ "signal-handler-parameters" };
38const QQmlSA::LoggerWarningId qmlUnresolvedType{ "unresolved-type" };
39const QQmlSA::LoggerWarningId qmlRestrictedType{ "restricted-type" };
40const QQmlSA::LoggerWarningId qmlPrefixedImportType{ "prefixed-import-type" };
41const QQmlSA::LoggerWarningId qmlIncompatibleType{ "incompatible-type" };
42const QQmlSA::LoggerWarningId qmlMissingProperty{ "missing-property" };
43const QQmlSA::LoggerWarningId qmlNonListProperty{ "non-list-property" };
44const QQmlSA::LoggerWarningId qmlReadOnlyProperty{ "read-only-property" };
45const QQmlSA::LoggerWarningId qmlDuplicatePropertyBinding{ "duplicate-property-binding" };
46const QQmlSA::LoggerWarningId qmlDuplicatedName{ "duplicated-name" };
47const QQmlSA::LoggerWarningId qmlDeferredPropertyId{ "deferred-property-id" };
49const QQmlSA::LoggerWarningId qmlUnusedImports{ "unused-imports" };
50const QQmlSA::LoggerWarningId qmlMultilineStrings{ "multiline-strings" };
52const QQmlSA::LoggerWarningId qmlSyntaxIdQuotation{ "syntax.id-quotation" };
53const QQmlSA::LoggerWarningId qmlSyntaxDuplicateIds{ "syntax.duplicate-ids" };
55const QQmlSA::LoggerWarningId qmlAttachedPropertyReuse{ "attached-property-reuse" };
57const QQmlSA::LoggerWarningId qmlVarUsedBeforeDeclaration{ "var-used-before-declaration" };
58const QQmlSA::LoggerWarningId qmlInvalidLintDirective{ "invalid-lint-directive" };
59const QQmlSA::LoggerWarningId qmlUseProperFunction{ "use-proper-function" };
60const QQmlSA::LoggerWarningId qmlAccessSingleton{ "access-singleton-via-object" };
61const QQmlSA::LoggerWarningId qmlTopLevelComponent{ "top-level-component" };
62const QQmlSA::LoggerWarningId qmlUncreatableType{ "uncreatable-type" };
63const QQmlSA::LoggerWarningId qmlMissingEnumEntry{ "missing-enum-entry" };
64
66{
67 static const QList<QQmlJS::LoggerCategory> cats = defaultCategories();
68
69 for (const QQmlJS::LoggerCategory &category : cats)
71
72 // setup color output
77}
78
79const QList<QQmlJS::LoggerCategory> &QQmlJSLogger::defaultCategories()
80{
81 static const QList<QQmlJS::LoggerCategory> cats = {
83 QStringLiteral("Warn about required properties"), QtWarningMsg },
85 QStringLiteral("PropertyAliasCycles"),
86 QStringLiteral("Warn about alias cycles"), QtWarningMsg },
88 QStringLiteral("PropertyAliasCycles"),
89 QStringLiteral("Warn about unresolved aliases"), QtWarningMsg },
91 qmlImport.name().toString(), QStringLiteral("ImportFailure"),
92 QStringLiteral("Warn about failing imports and deprecated qmltypes"),
96 QStringLiteral("Warn about failing imports and deprecated qmltypes"), QtWarningMsg,
97 false, true },
99 QStringLiteral("Warn about with statements as they can cause false "
100 "positives when checking for unqualified access"),
101 QtWarningMsg },
103 QStringLiteral("InheritanceCycle"),
104 QStringLiteral("Warn about inheritance cycles"), QtWarningMsg },
106 QStringLiteral("Warn about deprecated properties and types"),
107 QtWarningMsg },
109 qmlSignalParameters.name().toString(), QStringLiteral("BadSignalHandlerParameters"),
110 QStringLiteral("Warn about bad signal handler parameters"), QtWarningMsg },
112 QStringLiteral("Warn about missing types"), QtWarningMsg },
114 QStringLiteral("UnresolvedType"),
115 QStringLiteral("Warn about unresolved types"), QtWarningMsg },
117 QStringLiteral("RestrictedType"),
118 QStringLiteral("Warn about restricted types"), QtWarningMsg },
120 QStringLiteral("PrefixedImportType"),
121 QStringLiteral("Warn about prefixed import types"), QtWarningMsg },
123 QStringLiteral("IncompatibleType"),
124 QStringLiteral("Warn about missing types"), QtWarningMsg },
126 QStringLiteral("MissingProperty"),
127 QStringLiteral("Warn about missing properties"), QtWarningMsg },
129 QStringLiteral("NonListProperty"),
130 QStringLiteral("Warn about non-list properties"), QtWarningMsg },
132 qmlReadOnlyProperty.name().toString(), QStringLiteral("ReadOnlyProperty"),
133 QStringLiteral("Warn about writing to read-only properties"), QtWarningMsg },
135 QStringLiteral("DuplicatePropertyBinding"),
136 QStringLiteral("Warn about duplicate property bindings"),
137 QtWarningMsg },
139 qmlDuplicatedName.name().toString(), QStringLiteral("DuplicatedName"),
140 QStringLiteral("Warn about duplicated property/signal names"), QtWarningMsg },
142 qmlDeferredPropertyId.name().toString(), QStringLiteral("DeferredPropertyId"),
144 "Warn about making deferred properties immediate by giving them an id."),
145 QtInfoMsg, true, true },
147 qmlUnqualified.name().toString(), QStringLiteral("UnqualifiedAccess"),
148 QStringLiteral("Warn about unqualified identifiers and how to fix them"),
149 QtWarningMsg },
151 QStringLiteral("Warn about unused imports"), QtInfoMsg },
153 QStringLiteral("MultilineStrings"),
154 QStringLiteral("Warn about multiline strings"), QtInfoMsg },
156 QStringLiteral("Syntax errors"), QtWarningMsg, false, true },
158 QStringLiteral("ID quotation"), QtWarningMsg, false, true },
160 QStringLiteral("ID duplication"), QtCriticalMsg, false, true },
162 QStringLiteral("Warn about compiler issues"), QtWarningMsg, true },
164 qmlAttachedPropertyReuse.name().toString(), QStringLiteral("AttachedPropertyReuse"),
165 QStringLiteral("Warn if attached types from parent components "
166 "aren't reused. This is handled by the QtQuick "
167 "lint plugin. Use Quick.AttachedPropertyReuse instead."),
168 QtCriticalMsg, true },
169 QQmlJS::LoggerCategory{ qmlPlugin.name().toString(), QStringLiteral("LintPluginWarnings"),
170 QStringLiteral("Warn if a qmllint plugin finds an issue"),
171 QtWarningMsg, true },
173 QStringLiteral("VarUsedBeforeDeclaration"),
174 QStringLiteral("Warn if a variable is used before declaration"),
175 QtWarningMsg },
177 qmlInvalidLintDirective.name().toString(), QStringLiteral("InvalidLintDirective"),
178 QStringLiteral("Warn if an invalid qmllint comment is found"), QtWarningMsg },
180 qmlUseProperFunction.name().toString(), QStringLiteral("UseProperFunction"),
181 QStringLiteral("Warn if var is used for storing functions"), QtWarningMsg },
183 qmlAccessSingleton.name().toString(), QStringLiteral("AccessSingletonViaObject"),
184 QStringLiteral("Warn if a singleton is accessed via an object"), QtWarningMsg },
186 qmlTopLevelComponent.name().toString(), QStringLiteral("TopLevelComponent"),
187 QStringLiteral("Fail when a top level Component are encountered"), QtWarningMsg },
189 qmlUncreatableType.name().toString(), QStringLiteral("UncreatableType"),
190 QStringLiteral("Warn if uncreatable types are created"), QtWarningMsg }
191 };
192
193 return cats;
194}
195
197{
198 return m_location == other.m_location && m_fixDescription == other.m_fixDescription
199 && m_replacement == other.m_replacement && m_filename == other.m_filename
200 && m_hint == other.m_hint && m_autoApplicable == other.m_autoApplicable;
201}
202
204{
205 return !(*this == other);
206}
207
208QList<QQmlJS::LoggerCategory> QQmlJSLogger::categories() const
209{
210 return m_categories.values();
211}
212
214{
215 if (m_categories.contains(category.name())) {
216 qWarning() << "Trying to re-register existing logger category" << category.name();
217 return;
218 }
219
220 m_categoryLevels[category.name()] = category.level();
221 m_categoryIgnored[category.name()] = category.isIgnored();
222 m_categories.insert(category.name(), category);
223}
224
226{
227 static QHash<QtMsgType, int> level = { { QtDebugMsg, 0 },
228 { QtInfoMsg, 1 },
229 { QtWarningMsg, 2 },
230 { QtCriticalMsg, 3 },
231 { QtFatalMsg, 4 } };
232 return level[a] < level[b];
233}
234
236 const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext,
237 bool showFileName, const std::optional<QQmlJSFixSuggestion> &suggestion,
238 const QString overrideFileName)
239{
240 Q_ASSERT(m_categoryLevels.contains(id.name().toString()));
241
242 if (isCategoryIgnored(id))
243 return;
244
245 // Note: assume \a type is the type we should prefer for logging
246
247 if (srcLocation.isValid()
248 && m_ignoredWarnings[srcLocation.startLine].contains(id.name().toString()))
249 return;
250
251 QString prefix;
252
253 if ((!overrideFileName.isEmpty() || !m_fileName.isEmpty()) && showFileName)
254 prefix =
255 (!overrideFileName.isEmpty() ? overrideFileName : m_fileName) + QStringLiteral(":");
256
257 if (srcLocation.isValid())
258 prefix += QStringLiteral("%1:%2:").arg(srcLocation.startLine).arg(srcLocation.startColumn);
259
260 if (!prefix.isEmpty())
261 prefix.append(QLatin1Char(' '));
262
263 // Note: we do the clamping to [Info, Critical] range since our logger only
264 // supports 3 categories
266
267 // Note: since we clamped our \a type, the output message is not printed
268 // exactly like it was requested, bear with us
269 m_output.writePrefixedMessage(u"%1%2 [%3]"_s.arg(prefix, message, id.name().toString()), type);
270
271 Message diagMsg;
272 diagMsg.message = message;
273 diagMsg.id = id.name();
274 diagMsg.loc = srcLocation;
275 diagMsg.type = type;
276 diagMsg.fixSuggestion = suggestion;
277
278 switch (type) {
279 case QtWarningMsg: m_warnings.push_back(diagMsg); break;
280 case QtCriticalMsg: m_errors.push_back(diagMsg); break;
281 case QtInfoMsg: m_infos.push_back(diagMsg); break;
282 default: break;
283 }
284
285 if (srcLocation.length > 0 && !m_code.isEmpty() && showContext)
286 printContext(overrideFileName, srcLocation);
287
288 if (suggestion.has_value())
289 printFix(suggestion.value());
290}
291
292void QQmlJSLogger::processMessages(const QList<QQmlJS::DiagnosticMessage> &messages,
294{
295 if (messages.isEmpty() || isCategoryIgnored(id))
296 return;
297
298 m_output.write(QStringLiteral("---\n"));
299
300 // TODO: we should instead respect message's category here (potentially, it
301 // should hold a category instead of type)
302 for (const QQmlJS::DiagnosticMessage &message : messages)
303 log(message.message, id, QQmlJS::SourceLocation(), false, false);
304
305 m_output.write(QStringLiteral("---\n\n"));
306}
307
308void QQmlJSLogger::printContext(const QString &overrideFileName,
310{
311 QString code = m_code;
312
313 if (!overrideFileName.isEmpty() && overrideFileName != QFileInfo(m_fileName).absolutePath()) {
314 QFile file(overrideFileName);
315 const bool success = file.open(QFile::ReadOnly);
316 Q_ASSERT(success);
318 }
319
320 IssueLocationWithContext issueLocationWithContext { code, location };
321 if (const QStringView beforeText = issueLocationWithContext.beforeText(); !beforeText.isEmpty())
322 m_output.write(beforeText);
323
324 bool locationMultiline = issueLocationWithContext.issueText().contains(QLatin1Char('\n'));
325
326 if (!issueLocationWithContext.issueText().isEmpty())
327 m_output.write(issueLocationWithContext.issueText().toString(), QtCriticalMsg);
328 m_output.write(issueLocationWithContext.afterText().toString() + QLatin1Char('\n'));
329
330 // Do not draw location indicator for multiline locations
331 if (locationMultiline)
332 return;
333
334 int tabCount = issueLocationWithContext.beforeText().count(QLatin1Char('\t'));
335 int locationLength = location.length == 0 ? 1 : location.length;
336 m_output.write(QString::fromLatin1(" ").repeated(issueLocationWithContext.beforeText().size()
337 - tabCount)
338 + QString::fromLatin1("\t").repeated(tabCount)
339 + QString::fromLatin1("^").repeated(locationLength) + QLatin1Char('\n'));
340}
341
342void QQmlJSLogger::printFix(const QQmlJSFixSuggestion &fixItem)
343{
344 const QString currentFileAbsPath = QFileInfo(m_fileName).absolutePath();
345 QString code = m_code;
346 QString currentFile;
347 m_output.writePrefixedMessage(fixItem.fixDescription(), QtInfoMsg);
348
349 if (!fixItem.location().isValid())
350 return;
351
352 const QString filename = fixItem.filename();
353 if (filename == currentFile) {
354 // Nothing to do in this case, we've already read the code
355 } else if (filename.isEmpty() || filename == currentFileAbsPath) {
356 code = m_code;
357 } else {
358 QFile file(filename);
359 const bool success = file.open(QFile::ReadOnly);
360 Q_ASSERT(success);
362 currentFile = filename;
363 }
364
365 IssueLocationWithContext issueLocationWithContext { code, fixItem.location() };
366
367 if (const QStringView beforeText = issueLocationWithContext.beforeText();
368 !beforeText.isEmpty()) {
369 m_output.write(beforeText);
370 }
371
372 // The replacement string can be empty if we're only pointing something out to the user
373 const QString replacement = fixItem.replacement();
374 QStringView replacementString = replacement.isEmpty()
375 ? issueLocationWithContext.issueText()
376 : replacement;
377
378 // But if there's nothing to change it cannot be auto-applied
379 Q_ASSERT(!replacement.isEmpty() || !fixItem.isAutoApplicable());
380
381 m_output.write(replacementString, QtDebugMsg);
382 m_output.write(issueLocationWithContext.afterText().toString() + u'\n');
383
384 int tabCount = issueLocationWithContext.beforeText().count(u'\t');
385
386 // Do not draw location indicator for multiline replacement strings
387 if (replacementString.contains(u'\n'))
388 return;
389
390 m_output.write(u" "_s.repeated(
391 issueLocationWithContext.beforeText().size() - tabCount)
392 + u"\t"_s.repeated(tabCount)
393 + u"^"_s.repeated(replacement.size()) + u'\n');
394}
395
398 const QString &replacement)
399 : m_location{ location }, m_fixDescription{ fixDescription }, m_replacement{ replacement }
400{
401}
402
[custom type definition]
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1218
void write(const QStringView message, int color=-1)
void writePrefixedMessage(const QString &message, QtMsgType type, const QString &prefix=QString())
void insertMapping(int colorID, ColorCode colorCode)
QString absolutePath() const
Returns the absolute path of the file system entry this QFileInfo refers to, excluding the entry's na...
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
void push_back(parameter_type t)
Definition qlist.h:675
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
bool contains(const Key &key) const
Definition qmap.h:341
QList< T > values() const
Definition qmap.h:397
bool operator!=(const QQmlJSFixSuggestion &) const
bool operator==(const QQmlJSFixSuggestion &) const
QQmlJSFixSuggestion()=default
void processMessages(const QList< QQmlJS::DiagnosticMessage > &messages, const QQmlJS::LoggerWarningId id)
QString code() const
bool isCategoryIgnored(QQmlJS::LoggerWarningId id) const
QList< QQmlJS::LoggerCategory > categories() const
static const QList< QQmlJS::LoggerCategory > & defaultCategories()
void log(const QString &message, QQmlJS::LoggerWarningId id, const QQmlJS::SourceLocation &srcLocation, bool showContext=true, bool showFileName=true, const std::optional< QQmlJSFixSuggestion > &suggestion={}, const QString overrideFileName=QString())
void registerCategory(const QQmlJS::LoggerCategory &category)
\inmodule QtQmlCompiler
QAnyStringView name() const
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QString & append(QChar c)
Definition qstring.cpp:3252
const QLoggingCategory & category()
[1]
Combined button and popup list for selecting options.
#define QT_WARNING_POP
#define QT_WARNING_DISABLE_GCC(text)
#define QT_WARNING_PUSH
QtMsgType
Definition qlogging.h:29
@ QtCriticalMsg
Definition qlogging.h:32
@ QtInfoMsg
Definition qlogging.h:34
@ QtWarningMsg
Definition qlogging.h:31
@ QtFatalMsg
Definition qlogging.h:33
@ QtDebugMsg
Definition qlogging.h:30
#define qWarning
Definition qlogging.h:166
GLint location
GLboolean GLboolean GLboolean b
GLenum GLuint GLint level
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum type
GLuint GLsizei const GLchar * message
const QQmlSA::LoggerWarningId qmlPrefixedImportType
const QQmlSA::LoggerWarningId qmlAliasCycle
const QQmlSA::LoggerWarningId qmlRestrictedType
const QQmlSA::LoggerWarningId qmlSignalParameters
const QQmlSA::LoggerWarningId qmlPlugin
const QQmlSA::LoggerWarningId qmlUnresolvedAlias
const QQmlSA::LoggerWarningId qmlDuplicatePropertyBinding
const QQmlSA::LoggerWarningId qmlRecursionDepthErrors
const QQmlSA::LoggerWarningId qmlSyntaxIdQuotation
const QQmlSA::LoggerWarningId qmlDuplicatedName
const QQmlSA::LoggerWarningId qmlReadOnlyProperty
const QQmlSA::LoggerWarningId qmlUncreatableType
const QQmlSA::LoggerWarningId qmlUseProperFunction
const QQmlSA::LoggerWarningId qmlMissingProperty
const QQmlSA::LoggerWarningId qmlMultilineStrings
const QQmlSA::LoggerWarningId qmlSyntaxDuplicateIds
const QQmlSA::LoggerWarningId qmlUnqualified
const QQmlSA::LoggerWarningId qmlMissingEnumEntry
const QQmlSA::LoggerWarningId qmlRequired
const QQmlSA::LoggerWarningId qmlTopLevelComponent
const QQmlSA::LoggerWarningId qmlDeferredPropertyId
const QQmlSA::LoggerWarningId qmlVarUsedBeforeDeclaration
const QQmlSA::LoggerWarningId qmlAccessSingleton
const QQmlSA::LoggerWarningId qmlWith
static bool isMsgTypeLess(QtMsgType a, QtMsgType b)
const QQmlSA::LoggerWarningId qmlInvalidLintDirective
const QQmlSA::LoggerWarningId qmlUnresolvedType
const QQmlSA::LoggerWarningId qmlImport
const QQmlSA::LoggerWarningId qmlMissingType
const QQmlSA::LoggerWarningId qmlInheritanceCycle
const QQmlSA::LoggerWarningId qmlIncompatibleType
const QQmlSA::LoggerWarningId qmlUnusedImports
const QQmlSA::LoggerWarningId qmlDeprecated
const QQmlSA::LoggerWarningId qmlAttachedPropertyReuse
const QQmlSA::LoggerWarningId qmlCompiler
const QQmlSA::LoggerWarningId qmlSyntax
const QQmlSA::LoggerWarningId qmlNonListProperty
static QString absolutePath(const QString &path)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QStringLiteral(str)
QFile file
[0]
QSharedPointer< T > other(t)
[5]
\inmodule QtCore \reentrant
Definition qchar.h:18