Qt 6.x
The Qt SDK
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 <private/qqmljslogger_p.h>
5#include <private/qqmlsa_p.h>
6
7#include <QtQmlCompiler/qqmljsloggingutils.h>
8
9#include <QtCore/qglobal.h>
10#include <QtCore/qfile.h>
11#include <QtCore/qfileinfo.h>
12
13// GCC 11 thinks diagMsg.fixSuggestion.fixes.d.ptr is somehow uninitialized in
14// QList::emplaceBack(), probably called from QQmlJsLogger::log()
15// Ditto for GCC 12, but it emits a different warning
17QT_WARNING_DISABLE_GCC("-Wuninitialized")
18QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
19#include <QtCore/qlist.h>
21
23
24using namespace Qt::StringLiterals;
25
28const QQmlJS::LoggerWarningId qmlUnresolvedAlias{ "unresolved-alias" };
30const QQmlJS::LoggerWarningId qmlRecursionDepthErrors{ "recursion-depth-errors" };
32const QQmlJS::LoggerWarningId qmlInheritanceCycle{ "inheritance-cycle" };
34const QQmlJS::LoggerWarningId qmlSignalParameters{ "signal-handler-parameters" };
36const QQmlJS::LoggerWarningId qmlUnresolvedType{ "unresolved-type" };
37const QQmlJS::LoggerWarningId qmlRestrictedType{ "restricted-type" };
38const QQmlJS::LoggerWarningId qmlPrefixedImportType{ "prefixed-import-type" };
39const QQmlJS::LoggerWarningId qmlIncompatibleType{ "incompatible-type" };
40const QQmlJS::LoggerWarningId qmlMissingProperty{ "missing-property" };
41const QQmlJS::LoggerWarningId qmlNonListProperty{ "non-list-property" };
42const QQmlJS::LoggerWarningId qmlReadOnlyProperty{ "read-only-property" };
43const QQmlJS::LoggerWarningId qmlDuplicatePropertyBinding{ "duplicate-property-binding" };
44const QQmlJS::LoggerWarningId qmlDuplicatedName{ "duplicated-name" };
45const QQmlJS::LoggerWarningId qmlDeferredPropertyId{ "deferred-property-id" };
47const QQmlJS::LoggerWarningId qmlUnusedImports{ "unused-imports" };
48const QQmlJS::LoggerWarningId qmlMultilineStrings{ "multiline-strings" };
50const QQmlJS::LoggerWarningId qmlSyntaxIdQuotation{ "syntax.id-quotation" };
51const QQmlJS::LoggerWarningId qmlSyntaxDuplicateIds{ "syntax.duplicate-ids" };
53const QQmlJS::LoggerWarningId qmlAttachedPropertyReuse{ "attached-property-reuse" };
55const QQmlJS::LoggerWarningId qmlVarUsedBeforeDeclaration{ "var-used-before-declaration" };
56const QQmlJS::LoggerWarningId qmlInvalidLintDirective{ "invalid-lint-directive" };
57const QQmlJS::LoggerWarningId qmlUseProperFunction{ "use-proper-function" };
58const QQmlJS::LoggerWarningId qmlAccessSingleton{ "access-singleton-via-object" };
59const QQmlJS::LoggerWarningId qmlTopLevelComponent{ "top-level-component" };
60const QQmlJS::LoggerWarningId qmlUncreatableType{ "uncreatable-type" };
61const QQmlJS::LoggerWarningId qmlMissingEnumEntry{ "missing-enum-entry" };
62
64{
66
67 for (const QQmlJS::LoggerCategory &category : cats)
69
70 // setup color output
75}
76
78{
79 static const QList<QQmlJS::LoggerCategory> cats = {
81 QStringLiteral("Warn about required properties"), QtWarningMsg },
83 QStringLiteral("PropertyAliasCycles"),
84 QStringLiteral("Warn about alias cycles"), QtWarningMsg },
86 QStringLiteral("PropertyAliasCycles"),
87 QStringLiteral("Warn about unresolved aliases"), QtWarningMsg },
89 qmlImport.name().toString(), QStringLiteral("ImportFailure"),
90 QStringLiteral("Warn about failing imports and deprecated qmltypes"),
94 QStringLiteral("Warn about failing imports and deprecated qmltypes"), QtWarningMsg,
95 false, true },
97 QStringLiteral("Warn about with statements as they can cause false "
98 "positives when checking for unqualified access"),
101 QStringLiteral("InheritanceCycle"),
102 QStringLiteral("Warn about inheritance cycles"), QtWarningMsg },
104 QStringLiteral("Warn about deprecated properties and types"),
105 QtWarningMsg },
107 qmlSignalParameters.name().toString(), QStringLiteral("BadSignalHandlerParameters"),
108 QStringLiteral("Warn about bad signal handler parameters"), QtWarningMsg },
110 QStringLiteral("Warn about missing types"), QtWarningMsg },
112 QStringLiteral("UnresolvedType"),
113 QStringLiteral("Warn about unresolved types"), QtWarningMsg },
115 QStringLiteral("RestrictedType"),
116 QStringLiteral("Warn about restricted types"), QtWarningMsg },
118 QStringLiteral("PrefixedImportType"),
119 QStringLiteral("Warn about prefixed import types"), QtWarningMsg },
121 QStringLiteral("IncompatibleType"),
122 QStringLiteral("Warn about missing types"), QtWarningMsg },
124 QStringLiteral("MissingProperty"),
125 QStringLiteral("Warn about missing properties"), QtWarningMsg },
127 QStringLiteral("NonListProperty"),
128 QStringLiteral("Warn about non-list properties"), QtWarningMsg },
130 qmlReadOnlyProperty.name().toString(), QStringLiteral("ReadOnlyProperty"),
131 QStringLiteral("Warn about writing to read-only properties"), QtWarningMsg },
133 QStringLiteral("DuplicatePropertyBinding"),
134 QStringLiteral("Warn about duplicate property bindings"),
135 QtWarningMsg },
137 qmlDuplicatedName.name().toString(), QStringLiteral("DuplicatedName"),
138 QStringLiteral("Warn about duplicated property/signal names"), QtWarningMsg },
140 qmlDeferredPropertyId.name().toString(), QStringLiteral("DeferredPropertyId"),
142 "Warn about making deferred properties immediate by giving them an id."),
143 QtInfoMsg, true, true },
145 qmlUnqualified.name().toString(), QStringLiteral("UnqualifiedAccess"),
146 QStringLiteral("Warn about unqualified identifiers and how to fix them"),
147 QtWarningMsg },
149 QStringLiteral("Warn about unused imports"), QtInfoMsg },
151 QStringLiteral("MultilineStrings"),
152 QStringLiteral("Warn about multiline strings"), QtInfoMsg },
154 QStringLiteral("Syntax errors"), QtWarningMsg, false, true },
156 QStringLiteral("ID quotation"), QtWarningMsg, false, true },
158 QStringLiteral("ID duplication"), QtCriticalMsg, false, true },
160 QStringLiteral("Warn about compiler issues"), QtWarningMsg, true },
162 qmlAttachedPropertyReuse.name().toString(), QStringLiteral("AttachedPropertyReuse"),
163 QStringLiteral("Warn if attached types from parent components "
164 "aren't reused. This is handled by the QtQuick "
165 "lint plugin. Use Quick.AttachedPropertyReuse instead."),
166 QtCriticalMsg, true },
167 QQmlJS::LoggerCategory{ qmlPlugin.name().toString(), QStringLiteral("LintPluginWarnings"),
168 QStringLiteral("Warn if a qmllint plugin finds an issue"),
169 QtWarningMsg, true },
171 QStringLiteral("VarUsedBeforeDeclaration"),
172 QStringLiteral("Warn if a variable is used before declaration"),
173 QtWarningMsg },
175 qmlInvalidLintDirective.name().toString(), QStringLiteral("InvalidLintDirective"),
176 QStringLiteral("Warn if an invalid qmllint comment is found"), QtWarningMsg },
178 qmlUseProperFunction.name().toString(), QStringLiteral("UseProperFunction"),
179 QStringLiteral("Warn if var is used for storing functions"), QtWarningMsg },
181 qmlAccessSingleton.name().toString(), QStringLiteral("AccessSingletonViaObject"),
182 QStringLiteral("Warn if a singleton is accessed via an object"), QtWarningMsg },
184 qmlTopLevelComponent.name().toString(), QStringLiteral("TopLevelComponent"),
185 QStringLiteral("Fail when a top level Component are encountered"), QtWarningMsg },
187 qmlUncreatableType.name().toString(), QStringLiteral("UncreatableType"),
188 QStringLiteral("Warn if uncreatable types are created"), QtWarningMsg }
189 };
190
191 return cats;
192}
193
195{
196 return m_location == other.m_location && m_fixDescription == other.m_fixDescription
197 && m_replacement == other.m_replacement && m_filename == other.m_filename
198 && m_hint == other.m_hint && m_autoApplicable == other.m_autoApplicable;
199}
200
202{
203 return !(*this == other);
204}
205
207{
208 return m_categories.values();
209}
210
212{
213 if (m_categories.contains(category.name())) {
214 qWarning() << "Trying to re-register existing logger category" << category.name();
215 return;
216 }
217
218 m_categoryLevels[category.name()] = category.level();
219 m_categoryIgnored[category.name()] = category.isIgnored();
220 m_categories.insert(category.name(), category);
221}
222
224{
225 static QHash<QtMsgType, int> level = { { QtDebugMsg, 0 },
226 { QtInfoMsg, 1 },
227 { QtWarningMsg, 2 },
228 { QtCriticalMsg, 3 },
229 { QtFatalMsg, 4 } };
230 return level[a] < level[b];
231}
232
234 const QQmlJS::SourceLocation &srcLocation, QtMsgType type, bool showContext,
235 bool showFileName, const std::optional<QQmlJSFixSuggestion> &suggestion,
236 const QString overrideFileName)
237{
238 Q_ASSERT(m_categoryLevels.contains(id.name().toString()));
239
240 if (isCategoryIgnored(id))
241 return;
242
243 // Note: assume \a type is the type we should prefer for logging
244
245 if (srcLocation.isValid()
246 && m_ignoredWarnings[srcLocation.startLine].contains(id.name().toString()))
247 return;
248
249 QString prefix;
250
251 if ((!overrideFileName.isEmpty() || !m_fileName.isEmpty()) && showFileName)
252 prefix =
253 (!overrideFileName.isEmpty() ? overrideFileName : m_fileName) + QStringLiteral(":");
254
255 if (srcLocation.isValid())
256 prefix += QStringLiteral("%1:%2:").arg(srcLocation.startLine).arg(srcLocation.startColumn);
257
258 if (!prefix.isEmpty())
259 prefix.append(QLatin1Char(' '));
260
261 // Note: we do the clamping to [Info, Critical] range since our logger only
262 // supports 3 categories
264
265 // Note: since we clamped our \a type, the output message is not printed
266 // exactly like it was requested, bear with us
267 m_output.writePrefixedMessage(u"%1%2 [%3]"_s.arg(prefix, message, id.name().toString()), type);
268
269 Message diagMsg;
270 diagMsg.message = message;
271 diagMsg.id = id.name();
272 diagMsg.loc = srcLocation;
273 diagMsg.type = type;
274 diagMsg.fixSuggestion = suggestion;
275
276 switch (type) {
277 case QtWarningMsg: m_warnings.push_back(diagMsg); break;
278 case QtCriticalMsg: m_errors.push_back(diagMsg); break;
279 case QtInfoMsg: m_infos.push_back(diagMsg); break;
280 default: break;
281 }
282
283 if (srcLocation.length > 0 && !m_code.isEmpty() && showContext)
284 printContext(overrideFileName, srcLocation);
285
286 if (suggestion.has_value())
287 printFix(suggestion.value());
288}
289
292{
293 if (messages.isEmpty() || isCategoryIgnored(id))
294 return;
295
296 m_output.write(QStringLiteral("---\n"));
297
298 // TODO: we should instead respect message's category here (potentially, it
299 // should hold a category instead of type)
300 for (const QQmlJS::DiagnosticMessage &message : messages)
301 log(message.message, id, QQmlJS::SourceLocation(), false, false);
302
303 m_output.write(QStringLiteral("---\n\n"));
304}
305
306void QQmlJSLogger::printContext(const QString &overrideFileName,
308{
309 QString code = m_code;
310
311 if (!overrideFileName.isEmpty() && overrideFileName != QFileInfo(m_fileName).absolutePath()) {
312 QFile file(overrideFileName);
313 const bool success = file.open(QFile::ReadOnly);
314 Q_ASSERT(success);
316 }
317
318 IssueLocationWithContext issueLocationWithContext { code, location };
319 if (const QStringView beforeText = issueLocationWithContext.beforeText(); !beforeText.isEmpty())
320 m_output.write(beforeText);
321
322 bool locationMultiline = issueLocationWithContext.issueText().contains(QLatin1Char('\n'));
323
324 if (!issueLocationWithContext.issueText().isEmpty())
325 m_output.write(issueLocationWithContext.issueText().toString(), QtCriticalMsg);
326 m_output.write(issueLocationWithContext.afterText().toString() + QLatin1Char('\n'));
327
328 // Do not draw location indicator for multiline locations
329 if (locationMultiline)
330 return;
331
332 int tabCount = issueLocationWithContext.beforeText().count(QLatin1Char('\t'));
333 int locationLength = location.length == 0 ? 1 : location.length;
334 m_output.write(QString::fromLatin1(" ").repeated(issueLocationWithContext.beforeText().size()
335 - tabCount)
336 + QString::fromLatin1("\t").repeated(tabCount)
337 + QString::fromLatin1("^").repeated(locationLength) + QLatin1Char('\n'));
338}
339
340void QQmlJSLogger::printFix(const QQmlJSFixSuggestion &fixItem)
341{
342 const QString currentFileAbsPath = QFileInfo(m_fileName).absolutePath();
343 QString code = m_code;
344 QString currentFile;
345 m_output.writePrefixedMessage(fixItem.fixDescription(), QtInfoMsg);
346
347 if (!fixItem.location().isValid())
348 return;
349
350 const QString filename = fixItem.filename();
351 if (filename == currentFile) {
352 // Nothing to do in this case, we've already read the code
353 } else if (filename.isEmpty() || filename == currentFileAbsPath) {
354 code = m_code;
355 } else {
356 QFile file(filename);
357 const bool success = file.open(QFile::ReadOnly);
358 Q_ASSERT(success);
360 currentFile = filename;
361 }
362
363 IssueLocationWithContext issueLocationWithContext { code, fixItem.location() };
364
365 if (const QStringView beforeText = issueLocationWithContext.beforeText();
366 !beforeText.isEmpty()) {
367 m_output.write(beforeText);
368 }
369
370 // The replacement string can be empty if we're only pointing something out to the user
371 const QString replacement = fixItem.replacement();
372 QStringView replacementString = replacement.isEmpty()
373 ? issueLocationWithContext.issueText()
374 : replacement;
375
376 // But if there's nothing to change it cannot be auto-applied
377 Q_ASSERT(!replacement.isEmpty() || !fixItem.isAutoApplicable());
378
379 m_output.write(replacementString, QtDebugMsg);
380 m_output.write(issueLocationWithContext.afterText().toString() + u'\n');
381
382 int tabCount = issueLocationWithContext.beforeText().count(u'\t');
383
384 // Do not draw location indicator for multiline replacement strings
385 if (replacementString.contains(u'\n'))
386 return;
387
388 m_output.write(u" "_s.repeated(
389 issueLocationWithContext.beforeText().size() - tabCount)
390 + u"\t"_s.repeated(tabCount)
391 + u"^"_s.repeated(replacement.size()) + u'\n');
392}
393
396 const QString &replacement)
397 : m_location{ location }, m_fixDescription{ fixDescription }, m_replacement{ replacement }
398{
399}
400
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1071
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)
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QString absolutePath() const
Returns a file's path absolute path.
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
\inmodule QtCore
Definition qhash.h:818
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:991
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
void push_back(parameter_type t)
Definition qlist.h:672
iterator insert(const Key &key, const T &value)
Definition qmap.h:687
bool contains(const Key &key) const
Definition qmap.h:340
QList< T > values() const
Definition qmap.h:396
QString filename() const
bool operator!=(const QQmlJSFixSuggestion &) const
bool operator==(const QQmlJSFixSuggestion &) const
QQmlJSFixSuggestion()=default
QQmlJS::SourceLocation location() const
bool isAutoApplicable() const
QString replacement() const
QString fixDescription() const
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)
const QAnyStringView name() const
\inmodule QtCore
Definition qstringview.h:76
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString repeated(qsizetype times) const
Definition qstring.cpp:8112
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QString & append(QChar c)
Definition qstring.cpp:3227
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:162
GLint location
GLboolean GLboolean GLboolean b
GLenum GLuint GLint level
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum type
GLuint GLsizei const GLchar * message
const QQmlJS::LoggerWarningId qmlUnqualified
const QQmlJS::LoggerWarningId qmlNonListProperty
const QQmlJS::LoggerWarningId qmlUseProperFunction
const QQmlJS::LoggerWarningId qmlPlugin
const QQmlJS::LoggerWarningId qmlImport
const QQmlJS::LoggerWarningId qmlSignalParameters
const QQmlJS::LoggerWarningId qmlDuplicatedName
const QQmlJS::LoggerWarningId qmlTopLevelComponent
const QQmlJS::LoggerWarningId qmlRestrictedType
const QQmlJS::LoggerWarningId qmlUnusedImports
const QQmlJS::LoggerWarningId qmlPrefixedImportType
const QQmlJS::LoggerWarningId qmlSyntaxDuplicateIds
const QQmlJS::LoggerWarningId qmlWith
const QQmlJS::LoggerWarningId qmlCompiler
const QQmlJS::LoggerWarningId qmlMissingEnumEntry
const QQmlJS::LoggerWarningId qmlUnresolvedType
const QQmlJS::LoggerWarningId qmlRequired
const QQmlJS::LoggerWarningId qmlDuplicatePropertyBinding
const QQmlJS::LoggerWarningId qmlReadOnlyProperty
const QQmlJS::LoggerWarningId qmlMissingProperty
const QQmlJS::LoggerWarningId qmlUncreatableType
const QQmlJS::LoggerWarningId qmlRecursionDepthErrors
static bool isMsgTypeLess(QtMsgType a, QtMsgType b)
const QQmlJS::LoggerWarningId qmlDeprecated
const QQmlJS::LoggerWarningId qmlAttachedPropertyReuse
const QQmlJS::LoggerWarningId qmlVarUsedBeforeDeclaration
const QQmlJS::LoggerWarningId qmlInvalidLintDirective
const QQmlJS::LoggerWarningId qmlSyntaxIdQuotation
const QQmlJS::LoggerWarningId qmlMissingType
const QQmlJS::LoggerWarningId qmlDeferredPropertyId
const QQmlJS::LoggerWarningId qmlAliasCycle
const QQmlJS::LoggerWarningId qmlUnresolvedAlias
const QQmlJS::LoggerWarningId qmlMultilineStrings
const QQmlJS::LoggerWarningId qmlSyntax
const QQmlJS::LoggerWarningId qmlAccessSingleton
const QQmlJS::LoggerWarningId qmlInheritanceCycle
const QQmlJS::LoggerWarningId qmlIncompatibleType
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]
std::optional< QQmlJSFixSuggestion > fixSuggestion
QAnyStringView id
\inmodule QtCore \reentrant
Definition qchar.h:17