diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2020-03-18 11:44:13 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2020-03-18 12:37:04 +0100 |
commit | 13caf26b29283b544edc2974fa1ea0481c63b435 (patch) | |
tree | c06077293de36f69c42b5e1a38844e2b8bbe07c4 | |
parent | 36fb7cf832e801a7b3718fa443ec2f1b83e0fea2 (diff) | |
parent | 869efe4a49c5286493d7f039325992725bcac6c3 (diff) |
Merge remote-tracking branch 'origin/5.15' into dev
Conflicts:
tools/qmllint/findunqualified.cpp
Change-Id: I2593b5cc0db1d14e0c944aec4b88a80f46f5b0c1
27 files changed, 249 insertions, 82 deletions
diff --git a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp index 1708166a8a..e641a901ad 100644 --- a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp @@ -133,7 +133,7 @@ bool QLocalClientConnection::connectToServer() connect(m_socket, &QLocalSocket::connected, this, &QLocalClientConnection::connectionEstablished); connect(m_socket, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>( - &QLocalSocket::error), m_socket, [this](QLocalSocket::LocalSocketError) { + &QLocalSocket::errorOccurred), m_socket, [this](QLocalSocket::LocalSocketError) { m_socket->disconnectFromServer(); m_socket->connectToServer(m_filename); }, Qt::QueuedConnection); diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp index c6ace05b8f..82f3e53d68 100644 --- a/src/qml/animations/qabstractanimationjob.cpp +++ b/src/qml/animations/qabstractanimationjob.cpp @@ -45,8 +45,6 @@ #include "private/qqmlengine_p.h" #include "private/qqmlglobal_p.h" -#define DEFAULT_TIMER_INTERVAL 16 - QT_BEGIN_NAMESPACE #ifndef QT_NO_THREAD diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 0316e54c09..1f2e59b55e 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -549,6 +549,12 @@ bool IRBuilder::visit(QQmlJS::AST::UiInlineComponent *ast) recordError(ast->firstSourceLocation(), QLatin1String("Nested inline components are not supported")); return false; } + if (inlineComponentsNames.contains(ast->name.toString())) { + recordError(ast->firstSourceLocation(), QLatin1String("Inline component names must be unique per file")); + return false; + } else { + inlineComponentsNames.insert(ast->name.toString()); + } { QScopedValueRollback<bool> rollBack {insideInlineComponent, true}; if (!defineQMLObject(&idx, ast->component)) diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 1f2c11f7f3..254d21001b 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -543,6 +543,7 @@ public: QList<QQmlJS::DiagnosticMessage> errors; QSet<QString> illegalNames; + QSet<QString> inlineComponentsNames; QList<const QV4::CompiledData::Import *> _imports; QList<Pragma*> _pragmas; diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 43a9435f83..08e43c1341 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -1009,6 +1009,12 @@ bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedS if (!typeRecursionDetected) typeRecursionDetected = &localTypeRecursionDetected; + if (needsSorting()) { + std::stable_sort(imports.begin(), imports.end(), [](QQmlImportInstance *left, QQmlImportInstance *) { + return left->isInlineComponent; + }); + setNeedsSorting(false); + } for (int i=0; i<imports.count(); ++i) { const QQmlImportInstance *import = imports.at(i); if (import->resolveType(typeLoader, type, version_return, type_return, base, @@ -1070,6 +1076,17 @@ bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedS return false; } +bool QQmlImportNamespace::needsSorting() const +{ + return nextNamespace == this; +} + +void QQmlImportNamespace::setNeedsSorting(bool needsSorting) +{ + Q_ASSERT(nextNamespace == this || nextNamespace == nullptr); + nextNamespace = needsSorting ? this : nullptr; +} + QQmlImportsPrivate::QQmlImportsPrivate(QQmlTypeLoader *loader) : ref(1), typeLoader(loader) { } @@ -1782,6 +1799,7 @@ bool QQmlImports::addInlineComponentImport(QQmlImportInstance *const importInsta importInstance->version = QTypeRevision::zero(); importInstance->containingType = containingType; d->unqualifiedset.imports.push_back(importInstance); + d->unqualifiedset.setNeedsSorting(true); return true; } diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index 7416bb7a4c..f7f5c32bf4 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -122,7 +122,12 @@ public: QHashedString prefix; // Used by QQmlImportsPrivate::qualifiedSets + // set to this in unqualifiedSet to indicate that the lists of imports needs + // to be sorted when an inline component import was added + // We can't use flag pointer, as that does not work with QFieldList QQmlImportNamespace *nextNamespace; + bool needsSorting() const; + void setNeedsSorting(bool needsSorting); }; class Q_QML_PRIVATE_EXPORT QQmlImports diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index ba5ba96cbe..aa7e28add0 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -192,7 +192,12 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *obj r->d()->object = object; r->d()->property = property; r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject)); - r->d()->setValueType(QQmlValueTypeFactory::valueType(typeId)); + auto valueType = QQmlValueTypeFactory::valueType(typeId); + if (!valueType) { + QMetaType metaType(typeId); + return engine->throwTypeError(QLatin1String("Type %1 is not a value type").arg(metaType.name())); + } + r->d()->setValueType(valueType); r->d()->setGadgetPtr(nullptr); return r->asReturnedValue(); } @@ -204,7 +209,12 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, const QVaria Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>()); r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject)); - r->d()->setValueType(QQmlValueTypeFactory::valueType(typeId)); + auto valueType = QQmlValueTypeFactory::valueType(typeId); + if (!valueType) { + QMetaType metaType(typeId); + return engine->throwTypeError(QLatin1String("Type %1 is not a value type").arg(metaType.name())); + } + r->d()->setValueType(valueType); r->d()->setGadgetPtr(nullptr); r->d()->setValue(value); return r->asReturnedValue(); diff --git a/src/qmldebug/qqmldebugconnection.cpp b/src/qmldebug/qqmldebugconnection.cpp index 15c56fc20d..9d495ce6e4 100644 --- a/src/qmldebug/qqmldebugconnection.cpp +++ b/src/qmldebug/qqmldebugconnection.cpp @@ -389,7 +389,7 @@ void QQmlDebugConnection::connectToHost(const QString &hostName, quint16 port) connect(socket, &QAbstractSocket::disconnected, this, &QQmlDebugConnection::socketDisconnected); connect(socket, &QAbstractSocket::connected, this, &QQmlDebugConnection::socketConnected); connect(socket, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>( - &QAbstractSocket::error), this, &QQmlDebugConnection::socketError); + &QAbstractSocket::errorOccurred), this, &QQmlDebugConnection::socketError); connect(socket, &QAbstractSocket::stateChanged, this, &QQmlDebugConnection::socketStateChanged); socket->connectToHost(hostName, port); } @@ -417,7 +417,7 @@ public: connect(parent, &QLocalSocket::stateChanged, this, &LocalSocketSignalTranslator::onStateChanged); connect(parent, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>( - &QLocalSocket::error), this, &LocalSocketSignalTranslator::onError); + &QLocalSocket::errorOccurred), this, &LocalSocketSignalTranslator::onError); } void onError(QLocalSocket::LocalSocketError error) diff --git a/src/quick/doc/src/advtutorial.qdoc b/src/quick/doc/src/advtutorial.qdoc index a18d4493ea..63c70067fd 100644 --- a/src/quick/doc/src/advtutorial.qdoc +++ b/src/quick/doc/src/advtutorial.qdoc @@ -64,7 +64,6 @@ directory. /*! \title QML Advanced Tutorial 1 - Creating the Game Canvas and Blocks -\contentspage QML Advanced Tutorial \previouspage QML Advanced Tutorial \nextpage QML Advanced Tutorial 2 - Populating the Game Canvas @@ -132,7 +131,6 @@ types to get started. Next, we will populate the game canvas with some blocks. /*! \title QML Advanced Tutorial 2 - Populating the Game Canvas -\contentspage QML Advanced Tutorial \previouspage QML Advanced Tutorial 1 - Creating the Game Canvas and Blocks \nextpage QML Advanced Tutorial 3 - Implementing the Game Logic @@ -206,7 +204,6 @@ Now, we have a screen of blocks, and we can begin to add the game mechanics. /*! \title QML Advanced Tutorial 3 - Implementing the Game Logic -\contentspage QML Advanced Tutorial \previouspage QML Advanced Tutorial 2 - Populating the Game Canvas \nextpage QML Advanced Tutorial 4 - Finishing Touches @@ -293,7 +290,6 @@ until the next chapter - where your application becomes alive! /*! \title QML Advanced Tutorial 4 - Finishing Touches -\contentspage QML Advanced Tutorial \previouspage QML Advanced Tutorial 3 - Implementing the Game Logic \example tutorials/samegame/samegame4 diff --git a/src/quick/doc/src/dynamicview-tutorial.qdoc b/src/quick/doc/src/dynamicview-tutorial.qdoc index 6f44ba1947..d221e2ae62 100644 --- a/src/quick/doc/src/dynamicview-tutorial.qdoc +++ b/src/quick/doc/src/dynamicview-tutorial.qdoc @@ -50,7 +50,6 @@ directory. /*! \title QML Dynamic View Ordering Tutorial 1 - A Simple ListView and Delegate -\contentspage QML Dynamic View Ordering Tutorial \previouspage QML Dynamic View Ordering Tutorial \nextpage QML Dynamic View Ordering Tutorial 2 - Dragging View Items @@ -85,7 +84,6 @@ The second part of the application is the ListView itself to which we bind the m /*! \title QML Dynamic View Ordering Tutorial 2 - Dragging View Items -\contentspage QML Dynamic View Ordering Tutorial \previouspage QML Dynamic View Ordering Tutorial 1 - A Simple ListView and Delegate \nextpage QML Dynamic View Ordering Tutorial 3 - Moving Dragged Items @@ -128,7 +126,6 @@ so that is above other items in the stacking order and isn't obscured as it is d /*! \title QML Dynamic View Ordering Tutorial 3 - Moving Dragged Items -\contentspage QML Dynamic View Ordering Tutorial \previouspage QML Dynamic View Ordering Tutorial 2 - Dragging View Items \nextpage QML Dynamic View Ordering Tutorial 4 - Sorting Items @@ -180,7 +177,6 @@ property of the view and bind the \l {DelegateModel::}{model} and /*! \title QML Dynamic View Ordering Tutorial 4 - Sorting Items -\contentspage QML Dynamic View Ordering Tutorial \previouspage QML Dynamic View Ordering Tutorial 3 - Moving Dragged Items \example tutorials/dynamicview/dynamicview4 diff --git a/src/quick/doc/src/tutorial.qdoc b/src/quick/doc/src/tutorial.qdoc index 20021f2432..7cb5f655e8 100644 --- a/src/quick/doc/src/tutorial.qdoc +++ b/src/quick/doc/src/tutorial.qdoc @@ -54,7 +54,6 @@ Tutorial chapters: /*! \page qml-tutorial1.html \title QML Tutorial 1 - Basic Types -\contentspage QML Tutorial \previouspage QML Tutorial \nextpage QML Tutorial 2 - QML Components @@ -112,7 +111,6 @@ qmlscene tutorials/helloworld/tutorial1.qml /*! \page qml-tutorial2.html \title QML Tutorial 2 - QML Components -\contentspage QML Tutorial \previouspage QML Tutorial 1 - Basic Types \nextpage QML Tutorial 3 - States and Transitions @@ -184,7 +182,6 @@ We can react to any signal of our component through a property of the name \e 'o /*! \page qml-tutorial3.html \title QML Tutorial 3 - States and Transitions -\contentspage QML Tutorial \previouspage QML Tutorial 2 - QML Components In this chapter, we make this example a little bit more dynamic by introducing states and transitions. diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index f9e6ba03a8..5a281cdd4a 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -1030,6 +1030,7 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx) ? "static" : (m_bufferStrategy == GL_DYNAMIC_DRAW ? "dynamic" : "stream"))); } + static const bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER"); if (!m_rhi) { // If rendering with an OpenGL Core profile context, we need to create a VAO // to hold our vertex specification state. @@ -1037,9 +1038,9 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx) m_vao = new QOpenGLVertexArrayObject(this); m_vao->create(); } - - bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER"); m_useDepthBuffer = useDepth && ctx->openglContext()->format().depthBufferSize() > 0; + } else { + m_useDepthBuffer = useDepth; } } @@ -1400,12 +1401,11 @@ void Renderer::nodeWasRemoved(Node *node) m_elementsToDelete.add(e); if (m_renderNodeElements.isEmpty()) { - if (m_rhi) { - m_useDepthBuffer = true; - } else { - static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER"); + static const bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER"); + if (m_rhi) + m_useDepthBuffer = useDepth; + else m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0; - } } } } @@ -3716,7 +3716,7 @@ void Renderer::renderMergedBatch(PreparedRenderBatch *renderBatch) // split prep { batch->vbo.buf, quint32(draw.vertices) }, { batch->vbo.buf, quint32(draw.zorders) } }; - cb->setVertexInput(VERTEX_BUFFER_BINDING, 2, vbufBindings, + cb->setVertexInput(VERTEX_BUFFER_BINDING, m_useDepthBuffer ? 2 : 1, vbufBindings, batch->ibo.buf, draw.indices, m_uint32IndexForRhi ? QRhiCommandBuffer::IndexUInt32 : QRhiCommandBuffer::IndexUInt16); cb->drawIndexed(draw.indexCount); @@ -4052,8 +4052,8 @@ void Renderer::renderBatches() m_pstate.viewportSet = false; m_pstate.scissorSet = false; - m_gstate.depthTest = true; - m_gstate.depthWrite = true; + m_gstate.depthTest = m_useDepthBuffer; + m_gstate.depthWrite = m_useDepthBuffer; m_gstate.depthFunc = QRhiGraphicsPipeline::Less; m_gstate.blending = false; @@ -4085,8 +4085,8 @@ void Renderer::renderBatches() m_gstate.blending = true; // factors never change, always set for premultiplied alpha based blending - // depth test stays enabled but no need to write out depth from the - // transparent (back-to-front) pass + // depth test stays enabled (if m_useDepthBuffer, that is) but no need + // to write out depth from the transparent (back-to-front) pass m_gstate.depthWrite = false; QVarLengthArray<PreparedRenderBatch, 64> alphaRenderBatches; @@ -4507,6 +4507,35 @@ bool Renderer::prepareRhiRenderNode(Batch *batch, PreparedRenderBatch *renderBat updateClipState(rd->m_clip_list, batch); + QSGNode *xform = e->renderNode->parent(); + QMatrix4x4 matrix; + QSGNode *root = rootNode(); + if (e->root) { + matrix = qsg_matrixForRoot(e->root); + root = e->root->sgNode; + } + while (xform != root) { + if (xform->type() == QSGNode::TransformNodeType) { + matrix = matrix * static_cast<QSGTransformNode *>(xform)->combinedMatrix(); + break; + } + xform = xform->parent(); + } + rd->m_matrix = &matrix; + + QSGNode *opacity = e->renderNode->parent(); + rd->m_opacity = 1.0; + while (opacity != rootNode()) { + if (opacity->type() == QSGNode::OpacityNodeType) { + rd->m_opacity = static_cast<QSGOpacityNode *>(opacity)->combinedOpacity(); + break; + } + opacity = opacity->parent(); + } + + if (rd->m_prepareCallback) + rd->m_prepareCallback(); + renderBatch->batch = batch; renderBatch->sms = nullptr; @@ -4535,38 +4564,15 @@ void Renderer::renderRhiRenderNode(const Batch *batch) // split prepare-render ( state.m_scissorEnabled = batch->clipState.type & ClipState::ScissorClip; state.m_stencilEnabled = batch->clipState.type & ClipState::StencilClip; - QSGNode *xform = e->renderNode->parent(); - QMatrix4x4 matrix; - QSGNode *root = rootNode(); - if (e->root) { - matrix = qsg_matrixForRoot(e->root); - root = e->root->sgNode; - } - while (xform != root) { - if (xform->type() == QSGNode::TransformNodeType) { - matrix = matrix * static_cast<QSGTransformNode *>(xform)->combinedMatrix(); - break; - } - xform = xform->parent(); - } - rd->m_matrix = &matrix; - - QSGNode *opacity = e->renderNode->parent(); - rd->m_opacity = 1.0; - while (opacity != rootNode()) { - if (opacity->type() == QSGNode::OpacityNodeType) { - rd->m_opacity = static_cast<QSGOpacityNode *>(opacity)->combinedOpacity(); - break; - } - opacity = opacity->parent(); - } - const QSGRenderNode::StateFlags changes = e->renderNode->changedStates(); QRhiCommandBuffer *cb = commandBuffer(); - cb->beginExternal(); + const bool needsExternal = rd->m_needsExternalRendering; + if (needsExternal) + cb->beginExternal(); e->renderNode->render(&state); - cb->endExternal(); + if (needsExternal) + cb->endExternal(); rd->m_matrix = nullptr; rd->m_clip_list = nullptr; diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index 519e3cd017..8112ed2da7 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -77,6 +77,8 @@ QSGRenderNodePrivate::QSGRenderNodePrivate() : m_matrix(nullptr) , m_clip_list(nullptr) , m_opacity(1) + , m_needsExternalRendering(true) + , m_prepareCallback(nullptr) { } diff --git a/src/quick/scenegraph/coreapi/qsgrendernode_p.h b/src/quick/scenegraph/coreapi/qsgrendernode_p.h index 5c42e55689..534d630f15 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode_p.h +++ b/src/quick/scenegraph/coreapi/qsgrendernode_p.h @@ -51,12 +51,14 @@ // We mean it. // +#include <QtQuick/private/qtquickglobal_p.h> #include <QtQuick/qsgnode.h> #include <QtQuick/qsgrendernode.h> +#include <functional> QT_BEGIN_NAMESPACE -class QSGRenderNodePrivate +class Q_QUICK_PRIVATE_EXPORT QSGRenderNodePrivate { public: QSGRenderNodePrivate(); @@ -66,6 +68,11 @@ public: const QMatrix4x4 *m_matrix; const QSGClipNode *m_clip_list; qreal m_opacity; + + // ### Qt 6: change this into a value for flags() + bool m_needsExternalRendering; + // ### Qt 6: change this into a virtual prepare() function + std::function<void()> m_prepareCallback; }; QT_END_NAMESPACE diff --git a/tests/auto/qml/qmllint/data/ButtonLoader.qml b/tests/auto/qml/qmllint/data/ButtonLoader.qml new file mode 100644 index 0000000000..2721614735 --- /dev/null +++ b/tests/auto/qml/qmllint/data/ButtonLoader.qml @@ -0,0 +1,10 @@ +import QtQuick 2.12 + +Item { + Text { + id: roundButton + Text { + font.pixelSize: roundButton.font.pixelSize * 0.5 + } + } +} diff --git a/tests/auto/qml/qmllint/data/Dialog.qml b/tests/auto/qml/qmllint/data/Dialog.qml new file mode 100644 index 0000000000..bde8eae18b --- /dev/null +++ b/tests/auto/qml/qmllint/data/Dialog.qml @@ -0,0 +1,10 @@ +import QtQuick 2.12 + +Item { + id: control + property Text header + header: Text { + font.bold: true + padding: 12 + } +} diff --git a/tests/auto/qml/qmllint/data/Drawer.qml b/tests/auto/qml/qmllint/data/Drawer.qml new file mode 100644 index 0000000000..db1d785c6c --- /dev/null +++ b/tests/auto/qml/qmllint/data/Drawer.qml @@ -0,0 +1,5 @@ +import QtQml 2.12 as T + +T.QtObject { + objectName: T.Component.objectName +} diff --git a/tests/auto/qml/qmllint/data/Text.qml b/tests/auto/qml/qmllint/data/Text.qml new file mode 100644 index 0000000000..130578d1e3 --- /dev/null +++ b/tests/auto/qml/qmllint/data/Text.qml @@ -0,0 +1,6 @@ +import QtQuick 2.12 as T + +T.Text { + id: control + text: "'ello" +} diff --git a/tests/auto/qml/qmllint/data/Things/plugins.qmltypes b/tests/auto/qml/qmllint/data/Things/plugins.qmltypes index 00cda191cc..9d81c21070 100644 --- a/tests/auto/qml/qmllint/data/Things/plugins.qmltypes +++ b/tests/auto/qml/qmllint/data/Things/plugins.qmltypes @@ -12,5 +12,6 @@ Module { "CCC": 2 } } + Property { name: "palette"; type: "QPalette" } } } diff --git a/tests/auto/qml/qmllint/data/incompleteQmltypes.qml b/tests/auto/qml/qmllint/data/incompleteQmltypes.qml new file mode 100644 index 0000000000..ab06bbd8b0 --- /dev/null +++ b/tests/auto/qml/qmllint/data/incompleteQmltypes.qml @@ -0,0 +1,6 @@ +import Things 1.0 + +SomethingEntirelyStrange { + id: self + property var a: self.palette.weDontKnowIt +} diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index 1cd9f8b0d1..ed968d6623 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -160,6 +160,10 @@ void TestQmllint::dirtyQmlCode_data() << QStringLiteral("badTypeAssertion.qml") << QString("Warning: Property \"rrr\" not found on type \"Item\" at 5:39") << QString(); + QTest::newRow("incompleteQmltypes") + << QStringLiteral("incompleteQmltypes.qml") + << QString("Warning: Type \"QPalette\" of member \"palette\" not found at 5:26") + << QString(); } void TestQmllint::dirtyQmlCode() @@ -192,6 +196,9 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("goodParent") << QStringLiteral("goodParent.qml"); QTest::newRow("goodTypeAssertion") << QStringLiteral("goodTypeAssertion.qml"); QTest::newRow("AttachedProps") << QStringLiteral("AttachedProps.qml"); + QTest::newRow("unknownBuiltinFont") << QStringLiteral("ButtonLoader.qml"); + QTest::newRow("confusingImport") << QStringLiteral("Dialog.qml"); + QTest::newRow("qualifiedAttached") << QStringLiteral("Drawer.qml"); } void TestQmllint::cleanQmlCode() diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentDuplicateName.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentDuplicateName.qml new file mode 100644 index 0000000000..12cc79cf14 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentDuplicateName.qml @@ -0,0 +1,6 @@ +import QtQuick 2.12 +Item +{ + component IC: Item {} + component IC: Item {} +} diff --git a/tests/auto/qml/qqmllanguage/data/inlineComponentFoundBeforeOtherImports.qml b/tests/auto/qml/qqmllanguage/data/inlineComponentFoundBeforeOtherImports.qml new file mode 100644 index 0000000000..85903727fb --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/inlineComponentFoundBeforeOtherImports.qml @@ -0,0 +1,9 @@ +import QtQuick 2.12 + +Item +{ + component Rectangle: Item { + Component.onCompleted: console.info("Created") + } + Rectangle {} +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index c6076410b2..6d932c2665 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -318,6 +318,8 @@ private slots: void inlineComponentInSingleton(); void nonExistingInlineComponent_data(); void nonExistingInlineComponent(); + void inlineComponentFoundBeforeOtherImports(); + void inlineComponentDuplicateNameError(); void selfReference(); void selfReferencingSingleton(); @@ -5743,6 +5745,29 @@ void tst_qqmllanguage::nonExistingInlineComponent() QCOMPARE(error.column(), column); } +void tst_qqmllanguage::inlineComponentFoundBeforeOtherImports() +{ + QQmlEngine engine; + QUrl url = testFileUrl("inlineComponentFoundBeforeOtherImports.qml"); + QQmlComponent component(&engine, url); + + QTest::ignoreMessage(QtMsgType::QtInfoMsg, "Created"); + QScopedPointer<QObject> root {component.create()}; +} + +void tst_qqmllanguage::inlineComponentDuplicateNameError() +{ + QQmlEngine engine; + QUrl url = testFileUrl("inlineComponentDuplicateName.qml"); + QQmlComponent component(&engine, url); + + QString message = QLatin1String("%1:5 Inline component names must be unique per file\n").arg(url.toString()); + QScopedPointer<QObject> root {component.create()}; + QVERIFY(root.isNull()); + QVERIFY(component.isError()); + QCOMPARE(component.errorString(), message); +} + class TestItem : public QObject { Q_OBJECT diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp index 6155ea4637..305dd65c66 100644 --- a/tools/qmllint/findunqualified.cpp +++ b/tools/qmllint/findunqualified.cpp @@ -43,6 +43,7 @@ static const QString prefixedName(const QString &prefix, const QString &name) { + Q_ASSERT(!prefix.endsWith('.')); return prefix.isEmpty() ? name : (prefix + QLatin1Char('.') + name); } @@ -88,11 +89,9 @@ void FindUnqualifiedIDVisitor::parseHeaders(QQmlJS::AST::UiHeaderItemList *heade uri = uri->next; } path.chop(1); - QString prefix = QLatin1String(""); - if (import->asToken.isValid()) { - prefix += import->importId + QLatin1Char('.'); - } - importHelper(path, prefix, import->version->version); + importHelper(path, + import->asToken.isValid() ? import->importId.toString() : QString(), + import->version->version); } } header = header->next; diff --git a/tools/qmllint/importedmembersvisitor.cpp b/tools/qmllint/importedmembersvisitor.cpp index 676903d135..c5214f2bb9 100644 --- a/tools/qmllint/importedmembersvisitor.cpp +++ b/tools/qmllint/importedmembersvisitor.cpp @@ -57,10 +57,13 @@ ScopeTree *ImportedMembersVisitor::result(const QString &scopeName) const bool ImportedMembersVisitor::visit(UiObjectDefinition *definition) { ScopeTree::Ptr scope(new ScopeTree(ScopeType::QMLScope)); - auto qualifiedId = definition->qualifiedTypeNameId; - while (qualifiedId && qualifiedId->next) - qualifiedId = qualifiedId->next; - scope->setSuperclassName(qualifiedId->name.toString()); + QString superType; + for (auto segment = definition->qualifiedTypeNameId; segment; segment = segment->next) { + if (!superType.isEmpty()) + superType.append('.'); + superType.append(segment->name.toString()); + } + scope->setSuperclassName(superType); if (!m_rootObject) m_rootObject = scope; m_currentObjects.append(scope); diff --git a/tools/qmllint/scopetree.cpp b/tools/qmllint/scopetree.cpp index e7e0113f35..8c5358c7a5 100644 --- a/tools/qmllint/scopetree.cpp +++ b/tools/qmllint/scopetree.cpp @@ -134,6 +134,15 @@ private: QStringRef m_afterText; }; +static const QStringList unknownBuiltins = { + // TODO: "string" should be added to builtins.qmltypes, and the special handling below removed + QStringLiteral("alias"), // TODO: we cannot properly resolve aliases, yet + QStringLiteral("QRectF"), // TODO: should be added to builtins.qmltypes + QStringLiteral("QFont"), // TODO: should be added to builtins.qmltypes + QStringLiteral("QJSValue"), // We cannot say anything intelligent about untyped JS values. + QStringLiteral("variant"), // Same for generic variants +}; + bool ScopeTree::checkMemberAccess( const QString &code, FieldMemberList *members, @@ -169,10 +178,31 @@ bool ScopeTree::checkMemberAccess( } return true; } - const ScopeTree *type = (scopeIt->type() && access->m_parentType.isEmpty()) - ? scopeIt->type() - : types.value(typeName).get(); - return checkMemberAccess(code, access.get(), type, types, colorOut); + + if (!access->m_child) + return true; + + if (const ScopeTree *type = scopeIt->type()) { + if (access->m_parentType.isEmpty()) + return checkMemberAccess(code, access.get(), type, types, colorOut); + } + + if (unknownBuiltins.contains(typeName)) + return true; + + const auto it = types.find(typeName); + if (it != types.end()) + return checkMemberAccess(code, access.get(), it->get(), types, colorOut); + + colorOut.write("Warning: ", Warning); + colorOut.write( + QString::fromLatin1("Type \"%1\" of member \"%2\" not found at %3:%4.\n") + .arg(typeName) + .arg(access->m_name) + .arg(access->m_location.startLine) + .arg(access->m_location.startColumn), Normal); + printContext(colorOut, code, access->m_location); + return false; } const auto scopeMethodIt = scope->m_methods.find(access->m_name); @@ -251,13 +281,6 @@ bool ScopeTree::checkMemberAccess( return false; } -static const QStringList unknownBuiltins = { - QStringLiteral("alias"), // TODO: we cannot properly resolve aliases, yet - QStringLiteral("QRectF"), // TODO: should be added to builtins.qmltypes - QStringLiteral("QJSValue"), // We cannot say anything intelligent about untyped JS values. - QStringLiteral("variant"), // Same for generic variants -}; - bool ScopeTree::recheckIdentifiers( const QString &code, const QHash<QString, const ScopeTree *> &qmlIDs, @@ -287,9 +310,24 @@ bool ScopeTree::recheckIdentifiers( auto it = qmlIDs.find(memberAccessTree->m_name); if (it != qmlIDs.end()) { - if (!checkMemberAccess(code, memberAccessTree.get(), *it, types, colorOut)) - noUnqualifiedIdentifier = false; - continue; + if (*it != nullptr) { + if (!checkMemberAccess(code, memberAccessTree.get(), *it, types, colorOut)) + noUnqualifiedIdentifier = false; + continue; + } else if (memberAccessTree->m_child + && memberAccessTree->m_child->m_name.front().isUpper()) { + // It could be a qualified type name + const QString qualified = memberAccessTree->m_name + QLatin1Char('.') + + memberAccessTree->m_child->m_name; + const auto typeIt = types.find(qualified); + if (typeIt != types.end()) { + if (!checkMemberAccess(code, memberAccessTree->m_child.get(), typeIt->get(), + types, colorOut)) { + noUnqualifiedIdentifier = false; + } + continue; + } + } } auto qmlScope = currentScope->currentQMLScope(); |