aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qmldom
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qmldom')
-rw-r--r--tests/auto/qmldom/CMakeLists.txt6
-rw-r--r--tests/auto/qmldom/combined/CMakeLists.txt46
-rw-r--r--tests/auto/qmldom/combined/tst_dom_all.cpp43
-rw-r--r--tests/auto/qmldom/domdata/domitem/Base.qml5
-rw-r--r--tests/auto/qmldom/domdata/domitem/Derived.qml5
-rw-r--r--tests/auto/qmldom/domdata/domitem/ImportMeImplicitly.ui.qml6
-rw-r--r--tests/auto/qmldom/domdata/domitem/MyComponent.qml5
-rw-r--r--tests/auto/qmldom/domdata/domitem/MySingleton.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/TestImports.qml10
-rw-r--r--tests/auto/qmldom/domdata/domitem/WithImplicitImport.qml5
-rw-r--r--tests/auto/qmldom/domdata/domitem/Yyy.qml35
-rw-r--r--tests/auto/qmldom/domdata/domitem/aliasProperties.qml53
-rw-r--r--tests/auto/qmldom/domdata/domitem/astComments.qml12
-rw-r--r--tests/auto/qmldom/domdata/domitem/attachedOrGroupedProperties.qml34
-rw-r--r--tests/auto/qmldom/domdata/domitem/breakStatement.qml9
-rw-r--r--tests/auto/qmldom/domdata/domitem/callExpressions.qml61
-rw-r--r--tests/auto/qmldom/domdata/domitem/checkScopes.qml11
-rw-r--r--tests/auto/qmldom/domdata/domitem/commaExpression.qml8
-rw-r--r--tests/auto/qmldom/domdata/domitem/conditionalExpression.qml8
-rw-r--r--tests/auto/qmldom/domdata/domitem/continueStatement.qml9
-rw-r--r--tests/auto/qmldom/domdata/domitem/crashes/lambda.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/crashes/templateStrings.qml10
-rw-r--r--tests/auto/qmldom/domdata/domitem/ecmaScriptClass.qml44
-rw-r--r--tests/auto/qmldom/domdata/domitem/emptyMethodBody.qml6
-rw-r--r--tests/auto/qmldom/domdata/domitem/enumDeclarations.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/fieldMemberExpression.qml32
-rw-r--r--tests/auto/qmldom/domdata/domitem/fileLocationRegion.qml8
-rw-r--r--tests/auto/qmldom/domdata/domitem/fileLocationRegions/enums.qml12
-rw-r--r--tests/auto/qmldom/domdata/domitem/fileLocationRegions/functions.qml15
-rw-r--r--tests/auto/qmldom/domdata/domitem/fileLocationRegions/imports.qml11
-rw-r--r--tests/auto/qmldom/domdata/domitem/fileLocationRegions/pragmas.qml11
-rw-r--r--tests/auto/qmldom/domdata/domitem/finalizeScriptExpressions.qml29
-rw-r--r--tests/auto/qmldom/domdata/domitem/forStatements.qml15
-rw-r--r--tests/auto/qmldom/domdata/domitem/ifStatements.qml27
-rw-r--r--tests/auto/qmldom/domdata/domitem/import.js2
-rw-r--r--tests/auto/qmldom/domdata/domitem/inactiveVisitorMarkerCrash.qml13
-rw-r--r--tests/auto/qmldom/domdata/domitem/inlineComponents.qml17
-rw-r--r--tests/auto/qmldom/domdata/domitem/inlineObject.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/invalidAliasProperties.qml46
-rw-r--r--tests/auto/qmldom/domdata/domitem/iterationStatements.qml64
-rw-r--r--tests/auto/qmldom/domdata/domitem/module.mjs6
-rw-r--r--tests/auto/qmldom/domdata/domitem/nullStatements.qml14
-rw-r--r--tests/auto/qmldom/domdata/domitem/objectBindings.qml13
-rw-r--r--tests/auto/qmldom/domdata/domitem/propertyBindings.qml13
-rw-r--r--tests/auto/qmldom/domdata/domitem/returnStatements.qml14
-rw-r--r--tests/auto/qmldom/domdata/domitem/simplestJSStatement.js1
-rw-r--r--tests/auto/qmldom/domdata/domitem/simplestJSmodule.mjs1
-rw-r--r--tests/auto/qmldom/domdata/domitem/switchStatement.qml28
-rw-r--r--tests/auto/qmldom/domdata/domitem/test1.qml32
-rw-r--r--tests/auto/qmldom/domdata/domitem/tryStatements.qml10
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/decrement.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/delete.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/increment.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/not.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/postDecrement.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/postIncrement.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/tilde.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/typeof.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryMinus.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryPlus.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/unaryExpressions/void.qml7
-rw-r--r--tests/auto/qmldom/domdata/domitem/variableDeclarations.qml23
-rw-r--r--tests/auto/qmldom/domdata/domitem/visitTreeFilter.qml4
-rw-r--r--tests/auto/qmldom/domdata/dommerging/test1.qml12
-rw-r--r--tests/auto/qmldom/domdata/reformatter/MyComponent.qml5
-rw-r--r--tests/auto/qmldom/domdata/reformatter/arrowFunctions.qml6
-rw-r--r--tests/auto/qmldom/domdata/reformatter/arrowFunctionsReformatted.qml8
-rw-r--r--tests/auto/qmldom/domdata/reformatter/commentedFile.qml38
-rw-r--r--tests/auto/qmldom/domdata/reformatter/commentedFileReformatted.qml40
-rw-r--r--tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml40
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file1.qml47
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml56
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml50
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file1Unindented.qml47
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file2.qml58
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml67
-rw-r--r--tests/auto/qmldom/domdata/reformatter/inline.qml8
-rw-r--r--tests/auto/qmldom/domdata/reformatter/inlineReformatted.qml8
-rw-r--r--tests/auto/qmldom/domdata/reformatter/noMerge.qml14
-rw-r--r--tests/auto/qmldom/domdata/reformatter/noMergeReformatted.qml33
-rw-r--r--tests/auto/qmldom/domdata/reformatter/required.qml13
-rw-r--r--tests/auto/qmldom/domdata/reformatter/requiredReformatted.qml14
-rw-r--r--tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml14
-rw-r--r--tests/auto/qmldom/domdata/reformatter/spread.qml15
-rw-r--r--tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml18
-rw-r--r--tests/auto/qmldom/domdata/reformatter/template.qml22
-rw-r--r--tests/auto/qmldom/domdata/reformatter/templateReformatted.qml17
-rw-r--r--tests/auto/qmldom/domdata/reformatter/typeAnnotations.qml12
-rw-r--r--tests/auto/qmldom/domdata/reformatter/typeAnnotationsReformatted.qml12
-rw-r--r--tests/auto/qmldom/domitem/CMakeLists.txt28
-rw-r--r--tests/auto/qmldom/domitem/tst_qmldomitem.cpp191
-rw-r--r--tests/auto/qmldom/domitem/tst_qmldomitem.h3421
-rw-r--r--tests/auto/qmldom/errormessage/CMakeLists.txt14
-rw-r--r--tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp87
-rw-r--r--tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h20
-rw-r--r--tests/auto/qmldom/merging/CMakeLists.txt38
-rw-r--r--tests/auto/qmldom/merging/tst_dommerging.cpp5
-rw-r--r--tests/auto/qmldom/merging/tst_dommerging.h102
-rw-r--r--tests/auto/qmldom/path/CMakeLists.txt14
-rw-r--r--tests/auto/qmldom/path/tst_qmldompath.cpp230
-rw-r--r--tests/auto/qmldom/path/tst_qmldompath.h204
-rw-r--r--tests/auto/qmldom/reformatter/CMakeLists.txt36
-rw-r--r--tests/auto/qmldom/reformatter/tst_reformatter.cpp5
-rw-r--r--tests/auto/qmldom/reformatter/tst_reformatter.h774
-rw-r--r--tests/auto/qmldom/stringdumper/CMakeLists.txt14
-rw-r--r--tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp116
-rw-r--r--tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h81
107 files changed, 6449 insertions, 602 deletions
diff --git a/tests/auto/qmldom/CMakeLists.txt b/tests/auto/qmldom/CMakeLists.txt
index 00fb7850f4..f3186e2263 100644
--- a/tests/auto/qmldom/CMakeLists.txt
+++ b/tests/auto/qmldom/CMakeLists.txt
@@ -1,6 +1,12 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmldom.pro.
add_subdirectory(errormessage)
add_subdirectory(domitem)
add_subdirectory(path)
add_subdirectory(stringdumper)
+add_subdirectory(merging)
+add_subdirectory(reformatter)
+add_subdirectory(combined)
diff --git a/tests/auto/qmldom/combined/CMakeLists.txt b/tests/auto/qmldom/combined/CMakeLists.txt
new file mode 100644
index 0000000000..add44acf0b
--- /dev/null
+++ b/tests/auto/qmldom/combined/CMakeLists.txt
@@ -0,0 +1,46 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_dom_all Binary executing all tests together
+## (simpler to verify coverage)
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_dom_all LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/..
+ domdata/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_dom_all
+ SOURCES
+ tst_dom_all.cpp
+ ../stringdumper/tst_qmldomstringdumper.h
+ ../errormessage/tst_qmldomerrormessage.cpp ../errormessage/tst_qmldomerrormessage.h
+ ../path/tst_qmldompath.h
+ ../domitem/tst_qmldomitem.h
+ ../merging/tst_dommerging.h
+ ../reformatter/tst_reformatter.h
+ INCLUDE_DIRECTORIES
+ ..
+ DEFINES
+ NO_QTEST_MAIN
+ QT_DEPRECATED_WARNINGS
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/../domdata"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::Test
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_dom_all CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/domdata"
+)
diff --git a/tests/auto/qmldom/combined/tst_dom_all.cpp b/tests/auto/qmldom/combined/tst_dom_all.cpp
new file mode 100644
index 0000000000..6e3e0bd851
--- /dev/null
+++ b/tests/auto/qmldom/combined/tst_dom_all.cpp
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "stringdumper/tst_qmldomstringdumper.h"
+#include "errormessage/tst_qmldomerrormessage.h"
+#include "domitem/tst_qmldomitem.h"
+#include "merging/tst_dommerging.h"
+#include "path/tst_qmldompath.h"
+#include "reformatter/tst_reformatter.h"
+
+#include <QtCore/qdebug.h>
+
+int main(int argc, char *argv[])
+{
+ int status = 0;
+ {
+ QQmlJS::Dom::TestStringDumper test;
+ status |= QTest::qExec(&test, argc, argv);
+ }
+ {
+ QQmlJS::Dom::PathEls::TestPaths test;
+ status |= QTest::qExec(&test, argc, argv);
+ }
+ {
+ QQmlJS::Dom::TestErrorMessage test;
+ status |= QTest::qExec(&test, argc, argv);
+ }
+ {
+ QQmlJS::Dom::TestDomItem test;
+ status |= QTest::qExec(&test, argc, argv);
+ }
+ {
+ QQmlJS::Dom::TestDomMerging test;
+ status |= QTest::qExec(&test, argc, argv);
+ }
+ {
+ QQmlJS::Dom::TestReformatter test;
+ status |= QTest::qExec(&test, argc, argv);
+ }
+ if (status)
+ qWarning() << "Combined test failed!";
+ return status;
+}
diff --git a/tests/auto/qmldom/domdata/domitem/Base.qml b/tests/auto/qmldom/domdata/domitem/Base.qml
new file mode 100644
index 0000000000..919619ffe6
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/Base.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property int a
+}
diff --git a/tests/auto/qmldom/domdata/domitem/Derived.qml b/tests/auto/qmldom/domdata/domitem/Derived.qml
new file mode 100644
index 0000000000..8a6ce158c3
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/Derived.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Base {
+ property int b
+}
diff --git a/tests/auto/qmldom/domdata/domitem/ImportMeImplicitly.ui.qml b/tests/auto/qmldom/domdata/domitem/ImportMeImplicitly.ui.qml
new file mode 100644
index 0000000000..169d162469
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/ImportMeImplicitly.ui.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ property int helloProperty
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/MyComponent.qml b/tests/auto/qmldom/domdata/domitem/MyComponent.qml
new file mode 100644
index 0000000000..e661503f12
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/MyComponent.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Rectangle {
+ text: "bla"
+}
diff --git a/tests/auto/qmldom/domdata/domitem/MySingleton.qml b/tests/auto/qmldom/domdata/domitem/MySingleton.qml
new file mode 100644
index 0000000000..241b38b664
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/MySingleton.qml
@@ -0,0 +1,7 @@
+pragma Singleton
+import QtQuick 2.12
+
+QtObject {
+ signal mySignal()
+ function myFunc() {}
+}
diff --git a/tests/auto/qmldom/domdata/domitem/TestImports.qml b/tests/auto/qmldom/domdata/domitem/TestImports.qml
new file mode 100644
index 0000000000..8581cd2611
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/TestImports.qml
@@ -0,0 +1,10 @@
+import QtQuick
+import "../.."
+import "../dommerging"
+import "C:/some/path"
+import "http://bla.com/"
+import "/absolute/path"
+
+Item {
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/WithImplicitImport.qml b/tests/auto/qmldom/domdata/domitem/WithImplicitImport.qml
new file mode 100644
index 0000000000..5560aee727
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/WithImplicitImport.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/Yyy.qml b/tests/auto/qmldom/domdata/domitem/Yyy.qml
new file mode 100644
index 0000000000..b2235ff9cc
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/Yyy.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick 2.0
+import QtQuick as QQ
+
+Zzz {
+ id: root
+ width: height
+ Rectangle {
+ color: "green"
+ anchors.fill: parent
+ height: root.foo.height
+ width: root.height
+
+ }
+
+ function lala() {}
+ property Rectangle foo: Rectangle{ height: 200 }
+ function longfunction(a, b, c = "c", d = "d"): string {
+ return "hehe: " + c + d
+ }
+
+ // documentedFunction: is documented
+ // returns 'Good'
+ function documentedFunction(arg1, arg2 = "Qt"): string {
+ return "Good"
+ }
+ QQ.Rectangle {
+ color:"red"
+ }
+
+ component IC: Zzz { property SomeBase data }
+ property SomeBase mySomeBase
+}
diff --git a/tests/auto/qmldom/domdata/domitem/aliasProperties.qml b/tests/auto/qmldom/domdata/domitem/aliasProperties.qml
new file mode 100644
index 0000000000..e2521b7afe
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/aliasProperties.qml
@@ -0,0 +1,53 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+
+Item {
+ id: root
+ property alias objRef1: root
+ property alias objRef2: obj1
+ property int p1: 1
+ property alias a1i: root.p1
+ property alias a2q: obj1.objectName
+ property Item i1: Item{
+ id: it1
+ objectName: "it1"
+ property QtObject i11: QtObject {
+ id: it11
+ objectName: "it11"
+ property int p1: 42
+ }
+ }
+ QtObject {
+ id: obj1
+ objectName:"obj1"
+ property alias objRef2: obj2
+ property real p1: 2.0
+ property int p2: 33
+ property alias a3i: root.p1
+ property alias a5i: obj1.a4i
+ property alias a4i: root.a1i
+ property alias a6r: obj2.p2r
+ }
+ QtObject {
+ id: obj2
+ objectName:"obj2"
+ property real p2r: 3.0
+ property alias a8i: obj1.a3i
+ property alias a9q: root.a2q
+ property alias a11I: it1.objectName
+ property alias a11Q: it1.i11.objectName
+ property alias a12q: obj1.objectName
+ }
+ Rectangle {
+ color: "red"
+ Text{
+ id:t1
+ text: obj2.a11Q
+ }
+ Text{
+ text: obj1.objRef2.p2r
+ anchors.top: t1.bottom
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/astComments.qml b/tests/auto/qmldom/domdata/domitem/astComments.qml
new file mode 100644
index 0000000000..6fca4c69e5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/astComments.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function ninja() {
+ // tst comment
+ const patron = 34;
+/*Ast Comment*/ const ppp = 23;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/attachedOrGroupedProperties.qml b/tests/auto/qmldom/domdata/domitem/attachedOrGroupedProperties.qml
new file mode 100644
index 0000000000..ff7720506c
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/attachedOrGroupedProperties.qml
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Text {
+ id: grouped
+
+ // Dot notation
+ Binding {
+ grouped.font.family: "mono"
+ }
+
+ // Group notation
+ Test {
+ id: test
+ myText {
+ font {
+ pixelSize: 12
+ }
+ }
+ }
+
+ component Test : Rectangle {
+ property Text myText: text1
+ Text {
+ id: text1
+ }
+ }
+
+ Keys.onPressed: (event)=> {
+
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/breakStatement.qml b/tests/auto/qmldom/domdata/domitem/breakStatement.qml
new file mode 100644
index 0000000000..212fe095c0
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/breakStatement.qml
@@ -0,0 +1,9 @@
+import QtQuick
+
+Item {
+ function f() {
+ break helloWorld;
+ break;
+ }
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/callExpressions.qml b/tests/auto/qmldom/domdata/domitem/callExpressions.qml
new file mode 100644
index 0000000000..d0de350fae
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/callExpressions.qml
@@ -0,0 +1,61 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property var p: f()
+ // crash if arguments are wrongly collected, e.g. because they are stolen from other unimplemented scriptelements
+ property var p2: f(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
+
+ // dummy test: if these ones fail, then the deconstructing parameters cannot possibly work (they share some
+ // common code).
+ function deconstruct() {
+ let { a } = { a: 32, b: 42}, { b, c } = { b: 32, c: 42}, [ d, e, f ] = [ 111, 222, 333 ];
+ let x = [,,,,[1,2,3],,];
+ }
+
+ function f(q,w,e,r,t,y) {
+ let helloF = 32
+ return 42
+ }
+
+ function fWithDefault(q = 1, w = 2, e, r = 4, t, y = 6) {
+ let helloFWithDefault = {}
+ return 42
+ }
+
+ function marmelade(...onTheBread) {
+ let helloMarmelade = 123
+ return 42
+ }
+
+ function marmelade2(spread, it,...onTheBread) {
+ let helloMarmelade2 = 123
+ return 42
+ }
+
+ // check if nothing crashes for empty stuff
+ function empty({}, []) {
+ let {} = {};
+ let [] = [];
+ }
+
+ component MyType: Item{}
+
+ function withTypes(a: int, b: MyType) {}
+ function empty() {}
+ signal mySignal()
+
+
+ property var p3: evil({ hello: "World", y: "yyy"}, [1,2,3], { is: {a: 111, lot: 222, of: 333, fun: 444, really: ["!",]}})
+
+ function evil({ hello = "world", x = 42 },
+ [n = 42, m = 43, o = 44],
+ { destructuring, is = {a, lot, of}, fun = 42 } = {destructuring : 123, is : {x : 123}, fun : 456}) {
+ const helloEvil = "asdf"
+ return 42
+ }
+
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/checkScopes.qml b/tests/auto/qmldom/domdata/domitem/checkScopes.qml
new file mode 100644
index 0000000000..39903e9d90
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/checkScopes.qml
@@ -0,0 +1,11 @@
+import QtQuick
+
+Item {
+ id: root
+ property int myInt
+ property int myInt2
+
+ myInt: 42
+ myInt2: 123
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/commaExpression.qml b/tests/auto/qmldom/domdata/domitem/commaExpression.qml
new file mode 100644
index 0000000000..c6576b5dad
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/commaExpression.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+ function f(a, b, c) {
+ a, b, c;
+ }
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/conditionalExpression.qml b/tests/auto/qmldom/domdata/domitem/conditionalExpression.qml
new file mode 100644
index 0000000000..027078b0ef
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/conditionalExpression.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+ function f(a, b, c) {
+ a?b:c;
+ }
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/continueStatement.qml b/tests/auto/qmldom/domdata/domitem/continueStatement.qml
new file mode 100644
index 0000000000..735ded4bda
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/continueStatement.qml
@@ -0,0 +1,9 @@
+import QtQuick
+
+Item {
+ function f() {
+ continue helloWorld;
+ continue;
+ }
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/crashes/lambda.qml b/tests/auto/qmldom/domdata/domitem/crashes/lambda.qml
new file mode 100644
index 0000000000..cab0fec143
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/crashes/lambda.qml
@@ -0,0 +1,7 @@
+import QtQuick.Controls
+
+Action {
+ onTriggered: foo(Bla.Bar, function() {
+ console.log("Hello")
+ })
+}
diff --git a/tests/auto/qmldom/domdata/domitem/crashes/templateStrings.qml b/tests/auto/qmldom/domdata/domitem/crashes/templateStrings.qml
new file mode 100644
index 0000000000..feb5646496
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/crashes/templateStrings.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+
+Item {
+ property string verbatim1: 'A "verbatim" string!'
+ property string verbatim2: "A 'verbatim' string\u2757"
+ property string verbatim3: `400 + 300 is ${400 + 300}.
+
+mutliline`
+}
diff --git a/tests/auto/qmldom/domdata/domitem/ecmaScriptClass.qml b/tests/auto/qmldom/domdata/domitem/ecmaScriptClass.qml
new file mode 100644
index 0000000000..e86ea737e5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/ecmaScriptClass.qml
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+
+function f() {
+
+var count = 0;
+
+class Person {
+ constructor(name) {
+ this._name = name
+ }
+}
+
+class Employee extends Person{
+
+
+ constructor(name, age) {
+ super(name);
+ this._name = name;
+ this._age = age;
+ ++count;
+ }
+
+ get /* do we get the comment? */ name() {
+ return this._name.toUpperCase();
+ }
+
+ set name(newName){
+ if (newName) {
+ this._name = newName;
+ }
+ }
+
+ static get count() { return count;}
+}
+
+
+}
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/emptyMethodBody.qml b/tests/auto/qmldom/domdata/domitem/emptyMethodBody.qml
new file mode 100644
index 0000000000..d987d3e649
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/emptyMethodBody.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+Item {
+ function f(x) {}
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/enumDeclarations.qml b/tests/auto/qmldom/domdata/domitem/enumDeclarations.qml
new file mode 100644
index 0000000000..260f079fd6
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/enumDeclarations.qml
@@ -0,0 +1,7 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+Item {
+ enum Cats { Patron, Mafya, Kivrik = -1 }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/fieldMemberExpression.qml b/tests/auto/qmldom/domdata/domitem/fieldMemberExpression.qml
new file mode 100644
index 0000000000..8890a0c8cb
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fieldMemberExpression.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ id: root
+
+ property int p: 42
+
+ property Item property1: Item {
+ property bool p: 44
+ property Item property2: Item {
+ property string p: "Hello World"
+ property Item property3: Item {
+ property real p: 123
+ }
+ }
+ }
+
+ property var p1: p
+ property var p1Qualified: root.p
+ property var p1Bracket: root["p"]
+ property var p1Index: root[42]
+ property var p1Key: root[p]
+
+ property var p2: property1.p
+ property var p2Qualified: root.property1.p
+
+ property var p3: property1.property2.p
+ property var p3Qualified: root.property1.property2.p
+}
diff --git a/tests/auto/qmldom/domdata/domitem/fileLocationRegion.qml b/tests/auto/qmldom/domdata/domitem/fileLocationRegion.qml
new file mode 100644
index 0000000000..f441efb5e2
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fileLocationRegion.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+ property int helloWorld
+
+ // before helloWorld binding
+ helloWorld: 42 // after helloWorld binding
+}
diff --git a/tests/auto/qmldom/domdata/domitem/fileLocationRegions/enums.qml b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/enums.qml
new file mode 100644
index 0000000000..e6e0f7a205
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/enums.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ enum OSC {
+ sin = 1,
+ saw,
+ tri = 16
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/fileLocationRegions/functions.qml b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/functions.qml
new file mode 100644
index 0000000000..e045e5a4cc
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/functions.qml
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function a() : int {}
+
+ component A : Item {
+ function b(k: int) : int {}
+ }
+
+ signal k(int a)
+ signal kk(a: int)
+}
diff --git a/tests/auto/qmldom/domdata/domitem/fileLocationRegions/imports.qml b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/imports.qml
new file mode 100644
index 0000000000..ccf012bc63
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/imports.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQml 2.15
+import QtQuick.Controls as Patron
+import "../cats"
+
+Item {
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/fileLocationRegions/pragmas.qml b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/pragmas.qml
new file mode 100644
index 0000000000..5f34f9ae33
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/pragmas.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+pragma Singleton
+pragma ComponentBehavior: Bound
+pragma FunctionSignatureBehavior: Enforced
+pragma ValueTypeBehavior: Copy,Addressable
+
+import QtQml
+
+QtObject {}
diff --git a/tests/auto/qmldom/domdata/domitem/finalizeScriptExpressions.qml b/tests/auto/qmldom/domdata/domitem/finalizeScriptExpressions.qml
new file mode 100644
index 0000000000..5a0c811396
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/finalizeScriptExpressions.qml
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property var binding
+ binding: 42
+
+ property var bindingInPropertyDefinition: 123
+
+ function return42(aa: Item = 33, bb: string = "Hello", cc = binding): int {
+ return 42
+ }
+ function empty(aa: Item, bb: string, cc) {}
+ function full(aa: Item, bb: string, cc)
+ {
+ const x = 42;
+ const formula = (x + 5) * 2 + 10 - x
+ return formula;
+ }
+
+ id: idBinding
+
+ property var arrayBinding
+ arrayBinding:[]
+ property var objectBinding
+ objectBinding: Item {}
+}
diff --git a/tests/auto/qmldom/domdata/domitem/forStatements.qml b/tests/auto/qmldom/domdata/domitem/forStatements.qml
new file mode 100644
index 0000000000..6c06098309
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/forStatements.qml
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function f() {
+ let sum = 0, helloWorld = "hello"
+ for (let i = 0; i < 100; i = i + 1) {
+ sum = sum + 1
+ for (;;)
+ i = 42
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/ifStatements.qml b/tests/auto/qmldom/domdata/domitem/ifStatements.qml
new file mode 100644
index 0000000000..90d1bd3ac8
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/ifStatements.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ function conditional() {
+ let i = 5
+ if (i)
+ i = 42
+
+ if (i == 55)
+ i = 32
+ else
+ i = i - 1
+
+ if (i == 42) {
+ i = 111
+ }
+
+ if (i == 746) {
+ i = 123
+ } else {
+ i = 456
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/import.js b/tests/auto/qmldom/domdata/domitem/import.js
new file mode 100644
index 0000000000..e13db4c1a4
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/import.js
@@ -0,0 +1,2 @@
+.import "main.js" as Main
+console.log(Main.a);
diff --git a/tests/auto/qmldom/domdata/domitem/inactiveVisitorMarkerCrash.qml b/tests/auto/qmldom/domdata/domitem/inactiveVisitorMarkerCrash.qml
new file mode 100644
index 0000000000..c706251e47
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/inactiveVisitorMarkerCrash.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Window {
+ width: 640
+ height: 480
+ visible: true
+ title: qsTr("Hello World")
+
+ HelloWorld { myP: 55; myPPP: 55 }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/inlineComponents.qml b/tests/auto/qmldom/domdata/domitem/inlineComponents.qml
new file mode 100644
index 0000000000..5ae0af4eb1
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/inlineComponents.qml
@@ -0,0 +1,17 @@
+import QtQuick
+
+Item {
+ id: mainComponent
+
+ component IC1: Item { property string firstIC }
+ component IC2: Item { property string secondIC }
+
+ Item {
+ Item {
+ Item {
+ component IC3: Item { property string thirdIC }
+ }
+ }
+ }
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/inlineObject.qml b/tests/auto/qmldom/domdata/domitem/inlineObject.qml
new file mode 100644
index 0000000000..2f5941f4da
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/inlineObject.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ component IC: Item {}
+ property var myItem: Item {}
+ property var myItem2: IC {}
+}
diff --git a/tests/auto/qmldom/domdata/domitem/invalidAliasProperties.qml b/tests/auto/qmldom/domdata/domitem/invalidAliasProperties.qml
new file mode 100644
index 0000000000..7998642ef5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/invalidAliasProperties.qml
@@ -0,0 +1,46 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+import QtQuick
+
+Item {
+ id: root
+ property alias loop1: root.loop1
+ property alias loop2: obj1.loop3
+ property int p1: 1
+ property Item i1: Item{
+ id: it1
+ property Item i11: Item{
+ id: it11
+ property int p1: 42
+ }
+ }
+ QtObject {
+ id: obj1
+ property alias objRef2: obj2
+ property real p1: 2.0
+ property alias loop3: root.loop2
+ property alias loop4: root.loop1
+ property alias a3i: root.p1
+ property alias a7i: obj2.a8i // loop error
+ }
+ QtObject {
+ id: obj2
+ property alias a8i: obj1.a3i
+ property alias tooDeep: root.i1.i11.p1
+ property alias invalid1: noId
+ property alias invalid2: noId.objectName
+ property alias a13i: obj1.objRef2.a8i // invalid property a8i error
+ }
+ property alias tooDeepRef: obj2.tooDeep
+ Rectangle {
+ color: "red"
+ Text{
+ id:t1
+ text: obj1.p1
+ }
+ Text{
+ text: obj2.a8i
+ anchors.top: t1.bottom
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/iterationStatements.qml b/tests/auto/qmldom/domdata/domitem/iterationStatements.qml
new file mode 100644
index 0000000000..e75eeb2d7d
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/iterationStatements.qml
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+
+ function whileStatement() {
+ const i = 10;
+ while (i > 0) {
+ i = i -1;
+ while ( i > 100) {}
+ }
+
+ while (i > 0) i = i-1
+ }
+
+ function doWhile() {
+ let a = 7;
+ do {
+ const b = a;
+ a = a - 1;
+ } while (a > 0)
+
+ do a = a-1; while (a > 0)
+ }
+
+ function forOf() {
+ const iterable = [[1,2], [3,4],]
+ for (var [a,b] of iterable) {
+
+ let t;
+ for (const [a1, , a2, ...rest] of array) {
+
+ }
+ for (const k of [1,2,3,4,,,]) {
+ t += k;
+ }
+ for (t of a) {
+ {}
+ }
+ for (t of a) t += k
+ }
+ }
+
+ function forIn() {
+ const enumerable = {
+ list: [1, 2, 3, 4, 5],
+ name: 'John',
+ age: 25
+ };
+
+ for (var [a,b,c,d] in enumerable) {
+ let t;
+ for (t in enumerable) {
+ {}
+ }
+ for (const [a1, , a2, ...rest] in enumerable.list) {
+
+ }
+ for (let t in enumerable) t += k
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/module.mjs b/tests/auto/qmldom/domdata/domitem/module.mjs
new file mode 100644
index 0000000000..6838766329
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/module.mjs
@@ -0,0 +1,6 @@
+
+import { helper } from "utils.mjs"
+
+export function entry() {
+ return helper()
+}
diff --git a/tests/auto/qmldom/domdata/domitem/nullStatements.qml b/tests/auto/qmldom/domdata/domitem/nullStatements.qml
new file mode 100644
index 0000000000..f4be9b30de
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/nullStatements.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function testForNull() {
+ for (;;)
+ {}
+ for (;;)
+ x
+ {} {} {} {}
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/objectBindings.qml b/tests/auto/qmldom/domdata/domitem/objectBindings.qml
new file mode 100644
index 0000000000..c02a7113a5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/objectBindings.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick as QQ
+
+Item {
+ id: root
+ Item {}
+ QQ.Item {}
+ property var x: root.
+ QQ.Drag {}
+}
diff --git a/tests/auto/qmldom/domdata/domitem/propertyBindings.qml b/tests/auto/qmldom/domdata/domitem/propertyBindings.qml
new file mode 100644
index 0000000000..3c2931cb5d
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/propertyBindings.qml
@@ -0,0 +1,13 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ property int a: 42
+ property int b
+
+ b: a
+
+ id: root
+}
diff --git a/tests/auto/qmldom/domdata/domitem/returnStatements.qml b/tests/auto/qmldom/domdata/domitem/returnStatements.qml
new file mode 100644
index 0000000000..4f503244fb
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/returnStatements.qml
@@ -0,0 +1,14 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ // TODO: Add a test for returning void
+ function returningFunction(i) {
+ if (i)
+ return 123;
+ else
+ return 1 + 2;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/simplestJSStatement.js b/tests/auto/qmldom/domdata/domitem/simplestJSStatement.js
new file mode 100644
index 0000000000..d954a87fc0
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/simplestJSStatement.js
@@ -0,0 +1 @@
+let v=1;
diff --git a/tests/auto/qmldom/domdata/domitem/simplestJSmodule.mjs b/tests/auto/qmldom/domdata/domitem/simplestJSmodule.mjs
new file mode 100644
index 0000000000..401b25314b
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/simplestJSmodule.mjs
@@ -0,0 +1 @@
+export function entry() {}
diff --git a/tests/auto/qmldom/domdata/domitem/switchStatement.qml b/tests/auto/qmldom/domdata/domitem/switchStatement.qml
new file mode 100644
index 0000000000..b2bacec8c0
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/switchStatement.qml
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+QtObject {
+ function switchStatement(){
+ const animals = "cat";
+ const no = 0;
+ switch (animals) {
+ case "cat":
+ switch (no) {
+ case 0: return "patron";
+ case 1: return "mafik";
+ default: return "none";
+ }
+ case "dog": {
+ // check if qqmljsscope is created for this case
+ const name = "tekila";
+ let another = "mutantx";
+ return name;
+ }
+ default: return "monster";
+ case "moreCases!":
+ return "moreCaseClauses?"
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/test1.qml b/tests/auto/qmldom/domdata/domitem/test1.qml
new file mode 100644
index 0000000000..4a28129ee9
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/test1.qml
@@ -0,0 +1,32 @@
+import QtQuick
+import QtQuick as QQ
+
+Window {
+ visible: true
+ width: 640
+ height: 480
+ title: qsTr("Scroll")
+
+ Rectangle {
+ anchors.fill: parent
+
+ ListView {
+ width: parent.width
+ model: {
+ MySingleton.mySignal()
+ 20
+ }
+ delegate: ItemDelegate {
+ id: root
+ text: "Item " + (index + 1)
+ width: parent.width
+ Rectangle {
+ text: "bla"
+ }
+ MyComponent {
+ text: root.text
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/tryStatements.qml b/tests/auto/qmldom/domdata/domitem/tryStatements.qml
new file mode 100644
index 0000000000..5bd29db058
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/tryStatements.qml
@@ -0,0 +1,10 @@
+import QtQuick
+
+Item {
+ function f() {
+ try { insideTry; } catch(catchExpression) { insideCatch; } finally { insideFinally; }
+ try { insideTry; } catch(catchExpression) { insideCatch; }
+ try { insideTry; } finally { insideFinally; }
+ }
+
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/decrement.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/decrement.qml
new file mode 100644
index 0000000000..fb9fe6f252
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/decrement.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ --a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/delete.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/delete.qml
new file mode 100644
index 0000000000..39063b8c2f
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/delete.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ delete a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/increment.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/increment.qml
new file mode 100644
index 0000000000..e7acd3dfe5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/increment.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ ++a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/not.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/not.qml
new file mode 100644
index 0000000000..6c3a1aa035
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/not.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ !a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/postDecrement.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/postDecrement.qml
new file mode 100644
index 0000000000..52d170fae0
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/postDecrement.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ a--;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/postIncrement.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/postIncrement.qml
new file mode 100644
index 0000000000..30b4da8c8f
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/postIncrement.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ a++;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/tilde.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/tilde.qml
new file mode 100644
index 0000000000..b35a00bd79
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/tilde.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ ~a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/typeof.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/typeof.qml
new file mode 100644
index 0000000000..1696c5bd27
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/typeof.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ typeof a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryMinus.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryMinus.qml
new file mode 100644
index 0000000000..e711c4d2f5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryMinus.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ -a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryPlus.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryPlus.qml
new file mode 100644
index 0000000000..1b9ecbf5b5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/unaryPlus.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ +a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/unaryExpressions/void.qml b/tests/auto/qmldom/domdata/domitem/unaryExpressions/void.qml
new file mode 100644
index 0000000000..d9396e1b39
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/unaryExpressions/void.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.15
+
+Item {
+ function f(a) {
+ void a;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/variableDeclarations.qml b/tests/auto/qmldom/domdata/domitem/variableDeclarations.qml
new file mode 100644
index 0000000000..7d5d2ac9a5
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/variableDeclarations.qml
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ function count() {
+ let one = 1, two = 2, three = 3, four = 4, five = 5, six = 6
+ let testMe = 123
+ }
+ // for now only numeric literal and string literal is supported
+ function f() {
+ let sum = 0, helloWorld = "hello"
+ const a = 3;
+ const b = "patron";
+ var aa = helloWorld, bb = aa;
+
+ const bool1 = true;
+ let bool2 = false;
+ var nullVar = null;
+ return sum;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/domitem/visitTreeFilter.qml b/tests/auto/qmldom/domdata/domitem/visitTreeFilter.qml
new file mode 100644
index 0000000000..5d0b06f91f
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/visitTreeFilter.qml
@@ -0,0 +1,4 @@
+
+QtObject {
+ property int helloProperty
+}
diff --git a/tests/auto/qmldom/domdata/dommerging/test1.qml b/tests/auto/qmldom/domdata/dommerging/test1.qml
new file mode 100644
index 0000000000..6347510c3a
--- /dev/null
+++ b/tests/auto/qmldom/domdata/dommerging/test1.qml
@@ -0,0 +1,12 @@
+import QtQuick
+
+Rectangle {
+id: root
+
+width: { height *3/4 }
+height: 480
+Rectangle {
+ width: 58
+ heigth:80
+}
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/MyComponent.qml b/tests/auto/qmldom/domdata/reformatter/MyComponent.qml
new file mode 100644
index 0000000000..e661503f12
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/MyComponent.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Rectangle {
+ text: "bla"
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/arrowFunctions.qml b/tests/auto/qmldom/domdata/reformatter/arrowFunctions.qml
new file mode 100644
index 0000000000..5058d08ae3
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/arrowFunctions.qml
@@ -0,0 +1,6 @@
+Item {
+ arrow1: (x) => x
+ arrow2: (x) => x + x
+ arrow3: (x) => x > 3 ? x * x : x
+ arrow4: (x) => { return x > 3 ? x * x : x; }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/arrowFunctionsReformatted.qml b/tests/auto/qmldom/domdata/reformatter/arrowFunctionsReformatted.qml
new file mode 100644
index 0000000000..4b0bbabd3d
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/arrowFunctionsReformatted.qml
@@ -0,0 +1,8 @@
+Item {
+ arrow1: x => x
+ arrow2: x => x + x
+ arrow3: x => x > 3 ? x * x : x
+ arrow4: x => {
+ return x > 3 ? x * x : x;
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/commentedFile.qml b/tests/auto/qmldom/domdata/reformatter/commentedFile.qml
new file mode 100644
index 0000000000..1f64a0913b
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/commentedFile.qml
@@ -0,0 +1,38 @@
+// pre comment
+import QtQuick 2.15
+
+// pre item comment
+/* multi
+line */ // comment after multi line
+Item {
+function method(x) // function comment
+{ // just returns the double of x
+ return 2*x // yep twice as much
+} // post method
+// binding comment
+a: {// header
+
+// before x()
+// before x()
+x() // after x
+// before y = 8 + z + zz
+
+// before y = 8 + z + zz
+y = 8 +
+// before z
+z + // after z
+// before zz
+zz - // after z + zz
+/*before (a b)*/(/* before a */ a * /* after a */ b * /*after b*/ c) // after (a * b * c)
+
+ if (y == 6) // if comment
+ console.log("pippo")
+
+ a + b // comment
+
+// footer
+}
+// post binding comment
+}
+// footer file comment
+/* second comment */ /* third comment */
diff --git a/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted.qml b/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted.qml
new file mode 100644
index 0000000000..ab722f94be
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted.qml
@@ -0,0 +1,40 @@
+// pre comment
+import QtQuick 2.15
+
+// pre item comment
+/* multi
+line */ // comment after multi line
+Item {
+ function method(x) // function comment
+ { // just returns the double of x
+ return 2 * x; // yep twice as much
+ } // post method
+
+ // binding comment
+ a: {
+ // header
+
+ // before x()
+ // before x()
+ x(); // after x
+ // before y = 8 + z + zz
+
+ // before y = 8 + z + zz
+ y = 8 +
+ // before z
+ z + // after z
+ // before zz
+ zz - // after z + zz
+ /*before (a b)*/(/* before a */ a * /* after a */ b * /*after b*/ c); // after (a * b * c)
+
+ if (y == 6) // if comment
+ console.log("pippo");
+
+ a + b; // comment
+
+ // footer
+ }
+ // post binding comment
+}
+// footer file comment
+/* second comment */ /* third comment */
diff --git a/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml b/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml
new file mode 100644
index 0000000000..c6e33f9389
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml
@@ -0,0 +1,40 @@
+// pre comment
+import QtQuick 2.15
+
+// pre item comment
+/* multi
+line */ // comment after multi line
+Item {
+ function method(x) // function comment
+ { // just returns the double of x
+ return 2 * x; // yep twice as much
+ } // post method
+
+ // binding comment
+ a: {
+ // header
+
+ // before x()
+ // before x()
+ x(); // after x
+ // before y = 8 + z + zz
+
+ // before y = 8 + z + zz
+ y = 8 +
+ // before z
+ z + // after z
+ // before zz
+ zz - // after z + zz
+ /*before (a b)*/(/* before a */ a * /* after a */ b * /*after b*/ c); // after (a * b * c)
+
+ if (y == 6) // if comment
+ console.log("pippo");
+
+ a + b; // comment
+
+ // footer
+ }
+ // post binding comment
+}
+// footer file comment
+/* second comment */ /* third comment */
diff --git a/tests/auto/qmldom/domdata/reformatter/file1.qml b/tests/auto/qmldom/domdata/reformatter/file1.qml
new file mode 100644
index 0000000000..e0382bf57c
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/file1.qml
@@ -0,0 +1,47 @@
+pragma pippo
+import QtQuick 2.15
+import QtQuick.Window 2.15
+
+Window {
+ visible: true
+ width: 640
+ height: 480
+ title: qsTr("Scroll")
+
+ property var arr: [1,2,3]
+ property var arrTrailingComma: [1,2,3,]
+
+ Rectangle {
+ anchors.fill: parent
+
+ Behavior on opacity {}
+
+ ListView {
+ width: parent.width
+ model: {
+ MySingleton.mySignal()
+ 20
+ }
+ delegate: ItemDelegate {
+ id: root
+ text: "Item " + (index + 1)
+ width: parent.width
+ Rectangle {
+ text: "bla"
+ }
+ MyComponent {
+ text: root.text
+ function f(v = 4){
+ let c = 0
+ return {
+ a: function(){ if (b == 0) c += 78*5*v; }()
+ }
+ }
+ property int a: {
+ return 45
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml b/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml
new file mode 100644
index 0000000000..6a24f907d1
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml
@@ -0,0 +1,56 @@
+pragma pippo
+import QtQuick 2.15
+import QtQuick.Window 2.15
+
+Window {
+ property var arr: [1, 2, 3]
+ property var arrTrailingComma: [1, 2, 3,]
+
+ height: 480
+ title: qsTr("Scroll")
+ visible: true
+ width: 640
+
+ Rectangle {
+ anchors.fill: parent
+
+ Behavior on opacity {
+ }
+
+ ListView {
+ model: {
+ MySingleton.mySignal();
+ 20;
+ }
+ width: parent.width
+
+ delegate: ItemDelegate {
+ id: root
+
+ text: "Item " + (index + 1)
+ width: parent.width
+
+ Rectangle {
+ text: "bla"
+ }
+ MyComponent {
+ property int a: {
+ return 45;
+ }
+
+ function f(v = 4) {
+ let c = 0;
+ return {
+ a: function () {
+ if (b == 0)
+ c += 78 * 5 * v;
+ }()
+ };
+ }
+
+ text: root.text
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml b/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml
new file mode 100644
index 0000000000..2e7483f453
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml
@@ -0,0 +1,50 @@
+pragma pippo
+import QtQuick 2.15
+import QtQuick.Window 2.15
+
+Window {
+ visible: true
+ width: 640
+ height: 480
+ title: qsTr("Scroll")
+
+ property var arr: [1, 2, 3]
+ property var arrTrailingComma: [1, 2, 3,]
+
+ Rectangle {
+ anchors.fill: parent
+
+ Behavior on opacity {}
+
+ ListView {
+ width: parent.width
+ model: {
+ MySingleton.mySignal();
+ 20;
+ }
+ delegate: ItemDelegate {
+ id: root
+ text: "Item " + (index + 1)
+ width: parent.width
+ Rectangle {
+ text: "bla"
+ }
+ MyComponent {
+ text: root.text
+ function f(v = 4) {
+ let c = 0;
+ return {
+ a: function () {
+ if (b == 0)
+ c += 78 * 5 * v;
+ }()
+ };
+ }
+ property int a: {
+ return 45;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/file1Unindented.qml b/tests/auto/qmldom/domdata/reformatter/file1Unindented.qml
new file mode 100644
index 0000000000..4085b91e6e
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/file1Unindented.qml
@@ -0,0 +1,47 @@
+pragma pippo
+import QtQuick 2.15
+import QtQuick.Window 2.15
+
+Window {
+visible: true
+width: 640
+height: 480
+title: qsTr("Scroll")
+
+property var arr: [1,2,3]
+property var arrTrailingComma: [1,2,3,]
+
+Rectangle {
+anchors.fill: parent
+
+Behavior on opacity {}
+
+ListView {
+width: parent.width
+model: {
+MySingleton.mySignal()
+20
+}
+delegate: ItemDelegate {
+id: root
+text: "Item " + (index + 1)
+width: parent.width
+Rectangle {
+text: "bla"
+}
+MyComponent {
+text: root.text
+function f(v = 4){
+let c = 0
+return {
+a: function(){ if (b == 0) c += 78*5*v; }()
+}
+}
+property int a: {
+return 45
+}
+}
+}
+}
+}
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/file2.qml b/tests/auto/qmldom/domdata/reformatter/file2.qml
new file mode 100644
index 0000000000..5aeebc14e6
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/file2.qml
@@ -0,0 +1,58 @@
+import QtQuick 2.15
+import QtQuick.Window 2.15
+
+Window {
+ visible: true
+ width: 640
+ height: 480
+ title: qsTr("Scroll")
+
+ onTrigger: console.log("trigger signal emitted")
+
+ onSend: {
+ console.log("send signal emitted with notice: " + notice)
+ }
+
+ Rectangle {
+ anchors.fill: parent
+
+ ListView {
+ width: parent.width
+ model: {
+ MySingleton.mySignal()
+ 20
+ }
+ delegate: ItemDelegate {
+ id: root
+ text: "Item " + (index + 1)
+ width: parent.width
+ Rectangle {
+ text: "bla"
+ }
+ MyComponent {
+ text: root.text
+ function f(v){
+ let c = 0
+ return {
+ a: function(){ if (b == 0) c += 78*5*v; }()
+ }
+ }
+ property int a: {
+ let x = isNaN;
+ (45)
+ x ? 5 + 1 : 8
+ }
+ property list<Item> b: [ Item{
+ width: 5
+ },
+ Item{
+ width: 6
+ }]
+ }
+ }
+ }
+ }
+ Component.onCompleted: {
+ console.log("loaded")
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml b/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml
new file mode 100644
index 0000000000..3ed3aa208c
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml
@@ -0,0 +1,67 @@
+import QtQuick 2.15
+import QtQuick.Window 2.15
+
+Window {
+ height: 480
+ title: qsTr("Scroll")
+ visible: true
+ width: 640
+
+ Component.onCompleted: {
+ console.log("loaded");
+ }
+ onSend: {
+ console.log("send signal emitted with notice: " + notice);
+ }
+ onTrigger: console.log("trigger signal emitted")
+
+ Rectangle {
+ anchors.fill: parent
+
+ ListView {
+ model: {
+ MySingleton.mySignal();
+ 20;
+ }
+ width: parent.width
+
+ delegate: ItemDelegate {
+ id: root
+
+ text: "Item " + (index + 1)
+ width: parent.width
+
+ Rectangle {
+ text: "bla"
+ }
+ MyComponent {
+ property int a: {
+ let x = isNaN;
+ (45);
+ x ? 5 + 1 : 8;
+ }
+ property list<Item> b: [
+ Item {
+ width: 5
+ },
+ Item {
+ width: 6
+ }
+ ]
+
+ function f(v) {
+ let c = 0;
+ return {
+ a: function () {
+ if (b == 0)
+ c += 78 * 5 * v;
+ }()
+ };
+ }
+
+ text: root.text
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/inline.qml b/tests/auto/qmldom/domdata/reformatter/inline.qml
new file mode 100644
index 0000000000..3429e9a2af
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/inline.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+component CustomText: Text {
+color: "red"
+text: "Test Text"
+}
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/inlineReformatted.qml b/tests/auto/qmldom/domdata/reformatter/inlineReformatted.qml
new file mode 100644
index 0000000000..749da25332
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/inlineReformatted.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+ component CustomText: Text {
+ color: "red"
+ text: "Test Text"
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/noMerge.qml b/tests/auto/qmldom/domdata/reformatter/noMerge.qml
new file mode 100644
index 0000000000..e8606f5d10
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/noMerge.qml
@@ -0,0 +1,14 @@
+import QtQuick
+
+Item{
+ property var arrayNoMerge
+ property var array2: [1,2,3]
+ property list<Item> array: [Item {}, Item{ nr: 2 }]
+ default property list<Item> nonMergeable
+ property list<Item> singleObjList
+ singleObjList: Item{
+ el: "alone"
+ }
+ nonMergeable: [Item {}, Item{ nr: "II" }]
+ arrayNoMerge: [Item {}, Item{ nr: 2 }]
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/noMergeReformatted.qml b/tests/auto/qmldom/domdata/reformatter/noMergeReformatted.qml
new file mode 100644
index 0000000000..a441885bb7
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/noMergeReformatted.qml
@@ -0,0 +1,33 @@
+import QtQuick
+
+Item {
+ property list<Item> array: [
+ Item {
+ },
+ Item {
+ nr: 2
+ }
+ ]
+ property var array2: [1, 2, 3]
+ property var arrayNoMerge
+ default property list<Item> nonMergeable
+ property list<Item> singleObjList
+
+ arrayNoMerge: [
+ Item {
+ },
+ Item {
+ nr: 2
+ }
+ ]
+ nonMergeable: [
+ Item {
+ },
+ Item {
+ nr: "II"
+ }
+ ]
+ singleObjList: Item {
+ el: "alone"
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/required.qml b/tests/auto/qmldom/domdata/reformatter/required.qml
new file mode 100644
index 0000000000..29103114bd
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/required.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.15
+
+Item {
+ id: theFoo
+
+ required property Item theItem
+ required data
+
+ function foo() {
+ theItem.foo("The issue is exacerbated if the object literal is wrapped onto the next line like so:",
+ {"foo": theFoo})
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/requiredReformatted.qml b/tests/auto/qmldom/domdata/reformatter/requiredReformatted.qml
new file mode 100644
index 0000000000..2f9420a22e
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/requiredReformatted.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.15
+
+Item {
+ id: theFoo
+
+ required data
+ required property Item theItem
+
+ function foo() {
+ theItem.foo("The issue is exacerbated if the object literal is wrapped onto the next line like so:", {
+ "foo": theFoo
+ });
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml b/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml
new file mode 100644
index 0000000000..7473283605
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.15
+
+Item {
+ id: theFoo
+
+ required data
+ required property Item theItem
+
+ function foo() {
+ theItem.foo("The issue is exacerbated if the object literal is wrapped onto the next line like so:", {
+ "foo": theFoo
+ });
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/spread.qml b/tests/auto/qmldom/domdata/reformatter/spread.qml
new file mode 100644
index 0000000000..e8c30d0643
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/spread.qml
@@ -0,0 +1,15 @@
+import QtQuick 2.15
+
+Item {
+ function myFunction() {}
+ function foo() {
+ iterableObj = [1,2]
+ obj = {a:42}
+ let x = (console.log("bla\n"), 3);
+ myFunction(...iterableObj); // pass all elements of iterableObj as arguments to function myFunction
+ let arr = [...iterableObj, '4', 'five', 6]; // combine two arrays by inserting all elements from iterableObj
+ //let objClone = { ...obj }; // pass all key:value pairs from an object (ES2018)
+ myFunction(-1, ...args, 2, ...[3]);
+ console.log(Math.max(...[1, 2, 3, 4]))
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml b/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml
new file mode 100644
index 0000000000..fde8ffd686
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.15
+
+Item {
+ function foo() {
+ iterableObj = [1, 2];
+ obj = {
+ a: 42
+ };
+ let x = (console.log("bla\n"), 3);
+ myFunction(...iterableObj); // pass all elements of iterableObj as arguments to function myFunction
+ let arr = [...iterableObj, '4', 'five', 6]; // combine two arrays by inserting all elements from iterableObj
+ //let objClone = { ...obj }; // pass all key:value pairs from an object (ES2018)
+ myFunction(-1, ...args, 2, ...[3]);
+ console.log(Math.max(...[1, 2, 3, 4]));
+ }
+ function myFunction() {
+ }
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/template.qml b/tests/auto/qmldom/domdata/reformatter/template.qml
new file mode 100644
index 0000000000..886922838b
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/template.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.15
+
+Text {
+ property int i: 3
+ text: "´"+
+ "m
+ l
+
+ "+
+ 'b'+'
+ multi
+ l
+ l'+
+ `template` +
+ `t${i+6}`+
+ `t${i+`nested${i}`}`+
+ `t${function(){return 5}()}`+
+ `t\${i}
+ ${i +
+ 2}`
+ width: 300
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/templateReformatted.qml b/tests/auto/qmldom/domdata/reformatter/templateReformatted.qml
new file mode 100644
index 0000000000..bfc8d28db1
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/templateReformatted.qml
@@ -0,0 +1,17 @@
+import QtQuick 2.15
+
+Text {
+ property int i: 3
+
+ text: "´" + "m
+ l
+
+ " + 'b' + '
+ multi
+ l
+ l' + `template` + `t${i + 6}` + `t${i + `nested${i}`}` + `t${function () {
+ return 5;
+ }()}` + `t\${i}
+ ${i + 2}`
+ width: 300
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/typeAnnotations.qml b/tests/auto/qmldom/domdata/reformatter/typeAnnotations.qml
new file mode 100644
index 0000000000..63c4f5471c
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/typeAnnotations.qml
@@ -0,0 +1,12 @@
+import QtQuick
+
+Item {
+function calculateWidth(object: Item<a>): list<Item> {
+var w = object.width / 3
+// ...
+// more javascript code
+// ...
+console.debug(w)
+return w
+}
+}
diff --git a/tests/auto/qmldom/domdata/reformatter/typeAnnotationsReformatted.qml b/tests/auto/qmldom/domdata/reformatter/typeAnnotationsReformatted.qml
new file mode 100644
index 0000000000..3b304176fb
--- /dev/null
+++ b/tests/auto/qmldom/domdata/reformatter/typeAnnotationsReformatted.qml
@@ -0,0 +1,12 @@
+import QtQuick
+
+Item {
+ function calculateWidth(object: Item<a>): list<Item> {
+ var w = object.width / 3;
+ // ...
+ // more javascript code
+ // ...
+ console.debug(w);
+ return w;
+ }
+}
diff --git a/tests/auto/qmldom/domitem/CMakeLists.txt b/tests/auto/qmldom/domitem/CMakeLists.txt
index 71983f0cdd..e6f8117902 100644
--- a/tests/auto/qmldom/domitem/CMakeLists.txt
+++ b/tests/auto/qmldom/domitem/CMakeLists.txt
@@ -1,15 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from domitem.pro.
#####################################################################
## tst_qmldomitem Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmldomitem LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/..
+ domdata/domitem/*)
+list(APPEND test_data ${test_data_glob})
+
qt_internal_add_test(tst_qmldomitem
SOURCES
- tst_qmldomitem.cpp
+ tst_qmldomitem.cpp tst_qmldomitem.h
DEFINES
QT_DEPRECATED_WARNINGS
- PUBLIC_LIBRARIES
- Qt::QmlDevToolsPrivate
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/../domdata"
+ LIBRARIES
Qt::QmlDomPrivate
+ Qt::Test
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_qmldomitem CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/domdata"
)
diff --git a/tests/auto/qmldom/domitem/tst_qmldomitem.cpp b/tests/auto/qmldom/domitem/tst_qmldomitem.cpp
index bec8db8c4a..97b7dcc3ed 100644
--- a/tests/auto/qmldom/domitem/tst_qmldomitem.cpp
+++ b/tests/auto/qmldom/domitem/tst_qmldomitem.cpp
@@ -1,190 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**/
-#include <QtQmlDom/private/qqmldomitem_p.h>
-#include <QtQmlDom/private/qqmldomtop_p.h>
-
-#include <QtTest/QtTest>
-#include <QCborValue>
-#include <QDebug>
-
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-namespace QQmlJS {
-namespace Dom {
-
-DomItem wrapInt(const DomItem &self, Path p, const int &i){
- return self.subDataPath(p, i).item;
-}
-
-class TestDomItem: public QObject
-{
- Q_OBJECT
-public:
-
-private slots:
- void initTestCase() {
- universePtr = std::make_shared<DomUniverse>(QStringLiteral(u"dummyUniverse"));
- envPtr = std::make_shared<DomEnvironment>(universePtr, QStringList());
- env = DomItem(envPtr);
- }
-
- void testList() {
- QList<int> l({1,2,3,4});
- QList<int> l2 = l;
- QList<int> l3({1});
- QList<int> l4 = l3;
- QCOMPARE(&(l[1]), &(l[1]));
- QCOMPARE(&(l3[0]), &(l3[0]));
- // QCOMPARE(&(l3[0]), &(l4[0])); // shallow copy actually copies els (QVector behavior)...
- DomItem list1 = env.subList(
- List::fromQListRef<int>(Path::field(u"list"), l, &wrapInt)).item;
- DomItem list2 = env.subList(
- List::fromQListRef<int>(Path::field(u"reverseList"), l, &wrapInt, ListOptions::Reverse)).item;
- QCOMPARE(list1.domKind(), DomKind::List);
- QCOMPARE(list1.indexes(), 4);
- QCOMPARE(list1[0].value().toInteger(), 1);
- QCOMPARE(list1[3].value().toInteger(), 4);
- QVERIFY(!list1[4]);
- QCOMPARE(list1[4].value().toInteger(-1), -1);
- QVERIFY(list1[0].value() != list2[0].value());
- QCOMPARE(list1[0].value(), list2[3].value());
- QCOMPARE(list1[3].value(), list2[0].value());
- QCOMPARE(list1.container(), env);
- }
- void testMap() {
- QMap<QString, int> map({{QStringLiteral(u"a"),1},{QStringLiteral(u"b"),2}});
- //QMap<QString, int> map2 = map;
- QMap<QString, int> map3({{QStringLiteral(u"a"),1}});
- //QMap<QString, int> map4 = map3;
- auto it = map.find(QStringLiteral(u"a"));
- auto it2 = map.find(QStringLiteral(u"a"));
- auto it3 = map3.find(QStringLiteral(u"a"));
- auto it4 = map3.find(QStringLiteral(u"a"));
- //auto it5 = map4.find(QStringLiteral(u"a"));
- QVERIFY(it != map.end());
- QVERIFY(it2 != map.end());
- QCOMPARE(&(*it), &(*it2));
- QCOMPARE(&(*it), &(map[QStringLiteral(u"a")]));
- QCOMPARE(&(it.value()), &(it2.value()));
- //QCOMPARE(&(*it), &(map2[QStringLiteral(u"a")]));
- QCOMPARE(&(*it3), &(*it4));
- //QCOMPARE(&(*it3), &(*it5));
- DomItem map1 = env.subMap(
- Map::fromMapRef<int>(
- Path::field(u"map"), map,
- &wrapInt)).item;
- QCOMPARE(map1.domKind(), DomKind::Map);
- QCOMPARE(map1[u"a"].value().toInteger(), 1);
- QCOMPARE(map1.key(QStringLiteral(u"a")).value().toInteger(), 1);
- QCOMPARE(map1[u"b"].value().toInteger(), 2);
- QVERIFY(!map1[u"c"]);
- QCOMPARE(map1.container(), env);
- }
- void testMultiMap() {
- QMultiMap<QString, int> mmap({{QStringLiteral(u"a"),1},{QStringLiteral(u"b"),2},{QStringLiteral(u"a"),3}});
- //QMultiMap<QString, int> mmap2 = mmap;
- QMultiMap<QString, int> mmap3({{QStringLiteral(u"a"),1}});
- //QMultiMap<QString, int> mmap4 = mmap3;
- auto it = mmap.find(QStringLiteral(u"a"));
- auto it2 = mmap.find(QStringLiteral(u"a"));
- //auto it3 = mmap2.find(QStringLiteral(u"a"));
- auto it4 = mmap3.find(QStringLiteral(u"a"));
- auto it5 = mmap3.find(QStringLiteral(u"a"));
- //auto it6 = mmap4.find(QStringLiteral(u"a"));
- QVERIFY(it != mmap.end());
- QVERIFY(it2 != mmap.end());
- QCOMPARE(&(it.value()), &(it2.value()));
- QCOMPARE(&(*it), &(it2.value()));
- //QCOMPARE(&(*it), &(*it2)); // copy has different address (copies elements for int)
- //QCOMPARE(&(*it), &(*it3));
- QCOMPARE(&(*it4), &(*it5));
- //QCOMPARE(&(*it4), &(*it6));
- DomItem map1 = env.subMap(
- Map::fromMultiMapRef<int>(
- Path::field(u"mmap"), mmap,
- &wrapInt)).item;
- QCOMPARE(map1[u"b"].index(0).value().toInteger(), 2);
- QVERIFY(!map1[u"b"].index(2));
- QVERIFY(!map1[u"c"]);
- QCOMPARE(map1[u"a"][0].value().toInteger(), 1);
- QCOMPARE(map1.key(QStringLiteral(u"a")).index(0).value().toInteger(), 1);
- QCOMPARE(map1.key(QStringLiteral(u"a")).index(1).value().toInteger(), 3);
- QCOMPARE(map1.container(), env);
- }
- void testReference() {
- Path p = Path::root(u"env");
- DomItem ref = env.subReferenceField(u"ref",p).item;
- QCOMPARE(ref.field(u"referredObjectPath").value().toString(), p.toString());
- QCOMPARE(ref.fields(), QList<QString>({QStringLiteral(u"referredObjectPath"), QStringLiteral(u"get")}));
- QCOMPARE(ref.field(u"get").internalKind(), DomType::DomEnvironment);
- }
- void testEnvUniverse() {
- QCOMPARE(env.internalKind(), DomType::DomEnvironment);
- QCOMPARE(env.pathFromOwner(), Path());
- QCOMPARE(env.containingObject().internalKind(), DomType::Empty);
- QCOMPARE(env.container().internalKind(), DomType::Empty);
- QCOMPARE(env.canonicalPath(), Path::root(u"env"));
- QCOMPARE(env.path(u"$env").internalKind(), DomType::DomEnvironment);
- QCOMPARE(env.top().internalKind(), DomType::DomEnvironment);
- QCOMPARE(env.environment().internalKind(), DomType::DomEnvironment);
- QCOMPARE(env.owningItemPtr(), envPtr);
- QCOMPARE(env.topPtr(), envPtr);
- DomItem univ = env.universe();
- QCOMPARE(univ.internalKind(), DomType::DomUniverse);
- QCOMPARE(univ.owningItemPtr(), universePtr);
- DomItem univ2 = env.path(u".universe");
- QCOMPARE(univ2.internalKind(), DomType::DomUniverse);
- QCOMPARE(univ2.owningItemPtr(), universePtr);
- QCOMPARE(univ2.topPtr(), universePtr);
- DomItem univ3 = env.field(u"universe");
- QCOMPARE(univ3.internalKind(), DomType::DomUniverse);
- }
-private:
- std::shared_ptr<DomUniverse> universePtr;
- std::shared_ptr<DomEnvironment> envPtr;
- DomItem env;
-};
-
-
-} // namespace Dom
-} // namespace QQmlJS
-QT_END_NAMESPACE
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include "tst_qmldomitem.h"
QTEST_MAIN(QQmlJS::Dom::TestDomItem)
-#include "tst_qmldomitem.moc"
diff --git a/tests/auto/qmldom/domitem/tst_qmldomitem.h b/tests/auto/qmldom/domitem/tst_qmldomitem.h
new file mode 100644
index 0000000000..4464333b6e
--- /dev/null
+++ b/tests/auto/qmldom/domitem/tst_qmldomitem.h
@@ -0,0 +1,3421 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLDOMITEM_H
+#define TST_QMLDOMITEM_H
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtQmlDom/private/qqmldomastdumper_p.h>
+#include <QtQmlDom/private/qqmldommock_p.h>
+#include <QtQmlDom/private/qqmldomcompare_p.h>
+#include <QtQmlDom/private/qqmldomfieldfilter_p.h>
+#include <QtQmlDom/private/qqmldomscriptelements_p.h>
+
+#include <QtTest/QtTest>
+#include <QtCore/QCborValue>
+#include <QtCore/QDebug>
+#include <QtCore/QLibraryInfo>
+#include <QtCore/QFileInfo>
+
+#include <deque>
+#include <memory>
+#include <utility>
+#include <variant>
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+inline DomItem wrapInt(const DomItem &self, const PathEls::PathComponent &p, const int &i)
+{
+ return self.subDataItem(p, i);
+}
+
+class DomTestClass
+{
+public:
+ std::shared_ptr<int> i;
+};
+
+class TestDomItem : public QObject
+{
+ Q_OBJECT
+public:
+ static ErrorGroups myErrors()
+ {
+ static ErrorGroups res { { NewErrorGroup("tests"), NewErrorGroup("domitem") } };
+ return res;
+ }
+
+private slots:
+ void initTestCase()
+ {
+ baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/domitem");
+ qmltypeDirs = QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::QmlImportsPath) });
+ universePtr =
+ std::shared_ptr<DomUniverse>(new DomUniverse(QStringLiteral(u"dummyUniverse")));
+ envPtr = std::shared_ptr<DomEnvironment>(
+ new DomEnvironment(QStringList(), DomEnvironment::Option::SingleThreaded,
+ DomCreationOption::None, universePtr));
+ env = DomItem(envPtr);
+ testOwnerPtr = std::shared_ptr<MockOwner>(new MockOwner(
+ Path::Root(u"env").field(u"testOwner"), 0,
+ QMap<QString, MockObject> {
+ MockObject(
+ Path::Field(u"obj1"),
+ QMap<QString, MockObject> {
+ MockObject(
+ Path::Field(u"obj1").field(u"obj1_2"),
+ QMap<QString, MockObject> {},
+ QMap<QString, QCborValue> {
+ { QStringLiteral(u"val1"), QCborValue(3) },
+ { QLatin1String("val2"), QCborValue(4) } })
+ .asStringPair() },
+ QMap<QString, QCborValue> {
+ { QStringLiteral(u"val1"), QCborValue(1) },
+ { QLatin1String("val2"), QCborValue(2) } })
+ .asStringPair(),
+ MockObject(
+ Path::Field(u"obj2"),
+ QMap<QString, MockObject> {
+ MockObject(
+ Path::Field(u"obj2").field(u"obj2_2"),
+ QMap<QString, MockObject> {},
+ QMap<QString, QCborValue> {
+ { QStringLiteral(u"val1"), QCborValue(5) },
+ { QLatin1String("val2"), QCborValue(6) },
+ { QLatin1String("valX"),
+ QCborValue(QStringLiteral(u"pippo")) } })
+ .asStringPair() },
+ QMap<QString, QCborValue> {
+ { QStringLiteral(u"val1"), QCborValue(7) },
+ { QLatin1String("val2"), QCborValue(8) } })
+ .asStringPair() },
+ QMap<QString, QCborValue> {
+ { QStringLiteral(u"val1"), QCborValue(9) },
+ },
+ QMap<QString, QMap<QString, MockObject>> {
+ { QStringLiteral(u"map"),
+ QMap<QString, MockObject> {
+ MockObject(Path::Field(u"map").key(u"a"),
+ QMap<QString, MockObject> {},
+ QMap<QString, QCborValue> {
+ { QStringLiteral(u"val1"), QCborValue(10) },
+ { QLatin1String("val2"), QCborValue(11) } })
+ .asStringPair(),
+ MockObject(Path::Field(u"map").key(u"b"),
+ QMap<QString, MockObject> {},
+ QMap<QString, QCborValue> {
+ { QStringLiteral(u"val1"), QCborValue(12) },
+ { QLatin1String("val2"), QCborValue(13) } })
+ .asStringPair() } } },
+ QMap<QString, QMultiMap<QString, MockObject>> {
+ { QStringLiteral(u"mmap"),
+ QMultiMap<QString, MockObject> {
+ { QStringLiteral(u"a"),
+ MockObject(
+ Path::Field(u"mmap").key(u"a").index(0),
+ QMap<QString, MockObject> {},
+ QMap<QString, QCborValue> {
+ { QStringLiteral(u"val1"), QCborValue(14) },
+ { QLatin1String("val2"), QCborValue(15) } }) },
+ { QStringLiteral(u"a"),
+ MockObject(Path::Field(u"mmap").key(u"a").index(1),
+ QMap<QString, MockObject> {},
+ QMap<QString, QCborValue> {
+ { QStringLiteral(u"val1"), QCborValue(16) },
+ { QLatin1String("val2"),
+ QCborValue(17) } }) } } } },
+ QMap<QString, QList<MockObject>> {
+ { QStringLiteral(u"list"),
+ QList<MockObject> {
+ MockObject(Path::Field(u"list").index(0),
+ QMap<QString, MockObject> {},
+ QMap<QString, QCborValue> {
+ { QStringLiteral(u"val1"), QCborValue(18) },
+ { QLatin1String("val2"), QCborValue(19) } }),
+ MockObject(Path::Field(u"list").index(1),
+ QMap<QString, MockObject> {},
+ QMap<QString, QCborValue> {
+ { QStringLiteral(u"val1"), QCborValue(20) },
+ { QLatin1String("val2"),
+ QCborValue(21) } }) } } }));
+ envPtr->setExtraOwningItem(QStringLiteral(u"testOwner"), testOwnerPtr);
+ tOwner = env.field(u"testOwner");
+ }
+
+ void testList()
+ {
+ QList<int> l({ 1, 2, 3, 4 });
+ QList<int> l2 = l;
+ QList<int> l3({ 1 });
+ QList<int> l4 = l3;
+ QCOMPARE(&(l[1]), &(l[1]));
+ QCOMPARE(&(l3[0]), &(l3[0]));
+ // QCOMPARE(&(l3[0]), &(l4[0])); // shallow copy actually copies els (QVector behavior)...
+ DomItem list1 = env.subListItem(List::fromQListRef<int>(Path::Field(u"list"), l, &wrapInt));
+ DomItem list2 = env.subListItem(List::fromQListRef<int>(Path::Field(u"reverseList"), l,
+ &wrapInt, ListOptions::Reverse));
+ QCOMPARE(list1.domKind(), DomKind::List);
+ QCOMPARE(list1.indexes(), 4);
+ QCOMPARE(list1[0].value().toInteger(), 1);
+ QCOMPARE(list1[3].value().toInteger(), 4);
+ QVERIFY(!list1[4]);
+ QCOMPARE(list1[4].value().toInteger(-1), -1);
+ QVERIFY(list1[0].value() != list2[0].value());
+ QCOMPARE(list1[0].value(), list2[3].value());
+ QCOMPARE(list1[3].value(), list2[0].value());
+ }
+ void testMap()
+ {
+ QMap<QString, int> map({ { QStringLiteral(u"a"), 1 }, { QStringLiteral(u"b"), 2 } });
+ // QMap<QString, int> map2 = map;
+ QMap<QString, int> map3({ { QStringLiteral(u"a"), 1 } });
+ // QMap<QString, int> map4 = map3;
+ auto it = map.find(QStringLiteral(u"a"));
+ auto it2 = map.find(QStringLiteral(u"a"));
+ auto it3 = map3.find(QStringLiteral(u"a"));
+ auto it4 = map3.find(QStringLiteral(u"a"));
+ // auto it5 = map4.find(QStringLiteral(u"a"));
+ QVERIFY(it != map.end());
+ QVERIFY(it2 != map.end());
+ QCOMPARE(&(*it), &(*it2));
+ QCOMPARE(&(*it), &(map[QStringLiteral(u"a")]));
+ QCOMPARE(&(it.value()), &(it2.value()));
+ // QCOMPARE(&(*it), &(map2[QStringLiteral(u"a")]));
+ QCOMPARE(&(*it3), &(*it4));
+ // QCOMPARE(&(*it3), &(*it5));
+ DomItem map1 = env.subMapItem(Map::fromMapRef<int>(Path::Field(u"map"), map, &wrapInt));
+ QCOMPARE(map1.domKind(), DomKind::Map);
+ QCOMPARE(map1[u"a"].value().toInteger(), 1);
+ QCOMPARE(map1.key(QStringLiteral(u"a")).value().toInteger(), 1);
+ QCOMPARE(map1[u"b"].value().toInteger(), 2);
+ QVERIFY(!map1[u"c"]);
+ }
+ void testMultiMap()
+ {
+ QMultiMap<QString, int> mmap({ { QStringLiteral(u"a"), 1 },
+ { QStringLiteral(u"b"), 2 },
+ { QStringLiteral(u"a"), 3 } });
+ // QMultiMap<QString, int> mmap2 = mmap;
+ QMultiMap<QString, int> mmap3({ { QStringLiteral(u"a"), 1 } });
+ // QMultiMap<QString, int> mmap4 = mmap3;
+ auto it = mmap.find(QStringLiteral(u"a"));
+ auto it2 = mmap.find(QStringLiteral(u"a"));
+ // auto it3 = mmap2.find(QStringLiteral(u"a"));
+ auto it4 = mmap3.find(QStringLiteral(u"a"));
+ auto it5 = mmap3.find(QStringLiteral(u"a"));
+ // auto it6 = mmap4.find(QStringLiteral(u"a"));
+ QVERIFY(it != mmap.end());
+ QVERIFY(it2 != mmap.end());
+ QCOMPARE(&(it.value()), &(it2.value()));
+ QCOMPARE(&(*it), &(it2.value()));
+ // QCOMPARE(&(*it), &(*it2)); // copy has different address (copies elements for int)
+ // QCOMPARE(&(*it), &(*it3));
+ QCOMPARE(&(*it4), &(*it5));
+ // QCOMPARE(&(*it4), &(*it6));
+ DomItem map1 = env.subMapItem(Map::fromMultiMapRef<int>(Path::Field(u"mmap"), mmap));
+ QCOMPARE(map1[u"b"].index(0).value().toInteger(), 2);
+ QVERIFY(!map1[u"b"].index(2));
+ QVERIFY(!map1[u"c"]);
+ QCOMPARE(map1[u"a"][0].value().toInteger(), 1);
+ QCOMPARE(map1.key(QStringLiteral(u"a")).index(0).value().toInteger(), 1);
+ QCOMPARE(map1.key(QStringLiteral(u"a")).index(1).value().toInteger(), 3);
+ {
+ QMultiMap<QString, DomTestClass> m1;
+ m1.insert(QStringLiteral(u"xx"), DomTestClass { std::shared_ptr<int>(new int(4)) });
+ QCOMPARE(m1.begin().value().i.use_count(), 1);
+ QMultiMap<QString, DomTestClass> m2 = m1;
+ m1.clear();
+ auto it = m2.cbegin();
+ auto end = m2.cend();
+ while (it != end) {
+ QCOMPARE(it.value().i.use_count(), 1);
+ m1.insert(it.key(), it.value());
+ QCOMPARE(it.value().i.use_count(), 2);
+ ++it;
+ }
+ m2.insert(QStringLiteral(u"xy"), DomTestClass { std::shared_ptr<int>(new int(8)) });
+ QMultiMap<QString, DomTestClass> m3 = m2;
+ m3.begin().value() = DomTestClass { std::shared_ptr<int>(new int(2)) };
+ auto it2 = m2.begin();
+ auto it3 = m3.begin();
+ QCOMPARE(*it2.value().i, 4);
+ QCOMPARE(*it3.value().i, 2);
+ QCOMPARE(it2.value().i.use_count(), 2);
+ QCOMPARE(it3.value().i.use_count(), 1);
+ m3.insert(QStringLiteral(u"xz"), DomTestClass { std::shared_ptr<int>(new int(16)) });
+ it2 = m2.begin();
+ it3 = m3.begin();
+ QCOMPARE(*it2.value().i, 4);
+ QCOMPARE(*it3.value().i, 2);
+ QCOMPARE(it2.value().i.use_count(), 2);
+ QCOMPARE(it3.value().i.use_count(), 1);
+ ++it2;
+ ++it3;
+ QCOMPARE(*it2.value().i, 8);
+ QCOMPARE(*it3.value().i, 8);
+ QCOMPARE(it2.value().i.use_count(), 2);
+ QCOMPARE(it3.value().i.use_count(), 2);
+ ++it2;
+ ++it3;
+ QVERIFY(it2 == m2.end());
+ QVERIFY(it3 != m3.end());
+ QCOMPARE(*it3.value().i, 16);
+ QCOMPARE(it3.value().i.use_count(), 1);
+ ++it3;
+ QVERIFY(it3 == m3.end());
+ }
+ }
+ void testReference()
+ {
+ Path p = Path::Root(u"env");
+ DomItem ref = env.subReferenceItem(PathEls::Field(u"ref"), p);
+ QCOMPARE(ref.field(u"referredObjectPath").value().toString(), p.toString());
+ QCOMPARE(ref.fields(),
+ QList<QString>({ QStringLiteral(u"referredObjectPath"), QStringLiteral(u"get") }));
+ QCOMPARE(ref.field(u"get").internalKind(), DomType::DomEnvironment);
+ // test stability (cache)
+ QCOMPARE(ref.field(u"get").internalKind(), DomType::DomEnvironment);
+ }
+ void testRefCache()
+ {
+ Path refPath = env.canonicalPath().field(u"dummyRef");
+ RefCacheEntry e0 = RefCacheEntry::forPath(env, refPath);
+ QCOMPARE(e0.cached, RefCacheEntry::Cached::None);
+ bool didAdd1 = RefCacheEntry::addForPath(
+ env, refPath, RefCacheEntry { RefCacheEntry::Cached::First, {} });
+ QVERIFY(didAdd1);
+ RefCacheEntry e1 = RefCacheEntry::forPath(env, refPath);
+ QCOMPARE(e1.cached, RefCacheEntry::Cached::All);
+ QCOMPARE(e1.canonicalPaths.isEmpty(), true);
+ bool didAdd2 = RefCacheEntry::addForPath(
+ env, refPath,
+ RefCacheEntry { RefCacheEntry::Cached::First, { env.canonicalPath() } },
+ AddOption::Overwrite);
+ QVERIFY(didAdd2);
+ RefCacheEntry e2 = RefCacheEntry::forPath(env, refPath);
+ QCOMPARE(e2.cached, RefCacheEntry::Cached::First);
+ QCOMPARE(e2.canonicalPaths.size(), 1);
+ QCOMPARE(e2.canonicalPaths.first().toString(), env.canonicalPath().toString());
+ bool didAdd3 = RefCacheEntry::addForPath(
+ env, refPath,
+ RefCacheEntry { RefCacheEntry::Cached::All,
+ { env.canonicalPath(), tOwner.canonicalPath() } },
+ AddOption::Overwrite);
+ QVERIFY(didAdd3);
+ RefCacheEntry e3 = RefCacheEntry::forPath(env, refPath);
+ QCOMPARE(e3.cached, RefCacheEntry::Cached::All);
+ QCOMPARE(e3.canonicalPaths.size(), 2);
+ QCOMPARE(e3.canonicalPaths.first().toString(), env.canonicalPath().toString());
+ QCOMPARE(e3.canonicalPaths.last().toString(), tOwner.canonicalPath().toString());
+ }
+ void testEnvUniverse()
+ {
+ QCOMPARE(env.internalKind(), DomType::DomEnvironment);
+ QCOMPARE(env.pathFromOwner(), Path());
+ QCOMPARE(env.containingObject().internalKind(), DomType::Empty);
+ QCOMPARE(env.container().internalKind(), DomType::Empty);
+ QCOMPARE(env.canonicalPath(), Path::Root(u"env"));
+ QCOMPARE(env.path(u"$env").internalKind(), DomType::DomEnvironment);
+ QCOMPARE(env.top().internalKind(), DomType::DomEnvironment);
+ QCOMPARE(env.environment().internalKind(), DomType::DomEnvironment);
+ QCOMPARE(env.owningItemPtr(), envPtr);
+ QCOMPARE(env.topPtr(), envPtr);
+ DomItem univ = env.universe();
+ QCOMPARE(univ.internalKind(), DomType::DomUniverse);
+ QCOMPARE(univ.owningItemPtr(), universePtr);
+ DomItem univ2 = env.path(u".universe");
+ QCOMPARE(univ2.internalKind(), DomType::DomUniverse);
+ QCOMPARE(univ2.owningItemPtr(), universePtr);
+ QCOMPARE(univ2.topPtr(), universePtr);
+ DomItem univ3 = env.field(u"universe");
+ QCOMPARE(univ3.internalKind(), DomType::DomUniverse);
+ }
+
+ void testTOwner()
+ {
+ QVERIFY(env.fields().contains(QLatin1String("testOwner")));
+ QCOMPARE(tOwner.internalKind(), DomType::MockOwner);
+ QCOMPARE(tOwner.pathFromOwner(), Path());
+ DomItem map = tOwner.field(u"map");
+ QCOMPARE(map[u"b"].field(u"val1").value().toInteger(), 12);
+ QCOMPARE(map[u"b"].container(), map);
+ QCOMPARE(map[u"b"].container()[u"b"].field(u"val1").value().toInteger(), 12);
+ QCOMPARE(map[u"b"].containingObject(), tOwner);
+ DomItem mmap = tOwner.field(u"mmap");
+ QCOMPARE(mmap[u"a"].index(0).field(u"val1").value().toInteger(), 14);
+ QCOMPARE(mmap[u"a"].container(), mmap);
+ QCOMPARE(mmap[u"a"].container()[u"a"].index(0).field(u"val1").value().toInteger(), 14);
+ QCOMPARE(mmap[u"a"].containingObject(), tOwner);
+ QCOMPARE(mmap[u"a"].index(0).container(), mmap[u"a"]);
+ QCOMPARE(mmap[u"a"].index(0).containingObject(), tOwner);
+ DomItem list = tOwner.field(u"list");
+ QCOMPARE(list[0].field(u"val1").value().toInteger(), 18);
+ QCOMPARE(list.container(), tOwner);
+ QCOMPARE(list[0].container(), list);
+ QCOMPARE(list[0].containingObject(), tOwner);
+ QCOMPARE(list[0].container()[0].field(u"val1").value().toInteger(), 18);
+ QCOMPARE(tOwner.containingObject().internalKind(), DomType::DomEnvironment);
+ QCOMPARE(tOwner.container().internalKind(), DomType::DomEnvironment);
+ QCOMPARE(tOwner.fields(),
+ QStringList({ QStringLiteral(u"val1"), QStringLiteral(u"obj1"),
+ QStringLiteral(u"obj2"), QStringLiteral(u"map"),
+ QStringLiteral(u"mmap"), QStringLiteral(u"list") }));
+ auto tOwner2 = env.path(u"$env.testOwner");
+ QCOMPARE(tOwner2.internalKind(), DomType::MockOwner);
+ auto tOwner3 = tOwner.path(u"$env.testOwner");
+ QCOMPARE(tOwner3.internalKind(), DomType::MockOwner);
+ QList<qint64> values;
+ tOwner.visitTree(Path(), [&values](const Path &p, DomItem i, bool) {
+ if (i.pathFromOwner() != p)
+ myErrors()
+ .error(QStringLiteral(u"unexpected path %1 %2")
+ .arg(i.pathFromOwner().toString(), p.toString()))
+ .handle(defaultErrorHandler);
+ Q_ASSERT(i == i.path(i.canonicalPath()));
+ if (DomItem v1 = i.path(u".val1"))
+ values.append(v1.value().toInteger());
+ return true;
+ });
+ QCOMPARE(values, QList<qint64>({ 9, 1, 3, 7, 5, 10, 12, 14, 16, 18, 20 }));
+ }
+ void testSubObj()
+ {
+ auto obj1 = tOwner.field(u"obj1");
+ QCOMPARE(obj1.internalKind(), DomType::MockObject);
+ auto obj1_1 = env.path(u".testOwner.obj1.obj1_2");
+ QCOMPARE(obj1_1.internalKind(), DomType::MockObject);
+ QCOMPARE(obj1_1.field(u"val1").value().toInteger(), 3);
+ }
+ void testEquality()
+ {
+ auto obj1 = tOwner.field(u"obj1");
+ auto obj1_1 = env.path(u".testOwner.obj1.obj1_2");
+ QCOMPARE(obj1_1.container(), obj1);
+ QCOMPARE(obj1_1.environment(), env);
+ QCOMPARE(obj1_1.owner(), tOwner);
+ }
+ void testLoadNoDep()
+ {
+#ifdef Q_OS_ANDROID
+ QSKIP("Test uncompatible with Android (QTBUG-100171)");
+#endif
+ auto univPtr = std::shared_ptr<QQmlJS::Dom::DomUniverse>(
+ new QQmlJS::Dom::DomUniverse(QLatin1String("univ1")));
+ auto envPtr = std::shared_ptr<QQmlJS::Dom::DomEnvironment>(new QQmlJS::Dom::DomEnvironment(
+ qmltypeDirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies,
+ DomCreationOption::None, univPtr));
+ QQmlJS::Dom::DomItem env(envPtr);
+ QVERIFY(env);
+ QString testFile1 = baseDir + QLatin1String("/test1.qml");
+ DomItem tFile;
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile1),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, baseDir), {});
+ envPtr->loadPendingDependencies();
+
+ QVERIFY(tFile);
+ tFile = tFile.field(Fields::currentItem);
+ QVERIFY(tFile);
+ DomItem comp1 = tFile.field(Fields::components).key(QString()).index(0);
+ QVERIFY(comp1);
+ DomItem obj1 = comp1.field(Fields::objects).index(0);
+ QVERIFY(obj1);
+
+ tFile.visitTree(Path(), [&tFile](const Path &p, DomItem i, bool) {
+ if (!(i == i.path(i.canonicalPath()))) {
+ DomItem i2 = i.path(i.canonicalPath());
+ qDebug() << p << i.canonicalPath() << i.internalKindStr() << i2.internalKindStr()
+ << i.id() << i2.id() << i.pathFromOwner() << i2.pathFromOwner();
+ }
+ Q_ASSERT(i == i.path(i.canonicalPath()));
+ Q_ASSERT(!i || i == tFile.path(i.canonicalPath()));
+ Q_ASSERT(!i || i.containingObject());
+
+ return true;
+ });
+
+ {
+ DomItem width = obj1.field(Fields::bindings).key(QLatin1String("width")).index(0);
+ QCOMPARE(width.field(Fields::value).qmlObject(), obj1);
+ DomItem w = obj1.bindings().key(QLatin1String("width"));
+ QVERIFY(w.indexes() > 0);
+ QCOMPARE(w.indexes(), 1);
+ QVERIFY(w.index(0).as<Binding>());
+ QVERIFY(w.index(0).as<Binding>()->scriptExpressionValue());
+ QCOMPARE(w.index(0).as<Binding>()->scriptExpressionValue()->code(), u"640");
+ PropertyInfo mPInfo;
+ mPInfo.bindings = { width };
+ mPInfo.propertyDefs.append(width);
+ DomItem wrappedPInfo = obj1.wrapField(Fields::propertyInfos, mPInfo);
+ QVERIFY(wrappedPInfo);
+ const SimpleObjectWrapBase *wrappedPInfoPtr =
+ static_cast<const SimpleObjectWrapBase *>(wrappedPInfo.base());
+ QVERIFY(wrappedPInfoPtr);
+ const PropertyInfo *p1 =
+ reinterpret_cast<const PropertyInfo *>(wrappedPInfoPtr->m_value.data());
+ PropertyInfo p2 = wrappedPInfoPtr->m_value.value<PropertyInfo>();
+ QCOMPARE(mPInfo.bindings.size(), 1);
+ QCOMPARE(mPInfo.propertyDefs.size(), 1);
+ QCOMPARE(mPInfo.bindings.first().toString(), mPInfo.bindings.first().toString());
+ QCOMPARE(mPInfo.propertyDefs.first().toString(),
+ mPInfo.propertyDefs.first().toString());
+
+ QCOMPARE(p2.bindings.size(), 1);
+ QCOMPARE(p2.propertyDefs.size(), 1);
+ QCOMPARE(p2.bindings.first().toString(), mPInfo.bindings.first().toString());
+ QCOMPARE(p2.propertyDefs.first().toString(), mPInfo.propertyDefs.first().toString());
+ QCOMPARE(p1->bindings.size(), 1);
+ QCOMPARE(p1->propertyDefs.size(), 1);
+ QCOMPARE(p1->bindings.first().toString(), mPInfo.bindings.first().toString());
+ QCOMPARE(p1->propertyDefs.first().toString(), mPInfo.propertyDefs.first().toString());
+ }
+ QString bPath = QFileInfo(baseDir).canonicalPath();
+ if (!bPath.isEmpty()) {
+ Path p = tFile.canonicalPath();
+ Q_ASSERT(env.path(p));
+ env.ownerAs<DomEnvironment>()->removePath(bPath);
+ Q_ASSERT(!env.path(p));
+ }
+ }
+
+ void testLoadDep()
+ {
+#ifdef Q_OS_ANDROID
+ QSKIP("Test uncompatible with Android (QTBUG-100171)");
+#endif
+ auto univPtr = std::shared_ptr<QQmlJS::Dom::DomUniverse>(
+ new QQmlJS::Dom::DomUniverse(QLatin1String("univ1")));
+ auto envPtr = std::shared_ptr<QQmlJS::Dom::DomEnvironment>(new QQmlJS::Dom::DomEnvironment(
+ qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option::SingleThreaded, {}, univPtr));
+ QQmlJS::Dom::DomItem env(envPtr);
+ QVERIFY(env);
+ QString testFile1 = baseDir + QLatin1String("/test1.qml");
+ DomItem tFile;
+ envPtr->loadBuiltins();
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile1),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, baseDir), {});
+ envPtr->loadPendingDependencies();
+
+ QVERIFY(tFile);
+ tFile = tFile.field(Fields::currentItem);
+ QVERIFY(tFile);
+ DomItem comp1 = tFile.field(Fields::components).key(QString()).index(0);
+ QVERIFY(comp1);
+ DomItem obj1 = comp1.field(Fields::objects).index(0);
+ QVERIFY(obj1);
+
+ {
+ using namespace Qt::StringLiterals;
+
+ QList<DomItem> rect =
+ obj1.lookup(u"Rectangle"_s, LookupType::Type, LookupOption::Normal);
+ QList<DomItem> rect2 =
+ obj1.lookup(u"Rectangle"_s, LookupType::Symbol, LookupOption::Normal);
+ QList<DomItem> rectAs =
+ obj1.lookup(u"QQ.Rectangle"_s, LookupType::Symbol, LookupOption::Normal);
+
+ QVERIFY(rect.size() == 1);
+ QVERIFY(rect2.size() == 1);
+ QVERIFY(rectAs.size() == 1);
+ QCOMPARE(rect.first().internalKind(), DomType::Export);
+ QCOMPARE(rect.first(), rect2.first());
+ QCOMPARE(rect.first(), rectAs.first());
+ DomItem rect3 = rect.first().proceedToScope();
+ QCOMPARE(rect3.internalKind(), DomType::QmlObject);
+ QList<DomItem> rects;
+ obj1.resolve(
+ Path::Current(PathCurrent::Lookup).field(Fields::type).key(u"Rectangle"_s),
+ [&rects](Path, const DomItem &el) {
+ rects.append(el);
+ return true;
+ },
+ {});
+ QVERIFY(rects.size() == 1);
+ for (const DomItem &el : rects) {
+ QCOMPARE(rect.first(), el);
+ }
+ }
+ {
+ QString fPath = tFile.canonicalFilePath();
+ QString fPath2 = fPath.mid(0, fPath.lastIndexOf(u'/')) % u"/MySingleton.qml";
+ Path p2 = Paths::qmlFileObjectPath(fPath2);
+ DomItem f2 = env.path(p2);
+ QVERIFY2(f2, "Directory dependencies did not load MySingleton.qml");
+ }
+ {
+ QString fPath = tFile.canonicalFilePath();
+ QString fPath2 = fPath.mid(0, fPath.lastIndexOf(u'/')) % u"/ImportMeImplicitly.ui.qml";
+ Path p2 = Paths::qmlFileObjectPath(fPath2);
+ DomItem f2 = env.path(p2);
+ QVERIFY2(f2, "Directory dependencies did not load .ui.qml file!");
+ }
+ }
+
+ void testImports()
+ {
+#ifdef Q_OS_ANDROID
+ QSKIP("Test uncompatible with Android (QTBUG-100171)");
+#endif
+ using namespace Qt::StringLiterals;
+
+ QString testFile1 = baseDir + QLatin1String("/TestImports.qml");
+ auto envPtr = DomEnvironment::create(
+ QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+
+ DomItem tFile;
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile1),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ QVERIFY(tFile);
+ QList<QmlUri> importedModules;
+ for (auto &import : tFile.field(Fields::imports).values()) {
+ if (const Import *importPtr = import.as<Import>()) {
+ if (!importPtr->implicit)
+ importedModules.append(importPtr->uri);
+ }
+ }
+ QCOMPARE(importedModules.at(0).moduleUri(), u"QtQuick"_s);
+ QCOMPARE(importedModules.at(0).directoryString(), u""_s);
+ QCOMPARE(importedModules.at(1).directoryString(), u"../.."_s);
+ QCOMPARE(importedModules.at(1).localPath(), u"../.."_s);
+ QCOMPARE(importedModules.at(1).absoluteLocalPath(), QString());
+ QCOMPARE(importedModules.at(1).absoluteLocalPath(u"/bla/bla"_s), u"/bla/bla/../..");
+ QCOMPARE(importedModules.at(2).directoryString(), u"../dommerging"_s);
+ QCOMPARE(importedModules.at(2).localPath(), u"../dommerging"_s);
+ QCOMPARE(importedModules.at(2).absoluteLocalPath(), QString());
+ QCOMPARE(importedModules.at(2).absoluteLocalPath(u"/bla/bla"_s),
+ u"/bla/bla/../dommerging");
+ QCOMPARE(importedModules.at(3).directoryString(), u"C:/some/path"_s);
+ QCOMPARE(importedModules.at(3).localPath(), u"C:/some/path"_s);
+ QCOMPARE(importedModules.at(4).directoryString(), u"http://bla.com/"_s);
+ QCOMPARE(importedModules.at(4).directoryUrl().toString(), u"http://bla.com/"_s);
+ QCOMPARE(importedModules.at(5).absoluteLocalPath(), u"/absolute/path"_s);
+ QVERIFY(QmlUri::fromDirectoryString("QtQuick") != importedModules.at(0));
+ QCOMPARE(QmlUri::fromUriString("QtQuick"), importedModules.at(0));
+ }
+ void testDeepCopy()
+ {
+ QString testFile = baseDir + QLatin1String("/test1.qml");
+
+ auto envPtr = DomEnvironment::create(
+ qmltypeDirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+
+ DomItem tFile; // place where to store the loaded file
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ envPtr->loadPendingDependencies();
+ DomItem f = tFile.fileObject();
+ QString dump1;
+ f.dump([&dump1](QStringView v) { dump1.append(v); });
+ MutableDomItem copy = f.makeCopy();
+ QString dump2;
+ copy.item().dump([&dump2](QStringView v) { dump2.append(v); });
+ QString diff = lineDiff(dump1, dump2, 2);
+ if (!diff.isEmpty())
+ qDebug().nospace().noquote() << diff;
+ QCOMPARE(dump1, dump2);
+ QStringList diffs = domCompareStrList(f, copy, FieldFilter::compareFilter());
+ if (!diffs.isEmpty())
+ qDebug() << "testDeepCopy.diffs:" << diffs;
+ QVERIFY(diffs.isEmpty());
+ DomItem env(envPtr);
+ DomItem univFile = env.universe().path(f.canonicalPath());
+ MutableDomItem univFileCopy = univFile.makeCopy();
+ QStringList univFileDiffs =
+ domCompareStrList(univFile, univFileCopy, FieldFilter::compareFilter());
+ if (!univFileDiffs.isEmpty())
+ qDebug() << "testDeepCopy.univFileDiffs:" << univFileDiffs;
+ QVERIFY(univFileDiffs.isEmpty());
+ QString bPath = QFileInfo(baseDir).canonicalFilePath();
+ if (!bPath.isEmpty()) {
+ Path p = f.canonicalPath();
+ Q_ASSERT(env.path(p));
+ env.ownerAs<DomEnvironment>()->removePath(bPath);
+ Q_ASSERT(!env.path(p));
+ }
+ }
+
+ void testInMemory()
+ {
+ DomItem res = DomItem::fromCode("MyItem{}");
+ QCOMPARE(res.qmlObject(GoTo::Strict), DomItem());
+ DomItem obj = res.qmlObject(GoTo::MostLikely);
+ QCOMPARE(obj.name(), u"MyItem");
+ }
+
+ static void checkAliases(const DomItem &qmlObj)
+ {
+ using namespace Qt::StringLiterals;
+
+ if (const QmlObject *qmlObjPtr = qmlObj.as<QmlObject>()) {
+ auto pDefs = qmlObjPtr->propertyDefs();
+ auto i = pDefs.constBegin();
+ while (i != pDefs.constEnd()) {
+ if (i.value().isAlias()) {
+ QString propName = i.key();
+ DomItem value = qmlObj.bindings().key(propName).index(0).field(Fields::value);
+ LocallyResolvedAlias rAlias =
+ qmlObjPtr->resolveAlias(qmlObj, value.ownerAs<ScriptExpression>());
+ if (propName.startsWith(u"a")) {
+ QCOMPARE(rAlias.baseObject.internalKind(), DomType::QmlObject);
+ switch (propName.last(1).at(0).unicode()) {
+ case u'i':
+ QCOMPARE(rAlias.status, LocallyResolvedAlias::Status::ResolvedProperty);
+ QCOMPARE(rAlias.typeName, u"int"_s);
+ QVERIFY(rAlias.accessedPath.isEmpty());
+ QCOMPARE(rAlias.localPropertyDef.internalKind(),
+ DomType::PropertyDefinition);
+ break;
+ case u'r':
+ QCOMPARE(rAlias.status, LocallyResolvedAlias::Status::ResolvedProperty);
+ QCOMPARE(rAlias.typeName, u"real"_s);
+ QVERIFY(rAlias.accessedPath.isEmpty());
+ QCOMPARE(rAlias.localPropertyDef.internalKind(),
+ DomType::PropertyDefinition);
+ break;
+ case u'I':
+ QCOMPARE(rAlias.status, LocallyResolvedAlias::Status::ResolvedObject);
+ QCOMPARE(rAlias.typeName, u"Item"_s);
+ QCOMPARE(rAlias.accessedPath, QStringList { u"objectName"_s });
+ QVERIFY(!rAlias.localPropertyDef);
+ break;
+ case u'q':
+ QCOMPARE(rAlias.status, LocallyResolvedAlias::Status::ResolvedObject);
+ QCOMPARE(rAlias.typeName, u"QtObject"_s);
+ QCOMPARE(rAlias.accessedPath, QStringList { u"objectName"_s });
+ QVERIFY(!rAlias.localPropertyDef);
+ break;
+ case u'Q':
+ QCOMPARE(rAlias.status, LocallyResolvedAlias::Status::ResolvedObject);
+ QCOMPARE(rAlias.typeName, u"QtObject"_s);
+ QCOMPARE(rAlias.accessedPath, QStringList { u"objectName"_s });
+ QVERIFY(rAlias.localPropertyDef);
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ } else if (propName.startsWith(u"loop")) {
+ QCOMPARE(rAlias.status, LocallyResolvedAlias::Status::Loop);
+ } else if (propName.startsWith(u"tooDeep")) {
+ QCOMPARE(rAlias.status, LocallyResolvedAlias::Status::TooDeep);
+ } else if (propName.startsWith(u"invalid")) {
+ QCOMPARE(rAlias.status, LocallyResolvedAlias::Status::Invalid);
+ } else if (propName.startsWith(u"objRef")) {
+ QCOMPARE(rAlias.status, LocallyResolvedAlias::Status::ResolvedObject);
+ } else {
+ Q_ASSERT(false);
+ }
+ }
+ ++i;
+ }
+ for (const DomItem &obj : qmlObj.children().values()) {
+ if (obj.as<QmlObject>())
+ checkAliases(obj);
+ }
+ }
+ }
+
+ void testAliasResolve_data()
+ {
+ QTest::addColumn<QString>("inFile");
+
+ QTest::newRow("aliasProperties") << QStringLiteral(u"aliasProperties.qml");
+ QTest::newRow("invalidAliasProperties") << QStringLiteral(u"invalidAliasProperties.qml");
+ }
+ void testAliasResolve()
+ {
+ using namespace Qt::StringLiterals;
+
+ QFETCH(QString, inFile);
+ QString testFile1 = baseDir + u"/"_s + inFile;
+ auto envPtr = DomEnvironment::create(
+ QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+
+ DomItem tFile;
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile1),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ DomItem rootObj = tFile.qmlObject(GoTo::MostLikely);
+ checkAliases(rootObj);
+ }
+
+ void inlineComponents()
+ {
+ using namespace Qt::StringLiterals;
+
+ QString testFile = baseDir + u"/inlineComponents.qml"_s;
+
+ DomCreationOptions options{ DomCreationOption::WithScriptExpressions };
+ auto envPtr = DomEnvironment::create(
+ QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies, options);
+
+ DomItem tFile;
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ auto rootQmlObject = tFile.rootQmlObject(GoTo::MostLikely);
+
+ // check if the lookup can find the inline components correctly, to see if the
+ // visitScopeChain also visit them.
+ auto ic3 = rootQmlObject.lookup("IC3", LookupType::Type, LookupOption::Normal,
+ [](const ErrorMessage &) {});
+
+ QCOMPARE(ic3.size(), 1);
+ QCOMPARE(ic3.front().name(), "inlineComponents.IC3");
+ QCOMPARE(ic3.front().field(Fields::nameIdentifiers).internalKind(), DomType::ScriptType);
+ QCOMPARE(ic3.front()
+ .field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .value()
+ .toString(),
+ u"IC3"_s);
+
+ auto ic1 = rootQmlObject.lookup("IC1", LookupType::Type, LookupOption::Normal,
+ [](const ErrorMessage &) {});
+
+ QCOMPARE(ic1.size(), 1);
+ QCOMPARE(ic1.front().name(), "inlineComponents.IC1");
+ }
+
+ void inlineObject()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/inlineObject.qml"_s;
+
+ auto envPtr = DomEnvironment::create(
+ QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+
+ DomItem tFile;
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ auto rootQmlObject = tFile.rootQmlObject(GoTo::MostLikely);
+
+ // check that the inline objects have their prototypes set.
+
+ {
+ auto prototypes = rootQmlObject.propertyInfos()
+ .key(u"myItem"_s)
+ .field(Fields::bindings)
+ .index(0)
+ .field(Fields::value)
+ .field(Fields::prototypes);
+ QVERIFY(prototypes.internalKind() != DomType::Empty);
+ QCOMPARE(prototypes.indexes(), 1);
+ QCOMPARE(prototypes.index(0)
+ .field(Fields::referredObjectPath)
+ .as<ConstantData>()
+ ->value()
+ .toString(),
+ u"@lookup.type[\"Item\"]"_s);
+ }
+
+ {
+ auto prototypes2 = rootQmlObject.propertyInfos()
+ .key(u"myItem2"_s)
+ .field(Fields::bindings)
+ .index(0)
+ .field(Fields::value)
+ .field(Fields::prototypes);
+ QVERIFY(prototypes2.internalKind() != DomType::Empty);
+ QCOMPARE(prototypes2.indexes(), 1);
+ QCOMPARE(prototypes2.index(0)
+ .field(Fields::referredObjectPath)
+ .as<ConstantData>()
+ ->value()
+ .toString(),
+ u"@lookup.type[\"IC\"]"_s);
+ }
+ }
+
+ void scopesInDom()
+ {
+ QString fileName = baseDir + u"/checkScopes.qml"_s;
+
+ const QStringList importPaths = {
+ QLibraryInfo::path(QLibraryInfo::QmlImportsPath),
+ };
+
+ DomItem tFile;
+
+ auto envPtr = DomEnvironment::create(
+ importPaths,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies,
+ WithSemanticAnalysis);
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, fileName),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ auto root = tFile.rootQmlObject(GoTo::MostLikely);
+
+ {
+ auto rootQmlObject = root.as<QmlObject>();
+ QVERIFY(rootQmlObject);
+ auto rootScope = rootQmlObject->semanticScope();
+ QVERIFY(rootScope);
+ QVERIFY(rootScope->hasOwnProperty("myInt"));
+ QVERIFY(rootScope->hasOwnProperty("myInt2"));
+ QVERIFY(rootScope->hasOwnPropertyBindings("myInt"));
+ QVERIFY(rootScope->hasOwnPropertyBindings("myInt2"));
+ }
+ }
+
+ void propertyBindings()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/propertyBindings.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ // check the binding to a and b
+ DomItem a = rootQmlObject.path(".bindings[\"a\"][0].value.scriptElement.value");
+ QCOMPARE(a.value().toDouble(), 42);
+
+ DomItem b = rootQmlObject.path(".bindings[\"b\"][0].value.scriptElement.identifier");
+ QCOMPARE(b.value().toString(), "a");
+
+ DomItem root = rootQmlObject.path(".idStr");
+ QCOMPARE(root.value().toString(), "root");
+
+ DomItem componentIds = rootQmlObject.component().path(".ids");
+ QCOMPARE(componentIds.keys(), QSet<QString>({ "root" }));
+
+ DomItem idScriptExpression =
+ componentIds.key("root").index(0).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(idScriptExpression.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(idScriptExpression.field(Fields::identifier).value().toString(), "root");
+ }
+
+ void variableDeclarations()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/variableDeclarations.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ DomItem block = rootQmlObject.path(".methods[\"f\"][0].body.scriptElement");
+
+ // check that variabledeclarationlists and statement lists are correctly collected
+ {
+ // let one = 1, two = 2, three = 3, four = 4, five = 5, six = 6
+ std::vector<QString> data = { u"one"_s, u"two"_s, u"three"_s,
+ u"four"_s, u"five"_s, u"six"_s };
+ DomItem block =
+ rootQmlObject.path(".methods[\"count\"][0].body.scriptElement.statements");
+ DomItem variableDeclaration = block.index(0).field(Fields::declarations);
+ QCOMPARE((size_t)variableDeclaration.indexes(), data.size());
+ for (size_t i = 0; i < data.size(); ++i) {
+ DomItem variableDeclarationEntry = variableDeclaration.index(i);
+ QCOMPARE(variableDeclarationEntry.field(Fields::identifier).value().toString(),
+ data[i]);
+ QCOMPARE((size_t)variableDeclarationEntry.field(Fields::initializer)
+ .value()
+ .toInteger(),
+ i + 1);
+ }
+ // let testMe = 123
+ DomItem testDeclaration = block.index(1).field(Fields::declarations);
+ QCOMPARE(testDeclaration.indexes(), 1);
+ QCOMPARE(testDeclaration.index(0).field(Fields::identifier).value().toString(),
+ u"testMe"_s);
+ QCOMPARE(testDeclaration.index(0).field(Fields::initializer).value().toInteger(),
+ 123ll);
+ }
+
+ // if there is a failure in the following line, then thats because either the
+ // variabledeclarationlist or the statement list was wrongly collected and the
+ // domcreator gave up on creating the Dom (unexpected DomType: DomType::ScriptExpression
+ // means that no Dom was constructed for f's body)
+
+ // This block should have a semantic scope that defines sum and helloWorld
+ auto blockSemanticScope = block.semanticScope();
+ QVERIFY(blockSemanticScope);
+ QVERIFY(blockSemanticScope);
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"sum"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"helloWorld"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"a"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"b"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"aa"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"bb"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"bool1"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"bool2"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"nullVar"_s));
+ DomItem statements = block.field(Fields::statements);
+ QCOMPARE(statements.indexes(), 8);
+
+ // avoid that toInteger calls return 0 on error, which sometimes is the correct response.
+ const int invalidEnumValue = 9999;
+
+ {
+ // let sum = 0, helloWorld = "hello"
+ DomItem variableDeclaration = statements.index(0).field(Fields::declarations);
+ QCOMPARE(variableDeclaration.indexes(), 2);
+ DomItem sumInitialization = variableDeclaration.index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(sumInitialization.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Let);
+ QCOMPARE(sumInitialization.field(Fields::identifier).value().toString(), "sum");
+ QCOMPARE(sumInitialization.field(Fields::initializer)
+ .field(Fields::value)
+ .value()
+ .toDouble(),
+ 0);
+
+ DomItem helloWorldInitialization = variableDeclaration.index(1);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(helloWorldInitialization.field(Fields::scopeType)
+ .value()
+ .toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Let);
+ QCOMPARE(helloWorldInitialization.field(Fields::identifier).value().toString(),
+ "helloWorld");
+ QCOMPARE(helloWorldInitialization.field(Fields::initializer)
+ .field(Fields::value)
+ .value()
+ .toString(),
+ "hello");
+ }
+ {
+ // const a = 3
+ DomItem a = statements.index(1).field(Fields::declarations).index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(a.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Const);
+ QCOMPARE(a.field(Fields::identifier).value().toString(), "a");
+ QCOMPARE(a.field(Fields::initializer).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(a.field(Fields::initializer).field(Fields::value).value().toInteger(), 3);
+ }
+ {
+ // const b = "patron"
+ DomItem b = statements.index(2).field(Fields::declarations).index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(b.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Const);
+ QCOMPARE(b.field(Fields::identifier).value().toString(), "b");
+ QCOMPARE(b.field(Fields::initializer).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(b.field(Fields::initializer).field(Fields::value).value().toString(),
+ "patron");
+ }
+ {
+ // var aa = helloWorld
+ DomItem aa = statements.index(3).field(Fields::declarations).index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(aa.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Var);
+ QCOMPARE(aa.field(Fields::identifier).value().toString(), "aa");
+ QCOMPARE(aa.field(Fields::initializer).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(aa.field(Fields::initializer).field(Fields::identifier).value().toString(),
+ "helloWorld");
+ // var bb = aa
+ DomItem bb = statements.index(3).field(Fields::declarations).index(1);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(bb.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Var);
+ QCOMPARE(bb.field(Fields::identifier).value().toString(), "bb");
+ QCOMPARE(bb.field(Fields::initializer).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(bb.field(Fields::initializer).field(Fields::identifier).value().toString(),
+ "aa");
+ }
+ {
+ // const bool1 = true
+ DomItem bool1 = statements.index(4).field(Fields::declarations).index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(bool1.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Const);
+ QCOMPARE(bool1.field(Fields::identifier).value().toString(), "bool1");
+ QCOMPARE(bool1.field(Fields::initializer).internalKind(), DomType::ScriptLiteral);
+ QVERIFY(bool1.field(Fields::initializer).field(Fields::value).value().isTrue());
+ }
+ {
+ // let bool2 = false
+ DomItem bool2 = statements.index(5).field(Fields::declarations).index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(bool2.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Let);
+ QCOMPARE(bool2.field(Fields::identifier).value().toString(), "bool2");
+ QCOMPARE(bool2.field(Fields::initializer).internalKind(), DomType::ScriptLiteral);
+ QVERIFY(bool2.field(Fields::initializer).field(Fields::value).value().isFalse());
+ }
+ {
+ // var nullVar = null
+ DomItem nullVar = statements.index(6).field(Fields::declarations).index(0);
+ QEXPECT_FAIL(0, "ScopeTypes not implmented yet, as not needed by qmlls for now!",
+ Continue);
+ QCOMPARE(nullVar.field(Fields::scopeType).value().toInteger(invalidEnumValue),
+ ScriptElements::VariableDeclarationEntry::ScopeType::Var);
+ QCOMPARE(nullVar.field(Fields::identifier).value().toString(), "nullVar");
+ QCOMPARE(nullVar.field(Fields::initializer).internalKind(), DomType::ScriptLiteral);
+ QVERIFY(nullVar.field(Fields::initializer).field(Fields::value).value().isNull());
+ }
+ }
+
+ void ifStatements()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/ifStatements.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ DomItem block = rootQmlObject.path(".methods[\"conditional\"][0].body.scriptElement");
+ auto blockSemanticScope = block.semanticScope();
+ QVERIFY(blockSemanticScope);
+
+ DomItem statements = block.field(Fields::statements);
+ QCOMPARE(statements.indexes(), 5);
+
+ // let i = 5
+ DomItem iDeclaration = statements.index(0);
+ QCOMPARE(iDeclaration.internalKind(), DomType::ScriptVariableDeclaration);
+
+ {
+ // if (i)
+ // i = 42
+ DomItem conditional = statements.index(1);
+ DomItem condition = conditional.field(Fields::condition);
+ QCOMPARE(condition.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(condition.field(Fields::identifier).value().toString(), u"i"_s);
+
+ DomItem consequence = conditional.field(Fields::consequence);
+ auto nonBlockSemanticScope = consequence.semanticScope();
+ QVERIFY(!nonBlockSemanticScope); // because there is no block
+
+ QCOMPARE(consequence.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(consequence.field(Fields::left).field(Fields::identifier).value().toString(),
+ u"i"_s);
+ QCOMPARE(consequence.field(Fields::right).field(Fields::value).value().toDouble(), 42);
+
+ QCOMPARE(conditional.field(Fields::alternative).internalKind(), DomType::Empty);
+ }
+ {
+ // if (i == 55)
+ // i = 32
+ // else
+ // i = i - 1
+ DomItem conditional = statements.index(2);
+ DomItem condition = conditional.field(Fields::condition);
+ QCOMPARE(condition.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(condition.field(Fields::right).field(Fields::value).value().toDouble(), 55);
+
+ DomItem consequence = conditional.field(Fields::consequence);
+ auto nonBlockSemanticScope = consequence.semanticScope();
+ QVERIFY(!nonBlockSemanticScope);
+
+ QCOMPARE(consequence.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(consequence.field(Fields::left).field(Fields::identifier).value().toString(),
+ u"i"_s);
+ QCOMPARE(consequence.field(Fields::right).field(Fields::value).value().toDouble(), 32);
+
+ DomItem alternative = conditional.field(Fields::alternative);
+ QCOMPARE(alternative.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(alternative.field(Fields::left).field(Fields::identifier).value().toString(),
+ u"i"_s);
+ QCOMPARE(alternative.field(Fields::right).internalKind(),
+ DomType::ScriptBinaryExpression);
+ }
+ {
+ // if (i == 42) {
+ // i = 111
+ // }
+ DomItem conditional = statements.index(3);
+ DomItem condition = conditional.field(Fields::condition);
+ QCOMPARE(condition.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(condition.field(Fields::right).field(Fields::value).value().toDouble(), 42);
+
+ DomItem consequence = conditional.field(Fields::consequence);
+ auto blockSemanticScope = consequence.semanticScope();
+ QVERIFY(blockSemanticScope);
+
+ QCOMPARE(consequence.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(consequence.field(Fields::statements).indexes(), 1);
+ DomItem consequence1 = consequence.field(Fields::statements).index(0);
+ QCOMPARE(consequence1.field(Fields::left).field(Fields::identifier).value().toString(),
+ u"i"_s);
+ QCOMPARE(consequence1.field(Fields::right).field(Fields::value).value().toDouble(),
+ 111);
+
+ QCOMPARE(conditional.field(Fields::alternative).internalKind(), DomType::Empty);
+ }
+ {
+ // if (i == 746) {
+ // i = 123
+ // } else {
+ // i = 456
+ // }
+
+ DomItem conditional = statements.index(4);
+ DomItem condition = conditional.field(Fields::condition);
+ QCOMPARE(condition.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(condition.field(Fields::right).field(Fields::value).value().toDouble(), 746);
+
+ {
+ DomItem consequence = conditional.field(Fields::consequence);
+ QCOMPARE(consequence.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(consequence.field(Fields::statements).indexes(), 1);
+ DomItem consequence1 = consequence.field(Fields::statements).index(0);
+ QCOMPARE(consequence1.field(Fields::left)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"i"_s);
+ QCOMPARE(consequence1.field(Fields::right).field(Fields::value).value().toDouble(),
+ 123);
+ }
+
+ {
+ DomItem alternative = conditional.field(Fields::alternative);
+ auto blockSemanticScope = alternative.semanticScope();
+ QVERIFY(blockSemanticScope);
+
+ QCOMPARE(alternative.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(alternative.field(Fields::statements).indexes(), 1);
+ DomItem alternative1 = alternative.field(Fields::statements).index(0);
+ QCOMPARE(alternative1.field(Fields::left)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"i"_s);
+ QCOMPARE(alternative1.field(Fields::right).field(Fields::value).value().toDouble(),
+ 456);
+ }
+ }
+ }
+
+ void returnStatement()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/returnStatements.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ DomItem block = rootQmlObject.path(".methods[\"returningFunction\"][0].body.scriptElement");
+ QCOMPARE(block.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(block.field(Fields::statements).indexes(), 1);
+ DomItem conditional = block.field(Fields::statements).index(0);
+ DomItem consequence = conditional.field(Fields::consequence);
+ QCOMPARE(consequence.internalKind(), DomType::ScriptReturnStatement);
+ {
+ DomItem returnValue = consequence.field(Fields::expression);
+ QCOMPARE(returnValue.internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(returnValue.field(Fields::value).value().toDouble(), 123);
+ }
+ DomItem alternative = conditional.field(Fields::alternative);
+ QCOMPARE(alternative.internalKind(), DomType::ScriptReturnStatement);
+ {
+ DomItem returnValue = alternative.field(Fields::expression);
+ QCOMPARE(returnValue.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(returnValue.field(Fields::left).field(Fields::value).value().toDouble(), 1);
+ QCOMPARE(returnValue.field(Fields::right).field(Fields::value).value().toDouble(), 2);
+ }
+ }
+
+ void forStatements()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/forStatements.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ DomItem block = rootQmlObject.path(".methods[\"f\"][0].body.scriptElement");
+ DomItem statements = block.field(Fields::statements);
+ DomItem forLoop = statements.index(1);
+ {
+ // for ( >> let i = 0 << ; i < 100; i = i + 1) {
+ DomItem declarationList =
+ forLoop.field(Fields::declarations).field(Fields::declarations);
+ QCOMPARE(declarationList.internalKind(), DomType::List);
+
+ QCOMPARE(declarationList.indexes(), 1);
+ DomItem declaration = declarationList.index(0);
+
+ QCOMPARE(declaration.internalKind(), DomType::ScriptVariableDeclarationEntry);
+ QCOMPARE(declaration.field(Fields::initializer).internalKind(), DomType::ScriptLiteral);
+
+ QCOMPARE(declaration.field(Fields::identifier).value().toString(), "i");
+ QCOMPARE(declaration.field(Fields::initializer).field(Fields::value).value().toDouble(),
+ 0);
+ }
+ {
+ // for ( let i = 0; >> i < 100 <<; i = i + 1) {
+ DomItem condition = forLoop.field(Fields::condition);
+ QCOMPARE(condition.internalKind(), DomType::ScriptBinaryExpression);
+
+ QCOMPARE(condition.field(Fields::left).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(condition.field(Fields::left).field(Fields::identifier).value().toString(),
+ "i");
+
+ QCOMPARE(condition.field(Fields::right).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(condition.field(Fields::right).field(Fields::value).value().toDouble(), 100);
+ }
+ {
+ // for ( let i = 0; i < 100; >> i = i + 1 << ) {
+ DomItem expression = forLoop.field(Fields::expression);
+ QCOMPARE(expression.internalKind(), DomType::ScriptBinaryExpression);
+ DomItem left = expression.field(Fields::left);
+ QCOMPARE(left.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(left.field(Fields::identifier).value().toString(), "i");
+
+ // for ( let i = 0; i < 100; i = >> i + 1 << ) {
+ DomItem right = expression.field(Fields::right);
+ QCOMPARE(right.internalKind(), DomType::ScriptBinaryExpression);
+ DomItem left2 = right.field(Fields::left);
+ QCOMPARE(left2.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(left2.field(Fields::identifier).value().toString(), "i");
+
+ DomItem right2 = right.field(Fields::right);
+ QCOMPARE(right2.internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(right2.field(Fields::value).value().toDouble(), 1);
+ }
+ {
+ // test the body of the for-loop
+ DomItem body = forLoop.field(Fields::body);
+ QCOMPARE(body.internalKind(), DomType::ScriptBlockStatement);
+ auto blockSemanticScope = body.semanticScope();
+ QVERIFY(blockSemanticScope);
+
+ DomItem statementList = body.field(Fields::statements);
+ QCOMPARE(statementList.indexes(), 2);
+ {
+ // >> sum = sum + 1 <<
+ DomItem binaryExpression = statementList.index(0);
+ QCOMPARE(binaryExpression.internalKind(), DomType::ScriptBinaryExpression);
+
+ DomItem left = binaryExpression.field(Fields::left);
+ QCOMPARE(left.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(left.field(Fields::identifier).value().toString(), "sum");
+
+ // sum = >> sum + 1 <<
+ DomItem right = binaryExpression.field(Fields::right);
+ QCOMPARE(right.internalKind(), DomType::ScriptBinaryExpression);
+
+ DomItem left2 = right.field(Fields::left);
+ QCOMPARE(left2.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(left2.field(Fields::identifier).value().toString(), "sum");
+
+ DomItem right2 = right.field(Fields::right);
+ QCOMPARE(right2.internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(right2.field(Fields::value).value().toDouble(), 1);
+ }
+
+ {
+ // >> for (;;) <<
+ // i = 42
+ DomItem innerForLoop = statementList.index(1);
+ QCOMPARE(innerForLoop.internalKind(), DomType::ScriptForStatement);
+ QCOMPARE(innerForLoop.field(Fields::declarations).indexes(), 0);
+ QVERIFY(!innerForLoop.field(Fields::initializer));
+ QVERIFY(!innerForLoop.field(Fields::condition));
+ QVERIFY(!innerForLoop.field(Fields::expression));
+ QVERIFY(innerForLoop.field(Fields::body));
+
+ // for (;;)
+ // >> i = 42 <<
+ DomItem expression = innerForLoop.field(Fields::body);
+ QCOMPARE(expression.internalKind(), DomType::ScriptBinaryExpression);
+
+ DomItem left = expression.field(Fields::left);
+ QCOMPARE(left.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(left.field(Fields::identifier).value().toString(), "i");
+
+ DomItem right = expression.field(Fields::right);
+ QCOMPARE(right.internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(right.field(Fields::value).value().toDouble(), 42);
+ }
+ }
+ }
+
+ void nullStatements()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/nullStatements.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ DomItem block = rootQmlObject.methods()
+ .key(u"testForNull"_s)
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement);
+
+ QVERIFY(block);
+ // First for
+ DomItem statements = block.field(Fields::statements);
+ QCOMPARE(statements.internalKind(), DomType::List);
+ QVERIFY(statements.index(0).field(Fields::body).field(Fields::statements).internalKind()
+ != DomType::Empty);
+ QCOMPARE(statements.index(0).field(Fields::body).field(Fields::statements).length(), 0);
+
+ // Second for
+ DomItem secondFor = statements.index(1).field(Fields::body);
+ QVERIFY(secondFor.internalKind() == DomType::ScriptIdentifierExpression);
+ QCOMPARE(secondFor.field(Fields::identifier).value().toString(), u"x"_s);
+
+ // Empty block
+ QVERIFY(statements.index(3).field(Fields::statements).internalKind() != DomType::Empty);
+ QCOMPARE(statements.index(3).field(Fields::statements).length(), 0);
+ }
+
+ void deconstruction()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/callExpressions.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"deconstruct"_s).index(0);
+ DomItem statement = method.field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(0);
+ QCOMPARE(statement.internalKind(), DomType::ScriptVariableDeclaration);
+ QCOMPARE(statement.field(Fields::declarations).indexes(), 3);
+
+ {
+ DomItem entry = statement.field(Fields::declarations).index(0);
+ QVERIFY(!entry.field(Fields::identifier));
+ DomItem deconstructedProperty =
+ entry.field(Fields::bindingElement).field(Fields::properties);
+
+ QCOMPARE(deconstructedProperty.indexes(), 1);
+ QCOMPARE(deconstructedProperty.index(0).field(Fields::name).value().toString(), u"a"_s);
+
+ DomItem initializer = entry.field(Fields::initializer).field(Fields::properties);
+ QCOMPARE(initializer.indexes(), 2);
+
+ DomItem a32 = initializer.index(0);
+ QCOMPARE(a32.field(Fields::initializer).value().toInteger(), 32);
+ QCOMPARE(a32.field(Fields::name).value().toString(), "a");
+
+ DomItem b42 = initializer.index(1);
+ QCOMPARE(b42.field(Fields::initializer).value().toInteger(), 42);
+ QCOMPARE(b42.field(Fields::name).value().toString(), "b");
+ }
+ {
+ DomItem entry = statement.field(Fields::declarations).index(1);
+ QVERIFY(!entry.field(Fields::identifier));
+ DomItem deconstructedProperty =
+ entry.field(Fields::bindingElement).field(Fields::properties);
+
+ QCOMPARE(deconstructedProperty.indexes(), 2);
+ QCOMPARE(deconstructedProperty.index(0).field(Fields::name).value().toString(), u"b"_s);
+ QCOMPARE(deconstructedProperty.index(1).field(Fields::name).value().toString(), u"c"_s);
+
+ DomItem initializer = entry.field(Fields::initializer).field(Fields::properties);
+ QCOMPARE(initializer.indexes(), 2);
+
+ DomItem a32 = initializer.index(0);
+ QCOMPARE(a32.field(Fields::initializer).value().toInteger(), 32);
+ QCOMPARE(a32.field(Fields::name).value().toString(), "b");
+
+ DomItem b42 = initializer.index(1);
+ QCOMPARE(b42.field(Fields::initializer).value().toInteger(), 42);
+ QCOMPARE(b42.field(Fields::name).value().toString(), "c");
+ }
+ {
+ DomItem entry = statement.field(Fields::declarations).index(2);
+ QVERIFY(!entry.field(Fields::identifier));
+ DomItem deconstructedProperty =
+ entry.field(Fields::bindingElement).field(Fields::elements);
+
+ QCOMPARE(deconstructedProperty.indexes(), 3);
+ QCOMPARE(deconstructedProperty.index(0).field(Fields::identifier).value().toString(),
+ u"d"_s);
+ QCOMPARE(deconstructedProperty.index(1).field(Fields::identifier).value().toString(),
+ u"e"_s);
+ QCOMPARE(deconstructedProperty.index(2).field(Fields::identifier).value().toString(),
+ u"f"_s);
+
+ DomItem initializer = entry.field(Fields::initializer).field(Fields::elements);
+ QCOMPARE(initializer.indexes(), 3);
+
+ for (int i = 0; i < initializer.indexes(); ++i) {
+ QCOMPARE(initializer.index(i).field(Fields::initializer).value().toInteger(),
+ (i + 1) * 111);
+ }
+ }
+ {
+ DomItem statement = method.field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(1);
+ DomItem listWithElision = statement.field(Fields::declarations)
+ .index(0)
+ .field(Fields::initializer)
+ .field(Fields::elements);
+ const int listElements = 6;
+ const int elisionCount = 4;
+ QCOMPARE(listWithElision.indexes(), listElements);
+ for (int i = 0; i < elisionCount; ++i) {
+ QCOMPARE(listWithElision.index(i).internalKind(), DomType::ScriptElision);
+ }
+ QCOMPARE(listWithElision.index(elisionCount).internalKind(), DomType::ScriptArrayEntry);
+ QCOMPARE(listWithElision.index(elisionCount + 1).internalKind(),
+ DomType::ScriptElision);
+ }
+ }
+
+ void callExpressions()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/callExpressions.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+
+ {
+ DomItem p1 = rootQmlObject.path(".bindings[\"p\"][0].value.scriptElement");
+ QCOMPARE(p1.internalKind(), DomType::ScriptCallExpression);
+ QCOMPARE(p1.field(Fields::callee).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1.field(Fields::callee).field(Fields::identifier).value().toString(), "f");
+ QCOMPARE(p1.field(Fields::arguments).internalKind(), DomType::List);
+ QCOMPARE(p1.field(Fields::arguments).indexes(), 0);
+ }
+
+ {
+ DomItem p2 = rootQmlObject.path(".bindings[\"p2\"][0].value.scriptElement");
+ QCOMPARE(p2.internalKind(), DomType::ScriptCallExpression);
+ QCOMPARE(p2.field(Fields::callee).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p2.field(Fields::callee).field(Fields::identifier).value().toString(), "f");
+
+ DomItem p2List = p2.field(Fields::arguments);
+ QCOMPARE(p2List.indexes(), 20);
+ for (int i = 0; i < p2List.indexes(); ++i) {
+ QCOMPARE(p2List.index(i).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(p2List.index(i).field(Fields::value).value().toInteger(), i + 1);
+ }
+ }
+
+ {
+ DomItem p3 = rootQmlObject.path(".bindings[\"p3\"][0].value.scriptElement");
+ QCOMPARE(p3.internalKind(), DomType::ScriptCallExpression);
+ QCOMPARE(p3.field(Fields::callee).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p3.field(Fields::callee).field(Fields::identifier).value().toString(), "evil");
+
+ DomItem p3List = p3.field(Fields::arguments);
+ QCOMPARE(p3List.indexes(), 3);
+
+ DomItem firstArg = p3List.index(0);
+ QCOMPARE(firstArg.internalKind(), DomType::ScriptObject);
+
+ {
+ DomItem helloWorld = firstArg.field(Fields::properties).index(0);
+ QCOMPARE(helloWorld.internalKind(), DomType::ScriptProperty);
+ QCOMPARE(helloWorld.field(Fields::initializer).value().toString(), "World");
+ QCOMPARE(helloWorld.field(Fields::name).value().toString(), "hello");
+ }
+
+ {
+ DomItem yyyy = firstArg.field(Fields::properties).index(1);
+ QCOMPARE(yyyy.internalKind(), DomType::ScriptProperty);
+ QCOMPARE(yyyy.field(Fields::initializer).value().toString(), "yyy");
+ QCOMPARE(yyyy.field(Fields::name).value().toString(), "y");
+ }
+
+ DomItem secondArg = p3List.index(1);
+ QCOMPARE(secondArg.internalKind(), DomType::ScriptArray);
+ {
+ for (int i = 0; i < 3; ++i) {
+ DomItem current = secondArg.field(Fields::elements).index(i);
+ QCOMPARE(current.internalKind(), DomType::ScriptArrayEntry);
+ QCOMPARE(current.field(Fields::initializer).value().toInteger(), i + 1);
+ }
+ }
+
+ DomItem thirdArg = p3List.index(2);
+ QCOMPARE(thirdArg.internalKind(), DomType::ScriptObject);
+ {
+ DomItem is = thirdArg.field(Fields::properties).index(0);
+ QCOMPARE(is.internalKind(), DomType::ScriptProperty);
+ QCOMPARE(is.field(Fields::name).value().toString(), "is");
+ DomItem initializer = is.field(Fields::initializer).field(Fields::properties);
+
+ QCOMPARE(initializer.index(0).field(Fields::name).value().toString(), "a");
+ QCOMPARE(initializer.index(0).field(Fields::initializer).value().toInteger(), 111);
+
+ QCOMPARE(initializer.index(1).field(Fields::name).value().toString(), "lot");
+ QCOMPARE(initializer.index(1).field(Fields::initializer).value().toInteger(), 222);
+
+ QCOMPARE(initializer.index(2).field(Fields::name).value().toString(), "of");
+ QCOMPARE(initializer.index(2).field(Fields::initializer).value().toInteger(), 333);
+
+ QCOMPARE(initializer.index(3).field(Fields::name).value().toString(), "fun");
+ QCOMPARE(initializer.index(3).field(Fields::initializer).value().toInteger(), 444);
+
+ QCOMPARE(initializer.index(4).field(Fields::name).value().toString(), "really");
+ QCOMPARE(initializer.index(4)
+ .field(Fields::initializer)
+ .field(Fields::elements)
+ .index(0)
+ .field(Fields::initializer)
+ .value()
+ .toString(),
+ "!");
+ }
+ }
+
+ {
+ DomItem functionFs = rootQmlObject.field(Fields::methods).key(u"f");
+ QCOMPARE(functionFs.indexes(), 1);
+ DomItem functionF = functionFs.index(0);
+
+ QVERIFY(!functionF.semanticScope().isNull());
+ QVERIFY(functionF.semanticScope()->ownJSIdentifier("helloF").has_value());
+ DomItem parameters = functionF.field(Fields::parameters);
+ std::vector<QString> parameterNames = {
+ u"q"_s, u"w"_s, u"e"_s, u"r"_s, u"t"_s, u"y"_s
+ };
+ QCOMPARE(parameterNames.size(), (size_t)parameters.indexes());
+
+ for (size_t i = 0; i < parameterNames.size(); ++i) {
+ DomItem currentParameter = parameters.index(i);
+ QCOMPARE(currentParameter.field(Fields::name).value().toString(),
+ parameterNames[i]);
+ }
+ }
+
+ {
+ DomItem functionFWithDefaults =
+ rootQmlObject.field(Fields::methods).key(u"fWithDefault");
+ QCOMPARE(functionFWithDefaults.indexes(), 1);
+ DomItem functionFWithDefault = functionFWithDefaults.index(0);
+ QVERIFY(!functionFWithDefault.semanticScope().isNull());
+ QVERIFY(functionFWithDefault.semanticScope()->ownJSIdentifier(u"helloFWithDefault"_s));
+ DomItem parameters = functionFWithDefault.field(Fields::parameters);
+
+ const std::vector<QString> parameterNames = { u"q"_s, u"w"_s, u"e"_s,
+ u"r"_s, u"t"_s, u"y"_s };
+ const QSet<QString> parameterWithoutDefault = { u"e"_s, u"t"_s };
+
+ QCOMPARE(parameterNames.size(), (size_t)parameters.indexes());
+
+ for (size_t i = 0; i < parameterNames.size(); ++i) {
+ DomItem currentParameter = parameters.index(i);
+ QCOMPARE(currentParameter.field(Fields::name).value().toString(),
+ parameterNames[i]);
+
+ DomItem scriptElement =
+ currentParameter.field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(scriptElement.internalKind(), DomType::ScriptFormalParameter);
+ QCOMPARE(scriptElement.field(Fields::identifier).value().toString(),
+ parameterNames[i]);
+ if (!parameterWithoutDefault.contains(parameterNames[i])) {
+ QCOMPARE((size_t)scriptElement.field(Fields::initializer).value().toInteger(),
+ i + 1);
+ }
+ }
+ }
+
+ {
+ // note: no need to check the inside of ScriptObject and ScriptArray as those are
+ // already tested in deconstruction().
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"evil"_s).index(0);
+ QVERIFY(!method.semanticScope().isNull());
+ QVERIFY(method.semanticScope()->ownJSIdentifier("helloEvil").has_value());
+ DomItem parameters = method.field(Fields::parameters);
+ QCOMPARE(parameters.indexes(), 3);
+
+ {
+ DomItem parameter1 =
+ parameters.index(0).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(parameter1.internalKind(), DomType::ScriptFormalParameter);
+
+ DomItem objectPattern = parameter1.field(Fields::bindingElement);
+ QCOMPARE(objectPattern.internalKind(), DomType::ScriptObject);
+ QCOMPARE(objectPattern.field(Fields::properties).indexes(), 2);
+ }
+ {
+ DomItem parameter2 =
+ parameters.index(1).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(parameter2.internalKind(), DomType::ScriptFormalParameter);
+
+ DomItem objectPattern = parameter2.field(Fields::bindingElement);
+ QCOMPARE(objectPattern.internalKind(), DomType::ScriptArray);
+ QCOMPARE(objectPattern.field(Fields::elements).indexes(), 3);
+ }
+ {
+ DomItem parameter3 =
+ parameters.index(2).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(parameter3.internalKind(), DomType::ScriptFormalParameter);
+
+ DomItem objectPattern = parameter3.field(Fields::bindingElement);
+ QCOMPARE(objectPattern.internalKind(), DomType::ScriptObject);
+ QCOMPARE(objectPattern.field(Fields::properties).indexes(), 3);
+
+ DomItem parameter3DefaultValue = parameter3.field(Fields::initializer);
+ QCOMPARE(parameter3DefaultValue.internalKind(), DomType::ScriptObject);
+
+ DomItem objectPatternDefaultValue =
+ parameter3DefaultValue.field(Fields::properties);
+ QCOMPARE(objectPatternDefaultValue.indexes(), 3);
+ }
+ }
+ {
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"marmelade"_s).index(0);
+ QVERIFY(!method.semanticScope().isNull());
+ QVERIFY(method.semanticScope()->ownJSIdentifier(u"helloMarmelade"_s));
+ DomItem parameters = method.field(Fields::parameters);
+ QCOMPARE(parameters.indexes(), 1);
+ {
+ DomItem parameter1 =
+ parameters.index(0).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(parameter1.internalKind(), DomType::ScriptFormalParameter);
+ QCOMPARE(parameter1.field(Fields::identifier).value().toString(), "onTheBread");
+ }
+ }
+ {
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"marmelade2"_s).index(0);
+ QVERIFY(!method.semanticScope().isNull());
+ QVERIFY(method.semanticScope()->ownJSIdentifier(u"helloMarmelade2"_s));
+ DomItem parameters = method.field(Fields::parameters);
+ QCOMPARE(parameters.indexes(), 3);
+ {
+ DomItem parameter3 =
+ parameters.index(2).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(parameter3.internalKind(), DomType::ScriptFormalParameter);
+ QCOMPARE(parameter3.field(Fields::identifier).value().toString(), "onTheBread");
+ }
+ }
+ {
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"withTypes"_s).index(0);
+ QVERIFY(!method.semanticScope().isNull());
+ QCOMPARE(method.semanticScope()->scopeType(),
+ QQmlJSScope::ScopeType::JSFunctionScope);
+ DomItem parameters = method.field(Fields::parameters);
+ QCOMPARE(parameters.indexes(), 2);
+ QCOMPARE(parameters.index(0)
+ .field(Fields::value)
+ .field(Fields::scriptElement)
+ .field(Fields::type)
+ .field(Fields::typeName)
+ .value()
+ .toString(),
+ "int");
+ QCOMPARE(parameters.index(1)
+ .field(Fields::value)
+ .field(Fields::scriptElement)
+ .field(Fields::type)
+ .field(Fields::typeName)
+ .value()
+ .toString(),
+ "MyType");
+ }
+ {
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"empty"_s).index(0);
+ QVERIFY(!method.semanticScope().isNull());
+ QCOMPARE(method.semanticScope()->scopeType(),
+ QQmlJSScope::ScopeType::JSFunctionScope);
+ }
+ {
+ DomItem method = rootQmlObject.field(Fields::methods).key(u"mySignal"_s).index(0);
+ // signals contain the scope of the QML object owning them
+ QVERIFY(!method.semanticScope().isNull());
+ QCOMPARE(method.semanticScope()->scopeType(), QQmlJSScope::ScopeType::QMLScope);
+ }
+ }
+
+ void fieldMemberExpression()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/fieldMemberExpression.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+
+ {
+ DomItem p1 = rootQmlObject.path(".bindings[\"p1\"][0].value.scriptElement");
+ QCOMPARE(p1.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1.field(Fields::identifier).value().toString(), "p");
+ }
+
+ {
+ DomItem p1Qualified =
+ rootQmlObject.path(".bindings[\"p1Qualified\"][0].value.scriptElement");
+ fieldMemberExpressionHelper(p1Qualified, QStringList{ u"p"_s });
+ QCOMPARE(p1Qualified.field(Fields::left).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1Qualified.field(Fields::left).field(Fields::identifier).value().toString(),
+ "root");
+ }
+ {
+ // property var p1Bracket: root["p"]
+ DomItem p1 = rootQmlObject.path(".bindings[\"p1Bracket\"][0].value.scriptElement");
+ QCOMPARE(p1.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(p1.field(Fields::operation).value().toInteger(),
+ ScriptElements::BinaryExpression::ArrayMemberAccess);
+
+ QCOMPARE(p1.field(Fields::left).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1.field(Fields::left).field(Fields::identifier).value().toString(), "root");
+
+ QCOMPARE(p1.field(Fields::right).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(p1.field(Fields::right).field(Fields::value).value().toString(), "p");
+ }
+ {
+ // property var p1Index: root[42]
+ DomItem p1 = rootQmlObject.path(".bindings[\"p1Index\"][0].value.scriptElement");
+ QCOMPARE(p1.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(p1.field(Fields::operation).value().toInteger(),
+ ScriptElements::BinaryExpression::ArrayMemberAccess);
+
+ QCOMPARE(p1.field(Fields::left).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1.field(Fields::left).field(Fields::identifier).value().toString(), "root");
+
+ QCOMPARE(p1.field(Fields::right).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(p1.field(Fields::right).field(Fields::value).value().toInteger(), 42);
+ }
+ {
+ // property var p1Key: root[p]
+ DomItem p1 = rootQmlObject.path(".bindings[\"p1Key\"][0].value.scriptElement");
+ QCOMPARE(p1.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(p1.field(Fields::operation).value().toInteger(),
+ ScriptElements::BinaryExpression::ArrayMemberAccess);
+
+ QCOMPARE(p1.field(Fields::left).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1.field(Fields::left).field(Fields::identifier).value().toString(), "root");
+
+ QCOMPARE(p1.field(Fields::right).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1.field(Fields::right).field(Fields::identifier).value().toString(), "p");
+ }
+ {
+ DomItem p1Qualified =
+ rootQmlObject.path(".bindings[\"p1Qualified\"][0].value.scriptElement");
+ fieldMemberExpressionHelper(p1Qualified, QStringList{ u"p"_s });
+ QCOMPARE(p1Qualified.field(Fields::left).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(p1Qualified.field(Fields::left).field(Fields::identifier).value().toString(),
+ "root");
+ }
+
+ {
+ DomItem p3 = rootQmlObject.path(".bindings[\"p3\"][0].value.scriptElement");
+ fieldMemberExpressionHelper(p3, QStringList{ u"property2"_s, u"p"_s });
+ DomItem p3Left = p3.field(Fields::left).field(Fields::left);
+ QCOMPARE(p3Left.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p3Left.field(Fields::identifier).value().toString(), "property1");
+ }
+
+ {
+ DomItem p3Qualified =
+ rootQmlObject.path(".bindings[\"p3Qualified\"][0].value.scriptElement");
+ fieldMemberExpressionHelper(p3Qualified,
+ QStringList{ u"property1"_s, u"property2"_s, u"p"_s });
+ DomItem p3Left =
+ p3Qualified.field(Fields::left).field(Fields::left).field(Fields::left);
+ QCOMPARE(p3Left.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(p3Left.field(Fields::identifier).value().toString(), "root");
+ }
+ }
+
+ void switchStatement()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/switchStatement.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ QVERIFY(rootQmlObject);
+
+ {
+ DomItem statements = rootQmlObject.methods()
+ .key(u"switchStatement")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements);
+ QVERIFY(statements);
+ QCOMPARE(statements.index(0).internalKind(), DomType::ScriptVariableDeclaration);
+ QCOMPARE(statements.index(1).internalKind(), DomType::ScriptVariableDeclaration);
+
+ DomItem firstSwitch = statements.index(2);
+ QVERIFY(firstSwitch);
+ QCOMPARE(firstSwitch.internalKind(), DomType::ScriptSwitchStatement);
+ QCOMPARE(firstSwitch.field(Fields::caseBlock).internalKind(), DomType::ScriptCaseBlock);
+ QCOMPARE(firstSwitch.field(Fields::expression).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(firstSwitch.field(Fields::expression)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"animals");
+
+ DomItem caseClauses = firstSwitch.field(Fields::caseBlock).field(Fields::caseClauses);
+ QVERIFY(caseClauses);
+ QCOMPARE(caseClauses.index(0).internalKind(), DomType::ScriptCaseClause);
+ QCOMPARE(caseClauses.index(0).field(Fields::expression).internalKind(),
+ DomType::ScriptLiteral);
+ QCOMPARE(caseClauses.index(0).field(Fields::expression).value().toString(), u"cat");
+
+ DomItem innerSwitch = caseClauses.index(0).field(Fields::statements).index(0);
+ QVERIFY(innerSwitch);
+ QCOMPARE(innerSwitch.internalKind(), DomType::ScriptSwitchStatement);
+ QCOMPARE(innerSwitch.field(Fields::expression).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(innerSwitch.field(Fields::expression).value().toString(), u"no");
+
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(0)
+ .internalKind(),
+ DomType::ScriptCaseClause);
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(0)
+ .field(Fields::expression)
+ .internalKind(),
+ DomType::ScriptLiteral);
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(0)
+ .field(Fields::expression)
+ .value()
+ .toInteger(),
+ 0);
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(0)
+ .field(Fields::statements)
+ .index(0)
+ .field(Fields::expression)
+ .value()
+ .toString(),
+ u"patron");
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(1)
+ .internalKind(),
+ DomType::ScriptCaseClause);
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(1)
+ .field(Fields::statements)
+ .index(0)
+ .field(Fields::expression)
+ .value()
+ .toString(),
+ u"mafik");
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::defaultClause)
+ .internalKind(),
+ DomType::ScriptDefaultClause);
+ QCOMPARE(innerSwitch.field(Fields::caseBlock)
+ .field(Fields::defaultClause)
+ .field(Fields::statements)
+ .index(0)
+ .field(Fields::expression)
+ .value()
+ .toString(),
+ u"none");
+
+ // case "dog"
+ DomItem caseDogBlock = firstSwitch.field(Fields::caseBlock)
+ .field(Fields::caseClauses)
+ .index(1)
+ .field(Fields::statements)
+ .index(0);
+ QVERIFY(caseDogBlock);
+ // Check if semantic scope is correctly created for the CaseClause
+ auto blockSemanticScope = caseDogBlock.semanticScope();
+ QVERIFY(blockSemanticScope);
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"name"_s));
+ QVERIFY(blockSemanticScope->ownJSIdentifier(u"another"_s));
+
+ // Default clause
+ DomItem defaultClause =
+ firstSwitch.field(Fields::caseBlock).field(Fields::defaultClause);
+ QVERIFY(defaultClause);
+ QCOMPARE(defaultClause.internalKind(), DomType::ScriptDefaultClause);
+ QCOMPARE(defaultClause.field(Fields::statements).index(0).internalKind(),
+ DomType::ScriptReturnStatement);
+ QCOMPARE(defaultClause.field(Fields::statements)
+ .index(0)
+ .field(Fields::expression)
+ .internalKind(),
+ DomType::ScriptLiteral);
+ QCOMPARE(defaultClause.field(Fields::statements)
+ .index(0)
+ .field(Fields::expression)
+ .value()
+ .toString(),
+ u"monster");
+
+ // case "moreCases!"
+ const DomItem moreCases = firstSwitch.field(Fields::caseBlock)
+ .field(Fields::moreCaseClauses)
+ .index(0)
+ .field(Fields::statements)
+ .index(0);
+
+ QCOMPARE(moreCases.internalKind(),
+ DomType::ScriptReturnStatement);
+ QCOMPARE(moreCases.field(Fields::expression).value().toString(), u"moreCaseClauses?");
+ }
+ }
+
+ void iterationStatements()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/iterationStatements.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+
+ QVERIFY(rootQmlObject);
+ DomItem methods = rootQmlObject.methods();
+ QVERIFY(methods);
+
+ {
+ // while statement
+ DomItem whileTest = methods.key("whileStatement")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(1);
+ QVERIFY(whileTest);
+ QCOMPARE(whileTest.internalKind(), DomType::ScriptWhileStatement);
+ auto whileScope = whileTest.semanticScope();
+ QVERIFY(whileScope);
+ DomItem whileBody = whileTest.field(Fields::body);
+ QVERIFY(whileBody);
+ QCOMPARE(whileBody.internalKind(), DomType::ScriptBlockStatement);
+ auto scope = whileBody.semanticScope();
+ QVERIFY(scope);
+ QCOMPARE(whileBody.field(Fields::statements).index(0).internalKind(),
+ DomType::ScriptBinaryExpression); // i = i - 1
+ QCOMPARE(
+ whileBody.field(Fields::statements).index(0).field(Fields::left).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(whileBody.field(Fields::statements)
+ .index(0)
+ .field(Fields::left)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"i");
+ DomItem whileExpression = whileTest.field(Fields::expression);
+ QVERIFY(whileExpression);
+ QCOMPARE(whileExpression.internalKind(), DomType::ScriptBinaryExpression); // i > 0
+ QCOMPARE(whileExpression.field(Fields::left).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(whileExpression.field(Fields::left)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"i");
+ QCOMPARE(whileExpression.field(Fields::right).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(whileExpression.field(Fields::right)
+ .field(Fields::identifier)
+ .value()
+ .toInteger(),
+ 0);
+
+ DomItem singleLineWhile = methods.key("whileStatement")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(2);
+ QVERIFY(singleLineWhile);
+ QCOMPARE(singleLineWhile.internalKind(), DomType::ScriptWhileStatement);
+ DomItem singleLineWhileBody = singleLineWhile.field(Fields::body);
+ QVERIFY(singleLineWhileBody);
+ QCOMPARE(singleLineWhileBody.internalKind(), DomType::ScriptBinaryExpression);
+ auto singleLineWhileScope = singleLineWhile.semanticScope();
+ QVERIFY(singleLineWhileScope);
+ QVERIFY(singleLineWhileScope->jsIdentifier("i")); // i is in the parent scope
+ }
+
+ {
+ // do-while statement
+ DomItem doWhile = methods.key("doWhile")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(1);
+ QVERIFY(doWhile);
+ QCOMPARE(doWhile.internalKind(), DomType::ScriptDoWhileStatement);
+
+ auto doWhileScope = doWhile.semanticScope();
+ QVERIFY(doWhileScope);
+ DomItem doWhileBody = doWhile.field(Fields::body);
+ QVERIFY(doWhileBody);
+ auto doWhileBodyScope = doWhileBody.semanticScope();
+ QVERIFY(doWhileBodyScope);
+ QVERIFY(doWhileBodyScope->ownJSIdentifier("b")); // const b = a
+ QCOMPARE(doWhileBody.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(doWhileBody.field(Fields::statements).index(0).internalKind(),
+ DomType::ScriptVariableDeclaration); // const b = a
+ QCOMPARE(doWhileBody.field(Fields::statements).index(1).internalKind(),
+ DomType::ScriptBinaryExpression); // a = a - 1
+
+ DomItem doWhileExpression = doWhile.field(Fields::expression);
+ QVERIFY(doWhileExpression);
+ QCOMPARE(doWhileExpression.internalKind(), DomType::ScriptBinaryExpression); // a > 0
+ QCOMPARE(doWhileExpression.field(Fields::left).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(doWhileExpression.field(Fields::left)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"a");
+ QCOMPARE(doWhileExpression.field(Fields::right).internalKind(), DomType::ScriptLiteral);
+ QCOMPARE(doWhileExpression.field(Fields::right)
+ .field(Fields::identifier)
+ .value()
+ .toInteger(),
+ 0);
+ DomItem singleDoWhile = methods.key("doWhile")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(2);
+ auto singleDoWhileScope = singleDoWhile.semanticScope();
+ QVERIFY(singleDoWhileScope);
+ QVERIFY(singleDoWhileScope->jsIdentifier("a")); // a = a - 1
+ }
+
+ {
+ // for..of
+ DomItem statements = methods.key("forOf")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements);
+ QVERIFY(statements);
+ QCOMPARE(statements.index(0).internalKind(), DomType::ScriptVariableDeclaration);
+ DomItem outerForEach = statements.index(1);
+ QVERIFY(outerForEach);
+ QCOMPARE(outerForEach.internalKind(), DomType::ScriptForEachStatement);
+ DomItem bindingElements =
+ outerForEach.field(Fields::bindingElement).field(Fields::bindingElement);
+ QVERIFY(bindingElements);
+ QCOMPARE(bindingElements.internalKind(), DomType::ScriptArray);
+ QCOMPARE(bindingElements.field(Fields::elements).length(), 2);
+ QCOMPARE(bindingElements.field(Fields::elements)
+ .index(1)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "b");
+ QCOMPARE(outerForEach.field(Fields::expression).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(outerForEach.field(Fields::expression)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"iterable");
+ DomItem forEachStatements = outerForEach.field(Fields::body).field(Fields::statements);
+ QCOMPARE(forEachStatements.index(0).internalKind(), DomType::ScriptVariableDeclaration);
+ QCOMPARE(forEachStatements.index(1).internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(forEachStatements.index(2).internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(forEachStatements.index(3).internalKind(), DomType::ScriptForEachStatement);
+ DomItem firstForEach = forEachStatements.index(1);
+ QVERIFY(firstForEach);
+ DomItem bindingElement =
+ firstForEach.field(Fields::bindingElement).field(Fields::bindingElement);
+ QCOMPARE(bindingElement.internalKind(), DomType::ScriptArray);
+
+ QCOMPARE(bindingElement.field(Fields::elements).length(), 4);
+ QCOMPARE(bindingElement.field(Fields::elements)
+ .index(0)
+ .field(Fields::identifier)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "a1");
+ DomItem secondForEach = forEachStatements.index(2);
+ QCOMPARE(secondForEach.field(Fields::bindingElement).internalKind(),
+ DomType::ScriptPattern);
+ QCOMPARE(secondForEach.field(Fields::bindingElement)
+ .field(Fields::identifier)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "k");
+ QCOMPARE(secondForEach.internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(secondForEach.field(Fields::expression).internalKind(), DomType::ScriptArray);
+ QVERIFY(secondForEach.field(Fields::body));
+ QCOMPARE(secondForEach.field(Fields::body).internalKind(),
+ DomType::ScriptBlockStatement);
+ DomItem thirdForEach = forEachStatements.index(3);
+ QCOMPARE(thirdForEach.field(Fields::bindingElement).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(thirdForEach.field(Fields::bindingElement).value().toString(), "t");
+
+ QCOMPARE(thirdForEach.internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(thirdForEach.field(Fields::expression).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(thirdForEach.field(Fields::expression)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"a");
+ QVERIFY(thirdForEach.field(Fields::body));
+ QCOMPARE(thirdForEach.field(Fields::body).internalKind(),
+ DomType::ScriptBlockStatement);
+
+ DomItem forthForEach = forEachStatements.index(3);
+ QVERIFY(forthForEach);
+ auto forthForEachScope = forthForEach.semanticScope();
+ QVERIFY(forthForEachScope);
+ QVERIFY(forthForEachScope->jsIdentifier("t")); // t lives in parent scope
+ }
+
+ {
+ // for..in
+ DomItem statements = methods.key("forIn")
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements);
+ QVERIFY(statements);
+ QCOMPARE(statements.index(0).internalKind(), DomType::ScriptVariableDeclaration);
+ DomItem outerForEach = statements.index(1);
+ QVERIFY(outerForEach);
+ auto outerForEachScope = outerForEach.semanticScope();
+ QVERIFY(outerForEachScope);
+ QVERIFY(outerForEachScope->jsIdentifier("a")); // var [a,b,c,d]
+ QVERIFY(outerForEachScope->jsIdentifier("b"));
+ QVERIFY(outerForEachScope->jsIdentifier("c"));
+ QVERIFY(outerForEachScope->jsIdentifier("d"));
+ QCOMPARE(outerForEach.internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(statements.index(1).internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(outerForEach.field(Fields::expression)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "enumerable");
+ auto outerForEachBodyScope = outerForEach.field(Fields::body).semanticScope();
+ QVERIFY(outerForEachBodyScope);
+ QVERIFY(outerForEachBodyScope->ownJSIdentifier("t")); // let t
+ DomItem forInStatements = outerForEach.field(Fields::body).field(Fields::statements);
+ QCOMPARE(forInStatements.index(0).internalKind(), DomType::ScriptVariableDeclaration);
+ QCOMPARE(forInStatements.index(1).internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(forInStatements.index(2).internalKind(), DomType::ScriptForEachStatement);
+ DomItem firstForEach = forInStatements.index(1);
+ QVERIFY(firstForEach);
+ auto firstForEachScope = firstForEach.semanticScope();
+ QVERIFY(firstForEachScope);
+ QVERIFY(firstForEachScope->jsIdentifier("t"));
+ QCOMPARE(firstForEach.field(Fields::bindingElement).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(firstForEach.field(Fields::bindingElement)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "t");
+ QCOMPARE(firstForEach.internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(firstForEach.field(Fields::expression).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(firstForEach.field(Fields::expression)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "enumerable");
+ QVERIFY(firstForEach.field(Fields::body));
+ QCOMPARE(firstForEach.field(Fields::body).internalKind(),
+ DomType::ScriptBlockStatement);
+
+ DomItem secondForEach = forInStatements.index(2);
+ QVERIFY(secondForEach);
+ auto secondForEachScope = secondForEach.semanticScope();
+ QVERIFY(secondForEachScope);
+ QVERIFY(secondForEachScope->ownJSIdentifier("a1")); // const [a1,,a2,...rest]
+ QVERIFY(secondForEachScope->ownJSIdentifier("a2"));
+ QVERIFY(secondForEachScope->ownJSIdentifier("rest"));
+
+ DomItem bindingElement =
+ secondForEach.field(Fields::bindingElement).field(Fields::bindingElement);
+ QCOMPARE(bindingElement.internalKind(), DomType::ScriptArray);
+ QCOMPARE(bindingElement.field(Fields::elements)
+ .index(3)
+ .field(Fields::identifier)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ "rest");
+ QCOMPARE(secondForEach.internalKind(), DomType::ScriptForEachStatement);
+ QCOMPARE(secondForEach.field(Fields::expression).internalKind(),
+ DomType::ScriptBinaryExpression);
+ QVERIFY(secondForEach.field(Fields::body));
+ QCOMPARE(secondForEach.field(Fields::body).internalKind(),
+ DomType::ScriptBlockStatement);
+ DomItem thirdForEach = forInStatements.index(3);
+ QVERIFY(thirdForEach);
+ auto thirdForEachScope = thirdForEach.semanticScope();
+ QVERIFY(thirdForEachScope);
+ QVERIFY(thirdForEachScope->ownJSIdentifier("t"));
+ }
+ }
+
+ void doNotCrashEcmaScriptClass()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/ecmaScriptClass.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ QVERIFY(rootQmlObject);
+ }
+
+ void bindingAttachedOrGroupedProperties()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/attachedOrGroupedProperties.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ QVERIFY(rootQmlObject);
+
+ DomItem dotNotation = rootQmlObject.path(u".children[0].bindings[\"grouped.font.family\"][0].bindingIdentifiers");
+ QVERIFY(dotNotation);
+ QCOMPARE(dotNotation.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(dotNotation.field(Fields::left).internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(dotNotation.field(Fields::right).field(Fields::identifier).value().toString(), u"family");
+ QCOMPARE(dotNotation.field(Fields::right).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(dotNotation.field(Fields::left).field(Fields::left).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(dotNotation.field(Fields::left).field(Fields::left).field(Fields::identifier).value().toString(), u"grouped");
+ QCOMPARE(dotNotation.field(Fields::left).field(Fields::right).internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(dotNotation.field(Fields::left).field(Fields::right).field(Fields::identifier).value().toString(), u"font");
+ auto dotNotationScope = dotNotation.semanticScope();
+ QVERIFY(!dotNotationScope);
+
+ DomItem groupNotationChild1 = rootQmlObject.path(u".children[1].children[0]");
+ QVERIFY(groupNotationChild1);
+ QCOMPARE(groupNotationChild1.internalKind(), DomType::QmlObject);
+ QCOMPARE(groupNotationChild1.field(Fields::name).value().toString(), u"myText");
+ auto myTextScope = groupNotationChild1.semanticScope();
+ QVERIFY(myTextScope);
+ QCOMPARE(myTextScope->scopeType(), QQmlJSScope::ScopeType::GroupedPropertyScope);
+ QVERIFY(myTextScope->hasProperty("font"));
+
+ DomItem groupNotationChild2 = groupNotationChild1.path(u".children[0]");
+ QCOMPARE(groupNotationChild2.internalKind(), DomType::QmlObject);
+ QCOMPARE(groupNotationChild2.field(Fields::name).value().toString(), u"font");
+
+ auto fontScope = groupNotationChild2.semanticScope();
+ QVERIFY(fontScope);
+ QCOMPARE(fontScope->scopeType(), QQmlJSScope::ScopeType::GroupedPropertyScope);
+ QVERIFY(fontScope->hasProperty("pixelSize"));
+
+ DomItem pixelSize = groupNotationChild2.path(u".bindings[\"pixelSize\"][0].bindingIdentifiers");
+ QCOMPARE(pixelSize.internalKind(), DomType::ScriptIdentifierExpression);
+ QCOMPARE(pixelSize.field(Fields::identifier).value().toString(), u"pixelSize");
+
+ DomItem attached = rootQmlObject.path(u".bindings[\"Keys.onPressed\"][0].bindingIdentifiers");
+ QVERIFY(attached);
+ QCOMPARE(attached.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(attached.field(Fields::left).field(Fields::identifier).value().toString(), u"Keys");
+ QCOMPARE(attached.field(Fields::right).field(Fields::identifier).value().toString(), u"onPressed");
+ }
+
+ void enumDeclarations()
+ {
+ using namespace Qt::StringLiterals;
+ QString testFile = baseDir + u"/enumDeclarations.qml"_s;
+ DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ QVERIFY(fileObject);
+ DomItem enums = fileObject.path(u".components[\"\"][0].enumerations");
+ QVERIFY(enums);
+
+ DomItem catsEnum = enums.key("Cats").index(0);
+ QVERIFY(catsEnum);
+ QCOMPARE(catsEnum.internalKind(), DomType::EnumDecl);
+ QCOMPARE(catsEnum.name(), u"Cats");
+
+ auto values = catsEnum.field(Fields::values);
+ QCOMPARE(values.length(), 3);
+ QCOMPARE(values.index(0).internalKind(), DomType::EnumItem);
+ QCOMPARE(values.index(0).name(), u"Patron");
+ QCOMPARE(values.index(0).field(Fields::value).value().toInteger(), 0);
+ QCOMPARE(values.index(1).internalKind(), DomType::EnumItem);
+ QCOMPARE(values.index(1).name(), u"Mafya");
+ QCOMPARE(values.index(1).field(Fields::value).value().toInteger(), 1);
+ QCOMPARE(values.index(2).internalKind(), DomType::EnumItem);
+ QCOMPARE(values.index(2).name(), u"Kivrik");
+ QCOMPARE(values.index(2).field(Fields::value).value().toInteger(), -1);
+ }
+
+ void tryStatements()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/tryStatements.qml"_s;
+ const DomItem root = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ QVERIFY(root);
+ const DomItem statements = root.path(u".methods[\"f\"][0].body.scriptElement.statements");
+ QCOMPARE(statements.indexes(), 3);
+
+ // test the try blocks
+ for (int i = 0; i < 3; ++i) {
+ const DomItem statement = statements.index(i).field(Fields::block);
+ QVERIFY(statement);
+ QCOMPARE(statement.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(statement.field(Fields::statements)
+ .index(0)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"insideTry"_s);
+ }
+
+ // test the catch blocks
+ for (int i = 0; i < 3; ++i) {
+ const DomItem statement = statements.index(i).field(Fields::catchBlock);
+ if (i == 2) {
+ QVERIFY(!statement); // no catch in last statement
+ continue;
+ }
+
+ QVERIFY(statement);
+ QCOMPARE(statement.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(statement.field(Fields::statements)
+ .index(0)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"insideCatch"_s);
+
+ const DomItem expression = statements.index(i).field(Fields::catchParameter);
+ QVERIFY(expression);
+ QCOMPARE(expression.field(Fields::identifier)
+ .value()
+ .toString(),
+ u"catchExpression"_s);
+ }
+
+ // test the finally blocks
+ for (int i = 0; i < 3; ++i) {
+ const DomItem statement = statements.index(i).field(Fields::finallyBlock);
+ if (i == 1) {
+ QVERIFY(!statement); // no finally in last statement
+ continue;
+ }
+
+ QVERIFY(statement);
+ QCOMPARE(statement.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(statement.field(Fields::statements)
+ .index(0)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"insideFinally"_s);
+ }
+ }
+
+ void plainJSDOM_data()
+ {
+ QTest::addColumn<QString>("filename");
+ QTest::addColumn<QString>("content");
+
+ QTest::newRow("simplestJSStatement")
+ << "simplestJSStatement.js" << QString(u"let v=1;\n"_s);
+ QTest::newRow("import")
+ << "import.js"
+ << QString(u".import \"main.js\" as Main\nconsole.log(Main.a);\n"_s);
+ QTest::newRow("simplestJSmodule")
+ << "simplestJSmodule.mjs" << QString(u"export function entry() {}\n"_s);
+ }
+
+ // Verifies that DOM can load .js and .mjs files and
+ // parse / store the content inside the ScriptExpression
+ void plainJSDOM()
+ {
+ using namespace Qt::StringLiterals;
+ QFETCH(QString, filename);
+ QFETCH(QString, content);
+
+ QString testFile = baseDir + "/" + filename;
+ auto dom = parse(testFile, qmltypeDirs);
+ QVERIFY(dom);
+ QCOMPARE(dom.internalKind(), DomType::JsFile);
+ auto filePtr = dom.fileObject().ownerAs<JsFile>();
+ QVERIFY(filePtr && filePtr->isValid());
+ auto exprAsString = dom.field(Fields::expression)
+ .field(Fields::code)
+ .value()
+ .toString();
+ QVERIFY(!exprAsString.isEmpty());
+ exprAsString.replace("\r\n", "\n");
+ QCOMPARE(exprAsString, content);
+ }
+
+private:
+ struct DomItemWithLocation
+ {
+ DomItem item;
+ FileLocations::Tree tree;
+ };
+private slots:
+
+ void fileLocations_data()
+ {
+ QTest::addColumn<QString>("fileName");
+ QDir dir(baseDir);
+ for (const QString &file : dir.entryList(QDir::Files, QDir::Name)) {
+ if (!file.endsWith(".qml"))
+ continue;
+ QTest::addRow("%s", file.toStdString().c_str()) << baseDir + QDir::separator() + file;
+ }
+ }
+
+ /*!
+ \internal
+ Check if finalizeScriptExpression() was called with the correct FileLocations::Tree
+ argument when this test fails.
+ */
+ void fileLocations()
+ {
+ QFETCH(QString, fileName);
+
+ DomItem rootQmlObject = rootQmlObjectFromFile(fileName, qmltypeDirs);
+ std::deque<DomItemWithLocation> queue;
+
+ DomItem fileDomItem = rootQmlObject.containingFile();
+ std::shared_ptr<QmlFile> file = fileDomItem.ownerAs<QmlFile>();
+ QVERIFY(file);
+
+ DomItemWithLocation root{ fileDomItem, file->fileLocationsTree() };
+ queue.push_back(root);
+
+ while (!queue.empty()) {
+ DomItemWithLocation current = queue.front();
+ queue.pop_front();
+
+ auto subEls = current.tree->subItems();
+ for (auto it = subEls.begin(); it != subEls.end(); ++it) {
+ DomItem childItem = current.item.path(it.key());
+ FileLocations::Tree childTree =
+ std::static_pointer_cast<AttachedInfoT<FileLocations>>(it.value());
+ if (!childItem) {
+ const auto attachedInfo = FileLocations::findAttachedInfo(current.item);
+ const DomItem treeItem = current.item.path(attachedInfo.foundTreePath);
+ qDebug() << current.item.internalKindStr()
+ << "has incorrect FileLocations! Make sure that "
+ "finalizeScriptExpression is called with the right arguments. It "
+ "should print out some debugging information with the "
+ "qt.qmldom.astcreator.debug logging rule.";
+ qDebug() << "Current FileLocations has keys:"
+ << treeItem.field(Fields::subItems).keys()
+ << "but current Item of type" << current.item.internalKindStr()
+ << "has fields: " << current.item.fields()
+ << "and keys: " << current.item.keys()
+ << "and indexes: " << current.item.indexes();
+ }
+ QVERIFY(childItem.internalKind() != DomType::Empty);
+ queue.push_back({ childItem, childTree });
+ }
+ }
+ }
+
+private slots:
+ void finalizeScriptExpressions()
+ {
+ QString fileName = baseDir + u"/finalizeScriptExpressions.qml"_s;
+ DomItem rootQmlObject = rootQmlObjectFromFile(fileName, qmltypeDirs);
+
+ /*
+ Check if the path obtained by the filelocations is the same as the DomItem path. Both
+ need to be equal to find DomItem's from their location in the source code.
+ */
+ auto compareFileLocationsPathWithCanonicalPath = [](const DomItem &item) {
+ Path canonical = item.canonicalPath();
+ QVERIFY(canonical.length() > 0);
+ if (canonical.length() > 0)
+ QCOMPARE(canonical.toString(),
+ FileLocations::treeOf(item)->canonicalPathForTesting());
+ };
+
+ /*
+ for all places, where scriptelements are attached to Qml elements in the Dom, check if:
+ a) scriptelement is accessible from the DomItem (is it correclty attached?)
+ b) scriptelement has the correct path (is its pathFromOwner the path where it was
+ attached?)
+
+ For bindings to objects, arrays and scripts, check that the bindingIdentifiers are correctly
+ attached in the Dom.
+ */
+
+ {
+ DomItem binding = rootQmlObject.field(Fields::bindings).key("binding");
+ QCOMPARE(binding.indexes(), 1);
+
+ QCOMPARE(binding.index(0).field(Fields::value).internalKind(),
+ DomType::ScriptExpression);
+ QCOMPARE(binding.index(0)
+ .field(Fields::value)
+ .field(Fields::scriptElement)
+ .internalKind(),
+ DomType::ScriptLiteral);
+ // Fields::value is in the path of the owner, and therefore should not be in
+ // pathFromOwner!
+ DomItem scriptElement =
+ binding.index(0).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(scriptElement.pathFromOwner(), Path().field(Fields::scriptElement));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ }
+
+ {
+ DomItem bindingInPropertyDefinition =
+ rootQmlObject.field(Fields::bindings).key("bindingInPropertyDefinition");
+ QCOMPARE(bindingInPropertyDefinition.indexes(), 1);
+
+ QCOMPARE(bindingInPropertyDefinition.index(0).field(Fields::value).internalKind(),
+ DomType::ScriptExpression);
+ QCOMPARE(bindingInPropertyDefinition.index(0)
+ .field(Fields::value)
+ .field(Fields::scriptElement)
+ .internalKind(),
+ DomType::ScriptLiteral);
+ // Fields::value is in the path of the owner, and therefore should not be in
+ // pathFromOwner!
+ DomItem scriptElement = bindingInPropertyDefinition.index(0)
+ .field(Fields::value)
+ .field(Fields::scriptElement);
+ QCOMPARE(scriptElement.pathFromOwner(), Path().field(Fields::scriptElement));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ }
+ // check the parameters + returnType of the method
+ {
+ DomItem return42 = rootQmlObject.field(Fields::methods).key("return42");
+ QCOMPARE(return42.indexes(), 1);
+
+ DomItem typeAnnotation =
+ return42.index(0).field(Fields::returnType).field(Fields::scriptElement);
+ QCOMPARE(typeAnnotation.internalKind(), DomType::ScriptType);
+ compareFileLocationsPathWithCanonicalPath(typeAnnotation);
+
+ DomItem parameters = return42.index(0).field(Fields::parameters);
+ QCOMPARE(parameters.indexes(), 3);
+ for (int i = 0; i < 3; ++i) {
+ QCOMPARE(parameters.index(i).field(Fields::defaultValue).internalKind(),
+ DomType::ScriptExpression);
+ DomItem scriptElement =
+ parameters.index(i).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(scriptElement.internalKind(), DomType::ScriptFormalParameter);
+ QCOMPARE(scriptElement.pathFromOwner(), Path().field(Fields::scriptElement));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ }
+ }
+ // check the body of the methods
+ QList<QString> methodNames = { "full", "return42" };
+ for (QString &methodName : methodNames) {
+ DomItem method = rootQmlObject.field(Fields::methods).key(methodName);
+ DomItem body = method.index(0).field(Fields::body);
+ QCOMPARE(body.internalKind(), DomType::ScriptExpression);
+ DomItem scriptElement = body.field(Fields::scriptElement);
+ QCOMPARE(scriptElement.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(scriptElement.pathFromOwner(), Path().field(Fields::scriptElement));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ }
+
+ {
+ DomItem binding = rootQmlObject.field(Fields::bindings).key("arrayBinding");
+ QCOMPARE(binding.indexes(), 1);
+ QCOMPARE(binding.index(0).field(Fields::value).internalKind(),
+ DomType::ScriptExpression);
+ QCOMPARE(binding.index(0)
+ .field(Fields::value)
+ .field(Fields::scriptElement)
+ .internalKind(),
+ DomType::ScriptArray);
+ // Fields::value is in the path of the owner, and therefore should not be in
+ // pathFromOwner!
+ DomItem scriptElement =
+ binding.index(0).field(Fields::value).field(Fields::scriptElement);
+ QCOMPARE(scriptElement.pathFromOwner(), Path().field(Fields::scriptElement));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ // also check that the left hand side of the binding is correctly attached to the Dom:
+ scriptElement = binding.index(0).field(Fields::bindingIdentifiers);
+ QCOMPARE(scriptElement.pathFromOwner(),
+ Path::fromString(u".components[\"\"][0].objects[0].bindings[\"arrayBinding\"]["
+ u"0].bindingIdentifiers"));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ }
+ {
+ DomItem binding = rootQmlObject.field(Fields::bindings).key("objectBinding");
+ QCOMPARE(binding.indexes(), 1);
+ QCOMPARE(binding.index(0).field(Fields::value).internalKind(), DomType::QmlObject);
+ // check that the left hand side of the binding is correctly attached to the Dom:
+ DomItem scriptElement = binding.index(0).field(Fields::bindingIdentifiers);
+ QCOMPARE(scriptElement.pathFromOwner(),
+ Path::fromString(u".components[\"\"][0].objects[0].bindings[\"objectBinding\"]["
+ u"0].bindingIdentifiers"));
+ compareFileLocationsPathWithCanonicalPath(scriptElement);
+ }
+ }
+
+ void goToFile()
+ {
+ using namespace Qt::StringLiterals;
+ const QString filePathA = baseDir + u"/nullStatements.qml"_s;
+ const QString filePathB = baseDir + u"/propertyBindings.qml"_s;
+ const QString canonicalFilePathB = QFileInfo(filePathB).canonicalFilePath();
+ QVERIFY(!canonicalFilePathB.isEmpty());
+
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+ auto envPtr = DomEnvironment::create(
+ qmltypeDirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies,
+ options);
+
+ DomItem fileA;
+ DomItem fileB;
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, filePathA),
+ [&fileA](Path, const DomItem &, const DomItem &newIt) {
+ fileA = newIt.fileObject();
+ });
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, filePathB),
+ [&fileB](Path, const DomItem &, const DomItem &newIt) {
+ fileB = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ QCOMPARE(fileA.goToFile(canonicalFilePathB), fileB);
+ }
+
+ void goUp()
+ {
+ using namespace Qt::StringLiterals;
+ const QString filePath = baseDir + u"/nullStatements.qml"_s;
+ const QString canonicalFilePathB = QFileInfo(filePath).canonicalFilePath();
+ QVERIFY(!canonicalFilePathB.isEmpty());
+
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+ auto envPtr = DomEnvironment::create(
+ qmltypeDirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies,
+ options);
+
+ DomItem fileA;
+ DomItem fileB;
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, filePath),
+ [&fileA](Path, const DomItem &, const DomItem &newIt) {
+ fileA = newIt.fileObject();
+ });
+
+ envPtr->loadPendingDependencies();
+
+ QCOMPARE(fileA.top().goUp(1), DomItem());
+ QCOMPARE(fileA.top().directParent(), DomItem());
+
+ DomItem component = fileA.field(Fields::components).key(QString()).index(0);
+
+ DomItem forStatement = component.field(Fields::objects)
+ .index(0)
+ .field(Fields::methods)
+ .key(u"testForNull"_s)
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(0)
+ .field(Fields::body);
+
+ DomItem forStatementBlock = forStatement.field(Fields::statements);
+
+ QCOMPARE(forStatementBlock.directParent(), forStatement);
+ QCOMPARE(forStatementBlock.goUp(1), forStatement);
+ QCOMPARE(forStatementBlock.goUp(11), component);
+
+ QCOMPARE(forStatement.component(GoTo::Strict), component);
+ }
+
+private:
+ static DomItem parse(const QString &path, const QStringList &qmltypeDirs)
+ {
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+
+ auto envPtr = DomEnvironment::create(
+ qmltypeDirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies,
+ options);
+
+ DomItem fileItem;
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, path),
+ [&fileItem](Path, const DomItem &, const DomItem &newIt) {
+ fileItem = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+ return fileItem;
+ }
+
+ static DomItem rootQmlObjectFromFile(const QString &path, const QStringList &qmltypeDirs)
+ {
+ auto dom = parse(path, qmltypeDirs);
+ return dom.rootQmlObject(GoTo::MostLikely);
+ }
+
+ void fieldMemberExpressionHelper(const DomItem &actual, const QStringList &expected)
+ {
+ Q_ASSERT(!expected.isEmpty());
+ auto currentString = expected.rbegin();
+ auto endString = expected.rend();
+ DomItem current = actual;
+
+ for (; currentString != endString; ++currentString, current = current.field(Fields::left)) {
+ QCOMPARE(current.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(current.field(Fields::operation).value().toInteger(),
+ ScriptElements::BinaryExpression::FieldMemberAccess);
+ QCOMPARE(current.field(Fields::right).internalKind(),
+ DomType::ScriptIdentifierExpression);
+ QCOMPARE(current.field(Fields::right).field(Fields::identifier).value().toString(),
+ *currentString);
+ }
+ }
+
+private slots:
+ void mapsKeyedByFileLocationRegion()
+ {
+ using namespace Qt::StringLiterals;
+ const QString filePath = baseDir + u"/fileLocationRegion.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(filePath, qmltypeDirs);
+ QVERIFY(rootQmlObject);
+
+ // test if preComments map works correctly with DomItem interface
+ const DomItem binding = rootQmlObject.field(Fields::bindings).key(u"helloWorld"_s).index(0);
+ const DomItem bindingRegionComments =
+ binding.field(Fields::comments).field(Fields::regionComments);
+ const DomItem preComments =
+ bindingRegionComments.key(fileLocationRegionName(FileLocationRegion::IdentifierRegion))
+ .field(Fields::preComments);
+
+ QCOMPARE(preComments.indexes(), 1);
+ QString rawPreComment = preComments.index(0).field(Fields::rawComment).value().toString();
+ QCOMPARE(preComments.index(0)
+ .field(Fields::rawComment)
+ .value()
+ .toString()
+ // replace weird newlines by \n
+ .replace("\r\n", "\n")
+ .replace("\r", "\n"),
+ u" // before helloWorld binding\n "_s);
+
+ // test if postComments map works correctly with DomItem interface
+ const DomItem postComments =
+ bindingRegionComments
+ .key(fileLocationRegionName(FileLocationRegion::MainRegion))
+ .field(Fields::postComments);
+ QCOMPARE(postComments.indexes(), 1);
+ QCOMPARE(postComments.index(0)
+ .field(Fields::rawComment)
+ .value()
+ .toString()
+ // replace the windows newlines by \n
+ .replace("\r\n", "\n")
+ .replace("\r", "\n"),
+ u" // after helloWorld binding\n"_s);
+
+ const auto fileLocations = FileLocations::findAttachedInfo(binding);
+ const DomItem bindingFileLocation =
+ rootQmlObject.path(fileLocations.foundTreePath).field(Fields::infoItem);
+
+ // test if FileLocation Tree map works correctly with DomItem interface
+ QCOMPARE(bindingFileLocation.field(Fields::fullRegion).value(),
+ bindingFileLocation.field(Fields::regions)
+ .key(fileLocationRegionName(FileLocationRegion::MainRegion))
+ .value());
+
+ QCOMPARE(bindingFileLocation.field(Fields::fullRegion).value(),
+ sourceLocationToQCborValue(fileLocations.foundTree->info().fullRegion));
+
+ QCOMPARE(bindingFileLocation.field(Fields::regions)
+ .key(fileLocationRegionName(FileLocationRegion::MainRegion))
+ .value(),
+ sourceLocationToQCborValue(fileLocations.foundTree->info().regions[MainRegion]));
+
+ QCOMPARE(bindingFileLocation.field(Fields::regions)
+ .key(fileLocationRegionName(FileLocationRegion::ColonTokenRegion))
+ .value(),
+ sourceLocationToQCborValue(fileLocations.foundTree->info().regions[ColonTokenRegion]));
+ }
+
+ // add qml files here that should not crash the dom construction
+ void crashes_data()
+ {
+ QTest::addColumn<QString>("filePath");
+
+ QTest::addRow("inactiveVisitorMarkerCrash")
+ << baseDir + u"/inactiveVisitorMarkerCrash.qml"_s;
+
+ QTest::addRow("templateStrings")
+ << baseDir + u"/crashes/templateStrings.qml"_s;
+
+ QTest::addRow("lambda")
+ << baseDir + u"/crashes/lambda.qml"_s;
+ }
+ void crashes()
+ {
+ QFETCH(QString, filePath);
+
+ const DomItem rootQmlObject = rootQmlObjectFromFile(filePath, qmltypeDirs);
+ QVERIFY(rootQmlObject);
+ }
+
+ void continueStatement()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/continueStatement.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ const DomItem block =
+ rootQmlObject.path(".methods[\"f\"][0].body.scriptElement.statements");
+
+ const DomItem firstContinue = block.index(0);
+ QCOMPARE(firstContinue.internalKind(), DomType::ScriptContinueStatement);
+ QCOMPARE(firstContinue.field(Fields::label).value().toString("UNEXISTING"),
+ u"helloWorld"_s);
+
+ const DomItem secondContinue = block.index(1);
+ QCOMPARE(secondContinue.internalKind(), DomType::ScriptContinueStatement);
+ QCOMPARE(secondContinue.field(Fields::label).internalKind(), DomType::Empty);
+ }
+
+ void breakStatement()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/breakStatement.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ const DomItem block =
+ rootQmlObject.path(".methods[\"f\"][0].body.scriptElement.statements");
+
+ const DomItem firstContinue = block.index(0);
+ QCOMPARE(firstContinue.internalKind(), DomType::ScriptBreakStatement);
+ QCOMPARE(firstContinue.field(Fields::label).value().toString("UNEXISTING"),
+ u"helloWorld"_s);
+
+ const DomItem secondContinue = block.index(1);
+ QCOMPARE(secondContinue.internalKind(), DomType::ScriptBreakStatement);
+ QCOMPARE(secondContinue.field(Fields::label).internalKind(), DomType::Empty);
+ }
+
+ void emptyMethodBody()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/emptyMethodBody.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ const DomItem block = rootQmlObject.path(".methods[\"f\"][0].body.scriptElement");
+
+ QCOMPARE(block.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(block.field(Fields::statements).indexes(), 0);
+ }
+
+ void commaExpression()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/commaExpression.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ const DomItem commaExpression = rootQmlObject.path(".methods[\"f\"][0].body.scriptElement.statements[0]");
+
+ QCOMPARE(commaExpression.internalKind(), DomType::ScriptBinaryExpression);
+ QCOMPARE(commaExpression.field(Fields::right)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"c"_s);
+ QCOMPARE(commaExpression.field(Fields::left)
+ .field(Fields::right)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"b"_s);
+ QCOMPARE(commaExpression.field(Fields::left)
+ .field(Fields::left)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"a"_s);
+ }
+
+ void conditionalExpression()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/conditionalExpression.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+ const DomItem commaExpression = rootQmlObject.path(".methods[\"f\"][0].body.scriptElement.statements[0]");
+
+ QCOMPARE(commaExpression.internalKind(), DomType::ScriptConditionalExpression);
+ QCOMPARE(commaExpression.field(Fields::condition)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"a"_s);
+ QCOMPARE(commaExpression.field(Fields::consequence)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"b"_s);
+ QCOMPARE(commaExpression.field(Fields::alternative)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"c"_s);
+ }
+
+ void unaryExpression_data()
+ {
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<DomType>("type");
+
+ const QString folder = baseDir + u"/unaryExpressions/"_s;
+
+ QTest::addRow("minus") << folder + u"unaryMinus.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("plus") << folder + u"unaryPlus.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("tilde") << folder + u"tilde.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("not") << folder + u"not.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("typeof") << folder + u"typeof.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("delete") << folder + u"delete.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("void") << folder + u"void.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("increment") << folder + u"increment.qml"_s << DomType::ScriptUnaryExpression;
+ QTest::addRow("decrement") << folder + u"decrement.qml"_s << DomType::ScriptUnaryExpression;
+
+ // post stuff
+ QTest::addRow("postIncrement")
+ << folder + u"postIncrement.qml"_s << DomType::ScriptPostExpression;
+ QTest::addRow("postDecrement")
+ << folder + u"postDecrement.qml"_s << DomType::ScriptPostExpression;
+ }
+
+ void unaryExpression()
+ {
+ using namespace Qt::StringLiterals;
+ QFETCH(QString, fileName);
+ QFETCH(DomType, type);
+ const DomItem rootQmlObject = rootQmlObjectFromFile(fileName, qmltypeDirs);
+ const DomItem firstStatement =
+ rootQmlObject.path(".methods[\"f\"][0].body.scriptElement.statements[0]");
+
+ QCOMPARE(firstStatement.internalKind(), type);
+ QCOMPARE(firstStatement.field(Fields::expression)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"a"_s);
+ }
+
+ void objectBindings()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/objectBindings.qml"_s;
+ const DomItem rootQmlObject = rootQmlObjectFromFile(testFile, qmltypeDirs);
+
+ const DomItem xBinding = rootQmlObject.path(".bindings[\"x\"][0].value");
+ QCOMPARE(xBinding.field(Fields::name).value().toString(), u"root.QQ.Drag");
+ QCOMPARE(xBinding.field(Fields::nameIdentifiers).internalKind(),
+ DomType::ScriptType);
+ QCOMPARE(xBinding.field(Fields::nameIdentifiers).field(Fields::typeName).internalKind(),
+ DomType::ScriptBinaryExpression);
+ QCOMPARE(xBinding.field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .field(Fields::operation)
+ .value()
+ .toInteger(-1),
+ ScriptElements::BinaryExpression::FieldMemberAccess);
+
+ QCOMPARE(xBinding.field(Fields::nameIdentifiers).field(Fields::typeName).field(Fields::left).internalKind(),
+ DomType::ScriptBinaryExpression);
+ QCOMPARE(xBinding.field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .field(Fields::left)
+ .field(Fields::right)
+ .value()
+ .toString(),
+ u"QQ");
+ QCOMPARE(xBinding.field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .field(Fields::left)
+ .field(Fields::left)
+ .value()
+ .toString(),
+ u"root");
+
+ const DomItem item = rootQmlObject.path(".children[0]");
+ QCOMPARE(item.field(Fields::nameIdentifiers).field(Fields::typeName).value().toString(),
+ u"Item");
+
+ const DomItem qqItem = rootQmlObject.path(".children[1]");
+ QCOMPARE(qqItem.field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .field(Fields::operation)
+ .value()
+ .toInteger(-1),
+ ScriptElements::BinaryExpression::FieldMemberAccess);
+ QCOMPARE(qqItem.field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .field(Fields::right)
+ .value()
+ .toString(),
+ u"Item");
+ QCOMPARE(qqItem.field(Fields::nameIdentifiers)
+ .field(Fields::typeName)
+ .field(Fields::left)
+ .value()
+ .toString(),
+ u"QQ");
+ }
+
+ void scriptExpression()
+ {
+ // verifying support of ECMA script modules by ScriptExpression
+ const ScriptExpression esmExport("export function a(){}",
+ ScriptExpression::ExpressionType::ESMCode);
+ QVERIFY(esmExport.localErrors().empty());
+ }
+
+ void semanticAnalysis()
+ {
+
+ DomItem baseItem;
+ DomItem derivedItem;
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+
+ auto envPtr =
+ DomEnvironment::create(qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option{}, options);
+
+ envPtr->loadFile(
+ FileToLoad::fromFileSystem(envPtr, baseDir + u"/Base.qml"_s),
+ [&baseItem](Path, const DomItem &, const DomItem &newIt) {
+ baseItem = newIt.rootQmlObject(GoTo::MostLikely);
+ });
+
+ envPtr->loadFile(
+ FileToLoad::fromFileSystem(envPtr, baseDir + u"/Derived.qml"_s),
+ [&derivedItem](Path, const DomItem &, const DomItem &newIt) {
+ derivedItem = newIt.rootQmlObject(GoTo::MostLikely);
+ });
+ envPtr->loadPendingDependencies();
+
+ const auto baseScope = baseItem.semanticScope();
+ const auto derivedScope = derivedItem.semanticScope();
+
+ QCOMPARE_NE(baseScope, QQmlJSScope::ConstPtr{});
+ QCOMPARE(baseScope, derivedScope->baseType());
+ }
+
+ void propertyDefinitionScopes()
+ {
+ DomItem qmlObject;
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+
+ auto envPtr =
+ DomEnvironment::create(qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option{}, options);
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, baseDir + u"/propertyBindings.qml"_s),
+ [&qmlObject](Path, const DomItem &, const DomItem &newIt) {
+ qmlObject = newIt.rootQmlObject(GoTo::MostLikely);
+ });
+ envPtr->loadPendingDependencies();
+
+ {
+ const auto a = qmlObject.field(Fields::propertyDefs).key(u"a").index(0);
+ const auto scopeA = a.semanticScope();
+ QCOMPARE_NE(scopeA, QQmlJSScope::ConstPtr{});
+ QCOMPARE(scopeA->scopeType(), QQmlSA::ScopeType::QMLScope);
+ }
+
+ {
+ const auto b = qmlObject.field(Fields::propertyDefs).key(u"b").index(0);
+ const auto scopeB = b.semanticScope();
+ QCOMPARE_NE(scopeB, QQmlJSScope::ConstPtr{});
+ QCOMPARE(scopeB->scopeType(), QQmlSA::ScopeType::QMLScope);
+ }
+ }
+
+ // simulate qmlls loading the same file twice like in QTBUG-123591
+ void loadFileTwice()
+ {
+ DomItem qmlObject;
+ DomItem qmlObject2;
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+ options.setFlag(DomCreationOption::WithRecovery);
+
+ std::shared_ptr<DomEnvironment> envPtr = DomEnvironment::create(
+ qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option::SingleThreaded, options);
+
+ const QString fileName{ baseDir + u"/propertyBindings.qml"_s };
+ QFile file(fileName);
+ QVERIFY(file.open(QFile::ReadOnly));
+ const QString content = file.readAll();
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, baseDir + u"/propertyBindings.qml"_s),
+ [&qmlObject](Path, const DomItem &, const DomItem &newIt) {
+ qmlObject = newIt.rootQmlObject(GoTo::MostLikely);
+ });
+ envPtr->loadPendingDependencies();
+
+ // should not assert when loading the same file again
+ auto envPtrChild = envPtr->makeCopy(DomItem(envPtr));
+ envPtrChild->loadFile(
+ FileToLoad::fromMemory(envPtr, baseDir + u"/propertyBindings.qml"_s, content),
+ [&qmlObject2](Path, const DomItem &, const DomItem &newIt) {
+ qmlObject2 = newIt.rootQmlObject(GoTo::MostLikely);
+ });
+ envPtrChild->loadPendingDependencies();
+ }
+
+ void visitTreeFilter()
+ {
+ DomItem qmlObject;
+ DomCreationOptions options;
+ options.setFlag(DomCreationOption::WithScriptExpressions);
+ options.setFlag(DomCreationOption::WithSemanticAnalysis);
+
+ auto envPtr =
+ DomEnvironment::create(qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option{}, options);
+
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, baseDir + u"/visitTreeFilter.qml"_s),
+ [&qmlObject](Path, const DomItem &, const DomItem &newIt) {
+ qmlObject = newIt.rootQmlObject(GoTo::MostLikely);
+ });
+ envPtr->loadPendingDependencies();
+
+ FieldFilter filter({}, { { QString(), QString::fromUtf16(Fields::propertyDefs) } });
+
+ // check if propertyDefs is visited without the filter
+ bool success = false;
+ qmlObject.visitTree(
+ Path(), emptyChildrenVisitor, VisitOption::Recurse | VisitOption::VisitSelf,
+ [&success](const Path &p, const DomItem &, bool) {
+ const QString pathString = p.toString();
+ if (p && p.checkHeadName(Fields::propertyDefs)) {
+ success = true;
+ }
+ return true;
+ },
+ emptyChildrenVisitor);
+ QVERIFY(success);
+
+ // check that propertyDefs is not visited with the filter
+ success = true;
+ qmlObject.visitTree(
+ Path(), emptyChildrenVisitor, VisitOption::Recurse | VisitOption::VisitSelf,
+ [&success](const Path &p, const DomItem &, bool) {
+ if (p && p.checkHeadName(Fields::propertyDefs)) {
+ qWarning() << "Filter did not filter propertyDefs at path" << p;
+ success = false;
+ }
+ return true;
+ },
+ emptyChildrenVisitor, filter);
+ QVERIFY(success);
+ }
+
+ void fileLocationRegions_data()
+ {
+ QTest::addColumn<QString>("filePath");
+ QTest::addColumn<FileLocationRegion>("region");
+ QTest::addColumn<QSet<QQmlJS::SourceLocation>>("expectedLocs");
+
+ QTest::newRow("import") << baseDir + u"/fileLocationRegions/imports.qml"_s << ImportTokenRegion <<
+ QSet {
+ QQmlJS::SourceLocation{112, 6, 4, 1},
+ QQmlJS::SourceLocation{127, 6, 5, 1},
+ };
+ QTest::newRow("importUri") << baseDir + u"/fileLocationRegions/imports.qml"_s << ImportUriRegion <<
+ QSet {
+ QQmlJS::SourceLocation{119, 7, 4, 8},
+ QQmlJS::SourceLocation{152, 16, 6, 8},
+ QQmlJS::SourceLocation{186, 9, 7, 8}
+ };
+ QTest::newRow("asToken") << baseDir + u"/fileLocationRegions/imports.qml"_s << AsTokenRegion <<
+ QSet {
+ QQmlJS::SourceLocation{169, 2, 6, 25}
+ };
+ QTest::newRow("version") << baseDir + u"/fileLocationRegions/imports.qml"_s << VersionRegion <<
+ QSet {
+ QQmlJS::SourceLocation{140, 4, 5, 14}
+ };
+ QTest::newRow("namespace") << baseDir + u"/fileLocationRegions/imports.qml"_s << IdNameRegion <<
+ QSet {
+ QQmlJS::SourceLocation{172, 6, 6, 28}
+ };
+
+ QTest::newRow("function") << baseDir + u"/fileLocationRegions/functions.qml"_s
+ << FunctionKeywordRegion
+ << QSet{ QQmlJS::SourceLocation{ 139, 9, 7, 5 },
+ QQmlJS::SourceLocation{ 195, 9, 10, 9 } };
+
+ QTest::newRow("signal") << baseDir + u"/fileLocationRegions/functions.qml"_s
+ << SignalKeywordRegion
+ << QSet{ QQmlJS::SourceLocation{ 234, 6, 13, 5 },
+ QQmlJS::SourceLocation{ 254, 6, 14, 5 } };
+ QTest::newRow("return-type-identifier")
+ << baseDir + u"/fileLocationRegions/functions.qml"_s << TypeIdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 154, 3, 7, 20 },
+ QQmlJS::SourceLocation{ 216, 3, 10, 30 } };
+ QTest::newRow("function-parameter-type-identifier")
+ << baseDir + u"/fileLocationRegions/functions.qml"_s << TypeIdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 209, 3, 10, 23 } };
+ QTest::newRow("signal-parameter-type-identifier")
+ << baseDir + u"/fileLocationRegions/functions.qml"_s << TypeIdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 243, 3, 13, 14 },
+ QQmlJS::SourceLocation{ 267, 3, 14, 18 } };
+ QTest::newRow("signal-parameter-identifier")
+ << baseDir + u"/fileLocationRegions/functions.qml"_s << IdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 247, 1, 13, 18 },
+ QQmlJS::SourceLocation{ 264, 1, 14, 15 } };
+
+ QTest::newRow("pragma-keyword")
+ << baseDir + u"/fileLocationRegions/pragmas.qml"_s << PragmaKeywordRegion
+ << QSet{ QQmlJS::SourceLocation{ 112, 6, 4, 1 },
+ QQmlJS::SourceLocation{ 129, 6, 5, 1 },
+ QQmlJS::SourceLocation{ 161, 6, 6, 1 },
+ QQmlJS::SourceLocation{ 204, 6, 7, 1 }};
+ QTest::newRow("pragmaId")
+ << baseDir + u"/fileLocationRegions/pragmas.qml"_s << IdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 119, 9, 4, 8 },
+ QQmlJS::SourceLocation{ 136, 17, 5, 8 },
+ QQmlJS::SourceLocation{ 168, 25, 6, 8 },
+ QQmlJS::SourceLocation{ 211, 17, 7, 8 }};
+ QTest::newRow("pragmaValues")
+ << baseDir + u"/fileLocationRegions/pragmas.qml"_s << PragmaValuesRegion
+ << QSet{ QQmlJS::SourceLocation{ 155, 5, 5, 27 },
+ QQmlJS::SourceLocation{ 195, 8, 6, 35 },
+ QQmlJS::SourceLocation{ 230, 4, 7, 27 },
+ QQmlJS::SourceLocation{ 235, 11, 7, 32 }};
+
+ QTest::newRow("enum-keyword")
+ << baseDir + u"/fileLocationRegions/enums.qml"_s << EnumKeywordRegion
+ << QSet{ QQmlJS::SourceLocation{ 139, 4, 7, 5 }};
+ QTest::newRow("enum-id")
+ << baseDir + u"/fileLocationRegions/enums.qml"_s << IdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 144, 3, 7, 10 }};
+ QTest::newRow("enum-member")
+ << baseDir + u"/fileLocationRegions/enums.qml"_s << IdentifierRegion
+ << QSet{ QQmlJS::SourceLocation{ 158, 3, 8, 9 },
+ QQmlJS::SourceLocation{ 175, 3, 9, 9 },
+ QQmlJS::SourceLocation{ 188, 3, 10, 9 }};
+ QTest::newRow("enum-value")
+ << baseDir + u"/fileLocationRegions/enums.qml"_s << EnumValueRegion
+ << QSet{ QQmlJS::SourceLocation{ 164, 1, 8, 15 },
+ QQmlJS::SourceLocation{ 194, 2, 10, 15 }};
+ }
+
+ void fileLocationRegions()
+ {
+ QFETCH(QString, filePath);
+ QFETCH(FileLocationRegion, region);
+ QFETCH(QSet<QQmlJS::SourceLocation>, expectedLocs);
+ auto envPtr = DomEnvironment::create(
+ QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+
+ QFile f(filePath);
+ QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text));
+ QString code = f.readAll();
+ DomItem file;
+ envPtr->loadFile(FileToLoad::fromMemory(envPtr, filePath, code),
+ [&file](Path, const DomItem &, const DomItem &newIt) {
+ file = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
+
+ const auto tree = FileLocations::treeOf(file);
+ using AttachedInfo = AttachedInfoT<FileLocations>;
+ QSet<QQmlJS::SourceLocation> locs;
+ auto visitor = [&](const Path &currentPath, const AttachedInfo::Ptr &attachedInfo){
+ Q_UNUSED(currentPath);
+ const auto regions = attachedInfo->info().regions;
+ if (regions.contains(region)) {
+ locs << regions.value(region);
+ }
+ return true;
+ };
+ AttachedInfo::visitTree(tree, visitor, Path());
+ [&] {
+ QVERIFY(locs.contains(expectedLocs));
+ }();
+
+ if (QTest::currentTestFailed()) {
+ qDebug() << "Got:\n";
+ for (auto &x : locs) {
+ qDebug() << "Offset: " << x.offset
+ << ", Length:" << x.length
+ << ", Startline: " << x.startLine
+ << ", StartColumn: " << x.startColumn;
+ }
+ qDebug() << "But expected: \n";
+ for (auto &x : expectedLocs) {
+ qDebug() << "Offset: " << x.offset
+ << ", Length:" << x.length
+ << ", Startline: " << x.startLine
+ << ", StartColumn: " << x.startColumn;
+ }
+ }
+ }
+
+ void doNotCrashAtAstComments()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/astComments.qml"_s;
+ const DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ const DomItem astComments = fileObject.path(".astComments");
+
+ // Visiting astComment element shouldn't fail
+ QSet<QStringView> comments;
+ astComments.visitTree(
+ Path(),
+ [&comments](const Path &, const DomItem &item, bool) {
+ if (item.internalKind() == DomType::Comment) {
+ auto comment = item.as<Comment>();
+ comments << comment->rawComment();
+ }
+ return true;
+ }
+ );
+
+ QVERIFY(comments.contains(u"/*Ast Comment*/ "_s));
+ }
+
+private:
+ QString baseDir;
+ QStringList qmltypeDirs;
+ std::shared_ptr<DomUniverse> universePtr;
+ std::shared_ptr<DomEnvironment> envPtr;
+ DomItem env;
+ std::shared_ptr<MockOwner> testOwnerPtr;
+ DomItem tOwner;
+};
+
+} // namespace Dom
+} // namespace QQmlJS
+QT_END_NAMESPACE
+
+#endif
diff --git a/tests/auto/qmldom/errormessage/CMakeLists.txt b/tests/auto/qmldom/errormessage/CMakeLists.txt
index 4a8f5043b9..7c82876827 100644
--- a/tests/auto/qmldom/errormessage/CMakeLists.txt
+++ b/tests/auto/qmldom/errormessage/CMakeLists.txt
@@ -1,15 +1,23 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from errormessage.pro.
#####################################################################
## tst_qmldomerrormessage Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmldomerrormessage LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmldomerrormessage
SOURCES
- tst_qmldomerrormessage.cpp
+ tst_qmldomerrormessage.cpp tst_qmldomerrormessage.h
DEFINES
QT_DEPRECATED_WARNINGS
- PUBLIC_LIBRARIES
- Qt::QmlDevToolsPrivate
+ LIBRARIES
Qt::QmlDomPrivate
)
diff --git a/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp
index e36278771d..2c2007c119 100644
--- a/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp
+++ b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp
@@ -1,40 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_qmldomerrormessage.h"
#include <QtQmlDom/private/qqmldomerrormessage_p.h>
#include <QtTest/QtTest>
@@ -62,35 +29,31 @@ void registerMyError() {
static auto myError1 = ErrorMessage::msg("my.company.error1", myErrors().warning(u"Error number 1"));
static auto myError2 = ErrorMessage::msg("my.company.error2", myErrors().error(u"Error number 2 on %1"));
-class TestErrorMessage: public QObject
+void TestErrorMessage::testError()
{
- Q_OBJECT
-private slots:
- void testError()
- {
- registerMyError();
- auto err0 = ErrorMessage::load(myError0);
- QCOMPARE(err0.errorId, QLatin1String(myError0));
- QCOMPARE(err0.message, dumperToString(u"Error number 0"));
- QCOMPARE(err0.level, ErrorLevel::Warning);
- auto err1 = ErrorMessage::load(QLatin1String("my.company.error1"));
- QCOMPARE(err1.errorId, myError1);
- QCOMPARE(err1.message, dumperToString(u"Error number 1"));
- QCOMPARE(err1.level, ErrorLevel::Warning);
- auto err1bis = ErrorMessage::load("my.company.error1");
- QCOMPARE(err1bis.errorId, myError1);
- QCOMPARE(err1bis.message, dumperToString(u"Error number 1"));
- QCOMPARE(err1bis.level, ErrorLevel::Warning);
- auto err2 = ErrorMessage::load(myError2, QLatin1String("extra info"));
- QCOMPARE(err2.errorId, myError2);
- QCOMPARE(err2.message, dumperToString(u"Error number 2 on extra info"));
- QCOMPARE(err2.level, ErrorLevel::Error);
- }
-};
+ registerMyError();
+ auto err0 = ErrorMessage::load(myError0);
+ QCOMPARE(err0.errorId, QLatin1String(myError0));
+ QCOMPARE(err0.message, dumperToString(u"Error number 0"));
+ QCOMPARE(err0.level, ErrorLevel::Warning);
+ auto err1 = ErrorMessage::load(QLatin1String("my.company.error1"));
+ QCOMPARE(err1.errorId, myError1);
+ QCOMPARE(err1.message, dumperToString(u"Error number 1"));
+ QCOMPARE(err1.level, ErrorLevel::Warning);
+ auto err1bis = ErrorMessage::load("my.company.error1");
+ QCOMPARE(err1bis.errorId, myError1);
+ QCOMPARE(err1bis.message, dumperToString(u"Error number 1"));
+ QCOMPARE(err1bis.level, ErrorLevel::Warning);
+ auto err2 = ErrorMessage::load(myError2, QLatin1String("extra info"));
+ QCOMPARE(err2.errorId, myError2);
+ QCOMPARE(err2.message, dumperToString(u"Error number 2 on extra info"));
+ QCOMPARE(err2.level, ErrorLevel::Error);
+}
}
}
QT_END_NAMESPACE
+#ifndef NO_QTEST_MAIN
QTEST_MAIN(QQmlJS::Dom::TestErrorMessage)
-#include "tst_qmldomerrormessage.moc"
+#endif
diff --git a/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h
new file mode 100644
index 0000000000..31628ef52d
--- /dev/null
+++ b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtQmlDom/qqmldom_global.h>
+#include <QtTest/QtTest>
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+
+class QMLDOM_EXPORT TestErrorMessage: public QObject
+{
+ Q_OBJECT
+private slots:
+ void testError();
+};
+
+} // Dom
+} // QQmlJS
+QT_END_NAMESPACE
diff --git a/tests/auto/qmldom/merging/CMakeLists.txt b/tests/auto/qmldom/merging/CMakeLists.txt
new file mode 100644
index 0000000000..a33df96216
--- /dev/null
+++ b/tests/auto/qmldom/merging/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# Generated from domitem.pro.
+
+#####################################################################
+## tst_qmldomitem Binary:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_dommerging LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/..
+ domdata/dommerging)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_dommerging
+ SOURCES
+ tst_dommerging.cpp tst_dommerging.h
+ DEFINES
+ QT_DEPRECATED_WARNINGS
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/../domdata"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::Test
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_qmldomitem CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/domdata"
+)
diff --git a/tests/auto/qmldom/merging/tst_dommerging.cpp b/tests/auto/qmldom/merging/tst_dommerging.cpp
new file mode 100644
index 0000000000..1fa994b1a3
--- /dev/null
+++ b/tests/auto/qmldom/merging/tst_dommerging.cpp
@@ -0,0 +1,5 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_dommerging.h"
+QTEST_MAIN(QQmlJS::Dom::TestDomMerging)
diff --git a/tests/auto/qmldom/merging/tst_dommerging.h b/tests/auto/qmldom/merging/tst_dommerging.h
new file mode 100644
index 0000000000..59937b279e
--- /dev/null
+++ b/tests/auto/qmldom/merging/tst_dommerging.h
@@ -0,0 +1,102 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_DOMMERGING_H
+#define TST_DOMMERGING_H
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtQmlDom/private/qqmldomastdumper_p.h>
+
+#include <QtTest/QtTest>
+#include <QCborValue>
+#include <QDebug>
+#include <QLibraryInfo>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+
+class QMLDOM_EXPORT TestDomMerging : public QObject
+{
+ Q_OBJECT
+public:
+ static ErrorGroups myErrors()
+ {
+ static ErrorGroups res { { NewErrorGroup("tests"), NewErrorGroup("domitem") } };
+ return res;
+ }
+
+private slots:
+ void initTestCase()
+ {
+ QString baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/dommerging");
+ QStringList qmltypeDirs =
+ QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
+
+ auto envPtr = std::shared_ptr<QQmlJS::Dom::DomEnvironment>(new QQmlJS::Dom::DomEnvironment(
+ qmltypeDirs,
+ DomEnvironment::Option::SingleThreaded | DomEnvironment::Option::NoDependencies));
+ QString testFile1 = baseDir + QLatin1String("/test1.qml");
+
+ envPtr->loadFile(
+ FileToLoad::fromFileSystem(envPtr, testFile1),
+ [this](Path, const DomItem &, const DomItem &newIt) { this->tFile = newIt; });
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, baseDir), {});
+ envPtr->loadPendingDependencies();
+
+ QVERIFY(tFile);
+ tFile = tFile.field(Fields::currentItem);
+ QVERIFY(tFile);
+ }
+
+ void testReformat()
+ {
+ DomItem comp1 = tFile.field(Fields::components).key(QString()).index(0);
+ QVERIFY(comp1);
+ DomItem obj1 = comp1.field(Fields::objects).index(0);
+ QVERIFY(obj1);
+ DomItem width = obj1.field(Fields::bindings).key(QLatin1String("width")).index(0);
+ DomItem w = obj1.bindings().key(QLatin1String("width"));
+ QVERIFY(w.length() > 0);
+ QCOMPARE(w.length(), 1);
+ DomItem exp = width.field(Fields::value);
+ if (std::shared_ptr<ScriptExpression> expPtr = exp.ownerAs<ScriptExpression>()) {
+ QCOMPARE(expPtr->code(), u"{ height *3/4 }");
+ }
+ MutableDomItem myobj(obj1);
+ PropertyDefinition pDef;
+ pDef.name = QLatin1String("foo");
+ pDef.typeName = QLatin1String("int");
+ MutableDomItem propertyDef = myobj.addPropertyDef(pDef, AddOption::Overwrite);
+ QVERIFY(propertyDef);
+ MutableDomItem binding = myobj.addBinding(
+ Binding(QLatin1String("foo"),
+ std::shared_ptr<ScriptExpression>(new ScriptExpression(
+ QLatin1String("42"),
+ ScriptExpression::ExpressionType::BindingExpression))),
+ AddOption::Overwrite);
+ QVERIFY(binding);
+ QCOMPARE(binding.item().field(Fields::value).field(Fields::errors).length(), 0);
+ std::shared_ptr<ScriptExpression> expr =
+ binding.item().field(Fields::value).ownerAs<ScriptExpression>();
+ QVERIFY(expr && expr->ast());
+ QCOMPARE(expr->ast()->kind, AST::Node::Kind_NumericLiteral);
+ MutableDomItem pInfo = myobj.field(Fields::propertyInfos).key(QLatin1String("foo"));
+ // dumperToQDebug([pInfo](Sink s){ pInfo.dump(s); });
+ QCOMPARE(propertyDef.item(), pInfo.field(Fields::propertyDefs).index(0).item());
+ QCOMPARE(binding, pInfo.field(Fields::bindings).index(0));
+ }
+
+private:
+ std::shared_ptr<DomEnvironment> envPtr;
+ DomItem env;
+ DomItem tFile;
+};
+
+} // namespace Dom
+} // namespace QQmlJS
+QT_END_NAMESPACE
+
+#endif
diff --git a/tests/auto/qmldom/path/CMakeLists.txt b/tests/auto/qmldom/path/CMakeLists.txt
index 4c4109dd02..bf3e5d20f5 100644
--- a/tests/auto/qmldom/path/CMakeLists.txt
+++ b/tests/auto/qmldom/path/CMakeLists.txt
@@ -1,15 +1,23 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from path.pro.
#####################################################################
## tst_qmldompath Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmldompath LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmldompath
SOURCES
- tst_qmldompath.cpp
+ tst_qmldompath.cpp tst_qmldompath.h
DEFINES
QT_DEPRECATED_WARNINGS
- PUBLIC_LIBRARIES
- Qt::QmlDevToolsPrivate
+ LIBRARIES
Qt::QmlDomPrivate
)
diff --git a/tests/auto/qmldom/path/tst_qmldompath.cpp b/tests/auto/qmldom/path/tst_qmldompath.cpp
index b11abe6283..54a634e476 100644
--- a/tests/auto/qmldom/path/tst_qmldompath.cpp
+++ b/tests/auto/qmldom/path/tst_qmldompath.cpp
@@ -1,229 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**/
-#include <QtQmlDom/private/qqmldompath_p.h>
-#include <QtQmlDom/private/qqmldomitem_p.h>
-
-#include <QtTest/QtTest>
-
-QT_BEGIN_NAMESPACE
-namespace QQmlJS {
-namespace Dom {
-namespace PathEls {
-
-class TestPaths: public QObject {
- Q_OBJECT
-public:
- void testPathInternals(Path p1)
- {
- QCOMPARE(p1.component(0).kind(), Kind::Root);
- QCOMPARE(p1.component(1).kind(), Kind::Current);
-
- Path p11 = Path::field(u"test");
- QString s = QLatin1String("test");
- Path p2 = Path::field(s);
- Path p3 = Path::field(QLatin1String("test"));
- QCOMPARE(p11, p2);
- QCOMPARE(p11, p3);
- QVERIFY(p11.m_data->strData.isEmpty());
- QCOMPARE(p2.m_data->strData.length(), 1);
- QCOMPARE(p2.m_data->strData.first(), s);
- QCOMPARE(p3.m_data->strData.length(), 1);
- QCOMPARE(p3.m_data->strData.first(), s);
- }
-
-private slots:
- void pathComponentTestInternalAlloc() {
- PathComponent c;
- QCOMPARE(c.kind(), Kind::Empty);
- PathComponent c1{Current()};
- QCOMPARE(c1.kind(), Kind::Current);
- QVERIFY(c!=c1);
- QVERIFY(c<c1);
- QVERIFY(c1>c);
- PathComponent c1_1{Current(PathCurrent::Ids)};
- QCOMPARE(c1_1.kind(), Kind::Current);
- QVERIFY(c1 != c1_1);
- QVERIFY(c < c1_1);
- QCOMPARE(c1_1.name(), QLatin1String("@ids"));
- PathComponent c1_2{Current(u"ids")};
- QCOMPARE(c1_1, c1_2);
- PathComponent c2 = c1;
- QCOMPARE(c2.kind(), Kind::Current);
- QCOMPARE(c2, c1);
- PathComponent c3;
- QCOMPARE(c, c3);
- QCOMPARE(c3.kind(), Kind::Empty);
- c3 = c1;
- QCOMPARE(c3.kind(), Kind::Current);
- QCOMPARE(c3, c1);
- PathComponent c4{Field(u"bla")};
- QCOMPARE(c4.kind(), Kind::Field);
- QCOMPARE(c4.name(), QLatin1String("bla"));
- auto c5=PathComponent(Index(42));
- QCOMPARE(c5.kind(), Kind::Index);
- QCOMPARE(c5.index(), 42);
- auto c6=PathComponent(Key(u"bla"));
- QCOMPARE(c6.kind(), Kind::Key);
- QCOMPARE(c6.name(), QLatin1String("bla"));
- auto c7=PathComponent(Key(u" ugly\n \t \\string\"'bla"));
- QCOMPARE(c7.kind(), Kind::Key);
- QCOMPARE(c7.name(), QLatin1String(" ugly\n \t \\string\"'bla"));
- auto c8=PathComponent(Root(u"pippo"));
- QCOMPARE(c8.kind(), Kind::Root);
- QCOMPARE(c8.name(), QLatin1String("$pippo"));
- auto c8_1=PathComponent(Root(PathRoot::Env));
- QCOMPARE(c8_1.kind(), Kind::Root);
- QCOMPARE(c8_1.name(), QLatin1String("$env"));
- auto c8_2=PathComponent(Root(u"env"));
- QCOMPARE(c8_1, c8_2);
- auto c9=PathComponent(Current(u"ippo"));
- QCOMPARE(c9.kind(), Kind::Current);
- QCOMPARE(c9.name(), QLatin1String("@ippo"));
- auto c10=PathComponent(Any());
- QCOMPARE(c10.kind(), Kind::Any);
- QVERIFY(c9!=c10);
- auto c11=PathComponent(Filter([](DomItem){ return true; }));
- auto c12=c11;
- auto c13=PathComponent(Filter([](DomItem){ return false; }));
- auto c14=PathComponent(Filter([](DomItem){ return false; }, u"skipAll"));
- auto c15=PathComponent(Filter([](DomItem){ return true; }, u"skipAll"));
- QCOMPARE(c11.kind(), Kind::Filter);
- QCOMPARE(c11, c11);
- QVERIFY(c11 != c12); // native code assumed to be non comparable and different even if they are the same
- QVERIFY(c11 != c13);
- QVERIFY(c13 != c14);
- QVERIFY(c11 != c14);
- QCOMPARE(c14, c14);
- QCOMPARE(c14, c15); // same description (without < at the beginning) assumes same function even if different
- }
-
- void testPaths() {
- Path p;
- QCOMPARE(p.length(), 0);
- QCOMPARE(p.length(), 0);
- Path p0 = Path::root();
- QCOMPARE(p0[0].headKind(), Kind::Root);
- QCOMPARE(p0.length(), 1);
- Path p1 = p0.subCurrent();
- QCOMPARE(p1.length(), 2);
- testPathInternals(p1);
- QCOMPARE(p1[0].headKind(), Kind::Root);
- QCOMPARE(p1[1].headKind(), Kind::Current);
- auto p2 = p1.subField(u"aa");
- QCOMPARE(p2[2].headKind(), Kind::Field);
- auto p3 = p2.subIndex(4);
- QCOMPARE(p3[3].headKind(), Kind::Index);
- auto p4 = p3.subKey("bla");
- QCOMPARE(p4[4].headKind(), Kind::Key);
- auto p5 = p4.subAny();
- QCOMPARE(p5[5].headKind(), Kind::Any);
- auto p6 = p5.subEmpty();
- QCOMPARE(p6[6].headKind(), Kind::Empty);
- auto rString = u"$.@.aa[4][\"bla\"][*].";
- QCOMPARE(p6.toString(), rString);
- auto p7 = p6.subFilter([](DomItem){ return true; }, u"true");
- auto p7Str = p7.toString();
- QCOMPARE(p7Str, u"$.@.aa[4][\"bla\"][*].[?(true)]");
- auto p8 = p7.dropTail();
- QCOMPARE(p8.length(), 7);
- QCOMPARE(p8.toString(), rString);
- QCOMPARE(p8, p6);
- auto p9 = Path::fromString(rString);
- QCOMPARE(p9.length(), 7);
- auto p9Str = p9.toString();
- QCOMPARE(p9Str, rString);
- QCOMPARE(p9, p6);
- auto p10 = p6.dropFront();
- QCOMPARE(p10.length(), 6);
- auto p10Str = p10.toString();
- auto r2Str = u"@.aa[4][\"bla\"][*].";
- QCOMPARE(p10Str, r2Str);
- auto p11 = Path::fromString(r2Str);
- auto p11Str = p11.toString();
- QCOMPARE(p11Str, r2Str);
- QCOMPARE(p10, p11);
- auto p12 = p7.mid(1,6);
- auto p12Str = p12.toString();
- QCOMPARE(p12Str, r2Str);
- QCOMPARE(p10, p12);
- }
-
- void testPathSplit()
- {
- QList<Path> paths({Path(),
- Path::root(PathRoot::Env).subField(u"pippo").subKey(u"pluto").subIndex(4),
- Path::root(PathRoot::Env).subField(u"pippo").subKey(u"pluto"),
- Path::root(PathRoot::Env).subField(u"pippo"),
- Path::root(PathRoot::Env).subField(u"pippo").subField(u"pp"),
- Path::root(PathRoot::Env),
- Path::field(u"pippo").subIndex(4),
- Path::field(u"pippo").subKey(u"pluto").subIndex(4),
- Path::field(u"pippo").subKey(u"pluto"),
- Path::field(u"pippo"),
- Path::field(u"pippo").subField(u"pp"),
- Path::index(4),
- Path::key(u"zz")
- });
- foreach (Path p, paths) {
- Source s = p.split();
- QCOMPARE(p, s.pathToSource.subPath(s.pathFromSource));
- if (!s.pathFromSource)
- QVERIFY(!s.pathToSource);
- }
- QCOMPARE(paths.at(1).split().pathToSource, Path::root(PathRoot::Env));
- QCOMPARE(paths.at(2).split().pathToSource, Path::root(PathRoot::Env));
- QCOMPARE(paths.at(3).split().pathToSource, Path::root(PathRoot::Env));
- QCOMPARE(paths.at(4).split().pathToSource, Path::root(PathRoot::Env).subField(u"pippo"));
- QVERIFY(!paths.at(5).split().pathToSource);
- QVERIFY(!paths.at(6).split().pathToSource);
- QVERIFY(!paths.at(7).split().pathToSource);
- QVERIFY(!paths.at(8).split().pathToSource);
- QVERIFY(!paths.at(9).split().pathToSource);
- QCOMPARE(paths.at(10).split().pathToSource, Path::field(u"pippo"));
- QVERIFY(!paths.at(11).split().pathToSource);
- QVERIFY(!paths.at(12).split().pathToSource);
- }
-};
-
-} // namespace PathEls
-} // namespace Dom
-} // namespace QQmlJS
-QT_END_NAMESPACE
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include "tst_qmldompath.h"
QTEST_MAIN(QQmlJS::Dom::PathEls::TestPaths)
-#include "tst_qmldompath.moc"
diff --git a/tests/auto/qmldom/path/tst_qmldompath.h b/tests/auto/qmldom/path/tst_qmldompath.h
new file mode 100644
index 0000000000..f463b93164
--- /dev/null
+++ b/tests/auto/qmldom/path/tst_qmldompath.h
@@ -0,0 +1,204 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLDOMPATH_H
+#define TST_QMLDOMPATH_H
+#include <QtQmlDom/private/qqmldompath_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+
+#include <QtTest/QtTest>
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+namespace PathEls {
+
+class TestPaths: public QObject {
+ Q_OBJECT
+public:
+ void testPathInternals(const Path &p1)
+ {
+ QCOMPARE(p1.component(0).kind(), Kind::Root);
+ QCOMPARE(p1.component(1).kind(), Kind::Current);
+
+ Path p11 = Path::Field(u"test");
+ QString s = QLatin1String("test");
+ Path p2 = Path::Field(s);
+ Path p3 = Path::Field(QLatin1String("test"));
+ QCOMPARE(p11, p2);
+ QCOMPARE(p11, p3);
+ QVERIFY(p11.m_data->strData.isEmpty());
+ QCOMPARE(p2.m_data->strData.size(), 1);
+ QCOMPARE(p2.m_data->strData.first(), s);
+ QCOMPARE(p3.m_data->strData.size(), 1);
+ QCOMPARE(p3.m_data->strData.first(), s);
+ }
+
+private slots:
+ void pathComponentTestInternalAlloc() {
+ PathComponent c;
+ QCOMPARE(c.kind(), Kind::Empty);
+ PathComponent c1{Current()};
+ QCOMPARE(c1.kind(), Kind::Current);
+ QVERIFY(c!=c1);
+ QVERIFY(c<c1);
+ QVERIFY(c1>c);
+ PathComponent c1_1{Current(PathCurrent::Ids)};
+ QCOMPARE(c1_1.kind(), Kind::Current);
+ QVERIFY(c1 != c1_1);
+ QVERIFY(c < c1_1);
+ QCOMPARE(c1_1.name(), QLatin1String("@ids"));
+ PathComponent c1_2{Current(u"ids")};
+ QCOMPARE(c1_1, c1_2);
+ PathComponent c2 = c1;
+ QCOMPARE(c2.kind(), Kind::Current);
+ QCOMPARE(c2, c1);
+ PathComponent c3;
+ QCOMPARE(c, c3);
+ QCOMPARE(c3.kind(), Kind::Empty);
+ c3 = c1;
+ QCOMPARE(c3.kind(), Kind::Current);
+ QCOMPARE(c3, c1);
+ PathComponent c4{Field(u"bla")};
+ QCOMPARE(c4.kind(), Kind::Field);
+ QCOMPARE(c4.name(), QLatin1String("bla"));
+ auto c5=PathComponent(Index(42));
+ QCOMPARE(c5.kind(), Kind::Index);
+ QCOMPARE(c5.index(), 42);
+ auto c6 = PathComponent(Key(QStringLiteral(u"bla")));
+ QCOMPARE(c6.kind(), Kind::Key);
+ QCOMPARE(c6.name(), QLatin1String("bla"));
+ auto c7 = PathComponent(Key(QStringLiteral(u" ugly\n \t \\string\"'bla")));
+ QCOMPARE(c7.kind(), Kind::Key);
+ QCOMPARE(c7.name(), QLatin1String(" ugly\n \t \\string\"'bla"));
+ auto c8=PathComponent(Root(u"pippo"));
+ QCOMPARE(c8.kind(), Kind::Root);
+ QCOMPARE(c8.name(), QLatin1String("$pippo"));
+ auto c8_1=PathComponent(Root(PathRoot::Env));
+ QCOMPARE(c8_1.kind(), Kind::Root);
+ QCOMPARE(c8_1.name(), QLatin1String("$env"));
+ auto c8_2=PathComponent(Root(u"env"));
+ QCOMPARE(c8_1, c8_2);
+ auto c9=PathComponent(Current(u"ippo"));
+ QCOMPARE(c9.kind(), Kind::Current);
+ QCOMPARE(c9.name(), QLatin1String("@ippo"));
+ auto c10=PathComponent(Any());
+ QCOMPARE(c10.kind(), Kind::Any);
+ QVERIFY(c9!=c10);
+ auto c11=PathComponent(Filter([](const DomItem &){ return true; }));
+ auto c12=c11;
+ auto c13=PathComponent(Filter([](const DomItem &){ return false; }));
+ auto c14=PathComponent(Filter([](const DomItem &){ return false; }, u"skipAll"));
+ auto c15=PathComponent(Filter([](const DomItem &){ return true; }, u"skipAll"));
+ QCOMPARE(c11.kind(), Kind::Filter);
+ QCOMPARE(c11, c11);
+ QVERIFY(c11 != c12); // native code assumed to be non comparable and different even if they are the same
+ QVERIFY(c11 != c13);
+ QVERIFY(c13 != c14);
+ QVERIFY(c11 != c14);
+ QCOMPARE(c14, c14);
+ QCOMPARE(c14, c15); // same description (without < at the beginning) assumes same function even if different
+ }
+
+ void testPaths() {
+ Path p;
+ QCOMPARE(p.length(), 0);
+ QCOMPARE(p.length(), 0);
+ Path p0 = Path::Root();
+ QCOMPARE(p0[0].headKind(), Kind::Root);
+ QCOMPARE(p0.length(), 1);
+ Path p1 = p0.current();
+ QCOMPARE(p1.length(), 2);
+ testPathInternals(p1);
+ QCOMPARE(p1[0].headKind(), Kind::Root);
+ QCOMPARE(p1[1].headKind(), Kind::Current);
+ auto p2 = p1.field(u"aa");
+ QCOMPARE(p2[2].headKind(), Kind::Field);
+ auto p2b = p1.appendComponent(PathEls::Field(u"aa"));
+ QCOMPARE(p2b.length(), 3);
+ QCOMPARE(p2b[2].headKind(), Kind::Field);
+ QCOMPARE(p2b, p2);
+ auto p3a = p2.appendComponent(PathEls::Index(4));
+ QCOMPARE(p3a[3].headKind(), Kind::Index);
+ auto p3 = p2.index(4);
+ QCOMPARE(p3.length(), 4);
+ QCOMPARE(p3[3].headKind(), Kind::Index);
+ QCOMPARE(p3, p3a);
+ auto p4 = p3.key("bla");
+ QCOMPARE(p4[4].headKind(), Kind::Key);
+ auto p5 = p4.any();
+ QCOMPARE(p5[5].headKind(), Kind::Any);
+ auto p6 = p5.empty();
+ QCOMPARE(p6[6].headKind(), Kind::Empty);
+ auto rString = u"$.@.aa[4][\"bla\"][*].";
+ QCOMPARE(p6.toString(), rString);
+ auto p7 = p6.filter([](const DomItem &){ return true; }, u"true");
+ auto p7Str = p7.toString();
+ QCOMPARE(p7Str, u"$.@.aa[4][\"bla\"][*].[?(true)]");
+ auto p8 = p7.dropTail();
+ QCOMPARE(p8.length(), 7);
+ QCOMPARE(p8.toString(), rString);
+ QCOMPARE(p8, p6);
+ auto p9 = Path::fromString(rString);
+ QCOMPARE(p9.length(), 7);
+ auto p9Str = p9.toString();
+ QCOMPARE(p9Str, rString);
+ QCOMPARE(p9, p6);
+ auto p10 = p6.dropFront();
+ QCOMPARE(p10.length(), 6);
+ auto p10Str = p10.toString();
+ auto r2Str = u"@.aa[4][\"bla\"][*].";
+ QCOMPARE(p10Str, r2Str);
+ auto p11 = Path::fromString(r2Str);
+ auto p11Str = p11.toString();
+ QCOMPARE(p11Str, r2Str);
+ QCOMPARE(p10, p11);
+ auto p12 = p7.mid(1,6);
+ auto p12Str = p12.toString();
+ QCOMPARE(p12Str, r2Str);
+ QCOMPARE(p10, p12);
+ }
+
+ void testPathSplit()
+ {
+ const QList<Path> paths({Path(),
+ Path::Root(PathRoot::Env).field(u"pippo").key(u"pluto").index(4),
+ Path::Root(PathRoot::Env).field(u"pippo").key(u"pluto"),
+ Path::Root(PathRoot::Env).field(u"pippo"),
+ Path::Root(PathRoot::Env).field(u"pippo").field(u"pp"),
+ Path::Root(PathRoot::Env),
+ Path::Field(u"pippo").index(4),
+ Path::Field(u"pippo").key(u"pluto").index(4),
+ Path::Field(u"pippo").key(u"pluto"),
+ Path::Field(u"pippo"),
+ Path::Field(u"pippo").field(u"pp"),
+ Path::Index(4),
+ Path::Key(u"zz")
+ });
+ for (const Path &p : paths) {
+ Source s = p.split();
+ QCOMPARE(p, s.pathToSource.path(s.pathFromSource));
+ if (!s.pathFromSource)
+ QVERIFY(!s.pathToSource);
+ }
+ QCOMPARE(paths.at(1).split().pathToSource, Path::Root(PathRoot::Env));
+ QCOMPARE(paths.at(2).split().pathToSource, Path::Root(PathRoot::Env));
+ QCOMPARE(paths.at(3).split().pathToSource, Path::Root(PathRoot::Env));
+ QCOMPARE(paths.at(4).split().pathToSource, Path::Root(PathRoot::Env).field(u"pippo"));
+ QVERIFY(!paths.at(5).split().pathToSource);
+ QVERIFY(!paths.at(6).split().pathToSource);
+ QVERIFY(!paths.at(7).split().pathToSource);
+ QVERIFY(!paths.at(8).split().pathToSource);
+ QVERIFY(!paths.at(9).split().pathToSource);
+ QCOMPARE(paths.at(10).split().pathToSource, Path::Field(u"pippo"));
+ QVERIFY(!paths.at(11).split().pathToSource);
+ QVERIFY(!paths.at(12).split().pathToSource);
+ }
+};
+
+} // namespace PathEls
+} // namespace Dom
+} // namespace QQmlJS
+QT_END_NAMESPACE
+
+#endif
diff --git a/tests/auto/qmldom/reformatter/CMakeLists.txt b/tests/auto/qmldom/reformatter/CMakeLists.txt
new file mode 100644
index 0000000000..1b8cfb0d8a
--- /dev/null
+++ b/tests/auto/qmldom/reformatter/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_reformatter:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_reformatter LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/..
+ domdata/reformatter/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_reformatter
+ SOURCES
+ tst_reformatter.cpp tst_reformatter.h
+ DEFINES
+ QT_DEPRECATED_WARNINGS
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/../domdata"
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::Test
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_reformatter CONDITION ANDROID OR IOS
+ DEFINES
+ QT_REFORMATTERTEST_DATADIR=":/domdata"
+)
diff --git a/tests/auto/qmldom/reformatter/tst_reformatter.cpp b/tests/auto/qmldom/reformatter/tst_reformatter.cpp
new file mode 100644
index 0000000000..00d26bc55b
--- /dev/null
+++ b/tests/auto/qmldom/reformatter/tst_reformatter.cpp
@@ -0,0 +1,5 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "tst_reformatter.h"
+QTEST_MAIN(QQmlJS::Dom::TestReformatter)
diff --git a/tests/auto/qmldom/reformatter/tst_reformatter.h b/tests/auto/qmldom/reformatter/tst_reformatter.h
new file mode 100644
index 0000000000..31d80097c1
--- /dev/null
+++ b/tests/auto/qmldom/reformatter/tst_reformatter.h
@@ -0,0 +1,774 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLDOMCODEFORMATTER_H
+#define TST_QMLDOMCODEFORMATTER_H
+#include <QtQmlDom/private/qqmldomlinewriter_p.h>
+#include <QtQmlDom/private/qqmldomindentinglinewriter_p.h>
+#include <QtQmlDom/private/qqmldomoutwriter_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtQmlDom/private/qqmldomreformatter_p.h>
+
+#include <QtTest/QtTest>
+#include <QCborValue>
+#include <QDebug>
+#include <QLibraryInfo>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+
+class TestReformatter : public QObject
+{
+ Q_OBJECT
+public:
+private:
+ // TODO Move to a dedicated LineWriter factory / LineWriter API ?
+ enum class LineWriterType { Default, Indenting };
+ std::unique_ptr<LineWriter> getLineWriter(const SinkF &innerSink,
+ const LineWriterOptions &lwOptions)
+ {
+ return lwOptions.maxLineLength > 0
+ ? getLineWriter(LineWriterType::Indenting, innerSink, lwOptions)
+ : getLineWriter(LineWriterType::Default, innerSink, lwOptions);
+ }
+
+ std::unique_ptr<LineWriter> getLineWriter(LineWriterType type, const SinkF &innerSink,
+ const LineWriterOptions &lwOptions)
+ {
+ switch (type) {
+ case LineWriterType::Indenting:
+ return std::make_unique<IndentingLineWriter>(innerSink, QLatin1String("*testStream*"),
+ lwOptions);
+ default:
+ return std::make_unique<LineWriter>(innerSink, QLatin1String("*testStream*"),
+ lwOptions);
+ }
+ Q_UNREACHABLE_RETURN(nullptr);
+ }
+
+ // "Unix" LineWriter (with '\n' line endings) is used by default,
+ // under the assumption that line endings are properly tested in lineWriter() test.
+ static LineWriterOptions defaultLineWriterOptions()
+ {
+ LineWriterOptions opts;
+ opts.lineEndings = LineWriterOptions::LineEndings::Unix;
+ return opts;
+ }
+
+ QString formatJSCode(const QString &jsCode,
+ const LineWriterOptions &lwOptions = defaultLineWriterOptions())
+ {
+ return formatPlainJS(jsCode, ScriptExpression::ExpressionType::JSCode, lwOptions);
+ }
+
+ QString formatJSModuleCode(const QString &jsCode,
+ const LineWriterOptions &lwOptions = defaultLineWriterOptions())
+ {
+ return formatPlainJS(jsCode, ScriptExpression::ExpressionType::ESMCode, lwOptions);
+ }
+
+ QString formatPlainJS(const QString &jsCode, ScriptExpression::ExpressionType exprType,
+ const LineWriterOptions &lwOptions = defaultLineWriterOptions())
+ {
+ QString resultStr;
+ QTextStream res(&resultStr);
+ auto lwPtr = getLineWriter([&res](QStringView s) { res << s; }, lwOptions);
+ assert(lwPtr);
+ OutWriter ow(*lwPtr);
+
+ const ScriptExpression scriptItem(jsCode, exprType);
+ scriptItem.writeOut(DomItem(), ow);
+
+ lwPtr->flush(); // flush instead of eof to ignore line endings
+ res.flush();
+ return resultStr;
+ }
+
+private slots:
+ void reindent_data()
+ {
+ QTest::addColumn<QString>("inFile");
+ QTest::addColumn<QString>("outFile");
+
+ QTest::newRow("file1") << QStringLiteral(u"file1.qml") << QStringLiteral(u"file1.qml");
+ QTest::newRow("file1 unindented")
+ << QStringLiteral(u"file1Unindented.qml") << QStringLiteral(u"file1.qml");
+ }
+
+ void reindent()
+ {
+ QFETCH(QString, inFile);
+ QFETCH(QString, outFile);
+
+ QFile fIn(QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter/") + inFile);
+ if (!fIn.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning() << "could not open file" << inFile;
+ return;
+ }
+ QFile fOut(QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter/") + outFile);
+ if (!fOut.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning() << "could not open file" << outFile;
+ return;
+ }
+ QTextStream in(&fIn);
+ QTextStream out(&fOut);
+ QString resultStr;
+ QTextStream res(&resultStr);
+ QString line = in.readLine();
+ IndentingLineWriter lw([&res](QStringView s) { res << s; }, QLatin1String("*testStream*"));
+ QList<SourceLocation *> sourceLocations;
+ while (!line.isNull()) {
+ SourceLocation *loc = new SourceLocation;
+ sourceLocations.append(loc);
+ lw.write(line, loc);
+ lw.write(u"\n");
+ line = in.readLine();
+ }
+ lw.eof();
+ res.flush();
+ QString fullRes = resultStr;
+ res.seek(0);
+ line = out.readLine();
+ QString resLine = res.readLine();
+ int iLoc = 0;
+ int nextLoc = 0;
+ while (!line.isNull() && !resLine.isNull()) {
+ QCOMPARE(resLine, line);
+ if (iLoc == nextLoc && iLoc < sourceLocations.size()) {
+ QString l2 =
+ fullRes.mid(sourceLocations[iLoc]->offset, sourceLocations[iLoc]->length);
+ if (!l2.contains(QLatin1Char('\n'))) {
+ QCOMPARE(l2, line);
+ } else {
+ qDebug() << "skip checks of multiline location (line was split)" << l2;
+ iLoc -= l2.count(QLatin1Char('\n'));
+ }
+ ++nextLoc;
+ } else {
+ qDebug() << "skip multiline recover";
+ }
+ ++iLoc;
+ line = out.readLine();
+ resLine = res.readLine();
+ }
+ QCOMPARE(resLine.isNull(), line.isNull());
+ for (auto sLoc : sourceLocations)
+ delete sLoc;
+ }
+
+ void lineByLineReformatter_data()
+ {
+ QTest::addColumn<QString>("inFile");
+ QTest::addColumn<QString>("outFile");
+ QTest::addColumn<LineWriterOptions>("options");
+ LineWriterOptions defaultOptions;
+ LineWriterOptions noReorderOptions;
+ noReorderOptions.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
+
+ QTest::newRow("file1") << QStringLiteral(u"file1.qml")
+ << QStringLiteral(u"file1Reformatted.qml") << defaultOptions;
+
+ QTest::newRow("file2") << QStringLiteral(u"file2.qml")
+ << QStringLiteral(u"file2Reformatted.qml") << defaultOptions;
+
+ QTest::newRow("commentedFile")
+ << QStringLiteral(u"commentedFile.qml")
+ << QStringLiteral(u"commentedFileReformatted.qml") << defaultOptions;
+
+ QTest::newRow("required") << QStringLiteral(u"required.qml")
+ << QStringLiteral(u"requiredReformatted.qml") << defaultOptions;
+
+ QTest::newRow("inline") << QStringLiteral(u"inline.qml")
+ << QStringLiteral(u"inlineReformatted.qml") << defaultOptions;
+
+ QTest::newRow("spread") << QStringLiteral(u"spread.qml")
+ << QStringLiteral(u"spreadReformatted.qml") << defaultOptions;
+
+ QTest::newRow("template") << QStringLiteral(u"template.qml")
+ << QStringLiteral(u"templateReformatted.qml") << defaultOptions;
+
+ QTest::newRow("typeAnnotations")
+ << QStringLiteral(u"typeAnnotations.qml")
+ << QStringLiteral(u"typeAnnotationsReformatted.qml") << defaultOptions;
+
+ QTest::newRow("file1NoReorder")
+ << QStringLiteral(u"file1.qml") << QStringLiteral(u"file1Reformatted2.qml")
+ << noReorderOptions;
+ }
+
+ void lineByLineReformatter()
+ {
+ QFETCH(QString, inFile);
+ QFETCH(QString, outFile);
+ QFETCH(LineWriterOptions, options);
+
+ QString baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter");
+ QStringList qmltypeDirs =
+ QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
+ auto envPtr = DomEnvironment::create(
+ qmltypeDirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+ QString testFilePath = baseDir + QLatin1Char('/') + inFile;
+ DomItem tFile;
+ envPtr->loadBuiltins();
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFilePath),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ envPtr->loadPendingDependencies();
+
+ MutableDomItem myFile = tFile.field(Fields::currentItem);
+
+ QString resultStr;
+ QTextStream res(&resultStr);
+ IndentingLineWriter lw([&res](QStringView s) { res << s; }, QLatin1String("*testStream*"),
+ options);
+ OutWriter ow(lw);
+ DomItem qmlFile = tFile.field(Fields::currentItem);
+ qmlFile.writeOut(ow);
+ lw.eof();
+ res.flush();
+ QString fullRes = resultStr;
+ res.seek(0);
+ QFile fOut(baseDir + QLatin1Char('/') + outFile);
+ if (!fOut.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning() << "could not open file" << outFile;
+ return;
+ }
+ QTextStream out(&fOut);
+ QString line = out.readLine();
+ QString resLine = res.readLine();
+ auto writeReformatted = [fullRes]() {
+ qDebug().noquote().nospace() << "Reformatted output:\n"
+ << "-----------------\n"
+ << fullRes << "-----------------\n";
+ };
+ while (!line.isNull() && !resLine.isNull()) {
+ if (resLine != line)
+ writeReformatted();
+ QCOMPARE(resLine, line);
+ line = out.readLine();
+ resLine = res.readLine();
+ }
+ if (resLine.isNull() != line.isNull()) {
+ writeReformatted();
+ qDebug() << "reformatted at end" << resLine.isNull() << resLine
+ << "reference at end:" << line.isNull() << line;
+ }
+ QCOMPARE(resLine.isNull(), line.isNull());
+ }
+
+ void manualReformatter_data()
+ {
+ LineWriterOptions noReorderOptions;
+ QTest::addColumn<QString>("inFile");
+ QTest::addColumn<QString>("outFile");
+ QTest::addColumn<LineWriterOptions>("options");
+ LineWriterOptions defaultOptions;
+
+ noReorderOptions.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
+
+ QTest::newRow("file1") << QStringLiteral(u"file1.qml")
+ << QStringLiteral(u"file1Reformatted.qml") << defaultOptions;
+
+ QTest::newRow("file2") << QStringLiteral(u"file2.qml")
+ << QStringLiteral(u"file2Reformatted.qml") << defaultOptions;
+
+ QTest::newRow("commentedFile")
+ << QStringLiteral(u"commentedFile.qml")
+ << QStringLiteral(u"commentedFileReformatted2.qml") << defaultOptions;
+
+ QTest::newRow("required") << QStringLiteral(u"required.qml")
+ << QStringLiteral(u"requiredReformatted2.qml") << defaultOptions;
+
+ QTest::newRow("inline") << QStringLiteral(u"inline.qml")
+ << QStringLiteral(u"inlineReformatted.qml") << defaultOptions;
+
+ QTest::newRow("spread") << QStringLiteral(u"spread.qml")
+ << QStringLiteral(u"spreadReformatted.qml") << defaultOptions;
+
+ QTest::newRow("template") << QStringLiteral(u"template.qml")
+ << QStringLiteral(u"templateReformatted.qml") << defaultOptions;
+
+ QTest::newRow("arrowFunctions")
+ << QStringLiteral(u"arrowFunctions.qml")
+ << QStringLiteral(u"arrowFunctionsReformatted.qml") << defaultOptions;
+
+ QTest::newRow("file1NoReorder")
+ << QStringLiteral(u"file1.qml") << QStringLiteral(u"file1Reformatted2.qml")
+ << noReorderOptions;
+ QTest::newRow("noMerge")
+ << QStringLiteral(u"noMerge.qml") << QStringLiteral(u"noMergeReformatted.qml")
+ << defaultOptions;
+ }
+
+ void manualReformatter()
+ {
+ QFETCH(QString, inFile);
+ QFETCH(QString, outFile);
+ QFETCH(LineWriterOptions, options);
+
+ QString baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter");
+ QStringList qmltypeDirs =
+ QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
+ auto envPtr = DomEnvironment::create(
+ qmltypeDirs,
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+ QString testFilePath = baseDir + QLatin1Char('/') + inFile;
+ DomItem tFile;
+ envPtr->loadBuiltins();
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFilePath),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ envPtr->loadPendingDependencies();
+
+ QString resultStr;
+ QTextStream res(&resultStr);
+ LineWriter lw([&res](QStringView s) { res << s; }, QLatin1String("*testStream*"), options);
+ OutWriter ow(lw);
+ ow.indentNextlines = true;
+ DomItem qmlFile = tFile.field(Fields::currentItem);
+ qmlFile.writeOut(ow);
+ lw.eof();
+ res.flush();
+ QString fullRes = resultStr;
+ res.seek(0);
+ QFile fOut(baseDir + QLatin1Char('/') + outFile);
+ if (!fOut.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning() << "could not open file" << outFile;
+ return;
+ }
+ QTextStream out(&fOut);
+ QString line = out.readLine();
+ QString resLine = res.readLine();
+ auto writeReformatted = [fullRes]() {
+ qDebug().noquote().nospace() << "Reformatted output:\n"
+ << "-----------------\n"
+ << fullRes << "-----------------\n";
+ };
+ while (!line.isNull() && !resLine.isNull()) {
+ if (resLine != line)
+ writeReformatted();
+ QCOMPARE(resLine, line);
+ line = out.readLine();
+ resLine = res.readLine();
+ }
+ if (resLine.isNull() != line.isNull()) {
+ writeReformatted();
+ qDebug() << "reformatted at end" << resLine.isNull() << resLine
+ << "reference at end:" << line.isNull() << line;
+ }
+ QCOMPARE(resLine.isNull(), line.isNull());
+ }
+
+ void indentInfo()
+ {
+ IndentInfo i1(u"\n\n ", 4);
+ QCOMPARE(i1.trailingString, u" ");
+ QCOMPARE(i1.nNewlines, 2);
+ QCOMPARE(i1.column, 2);
+ IndentInfo i2(u"\r\n\r\n ", 4);
+ QCOMPARE(i2.trailingString, u" ");
+ QCOMPARE(i2.nNewlines, 2);
+ QCOMPARE(i2.column, 2);
+ IndentInfo i3(u"\n ", 4);
+ QCOMPARE(i3.trailingString, u" ");
+ QCOMPARE(i3.nNewlines, 1);
+ QCOMPARE(i3.column, 1);
+ IndentInfo i4(u"\r\n ", 4);
+ QCOMPARE(i4.trailingString, u" ");
+ QCOMPARE(i4.nNewlines, 1);
+ QCOMPARE(i4.column, 1);
+ IndentInfo i5(u"\n", 4);
+ QCOMPARE(i5.trailingString, u"");
+ QCOMPARE(i5.nNewlines, 1);
+ QCOMPARE(i5.column, 0);
+ IndentInfo i6(u"\r\n", 4);
+ QCOMPARE(i6.trailingString, u"");
+ QCOMPARE(i6.nNewlines, 1);
+ QCOMPARE(i6.column, 0);
+ IndentInfo i7(u" ", 4);
+ QCOMPARE(i7.trailingString, u" ");
+ QCOMPARE(i7.nNewlines, 0);
+ QCOMPARE(i7.column, 2);
+ IndentInfo i8(u"", 4);
+ QCOMPARE(i8.trailingString, u"");
+ QCOMPARE(i8.nNewlines, 0);
+ QCOMPARE(i8.column, 0);
+ }
+
+ void lineWriter()
+ {
+ {
+ QString res;
+ LineWriterOptions opts;
+ opts.lineEndings = LineWriterOptions::LineEndings::Unix;
+ LineWriter lw([&res](QStringView v) { res.append(v); }, QLatin1String("*testStream*"),
+ opts);
+ lw.write(u"a\nb");
+ lw.write(u"c\r\nd");
+ lw.write(u"e\rf");
+ lw.write(u"g\r\n");
+ lw.write(u"h\r");
+ lw.write(u"\n");
+ QCOMPARE(res, u"a\nbc\nde\nfg\nh\n\n");
+ }
+ {
+ QString res;
+ LineWriterOptions opts;
+ opts.lineEndings = LineWriterOptions::LineEndings::Windows;
+ LineWriter lw([&res](QStringView v) { res.append(v); }, QLatin1String("*testStream*"),
+ opts);
+ lw.write(u"a\nb");
+ lw.write(u"c\r\nd");
+ lw.write(u"e\rf");
+ lw.write(u"g\r\n");
+ lw.write(u"h\r");
+ lw.write(u"\n");
+ QCOMPARE(res, u"a\r\nbc\r\nde\r\nfg\r\nh\r\n\r\n");
+ }
+ {
+ QString res;
+ LineWriterOptions opts;
+ opts.lineEndings = LineWriterOptions::LineEndings::OldMacOs;
+ LineWriter lw([&res](QStringView v) { res.append(v); }, QLatin1String("*testStream*"),
+ opts);
+ lw.write(u"a\nb");
+ lw.write(u"c\r\nd");
+ lw.write(u"e\rf");
+ lw.write(u"g\r\n");
+ lw.write(u"h\r");
+ lw.write(u"\n");
+ QCOMPARE(res, u"a\rbc\rde\rfg\rh\r\r");
+ }
+ }
+
+ void hoistableDeclaration_data()
+ {
+ QTest::addColumn<QString>("declarationToBeFormatted");
+ QTest::addColumn<QString>("expectedFormattedDeclaration");
+
+ QTest::newRow("Function") << QStringLiteral(u"function a(a,b){}")
+ << QStringLiteral(u"function a(a, b) {}");
+ QTest::newRow("AnonymousFunction") << QStringLiteral(u"let f=function (a,b){}")
+ << QStringLiteral(u"let f = function (a, b) {}");
+ QTest::newRow("Generator_lhs_star")
+ << QStringLiteral(u"function* g(a,b){}") << QStringLiteral(u"function* g(a, b) {}");
+ QTest::newRow("Generator_rhs_star")
+ << QStringLiteral(u"function *g(a,b){}") << QStringLiteral(u"function* g(a, b) {}");
+ QTest::newRow("AnonymousGenerator") << QStringLiteral(u"let g=function * (a,b){}")
+ << QStringLiteral(u"let g = function* (a, b) {}");
+ }
+
+ // https://262.ecma-international.org/7.0/#prod-HoistableDeclaration
+ void hoistableDeclaration()
+ {
+ QFETCH(QString, declarationToBeFormatted);
+ QFETCH(QString, expectedFormattedDeclaration);
+
+ QString formattedDeclaration = formatJSCode(declarationToBeFormatted);
+
+ QCOMPARE(formattedDeclaration, expectedFormattedDeclaration);
+ }
+
+ void exportDeclarations_data()
+ {
+ QTest::addColumn<QString>("exportToBeFormatted");
+ QTest::addColumn<QString>("expectedFormattedExport");
+ // not exhaustive list of ExportDeclarations as per
+ // https://262.ecma-international.org/7.0/#prod-ExportDeclaration
+
+ // LexicalDeclaration
+ QTest::newRow("LexicalDeclaration_let_Binding")
+ << QStringLiteral(u"export let name") << QStringLiteral(u"export let name;");
+ QTest::newRow("LexicalDeclaration_const_BindingList")
+ << QStringLiteral(u"export const "
+ u"n1=1,n2=2,n3=3,n4=4,n5=5")
+ << QStringLiteral(u"export const "
+ u"n1 = 1, n2 = 2, n3 = 3, n4 = 4, n5 = 5;");
+ QTest::newRow("LexicalDeclaration_const_ArrayBinding")
+ << QStringLiteral(u"export const "
+ u"[a,b]=a_and_b")
+ << QStringLiteral(u"export const "
+ u"[a, b] = a_and_b;");
+ QTest::newRow("LexicalDeclaration_let_ObjectBinding")
+ << QStringLiteral(u"export let "
+ u"{a,b:c}=a_and_b")
+ << QStringLiteral(u"export let "
+ u"{\na,\nb: c\n} = a_and_b;");
+
+ // ClassDeclaration
+ QTest::newRow("ClassDeclaration") << QStringLiteral(u"export "
+ u"class A extends B{}")
+ << QStringLiteral(u"export "
+ u"class A extends B {}");
+
+ // HoistableDeclaration
+ QTest::newRow("HoistableDeclaration_FunctionDeclaration")
+ << QStringLiteral(u"export "
+ u"function a(a,b){}")
+ << QStringLiteral(u"export "
+ u"function a(a, b) {}");
+ QTest::newRow("HoistableDeclaration_GeneratorDeclaration")
+ << QStringLiteral(u"export "
+ u"function * g(a,b){}")
+ << QStringLiteral(u"export "
+ u"function* g(a, b) {}");
+
+ // export ExportClause ;
+ QTest::newRow("ExportClause_Empty")
+ << QStringLiteral(u"export{}") << QStringLiteral(u"export {};");
+ QTest::newRow("ExportClause_1Specifier")
+ << QStringLiteral(u"export{one}") << QStringLiteral(u"export { one };");
+ QTest::newRow("ExportClause_Specifier_as")
+ << QStringLiteral(u"export{one as o}") << QStringLiteral(u"export { one as o };");
+ QTest::newRow("ExportClause_Specifier_as_StringLiteral")
+ << QStringLiteral(u"export{one as \"s\"}")
+ << QStringLiteral(u"export { one as \"s\" };");
+ QTest::newRow("ExportClause_ExportsList")
+ << QStringLiteral(u"export{one,two,three,four as fo,five}")
+ << QStringLiteral(u"export { one, two, three, four as fo, five };");
+
+ // export * FromClause ;
+ QTest::newRow("star") << QStringLiteral(u"export * from \"design\"")
+ << QStringLiteral(u"export * from \"design\";");
+ QTest::newRow("star_as_Specifier") << QStringLiteral(u"export * as star from \"design\"")
+ << QStringLiteral(u"export * as star from \"design\";");
+
+ // export ExportClause FromClause ;
+ QTest::newRow("ExportClause")
+ << QStringLiteral(u"export {i1 as n1,i2 as n2,nN} from \"M\"")
+ << QStringLiteral(u"export { i1 as n1, i2 as n2, nN } from \"M\";");
+
+ // export default HoistableDeclaration
+ QTest::newRow("Default_AnonymousFunction")
+ << QStringLiteral(u"export default function(a,b){}")
+ << QStringLiteral(u"export default function (a, b) {}");
+ QTest::newRow("Default_AnonymousGenerator")
+ << QStringLiteral(u"export default function * (a,b){}")
+ << QStringLiteral(u"export default function* (a, b) {}");
+ QTest::newRow("Default_Function") << QStringLiteral(u"export default function a(a,b){}")
+ << QStringLiteral(u"export default function a(a, b) {}");
+
+ // export default ClassDeclaration
+ QTest::newRow("Default_Class") << QStringLiteral(u"export default class A{}")
+ << QStringLiteral(u"export default class A {}");
+ QTest::newRow("Default_AnonymousClass")
+ << QStringLiteral(u"export default class extends A{}")
+ << QStringLiteral(u"export default class extends A{}");
+
+ // export default Expression
+ QTest::newRow("Default_Expression") << QStringLiteral(u"export default 1+1")
+ << QStringLiteral(u"export default 1 + 1;");
+ QTest::newRow("Default_ArrowFunctionExpression")
+ << QStringLiteral(u"export default(x,y)=> x+2")
+ << QStringLiteral(u"export default (x, y) => x + 2;");
+ }
+
+ // https://262.ecma-international.org/7.0/#prod-ExportDeclaration
+ void exportDeclarations()
+ {
+ QFETCH(QString, exportToBeFormatted);
+ QFETCH(QString, expectedFormattedExport);
+
+ QString formattedExport = formatJSModuleCode(exportToBeFormatted);
+
+ QEXPECT_FAIL("ExportClause_Specifier_as_StringLiteral",
+ "export {a as \"string name\"} declaration is not supported yet", Abort);
+ QEXPECT_FAIL("star_as_Specifier", "export * as star declaration is not supported yet",
+ Abort);
+ QEXPECT_FAIL("Default_AnonymousClass", "QTBUG-122291", Abort);
+ QEXPECT_FAIL("Default_AnonymousFunction", "QTBUG-122291", Abort);
+ QEXPECT_FAIL("Default_AnonymousGenerator", "QTBUG-122291", Abort);
+ QCOMPARE(formattedExport, expectedFormattedExport);
+ }
+
+ void carryoverMJS_data()
+ {
+ QTest::addColumn<QString>("codeToBeFormatted");
+ QTest::addColumn<int>("maxLineLength");
+ QTest::addColumn<QString>("expectedFormattedCode");
+
+ QTest::newRow("LongExportList_NoMaxLineLength")
+ << QStringLiteral(u"export const n1=1,n2=2,n3=3,n4=4,n5=5") << -1
+ << QStringLiteral(u"export const n1 = 1, n2 = 2, n3 = 3, n4 = 4, n5 = 5;");
+ QTest::newRow("LongExportList_MaxLineLength20")
+ << QStringLiteral(u"export const n1=1,n2=2,n3=3,n4=4,n5=5") << 20
+ << QStringLiteral(u"export const n1 = 1,\n"
+ u" n2 = 2, n3 = 3,\n"
+ u" n4 = 4, n5 = 5;");
+ }
+
+ void carryoverMJS()
+ {
+ QFETCH(QString, codeToBeFormatted);
+ QFETCH(int, maxLineLength);
+ QFETCH(QString, expectedFormattedCode);
+
+ LineWriterOptions lwOptions;
+ lwOptions.maxLineLength = maxLineLength;
+ // TODO maybe fetch this
+ lwOptions.formatOptions.indentSize = 2;
+ QString formattedCode = formatJSModuleCode(codeToBeFormatted, lwOptions);
+
+ QEXPECT_FAIL("LongExportList_MaxLineLength20", "QTBUG-122260", Abort);
+ QCOMPARE(formattedCode, expectedFormattedCode);
+ }
+
+ void importDeclarations_data()
+ {
+ QTest::addColumn<QString>("importToBeFormatted");
+ QTest::addColumn<QString>("expectedFormattedImport");
+ // not exhaustive list of ExportDeclarations as per
+ // https://262.ecma-international.org/7.0/#prod-ImportDeclaration
+
+ // import ModuleSpecifier;
+ QTest::newRow("ModuleSpecifier")
+ << QStringLiteral(u"import \"Module\"") << QStringLiteral(u"import \"Module\";");
+
+ // import ImportClause FromClause ;
+ QTest::newRow("NameSpaceImport") << QStringLiteral(u"import * as d from \"design\";")
+ << QStringLiteral(u"import * as d from \"design\";");
+
+ QTest::newRow("NamedImports") << QStringLiteral(u"import {b,cd as c,d} from \"M\";")
+ << QStringLiteral(u"import { b, cd as c, d } from \"M\";");
+
+ QTest::newRow("DefaultBindung") << QStringLiteral(u"import defaultExport from \"M\"")
+ << QStringLiteral(u"import defaultExport from \"M\";");
+ QTest::newRow("DefaultBindung_NameSpaceImport")
+ << QStringLiteral(u"import defaultExport, * as m from \"M\";")
+ << QStringLiteral(u"import defaultExport, * as m from \"M\";");
+ QTest::newRow("DefaultBinding_NamedImports")
+ << QStringLiteral(u"import defaultExport,{b,cd as c,d} from \"M\";")
+ << QStringLiteral(u"import defaultExport, { b, cd as c, d } from \"M\";");
+
+ QTest::newRow("ImportClause_Specifier_as_StringLiteral")
+ << QStringLiteral(u"import{\"s\" as s} from \"M\"")
+ << QStringLiteral(u"import { \"s\" as s } from \"M\";");
+ }
+
+ // https://262.ecma-international.org/7.0/#prod-ImportDeclaration
+ void importDeclarations()
+ {
+ QFETCH(QString, importToBeFormatted);
+ QFETCH(QString, expectedFormattedImport);
+
+ QString formattedImport = formatJSModuleCode(importToBeFormatted);
+
+ QEXPECT_FAIL(
+ "ImportClause_Specifier_as_StringLiteral",
+ "import {\"string literal export\" as alias } declaration is not supported yet",
+ Abort);
+ QCOMPARE(formattedImport, expectedFormattedImport);
+ }
+
+ void methodDefinitions_data()
+ {
+ QTest::addColumn<QString>("methodToBeFormatted");
+ QTest::addColumn<QString>("expectedFormattedMethod");
+
+ // ObjectInitializer
+ QTest::newRow("ObjGetter") << QStringLiteral(u"const o={get a(){},}")
+ << QStringLiteral(u"const o = {\nget a(){}\n}");
+ QTest::newRow("ObjSetter") << QStringLiteral(u"const o={set a(a){},}")
+ << QStringLiteral(u"const o = {\nset a(a){}\n}");
+ QTest::newRow("ComputedObjPropertyGetter")
+ << QStringLiteral(u"const o={get [a+b](){},}")
+ << QStringLiteral(u"const o = {\nget [a + b](){}\n}");
+
+ // Generator
+ QTest::newRow("ObjPropertyGenerator")
+ << QStringLiteral(u"const o={*a(){1+1;},}")
+ << QStringLiteral(u"const o = {\n*a(){\n1 + 1;\n}\n}");
+ QTest::newRow("ComputedClassPropertyGenerator")
+ << QStringLiteral(u"class A{*[a+b](){}}")
+ << QStringLiteral(u"class A {\n*[a + b](){}\n}");
+
+ // ClassDefinitions
+ QTest::newRow("ClassGetter") << QStringLiteral(u"class A{get a(){}}")
+ << QStringLiteral(u"class A {\nget a(){}\n}");
+ QTest::newRow("ClassSetter") << QStringLiteral(u"class A{set a(a){}}")
+ << QStringLiteral(u"class A {\nset a(a){}\n}");
+ }
+
+ // https://262.ecma-international.org/7.0/#sec-method-definitions
+ void methodDefinitions()
+ {
+ QFETCH(QString, methodToBeFormatted);
+ QFETCH(QString, expectedFormattedMethod);
+
+ QString formattedMethod = formatJSCode(methodToBeFormatted);
+
+ QCOMPARE(formattedMethod, expectedFormattedMethod);
+ }
+
+ void statementList_data()
+ {
+ QTest::addColumn<QString>("codeToBeFormatted");
+ QTest::addColumn<QString>("expectedFormattedCode");
+
+ QTest::newRow("StatementsOnTheSameLine")
+ << QStringLiteral(u"a=1;b=1;") << QStringLiteral(u"a = 1;\nb = 1;");
+
+ QTest::newRow("StatementsOnSuccessiveLines")
+ << QStringLiteral(u"a=1;\nb=1;") << QStringLiteral(u"a = 1;\nb = 1;");
+
+ QTest::newRow("EmptyLineBetweenStatements")
+ << QStringLiteral(u"a=1;\n\nb=1;") << QStringLiteral(u"a = 1;\n\nb = 1;");
+
+ QTest::newRow("MultipleEmptyLinesBetweenStatements")
+ << QStringLiteral(u"a=1;\n\n\n\n\n\nb=1;") << QStringLiteral(u"a = 1;\n\nb = 1;");
+
+ QTest::newRow("MultilineStatementWithStatementOnTheFollowingLine")
+ << QStringLiteral(u"console.log(\n\n);\nb = 1;")
+ << QStringLiteral(u"console.log();\nb = 1;");
+
+ QTest::newRow("StatementWithPostCommentAndStatementOnTheFollowingLine")
+ << QStringLiteral(u"a=1;//\nb=1;") << QStringLiteral(u"a = 1;//\nb = 1;");
+
+ QTest::newRow("StatementWithPostCommentAndEmptyLineToNextStatement")
+ << QStringLiteral(u"a=1;//\n\nb=1;") << QStringLiteral(u"a = 1;//\n\nb = 1;");
+
+ QTest::newRow("StatementWithPostCommentAndMultipleEmptyLinesToNextStatement")
+ << QStringLiteral(u"a=1;//\n\n\n\n\nb=1;") << QStringLiteral(u"a = 1;//\n\nb = 1;");
+
+ QTest::newRow("StatementsWithCommentInBetweenThem")
+ << QStringLiteral(u"a=1;\n//\nb=1;") << QStringLiteral(u"a = 1;\n//\nb = 1;");
+
+ QTest::newRow("StatementsWithCommentAndSingleEmptyLineInBetweenThem")
+ << QStringLiteral(u"a=1;\n\n//\n\nb=1;")
+ << QStringLiteral(u"a = 1;\n\n//\n\nb = 1;");
+
+ QTest::newRow("StatementsWithCommentAndMultipleEmptyLinesInBetweenThem")
+ << QStringLiteral(u"a=1;\n\n\n\n//\n\n\nb=1;")
+ << QStringLiteral(u"a = 1;\n\n//\n\nb = 1;");
+
+ QTest::newRow("StatementWithSingleEmptyLineAndPreCommentOnNextStatement")
+ << QStringLiteral(u"a=1;\n\n//\nb=1;") << QStringLiteral(u"a = 1;\n\n//\nb = 1;");
+
+ QTest::newRow("StatementWithMultipleEmptyLinesAndPreCommentOnNextStatement")
+ << QStringLiteral(u"a=1;\n\n\n\n\n\n\n\n//\nb=1;")
+ << QStringLiteral(u"a = 1;\n\n//\nb = 1;");
+ }
+
+ void statementList()
+ {
+ QFETCH(QString, codeToBeFormatted);
+ QFETCH(QString, expectedFormattedCode);
+
+ QString formattedCode = formatJSCode(codeToBeFormatted);
+
+ QCOMPARE(formattedCode, expectedFormattedCode);
+ }
+
+private:
+};
+
+} // namespace Dom
+} // namespace QQmlJS
+QT_END_NAMESPACE
+
+#endif // TST_QMLDOMSCANNER_H
diff --git a/tests/auto/qmldom/stringdumper/CMakeLists.txt b/tests/auto/qmldom/stringdumper/CMakeLists.txt
index 1c809c2827..2b09281302 100644
--- a/tests/auto/qmldom/stringdumper/CMakeLists.txt
+++ b/tests/auto/qmldom/stringdumper/CMakeLists.txt
@@ -1,17 +1,25 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from stringdumper.pro.
#####################################################################
## tst_qmldomstringdumper Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmldomstringdumper LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmldomstringdumper
SOURCES
- tst_qmldomstringdumper.cpp
+ tst_qmldomstringdumper.cpp tst_qmldomstringdumper.h
DEFINES
QT_DEPRECATED_WARNINGS
INCLUDE_DIRECTORIES
../../../../src/qmldom
- PUBLIC_LIBRARIES
- Qt::QmlDevToolsPrivate
+ LIBRARIES
Qt::QmlDomPrivate
)
diff --git a/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp b/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp
index 04b9899d0f..fab3bc5be8 100644
--- a/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp
+++ b/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp
@@ -1,115 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**/
-#include <QtQmlDom/private/qqmldomstringdumper_p.h>
-
-#include <QtTest/QtTest>
-#include <QTextStream>
-#include <QDebug>
-
-#include <limits>
-
-QT_BEGIN_NAMESPACE
-namespace QQmlJS {
-namespace Dom {
-
-class TestStringDumper: public QObject
-{
- Q_OBJECT
-private slots:
- void testDumperToString() {
- QCOMPARE(dumperToString(u"bla"), QStringLiteral(u"bla"));
- QCOMPARE(dumperToString([](Sink s) { s(u"bla"); s(u"bla"); }), QStringLiteral(u"blabla"));
- }
-
- void testSinkInt() {
- QCOMPARE(dumperToString([](Sink s) { sinkInt(s, 1); }), QStringLiteral(u"1"));
- QCOMPARE(dumperToString([](Sink s) { sinkInt(s, 0); }), QStringLiteral(u"0"));
- QCOMPARE(dumperToString([](Sink s) { sinkInt(s, -1); }), QStringLiteral(u"-1"));
- QCOMPARE(dumperToString([](Sink s) { sinkInt(s, std::numeric_limits<qint32>::max()); }), QStringLiteral(u"2147483647"));
- QCOMPARE(dumperToString([](Sink s) { sinkInt(s, std::numeric_limits<qint32>::min()); }), QStringLiteral(u"-2147483648"));
- QCOMPARE(dumperToString([](Sink s) { sinkInt(s, std::numeric_limits<quint32>::max()); }), QStringLiteral(u"4294967295"));
- QCOMPARE(dumperToString([](Sink s) { sinkInt(s, std::numeric_limits<qint64>::min()); }), QStringLiteral(u"-9223372036854775808"));
- QCOMPARE(dumperToString([](Sink s) { sinkInt(s, std::numeric_limits<qint64>::max()); }), QStringLiteral(u"9223372036854775807"));
- QCOMPARE(dumperToString([](Sink s) { sinkInt(s, std::numeric_limits<quint64>::max()); }), QStringLiteral(u"18446744073709551615"));
- }
-
- void testSinkEscaped() {
- QCOMPARE(dumperToString([](Sink s) { sinkEscaped(s, u""); }), QStringLiteral(u"\"\""));
- QStringView s1 = u"bla:\"bla\\\""; // uR"(bla:"bla\")";
- QStringView s1Escaped = u"\"bla:\\\"bla\\\\\\\"\"";
- QCOMPARE(dumperToString([s1](Sink s) { sinkEscaped(s, s1); }), s1Escaped);
- QCOMPARE(dumperToString([](Sink s) { sinkEscaped(s, u"", EscapeOptions::NoOuterQuotes); }), QStringLiteral(u""));
- QStringView s2 = u"bla:\"bla\\\"\n";
- QStringView s2Escaped = u"bla:\\\"bla\\\\\\\"\\n"; // uR"(bla:\"bla\\\"\n)"
- QCOMPARE(dumperToString([s2](Sink s) { sinkEscaped(s, s2, EscapeOptions::NoOuterQuotes); }), s2Escaped);
- }
-
- void testDevNull() {
- sinkEscaped(devNull, u"");
- sinkEscaped(devNull, u"bla:\"bla\\\"\n");
- }
-
- void testSinkIndent() {
- QCOMPARE(dumperToString([](Sink s) { sinkIndent(s, 0); }), QStringLiteral(u""));
- QCOMPARE(dumperToString([](Sink s) { sinkIndent(s, 1); }), QStringLiteral(u" "));
- QCOMPARE(dumperToString([](Sink s) { sinkIndent(s, 10); }), QStringLiteral(u" "));
- QCOMPARE(dumperToString([](Sink s) { sinkIndent(s, 40); }), QStringLiteral(u" ").repeated(4));
- }
-
-
- void testSinkNewline(){
- QCOMPARE(dumperToString([](Sink s) { sinkNewline(s); }), QStringLiteral(u"\n"));
- QCOMPARE(dumperToString([](Sink s) { sinkNewline(s, 0); }), QStringLiteral(u"\n"));
- QCOMPARE(dumperToString([](Sink s) { sinkNewline(s, 1); }), QStringLiteral(u"\n "));
- QCOMPARE(dumperToString([](Sink s) { sinkNewline(s, 10); }), QStringLiteral(u"\n "));
- QCOMPARE(dumperToString([](Sink s) { sinkNewline(s, 40); }), QStringLiteral(u"\n") + QStringLiteral(u" ").repeated(4));
- }
-
- void testDumpErrorLevel(){
- QCOMPARE(dumperToString([](Sink s) { dumpErrorLevel(s, ErrorLevel::Hint); }), QStringLiteral(u"Hint"));
- QCOMPARE(dumperToString([](Sink s) { dumpErrorLevel(s, ErrorLevel::Error); }), QStringLiteral(u"Error"));
- QCOMPARE(dumperToString([](Sink s) { dumpErrorLevel(s, ErrorLevel::Warning); }), QStringLiteral(u"Warning"));
- }
-};
-
-} // namespace Dom
-} // namespace QQmlJS
-QT_END_NAMESPACE
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include "tst_qmldomstringdumper.h"
QTEST_MAIN(QQmlJS::Dom::TestStringDumper)
-#include "tst_qmldomstringdumper.moc"
diff --git a/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h b/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h
new file mode 100644
index 0000000000..28e844db73
--- /dev/null
+++ b/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h
@@ -0,0 +1,81 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef TST_QMLDOMSTRINGDUMPER_H
+#define TST_QMLDOMSTRINGDUMPER_H
+#include <QtQmlDom/private/qqmldomstringdumper_p.h>
+
+#include <QtTest/QtTest>
+#include <QTextStream>
+#include <QDebug>
+
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+
+class TestStringDumper: public QObject
+{
+ Q_OBJECT
+private slots:
+ void testDumperToString() {
+ QCOMPARE(dumperToString(u"bla"), QStringLiteral(u"bla"));
+ QCOMPARE(dumperToString([](Sink s) { s(u"bla"); s(u"bla"); }), QStringLiteral(u"blabla"));
+ }
+
+ void testSinkInt() {
+ QCOMPARE(dumperToString([](Sink s) { sinkInt(s, 1); }), QStringLiteral(u"1"));
+ QCOMPARE(dumperToString([](Sink s) { sinkInt(s, 0); }), QStringLiteral(u"0"));
+ QCOMPARE(dumperToString([](Sink s) { sinkInt(s, -1); }), QStringLiteral(u"-1"));
+ QCOMPARE(dumperToString([](Sink s) { sinkInt(s, std::numeric_limits<qint32>::max()); }), QStringLiteral(u"2147483647"));
+ QCOMPARE(dumperToString([](Sink s) { sinkInt(s, std::numeric_limits<qint32>::min()); }), QStringLiteral(u"-2147483648"));
+ QCOMPARE(dumperToString([](Sink s) { sinkInt(s, std::numeric_limits<quint32>::max()); }), QStringLiteral(u"4294967295"));
+ QCOMPARE(dumperToString([](Sink s) { sinkInt(s, std::numeric_limits<qint64>::min()); }), QStringLiteral(u"-9223372036854775808"));
+ QCOMPARE(dumperToString([](Sink s) { sinkInt(s, std::numeric_limits<qint64>::max()); }), QStringLiteral(u"9223372036854775807"));
+ QCOMPARE(dumperToString([](Sink s) { sinkInt(s, std::numeric_limits<quint64>::max()); }), QStringLiteral(u"18446744073709551615"));
+ }
+
+ void testSinkEscaped() {
+ QCOMPARE(dumperToString([](Sink s) { sinkEscaped(s, u""); }), QStringLiteral(u"\"\""));
+ QStringView s1 = u"bla:\"bla\\\""; // uR"(bla:"bla\")";
+ QStringView s1Escaped = u"\"bla:\\\"bla\\\\\\\"\"";
+ QCOMPARE(dumperToString([s1](Sink s) { sinkEscaped(s, s1); }), s1Escaped);
+ QCOMPARE(dumperToString([](Sink s) { sinkEscaped(s, u"", EscapeOptions::NoOuterQuotes); }), QStringLiteral(u""));
+ QStringView s2 = u"bla:\"bla\\\"\n";
+ QStringView s2Escaped = u"bla:\\\"bla\\\\\\\"\\n"; // uR"(bla:\"bla\\\"\n)"
+ QCOMPARE(dumperToString([s2](Sink s) { sinkEscaped(s, s2, EscapeOptions::NoOuterQuotes); }), s2Escaped);
+ }
+
+ void testDevNull() {
+ sinkEscaped(devNull, u"");
+ sinkEscaped(devNull, u"bla:\"bla\\\"\n");
+ }
+
+ void testSinkIndent() {
+ QCOMPARE(dumperToString([](Sink s) { sinkIndent(s, 0); }), QStringLiteral(u""));
+ QCOMPARE(dumperToString([](Sink s) { sinkIndent(s, 1); }), QStringLiteral(u" "));
+ QCOMPARE(dumperToString([](Sink s) { sinkIndent(s, 10); }), QStringLiteral(u" "));
+ QCOMPARE(dumperToString([](Sink s) { sinkIndent(s, 40); }), QStringLiteral(u" ").repeated(4));
+ }
+
+
+ void testSinkNewline(){
+ QCOMPARE(dumperToString([](Sink s) { sinkNewline(s); }), QStringLiteral(u"\n"));
+ QCOMPARE(dumperToString([](Sink s) { sinkNewline(s, 0); }), QStringLiteral(u"\n"));
+ QCOMPARE(dumperToString([](Sink s) { sinkNewline(s, 1); }), QStringLiteral(u"\n "));
+ QCOMPARE(dumperToString([](Sink s) { sinkNewline(s, 10); }), QStringLiteral(u"\n "));
+ QCOMPARE(dumperToString([](Sink s) { sinkNewline(s, 40); }), QStringLiteral(u"\n") + QStringLiteral(u" ").repeated(4));
+ }
+
+ void testDumpErrorLevel(){
+ QCOMPARE(dumperToString([](Sink s) { dumpErrorLevel(s, ErrorLevel::Error); }), QStringLiteral(u"Error"));
+ QCOMPARE(dumperToString([](Sink s) { dumpErrorLevel(s, ErrorLevel::Warning); }), QStringLiteral(u"Warning"));
+ }
+};
+
+} // namespace Dom
+} // namespace QQmlJS
+QT_END_NAMESPACE
+
+#endif