2025-04-29 07:24:05 +00:00
|
|
|
// Copyright (C) 2025 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
|
|
|
|
#include "qqmljslintervisitor_p.h"
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2025-04-29 07:38:41 +00:00
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
using namespace QQmlJS::AST;
|
|
|
|
|
2025-04-29 07:24:05 +00:00
|
|
|
namespace QQmlJS {
|
|
|
|
/*!
|
|
|
|
\internal
|
|
|
|
\class QQmlJS::LinterVisitor
|
|
|
|
Extends QQmlJSImportVisitor with extra warnings that are required for linting but unrelated to
|
|
|
|
QQmlJSImportVisitor actual task that is constructing QQmlJSScopes. One example of such warnings
|
|
|
|
are purely syntactic checks, or style-checks warnings that don't make sense during compilation.
|
|
|
|
*/
|
|
|
|
|
2025-04-29 07:38:41 +00:00
|
|
|
bool LinterVisitor::visit(StringLiteral *sl)
|
|
|
|
{
|
|
|
|
QQmlJSImportVisitor::visit(sl);
|
|
|
|
const QString s = m_logger->code().mid(sl->literalToken.begin(), sl->literalToken.length);
|
|
|
|
|
|
|
|
if (s.contains(QLatin1Char('\r')) || s.contains(QLatin1Char('\n')) || s.contains(QChar(0x2028u))
|
|
|
|
|| s.contains(QChar(0x2029u))) {
|
|
|
|
QString templateString;
|
|
|
|
|
|
|
|
bool escaped = false;
|
|
|
|
const QChar stringQuote = s[0];
|
|
|
|
for (qsizetype i = 1; i < s.size() - 1; i++) {
|
|
|
|
const QChar c = s[i];
|
|
|
|
|
|
|
|
if (c == u'\\') {
|
|
|
|
escaped = !escaped;
|
|
|
|
} else if (escaped) {
|
|
|
|
// If we encounter an escaped quote, unescape it since we use backticks here
|
|
|
|
if (c == stringQuote)
|
|
|
|
templateString.chop(1);
|
|
|
|
|
|
|
|
escaped = false;
|
|
|
|
} else {
|
|
|
|
if (c == u'`')
|
|
|
|
templateString += u'\\';
|
|
|
|
if (c == u'$' && i + 1 < s.size() - 1 && s[i + 1] == u'{')
|
|
|
|
templateString += u'\\';
|
|
|
|
}
|
|
|
|
|
|
|
|
templateString += c;
|
|
|
|
}
|
|
|
|
|
|
|
|
QQmlJSFixSuggestion suggestion = { "Use a template literal instead."_L1, sl->literalToken,
|
|
|
|
u"`" % templateString % u"`" };
|
|
|
|
suggestion.setAutoApplicable();
|
|
|
|
m_logger->log(QStringLiteral("String contains unescaped line terminator which is "
|
|
|
|
"deprecated."),
|
|
|
|
qmlMultilineStrings, sl->literalToken, true, true, suggestion);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2025-04-25 10:24:11 +00:00
|
|
|
bool LinterVisitor::preVisit(Node *n)
|
|
|
|
{
|
|
|
|
m_ancestryIncludingCurrentNode.push_back(n);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LinterVisitor::postVisit(Node *n)
|
|
|
|
{
|
|
|
|
Q_ASSERT(m_ancestryIncludingCurrentNode.back() == n);
|
|
|
|
m_ancestryIncludingCurrentNode.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
Node *LinterVisitor::astParentOfVisitedNode() const
|
|
|
|
{
|
|
|
|
if (m_ancestryIncludingCurrentNode.size() < 2)
|
|
|
|
return nullptr;
|
|
|
|
return m_ancestryIncludingCurrentNode[m_ancestryIncludingCurrentNode.size() - 2];
|
|
|
|
}
|
|
|
|
|
2025-04-25 10:24:11 +00:00
|
|
|
bool LinterVisitor::visit(CommaExpression *expression)
|
|
|
|
{
|
|
|
|
QQmlJSImportVisitor::visit(expression);
|
|
|
|
if (!expression->left || !expression->right)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// don't warn about commas in "for" statements
|
|
|
|
if (cast<ForStatement *>(astParentOfVisitedNode()))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
m_logger->log("Do not use comma expressions."_L1, qmlComma, expression->commaToken);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2025-04-25 11:20:52 +00:00
|
|
|
static void warnAboutLiteralConstructors(NewMemberExpression *expression, QQmlJSLogger *logger)
|
|
|
|
{
|
|
|
|
static constexpr std::array literals{ "Boolean"_L1, "Function"_L1, "JSON"_L1,
|
|
|
|
"Math"_L1, "Number"_L1, "String"_L1 };
|
|
|
|
|
|
|
|
const IdentifierExpression *identifier = cast<IdentifierExpression *>(expression->base);
|
|
|
|
if (!identifier)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (std::find(literals.cbegin(), literals.cend(), identifier->name) != literals.cend()) {
|
|
|
|
logger->log("Do not use '%1' as a constructor."_L1.arg(identifier->name),
|
|
|
|
qmlLiteralConstructor, identifier->identifierToken);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LinterVisitor::visit(NewMemberExpression *expression)
|
|
|
|
{
|
|
|
|
QQmlJSImportVisitor::visit(expression);
|
|
|
|
warnAboutLiteralConstructors(expression, m_logger);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2025-04-29 07:24:05 +00:00
|
|
|
} // namespace QQmlJS
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|