From aea732d607dbcc7ba2c86244ca1ab9086bb28ca6 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 31 Mar 2022 11:26:09 +0200 Subject: [PATCH] Avoid copying QQmlJSScope The factory should populate the pre-existing scope rather than copy it into place. This way we can detect inheritance cycles involving the pre-existing scope. However, now we may detect the inheritance cycles earlier, when resolving the types inside the lazy loading. We have the right pointers available there now, after all. Therefore, add a way to propagate base type errors out of the factory. When clearing the base type, we can now give a reason for that. When checking the inheritance cycles we retrieve that reason and log it. We also remove the special casing of the ScopeType property of QQmlJSScope. There is no real reason to set it in the ctor. As we delay the population of QQmlJSScope now, we have to set it later. Pick-to: 6.2 6.3 Task-number: QTBUG-102153 Change-Id: I49cf6e20f59fbdb6ed98a82040b3b159676f5975 Reviewed-by: Andrei Golubev Reviewed-by: Qt CI Bot Reviewed-by: Fabian Kosmale --- src/qmlcompiler/qdeferredpointer_p.h | 27 ++++-- src/qmlcompiler/qqmljscompiler.cpp | 3 +- src/qmlcompiler/qqmljsimportvisitor.cpp | 96 +++++++++++-------- src/qmlcompiler/qqmljsimportvisitor_p.h | 12 ++- src/qmlcompiler/qqmljslinter.cpp | 3 +- src/qmlcompiler/qqmljsscope.cpp | 77 +++++++++------ src/qmlcompiler/qqmljsscope_p.h | 31 ++++-- src/qmlcompiler/qqmljstypereader.cpp | 27 ++---- src/qmlcompiler/qqmljstypereader_p.h | 2 +- tests/auto/qml/qmllint/data/cycleHead.qml | 3 +- tests/auto/qml/qmllint/tst_qmllint.cpp | 2 +- .../auto/qml/qqmljsscope/tst_qqmljsscope.cpp | 3 +- tools/qmltc/qmltcvisitor.cpp | 5 +- 13 files changed, 179 insertions(+), 112 deletions(-) diff --git a/src/qmlcompiler/qdeferredpointer_p.h b/src/qmlcompiler/qdeferredpointer_p.h index e19676d7ea..8a72c7a913 100644 --- a/src/qmlcompiler/qdeferredpointer_p.h +++ b/src/qmlcompiler/qdeferredpointer_p.h @@ -47,16 +47,25 @@ QT_BEGIN_NAMESPACE template -class QDeferredFactory -{ -public: - T create() const; - bool isValid() const; -}; +class QDeferredSharedPointer; template class QDeferredWeakPointer; +template +class QDeferredFactory +{ +public: + bool isValid() const; + +private: + friend class QDeferredSharedPointer; + friend class QDeferredWeakPointer; + friend class QDeferredSharedPointer; + friend class QDeferredWeakPointer; + void populate(const QSharedPointer &) const; +}; + template class QDeferredSharedPointer { @@ -179,7 +188,7 @@ private: if (Factory *f = factory()) { Factory localFactory; std::swap(localFactory, *f); // Swap before executing, to avoid recursion - const_cast &>(*m_data) = localFactory.create(); + localFactory.populate(m_data.template constCast>()); } } @@ -245,8 +254,8 @@ private: if (factory->isValid()) { Factory localFactory; std::swap(localFactory, *factory); // Swap before executing, to avoid recursion - const_cast &>(*(m_data.toStrongRef())) - = localFactory.create(); + localFactory.populate( + m_data.toStrongRef().template constCast>()); } } } diff --git a/src/qmlcompiler/qqmljscompiler.cpp b/src/qmlcompiler/qqmljscompiler.cpp index c765a1c94e..14811c2aa2 100644 --- a/src/qmlcompiler/qqmljscompiler.cpp +++ b/src/qmlcompiler/qqmljscompiler.cpp @@ -645,7 +645,8 @@ void QQmlJSAotCompiler::setDocument( m_logger->setCode(irDocument->code); m_unitGenerator = &irDocument->jsGenerator; m_entireSourceCodeLines = irDocument->code.split(u'\n'); - QQmlJSImportVisitor visitor(m_importer, m_logger, + QQmlJSScope::Ptr target = QQmlJSScope::create(); + QQmlJSImportVisitor visitor(target, m_importer, m_logger, resourcePathInfo.canonicalPath() + u'/', m_qmldirFiles); m_typeResolver.init(&visitor, irDocument->program); diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp index 4f8a916940..5e1318fca2 100644 --- a/src/qmlcompiler/qqmljsimportvisitor.cpp +++ b/src/qmlcompiler/qqmljsimportvisitor.cpp @@ -74,15 +74,17 @@ inline QString getScopeName(const QQmlJSScope::ConstPtr &scope, QQmlJSScope::Sco return scope->baseTypeName(); } -QQmlJSImportVisitor::QQmlJSImportVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger, - const QString &implicitImportDirectory, - const QStringList &qmldirFiles) +QQmlJSImportVisitor::QQmlJSImportVisitor( + const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, QQmlJSLogger *logger, + const QString &implicitImportDirectory, const QStringList &qmldirFiles) : m_implicitImportDirectory(implicitImportDirectory), m_qmldirFiles(qmldirFiles), - m_currentScope(QQmlJSScope::create(QQmlJSScope::JSFunctionScope)), + m_currentScope(QQmlJSScope::create()), + m_exportedRootScope(target), m_importer(importer), m_logger(logger) { + m_currentScope->setScopeType(QQmlJSScope::JSFunctionScope); Q_ASSERT(logger); // must be valid m_globalScope = m_currentScope; @@ -116,10 +118,10 @@ QQmlJSImportVisitor::QQmlJSImportVisitor(QQmlJSImporter *importer, QQmlJSLogger QQmlJSImportVisitor::~QQmlJSImportVisitor() = default; -void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type, const QString &name, - const QQmlJS::SourceLocation &location) +void QQmlJSImportVisitor::populateCurrentScope( + QQmlJSScope::ScopeType type, const QString &name, const QQmlJS::SourceLocation &location) { - m_currentScope = QQmlJSScope::create(type, m_currentScope); + m_currentScope->setScopeType(type); setScopeName(m_currentScope, type, name); m_currentScope->setIsComposite(true); m_currentScope->setFilePath(QFileInfo(m_logger->fileName()).absoluteFilePath()); @@ -127,6 +129,22 @@ void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type, const QS m_scopesByIrLocation.insert({ location.startLine, location.startColumn }, m_currentScope); } +void QQmlJSImportVisitor::enterRootScope(QQmlJSScope::ScopeType type, const QString &name, const QQmlJS::SourceLocation &location) +{ + QQmlJSScope::reparent(m_currentScope, m_exportedRootScope); + m_currentScope = m_exportedRootScope; + populateCurrentScope(type, name, location); +} + +void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type, const QString &name, + const QQmlJS::SourceLocation &location) +{ + QQmlJSScope::Ptr newScope = QQmlJSScope::create(); + QQmlJSScope::reparent(m_currentScope, newScope); + m_currentScope = std::move(newScope); + populateCurrentScope(type, name, location); +} + bool QQmlJSImportVisitor::enterEnvironmentNonUnique(QQmlJSScope::ScopeType type, const QString &name, const QQmlJS::SourceLocation &location) @@ -295,11 +313,6 @@ void QQmlJSImportVisitor::resolveAliasesAndIds() } } -QQmlJSScope::Ptr QQmlJSImportVisitor::result() const -{ - return m_exportedRootScope; -} - QString QQmlJSImportVisitor::implicitImportDirectory( const QString &localFile, QQmlJSResourceFileMapper *mapper) { @@ -1048,28 +1061,34 @@ void QQmlJSImportVisitor::breakInheritanceCycles(const QQmlJSScope::Ptr &origina if (scopes.contains(scope)) { QString inheritenceCycle; for (const auto &seen : qAsConst(scopes)) { - if (!inheritenceCycle.isEmpty()) - inheritenceCycle.append(QLatin1String(" -> ")); inheritenceCycle.append(seen->baseTypeName()); + inheritenceCycle.append(QLatin1String(" -> ")); } + inheritenceCycle.append(scopes.first()->baseTypeName()); - m_logger->log(QStringLiteral("%1 is part of an inheritance cycle: %2") - .arg(scope->internalName()) - .arg(inheritenceCycle), - Log_InheritanceCycle, scope->sourceLocation()); + const QString message = QStringLiteral("%1 is part of an inheritance cycle: %2") + .arg(scope->internalName(), inheritenceCycle); + m_logger->log(message, Log_InheritanceCycle, scope->sourceLocation()); originalScope->clearBaseType(); + originalScope->setBaseTypeError(message); break; } scopes.append(scope); const auto newScope = scope->baseType(); - if (newScope.isNull() && !scope->baseTypeName().isEmpty()) { - m_logger->log(scope->baseTypeName() - + QStringLiteral(" was not found. Did you add all import paths?"), - Log_Import, scope->sourceLocation(), true, true, - QQmlJSUtils::didYouMean(scope->baseTypeName(), m_rootScopeImports.keys(), - scope->sourceLocation())); + if (newScope.isNull()) { + const QString error = scope->baseTypeError(); + const QString name = scope->baseTypeName(); + if (!error.isEmpty()) { + m_logger->log(error, Log_Import, scope->sourceLocation(), true, true); + } else if (!name.isEmpty()) { + m_logger->log( + name + QStringLiteral(" was not found. Did you add all import paths?"), + Log_Import, scope->sourceLocation(), true, true, + QQmlJSUtils::didYouMean(scope->baseTypeName(), m_rootScopeImports.keys(), + scope->sourceLocation())); + } } scope = newScope; @@ -1206,10 +1225,11 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition) Q_ASSERT(!superType.isEmpty()); if (superType.front().isUpper()) { - enterEnvironment(QQmlJSScope::QMLScope, superType, definition->firstSourceLocation()); - if (!m_exportedRootScope) { - m_exportedRootScope = m_currentScope; - m_exportedRootScope->setIsSingleton(m_rootIsSingleton); + if (rootScopeIsValid()) { + enterEnvironment(QQmlJSScope::QMLScope, superType, definition->firstSourceLocation()); + } else { + enterRootScope(QQmlJSScope::QMLScope, superType, definition->firstSourceLocation()); + m_currentScope->setIsSingleton(m_rootIsSingleton); } const QTypeRevision revision = QQmlJSScope::resolveTypes( @@ -1223,7 +1243,7 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition) } else { enterEnvironmentNonUnique(QQmlJSScope::GroupedPropertyScope, superType, definition->firstSourceLocation()); - Q_ASSERT(m_exportedRootScope); + Q_ASSERT(rootScopeIsValid()); QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports, &m_usedTypes); } @@ -2118,7 +2138,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob) bool QQmlJSImportVisitor::visit(ExportDeclaration *) { - Q_ASSERT(!m_exportedRootScope.isNull()); + Q_ASSERT(rootScopeIsValid()); Q_ASSERT(m_exportedRootScope != m_globalScope); Q_ASSERT(m_currentScope == m_globalScope); m_currentScope = m_exportedRootScope; @@ -2127,18 +2147,17 @@ bool QQmlJSImportVisitor::visit(ExportDeclaration *) void QQmlJSImportVisitor::endVisit(ExportDeclaration *) { - Q_ASSERT(!m_exportedRootScope.isNull()); + Q_ASSERT(rootScopeIsValid()); m_currentScope = m_exportedRootScope->parentScope(); Q_ASSERT(m_currentScope == m_globalScope); } bool QQmlJSImportVisitor::visit(ESModule *module) { - enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("module"), - module->firstSourceLocation()); - Q_ASSERT(m_exportedRootScope.isNull()); - m_exportedRootScope = m_currentScope; - m_exportedRootScope->setIsScript(true); + Q_ASSERT(!rootScopeIsValid()); + enterRootScope( + QQmlJSScope::JSLexicalScope, QStringLiteral("module"), module->firstSourceLocation()); + m_currentScope->setIsScript(true); importBaseModules(); leaveEnvironment(); return true; @@ -2152,9 +2171,10 @@ void QQmlJSImportVisitor::endVisit(ESModule *) bool QQmlJSImportVisitor::visit(Program *) { Q_ASSERT(m_globalScope == m_currentScope); - Q_ASSERT(m_exportedRootScope.isNull()); - m_exportedRootScope = m_currentScope; + Q_ASSERT(!rootScopeIsValid()); + *m_exportedRootScope = std::move(*QQmlJSScope::clone(m_currentScope)); m_exportedRootScope->setIsScript(true); + m_currentScope = m_exportedRootScope; importBaseModules(); return true; } diff --git a/src/qmlcompiler/qqmljsimportvisitor_p.h b/src/qmlcompiler/qqmljsimportvisitor_p.h index 69714ced36..e9347a49f0 100644 --- a/src/qmlcompiler/qqmljsimportvisitor_p.h +++ b/src/qmlcompiler/qqmljsimportvisitor_p.h @@ -61,12 +61,13 @@ struct QQmlJSResourceFileMapper; class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSImportVisitor : public QQmlJS::AST::Visitor { public: - QQmlJSImportVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger, + QQmlJSImportVisitor(const QQmlJSScope::Ptr &target, + QQmlJSImporter *importer, QQmlJSLogger *logger, const QString &implicitImportDirectory, const QStringList &qmldirFiles = QStringList()); ~QQmlJSImportVisitor(); - QQmlJSScope::Ptr result() const; + QQmlJSScope::Ptr result() const { return m_exportedRootScope; } QQmlJSLogger *logger() { return m_logger; } @@ -161,7 +162,7 @@ protected: QStringList m_qmldirFiles; QQmlJSScope::Ptr m_currentScope; QQmlJSScope::Ptr m_savedBindingOuterScope; - QQmlJSScope::Ptr m_exportedRootScope; + const QQmlJSScope::Ptr m_exportedRootScope; QQmlJSScope::ConstPtr m_globalScope; QQmlJSScopesById m_scopesById; QQmlJSImporter::ImportedTypes m_rootScopeImports; @@ -226,6 +227,7 @@ protected: void breakInheritanceCycles(const QQmlJSScope::Ptr &scope); void checkDeprecation(const QQmlJSScope::ConstPtr &scope); void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope); + bool rootScopeIsValid() const { return m_exportedRootScope->sourceLocation().isValid(); } QQmlJSLogger *m_logger; enum class LiteralOrScriptParseResult { Invalid, Script, Literal }; @@ -300,6 +302,10 @@ private: const QString &what, const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation()); void addImportWithLocation(const QString &name, const QQmlJS::SourceLocation &loc); + void populateCurrentScope(QQmlJSScope::ScopeType type, const QString &name, + const QQmlJS::SourceLocation &location); + void enterRootScope(QQmlJSScope::ScopeType type, const QString &name, + const QQmlJS::SourceLocation &location); }; QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmljslinter.cpp b/src/qmlcompiler/qqmljslinter.cpp index 83145e8b3e..e8ca914b04 100644 --- a/src/qmlcompiler/qqmljslinter.cpp +++ b/src/qmlcompiler/qqmljslinter.cpp @@ -311,7 +311,8 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename, m_logger->setFileName(m_useAbsolutePath ? info.absoluteFilePath() : filename); m_logger->setCode(code); m_logger->setSilent(silent || json); - QQmlJSImportVisitor v { &m_importer, m_logger.get(), + QQmlJSScope::Ptr target = QQmlJSScope::create(); + QQmlJSImportVisitor v { target, &m_importer, m_logger.get(), QQmlJSImportVisitor::implicitImportDirectory( m_logger->fileName(), m_importer.resourceFileMapper()), qmldirFiles }; diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp index a13fa32790..5b23b247b3 100644 --- a/src/qmlcompiler/qqmljsscope.cpp +++ b/src/qmlcompiler/qqmljsscope.cpp @@ -92,23 +92,23 @@ static bool searchBaseAndExtensionTypes(QQmlJSScopePtr type, const Action &check return false; } -QQmlJSScope::QQmlJSScope(ScopeType type, const QQmlJSScope::Ptr &parentScope) - : m_parentScope(parentScope), m_scopeType(type) {} - -QQmlJSScope::Ptr QQmlJSScope::create(ScopeType type, const QQmlJSScope::Ptr &parentScope) +void QQmlJSScope::reparent(const QQmlJSScope::Ptr &parentScope, const QQmlJSScope::Ptr &childScope) { - QSharedPointer childScope(new QQmlJSScope{type, parentScope}); + if (const QQmlJSScope::Ptr parent = childScope->m_parentScope.toStrongRef()) + parent->m_childScopes.removeOne(childScope); if (parentScope) - parentScope->m_childScopes.push_back(childScope); - return childScope; + parentScope->m_childScopes.append(childScope); + childScope->m_parentScope = parentScope; } QQmlJSScope::Ptr QQmlJSScope::clone(const ConstPtr &origin) { if (origin.isNull()) return QQmlJSScope::Ptr(); - QQmlJSScope::Ptr cloned = create(origin->m_scopeType, origin->m_parentScope); + QQmlJSScope::Ptr cloned = create(); *cloned = *origin; + if (QQmlJSScope::Ptr parent = cloned->parentScope()) + parent->m_childScopes.append(cloned); return cloned; } @@ -382,8 +382,9 @@ QTypeRevision QQmlJSScope::resolveType( const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &context, QSet *usedTypes) { - const auto baseType = findType(self->m_baseTypeName, context, usedTypes); - if (!self->m_baseType.scope && !self->m_baseTypeName.isEmpty()) + const QString baseTypeName = self->baseTypeName(); + const auto baseType = findType(baseTypeName, context, usedTypes); + if (!self->m_baseType.scope && !baseTypeName.isEmpty()) self->m_baseType = { baseType.scope, baseType.revision }; if (!self->m_attachedType && !self->m_attachedTypeName.isEmpty()) @@ -447,7 +448,7 @@ void QQmlJSScope::updateChildScope( const auto propertyIt = type->m_properties.find(childScope->internalName()); if (propertyIt != type->m_properties.end()) { childScope->m_baseType.scope = QQmlJSScope::ConstPtr(propertyIt->type()); - childScope->m_baseTypeName = propertyIt->typeName(); + childScope->setBaseTypeName(propertyIt->typeName()); return true; } return false; @@ -457,7 +458,7 @@ void QQmlJSScope::updateChildScope( if (const auto attachedBase = findType( childScope->internalName(), contextualTypes, usedTypes).scope) { childScope->m_baseType.scope = attachedBase->attachedType(); - childScope->m_baseTypeName = attachedBase->attachedTypeName(); + childScope->setBaseTypeName(attachedBase->attachedTypeName()); } break; default: @@ -508,8 +509,10 @@ void QQmlJSScope::resolveEnums(const QQmlJSScope::Ptr &self, const QQmlJSScope:: if (it->type()) continue; Q_ASSERT(intType); // We need an "int" type to resolve enums - auto enumScope = QQmlJSScope::create(EnumScope, self); - enumScope->m_baseTypeName = QStringLiteral("int"); + QQmlJSScope::Ptr enumScope = QQmlJSScope::create(); + reparent(self, enumScope); + enumScope->m_scopeType = EnumScope; + enumScope->setBaseTypeName(QStringLiteral("int")); enumScope->m_baseType.scope = intType; enumScope->m_semantics = AccessSemantics::Value; enumScope->m_internalName = self->internalName() + QStringLiteral("::") + it->name(); @@ -761,6 +764,28 @@ bool QQmlJSScope::isNameDeferred(const QString &name) const return isDeferred; } +void QQmlJSScope::setBaseTypeName(const QString &baseTypeName) +{ + m_flags.setFlag(HasBaseTypeError, false); + m_baseTypeNameOrError = baseTypeName; +} + +QString QQmlJSScope::baseTypeName() const +{ + return m_flags.testFlag(HasBaseTypeError) ? QString() : m_baseTypeNameOrError; +} + +void QQmlJSScope::setBaseTypeError(const QString &baseTypeError) +{ + m_flags.setFlag(HasBaseTypeError); + m_baseTypeNameOrError = baseTypeError; +} + +QString QQmlJSScope::baseTypeError() const +{ + return m_flags.testFlag(HasBaseTypeError) ? m_baseTypeNameOrError : QString(); +} + QString QQmlJSScope::attachedTypeName() const { QString name; @@ -792,7 +817,7 @@ bool QQmlJSScope::isResolved() const const bool nameIsEmpty = (m_scopeType == ScopeType::AttachedPropertyScope || m_scopeType == ScopeType::GroupedPropertyScope) ? m_internalName.isEmpty() - : m_baseTypeName.isEmpty(); + : m_baseTypeNameOrError.isEmpty(); if (nameIsEmpty) return true; if (m_baseType.scope.isNull()) @@ -865,31 +890,29 @@ bool QQmlJSScope::Export::isValid() const return m_version.isValid() || !m_package.isEmpty() || !m_type.isEmpty(); } -QQmlJSScope QDeferredFactory::create() const +void QDeferredFactory::populate(const QSharedPointer &scope) const { QQmlJSTypeReader typeReader(m_importer, m_filePath); - QQmlJSScope::Ptr result = typeReader(); + typeReader(scope); m_importer->m_globalWarnings.append(typeReader.errors()); - result->setInternalName(internalName()); - QQmlJSScope::resolveEnums(result, m_importer->builtinInternalNames().value(u"int"_qs).scope); + scope->setInternalName(internalName()); + QQmlJSScope::resolveEnums(scope, m_importer->builtinInternalNames().value(u"int"_qs).scope); - if (m_isSingleton && !result->isSingleton()) { + if (m_isSingleton && !scope->isSingleton()) { m_importer->m_globalWarnings.append( { QStringLiteral( "Type %1 declared as singleton in qmldir but missing pragma Singleton") - .arg(result->internalName()), + .arg(scope->internalName()), QtCriticalMsg, QQmlJS::SourceLocation() }); - result->setIsSingleton(true); - } else if (!m_isSingleton && result->isSingleton()) { + scope->setIsSingleton(true); + } else if (!m_isSingleton && scope->isSingleton()) { m_importer->m_globalWarnings.append( { QStringLiteral("Type %1 not declared as singleton in qmldir " "but using pragma Singleton") - .arg(result->internalName()), + .arg(scope->internalName()), QtCriticalMsg, QQmlJS::SourceLocation() }); - result->setIsSingleton(false); + scope->setIsSingleton(false); } - - return std::move(*result); } bool QQmlJSScope::canAssign(const QQmlJSScope::ConstPtr &derived) const diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h index da670f0c71..f68c5d0596 100644 --- a/src/qmlcompiler/qqmljsscope_p.h +++ b/src/qmlcompiler/qqmljsscope_p.h @@ -95,7 +95,8 @@ public: CustomParser = 0x10, Array = 0x20, InlineComponent = 0x40, - WrappedInImplicitComponent = 0x80 + WrappedInImplicitComponent = 0x80, + HasBaseTypeError = 0x100 }; Q_DECLARE_FLAGS(Flags, Flag) Q_FLAGS(Flags); @@ -241,8 +242,7 @@ public: UnnamedPropertyTarget // default property bindings, where property name is unspecified }; - static QQmlJSScope::Ptr create(ScopeType type = QQmlJSScope::QMLScope, - const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr()); + static QQmlJSScope::Ptr create() { return QSharedPointer(new QQmlJSScope); } static QQmlJSScope::Ptr clone(const QQmlJSScope::ConstPtr &origin); static QQmlJSScope::ConstPtr findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope); @@ -256,6 +256,8 @@ public: return QQmlJSScope::WeakConstPtr(m_parentScope).toStrongRef(); } + static void reparent(const QQmlJSScope::Ptr &parentScope, const QQmlJSScope::Ptr &childScope); + void insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier); // inserts property as qml identifier as well as the corresponding @@ -264,6 +266,7 @@ public: bool isIdInCurrentScope(const QString &id) const; ScopeType scopeType() const { return m_scopeType; } + void setScopeType(ScopeType type) { m_scopeType = type; } void addOwnMethod(const QQmlJSMetaMethod &method) { m_methods.insert(method.methodName(), method); } QMultiHash ownMethods() const { return m_methods; } @@ -322,11 +325,15 @@ public: // If isComposite(), this is the QML/JS name of the prototype. Otherwise it's the // relevant base class (in the hierarchy starting from QObject) of a C++ type. - void setBaseTypeName(const QString &baseTypeName) { m_baseTypeName = baseTypeName; } - QString baseTypeName() const { return m_baseTypeName; } + void setBaseTypeName(const QString &baseTypeName); + QString baseTypeName() const; + QQmlJSScope::ConstPtr baseType() const { return m_baseType.scope; } QTypeRevision baseTypeRevision() const { return m_baseType.revision; } + void clearBaseType() { m_baseType = {}; } + void setBaseTypeError(const QString &baseTypeError); + QString baseTypeError() const; void addOwnProperty(const QQmlJSMetaProperty &prop) { m_properties.insert(prop.propertyName(), prop); } QHash ownProperties() const { return m_properties; } @@ -552,7 +559,7 @@ public: }; private: - QQmlJSScope(ScopeType type, const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr()); + QQmlJSScope() = default; QQmlJSScope(const QQmlJSScope &) = default; QQmlJSScope &operator=(const QQmlJSScope &) = default; @@ -587,7 +594,7 @@ private: QString m_filePath; QString m_internalName; - QString m_baseTypeName; + QString m_baseTypeNameOrError; // We only need the revision for the base type as inheritance is // the only relation between two types where the revisions matter. @@ -627,8 +634,6 @@ public: m_filePath(filePath), m_importer(importer) {} - QQmlJSScope create() const; - bool isValid() const { return !m_filePath.isEmpty() && m_importer != nullptr; @@ -645,6 +650,14 @@ public: } private: + friend class QDeferredSharedPointer; + friend class QDeferredSharedPointer; + friend class QDeferredWeakPointer; + friend class QDeferredWeakPointer; + + // Should only be called when lazy-loading the type in a deferred pointer. + void populate(const QSharedPointer &scope) const; + QString m_filePath; QQmlJSImporter *m_importer = nullptr; bool m_isSingleton = false; diff --git a/src/qmlcompiler/qqmljstypereader.cpp b/src/qmlcompiler/qqmljstypereader.cpp index ae3c0becfe..753fbe12f8 100644 --- a/src/qmlcompiler/qqmljstypereader.cpp +++ b/src/qmlcompiler/qqmljstypereader.cpp @@ -40,13 +40,13 @@ QT_BEGIN_NAMESPACE -QQmlJSScope::Ptr QQmlJSTypeReader::operator()() +bool QQmlJSTypeReader::operator ()(const QSharedPointer &scope) { using namespace QQmlJS::AST; const QFileInfo info { m_file }; - QString baseName = info.baseName(); - const QString scopeName = baseName.endsWith(QStringLiteral(".ui")) ? baseName.chopped(3) - : baseName; + const QString baseName = info.baseName(); + scope->setInternalName(baseName.endsWith(QStringLiteral(".ui")) ? baseName.chopped(3) + : baseName); QQmlJS::Engine engine; QQmlJS::Lexer lexer(&engine); @@ -55,16 +55,10 @@ QQmlJSScope::Ptr QQmlJSTypeReader::operator()() const bool isESModule = lowerSuffix == QLatin1String("mjs"); const bool isJavaScript = isESModule || lowerSuffix == QLatin1String("js"); - auto errorResult = [&](){ - QQmlJSScope::Ptr result = QQmlJSScope::create( - isJavaScript ? QQmlJSScope::JSLexicalScope : QQmlJSScope::QMLScope); - result->setInternalName(scopeName); - return result; - }; QFile file(m_file); if (!file.open(QFile::ReadOnly)) - return errorResult(); + return false; QString code = QString::fromUtf8(file.readAll()); file.close(); @@ -76,11 +70,11 @@ QQmlJSScope::Ptr QQmlJSTypeReader::operator()() : parser.parseProgram()) : parser.parse(); if (!success) - return errorResult(); + return false; QQmlJS::AST::Node *rootNode = parser.rootNode(); if (!rootNode) - return errorResult(); + return false; QQmlJSLogger logger; logger.setFileName(m_file); @@ -88,13 +82,10 @@ QQmlJSScope::Ptr QQmlJSTypeReader::operator()() logger.setSilent(true); QQmlJSImportVisitor membersVisitor( - m_importer, &logger, + scope, m_importer, &logger, QQmlJSImportVisitor::implicitImportDirectory(m_file, m_importer->resourceFileMapper())); rootNode->accept(&membersVisitor); - auto result = membersVisitor.result(); - Q_ASSERT(result); - result->setInternalName(scopeName); - return result; + return true; } QT_END_NAMESPACE diff --git a/src/qmlcompiler/qqmljstypereader_p.h b/src/qmlcompiler/qqmljstypereader_p.h index 4dcdc3dc7f..f4dd8ceb15 100644 --- a/src/qmlcompiler/qqmljstypereader_p.h +++ b/src/qmlcompiler/qqmljstypereader_p.h @@ -59,7 +59,7 @@ public: , m_file(file) {} - QQmlJSScope::Ptr operator()(); + bool operator()(const QSharedPointer &scope); QList errors() const { return m_errors; } private: diff --git a/tests/auto/qml/qmllint/data/cycleHead.qml b/tests/auto/qml/qmllint/data/cycleHead.qml index 3cc5e9acdf..080eab381f 100644 --- a/tests/auto/qml/qmllint/data/cycleHead.qml +++ b/tests/auto/qml/qmllint/data/cycleHead.qml @@ -2,5 +2,6 @@ import Cycle import QtQuick Item { - MenuItem {} + property MenuItem item: a + MenuItem { id: a } } diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index efe1dd18c9..6ebc069e22 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -483,7 +483,7 @@ void TestQmllint::dirtyQmlCode_data() QTest::newRow("inheritanceCycle") << QStringLiteral("Cycle1.qml") << Result { { Message { - QStringLiteral("Cycle2 is part of an inheritance cycle: Cycle2 -> Cycle3 " + QStringLiteral("Cycle1 is part of an inheritance cycle: Cycle2 -> Cycle3 " "-> Cycle1 -> Cycle2"), 2, 1 } } }; QTest::newRow("badQmldirImportAndDepend") diff --git a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp index ba75ea7e2d..ad4c29143b 100644 --- a/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp +++ b/tests/auto/qml/qqmljsscope/tst_qqmljsscope.cpp @@ -84,7 +84,8 @@ class tst_qqmljsscope : public QQmlDataTest logger.setFileName(url); logger.setCode(sourceCode); logger.setSilent(true); - QQmlJSImportVisitor visitor(&m_importer, &logger, dataDirectory()); + QQmlJSScope::Ptr target = QQmlJSScope::create(); + QQmlJSImportVisitor visitor(target, &m_importer, &logger, dataDirectory()); QQmlJSTypeResolver typeResolver { &m_importer }; typeResolver.init(&visitor, document.program); return visitor.result(); diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp index 213acb89f6..ba85bd8f4f 100644 --- a/tools/qmltc/qmltcvisitor.cpp +++ b/tools/qmltc/qmltcvisitor.cpp @@ -64,7 +64,8 @@ static bool isOrUnderComponent(QQmlJSScope::ConstPtr type) QmltcVisitor::QmltcVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger, const QString &implicitImportDirectory, const QStringList &qmldirFiles) - : QQmlJSImportVisitor(importer, logger, implicitImportDirectory, qmldirFiles) + : QQmlJSImportVisitor( + QQmlJSScope::create(), importer, logger, implicitImportDirectory, qmldirFiles) { m_qmlTypeNames.append(QFileInfo(logger->fileName()).baseName()); // put document root } @@ -267,7 +268,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiInlineComponent *component) void QmltcVisitor::endVisit(QQmlJS::AST::UiProgram *program) { QQmlJSImportVisitor::endVisit(program); - if (!m_exportedRootScope) // in case we failed badly + if (!rootScopeIsValid()) // in case we failed badly return; QHash> bindings;