diff options
Diffstat (limited to 'tools/qmltc/qmltcvisitor.cpp')
-rw-r--r-- | tools/qmltc/qmltcvisitor.cpp | 78 |
1 files changed, 54 insertions, 24 deletions
diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp index 236ad76467..119308ef65 100644 --- a/tools/qmltc/qmltcvisitor.cpp +++ b/tools/qmltc/qmltcvisitor.cpp @@ -8,6 +8,7 @@ #include <QtCore/qstack.h> #include <QtCore/qdir.h> #include <QtCore/qloggingcategory.h> +#include <QtQml/private/qqmlsignalnames_p.h> #include <private/qqmljsutils_p.h> @@ -101,6 +102,9 @@ void QmltcVisitor::findCppIncludes() // look in type addCppInclude(type); + if (type->isListProperty()) + addCppInclude(type->valueType()); + // look in type's base type auto base = type->baseType(); if (!base && type->isComposite()) @@ -133,8 +137,9 @@ void QmltcVisitor::findCppIncludes() Q_ASSERT(type); const auto scopeType = type->scopeType(); - if (scopeType != QQmlJSScope::QMLScope && scopeType != QQmlJSScope::GroupedPropertyScope - && scopeType != QQmlJSScope::AttachedPropertyScope) { + if (scopeType != QQmlSA::ScopeType::QMLScope + && scopeType != QQmlSA::ScopeType::GroupedPropertyScope + && scopeType != QQmlSA::ScopeType::AttachedPropertyScope) { continue; } @@ -174,7 +179,7 @@ void QmltcVisitor::findCppIncludes() static void addCleanQmlTypeName(QStringList *names, const QQmlJSScope::ConstPtr &scope) { - Q_ASSERT(scope->scopeType() == QQmlJSScope::QMLScope); + Q_ASSERT(scope->scopeType() == QQmlSA::ScopeType::QMLScope); Q_ASSERT(!scope->isArrayScope()); Q_ASSERT(!scope->baseTypeName().isEmpty()); // the scope is guaranteed to be a new QML type, so any prefixes (separated @@ -197,7 +202,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object) } // we're not interested in non-QML scopes - if (m_currentScope->scopeType() != QQmlJSScope::QMLScope) + if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope) return true; if (m_currentScope->isInlineComponent()) { @@ -216,7 +221,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object) void QmltcVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *object) { - if (m_currentScope->scopeType() == QQmlJSScope::QMLScope) + if (m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope) m_qmlTypeNames.removeLast(); QQmlJSImportVisitor::endVisit(object); } @@ -268,7 +273,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiPublicMember *publicMember) owner->addOwnProperty(property); } - const QString notifyName = name + u"Changed"_s; + const QString notifyName = QQmlSignalNames::propertyNameToChangedSignalName(name); // also check that notify is already a method of the scope { auto owningScope = m_savedBindingOuterScope ? m_savedBindingOuterScope : m_currentScope; @@ -280,7 +285,7 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiPublicMember *publicMember) u"internal error: %1 found for property '%2'"_s.arg(errorString, name), qmlCompiler, publicMember->identifierToken); return false; - } else if (methods[0].methodType() != QQmlJSMetaMethod::Signal) { + } else if (methods[0].methodType() != QQmlJSMetaMethodType::Signal) { m_logger->log(u"internal error: method %1 of property %2 must be a signal"_s.arg( notifyName, name), qmlCompiler, publicMember->identifierToken); @@ -336,17 +341,17 @@ void QmltcVisitor::endVisit(QQmlJS::AST::UiProgram *program) for (const QList<QQmlJSScope::ConstPtr> &qmlTypes : m_pureQmlTypes) for (const QQmlJSScope::ConstPtr &type : qmlTypes) - checkForNamingCollisionsWithCpp(type); + checkNamesAndTypes(type); } QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding) { switch (binding.bindingType()) { - case QQmlJSMetaPropertyBinding::Object: + case QQmlSA::BindingType::Object: return binding.objectType(); - case QQmlJSMetaPropertyBinding::Interceptor: + case QQmlSA::BindingType::Interceptor: return binding.interceptorType(); - case QQmlJSMetaPropertyBinding::ValueSource: + case QQmlSA::BindingType::ValueSource: return binding.valueSourceType(); // TODO: AttachedProperty and GroupProperty are not supported yet, // but have to also be acknowledged here @@ -435,7 +440,7 @@ void QmltcVisitor::postVisitResolve( // match scopes to indices of QmlIR::Object from QmlIR::Document qsizetype count = 0; const auto setIndex = [&](const QQmlJSScope::Ptr ¤t) { - if (current->scopeType() != QQmlJSScope::QMLScope || current->isArrayScope()) + if (current->scopeType() != QQmlSA::ScopeType::QMLScope || current->isArrayScope()) return; Q_ASSERT(!m_qmlIrObjectIndices.contains(current)); m_qmlIrObjectIndices[current] = count; @@ -547,9 +552,9 @@ void QmltcVisitor::postVisitResolve( if (scope->isArrayScope()) // special kind of QQmlJSScope::QMLScope return; switch (scope->scopeType()) { - case QQmlJSScope::QMLScope: - case QQmlJSScope::GroupedPropertyScope: - case QQmlJSScope::AttachedPropertyScope: { + case QQmlSA::ScopeType::QMLScope: + case QQmlSA::ScopeType::GroupedPropertyScope: + case QQmlSA::ScopeType::AttachedPropertyScope: { ++qmlScopeCount[scope->enclosingInlineComponentName()]; break; } @@ -653,7 +658,7 @@ void QmltcVisitor::setupAliases() } } -void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &type) +void QmltcVisitor::checkNamesAndTypes(const QQmlJSScope::ConstPtr &type) { static const QString cppKeywords[] = { u"alignas"_s, @@ -672,20 +677,20 @@ void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr & u"case"_s, u"catch"_s, u"char"_s, - u"char8_t"_s, u"char16_t"_s, u"char32_t"_s, + u"char8_t"_s, u"class"_s, + u"co_await"_s, + u"co_return"_s, + u"co_yield"_s, u"compl"_s, u"concept"_s, u"const"_s, + u"const_cast"_s, u"consteval"_s, u"constexpr"_s, - u"const_cast"_s, u"continue"_s, - u"co_await"_s, - u"co_return"_s, - u"co_yield"_s, u"decltype"_s, u"default"_s, u"delete"_s, @@ -753,6 +758,7 @@ void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr & u"xor"_s, u"xor_eq"_s, }; + Q_ASSERT(std::is_sorted(std::begin(cppKeywords), std::end(cppKeywords))); const auto isReserved = [&](QStringView word) { if (word.startsWith(QChar(u'_')) && word.size() >= 2 @@ -769,6 +775,23 @@ void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr & qmlCompiler, type->sourceLocation()); }; + const auto validateType = [&type, this](const QQmlJSScope::ConstPtr &typeToCheck, + QStringView name, QStringView errorPrefix) { + if (type->moduleName().isEmpty() || typeToCheck.isNull()) + return; + + if (typeToCheck->isComposite() && typeToCheck->moduleName() != type->moduleName()) { + m_logger->log( + QStringLiteral( + "Can't compile the %1 type \"%2\" to C++ because it " + "lives in \"%3\" instead of the current file's \"%4\" QML module.") + .arg(errorPrefix, name, typeToCheck->moduleName(), type->moduleName()), + qmlCompiler, type->sourceLocation()); + } + }; + + validateType(type->baseType(), type->baseTypeName(), u"QML base"); + const auto enums = type->ownEnumerations(); for (auto it = enums.cbegin(); it != enums.cend(); ++it) { const QQmlJSMetaEnum e = it.value(); @@ -783,16 +806,23 @@ void QmltcVisitor::checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr & for (auto it = properties.cbegin(); it != properties.cend(); ++it) { const QQmlJSMetaProperty &p = it.value(); validate(p.propertyName(), u"Property"); + + if (!p.isAlias() && !p.typeName().isEmpty()) + validateType(p.type(), p.typeName(), u"QML property"); } const auto methods = type->ownMethods(); for (auto it = methods.cbegin(); it != methods.cend(); ++it) { const QQmlJSMetaMethod &m = it.value(); validate(m.methodName(), u"Method"); + if (!m.returnTypeName().isEmpty()) + validateType(m.returnType(), m.returnTypeName(), u"QML method return"); - const auto parameterNames = m.parameterNames(); - for (const auto &name : parameterNames) - validate(name, u"Method '%1' parameter"_s.arg(m.methodName())); + for (const auto ¶meter : m.parameters()) { + validate(parameter.name(), u"Method '%1' parameter"_s.arg(m.methodName())); + if (!parameter.typeName().isEmpty()) + validateType(parameter.type(), parameter.typeName(), u"QML parameter"); + } } // TODO: one could also test signal handlers' parameters but we do not store |