aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2022-09-28 20:04:44 +0200
committerSami Shalayel <sami.shalayel@qt.io>2022-11-23 10:29:25 +0100
commit3103bf5a4267cef2c402649cade0a18024a9130b (patch)
tree9d8d0b45394260e7c1f133673dee00d7cc0b833d
parented47bff4118f677e135f3b7c113b035ac991c5ca (diff)
QQmlJSImportVisitor: warn when uncreatables are created
isCreatable in qqmljsscope just returns the value of the flag as it was read from the qmltypes, which is slightly confusing as isCreatable is an opt-out option. Instead, isCreatable should reflect whether the type is creatable or not. A type is uncreatable if and only if it is a singleton, an attached type, a c++ type with QML_UNCREATABLE and types without default constructor. This uncreatibility can also be inherited to composite types. Types without default constructor require QML_UNCREATABLE or QML_ANONYMOUS, and will be handled in another commit. Now that uncreatable types can be detected, emit a warning when a singleton or an uncreatable type is created: up to now no such warning was emitted. This warning can be seen when using qmllint or qmltc. By the way, also fix qmltc to not assert when something goes wrong (e.g. because an uncreatable or singleton type was created). Change-Id: I9a82106a801d14063407eb4e54858b1ca9fd578b Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-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);