aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp18
-rw-r--r--src/qmlcompiler/qqmljslogger.cpp7
-rw-r--r--src/qmlcompiler/qqmljslogger_p.h1
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp34
-rw-r--r--src/qmlcompiler/qqmljsscope_p.h7
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader.cpp2
-rw-r--r--tests/auto/qml/qmltc_qprocess/CMakeLists.txt10
-rw-r--r--tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h52
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/singletonUncreatable.qml5
-rw-r--r--tests/auto/qml/qmltc_qprocess/data/uncreatable.qml23
-rw-r--r--tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp22
-rw-r--r--tools/qmltc/qmltcvisitor.cpp5
12 files changed, 171 insertions, 15 deletions
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 4cc7849ce4..84cb2b4c63 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qqmljsimportvisitor_p.h"
+#include "qqmljsmetatypes_p.h"
#include "qqmljsresourcefilemapper_p.h"
#include <QtCore/qfileinfo.h>
@@ -1339,13 +1340,24 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
const QTypeRevision revision = QQmlJSScope::resolveTypes(
m_currentScope, m_rootScopeImports, &m_usedTypes);
- if (isRoot) {
- if (auto base = m_currentScope->baseType();
- base && base->internalName() == u"QQmlComponent") {
+ if (auto base = m_currentScope->baseType(); base) {
+ if (isRoot && base->internalName() == u"QQmlComponent") {
m_logger->log(u"Qml top level type cannot be 'Component'."_s, qmlTopLevelComponent,
definition->qualifiedTypeNameId->identifierToken, true, true);
return false;
}
+ if (base->isSingleton() && m_currentScope->isComposite()) {
+ m_logger->log(u"Singleton Type %1 is not creatable."_s.arg(
+ m_currentScope->baseTypeName()),
+ qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
+ true, true);
+
+ } else if (!base->isCreatable()) {
+ // composite type m_currentScope is allowed to be uncreatable, but it cannot be the base of anything else
+ m_logger->log(u"Type %1 is not creatable."_s.arg(m_currentScope->baseTypeName()),
+ qmlUncreatableType, definition->qualifiedTypeNameId->identifierToken,
+ true, true);
+ }
}
if (m_nextIsInlineComponent) {
Q_ASSERT(std::holds_alternative<InlineComponentNameType>(m_currentRootName));
diff --git a/src/qmlcompiler/qqmljslogger.cpp b/src/qmlcompiler/qqmljslogger.cpp
index d5e8c219fe..976f706a9b 100644
--- a/src/qmlcompiler/qqmljslogger.cpp
+++ b/src/qmlcompiler/qqmljslogger.cpp
@@ -56,6 +56,8 @@ const LoggerWarningId qmlInvalidLintDirective { "invalid-lint-directive" };
const LoggerWarningId qmlUseProperFunction { "use-proper-function" };
const LoggerWarningId qmlAccessSingleton { "access-singleton-via-object" };
const LoggerWarningId qmlTopLevelComponent { "top-level-component" };
+const LoggerWarningId qmlUncreatableType { "uncreatable-type" };
+
const QList<QQmlJSLogger::Category> &QQmlJSLogger::defaultCategories()
{
@@ -172,7 +174,10 @@ const QList<QQmlJSLogger::Category> &QQmlJSLogger::defaultCategories()
QStringLiteral("Warn if a singleton is accessed via an object"), QtWarningMsg },
QQmlJSLogger::Category {
qmlTopLevelComponent.name().toString(), QStringLiteral("TopLevelComponent"),
- QStringLiteral("Fail when a top level Component are encountered"), QtWarningMsg }
+ QStringLiteral("Fail when a top level Component are encountered"), QtWarningMsg },
+ QQmlJSLogger::Category {
+ qmlUncreatableType.name().toString(), QStringLiteral("UncreatableType"),
+ QStringLiteral("Warn if uncreatable types are created"), QtWarningMsg }
};
return cats;
diff --git a/src/qmlcompiler/qqmljslogger_p.h b/src/qmlcompiler/qqmljslogger_p.h
index f91518041d..34be098832 100644
--- a/src/qmlcompiler/qqmljslogger_p.h
+++ b/src/qmlcompiler/qqmljslogger_p.h
@@ -130,6 +130,7 @@ extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlInvalidLintDirectiv
extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUseProperFunction;
extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlAccessSingleton;
extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlTopLevelComponent;
+extern const Q_QMLCOMPILER_PRIVATE_EXPORT LoggerWarningId qmlUncreatableType;
struct Message : public QQmlJS::DiagnosticMessage
{
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index eda3b28e60..3ce732a742 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -1196,4 +1196,38 @@ QQmlJSScope::InlineComponentOrDocumentRootName QQmlJSScope::enclosingInlineCompo
return RootDocumentNameType();
}
+/*!
+ \internal
+ Returns true if the current type is creatable by checking all the required base classes.
+ "Uncreatability" is only inherited from base types for composite types (in qml) and not for non-composite types (c++).
+
+ For the exact definition:
+ A type is uncreatable if and only if one of its composite base type or its first non-composite base type matches
+ following criteria:
+ \list
+ \li the base type is a singleton, or
+ \li the base type is an attached type, or
+ \li the base type is a C++ type with the QML_UNCREATABLE or QML_ANONYMOUS macro, or
+ \li the base type is a type without default constructor (in that case, it really needs QML_UNCREATABLE or QML_ANONYMOUS)
+ \endlist
+ */
+bool QQmlJSScope::isCreatable() const
+{
+ auto isCreatableNonRecursive = [](const QQmlJSScope *scope) {
+ return scope->hasCreatableFlag() && !scope->isSingleton() && scope->scopeType() == QMLScope;
+ };
+
+ for (const QQmlJSScope* scope = this; scope; scope = scope->baseType().get()) {
+ if (!scope->isComposite()) {
+ // just check the first nonComposite (c++) base for isCreatableNonRecursive() and then stop
+ return isCreatableNonRecursive(scope);
+ } else {
+ // check all composite (qml) bases for isCreatableNonRecursive().
+ if (isCreatableNonRecursive(scope))
+ return true;
+ }
+ }
+ // no uncreatable bases found
+ return false;
+}
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h
index 0745acd668..70b51fec5f 100644
--- a/src/qmlcompiler/qqmljsscope_p.h
+++ b/src/qmlcompiler/qqmljsscope_p.h
@@ -508,7 +508,8 @@ public:
}
bool isSingleton() const { return m_flags & Singleton; }
- bool isCreatable() const { return m_flags & Creatable; }
+ bool isCreatable() const;
+ bool hasCreatableFlag() const { return m_flags & Creatable; }
/*!
* \internal
*
@@ -522,7 +523,7 @@ public:
bool isWrappedInImplicitComponent() const { return m_flags & WrappedInImplicitComponent; }
bool extensionIsNamespace() const { return m_flags & HasExtensionNamespace; }
void setIsSingleton(bool v) { m_flags.setFlag(Singleton, v); }
- void setIsCreatable(bool v) { m_flags.setFlag(Creatable, v); }
+ void setCreatableFlag(bool v) { m_flags.setFlag(Creatable, v); }
void setIsComposite(bool v) { m_flags.setFlag(Composite, v); }
void setIsScript(bool v) { m_flags.setFlag(Script, v); }
void setHasCustomParser(bool v)
@@ -758,7 +759,7 @@ private:
QString m_extensionTypeName;
QQmlJSScope::WeakConstPtr m_extensionType;
- Flags m_flags;
+ Flags m_flags = Creatable; // all types are marked as creatable by default.
AccessSemantics m_semantics = AccessSemantics::Reference;
QQmlJS::SourceLocation m_sourceLocation;
diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
index fe884b39ec..8c6368dfde 100644
--- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp
+++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
@@ -212,7 +212,7 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
} else if (name == QLatin1String("isSingleton")) {
scope->setIsSingleton(readBoolBinding(script));
} else if (name == QLatin1String("isCreatable")) {
- scope->setIsCreatable(readBoolBinding(script));
+ scope->setCreatableFlag(readBoolBinding(script));
} else if (name == QLatin1String("isComposite")) {
scope->setIsComposite(readBoolBinding(script));
} else if (name == QLatin1String("hasCustomParser")) {
diff --git a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
index 5644a622a2..04d2084d4f 100644
--- a/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
+++ b/tests/auto/qml/qmltc_qprocess/CMakeLists.txt
@@ -9,6 +9,11 @@ qt_internal_add_test(tst_qmltc_qprocess
Qt::Qml
Qt::QuickTestUtilsPrivate
)
+
+# special setup for singleton files:
+set_source_files_properties(data/SingletonThing.qml data/singletonUncreatable.qml
+ PROPERTIES QT_QML_SINGLETON_TYPE true)
+
qt6_add_qml_module(tst_qmltc_qprocess
VERSION 1.0
URI QmltcQProcessTests
@@ -23,6 +28,8 @@ qt6_add_qml_module(tst_qmltc_qprocess
data/invalidAliasRevision.qml
data/ComponentType.qml
data/inlineComponentWithEnum.qml
+ data/singletonUncreatable.qml
+ data/uncreatable.qml
)
set(common_libraries
@@ -37,9 +44,6 @@ set(common_libraries
target_include_directories(tst_qmltc_qprocess PUBLIC cpptypes/)
target_link_libraries(tst_qmltc_qprocess PUBLIC ${common_libraries})
-# special setup for singleton files:
-set_source_files_properties(SingletonThing.qml PROPERTIES QT_QML_SINGLETON_TYPE true)
-
add_dependencies(tst_qmltc_qprocess Qt::qmltc)
# fetch --resource arguments manually (mimics the logic of qmltc compilation
diff --git a/tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h b/tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h
index a6fdb4a51a..6082870c76 100644
--- a/tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h
+++ b/tests/auto/qml/qmltc_qprocess/cpptypes/testtype.h
@@ -3,12 +3,15 @@
#ifndef TESTTYPE_H
#define TESTTYPE_H
+
+#include <QtQmlIntegration/qqmlintegration.h>
#include <QtCore/qobject.h>
#include <QtQml/qqmlregistration.h>
class TypeWithVersionedAlias : public QObject
{
Q_OBJECT
+ QML_UNCREATABLE("")
QML_ELEMENT
QString m_readAndWrite;
@@ -17,4 +20,53 @@ public:
Q_PROPERTY(QString notExisting MEMBER m_readAndWrite REVISION(6, 0));
Q_PROPERTY(QString existing MEMBER m_readAndWrite REVISION(1, 0));
};
+
+class UncreatableType : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_UNCREATABLE("")
+};
+
+class NoDefaultConstructorType : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ NoDefaultConstructorType() = delete;
+};
+
+class SingletonType : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_SINGLETON
+};
+
+class NotSingletonType : public SingletonType
+{
+ Q_OBJECT
+ QML_ELEMENT
+};
+
+class NormalTypeAttached : public QObject
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+public:
+ NormalTypeAttached(QObject* parent): QObject(parent) {}
+};
+
+class NormalType : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_ATTACHED(NormalTypeAttached)
+
+ static NormalTypeAttached *qmlAttachedProperties(QObject *object) {
+ return new NormalTypeAttached(object);
+ }
+};
+
+
+
#endif // TESTTYPE_H
diff --git a/tests/auto/qml/qmltc_qprocess/data/singletonUncreatable.qml b/tests/auto/qml/qmltc_qprocess/data/singletonUncreatable.qml
new file mode 100644
index 0000000000..d48a4ee186
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/singletonUncreatable.qml
@@ -0,0 +1,5 @@
+pragma Singleton
+
+UncreatableType {
+
+}
diff --git a/tests/auto/qml/qmltc_qprocess/data/uncreatable.qml b/tests/auto/qml/qmltc_qprocess/data/uncreatable.qml
new file mode 100644
index 0000000000..aad6ef3421
--- /dev/null
+++ b/tests/auto/qml/qmltc_qprocess/data/uncreatable.qml
@@ -0,0 +1,23 @@
+import QtQuick
+
+Item {
+ // Illegal cases:
+ UncreatableType {}
+ SingletonThing {}
+ SingletonType {}
+
+ component A: SingletonThing {}
+ component AA: A {}
+ component AAA: AA {}
+ AAA {}
+
+ component B: SingletonType {}
+ component BB: B {}
+ component BBB: BB {}
+ BBB {}
+
+ // Legal cases, where qmltc should not crash
+ property SingletonThing myQmlSingleton
+ property SingletonType myCppSingleton
+ NotSingletonType {} // ok because a non composite type inheriting from a singleton does not become a singleton!
+}
diff --git a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
index ae01f4697f..ea30e3c83f 100644
--- a/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
+++ b/tests/auto/qml/qmltc_qprocess/tst_qmltc_qprocess.cpp
@@ -189,9 +189,25 @@ void tst_qmltc_qprocess::inlineComponent()
void tst_qmltc_qprocess::singleton()
{
- const auto errors = runQmltc(u"SingletonThing.qml"_s, false);
- QEXPECT_FAIL("", "qmltc does not support singletons at the moment", Continue);
- QVERIFY(!errors.contains(u"Singleton types are not supported"_s));
+ {
+ const auto errors = runQmltc(u"singletonUncreatable.qml"_s, false);
+ QVERIFY(errors.contains("singletonUncreatable.qml:3:1: Type UncreatableType is not "
+ "creatable. [uncreatable-type]"));
+ }
+ {
+ const auto errors = runQmltc(u"uncreatable.qml"_s, false);
+ QVERIFY(errors.contains(
+ "uncreatable.qml:5:5: Type UncreatableType is not creatable. [uncreatable-type]"));
+ QVERIFY(errors.contains("uncreatable.qml:6:5: Singleton Type SingletonThing is not "
+ "creatable. [uncreatable-type]"));
+ QVERIFY(errors.contains("uncreatable.qml:7:5: Singleton Type SingletonType is not "
+ "creatable. [uncreatable-type]"));
+ QVERIFY(errors.contains("uncreatable.qml:9:18: Singleton Type SingletonThing is not "
+ "creatable. [uncreatable-type]"));
+ QVERIFY(errors.contains("uncreatable.qml:14:18: Singleton Type SingletonType is not "
+ "creatable. [uncreatable-type]"));
+ QVERIFY(!errors.contains("NotSingletonType"));
+ }
}
void tst_qmltc_qprocess::warningsAsErrors()
diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp
index 3143316652..c777076d0c 100644
--- a/tools/qmltc/qmltcvisitor.cpp
+++ b/tools/qmltc/qmltcvisitor.cpp
@@ -103,7 +103,10 @@ void QmltcVisitor::findCppIncludes()
// look in type's base type
auto base = type->baseType();
- Q_ASSERT(base || !type->isComposite());
+ if (!base && type->isComposite())
+ // in this case, qqmljsimportvisitor would have already print an error message
+ // about the missing type, so just return silently without crashing
+ return;
if (!base || visitType(base))
return;
addCppInclude(base);