aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2025-05-22 11:57:34 +0200
committerUlf Hermann <ulf.hermann@qt.io>2025-06-27 11:41:07 +0000
commita4aee687762ffafda800e16f03797f6aebff4fd8 (patch)
tree9a265f2c2d324183d9f8aff6bbd4a8e8cabf678b /src
parentfc0bf5a9fd077da6e5d0943d1edae0140ea23a10 (diff)
qmllint: Catch JS variable declarations in QML elements
JS variable declarations in a QML element are not allowed. The engine rejects them in the IR builder. As the linter doesn't run this part of the compilation pipeline, it would never report the issue. This commit fixes the issue by adding the necessary check. It also avoids an assert in insertJSIdentifier when assertions are enabled. Fixes: QTBUG-137030 Pick-to: 6.10 6.9 6.8 Change-Id: Iff7a6e02bf852cf3a9affcac6ec40b7bd98cda83 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp56
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor_p.h4
2 files changed, 49 insertions, 11 deletions
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 8d552943f4..9ad44bdbc2 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -84,6 +84,36 @@ static bool causesImplicitComponentWrapping(const QQmlJSMetaProperty &property,
/*!
\internal
+ A guarded version of insertJSIdentifier. If the scope is a QML scope,
+ it will log a syntax error instead.
+ Returns true if insertion was successful, otherwise false
+ */
+bool QQmlJSImportVisitor::safeInsertJSIdentifier(QQmlJSScope::Ptr &scope, const QString &name,
+ const QQmlJSScope::JavaScriptIdentifier &identifier)
+{
+ /* The grammar currently allows putting a variable declaration into a UiObjectMember
+ and we only complain about it in the IRBbuilder. It is unclear whether we should change
+ the grammar, as the linter would need to handle invalid programs anyway, so we'd need
+ to add some recovery rule to the grammar in any case.
+ We use this method instead to avoid an assertion in insertJSIdentifier
+ */
+ if (scope->scopeType() != QQmlSA::ScopeType::QMLScope) {
+ scope->insertJSIdentifier(name, identifier);
+ return true;
+ } else {
+ const QQmlJSScope *scopePtr = scope.get();
+ std::pair<const QQmlJSScope*, QString> misplaced { scopePtr, name };
+ if (misplacedJSIdentifiers.contains(misplaced))
+ return false; // we only want to warn once
+ misplacedJSIdentifiers.insert(misplaced);
+ m_logger->log(u"JavaScript declarations are not allowed in QML elements"_s, qmlSyntax,
+ identifier.location);
+ return false;
+ }
+}
+
+/*!
+ \internal
Sets the name of \a scope to \a name based on \a type.
*/
inline void setScopeName(QQmlJSScope::Ptr &scope, QQmlJSScope::ScopeType type, const QString &name)
@@ -1501,9 +1531,9 @@ void QQmlJSImportVisitor::flushPendingSignalParameters()
{
const QQmlJSMetaSignalHandler handler = m_signalHandlers[m_pendingSignalHandler];
for (const QString &parameter : handler.signalParameters) {
- m_currentScope->insertJSIdentifier(parameter,
- { QQmlJSScope::JavaScriptIdentifier::Injected,
- m_pendingSignalHandler, std::nullopt, false });
+ safeInsertJSIdentifier(m_currentScope, parameter,
+ { QQmlJSScope::JavaScriptIdentifier::Injected,
+ m_pendingSignalHandler, std::nullopt, false });
}
m_pendingSignalHandler = QQmlJS::SourceLocation();
}
@@ -1993,10 +2023,10 @@ void QQmlJSImportVisitor::visitFunctionExpressionHelper(QQmlJS::AST::FunctionExp
const QQmlJS::SourceLocation functionLocation = fexpr->identifierToken.isValid()
? fexpr->identifierToken
: fexpr->functionToken;
- m_currentScope->insertJSIdentifier(name,
- { QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
- functionLocation, method.returnTypeName(),
- false });
+ safeInsertJSIdentifier(m_currentScope, name,
+ { QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
+ functionLocation, method.returnTypeName(),
+ false });
}
enterEnvironment(QQmlSA::ScopeType::JSFunctionScope, name, fexpr->firstSourceLocation());
} else {
@@ -2817,7 +2847,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::Catch *catchStatement)
{
enterEnvironment(QQmlSA::ScopeType::JSLexicalScope, QStringLiteral("catch"),
catchStatement->firstSourceLocation());
- m_currentScope->insertJSIdentifier(
+ safeInsertJSIdentifier(m_currentScope,
catchStatement->patternElement->bindingIdentifier.toString(),
{ QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
catchStatement->patternElement->firstSourceLocation(), std::nullopt,
@@ -2872,7 +2902,9 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl)
}
const bool isConst = vdl->declaration->scope == QQmlJS::AST::VariableScope::Const;
- m_currentScope->insertJSIdentifier(name, { kind, location, typeName, isConst });
+ // Break if insertion failed to avoid multiple warnings at the same location
+ if (!safeInsertJSIdentifier(m_currentScope, name, { kind, location, typeName, isConst }))
+ break;
vdl = vdl->next;
}
return true;
@@ -2887,7 +2919,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::FormalParameterList *fpl)
if (TypeAnnotation *annotation = boundName.typeAnnotation.data())
if (Type *type = annotation->type)
typeName = type->toString();
- m_currentScope->insertJSIdentifier(boundName.id,
+ safeInsertJSIdentifier(m_currentScope, boundName.id,
{ QQmlJSScope::JavaScriptIdentifier::Parameter,
boundName.location, typeName, false });
}
@@ -3130,13 +3162,15 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::PatternElement *element)
if (TypeAnnotation *annotation = name.typeAnnotation.data())
if (Type *type = annotation->type)
typeName = type->toString();
- m_currentScope->insertJSIdentifier(
+ const bool couldInsert = safeInsertJSIdentifier(m_currentScope,
name.id,
{ (element->scope == QQmlJS::AST::VariableScope::Var)
? QQmlJSScope::JavaScriptIdentifier::FunctionScoped
: QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
name.location, typeName,
element->scope == QQmlJS::AST::VariableScope::Const });
+ if (!couldInsert)
+ break;
}
}
diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h
index d69f975a8a..090b4a92f7 100644
--- a/src/qmlcompiler/qqmljsimportvisitor_p.h
+++ b/src/qmlcompiler/qqmljsimportvisitor_p.h
@@ -194,6 +194,7 @@ protected:
QSet<QString> m_usedTypes;
QList<UnfinishedBinding> m_bindings;
+ QSet<std::pair<const QQmlJSScope *, QString>> misplacedJSIdentifiers;
// stores JS functions and Script bindings per scope (only the name). mimics
// the content of QmlIR::Object::functionsAndExpressions
@@ -410,6 +411,9 @@ private:
void enterRootScope(QQmlJSScope::ScopeType type, const QString &name,
const QQmlJS::SourceLocation &location);
+ bool safeInsertJSIdentifier(QQmlJSScope::Ptr &scope, const QString &name,
+ const QQmlJSScope::JavaScriptIdentifier &identifier);
+
QList<QQmlJS::DiagnosticMessage> importFromHost(
const QString &path, const QString &prefix, const QQmlJS::SourceLocation &location);
QList<QQmlJS::DiagnosticMessage> importFromQrc(