diff options
author | Sami Shalayel <sami.shalayel@qt.io> | 2024-02-14 16:25:58 +0100 |
---|---|---|
committer | Sami Shalayel <sami.shalayel@qt.io> | 2024-02-19 10:37:07 +0100 |
commit | db7267cd61a5ae094686726ee222e11fef08e977 (patch) | |
tree | 4a90077761ce138f8104477e052bf4ee967a10ae /tools | |
parent | 7cc3d8af146878c051baebe1342a84384d299583 (diff) |
qmltc: warn about QML defined types from other modules
Add a warning for QML defined types imported from other modules in
qmltcvisitor to avoid qmltc generating invalid C++ code.
This requires populating the moduleName member of qqmljsscope where it
was ignored:
* for qmldir files imported via command line options
* for inline components
* for the file to be compiled
The module name of the QML file to be compiled is passed
from CMake to qmltc via a newly introduced command line argument, and is
simulated in the tst_qmltc_qprocess tests.
Pick-to: 6.5 6.6 6.7
Fixes: QTBUG-121592
Change-Id: I965d759a028546c648ccf6c9aa469b84538be446
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/qmltc/main.cpp | 13 | ||||
-rw-r--r-- | tools/qmltc/qmltcvisitor.cpp | 34 | ||||
-rw-r--r-- | tools/qmltc/qmltcvisitor.h | 2 |
3 files changed, 42 insertions, 7 deletions
diff --git a/tools/qmltc/main.cpp b/tools/qmltc/main.cpp index 7c3bf3bacd..e88cae6bd7 100644 --- a/tools/qmltc/main.cpp +++ b/tools/qmltc/main.cpp @@ -99,6 +99,13 @@ int main(int argc, char **argv) QCoreApplication::translate("main", "namespace") }; parser.addOption(namespaceOption); + QCommandLineOption moduleOption{ + u"module"_s, + QCoreApplication::translate("main", + "Name of the QML module that this QML code belongs to."), + QCoreApplication::translate("main", "module") + }; + parser.addOption(moduleOption); QCommandLineOption exportOption{ u"export"_s, QCoreApplication::translate( "main", "Export macro used in the generated C++ code"), @@ -262,7 +269,11 @@ int main(int argc, char **argv) logger.setCode(sourceCode); setupLogger(logger); - QmltcVisitor visitor(QQmlJSScope::create(), &importer, &logger, + auto currentScope = QQmlJSScope::create(); + if (parser.isSet(moduleOption)) + currentScope->setModuleName(parser.value(moduleOption)); + + QmltcVisitor visitor(currentScope, &importer, &logger, QQmlJSImportVisitor::implicitImportDirectory(url, &mapper), qmldirFiles); visitor.setMode(QmltcVisitor::Compile); QmltcTypeResolver typeResolver { &importer }; diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp index 88ec451891..95cb280c4d 100644 --- a/tools/qmltc/qmltcvisitor.cpp +++ b/tools/qmltc/qmltcvisitor.cpp @@ -338,7 +338,7 @@ 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) @@ -655,7 +655,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, @@ -772,6 +772,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(); @@ -786,16 +803,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 diff --git a/tools/qmltc/qmltcvisitor.h b/tools/qmltc/qmltcvisitor.h index c7d2990848..111df0e885 100644 --- a/tools/qmltc/qmltcvisitor.h +++ b/tools/qmltc/qmltcvisitor.h @@ -22,7 +22,7 @@ class QmltcVisitor : public QQmlJSImportVisitor void postVisitResolve(const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> &qmlIrOrderedBindings); void setupAliases(); - void checkForNamingCollisionsWithCpp(const QQmlJSScope::ConstPtr &type); + void checkNamesAndTypes(const QQmlJSScope::ConstPtr &type); void setRootFilePath(); QString sourceDirectoryPath(const QString &path); |