From f7e29f7d2757e6ac577aea998f53ba6745b0e75d Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 11 Feb 2025 12:12:16 +0100 Subject: [PATCH] QtQml: Clean up qmldir redirection We need to perform the redirection before inserting imports into namespaces. Through the redirection we might discover a module that we have already imported before. In that case we must not import it again. Pick-to: 6.9 6.8 Fixes: QTBUG-133587 Change-Id: I47a279461763b5397137002a9e7c7d3bfc7ad15d Reviewed-by: Fabian Kosmale --- src/qml/qml/qqmlimport.cpp | 357 +++++++++++------- src/qml/qml/qqmlimport_p.h | 26 +- src/qml/qml/qqmltypeloader.cpp | 3 +- tests/auto/qml/qmlcppcodegen/CMakeLists.txt | 4 + .../qml/qmlcppcodegen/data/CMakeLists.txt | 2 + .../data/WithSubDir/CMakeLists.txt | 23 ++ .../data/WithSubDir/qml/GreenRect.qml | 6 + .../data/WithSubDir/qml/MyScript.js | 2 + .../qml/qmlcppcodegen/data/multiRedirect.qml | 7 + .../qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 13 + 10 files changed, 300 insertions(+), 143 deletions(-) create mode 100644 tests/auto/qml/qmlcppcodegen/data/WithSubDir/CMakeLists.txt create mode 100644 tests/auto/qml/qmlcppcodegen/data/WithSubDir/qml/GreenRect.qml create mode 100644 tests/auto/qml/qmlcppcodegen/data/WithSubDir/qml/MyScript.js create mode 100644 tests/auto/qml/qmlcppcodegen/data/multiRedirect.qml diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 62f0f62386..7f07dc1452 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -769,13 +769,24 @@ bool QQmlImports::resolveType( Q_UNREACHABLE(); } -QQmlImportInstance *QQmlImportNamespace::findImport(const QString &uri) const +QQmlImportInstance *QQmlImportNamespace::findImportByModuleUri( + const QString &moduleUri, QTypeRevision version) const { - for (QQmlImportInstance *import : imports) { - if (import->uri == uri) - return import; - } - return nullptr; + const auto end = imports.cend(); + const auto it = std::find_if(imports.cbegin(), end, [&](const QQmlImportInstance *import) { + return import->uri == moduleUri && import->version == version; + }); + return it == end ? nullptr : *it; +} + +QQmlImportInstance *QQmlImportNamespace::findImportByLocation( + const QString &location, QTypeRevision version) const +{ + const auto end = imports.cend(); + const auto it = std::find_if(imports.cbegin(), end, [&](const QQmlImportInstance *import) { + return import->url == location && import->version == version; + }); + return it == end ? nullptr : *it; } bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, @@ -920,8 +931,7 @@ void QQmlImports::registerBuiltinModuleTypes( } QString QQmlImports::redirectQmldirContent( - QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir, - QQmlImportInstance *inserted) + QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir) { const QString preferredPath = qmldir->preferredPath(); const QString url = preferredPath.startsWith(u':') @@ -935,9 +945,6 @@ QString QQmlImports::redirectQmldirContent( if (redirected.hasContent() && !redirected.hasError()) *qmldir = std::move(redirected); - if (const QString qmldirUri = qmldir->typeNamespace(); !qmldirUri.isEmpty()) - inserted->uri = qmldirUri; - return url; } @@ -991,7 +998,7 @@ QString QQmlImports::resolvedUri(const QString &dir_arg, QQmlTypeLoader *typeLoa return stableRelativePath; } -QTypeRevision QQmlImports::matchingQmldirVersion( +static QTypeRevision matchingQmldirVersion( const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, QTypeRevision version, QList *errors) { @@ -1065,13 +1072,13 @@ QTypeRevision QQmlImports::matchingQmldirVersion( || (version.hasMinorVersion() && (lowestMinorVersion > version.minorVersion() || highestMinorVersion < version.minorVersion())))) { - errors->prepend(moduleNotFoundError(uri, version)); + errors->prepend(QQmlImports::moduleNotFoundError(uri, version)); return QTypeRevision(); } // ... otherwise, anything is valid. if (bestMajorVersion < 0) - return validVersion(); + return QQmlImports::validVersion(); return QTypeRevision::fromVersion( bestMajorVersion, @@ -1132,9 +1139,47 @@ static QString getVersionInfo(QTypeRevision version) { return version.isValid() ? QDebug::toString(version) : u"(latest)"_s; } +static QTypeRevision matchingModuleVersionForLibraryImport( + const QString &uri, QTypeRevision version, QList *errors) +{ + const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version); + if (!matchingVersion.isValid()) + errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version))); + return matchingVersion; +} + +static QTypeRevision finalizeLibraryImport( + const QString &uri, QTypeRevision version, const QQmlTypeLoaderQmldirContent &qmldir, + QQmlImportInstance *inserted, QList *errors) +{ + // Ensure that we are actually providing something + const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version); + if (matchingVersion.isValid()) + return matchingVersion; + + if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) { + if (qmldir.plugins().isEmpty()) { + if (!qmldir.imports().isEmpty()) + return QQmlImports::validVersion(); // This is a pure redirection + if (qmldir.hasTypeInfo()) + return QQmlImports::validVersion(); // A pure C++ module without plugin + } + errors->prepend(QQmlImports::moduleNotFoundError(uri, relevantVersion(uri, version))); + return QTypeRevision(); + } else { + // Verify that the qmldir content is valid for this version + version = matchingQmldirVersion(qmldir, uri, version, errors); + if (!version.isValid()) + return QTypeRevision(); + } + + Q_ASSERT(version.isValid()); + return version; +} + QTypeRevision QQmlImports::addLibraryImport( QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix, - QTypeRevision version, const QString &qmldirIdentifier, const QString &qmldirUrl, + QTypeRevision requestedVersion, const QString &qmldirIdentifier, const QString &qmldirUrl, ImportFlags flags, quint16 precedence, QList *errors) { Q_ASSERT(typeLoader); @@ -1143,64 +1188,73 @@ QTypeRevision QQmlImports::addLibraryImport( if (lcQmlImport().isDebugEnabled()) { qCDebug(lcQmlImport) << "addLibraryImport:" << qPrintable(baseUrl().toString()) - << uri << "version" << getVersionInfo(version) << "as" << prefix; + << uri << "version" << getVersionInfo(requestedVersion) << "as" << prefix; } QQmlImportNamespace *nameSpace = importNamespace(prefix); Q_ASSERT(nameSpace); + const bool noQmldir = qmldirIdentifier.isEmpty(); + const bool isIncomplete = (flags & QQmlImports::ImportIncomplete); + if (noQmldir || isIncomplete) { + QQmlImportInstance *inserted = addImportToNamespace( + nameSpace, uri, qmldirUrl, requestedVersion, + QV4::CompiledData::Import::ImportLibrary, errors, + precedence); + Q_ASSERT(inserted); + + if (noQmldir && !isIncomplete) { + // No need to wait for the qmldir to become available if we're not supposed to use it. + if (!QQmlMetaType::typeModule(uri, requestedVersion)) + QQmlMetaType::qmlRegisterModuleTypes(uri); + return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors); + } + + return validVersion(requestedVersion); + } + + QQmlTypeLoaderQmldirContent qmldir; + if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors)) { + // qmldir had errors. + return QTypeRevision(); + } + + // qmldir is remote and can't be loaded synchronously, but we may already know the module. + if (!qmldir.hasContent()) + return matchingModuleVersionForLibraryImport(uri, requestedVersion, errors); + + // Load the plugin before redirecting. Otherwise we might not find the qmldir we're looking for. + const QTypeRevision importedVersion = importExtension( + typeLoader, uri, requestedVersion, &qmldir, errors); + if (!importedVersion.isValid()) + return QTypeRevision(); + + QString resolvedUrl; + QString resolvedUri; + if (qmldir.hasRedirection()) { + resolvedUrl = redirectQmldirContent(typeLoader, &qmldir); + resolvedUri = qmldir.typeNamespace(); + if (QQmlImportInstance *existing + = nameSpace->findImportByLocation(resolvedUrl, requestedVersion)) { + return finalizeLibraryImport(uri, importedVersion, qmldir, existing, errors); + } + } else { + resolvedUrl = qmldirUrl; + resolvedUri = uri; + } + QQmlImportInstance *inserted = addImportToNamespace( - nameSpace, uri, qmldirUrl, version, + nameSpace, resolvedUri, resolvedUrl, requestedVersion, QV4::CompiledData::Import::ImportLibrary, errors, precedence); Q_ASSERT(inserted); - if (!(flags & QQmlImports::ImportIncomplete)) { - QQmlTypeLoaderQmldirContent qmldir; + registerBuiltinModuleTypes(qmldir, importedVersion); - if (!qmldirIdentifier.isEmpty()) { - if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors)) - return QTypeRevision(); + if (!inserted->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) + return QTypeRevision(); - if (qmldir.hasContent()) { - version = importExtension(typeLoader, uri, version, &qmldir, errors); - if (!version.isValid()) - return QTypeRevision(); - - const QString resolvedUrl = qmldir.hasRedirection() - ? redirectQmldirContent(typeLoader, &qmldir, inserted) - : qmldirUrl; - - registerBuiltinModuleTypes(qmldir, version); - - if (!inserted->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) - return QTypeRevision(); - } - } - - // Ensure that we are actually providing something - const QTypeRevision matchingVersion = QQmlMetaType::matchingModuleVersion(uri, version); - if (matchingVersion.isValid()) - return matchingVersion; - - if (inserted->qmlDirComponents.isEmpty() && inserted->qmlDirScripts.isEmpty()) { - if (qmldir.plugins().isEmpty()) { - if (!qmldir.imports().isEmpty()) - return validVersion(); // This is a pure redirection - if (qmldir.hasTypeInfo()) - return validVersion(); // A pure C++ module without plugin - } - errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version))); - return QTypeRevision(); - } else if (qmldir.hasContent()) { - // Verify that the qmldir content is valid for this version - version = matchingQmldirVersion(qmldir, uri, version, errors); - if (!version.isValid()) - return QTypeRevision(); - } - } - - return validVersion(version); + return finalizeLibraryImport(uri, importedVersion, qmldir, inserted, errors); } /*! @@ -1226,7 +1280,7 @@ QTypeRevision QQmlImports::addLibraryImport( */ QTypeRevision QQmlImports::addFileImport( QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix, - QTypeRevision version, ImportFlags flags, quint16 precedence, QString *localQmldir, + QTypeRevision requestedVersion, ImportFlags flags, quint16 precedence, QString *localQmldir, QList *errors) { Q_ASSERT(typeLoader); @@ -1235,7 +1289,7 @@ QTypeRevision QQmlImports::addFileImport( if (lcQmlImport().isDebugEnabled()) { qCDebug(lcQmlImport) << "addFileImport:" << qPrintable(baseUrl().toString()) - << uri << "version" << getVersionInfo(version) << "as" << prefix; + << uri << "version" << getVersionInfo(requestedVersion) << "as" << prefix; } if (uri.startsWith(Slash) || uri.startsWith(Colon)) { @@ -1329,53 +1383,85 @@ QTypeRevision QQmlImports::addFileImport( it != nameSpace->imports.constEnd(); ++it) { if ((*it)->url == url) { (*it)->implicitlyImported = true; - return validVersion(version); + return validVersion(requestedVersion); } } } - if (!(flags & QQmlImports::ImportIncomplete) && !qmldirIdentifier.isEmpty()) { - QQmlTypeLoaderQmldirContent qmldir; - if (!getQmldirContent(typeLoader, qmldirIdentifier, importUri, &qmldir, errors)) - return QTypeRevision(); + if ((flags & QQmlImports::ImportIncomplete) || qmldirIdentifier.isEmpty()) { + QQmlImportInstance *inserted = addImportToNamespace( + nameSpace, importUri, url, requestedVersion, QV4::CompiledData::Import::ImportFile, + errors, precedence); + Q_ASSERT(inserted); + return validVersion(requestedVersion); + } - if (qmldir.hasContent()) { - // Prefer the qmldir URI. Unless it doesn't exist. - const QString qmldirUri = qmldir.typeNamespace(); - if (!qmldirUri.isEmpty()) - importUri = qmldirUri; + QQmlTypeLoaderQmldirContent qmldir; + if (!getQmldirContent(typeLoader, qmldirIdentifier, importUri, &qmldir, errors)) + return QTypeRevision(); - QQmlImportInstance *inserted = addImportToNamespace( - nameSpace, importUri, url, version, QV4::CompiledData::Import::ImportFile, - errors, precedence); - Q_ASSERT(inserted); + if (!qmldir.hasContent()) { + QQmlImportInstance *inserted = addImportToNamespace( + nameSpace, importUri, url, requestedVersion, QV4::CompiledData::Import::ImportFile, + errors, precedence); + Q_ASSERT(inserted); + return validVersion(requestedVersion); + } - version = importExtension(typeLoader, importUri, version, &qmldir, errors); - if (!version.isValid()) - return QTypeRevision(); + // Prefer the qmldir URI. Unless it doesn't exist. + const QString qmldirUri = qmldir.typeNamespace(); + if (!qmldirUri.isEmpty()) + importUri = qmldirUri; - if (qmldir.hasRedirection()) - url = redirectQmldirContent(typeLoader, &qmldir, inserted); + // Load the plugin before redirecting. Otherwise we might not find the qmldir we're looking for. + const QTypeRevision importedVersion + = importExtension(typeLoader, importUri, requestedVersion, &qmldir, errors); + if (!importedVersion.isValid()) + return QTypeRevision(); - registerBuiltinModuleTypes(qmldir, version); - - if (!inserted->setQmldirContent(url, qmldir, nameSpace, errors)) - return QTypeRevision(); - - return validVersion(version); + QString resolvedUrl; + if (qmldir.hasRedirection()) { + resolvedUrl = redirectQmldirContent(typeLoader, &qmldir); + importUri = qmldir.typeNamespace(); + if (resolvedUrl != url) { + if (QQmlImportInstance *existing + = nameSpace->findImportByLocation(resolvedUrl, requestedVersion)) { + // We've alraedy seen this import. No need to add another entry. + return validVersion(existing->version); + } } + } else { + resolvedUrl = url; } QQmlImportInstance *inserted = addImportToNamespace( - nameSpace, importUri, url, version, QV4::CompiledData::Import::ImportFile, - errors, precedence); + nameSpace, importUri, resolvedUrl, requestedVersion, QV4::CompiledData::Import::ImportFile, + errors, precedence); Q_ASSERT(inserted); - return validVersion(version); + + registerBuiltinModuleTypes(qmldir, importedVersion); + + if (!inserted->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) + return QTypeRevision(); + + Q_ASSERT(importedVersion.isValid()); + return importedVersion; +} + +static QTypeRevision qmldirContentError(const QString &uri, QList *errors) +{ + if (errors->isEmpty()) { + QQmlError error; + error.setDescription(QQmlTypeLoader::tr("Cannot update qmldir content for '%1'").arg(uri)); + errors->prepend(error); + } + return QTypeRevision(); } QTypeRevision QQmlImports::updateQmldirContent( - QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix, - const QString &qmldirIdentifier, const QString &qmldirUrl, QList *errors) + QQmlTypeLoader *typeLoader, const QString &uri, QTypeRevision version, + const QString &prefix, const QString &qmldirIdentifier, const QString &qmldirUrl, + QList *errors) { Q_ASSERT(typeLoader); Q_ASSERT(errors); @@ -1387,48 +1473,61 @@ QTypeRevision QQmlImports::updateQmldirContent( QQmlImportNamespace *nameSpace = importNamespace(prefix); Q_ASSERT(nameSpace); - if (QQmlImportInstance *import = nameSpace->findImport(uri)) { - QQmlTypeLoaderQmldirContent qmldir; - if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors)) - return QTypeRevision(); + QQmlImportInstance *import = nameSpace->findImportByModuleUri(uri, version); + if (!import) + return qmldirContentError(uri, errors); - if (qmldir.hasContent()) { - QTypeRevision version = importExtension( - typeLoader, uri, import->version, &qmldir, errors); + QQmlTypeLoaderQmldirContent qmldir; + if (!getQmldirContent(typeLoader, qmldirIdentifier, uri, &qmldir, errors)) + return QTypeRevision(); + + if (!qmldir.hasContent()) + return qmldirContentError(uri, errors); + + // Load the plugin before redirecting. Otherwise we might not find the qmldir we're looking for. + version = importExtension(typeLoader, uri, import->version, &qmldir, errors); + if (!version.isValid()) + return QTypeRevision(); + + QString resolvedUrl; + if (qmldir.hasRedirection()) { + resolvedUrl = redirectQmldirContent(typeLoader, &qmldir); + if (resolvedUrl != import->url) { + if (QQmlImportInstance *existing + = nameSpace->findImportByLocation(resolvedUrl, import->version)) { + // We've re-discovered the same module via a different redirect. + // No need to import it again. + nameSpace->imports.removeOne(import); + delete import; + return validVersion(existing->version); + } + import->url = resolvedUrl; + } + import->uri = qmldir.typeNamespace(); + } else { + resolvedUrl = qmldirUrl; + } + + registerBuiltinModuleTypes(qmldir, version); + + if (import->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) { + if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) { + // The implicit import qmldir can be empty, and plugins have no extra versions + if (uri != QLatin1String(".") + && !QQmlMetaType::matchingModuleVersion(import->uri, version).isValid()) { + errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version))); + return QTypeRevision(); + } + } else { + // Verify that the qmldir content is valid for this version + version = matchingQmldirVersion(qmldir, import->uri, version, errors); if (!version.isValid()) return QTypeRevision(); - - const QString resolvedUrl = qmldir.hasRedirection() - ? redirectQmldirContent(typeLoader, &qmldir, import) - : qmldirUrl; - - registerBuiltinModuleTypes(qmldir, version); - - if (import->setQmldirContent(resolvedUrl, qmldir, nameSpace, errors)) { - if (import->qmlDirComponents.isEmpty() && import->qmlDirScripts.isEmpty()) { - // The implicit import qmldir can be empty, and plugins have no extra versions - if (uri != QLatin1String(".") && !QQmlMetaType::matchingModuleVersion(uri, version).isValid()) { - errors->prepend(moduleNotFoundError(uri, relevantVersion(uri, version))); - return QTypeRevision(); - } - } else { - // Verify that the qmldir content is valid for this version - version = matchingQmldirVersion(qmldir, uri, version, errors); - if (!version.isValid()) - return QTypeRevision(); - } - return validVersion(version); - } } + return validVersion(version); } - if (errors->isEmpty()) { - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("Cannot update qmldir content for '%1'").arg(uri)); - errors->prepend(error); - } - - return QTypeRevision(); + return qmldirContentError(uri, errors); } /*! diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index 193c650a2b..cad96eb81a 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -91,7 +91,10 @@ public: QList imports; - QQmlImportInstance *findImport(const QString &uri) const; + QQmlImportInstance *findImportByModuleUri( + const QString &moduleUri, QTypeRevision version) const; + QQmlImportInstance *findImportByLocation( + const QString &location, QTypeRevision version) const; bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, QTypeRevision *version_return, QQmlType* type_return, @@ -163,17 +166,19 @@ public: QTypeRevision addFileImport( QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix, - QTypeRevision version, ImportFlags flags, quint16 precedence, QString *localQmldir, - QList *errors); + QTypeRevision requestedVersion, ImportFlags flags, quint16 precedence, + QString *localQmldir, QList *errors); QTypeRevision addLibraryImport( QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix, - QTypeRevision version, const QString &qmldirIdentifier, const QString &qmldirUrl, - ImportFlags flags, quint16 precedence, QList *errors); + QTypeRevision requestedVersion, const QString &qmldirIdentifier, + const QString &qmldirUrl, ImportFlags flags, quint16 precedence, + QList *errors); QTypeRevision updateQmldirContent( - QQmlTypeLoader *typeLoader, const QString &uri, const QString &prefix, - const QString &qmldirIdentifier, const QString &qmldirUrl, QList *errors); + QQmlTypeLoader *typeLoader, const QString &uri, QTypeRevision version, + const QString &prefix, const QString &qmldirIdentifier, const QString &qmldirUrl, + QList *errors); void populateCache(QQmlTypeNameCache *cache) const; @@ -230,10 +235,6 @@ private: QQmlImportNamespace *findQualifiedNamespace(const QHashedStringRef &) const; - static QTypeRevision matchingQmldirVersion( - const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, - QTypeRevision version, QList *errors); - QTypeRevision importExtension( QQmlTypeLoader *typeLoader, const QString &uri, QTypeRevision version, const QQmlTypeLoaderQmldirContent *qmldir, QList *errors); @@ -242,8 +243,7 @@ private: const QQmlTypeLoaderQmldirContent &qmldir, QTypeRevision version); QString redirectQmldirContent( - QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir, - QQmlImportInstance *inserted); + QQmlTypeLoader *typeLoader, QQmlTypeLoaderQmldirContent *qmldir); bool getQmldirContent( QQmlTypeLoader *typeLoader, const QString &qmldirIdentifier, const QString &uri, diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 013902febf..89199c8652 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -831,7 +831,8 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer &da typeLoader()->setQmldirContent(qmldirIdentifier, data->content()); const QTypeRevision version = m_importCache->updateQmldirContent( - typeLoader(), import->uri, import->qualifier, qmldirIdentifier, qmldirUrl, errors); + typeLoader(), import->uri, import->version, import->qualifier, qmldirIdentifier, + qmldirUrl, errors); if (!version.isValid()) return false; diff --git a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt index 48a89a409c..39044bd3b2 100644 --- a/tests/auto/qml/qmlcppcodegen/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/CMakeLists.txt @@ -25,6 +25,8 @@ qt_internal_add_test(tst_qmlcppcodegen codegen_test_stringbuilderplugin confused_test_module confused_test_moduleplugin + with_subdir_test_module + with_subdir_test_moduleplugin DEFINES QT_NO_CAST_FROM_ASCII ) @@ -45,6 +47,8 @@ qt_internal_add_test(tst_qmlcppcodegen_interpreted codegen_test_stringbuilderplugin confused_test_module confused_test_moduleplugin + with_subdir_test_module + with_subdir_test_moduleplugin DEFINES QT_TEST_FORCE_INTERPRETER ) diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt index d2c3707ba8..dbf750c463 100644 --- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt @@ -2,6 +2,7 @@ # SPDX-License-Identifier: BSD-3-Clause add_subdirectory(Confused) +add_subdirectory(WithSubDir) set(cpp_sources ambiguous.h @@ -220,6 +221,7 @@ set(qml_files methods.qml modulePrefix.qml moveRegVoid.qml + multiRedirect.qml multiforeign.qml multipleCtors.qml namespaceWithEnum.qml diff --git a/tests/auto/qml/qmlcppcodegen/data/WithSubDir/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/WithSubDir/CMakeLists.txt new file mode 100644 index 0000000000..a887c39283 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/WithSubDir/CMakeLists.txt @@ -0,0 +1,23 @@ +qt_add_library(with_subdir_test_module STATIC) +qt_autogen_tools_initial_setup(with_subdir_test_module) + +qt_policy(SET QTP0001 NEW) +qt_policy(SET QTP0004 NEW) + +qt_add_qml_module(with_subdir_test_module + URI WithSubDir + VERSION 1.0 + QML_FILES + qml/GreenRect.qml + qml/MyScript.js + + # Hide it at compile time. Otherwise the "TestTypes" module may see it, + # but the "verify" module won't. + OUTPUT_DIRECTORY HiddenWithSubdir +) + +target_link_libraries(with_subdir_test_module + PRIVATE Qt6::Qml +) + +qt_autogen_tools_initial_setup(with_subdir_test_moduleplugin) diff --git a/tests/auto/qml/qmlcppcodegen/data/WithSubDir/qml/GreenRect.qml b/tests/auto/qml/qmlcppcodegen/data/WithSubDir/qml/GreenRect.qml new file mode 100644 index 0000000000..51ccd233ed --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/WithSubDir/qml/GreenRect.qml @@ -0,0 +1,6 @@ +import QtQml +import WithSubDir + +QtObject { + objectName: "green" +} diff --git a/tests/auto/qml/qmlcppcodegen/data/WithSubDir/qml/MyScript.js b/tests/auto/qml/qmlcppcodegen/data/WithSubDir/qml/MyScript.js new file mode 100644 index 0000000000..fd59bc74fd --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/WithSubDir/qml/MyScript.js @@ -0,0 +1,2 @@ +.pragma library +function hello() { console.log("World") } diff --git a/tests/auto/qml/qmlcppcodegen/data/multiRedirect.qml b/tests/auto/qml/qmlcppcodegen/data/multiRedirect.qml new file mode 100644 index 0000000000..dab692dce0 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/multiRedirect.qml @@ -0,0 +1,7 @@ +import QtQml +import WithSubDir + +QtObject { + property GreenRect g: GreenRect {} + objectName: g.objectName +} diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index 8216bf0125..d3f917e280 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -187,6 +187,7 @@ private slots: void multiDirectory(); void multiForeign(); void multiLookup(); + void multiRedirect(); void multipleCtors(); void namespaceWithEnum(); void noBuiltinsImport(); @@ -3804,6 +3805,18 @@ void tst_QmlCppCodegen::multiLookup() QCOMPARE(quitSpy.size(), 1); } +void tst_QmlCppCodegen::multiRedirect() +{ + QQmlEngine engine; + QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/multiRedirect.qml"_s)); + QVERIFY2(!component.isError(), qPrintable(component.errorString())); + + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + + QCOMPARE(object->objectName(), u"green"_s); +} + void tst_QmlCppCodegen::multipleCtors() { QQmlEngine engine;