diff options
author | Sami Shalayel <sami.shalayel@qt.io> | 2022-05-19 10:35:20 +0200 |
---|---|---|
committer | Sami Shalayel <sami.shalayel@qt.io> | 2022-05-23 12:50:02 +0200 |
commit | 69cd8c2779d18e82d9872c9f55c45caa029365b0 (patch) | |
tree | 02a0ff180d6e7b010f07f0b00cc2cdf9b5aa05cc | |
parent | 6e2d70f2b8a0a02d01ca9dba759bd3d5eec32235 (diff) |
qmlcompiler: Add qualified name to QQmlJSScope
Added moduleName and qualifiedName to QQmlJSScope. Those
properties are written in the scopes after they were
loaded by readQmlDir.
Fixes: QTBUG-103299
Change-Id: I3b2c68c43c3bf0ac6cf801b0e54cf4b412b4d4e5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
-rw-r--r-- | src/qmlcompiler/qqmljsimporter.cpp | 23 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsimporter_p.h | 1 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsscope.cpp | 33 | ||||
-rw-r--r-- | src/qmlcompiler/qqmljsscope_p.h | 16 | ||||
-rw-r--r-- | tests/auto/qml/qqmljsscope/data/qualifiedName.qml | 41 | ||||
-rw-r--r-- | tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp | 60 |
6 files changed, 173 insertions, 1 deletions
diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp index 5f99a3c1da..9fcd031ef0 100644 --- a/src/qmlcompiler/qqmljsimporter.cpp +++ b/src/qmlcompiler/qqmljsimporter.cpp @@ -513,6 +513,7 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper() return m_builtins; Import result; + result.name = QStringLiteral("QML"); QStringList qmltypesFiles = { QStringLiteral("builtins.qmltypes"), QStringLiteral("jsroot.qmltypes") }; @@ -523,7 +524,7 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper() readQmltypes(it.next(), &result.objects, &result.dependencies); qmltypesFiles.removeOne(it.fileName()); } - + setQualifiedNamesOn(result); importDependencies(result, &m_builtins); if (qmltypesFiles.isEmpty()) @@ -731,6 +732,7 @@ bool QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types, const QFileInfo file(qmldirPath); if (file.exists()) { const auto import = readQmldir(file.canonicalPath()); + setQualifiedNamesOn(import); m_seenQmldirFiles.insert(qmldirPath, import); m_seenImports.insert(importId, qmldirPath); importDependencies(import, cacheTypes.get(), prefix, version, isDependency); @@ -797,4 +799,23 @@ QQmlJSScope::ConstPtr QQmlJSImporter::jsGlobalObject() const return m_builtins.cppNames[u"GlobalObject"_s].scope; } +void QQmlJSImporter::setQualifiedNamesOn(const Import &import) +{ + for (auto &object : import.objects) { + if (object.exports.isEmpty()) + continue; + const QString qualifiedName = QQmlJSScope::qualifiedNameFrom( + import.name, object.exports.first().type(), + object.exports.first().revision(), + object.exports.last().revision()); + if (auto *factory = object.scope.factory()) { + factory->setQualifiedName(qualifiedName); + factory->setModuleName(import.name); + } else { + object.scope->setQualifiedName(qualifiedName); + object.scope->setModuleName(import.name); + } + } +} + QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmljsimporter_p.h b/src/qmlcompiler/qqmljsimporter_p.h index 7df909ffa0..b823e0403d 100644 --- a/src/qmlcompiler/qqmljsimporter_p.h +++ b/src/qmlcompiler/qqmljsimporter_p.h @@ -145,6 +145,7 @@ private: Import readDirectory(const QString &directory); QQmlJSScope::Ptr localFile2ScopeTree(const QString &filePath); + static void setQualifiedNamesOn(const Import &import); QStringList m_importPaths; diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp index 73a1da74ea..148c8c122e 100644 --- a/src/qmlcompiler/qqmljsscope.cpp +++ b/src/qmlcompiler/qqmljsscope.cpp @@ -41,6 +41,21 @@ QT_BEGIN_NAMESPACE +/*! + \class QQmlJSScope + \internal + \brief Tracks the types for the QmlCompiler + + QQmlJSScope tracks the types used in qml for the QmlCompiler. + + Multiple QQmlJSScope objects might be created for the same conceptual type, except when reused + due to extensive caching. Two QQmlJSScope objects are considered equal when they are backed + by the same implementation, that is, they have the same internalName. + The qualifiedName of the QQmlJSScope for a type imported from multiple modules will contain the + name of one of the modules that imported it, which is not unique and might change depending + on the caching in . +*/ + using namespace Qt::StringLiterals; void QQmlJSScope::reparent(const QQmlJSScope::Ptr &parentScope, const QQmlJSScope::Ptr &childScope) @@ -781,6 +796,22 @@ bool QQmlJSScope::isNameDeferred(const QString &name) const return isDeferred; } +QString QQmlJSScope::qualifiedNameFrom(const QString &moduleName, const QString &typeName, + const QTypeRevision &firstRevision, + const QTypeRevision &lastRevision) +{ + QString qualifiedName = + u"%1/%2 %3.%4"_s.arg(moduleName, typeName) + .arg(firstRevision.hasMajorVersion() ? firstRevision.majorVersion() : 0) + .arg(firstRevision.hasMinorVersion() ? firstRevision.minorVersion() : 0); + if (firstRevision != lastRevision) { + qualifiedName += u"-%1.%2"_s + .arg(lastRevision.hasMajorVersion() ? lastRevision.majorVersion() : 0) + .arg(lastRevision.hasMinorVersion() ? lastRevision.minorVersion() : 0); + } + return qualifiedName; +} + void QQmlJSScope::setBaseTypeName(const QString &baseTypeName) { m_flags.setFlag(HasBaseTypeError, false); @@ -915,6 +946,8 @@ bool QQmlJSScope::Export::isValid() const void QDeferredFactory<QQmlJSScope>::populate(const QSharedPointer<QQmlJSScope> &scope) const { + scope->setQualifiedName(m_qualifiedName); + scope->setModuleName(m_moduleName); QQmlJSTypeReader typeReader(m_importer, m_filePath); typeReader(scope); m_importer->m_globalWarnings.append(typeReader.errors()); diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h index 9c7b339f47..a2cc9a7e24 100644 --- a/src/qmlcompiler/qqmljsscope_p.h +++ b/src/qmlcompiler/qqmljsscope_p.h @@ -337,6 +337,14 @@ public: QQmlJSScope::ConstPtr baseType() const { return m_baseType.scope; } QTypeRevision baseTypeRevision() const { return m_baseType.revision; } + QString qualifiedName() const { return m_qualifiedName; } + void setQualifiedName(const QString &qualifiedName) { m_qualifiedName = qualifiedName; }; + static QString qualifiedNameFrom(const QString &moduleName, const QString &typeName, + const QTypeRevision &firstRevision, + const QTypeRevision &lastRevision); + QString moduleName() const { return m_moduleName; } + void setModuleName(const QString &moduleName) { m_moduleName = moduleName; } + void clearBaseType() { m_baseType = {}; } void setBaseTypeError(const QString &baseTypeError); QString baseTypeError() const; @@ -665,6 +673,9 @@ private: AccessSemantics m_semantics = AccessSemantics::Reference; QQmlJS::SourceLocation m_sourceLocation; + + QString m_qualifiedName; + QString m_moduleName; }; Q_DECLARE_TYPEINFO(QQmlJSScope::QmlIRCompatibilityBindingData, Q_RELOCATABLE_TYPE); @@ -693,6 +704,9 @@ public: m_isSingleton = isSingleton; } + void setQualifiedName(const QString &qualifiedName) { m_qualifiedName = qualifiedName; } + void setModuleName(const QString &moduleName) { m_moduleName = moduleName; } + private: friend class QDeferredSharedPointer<QQmlJSScope>; friend class QDeferredSharedPointer<const QQmlJSScope>; @@ -705,6 +719,8 @@ private: QString m_filePath; QQmlJSImporter *m_importer = nullptr; bool m_isSingleton = false; + QString m_qualifiedName; + QString m_moduleName; }; using QQmlJSExportedScope = QQmlJSScope::ExportedScope<QQmlJSScope::Ptr>; diff --git a/tests/auto/qml/qqmljsscope/data/qualifiedName.qml b/tests/auto/qml/qqmljsscope/data/qualifiedName.qml new file mode 100644 index 0000000000..56663254b0 --- /dev/null +++ b/tests/auto/qml/qqmljsscope/data/qualifiedName.qml @@ -0,0 +1,41 @@ +import QtQuick 2.1 as MyQualifiedImport +import QtQuick 2.0 + +MyQualifiedImport.Item { + id:shouldBeQtQuickItem + + Text { + id: shouldBeQtQuickText0 + } + + TextEdit {} + + MyQualifiedImport.TextEdit {} + + Component { + id: shouldBeQtQmlComponent + + Item { + + MyQualifiedImport.Text { + id: shouldBeQtQuickText1 + } + + Text { + id: shouldBeQtQuickText2 + } + + MyQualifiedImport.TextInput { + id: shouldBeQtQuickTextInput3 + } + + Timer { + id: indirectlyImportedFromQtQml + } + + MyQualifiedImport.Timer { + id: indirectlyImportedFromQtQml2 + } + } + } +} diff --git a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp index cb1a03ab2b..4b7e26bcbc 100644 --- a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp +++ b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp @@ -130,6 +130,7 @@ private Q_SLOTS: void scriptIndices(); void extensions(); void emptyBlockBinding(); + void qualifiedName(); public: tst_qqmljsscope() @@ -640,5 +641,64 @@ void tst_qqmljsscope::emptyBlockBinding() QVERIFY(root->hasOwnPropertyBindings(u"y"_s)); } +void tst_qqmljsscope::qualifiedName() +{ + QQmlJSScope::ConstPtr root = run(u"qualifiedName.qml"_s); + + auto qualifiedNameOf = [](const QQmlJSScope::ConstPtr &ptr) { + return ptr->baseType()->qualifiedName(); + }; + + QQmlJSScope::ConstPtr item = root; + + // normal case + QCOMPARE(qualifiedNameOf(item), "QtQuick/Item 2.0-6.3"); + + QCOMPARE(item->childScopes().size(), 4); + QQmlJSScope::ConstPtr textInItem = item->childScopes()[0]; + QQmlJSScope::ConstPtr nonQualifiedComponentInItem = item->childScopes()[1]; + QQmlJSScope::ConstPtr qualifiedComponentInItem = item->childScopes()[2]; + QQmlJSScope::ConstPtr componentInItem = item->childScopes()[3]; + + // qualified case + QCOMPARE(qualifiedNameOf(nonQualifiedComponentInItem), "QtQuick/TextEdit 2.0-6.3"); + + // qualified case + QCOMPARE(qualifiedNameOf(qualifiedComponentInItem), "QtQuick/TextEdit 2.0-6.3"); + + // normal case + QCOMPARE(qualifiedNameOf(textInItem), "QtQuick/Text 2.0-6.3"); + // qualified import of builtin variable + QCOMPARE(qualifiedNameOf(componentInItem), "QML/Component 1.0"); + QCOMPARE(componentInItem->baseType()->moduleName(), "QML"); + + QCOMPARE(componentInItem->childScopes().size(), 1); + + QQmlJSScope::ConstPtr itemInComponent = componentInItem->childScopes()[0]; + + QCOMPARE(qualifiedNameOf(itemInComponent), "QtQuick/Item 2.0-6.3"); + + QCOMPARE(itemInComponent->childScopes().size(), 5); + QQmlJSScope::ConstPtr qualifiedImportTextInItemInComponent = itemInComponent->childScopes()[0]; + QQmlJSScope::ConstPtr textInItemInComponent = itemInComponent->childScopes()[1]; + QQmlJSScope::ConstPtr qualifiedImportTextInputInItemInComponent = + itemInComponent->childScopes()[2]; + QQmlJSScope::ConstPtr indirectImportTimerInItemInComponent = itemInComponent->childScopes()[3]; + QQmlJSScope::ConstPtr qualifiedImportTimerInItemInComponent = itemInComponent->childScopes()[4]; + + QCOMPARE(qualifiedNameOf(qualifiedImportTextInItemInComponent), "QtQuick/Text 2.0-6.3"); + QCOMPARE(qualifiedNameOf(textInItemInComponent), "QtQuick/Text 2.0-6.3"); + QCOMPARE(textInItemInComponent->baseType()->moduleName(), "QtQuick"); + QCOMPARE(qualifiedImportTextInItemInComponent->baseType()->moduleName(), "QtQuick"); + + QCOMPARE(qualifiedNameOf(qualifiedImportTextInputInItemInComponent), + "QtQuick/TextInput 2.0-6.3"); + + QCOMPARE(qualifiedNameOf(indirectImportTimerInItemInComponent), "QtQml/Timer 2.0-6.0"); + QCOMPARE(qualifiedNameOf(qualifiedImportTimerInItemInComponent), "QtQml/Timer 2.0-6.0"); + QCOMPARE(indirectImportTimerInItemInComponent->baseType()->moduleName(), "QtQml"); + QCOMPARE(qualifiedImportTimerInItemInComponent->baseType()->moduleName(), "QtQml"); +} + QTEST_MAIN(tst_qqmljsscope) #include "tst_qqmljsscope.moc" |