qmltc: Cleanly reject custom parsed properties

Process all properties of custom parsed types and generate errors if the
custom parsed properties are actually used. Then print an extra error
stating that qmltc does not support custom parsers.

Pick-to: 6.9 6.8
Fixes: QTBUG-134206
Change-Id: I37e4f3f8d0ee4e0926c0d64c99a4a521b093a1ab
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
This commit is contained in:
Ulf Hermann 2025-04-11 13:02:07 +02:00
parent 373782c1c0
commit afb51a7e91
8 changed files with 57 additions and 8 deletions

View File

@ -603,7 +603,7 @@ void QQmlJSImportVisitor::processDefaultProperties()
QQmlJSScope::ConstPtr parentScope = it.key(); QQmlJSScope::ConstPtr parentScope = it.key();
// We can't expect custom parser default properties to be sensible, discard them for now. // We can't expect custom parser default properties to be sensible, discard them for now.
if (parentScope->isInCustomParserParent()) if (checkCustomParser(parentScope))
continue; continue;
/* consider: /* consider:
@ -1071,7 +1071,7 @@ void QQmlJSImportVisitor::processPropertyBindings()
// These warnings do not apply for custom parsers and their children and need to be // These warnings do not apply for custom parsers and their children and need to be
// handled on a case by case basis // handled on a case by case basis
if (scope->isInCustomParserParent()) if (checkCustomParser(scope))
continue; continue;
// TODO: Can this be in a better suited category? // TODO: Can this be in a better suited category?
@ -1273,7 +1273,7 @@ void QQmlJSImportVisitor::addDefaultProperties()
m_pendingDefaultProperties[m_currentScope->parentScope()] << m_currentScope; m_pendingDefaultProperties[m_currentScope->parentScope()] << m_currentScope;
if (parentScope->isInCustomParserParent()) if (checkCustomParser(parentScope))
return; return;
/* consider: /* consider:
@ -1370,7 +1370,7 @@ void QQmlJSImportVisitor::checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr sc
{ {
// These warnings do not apply for custom parsers and their children and need to be handled on a // These warnings do not apply for custom parsers and their children and need to be handled on a
// case by case basis // case by case basis
if (scope->isInCustomParserParent()) if (checkCustomParser(scope))
return; return;
auto children = scope->childScopes(); auto children = scope->childScopes();
@ -1396,6 +1396,11 @@ void QQmlJSImportVisitor::checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr sc
} }
} }
bool QQmlJSImportVisitor::checkCustomParser(const QQmlJSScope::ConstPtr &scope)
{
return scope->isInCustomParserParent();
}
void QQmlJSImportVisitor::flushPendingSignalParameters() void QQmlJSImportVisitor::flushPendingSignalParameters()
{ {
const QQmlJSMetaSignalHandler handler = m_signalHandlers[m_pendingSignalHandler]; const QQmlJSMetaSignalHandler handler = m_signalHandlers[m_pendingSignalHandler];
@ -2390,7 +2395,7 @@ void QQmlJSImportVisitor::endVisit(UiArrayBinding *arrayBinding)
const auto propertyName = getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope); const auto propertyName = getScopeName(m_currentScope, QQmlSA::ScopeType::QMLScope);
leaveEnvironment(); leaveEnvironment();
if (m_currentScope->isInCustomParserParent()) { if (checkCustomParser(m_currentScope)) {
// These warnings do not apply for custom parsers and their children and need to be handled // These warnings do not apply for custom parsers and their children and need to be handled
// on a case by case basis // on a case by case basis
return; return;
@ -2951,7 +2956,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
} }
} }
if (m_currentScope->isInCustomParserParent()) { if (checkCustomParser(m_currentScope)) {
// These warnings do not apply for custom parsers and their children and need to be handled // These warnings do not apply for custom parsers and their children and need to be handled
// on a case by case basis // on a case by case basis
} else { } else {

View File

@ -159,6 +159,8 @@ protected:
void throwRecursionDepthError() override; void throwRecursionDepthError() override;
virtual bool checkCustomParser(const QQmlJSScope::ConstPtr &scope);
QString m_implicitImportDirectory; QString m_implicitImportDirectory;
QStringList m_qmldirFiles; QStringList m_qmldirFiles;
QQmlJSScope::Ptr m_currentScope; QQmlJSScope::Ptr m_currentScope;
@ -266,7 +268,7 @@ protected:
template<typename ErrorHandler> template<typename ErrorHandler>
bool checkTypeResolved(const QQmlJSScope::ConstPtr &type, ErrorHandler handle) bool checkTypeResolved(const QQmlJSScope::ConstPtr &type, ErrorHandler handle)
{ {
if (type->isFullyResolved() || type->isInCustomParserParent()) if (type->isFullyResolved() || checkCustomParser(type))
return true; return true;
// Note: ignore duplicates, but only after we are certain that the type // Note: ignore duplicates, but only after we are certain that the type

View File

@ -48,6 +48,7 @@ qt6_add_qml_module(tst_qmltc_qprocess
data/componentDefinitionInnerRequiredProperty.qml data/componentDefinitionInnerRequiredProperty.qml
data/componentDefinitionInnerRequiredPropertyFromOutside.qml data/componentDefinitionInnerRequiredPropertyFromOutside.qml
data/innerLevelRequiredProperty.qml data/innerLevelRequiredProperty.qml
data/customParsed.qml
NO_GENERATE_EXTRA_QMLDIRS NO_GENERATE_EXTRA_QMLDIRS
) )

View File

@ -0,0 +1,7 @@
import QtQuick
Item {
ListModel {
ListElement { a: 5 }
}
}

View File

@ -58,6 +58,7 @@ private slots:
void componentDefinitionInnerRequiredProperty(); void componentDefinitionInnerRequiredProperty();
void componentDefinitionInnerRequiredPropertyFromOutside(); void componentDefinitionInnerRequiredPropertyFromOutside();
void innerLevelRequiredProperty(); void innerLevelRequiredProperty();
void customParsed();
}; };
#ifndef TST_QMLTC_QPROCESS_RESOURCES #ifndef TST_QMLTC_QPROCESS_RESOURCES
@ -364,5 +365,20 @@ void tst_qmltc_qprocess::innerLevelRequiredProperty()
} }
} }
void tst_qmltc_qprocess::customParsed()
{
const auto errors = runQmltc(u"customParsed.qml"_s, false);
QVERIFY(errors.contains(
u"customParsed.qml:5:9: Cannot assign to non-existent default property [missing-property]"
));
QVERIFY(errors.contains(
u"customParsed.qml:5:23: Could not find property \"a\". [missing-property]"
));
QVERIFY(errors.contains(
u"customParsed.qml:: qmltc does not support custom parsers such as ListModel or old forms "
"of Connections and PropertyChanges. [compiler]"
));
}
QTEST_MAIN(tst_qmltc_qprocess) QTEST_MAIN(tst_qmltc_qprocess)
#include "tst_qmltc_qprocess.moc" #include "tst_qmltc_qprocess.moc"

View File

@ -291,8 +291,14 @@ int main(int argc, char **argv)
QString(), QString(), QString()); QString(), QString(), QString());
passMan->analyze(QQmlJSScope::createQQmlSAElement(visitor.result())); passMan->analyze(QQmlJSScope::createQQmlSAElement(visitor.result()));
if (logger.hasErrors()) if (logger.hasErrors()) {
if (visitor.hasSeenCustomParsers()) {
logger.log(QStringLiteral("qmltc does not support custom parsers such as ListModel or "
"old forms of Connections and PropertyChanges."),
qmlCompiler, QQmlJS::SourceLocation());
}
return EXIT_FAILURE; return EXIT_FAILURE;
}
QList<QQmlJS::DiagnosticMessage> warnings = importer.takeGlobalWarnings(); QList<QQmlJS::DiagnosticMessage> warnings = importer.takeGlobalWarnings();
if (!warnings.isEmpty()) { if (!warnings.isEmpty()) {

View File

@ -337,6 +337,13 @@ void QmltcVisitor::endVisit(QQmlJS::AST::UiProgram *program)
checkNamesAndTypes(type); checkNamesAndTypes(type);
} }
bool QmltcVisitor::checkCustomParser(const QQmlJSScope::ConstPtr &scope)
{
if (QQmlJSImportVisitor::checkCustomParser(scope))
m_seenCustomParsers = true;
return false;
}
QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding) QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding)
{ {
switch (binding.bindingType()) { switch (binding.bindingType()) {

View File

@ -135,6 +135,9 @@ public:
enum Mode { Import, Compile }; enum Mode { Import, Compile };
void setMode(Mode mode) { m_mode = mode; } void setMode(Mode mode) { m_mode = mode; }
bool checkCustomParser(const QQmlJSScope::ConstPtr &scope) override;
bool hasSeenCustomParsers() const { return m_seenCustomParsers; }
protected: protected:
QStringList m_qmlTypeNames; // names of QML types arranged as a stack QStringList m_qmlTypeNames; // names of QML types arranged as a stack
QHash<QString, int> m_qmlTypeNameCounts; QHash<QString, int> m_qmlTypeNameCounts;
@ -184,6 +187,8 @@ protected:
QHash<QQmlJSScope::ConstPtr, int> m_typesWithId; QHash<QQmlJSScope::ConstPtr, int> m_typesWithId;
Mode m_mode = Mode::Import; Mode m_mode = Mode::Import;
bool m_seenCustomParsers = false;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE