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 <andrei.golubev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2022-03-31 11:26:09 +02:00
parent 6eaef57cd1
commit aea732d607
13 changed files with 179 additions and 112 deletions

View File

@ -47,16 +47,25 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
template<typename T> template<typename T>
class QDeferredFactory class QDeferredSharedPointer;
{
public:
T create() const;
bool isValid() const;
};
template<typename T> template<typename T>
class QDeferredWeakPointer; class QDeferredWeakPointer;
template<typename T>
class QDeferredFactory
{
public:
bool isValid() const;
private:
friend class QDeferredSharedPointer<const T>;
friend class QDeferredWeakPointer<const T>;
friend class QDeferredSharedPointer<T>;
friend class QDeferredWeakPointer<T>;
void populate(const QSharedPointer<T> &) const;
};
template<typename T> template<typename T>
class QDeferredSharedPointer class QDeferredSharedPointer
{ {
@ -179,7 +188,7 @@ private:
if (Factory *f = factory()) { if (Factory *f = factory()) {
Factory localFactory; Factory localFactory;
std::swap(localFactory, *f); // Swap before executing, to avoid recursion std::swap(localFactory, *f); // Swap before executing, to avoid recursion
const_cast<std::remove_const_t<T> &>(*m_data) = localFactory.create(); localFactory.populate(m_data.template constCast<std::remove_const_t<T>>());
} }
} }
@ -245,8 +254,8 @@ private:
if (factory->isValid()) { if (factory->isValid()) {
Factory localFactory; Factory localFactory;
std::swap(localFactory, *factory); // Swap before executing, to avoid recursion std::swap(localFactory, *factory); // Swap before executing, to avoid recursion
const_cast<std::remove_const_t<T> &>(*(m_data.toStrongRef())) localFactory.populate(
= localFactory.create(); m_data.toStrongRef().template constCast<std::remove_const_t<T>>());
} }
} }
} }

View File

@ -645,7 +645,8 @@ void QQmlJSAotCompiler::setDocument(
m_logger->setCode(irDocument->code); m_logger->setCode(irDocument->code);
m_unitGenerator = &irDocument->jsGenerator; m_unitGenerator = &irDocument->jsGenerator;
m_entireSourceCodeLines = irDocument->code.split(u'\n'); 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'/', resourcePathInfo.canonicalPath() + u'/',
m_qmldirFiles); m_qmldirFiles);
m_typeResolver.init(&visitor, irDocument->program); m_typeResolver.init(&visitor, irDocument->program);

View File

@ -74,15 +74,17 @@ inline QString getScopeName(const QQmlJSScope::ConstPtr &scope, QQmlJSScope::Sco
return scope->baseTypeName(); return scope->baseTypeName();
} }
QQmlJSImportVisitor::QQmlJSImportVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger, QQmlJSImportVisitor::QQmlJSImportVisitor(
const QString &implicitImportDirectory, const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, QQmlJSLogger *logger,
const QStringList &qmldirFiles) const QString &implicitImportDirectory, const QStringList &qmldirFiles)
: m_implicitImportDirectory(implicitImportDirectory), : m_implicitImportDirectory(implicitImportDirectory),
m_qmldirFiles(qmldirFiles), m_qmldirFiles(qmldirFiles),
m_currentScope(QQmlJSScope::create(QQmlJSScope::JSFunctionScope)), m_currentScope(QQmlJSScope::create()),
m_exportedRootScope(target),
m_importer(importer), m_importer(importer),
m_logger(logger) m_logger(logger)
{ {
m_currentScope->setScopeType(QQmlJSScope::JSFunctionScope);
Q_ASSERT(logger); // must be valid Q_ASSERT(logger); // must be valid
m_globalScope = m_currentScope; m_globalScope = m_currentScope;
@ -116,10 +118,10 @@ QQmlJSImportVisitor::QQmlJSImportVisitor(QQmlJSImporter *importer, QQmlJSLogger
QQmlJSImportVisitor::~QQmlJSImportVisitor() = default; QQmlJSImportVisitor::~QQmlJSImportVisitor() = default;
void QQmlJSImportVisitor::enterEnvironment(QQmlJSScope::ScopeType type, const QString &name, void QQmlJSImportVisitor::populateCurrentScope(
const QQmlJS::SourceLocation &location) 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); setScopeName(m_currentScope, type, name);
m_currentScope->setIsComposite(true); m_currentScope->setIsComposite(true);
m_currentScope->setFilePath(QFileInfo(m_logger->fileName()).absoluteFilePath()); 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); 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, bool QQmlJSImportVisitor::enterEnvironmentNonUnique(QQmlJSScope::ScopeType type,
const QString &name, const QString &name,
const QQmlJS::SourceLocation &location) const QQmlJS::SourceLocation &location)
@ -295,11 +313,6 @@ void QQmlJSImportVisitor::resolveAliasesAndIds()
} }
} }
QQmlJSScope::Ptr QQmlJSImportVisitor::result() const
{
return m_exportedRootScope;
}
QString QQmlJSImportVisitor::implicitImportDirectory( QString QQmlJSImportVisitor::implicitImportDirectory(
const QString &localFile, QQmlJSResourceFileMapper *mapper) const QString &localFile, QQmlJSResourceFileMapper *mapper)
{ {
@ -1048,28 +1061,34 @@ void QQmlJSImportVisitor::breakInheritanceCycles(const QQmlJSScope::Ptr &origina
if (scopes.contains(scope)) { if (scopes.contains(scope)) {
QString inheritenceCycle; QString inheritenceCycle;
for (const auto &seen : qAsConst(scopes)) { for (const auto &seen : qAsConst(scopes)) {
if (!inheritenceCycle.isEmpty())
inheritenceCycle.append(QLatin1String(" -> "));
inheritenceCycle.append(seen->baseTypeName()); inheritenceCycle.append(seen->baseTypeName());
inheritenceCycle.append(QLatin1String(" -> "));
} }
inheritenceCycle.append(scopes.first()->baseTypeName());
m_logger->log(QStringLiteral("%1 is part of an inheritance cycle: %2") const QString message = QStringLiteral("%1 is part of an inheritance cycle: %2")
.arg(scope->internalName()) .arg(scope->internalName(), inheritenceCycle);
.arg(inheritenceCycle), m_logger->log(message, Log_InheritanceCycle, scope->sourceLocation());
Log_InheritanceCycle, scope->sourceLocation());
originalScope->clearBaseType(); originalScope->clearBaseType();
originalScope->setBaseTypeError(message);
break; break;
} }
scopes.append(scope); scopes.append(scope);
const auto newScope = scope->baseType(); const auto newScope = scope->baseType();
if (newScope.isNull() && !scope->baseTypeName().isEmpty()) { if (newScope.isNull()) {
m_logger->log(scope->baseTypeName() const QString error = scope->baseTypeError();
+ QStringLiteral(" was not found. Did you add all import paths?"), const QString name = scope->baseTypeName();
Log_Import, scope->sourceLocation(), true, true, if (!error.isEmpty()) {
QQmlJSUtils::didYouMean(scope->baseTypeName(), m_rootScopeImports.keys(), m_logger->log(error, Log_Import, scope->sourceLocation(), true, true);
scope->sourceLocation())); } 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; scope = newScope;
@ -1206,10 +1225,11 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
Q_ASSERT(!superType.isEmpty()); Q_ASSERT(!superType.isEmpty());
if (superType.front().isUpper()) { if (superType.front().isUpper()) {
enterEnvironment(QQmlJSScope::QMLScope, superType, definition->firstSourceLocation()); if (rootScopeIsValid()) {
if (!m_exportedRootScope) { enterEnvironment(QQmlJSScope::QMLScope, superType, definition->firstSourceLocation());
m_exportedRootScope = m_currentScope; } else {
m_exportedRootScope->setIsSingleton(m_rootIsSingleton); enterRootScope(QQmlJSScope::QMLScope, superType, definition->firstSourceLocation());
m_currentScope->setIsSingleton(m_rootIsSingleton);
} }
const QTypeRevision revision = QQmlJSScope::resolveTypes( const QTypeRevision revision = QQmlJSScope::resolveTypes(
@ -1223,7 +1243,7 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
} else { } else {
enterEnvironmentNonUnique(QQmlJSScope::GroupedPropertyScope, superType, enterEnvironmentNonUnique(QQmlJSScope::GroupedPropertyScope, superType,
definition->firstSourceLocation()); definition->firstSourceLocation());
Q_ASSERT(m_exportedRootScope); Q_ASSERT(rootScopeIsValid());
QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports, &m_usedTypes); QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports, &m_usedTypes);
} }
@ -2118,7 +2138,7 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
bool QQmlJSImportVisitor::visit(ExportDeclaration *) bool QQmlJSImportVisitor::visit(ExportDeclaration *)
{ {
Q_ASSERT(!m_exportedRootScope.isNull()); Q_ASSERT(rootScopeIsValid());
Q_ASSERT(m_exportedRootScope != m_globalScope); Q_ASSERT(m_exportedRootScope != m_globalScope);
Q_ASSERT(m_currentScope == m_globalScope); Q_ASSERT(m_currentScope == m_globalScope);
m_currentScope = m_exportedRootScope; m_currentScope = m_exportedRootScope;
@ -2127,18 +2147,17 @@ bool QQmlJSImportVisitor::visit(ExportDeclaration *)
void QQmlJSImportVisitor::endVisit(ExportDeclaration *) void QQmlJSImportVisitor::endVisit(ExportDeclaration *)
{ {
Q_ASSERT(!m_exportedRootScope.isNull()); Q_ASSERT(rootScopeIsValid());
m_currentScope = m_exportedRootScope->parentScope(); m_currentScope = m_exportedRootScope->parentScope();
Q_ASSERT(m_currentScope == m_globalScope); Q_ASSERT(m_currentScope == m_globalScope);
} }
bool QQmlJSImportVisitor::visit(ESModule *module) bool QQmlJSImportVisitor::visit(ESModule *module)
{ {
enterEnvironment(QQmlJSScope::JSLexicalScope, QStringLiteral("module"), Q_ASSERT(!rootScopeIsValid());
module->firstSourceLocation()); enterRootScope(
Q_ASSERT(m_exportedRootScope.isNull()); QQmlJSScope::JSLexicalScope, QStringLiteral("module"), module->firstSourceLocation());
m_exportedRootScope = m_currentScope; m_currentScope->setIsScript(true);
m_exportedRootScope->setIsScript(true);
importBaseModules(); importBaseModules();
leaveEnvironment(); leaveEnvironment();
return true; return true;
@ -2152,9 +2171,10 @@ void QQmlJSImportVisitor::endVisit(ESModule *)
bool QQmlJSImportVisitor::visit(Program *) bool QQmlJSImportVisitor::visit(Program *)
{ {
Q_ASSERT(m_globalScope == m_currentScope); Q_ASSERT(m_globalScope == m_currentScope);
Q_ASSERT(m_exportedRootScope.isNull()); Q_ASSERT(!rootScopeIsValid());
m_exportedRootScope = m_currentScope; *m_exportedRootScope = std::move(*QQmlJSScope::clone(m_currentScope));
m_exportedRootScope->setIsScript(true); m_exportedRootScope->setIsScript(true);
m_currentScope = m_exportedRootScope;
importBaseModules(); importBaseModules();
return true; return true;
} }

View File

@ -61,12 +61,13 @@ struct QQmlJSResourceFileMapper;
class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSImportVisitor : public QQmlJS::AST::Visitor class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlJSImportVisitor : public QQmlJS::AST::Visitor
{ {
public: public:
QQmlJSImportVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger, QQmlJSImportVisitor(const QQmlJSScope::Ptr &target,
QQmlJSImporter *importer, QQmlJSLogger *logger,
const QString &implicitImportDirectory, const QString &implicitImportDirectory,
const QStringList &qmldirFiles = QStringList()); const QStringList &qmldirFiles = QStringList());
~QQmlJSImportVisitor(); ~QQmlJSImportVisitor();
QQmlJSScope::Ptr result() const; QQmlJSScope::Ptr result() const { return m_exportedRootScope; }
QQmlJSLogger *logger() { return m_logger; } QQmlJSLogger *logger() { return m_logger; }
@ -161,7 +162,7 @@ protected:
QStringList m_qmldirFiles; QStringList m_qmldirFiles;
QQmlJSScope::Ptr m_currentScope; QQmlJSScope::Ptr m_currentScope;
QQmlJSScope::Ptr m_savedBindingOuterScope; QQmlJSScope::Ptr m_savedBindingOuterScope;
QQmlJSScope::Ptr m_exportedRootScope; const QQmlJSScope::Ptr m_exportedRootScope;
QQmlJSScope::ConstPtr m_globalScope; QQmlJSScope::ConstPtr m_globalScope;
QQmlJSScopesById m_scopesById; QQmlJSScopesById m_scopesById;
QQmlJSImporter::ImportedTypes m_rootScopeImports; QQmlJSImporter::ImportedTypes m_rootScopeImports;
@ -226,6 +227,7 @@ protected:
void breakInheritanceCycles(const QQmlJSScope::Ptr &scope); void breakInheritanceCycles(const QQmlJSScope::Ptr &scope);
void checkDeprecation(const QQmlJSScope::ConstPtr &scope); void checkDeprecation(const QQmlJSScope::ConstPtr &scope);
void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope); void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope);
bool rootScopeIsValid() const { return m_exportedRootScope->sourceLocation().isValid(); }
QQmlJSLogger *m_logger; QQmlJSLogger *m_logger;
enum class LiteralOrScriptParseResult { Invalid, Script, Literal }; enum class LiteralOrScriptParseResult { Invalid, Script, Literal };
@ -300,6 +302,10 @@ private:
const QString &what, const QString &what,
const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation()); const QQmlJS::SourceLocation &srcLocation = QQmlJS::SourceLocation());
void addImportWithLocation(const QString &name, const QQmlJS::SourceLocation &loc); 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 QT_END_NAMESPACE

View File

@ -311,7 +311,8 @@ QQmlJSLinter::LintResult QQmlJSLinter::lintFile(const QString &filename,
m_logger->setFileName(m_useAbsolutePath ? info.absoluteFilePath() : filename); m_logger->setFileName(m_useAbsolutePath ? info.absoluteFilePath() : filename);
m_logger->setCode(code); m_logger->setCode(code);
m_logger->setSilent(silent || json); 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( QQmlJSImportVisitor::implicitImportDirectory(
m_logger->fileName(), m_importer.resourceFileMapper()), m_logger->fileName(), m_importer.resourceFileMapper()),
qmldirFiles }; qmldirFiles };

View File

@ -92,23 +92,23 @@ static bool searchBaseAndExtensionTypes(QQmlJSScopePtr type, const Action &check
return false; return false;
} }
QQmlJSScope::QQmlJSScope(ScopeType type, const QQmlJSScope::Ptr &parentScope) void QQmlJSScope::reparent(const QQmlJSScope::Ptr &parentScope, const QQmlJSScope::Ptr &childScope)
: m_parentScope(parentScope), m_scopeType(type) {}
QQmlJSScope::Ptr QQmlJSScope::create(ScopeType type, const QQmlJSScope::Ptr &parentScope)
{ {
QSharedPointer<QQmlJSScope> childScope(new QQmlJSScope{type, parentScope}); if (const QQmlJSScope::Ptr parent = childScope->m_parentScope.toStrongRef())
parent->m_childScopes.removeOne(childScope);
if (parentScope) if (parentScope)
parentScope->m_childScopes.push_back(childScope); parentScope->m_childScopes.append(childScope);
return childScope; childScope->m_parentScope = parentScope;
} }
QQmlJSScope::Ptr QQmlJSScope::clone(const ConstPtr &origin) QQmlJSScope::Ptr QQmlJSScope::clone(const ConstPtr &origin)
{ {
if (origin.isNull()) if (origin.isNull())
return QQmlJSScope::Ptr(); return QQmlJSScope::Ptr();
QQmlJSScope::Ptr cloned = create(origin->m_scopeType, origin->m_parentScope); QQmlJSScope::Ptr cloned = create();
*cloned = *origin; *cloned = *origin;
if (QQmlJSScope::Ptr parent = cloned->parentScope())
parent->m_childScopes.append(cloned);
return cloned; return cloned;
} }
@ -382,8 +382,9 @@ QTypeRevision QQmlJSScope::resolveType(
const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &context, const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &context,
QSet<QString> *usedTypes) QSet<QString> *usedTypes)
{ {
const auto baseType = findType(self->m_baseTypeName, context, usedTypes); const QString baseTypeName = self->baseTypeName();
if (!self->m_baseType.scope && !self->m_baseTypeName.isEmpty()) const auto baseType = findType(baseTypeName, context, usedTypes);
if (!self->m_baseType.scope && !baseTypeName.isEmpty())
self->m_baseType = { baseType.scope, baseType.revision }; self->m_baseType = { baseType.scope, baseType.revision };
if (!self->m_attachedType && !self->m_attachedTypeName.isEmpty()) if (!self->m_attachedType && !self->m_attachedTypeName.isEmpty())
@ -447,7 +448,7 @@ void QQmlJSScope::updateChildScope(
const auto propertyIt = type->m_properties.find(childScope->internalName()); const auto propertyIt = type->m_properties.find(childScope->internalName());
if (propertyIt != type->m_properties.end()) { if (propertyIt != type->m_properties.end()) {
childScope->m_baseType.scope = QQmlJSScope::ConstPtr(propertyIt->type()); childScope->m_baseType.scope = QQmlJSScope::ConstPtr(propertyIt->type());
childScope->m_baseTypeName = propertyIt->typeName(); childScope->setBaseTypeName(propertyIt->typeName());
return true; return true;
} }
return false; return false;
@ -457,7 +458,7 @@ void QQmlJSScope::updateChildScope(
if (const auto attachedBase = findType( if (const auto attachedBase = findType(
childScope->internalName(), contextualTypes, usedTypes).scope) { childScope->internalName(), contextualTypes, usedTypes).scope) {
childScope->m_baseType.scope = attachedBase->attachedType(); childScope->m_baseType.scope = attachedBase->attachedType();
childScope->m_baseTypeName = attachedBase->attachedTypeName(); childScope->setBaseTypeName(attachedBase->attachedTypeName());
} }
break; break;
default: default:
@ -508,8 +509,10 @@ void QQmlJSScope::resolveEnums(const QQmlJSScope::Ptr &self, const QQmlJSScope::
if (it->type()) if (it->type())
continue; continue;
Q_ASSERT(intType); // We need an "int" type to resolve enums Q_ASSERT(intType); // We need an "int" type to resolve enums
auto enumScope = QQmlJSScope::create(EnumScope, self); QQmlJSScope::Ptr enumScope = QQmlJSScope::create();
enumScope->m_baseTypeName = QStringLiteral("int"); reparent(self, enumScope);
enumScope->m_scopeType = EnumScope;
enumScope->setBaseTypeName(QStringLiteral("int"));
enumScope->m_baseType.scope = intType; enumScope->m_baseType.scope = intType;
enumScope->m_semantics = AccessSemantics::Value; enumScope->m_semantics = AccessSemantics::Value;
enumScope->m_internalName = self->internalName() + QStringLiteral("::") + it->name(); enumScope->m_internalName = self->internalName() + QStringLiteral("::") + it->name();
@ -761,6 +764,28 @@ bool QQmlJSScope::isNameDeferred(const QString &name) const
return isDeferred; 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 QQmlJSScope::attachedTypeName() const
{ {
QString name; QString name;
@ -792,7 +817,7 @@ bool QQmlJSScope::isResolved() const
const bool nameIsEmpty = (m_scopeType == ScopeType::AttachedPropertyScope const bool nameIsEmpty = (m_scopeType == ScopeType::AttachedPropertyScope
|| m_scopeType == ScopeType::GroupedPropertyScope) || m_scopeType == ScopeType::GroupedPropertyScope)
? m_internalName.isEmpty() ? m_internalName.isEmpty()
: m_baseTypeName.isEmpty(); : m_baseTypeNameOrError.isEmpty();
if (nameIsEmpty) if (nameIsEmpty)
return true; return true;
if (m_baseType.scope.isNull()) if (m_baseType.scope.isNull())
@ -865,31 +890,29 @@ bool QQmlJSScope::Export::isValid() const
return m_version.isValid() || !m_package.isEmpty() || !m_type.isEmpty(); return m_version.isValid() || !m_package.isEmpty() || !m_type.isEmpty();
} }
QQmlJSScope QDeferredFactory<QQmlJSScope>::create() const void QDeferredFactory<QQmlJSScope>::populate(const QSharedPointer<QQmlJSScope> &scope) const
{ {
QQmlJSTypeReader typeReader(m_importer, m_filePath); QQmlJSTypeReader typeReader(m_importer, m_filePath);
QQmlJSScope::Ptr result = typeReader(); typeReader(scope);
m_importer->m_globalWarnings.append(typeReader.errors()); m_importer->m_globalWarnings.append(typeReader.errors());
result->setInternalName(internalName()); scope->setInternalName(internalName());
QQmlJSScope::resolveEnums(result, m_importer->builtinInternalNames().value(u"int"_qs).scope); 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( m_importer->m_globalWarnings.append(
{ QStringLiteral( { QStringLiteral(
"Type %1 declared as singleton in qmldir but missing pragma Singleton") "Type %1 declared as singleton in qmldir but missing pragma Singleton")
.arg(result->internalName()), .arg(scope->internalName()),
QtCriticalMsg, QQmlJS::SourceLocation() }); QtCriticalMsg, QQmlJS::SourceLocation() });
result->setIsSingleton(true); scope->setIsSingleton(true);
} else if (!m_isSingleton && result->isSingleton()) { } else if (!m_isSingleton && scope->isSingleton()) {
m_importer->m_globalWarnings.append( m_importer->m_globalWarnings.append(
{ QStringLiteral("Type %1 not declared as singleton in qmldir " { QStringLiteral("Type %1 not declared as singleton in qmldir "
"but using pragma Singleton") "but using pragma Singleton")
.arg(result->internalName()), .arg(scope->internalName()),
QtCriticalMsg, QQmlJS::SourceLocation() }); QtCriticalMsg, QQmlJS::SourceLocation() });
result->setIsSingleton(false); scope->setIsSingleton(false);
} }
return std::move(*result);
} }
bool QQmlJSScope::canAssign(const QQmlJSScope::ConstPtr &derived) const bool QQmlJSScope::canAssign(const QQmlJSScope::ConstPtr &derived) const

View File

@ -95,7 +95,8 @@ public:
CustomParser = 0x10, CustomParser = 0x10,
Array = 0x20, Array = 0x20,
InlineComponent = 0x40, InlineComponent = 0x40,
WrappedInImplicitComponent = 0x80 WrappedInImplicitComponent = 0x80,
HasBaseTypeError = 0x100
}; };
Q_DECLARE_FLAGS(Flags, Flag) Q_DECLARE_FLAGS(Flags, Flag)
Q_FLAGS(Flags); Q_FLAGS(Flags);
@ -241,8 +242,7 @@ public:
UnnamedPropertyTarget // default property bindings, where property name is unspecified UnnamedPropertyTarget // default property bindings, where property name is unspecified
}; };
static QQmlJSScope::Ptr create(ScopeType type = QQmlJSScope::QMLScope, static QQmlJSScope::Ptr create() { return QSharedPointer<QQmlJSScope>(new QQmlJSScope); }
const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr());
static QQmlJSScope::Ptr clone(const QQmlJSScope::ConstPtr &origin); static QQmlJSScope::Ptr clone(const QQmlJSScope::ConstPtr &origin);
static QQmlJSScope::ConstPtr findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope); static QQmlJSScope::ConstPtr findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope);
@ -256,6 +256,8 @@ public:
return QQmlJSScope::WeakConstPtr(m_parentScope).toStrongRef(); 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); void insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier);
// inserts property as qml identifier as well as the corresponding // inserts property as qml identifier as well as the corresponding
@ -264,6 +266,7 @@ public:
bool isIdInCurrentScope(const QString &id) const; bool isIdInCurrentScope(const QString &id) const;
ScopeType scopeType() const { return m_scopeType; } ScopeType scopeType() const { return m_scopeType; }
void setScopeType(ScopeType type) { m_scopeType = type; }
void addOwnMethod(const QQmlJSMetaMethod &method) { m_methods.insert(method.methodName(), method); } void addOwnMethod(const QQmlJSMetaMethod &method) { m_methods.insert(method.methodName(), method); }
QMultiHash<QString, QQmlJSMetaMethod> ownMethods() const { return m_methods; } QMultiHash<QString, QQmlJSMetaMethod> 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 // 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. // relevant base class (in the hierarchy starting from QObject) of a C++ type.
void setBaseTypeName(const QString &baseTypeName) { m_baseTypeName = baseTypeName; } void setBaseTypeName(const QString &baseTypeName);
QString baseTypeName() const { return m_baseTypeName; } QString baseTypeName() const;
QQmlJSScope::ConstPtr baseType() const { return m_baseType.scope; } QQmlJSScope::ConstPtr baseType() const { return m_baseType.scope; }
QTypeRevision baseTypeRevision() const { return m_baseType.revision; } QTypeRevision baseTypeRevision() const { return m_baseType.revision; }
void clearBaseType() { m_baseType = {}; } void clearBaseType() { m_baseType = {}; }
void setBaseTypeError(const QString &baseTypeError);
QString baseTypeError() const;
void addOwnProperty(const QQmlJSMetaProperty &prop) { m_properties.insert(prop.propertyName(), prop); } void addOwnProperty(const QQmlJSMetaProperty &prop) { m_properties.insert(prop.propertyName(), prop); }
QHash<QString, QQmlJSMetaProperty> ownProperties() const { return m_properties; } QHash<QString, QQmlJSMetaProperty> ownProperties() const { return m_properties; }
@ -552,7 +559,7 @@ public:
}; };
private: private:
QQmlJSScope(ScopeType type, const QQmlJSScope::Ptr &parentScope = QQmlJSScope::Ptr()); QQmlJSScope() = default;
QQmlJSScope(const QQmlJSScope &) = default; QQmlJSScope(const QQmlJSScope &) = default;
QQmlJSScope &operator=(const QQmlJSScope &) = default; QQmlJSScope &operator=(const QQmlJSScope &) = default;
@ -587,7 +594,7 @@ private:
QString m_filePath; QString m_filePath;
QString m_internalName; QString m_internalName;
QString m_baseTypeName; QString m_baseTypeNameOrError;
// We only need the revision for the base type as inheritance is // We only need the revision for the base type as inheritance is
// the only relation between two types where the revisions matter. // the only relation between two types where the revisions matter.
@ -627,8 +634,6 @@ public:
m_filePath(filePath), m_importer(importer) m_filePath(filePath), m_importer(importer)
{} {}
QQmlJSScope create() const;
bool isValid() const bool isValid() const
{ {
return !m_filePath.isEmpty() && m_importer != nullptr; return !m_filePath.isEmpty() && m_importer != nullptr;
@ -645,6 +650,14 @@ public:
} }
private: private:
friend class QDeferredSharedPointer<QQmlJSScope>;
friend class QDeferredSharedPointer<const QQmlJSScope>;
friend class QDeferredWeakPointer<QQmlJSScope>;
friend class QDeferredWeakPointer<const QQmlJSScope>;
// Should only be called when lazy-loading the type in a deferred pointer.
void populate(const QSharedPointer<QQmlJSScope> &scope) const;
QString m_filePath; QString m_filePath;
QQmlJSImporter *m_importer = nullptr; QQmlJSImporter *m_importer = nullptr;
bool m_isSingleton = false; bool m_isSingleton = false;

View File

@ -40,13 +40,13 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
QQmlJSScope::Ptr QQmlJSTypeReader::operator()() bool QQmlJSTypeReader::operator ()(const QSharedPointer<QQmlJSScope> &scope)
{ {
using namespace QQmlJS::AST; using namespace QQmlJS::AST;
const QFileInfo info { m_file }; const QFileInfo info { m_file };
QString baseName = info.baseName(); const QString baseName = info.baseName();
const QString scopeName = baseName.endsWith(QStringLiteral(".ui")) ? baseName.chopped(3) scope->setInternalName(baseName.endsWith(QStringLiteral(".ui")) ? baseName.chopped(3)
: baseName; : baseName);
QQmlJS::Engine engine; QQmlJS::Engine engine;
QQmlJS::Lexer lexer(&engine); QQmlJS::Lexer lexer(&engine);
@ -55,16 +55,10 @@ QQmlJSScope::Ptr QQmlJSTypeReader::operator()()
const bool isESModule = lowerSuffix == QLatin1String("mjs"); const bool isESModule = lowerSuffix == QLatin1String("mjs");
const bool isJavaScript = isESModule || lowerSuffix == QLatin1String("js"); 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); QFile file(m_file);
if (!file.open(QFile::ReadOnly)) if (!file.open(QFile::ReadOnly))
return errorResult(); return false;
QString code = QString::fromUtf8(file.readAll()); QString code = QString::fromUtf8(file.readAll());
file.close(); file.close();
@ -76,11 +70,11 @@ QQmlJSScope::Ptr QQmlJSTypeReader::operator()()
: parser.parseProgram()) : parser.parseProgram())
: parser.parse(); : parser.parse();
if (!success) if (!success)
return errorResult(); return false;
QQmlJS::AST::Node *rootNode = parser.rootNode(); QQmlJS::AST::Node *rootNode = parser.rootNode();
if (!rootNode) if (!rootNode)
return errorResult(); return false;
QQmlJSLogger logger; QQmlJSLogger logger;
logger.setFileName(m_file); logger.setFileName(m_file);
@ -88,13 +82,10 @@ QQmlJSScope::Ptr QQmlJSTypeReader::operator()()
logger.setSilent(true); logger.setSilent(true);
QQmlJSImportVisitor membersVisitor( QQmlJSImportVisitor membersVisitor(
m_importer, &logger, scope, m_importer, &logger,
QQmlJSImportVisitor::implicitImportDirectory(m_file, m_importer->resourceFileMapper())); QQmlJSImportVisitor::implicitImportDirectory(m_file, m_importer->resourceFileMapper()));
rootNode->accept(&membersVisitor); rootNode->accept(&membersVisitor);
auto result = membersVisitor.result(); return true;
Q_ASSERT(result);
result->setInternalName(scopeName);
return result;
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -59,7 +59,7 @@ public:
, m_file(file) , m_file(file)
{} {}
QQmlJSScope::Ptr operator()(); bool operator()(const QSharedPointer<QQmlJSScope> &scope);
QList<QQmlJS::DiagnosticMessage> errors() const { return m_errors; } QList<QQmlJS::DiagnosticMessage> errors() const { return m_errors; }
private: private:

View File

@ -2,5 +2,6 @@ import Cycle
import QtQuick import QtQuick
Item { Item {
MenuItem {} property MenuItem item: a
MenuItem { id: a }
} }

View File

@ -483,7 +483,7 @@ void TestQmllint::dirtyQmlCode_data()
QTest::newRow("inheritanceCycle") QTest::newRow("inheritanceCycle")
<< QStringLiteral("Cycle1.qml") << QStringLiteral("Cycle1.qml")
<< Result { { Message { << Result { { Message {
QStringLiteral("Cycle2 is part of an inheritance cycle: Cycle2 -> Cycle3 " QStringLiteral("Cycle1 is part of an inheritance cycle: Cycle2 -> Cycle3 "
"-> Cycle1 -> Cycle2"), "-> Cycle1 -> Cycle2"),
2, 1 } } }; 2, 1 } } };
QTest::newRow("badQmldirImportAndDepend") QTest::newRow("badQmldirImportAndDepend")

View File

@ -84,7 +84,8 @@ class tst_qqmljsscope : public QQmlDataTest
logger.setFileName(url); logger.setFileName(url);
logger.setCode(sourceCode); logger.setCode(sourceCode);
logger.setSilent(true); 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 }; QQmlJSTypeResolver typeResolver { &m_importer };
typeResolver.init(&visitor, document.program); typeResolver.init(&visitor, document.program);
return visitor.result(); return visitor.result();

View File

@ -64,7 +64,8 @@ static bool isOrUnderComponent(QQmlJSScope::ConstPtr type)
QmltcVisitor::QmltcVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger, QmltcVisitor::QmltcVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger,
const QString &implicitImportDirectory, const QStringList &qmldirFiles) 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 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) void QmltcVisitor::endVisit(QQmlJS::AST::UiProgram *program)
{ {
QQmlJSImportVisitor::endVisit(program); QQmlJSImportVisitor::endVisit(program);
if (!m_exportedRootScope) // in case we failed badly if (!rootScopeIsValid()) // in case we failed badly
return; return;
QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> bindings; QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> bindings;