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>
This commit is contained in:
Sami Shalayel 2022-05-19 10:35:20 +02:00
parent 6e2d70f2b8
commit 69cd8c2779
6 changed files with 173 additions and 1 deletions

View File

@ -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

View File

@ -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;

View File

@ -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());

View File

@ -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>;

View File

@ -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
}
}
}
}

View File

@ -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"