aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qmldom
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qmldom')
-rw-r--r--tests/auto/qmldom/CMakeLists.txt1
-rw-r--r--tests/auto/qmldom/combined/CMakeLists.txt8
-rw-r--r--tests/auto/qmldom/combined/tst_dom_all.cpp7
-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/WithImplicitImport.qml5
-rw-r--r--tests/auto/qmldom/domdata/domitem/Yyy.qml35
-rw-r--r--tests/auto/qmldom/domdata/domitem/aliasProperties.qml2
-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/bracketsInBinding.qml5
-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/comments.qml12
-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.qml2
-rw-r--r--tests/auto/qmldom/domdata/domitem/iterationStatements.qml64
-rw-r--r--tests/auto/qmldom/domdata/domitem/lambdas.qml41
-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/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/reformatter/commentedFileReformatted.qml2
-rw-r--r--tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml2
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file1.qml2
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml5
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml4
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file1Unindented.qml2
-rw-r--r--tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml2
-rw-r--r--tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml4
-rw-r--r--tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml2
-rw-r--r--tests/auto/qmldom/domitem/CMakeLists.txt7
-rw-r--r--tests/auto/qmldom/domitem/tst_qmldomitem.cpp2
-rw-r--r--tests/auto/qmldom/domitem/tst_qmldomitem.h3259
-rw-r--r--tests/auto/qmldom/errormessage/CMakeLists.txt6
-rw-r--r--tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp2
-rw-r--r--tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h2
-rw-r--r--tests/auto/qmldom/merging/CMakeLists.txt7
-rw-r--r--tests/auto/qmldom/merging/tst_dommerging.cpp2
-rw-r--r--tests/auto/qmldom/merging/tst_dommerging.h15
-rw-r--r--tests/auto/qmldom/path/CMakeLists.txt6
-rw-r--r--tests/auto/qmldom/path/tst_qmldompath.cpp2
-rw-r--r--tests/auto/qmldom/path/tst_qmldompath.h18
-rw-r--r--tests/auto/qmldom/reformatter/CMakeLists.txt7
-rw-r--r--tests/auto/qmldom/reformatter/tst_reformatter.cpp2
-rw-r--r--tests/auto/qmldom/reformatter/tst_reformatter.h417
-rw-r--r--tests/auto/qmldom/standalone/CMakeLists.txt39
-rw-r--r--tests/auto/qmldom/standalone/tst_standalone.cpp5
-rw-r--r--tests/auto/qmldom/standalone/tst_standalone.h634
-rw-r--r--tests/auto/qmldom/stringdumper/CMakeLists.txt6
-rw-r--r--tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp2
-rw-r--r--tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h2
92 files changed, 4513 insertions, 781 deletions
diff --git a/tests/auto/qmldom/CMakeLists.txt b/tests/auto/qmldom/CMakeLists.txt
index 7e2a0b885f..f3186e2263 100644
--- a/tests/auto/qmldom/CMakeLists.txt
+++ b/tests/auto/qmldom/CMakeLists.txt
@@ -10,4 +10,3 @@ add_subdirectory(stringdumper)
add_subdirectory(merging)
add_subdirectory(reformatter)
add_subdirectory(combined)
-add_subdirectory(standalone)
diff --git a/tests/auto/qmldom/combined/CMakeLists.txt b/tests/auto/qmldom/combined/CMakeLists.txt
index 6903f89eff..add44acf0b 100644
--- a/tests/auto/qmldom/combined/CMakeLists.txt
+++ b/tests/auto/qmldom/combined/CMakeLists.txt
@@ -5,6 +5,13 @@
## 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}/..
@@ -20,7 +27,6 @@ qt_internal_add_test(tst_dom_all
../domitem/tst_qmldomitem.h
../merging/tst_dommerging.h
../reformatter/tst_reformatter.h
- ../standalone/tst_standalone.h
INCLUDE_DIRECTORIES
..
DEFINES
diff --git a/tests/auto/qmldom/combined/tst_dom_all.cpp b/tests/auto/qmldom/combined/tst_dom_all.cpp
index b3920f8e41..6e3e0bd851 100644
--- a/tests/auto/qmldom/combined/tst_dom_all.cpp
+++ b/tests/auto/qmldom/combined/tst_dom_all.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "stringdumper/tst_qmldomstringdumper.h"
#include "errormessage/tst_qmldomerrormessage.h"
@@ -7,7 +7,6 @@
#include "merging/tst_dommerging.h"
#include "path/tst_qmldompath.h"
#include "reformatter/tst_reformatter.h"
-#include "standalone/tst_standalone.h"
#include <QtCore/qdebug.h>
@@ -38,10 +37,6 @@ int main(int argc, char *argv[])
QQmlJS::Dom::TestReformatter test;
status |= QTest::qExec(&test, argc, argv);
}
- {
- QQmlJS::Dom::TestStandalone 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/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
index c8653e3446..e2521b7afe 100644
--- a/tests/auto/qmldom/domdata/domitem/aliasProperties.qml
+++ b/tests/auto/qmldom/domdata/domitem/aliasProperties.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
Item {
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/bracketsInBinding.qml b/tests/auto/qmldom/domdata/domitem/crashes/bracketsInBinding.qml
new file mode 100644
index 0000000000..bef28ffc45
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/crashes/bracketsInBinding.qml
@@ -0,0 +1,5 @@
+import QtQuick
+
+Item {
+ property int foo: {}
+}
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/comments.qml b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/comments.qml
new file mode 100644
index 0000000000..a69505c544
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/fileLocationRegions/comments.qml
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+/*
+splitting
+multiline
+*/
+// single line
+/* another comment */
+QtObject {}
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
index 266e4139b7..7998642ef5 100644
--- a/tests/auto/qmldom/domdata/domitem/invalidAliasProperties.qml
+++ b/tests/auto/qmldom/domdata/domitem/invalidAliasProperties.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
Item {
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/lambdas.qml b/tests/auto/qmldom/domdata/domitem/lambdas.qml
new file mode 100644
index 0000000000..c241bb77ae
--- /dev/null
+++ b/tests/auto/qmldom/domdata/domitem/lambdas.qml
@@ -0,0 +1,41 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+
+Item {
+ signal helloSignal
+
+ function method() {
+ console.log("helloMethod");
+ let myLambda = function(a, b) { return a + b };
+ let myArrow = (v, w) => a + b;
+ }
+
+ onHelloSignal: function(x, y, z) { console.log("HelloLambda"); }
+
+ function testNestedFunctions() {
+ function nested(tic, tac, toe) { return tic + tac/3 + toe/2}
+ nested()
+ }
+
+ function generators() {
+ function *myGeneratorDeclaration(a, b) { yield 5 };
+ let myGenerator = function*(tic, tac, toe) { yield tic + tac - toe };
+ }
+
+ function *generatorInQmlObject() {
+ function nested(q,w,e,r) { return q + w + e - r; }
+ function *nested2(a,z,e,r) { yield a + z + e - r; yield 42; }
+ yield 4;
+ yield* nested2(1,2,3,4);
+ const t = (function (a) {
+ return a + 100;
+ });
+ }
+ function traditionalLambda() {
+ const tradition = (function (a) {
+ return a + 100;
+ });
+ }
+}
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/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/reformatter/commentedFileReformatted.qml b/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted.qml
index 0911f0e102..ab722f94be 100644
--- a/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted.qml
+++ b/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted.qml
@@ -26,8 +26,10 @@ Item {
// 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
diff --git a/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml b/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml
index 522cbbc777..c6e33f9389 100644
--- a/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml
+++ b/tests/auto/qmldom/domdata/reformatter/commentedFileReformatted2.qml
@@ -26,8 +26,10 @@ Item {
// 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
diff --git a/tests/auto/qmldom/domdata/reformatter/file1.qml b/tests/auto/qmldom/domdata/reformatter/file1.qml
index 36819b9998..e0382bf57c 100644
--- a/tests/auto/qmldom/domdata/reformatter/file1.qml
+++ b/tests/auto/qmldom/domdata/reformatter/file1.qml
@@ -14,6 +14,8 @@ Window {
Rectangle {
anchors.fill: parent
+ Behavior on opacity {}
+
ListView {
width: parent.width
model: {
diff --git a/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml b/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml
index 361d054f9c..6a24f907d1 100644
--- a/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml
+++ b/tests/auto/qmldom/domdata/reformatter/file1Reformatted.qml
@@ -14,6 +14,9 @@ Window {
Rectangle {
anchors.fill: parent
+ Behavior on opacity {
+ }
+
ListView {
model: {
MySingleton.mySignal();
@@ -38,7 +41,7 @@ Window {
function f(v = 4) {
let c = 0;
return {
- "a": function () {
+ a: function () {
if (b == 0)
c += 78 * 5 * v;
}()
diff --git a/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml b/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml
index 18c719020c..2e7483f453 100644
--- a/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml
+++ b/tests/auto/qmldom/domdata/reformatter/file1Reformatted2.qml
@@ -14,6 +14,8 @@ Window {
Rectangle {
anchors.fill: parent
+ Behavior on opacity {}
+
ListView {
width: parent.width
model: {
@@ -32,7 +34,7 @@ Window {
function f(v = 4) {
let c = 0;
return {
- "a": function () {
+ a: function () {
if (b == 0)
c += 78 * 5 * v;
}()
diff --git a/tests/auto/qmldom/domdata/reformatter/file1Unindented.qml b/tests/auto/qmldom/domdata/reformatter/file1Unindented.qml
index 224f1c1dce..4085b91e6e 100644
--- a/tests/auto/qmldom/domdata/reformatter/file1Unindented.qml
+++ b/tests/auto/qmldom/domdata/reformatter/file1Unindented.qml
@@ -14,6 +14,8 @@ property var arrTrailingComma: [1,2,3,]
Rectangle {
anchors.fill: parent
+Behavior on opacity {}
+
ListView {
width: parent.width
model: {
diff --git a/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml b/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml
index 48a961930d..3ed3aa208c 100644
--- a/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml
+++ b/tests/auto/qmldom/domdata/reformatter/file2Reformatted.qml
@@ -52,7 +52,7 @@ Window {
function f(v) {
let c = 0;
return {
- "a": function () {
+ a: function () {
if (b == 0)
c += 78 * 5 * v;
}()
diff --git a/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml b/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml
index cb84168307..7473283605 100644
--- a/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml
+++ b/tests/auto/qmldom/domdata/reformatter/requiredReformatted2.qml
@@ -8,7 +8,7 @@ Item {
function foo() {
theItem.foo("The issue is exacerbated if the object literal is wrapped onto the next line like so:", {
- "foo": theFoo
- });
+ "foo": theFoo
+ });
}
}
diff --git a/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml b/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml
index b01eca88d3..fde8ffd686 100644
--- a/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml
+++ b/tests/auto/qmldom/domdata/reformatter/spreadReformatted.qml
@@ -4,7 +4,7 @@ Item {
function foo() {
iterableObj = [1, 2];
obj = {
- "a": 42
+ a: 42
};
let x = (console.log("bla\n"), 3);
myFunction(...iterableObj); // pass all elements of iterableObj as arguments to function myFunction
diff --git a/tests/auto/qmldom/domitem/CMakeLists.txt b/tests/auto/qmldom/domitem/CMakeLists.txt
index bf12ad866c..e6f8117902 100644
--- a/tests/auto/qmldom/domitem/CMakeLists.txt
+++ b/tests/auto/qmldom/domitem/CMakeLists.txt
@@ -6,6 +6,13 @@
#####################################################################
## 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}/..
diff --git a/tests/auto/qmldom/domitem/tst_qmldomitem.cpp b/tests/auto/qmldom/domitem/tst_qmldomitem.cpp
index ea450e6969..97b7dcc3ed 100644
--- a/tests/auto/qmldom/domitem/tst_qmldomitem.cpp
+++ b/tests/auto/qmldom/domitem/tst_qmldomitem.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmldomitem.h"
QTEST_MAIN(QQmlJS::Dom::TestDomItem)
diff --git a/tests/auto/qmldom/domitem/tst_qmldomitem.h b/tests/auto/qmldom/domitem/tst_qmldomitem.h
index 433a92a2b2..6c74b7f746 100644
--- a/tests/auto/qmldom/domitem/tst_qmldomitem.h
+++ b/tests/auto/qmldom/domitem/tst_qmldomitem.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QMLDOMITEM_H
#define TST_QMLDOMITEM_H
@@ -9,6 +9,7 @@
#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>
@@ -16,14 +17,18 @@
#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(DomItem &self, const PathEls::PathComponent &p, const int &i)
+inline DomItem wrapInt(const DomItem &self, const PathEls::PathComponent &p, const int &i)
{
return self.subDataItem(p, i);
}
@@ -51,8 +56,9 @@ private slots:
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, universePtr));
+ 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,
@@ -363,7 +369,7 @@ private slots:
auto tOwner3 = tOwner.path(u"$env.testOwner");
QCOMPARE(tOwner3.internalKind(), DomType::MockOwner);
QList<qint64> values;
- tOwner.visitTree(Path(), [&values](Path p, DomItem i, bool) {
+ tOwner.visitTree(Path(), [&values](const Path &p, DomItem i, bool) {
if (i.pathFromOwner() != p)
myErrors()
.error(QStringLiteral(u"unexpected path %1 %2")
@@ -403,18 +409,15 @@ private slots:
qmltypeDirs,
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies,
- univPtr));
+ DomCreationOption::None, univPtr));
QQmlJS::Dom::DomItem env(envPtr);
QVERIFY(env);
QString testFile1 = baseDir + QLatin1String("/test1.qml");
DomItem tFile;
- // env.loadBuiltins();
- env.loadFile(
- testFile1, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadFile(baseDir, QString(), {}, LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ 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);
@@ -424,7 +427,7 @@ private slots:
DomItem obj1 = comp1.field(Fields::objects).index(0);
QVERIFY(obj1);
- tFile.visitTree(Path(), [&tFile](Path p, DomItem i, bool) {
+ 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()
@@ -489,18 +492,16 @@ private slots:
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));
+ qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option::SingleThreaded, {}, univPtr));
QQmlJS::Dom::DomItem env(envPtr);
QVERIFY(env);
QString testFile1 = baseDir + QLatin1String("/test1.qml");
DomItem tFile;
- env.loadBuiltins();
- env.loadFile(
- testFile1, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadFile(baseDir, QString(), {}, LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ 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);
@@ -531,13 +532,13 @@ private slots:
QList<DomItem> rects;
obj1.resolve(
Path::Current(PathCurrent::Lookup).field(Fields::type).key(u"Rectangle"_s),
- [&rects](Path, DomItem &el) {
+ [&rects](Path, const DomItem &el) {
rects.append(el);
return true;
},
{});
QVERIFY(rects.size() == 1);
- for (DomItem &el : rects) {
+ for (const DomItem &el : rects) {
QCOMPARE(rect.first(), el);
}
}
@@ -548,6 +549,13 @@ private slots:
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()
@@ -558,17 +566,17 @@ private slots:
using namespace Qt::StringLiterals;
QString testFile1 = baseDir + QLatin1String("/TestImports.qml");
- DomItem env = DomEnvironment::create(
+ auto envPtr = DomEnvironment::create(
QStringList(),
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
DomItem tFile;
- env.loadFile(
- testFile1, QString(),
- [&tFile](Path, DomItem &, DomItem &newIt) { tFile = newIt.fileObject(); },
- LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFile1),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) {
+ tFile = newIt.fileObject();
+ });
+ envPtr->loadPendingDependencies();
QVERIFY(tFile);
QList<QmlUri> importedModules;
@@ -601,17 +609,15 @@ private slots:
{
QString testFile = baseDir + QLatin1String("/test1.qml");
- DomItem env = DomEnvironment::create(
+ auto envPtr = DomEnvironment::create(
qmltypeDirs,
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
DomItem tFile; // place where to store the loaded file
- env.loadFile(
- testFile, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ 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); });
@@ -626,6 +632,7 @@ private slots:
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 =
@@ -645,11 +652,12 @@ private slots:
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(DomItem &qmlObj)
+ static void checkAliases(const DomItem &qmlObj)
{
using namespace Qt::StringLiterals;
@@ -714,7 +722,7 @@ private slots:
}
++i;
}
- for (DomItem obj : qmlObj.children().values()) {
+ for (const DomItem &obj : qmlObj.children().values()) {
if (obj.as<QmlObject>())
checkAliases(obj);
}
@@ -734,22 +742,3185 @@ private slots:
QFETCH(QString, inFile);
QString testFile1 = baseDir + u"/"_s + inFile;
- DomItem env = DomEnvironment::create(
+ auto envPtr = DomEnvironment::create(
QStringList(),
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
DomItem tFile;
- env.loadFile(
- testFile1, QString(),
- [&tFile](Path, DomItem &, DomItem &newIt) { tFile = newIt.fileObject(); },
- LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ 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;
+
+ QTest::addRow("bracketsInBinding")
+ << baseDir + u"/crashes/bracketsInBinding.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 populateLazyFileBeforeCommitToBase()
+ {
+ DomItem qmlObject;
+ 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{ QDir::cleanPath(baseDir + u"/propertyBindings.qml"_s) };
+
+ {
+ DomItem envChild = DomItem(envPtr).makeCopy(DomItem::CopyOption::EnvConnected).item();
+ auto envPtrChild = envChild.ownerAs<DomEnvironment>();
+ envPtrChild->loadFile(
+ FileToLoad::fromFileSystem(envPtrChild, fileName),
+ [&qmlObject](Path, const DomItem &, const DomItem &newIt) {
+ qmlObject = newIt.fileObject();
+ });
+ envPtrChild->loadPendingDependencies();
+
+ const DomItem childEnv = DomItem(envPtrChild->shared_from_this());
+ // populate the lazy file by accessing it via the DomItem interface
+ const DomItem mainComponent =
+ childEnv.field(Fields::qmlFileWithPath)
+ .key(fileName)
+ .field(Fields::currentItem)
+ .field(Fields::components)
+ .key(QString());
+ QVERIFY(mainComponent);
+
+ envPtrChild->commitToBase(DomItem(envPtrChild));
+ } // destroy the temporary environment that the file was loaded into
+
+ // also make sure that the main component also exists in the base environment after the
+ // commitToBase call.
+ const DomItem env = DomItem(envPtr->shared_from_this());
+ const DomItem mainComponent = env.field(Fields::qmlFileWithPath)
+ .key(fileName)
+ .field(Fields::currentItem)
+ .field(Fields::components)
+ .key(QString());
+ QVERIFY(mainComponent);
+ }
+
+ void populateLazyFileAfterCommitToBase()
+ {
+ DomItem qmlObject;
+ 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{ QDir::cleanPath(baseDir + u"/propertyBindings.qml"_s) };
+
+ {
+ DomItem envChild = DomItem(envPtr).makeCopy(DomItem::CopyOption::EnvConnected).item();
+ auto envPtrChild = envChild.ownerAs<DomEnvironment>();
+ envPtrChild->loadFile(
+ FileToLoad::fromFileSystem(envPtrChild, fileName),
+ [&qmlObject](Path, const DomItem &, const DomItem &newIt) {
+ qmlObject = newIt.fileObject();
+ });
+ envPtrChild->loadPendingDependencies();
+ envPtrChild->commitToBase(DomItem(envPtrChild));
+ } // destroy the temporary environment that the file was loaded into
+
+ const DomItem env = DomItem(envPtr->shared_from_this());
+ // populate the lazy file by accessing it via the DomItem interface
+ const DomItem mainComponent = env.field(Fields::qmlFileWithPath)
+ .key(fileName)
+ .field(Fields::currentItem)
+ .field(Fields::components)
+ .key(QString());
+ QVERIFY(mainComponent);
+ }
+
+ void qtbug_124799()
+ {
+ // reproduces the completion crash in QTBUG-124799 that was actually not completion related:
+ // triggering the completion was triggering the population of a file, that led to a
+ // heap-use-after-free. The steps to reproduce the crash are following:
+ // 1. load a file in a temporary environment
+ // 2. grab an unpopulated qqmljsscope from the type resolver of the loaded file
+ // 3. destroy the temporary environment
+ // 4. update the loaded file with new content, to make sure the QQmlJSImporter (used to
+ // populate of qmlfiles) has no more strong references in the QmlFile.
+ // 5. populate the unpopulated qqmljsscope: its factory should have kept track that its
+ // environment is not the temporary one but the base one (because of the commitToBase()
+ // call) and use the correct QQmlJSImporter (if its the one from the temporary environment
+ // this will lead to the heap-use-after-free memory error you get when triggering
+ // completions before this fix)
+
+ DomItem qmlObject;
+ 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{ QDir::cleanPath(baseDir + u"/propertyBindings.qml"_s) };
+
+ QQmlJSScope::ConstPtr populateAfterEnvironmentDestruction;
+
+ {
+ DomItem envChild = DomItem(envPtr).makeCopy(DomItem::CopyOption::EnvConnected).item();
+ auto envPtrChild = envChild.ownerAs<DomEnvironment>();
+ envPtrChild->loadFile(
+ FileToLoad::fromFileSystem(envPtrChild, fileName),
+ [&qmlObject](Path, const DomItem &, const DomItem &newIt) {
+ qmlObject = newIt.fileObject();
+ });
+ envPtrChild->loadPendingDependencies();
+
+ auto qmlFilePtr = qmlObject.ownerAs<QmlFile>();
+ auto resolver = qmlFilePtr->typeResolver();
+ // simulate completion by grabbing some type from the resolver
+ populateAfterEnvironmentDestruction = resolver->importedTypes()[u"Derived"_s].scope;
+ envPtrChild->commitToBase(DomItem(envPtrChild));
+ }
+
+ // update the file
+ {
+ DomItem envChild = DomItem(envPtr).makeCopy(DomItem::CopyOption::EnvConnected).item();
+ auto envPtrChild = envChild.ownerAs<DomEnvironment>();
+
+ // simulate user typing something
+ QFile file(fileName);
+ QVERIFY(file.open(QFile::ReadOnly));
+ const QString content = file.readAll();
+ const QString newContent = content + "\n // important comment here\n";
+ envPtrChild->loadFile(FileToLoad::fromMemory(envPtrChild, fileName, newContent),
+ [&qmlObject](Path, const DomItem &, const DomItem &newIt) {
+ qmlObject = newIt.fileObject();
+ });
+ envPtrChild->loadPendingDependencies();
+ envPtrChild->commitToBase(DomItem(envPtrChild));
+ }
+
+ // step 3: populate the lazy qqmljsscope, it should not crash
+ QCOMPARE(populateAfterEnvironmentDestruction->filePath(),
+ QDir::cleanPath(baseDir + u"/Derived.qml"_s));
+ }
+
+ 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));
+ }
+
+ void doNotCrashOnMissingLogger()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = QDir::cleanPath(baseDir + u"/astComments.qml"_s);
+ const DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ auto filePtr = fileObject.as<QmlFile>();
+ QVERIFY(filePtr);
+ auto typeResolver = filePtr->typeResolver();
+ QVERIFY(typeResolver);
+ auto logger = typeResolver->logger();
+ // make sure that the logger is not use-after-free by checking its content
+ QCOMPARE(logger->fileName(), testFile);
+ }
+
+ void commentLocations()
+ {
+ auto envPtr = DomEnvironment::create(
+ QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+
+ const auto filePath = baseDir + u"/fileLocationRegions/comments.qml"_s;
+ 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 expctedCommentLocations = QSet {
+ QQmlJS::SourceLocation(0, 41, 1, 1),
+ QQmlJS::SourceLocation(42,68, 2, 1),
+ QQmlJS::SourceLocation(126,25, 6, 1),
+ QQmlJS::SourceLocation(152,14, 10, 1),
+ QQmlJS::SourceLocation(167,21, 11, 1)
+ };
+
+ QSet<SourceLocation> locs;
+ file.fileObject(GoTo::MostLikely).visitTree(Path(), [&locs](Path, const DomItem &item, bool){
+ if (item.internalKind() == DomType::Comment) {
+ const auto comment = item.as<Comment>();
+ if (comment) {
+ locs << comment->info().sourceLocation();
+ }
+ }
+ return true;
+ }, VisitOption::Default, emptyChildrenVisitor, emptyChildrenVisitor);
+
+
+ QCOMPARE(locs, expctedCommentLocations);
+ }
+
+ void lambdas()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/lambdas.qml"_s;
+ const DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ const DomItem mainObject = fileObject.field(Fields::components)
+ .key(QString())
+ .index(0)
+ .field(Fields::objects)
+ .index(0);
+ {
+ const DomItem lambda = mainObject.field(Fields::methods)
+ .key(u"method"_s)
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(1)
+ .field(Fields::declarations)
+ .index(0)
+ .field(Fields::initializer);
+ QVERIFY(lambda);
+ QCOMPARE(lambda.internalKind(), DomType::ScriptFunctionExpression);
+ QCOMPARE(lambda.field(Fields::name).value().toString(), u"myLambda"_s);
+ QCOMPARE(lambda.field(Fields::parameters).indexes(), 2);
+ QCOMPARE(lambda.field(Fields::parameters).index(0).field(Fields::identifier).value().toString(), u"a");
+ QCOMPARE(lambda.field(Fields::parameters).index(1).field(Fields::identifier).value().toString(), u"b");
+
+ auto scope = lambda.semanticScope();
+ QVERIFY(scope);
+ QVERIFY(scope->jsIdentifier(u"b"_s));
+
+ const DomItem body = lambda.field(Fields::body);
+ QCOMPARE(body.internalKind(), DomType::ScriptBlockStatement);
+ }
+ }
+ void arrow()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/lambdas.qml"_s;
+ const DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ const DomItem mainObject = fileObject.field(Fields::components)
+ .key(QString())
+ .index(0)
+ .field(Fields::objects)
+ .index(0);
+ {
+ const DomItem arrow = mainObject.field(Fields::methods)
+ .key(u"method"_s)
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(2)
+ .field(Fields::declarations)
+ .index(0)
+ .field(Fields::initializer);
+ QVERIFY(arrow);
+ QCOMPARE(arrow.internalKind(), DomType::ScriptFunctionExpression);
+ QCOMPARE(arrow.field(Fields::name).value().toString(), u"myArrow"_s);
+ QCOMPARE(arrow.field(Fields::parameters).indexes(), 2);
+ QCOMPARE(arrow.field(Fields::parameters)
+ .index(0)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"v");
+ QCOMPARE(arrow.field(Fields::parameters)
+ .index(1)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"w");
+
+ auto scope = arrow.semanticScope();
+ QVERIFY(scope);
+ QVERIFY(scope->jsIdentifier(u"w"_s));
+
+ const DomItem body = arrow.field(Fields::body);
+ QCOMPARE(body.internalKind(), DomType::ScriptBlockStatement);
+ QCOMPARE(body.field(Fields::statements).indexes(), 1);
+ QCOMPARE(body.field(Fields::statements).index(0).internalKind(),
+ DomType::ScriptReturnStatement);
+ }
+ }
+ void lamdbaInBinding()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/lambdas.qml"_s;
+ const DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ const DomItem mainObject = fileObject.field(Fields::components)
+ .key(QString())
+ .index(0)
+ .field(Fields::objects)
+ .index(0);
+ {
+ const DomItem lambda = mainObject.field(Fields::bindings)
+ .key(u"onHelloSignal"_s)
+ .index(0)
+ .field(Fields::value)
+ .field(Fields::scriptElement);
+ QVERIFY(lambda);
+ QCOMPARE(lambda.internalKind(), DomType::ScriptFunctionExpression);
+ QCOMPARE(lambda.field(Fields::name).value().toString(), QString());
+ QCOMPARE(lambda.field(Fields::parameters).indexes(), 3);
+ QCOMPARE(lambda.field(Fields::parameters).index(0).field(Fields::identifier).value().toString(), u"x");
+ QCOMPARE(lambda.field(Fields::parameters).index(2).field(Fields::identifier).value().toString(), u"z");
+ auto scope = lambda.semanticScope();
+ QVERIFY(scope);
+ QVERIFY(scope->jsIdentifier(u"z"_s));
+ const DomItem body = lambda.field(Fields::body);
+ QCOMPARE(body.internalKind(), DomType::ScriptBlockStatement);
+ }
+ }
+ void nestedFunction()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/lambdas.qml"_s;
+ const DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ const DomItem mainObject = fileObject.field(Fields::components)
+ .key(QString())
+ .index(0)
+ .field(Fields::objects)
+ .index(0);
+ {
+ const DomItem nested = mainObject.field(Fields::methods)
+ .key(u"testNestedFunctions"_s)
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(0);
+ QVERIFY(nested);
+ QCOMPARE(nested.internalKind(), DomType::ScriptFunctionExpression);
+ QCOMPARE(nested.field(Fields::name).value().toString(), u"nested"_s);
+ QCOMPARE(nested.field(Fields::parameters).indexes(), 3);
+ QCOMPARE(nested.field(Fields::parameters)
+ .index(0)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"tic");
+ QCOMPARE(nested.field(Fields::parameters)
+ .index(2)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"toe");
+ const DomItem body = nested.field(Fields::body);
+ QCOMPARE(body.internalKind(), DomType::ScriptBlockStatement);
+ auto scope = nested.semanticScope();
+ QVERIFY(scope);
+ QVERIFY(scope->jsIdentifier(u"toe"_s));
+ }
+ }
+ void generatorDeclaration()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/lambdas.qml"_s;
+ const DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ const DomItem mainObject = fileObject.field(Fields::components)
+ .key(QString())
+ .index(0)
+ .field(Fields::objects)
+ .index(0);
+ {
+ const DomItem generator = mainObject.field(Fields::methods)
+ .key(u"generators"_s)
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(0);
+ QVERIFY(generator);
+ QCOMPARE(generator.internalKind(), DomType::ScriptFunctionExpression);
+ QCOMPARE(generator.field(Fields::name).value().toString(), u"myGeneratorDeclaration"_s);
+ QCOMPARE(generator.field(Fields::parameters).indexes(), 2);
+ QCOMPARE(generator.field(Fields::parameters)
+ .index(0)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"a");
+ QCOMPARE(generator.field(Fields::parameters)
+ .index(1)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"b");
+ const DomItem body = generator.field(Fields::body);
+ QCOMPARE(body.internalKind(), DomType::ScriptBlockStatement);
+ auto scope = generator.semanticScope();
+ QVERIFY(scope);
+ QVERIFY(scope->jsIdentifier(u"b"_s));
+
+ const DomItem yieldExpression =
+ generator.field(Fields::body).field(Fields::statements).index(0);
+ QCOMPARE(yieldExpression.internalKind(), DomType::ScriptYieldExpression);
+ QCOMPARE(yieldExpression.field(Fields::expression).value().toInteger(), 5);
+ }
+ }
+ void generatorExpression()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/lambdas.qml"_s;
+ const DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ const DomItem mainObject = fileObject.field(Fields::components)
+ .key(QString())
+ .index(0)
+ .field(Fields::objects)
+ .index(0);
+ {
+ const DomItem generator = mainObject.field(Fields::methods)
+ .key(u"generators"_s)
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(1)
+ .field(Fields::declarations)
+ .index(0)
+ .field(Fields::initializer);
+ QVERIFY(generator);
+ QCOMPARE(generator.internalKind(), DomType::ScriptFunctionExpression);
+ QCOMPARE(generator.field(Fields::name).value().toString(), u"myGenerator"_s);
+ QCOMPARE(generator.field(Fields::parameters).indexes(), 3);
+ QCOMPARE(generator.field(Fields::parameters)
+ .index(0)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"tic");
+ QCOMPARE(generator.field(Fields::parameters)
+ .index(2)
+ .field(Fields::identifier)
+ .value()
+ .toString(),
+ u"toe");
+ const DomItem body = generator.field(Fields::body);
+ QCOMPARE(body.internalKind(), DomType::ScriptBlockStatement);
+ auto scope = generator.semanticScope();
+ QVERIFY(scope);
+ QVERIFY(scope->jsIdentifier(u"toe"_s));
+ }
+ }
+ void generatorDeclarationInQmlObject()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/lambdas.qml"_s;
+ const DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ const DomItem statements = fileObject.field(Fields::components)
+ .key(QString())
+ .index(0)
+ .field(Fields::objects)
+ .index(0)
+ .field(Fields::methods)
+ .key(u"generatorInQmlObject"_s)
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements);
+ {
+ const DomItem nested = statements.index(0);
+ QVERIFY(nested);
+ QCOMPARE(nested.internalKind(), DomType::ScriptFunctionExpression);
+
+ const DomItem nested2 = statements.index(1);
+ QVERIFY(nested2);
+ QCOMPARE(nested2.internalKind(), DomType::ScriptFunctionExpression);
+
+ const DomItem yield = statements.index(2);
+ QVERIFY(yield);
+ QCOMPARE(yield.internalKind(), DomType::ScriptYieldExpression);
+
+ const DomItem yieldStar = statements.index(3);
+ QVERIFY(yieldStar);
+ QCOMPARE(yieldStar.internalKind(), DomType::ScriptYieldExpression);
+
+ }
+ }
+ void traditionalLambda()
+ {
+ using namespace Qt::StringLiterals;
+ const QString testFile = baseDir + u"/lambdas.qml"_s;
+ const DomItem fileObject = rootQmlObjectFromFile(testFile, qmltypeDirs).fileObject();
+ const DomItem initializer = fileObject.field(Fields::components)
+ .key(QString())
+ .index(0)
+ .field(Fields::objects)
+ .index(0)
+ .field(Fields::methods)
+ .key(u"traditionalLambda"_s)
+ .index(0)
+ .field(Fields::body)
+ .field(Fields::scriptElement)
+ .field(Fields::statements)
+ .index(0)
+ .field(Fields::declarations)
+ .index(0)
+ .field(Fields::initializer);
+ QVERIFY(initializer);
+ QCOMPARE(initializer.internalKind(), DomType::ScriptParenthesizedExpression);
+ const DomItem lambda = initializer.field(Fields::expression);
+ QVERIFY(lambda);
+ QCOMPARE(lambda.internalKind(), DomType::ScriptFunctionExpression);
+ }
+
+
private:
QString baseDir;
QStringList qmltypeDirs;
diff --git a/tests/auto/qmldom/errormessage/CMakeLists.txt b/tests/auto/qmldom/errormessage/CMakeLists.txt
index 5a61ebcb29..7c82876827 100644
--- a/tests/auto/qmldom/errormessage/CMakeLists.txt
+++ b/tests/auto/qmldom/errormessage/CMakeLists.txt
@@ -7,6 +7,12 @@
## 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.h
diff --git a/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp
index 0ca1091a18..2c2007c119 100644
--- a/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp
+++ b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmldomerrormessage.h"
#include <QtQmlDom/private/qqmldomerrormessage_p.h>
diff --git a/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h
index f7d3a291b6..31628ef52d 100644
--- a/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h
+++ b/tests/auto/qmldom/errormessage/tst_qmldomerrormessage.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQmlDom/qqmldom_global.h>
#include <QtTest/QtTest>
diff --git a/tests/auto/qmldom/merging/CMakeLists.txt b/tests/auto/qmldom/merging/CMakeLists.txt
index 2eaf7b9615..a33df96216 100644
--- a/tests/auto/qmldom/merging/CMakeLists.txt
+++ b/tests/auto/qmldom/merging/CMakeLists.txt
@@ -6,6 +6,13 @@
#####################################################################
## 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}/..
diff --git a/tests/auto/qmldom/merging/tst_dommerging.cpp b/tests/auto/qmldom/merging/tst_dommerging.cpp
index 2dc1570e32..1fa994b1a3 100644
--- a/tests/auto/qmldom/merging/tst_dommerging.cpp
+++ b/tests/auto/qmldom/merging/tst_dommerging.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// 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
index 01d20014bf..59937b279e 100644
--- a/tests/auto/qmldom/merging/tst_dommerging.h
+++ b/tests/auto/qmldom/merging/tst_dommerging.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_DOMMERGING_H
#define TST_DOMMERGING_H
@@ -38,16 +38,13 @@ private slots:
auto envPtr = std::shared_ptr<QQmlJS::Dom::DomEnvironment>(new QQmlJS::Dom::DomEnvironment(
qmltypeDirs,
DomEnvironment::Option::SingleThreaded | DomEnvironment::Option::NoDependencies));
- QQmlJS::Dom::DomItem env(envPtr);
- QVERIFY(env);
QString testFile1 = baseDir + QLatin1String("/test1.qml");
- env.loadFile(
- testFile1, QString(),
- [this](Path, const DomItem &, const DomItem &newIt) { this->tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadFile(baseDir, QString(), {}, LoadOption::DefaultLoad);
- envPtr->loadPendingDependencies(env);
+ 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);
diff --git a/tests/auto/qmldom/path/CMakeLists.txt b/tests/auto/qmldom/path/CMakeLists.txt
index dbffe714d9..bf3e5d20f5 100644
--- a/tests/auto/qmldom/path/CMakeLists.txt
+++ b/tests/auto/qmldom/path/CMakeLists.txt
@@ -7,6 +7,12 @@
## 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.h
diff --git a/tests/auto/qmldom/path/tst_qmldompath.cpp b/tests/auto/qmldom/path/tst_qmldompath.cpp
index 86ef15e035..54a634e476 100644
--- a/tests/auto/qmldom/path/tst_qmldompath.cpp
+++ b/tests/auto/qmldom/path/tst_qmldompath.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmldompath.h"
QTEST_MAIN(QQmlJS::Dom::PathEls::TestPaths)
diff --git a/tests/auto/qmldom/path/tst_qmldompath.h b/tests/auto/qmldom/path/tst_qmldompath.h
index 24d8c30bcf..f463b93164 100644
--- a/tests/auto/qmldom/path/tst_qmldompath.h
+++ b/tests/auto/qmldom/path/tst_qmldompath.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QMLDOMPATH_H
#define TST_QMLDOMPATH_H
@@ -16,7 +16,7 @@ namespace PathEls {
class TestPaths: public QObject {
Q_OBJECT
public:
- void testPathInternals(Path p1)
+ void testPathInternals(const Path &p1)
{
QCOMPARE(p1.component(0).kind(), Kind::Root);
QCOMPARE(p1.component(1).kind(), Kind::Current);
@@ -85,11 +85,11 @@ private slots:
auto c10=PathComponent(Any());
QCOMPARE(c10.kind(), Kind::Any);
QVERIFY(c9!=c10);
- auto c11=PathComponent(Filter([](DomItem){ return true; }));
+ auto c11=PathComponent(Filter([](const 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"));
+ 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
@@ -132,7 +132,7 @@ private slots:
QCOMPARE(p6[6].headKind(), Kind::Empty);
auto rString = u"$.@.aa[4][\"bla\"][*].";
QCOMPARE(p6.toString(), rString);
- auto p7 = p6.filter([](DomItem){ return true; }, u"true");
+ 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();
@@ -161,7 +161,7 @@ private slots:
void testPathSplit()
{
- QList<Path> paths({Path(),
+ 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"),
@@ -175,7 +175,7 @@ private slots:
Path::Index(4),
Path::Key(u"zz")
});
- foreach (Path p, paths) {
+ for (const Path &p : paths) {
Source s = p.split();
QCOMPARE(p, s.pathToSource.path(s.pathFromSource));
if (!s.pathFromSource)
diff --git a/tests/auto/qmldom/reformatter/CMakeLists.txt b/tests/auto/qmldom/reformatter/CMakeLists.txt
index 7b7f7e4708..1b8cfb0d8a 100644
--- a/tests/auto/qmldom/reformatter/CMakeLists.txt
+++ b/tests/auto/qmldom/reformatter/CMakeLists.txt
@@ -4,6 +4,13 @@
#####################################################################
## 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}/..
diff --git a/tests/auto/qmldom/reformatter/tst_reformatter.cpp b/tests/auto/qmldom/reformatter/tst_reformatter.cpp
index 75f8b445e7..00d26bc55b 100644
--- a/tests/auto/qmldom/reformatter/tst_reformatter.cpp
+++ b/tests/auto/qmldom/reformatter/tst_reformatter.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// 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
index 908c2f66a9..5cf800c80c 100644
--- a/tests/auto/qmldom/reformatter/tst_reformatter.h
+++ b/tests/auto/qmldom/reformatter/tst_reformatter.h
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QMLDOMCODEFORMATTER_H
#define TST_QMLDOMCODEFORMATTER_H
@@ -8,6 +8,7 @@
#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>
@@ -24,6 +25,69 @@ 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()
{
@@ -145,18 +209,16 @@ private slots:
QString baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter");
QStringList qmltypeDirs =
QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
- DomItem env = DomEnvironment::create(
+ auto envPtr = DomEnvironment::create(
qmltypeDirs,
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
QString testFilePath = baseDir + QLatin1Char('/') + inFile;
DomItem tFile;
- env.loadBuiltins();
- env.loadFile(
- testFilePath, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadPendingDependencies();
+ 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);
@@ -252,20 +314,16 @@ private slots:
QString baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter");
QStringList qmltypeDirs =
QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
- DomItem env = DomEnvironment::create(
+ auto envPtr = DomEnvironment::create(
qmltypeDirs,
QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
| QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
QString testFilePath = baseDir + QLatin1Char('/') + inFile;
DomItem tFile;
- env.loadBuiltins();
- env.loadFile(
- testFilePath, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; },
- LoadOption::DefaultLoad);
- env.loadPendingDependencies();
-
- MutableDomItem myFile = tFile.field(Fields::currentItem);
+ envPtr->loadBuiltins();
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, testFilePath),
+ [&tFile](Path, const DomItem &, const DomItem &newIt) { tFile = newIt; });
+ envPtr->loadPendingDependencies();
QString resultStr;
QTextStream res(&resultStr);
@@ -388,6 +446,331 @@ private slots:
}
}
+ 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) {}");
+ QTest::newRow("yield") << QStringLiteral(u"let g=function*(a,b){yield a;}")
+ << QStringLiteral(u"let g = function* (a, b) {\nyield a;\n}");
+ QTest::newRow("yield*") << QStringLiteral(u"let g=function*(a,b){yield*a;}")
+ << QStringLiteral(u"let g = function* (a, b) {\nyield* a;\n}");
+ QTest::newRow("yield*NoSemicolon")
+ << QStringLiteral(u"let g=function*(a,b){yield*a}")
+ << QStringLiteral(u"let g = function* (a, b) {\nyield* a;\n}");
+ }
+
+ // 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:
};
diff --git a/tests/auto/qmldom/standalone/CMakeLists.txt b/tests/auto/qmldom/standalone/CMakeLists.txt
deleted file mode 100644
index e141ec0835..0000000000
--- a/tests/auto/qmldom/standalone/CMakeLists.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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)
-#####################################################################
-# Collect test data
-file(GLOB_RECURSE test_data_glob
- RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/..
- domdata/*)
-list(APPEND test_data ${test_data_glob})
-
-set(QMLDOM_EXTERNAL_BUILD OFF CACHE BOOL "If the build is against an external Qt, and not tested inside a build of this Qt" FORCE)
-add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/qmldom/standalone qmldomlib)
-
-if(MSVC)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
-elseif (MINGW)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj")
-endif()
-
-qt_internal_add_test(tst_standalone
- SOURCES
- tst_standalone.cpp
- DEFINES
- QT_DEPRECATED_WARNINGS
- QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/../domdata"
- LIBRARIES
- Qt::Core
- Qt::QmlPrivate
- qmldomlib
- TESTDATA ${test_data}
-)
-
-qt_internal_extend_target(tst_standalone CONDITION ANDROID OR IOS
- DEFINES
- QT_QMLTEST_DATADIR=":/domdata"
-)
diff --git a/tests/auto/qmldom/standalone/tst_standalone.cpp b/tests/auto/qmldom/standalone/tst_standalone.cpp
deleted file mode 100644
index 14b6424525..0000000000
--- a/tests/auto/qmldom/standalone/tst_standalone.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "tst_standalone.h"
-QTEST_MAIN(QQmlJS::Dom::TestStandalone)
diff --git a/tests/auto/qmldom/standalone/tst_standalone.h b/tests/auto/qmldom/standalone/tst_standalone.h
deleted file mode 100644
index c403c660e6..0000000000
--- a/tests/auto/qmldom/standalone/tst_standalone.h
+++ /dev/null
@@ -1,634 +0,0 @@
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include <QtTest/QtTest>
-#ifdef QMLDOM_STANDALONE
-# include "qmldom/qqmldom_global.h"
-// common declarations
-# include "qmldom/qqmldomitem_p.h"
-// comparisons of two DomItems
-# include "qmldom/qqmldomcompare_p.h"
-// field filters to compare only selected fields (ignore for example location changes)
-# include "qmldom/qqmldomfieldfilter_p.h"
-// needed to edit and cast to concrete type (PropertyDefinition, ScriptExpression,...)
-# include "qmldom/qqmldomelements_p.h"
-// cast of the top level items (DomEnvironments,...)
-# include "qmldom/qqmldomtop_p.h"
-#else
-# include <QtQmlDom/qqmldom_global.h>
-// common declarations
-# include <QtQmlDom/private/qqmldomitem_p.h>
-// comparisons of two DomItems
-# include <QtQmlDom/private/qqmldomcompare_p.h>
-// field filters to compare only selected fields (ignore for example location changes)
-# include <QtQmlDom/private/qqmldomfieldfilter_p.h>
-// needed to edit and cast to concrete type (PropertyDefinition, ScriptExpression,...)
-# include <QtQmlDom/private/qqmldomelements_p.h>
-// cast of the top level items (DomEnvironments,...)
-# include <QtQmlDom/private/qqmldomtop_p.h>
-#endif
-
-#include <QDebug>
-#include <QLatin1String>
-#include <QLatin1Char>
-#include <QLibraryInfo>
-#include <QDir>
-
-QT_BEGIN_NAMESPACE
-namespace QQmlJS {
-namespace Dom {
-
-class TestStandalone : public QObject
-{
- Q_OBJECT
-private slots:
- void testLoadEditSave()
- {
- QString baseDir = QLatin1String(QT_QMLTEST_DATADIR) + QLatin1String("/reformatter");
- QStringList qmltypeDirs =
- QStringList({ baseDir, QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) });
-
- // qDebug() << "Creating an environment loading qml from the directories" << qmltypeDirs;
- // qDebug() << "single threaded, no dependencies";
- DomItem env = DomEnvironment::create(
- qmltypeDirs,
- QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
- | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
-
- QString testFilePath = baseDir + QLatin1String("/file1.qml");
- DomItem tFile; // place where to store the loaded file
-
- // qDebug() << "loading the file" << testFilePath;
- env.loadFile(
- testFilePath, QString(),
- [&tFile](Path, const DomItem &, const DomItem &newIt) {
- tFile = newIt; // callback called when everything is loaded that receives the
- // loaded external file pair (path, oldValue, newValue)
- },
- LoadOption::DefaultLoad);
-
- // trigger the load
- env.loadPendingDependencies();
-
- // # Read only API: DomItem is a generic pointer for read only access to Dom Itmes :)
- {
- // ## declarative json like API
- DomItem qmlFile = tFile.field(Fields::currentItem);
- DomItem imports = qmlFile.field(Fields::imports);
- DomItem qmlObj = qmlFile.field(Fields::components)
- .key(QString())
- .index(0)
- .field(Fields::objects)
- .index(0);
-
- // ### Dump
- // any DomItem can be dumped
- // qDebug() << "writing to QDebug dumps that element:" << imports;
- // often the dump is too verbose, and one might want it to a separate file
- QString dumpFilePath = QDir(QDir::tempPath())
- .filePath(QFileInfo(testFilePath).baseName()
- + QLatin1String(".dump.json"));
- qmlFile.dump(dumpFilePath, FieldFilter::defaultFilter());
- // qDebug() << "dumped file to" << dumpFilePath;
-
- // ### Paths
- // qDebug() << "To identify a DomItem a canonical path can be used:"
- // << imports.canonicalPath();
- // a path can be converted to/from strings
- QString pString = imports.canonicalPath().toString();
- Path importsPath = Path::fromString(pString);
- // and loaded again using the .path(somePath) method
- DomItem imports2 = env.path(importsPath);
- QCOMPARE(imports, imports2);
- // the canonical path is absolute, but you can have relative paths
- Path first = Path::Index(0);
- DomItem firstImport = imports.path(first);
- // an existing path can also be extended
- Path firstImportPath = importsPath.index(0);
- QCOMPARE(firstImportPath, firstImport.canonicalPath());
- // the normal elements of a path are index, key, field
- // Uppercase static method creates one, lowercase appends to an existing path.
- Path mainComponentPath = Path::Field(Fields::components).key("").index(0);
- DomItem mainComponent = qmlFile.path(mainComponentPath);
- // DomItems have the same methods to access their elements
- DomItem mainComponent2 = qmlFile.field(Fields::components).key("").index(0);
- // two other special ements are root (root element for absolute paths)
- Path topPath = Path::Root(PathRoot::Top);
- QCOMPARE(topPath, importsPath[0]);
- // the current element performs an operation (tipically a lookup or iteration) at the
- // current path location (not handled here)
- Path lookupPath = Path::Current(PathCurrent::Lookup);
-
- // there are various visit methods to iterate/visit DomItems in particular visitTree
- // which is quite flexible.
- // They normally use callbacks that can return false to stop the iteration.
- // Still often the DomKind specific for loop presentated later are clearer and more
- // convenient
- {
- QString s;
- QTextStream dbg(&s);
- imports.visitTree(
- Path(),
- [&dbg](Path p, const DomItem &el, bool) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "*"
- << p.last().toString() << " " << domKindToString(el.domKind())
- << "(" << el.internalKindStr() << ")\n";
- // returning false here stops the whole iteration
- return true;
- },
- VisitOption::Default, // we want a recursive visit visiting also the top and
- // adopted
- [&dbg](Path p, const DomItem &, bool canonicalChild) {
- // returning false here skips that branch
- if (!canonicalChild) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "+"
- << p.last().toString() << " (adopted, will not recurse)\n";
- } else if (p && p.headIndex(0) % 2 == 1) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "-"
- << p.last().toString() << " *recursive visit skipped*\n";
- return false; // we skip odd entries in lists;
- } else {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "+"
- << p.last().toString() << "\n";
- }
- return true;
- },
- [&dbg](Path p, const DomItem &, bool) {
- dbg << QStringLiteral(u" ").repeated(p.length()) << "="
- << p.last().toString() << "\n";
- return true;
- });
- dbg.flush();
- QCOMPARE(s,
- QStringLiteral("* List(List)\n"
- "+\n"
- " *[0] Object(Import)\n"
- " +[0]\n"
- " *.uri Value(ConstantData)\n"
- " +.uri\n"
- " =.uri\n"
- " *.version Object(Version)\n"
- " +.version\n"
- " *.majorVersion Value(ConstantData)\n"
- " +.majorVersion\n"
- " =.majorVersion\n"
- " *.minorVersion Value(ConstantData)\n"
- " +.minorVersion\n"
- " =.minorVersion\n"
- " *.isLatest Value(ConstantData)\n"
- " +.isLatest\n"
- " =.isLatest\n"
- " *.isValid Value(ConstantData)\n"
- " +.isValid\n"
- " =.isValid\n"
- " *.stringValue Value(ConstantData)\n"
- " +.stringValue\n"
- " =.stringValue\n"
- " =.version\n"
- " *.implicit Value(ConstantData)\n"
- " +.implicit\n"
- " =.implicit\n"
- " *.comments Object(RegionComments)\n"
- " +.comments\n"
- " =.comments\n"
- " =[0]\n"
- " *[1] Object(Import)\n"
- " -[1] *recursive visit skipped*\n"
- " *[2] Object(Import)\n"
- " +[2]\n"
- " *.uri Value(ConstantData)\n"
- " +.uri\n"
- " =.uri\n"
- " *.version Object(Version)\n"
- " +.version\n"
- " *.majorVersion Value(ConstantData)\n"
- " +.majorVersion\n"
- " =.majorVersion\n"
- " *.minorVersion Value(ConstantData)\n"
- " +.minorVersion\n"
- " =.minorVersion\n"
- " *.isLatest Value(ConstantData)\n"
- " +.isLatest\n"
- " =.isLatest\n"
- " *.isValid Value(ConstantData)\n"
- " +.isValid\n"
- " =.isValid\n"
- " *.stringValue Value(ConstantData)\n"
- " +.stringValue\n"
- " =.stringValue\n"
- " =.version\n"
- " *.implicit Value(ConstantData)\n"
- " +.implicit\n"
- " =.implicit\n"
- " *.comments Object(RegionComments)\n"
- " +.comments\n"
- " =.comments\n"
- " =[2]\n"
- " *[3] Object(Import)\n"
- " -[3] *recursive visit skipped*\n"
- " *[4] Object(Import)\n"
- " +[4]\n"
- " *.uri Value(ConstantData)\n"
- " +.uri\n"
- " =.uri\n"
- " *.version Object(Version)\n"
- " +.version\n"
- " *.majorVersion Value(ConstantData)\n"
- " +.majorVersion\n"
- " =.majorVersion\n"
- " *.minorVersion Value(ConstantData)\n"
- " +.minorVersion\n"
- " =.minorVersion\n"
- " *.isLatest Value(ConstantData)\n"
- " +.isLatest\n"
- " =.isLatest\n"
- " *.isValid Value(ConstantData)\n"
- " +.isValid\n"
- " =.isValid\n"
- " *.stringValue Value(ConstantData)\n"
- " +.stringValue\n"
- " =.stringValue\n"
- " =.version\n"
- " *.comments Object(RegionComments)\n"
- " +.comments\n"
- " =.comments\n"
- " =[4]\n"
- "=\n"));
- }
-
- // ### DomKind
- // any DomItem belongs to 5 basic types
-
- // 1. Object (a C++ object)
- QCOMPARE(qmlFile.domKind(), DomKind::Object);
- // The underlying type of the c++ object can be found with .internalKind()
- QCOMPARE(qmlFile.internalKind(), DomType::QmlFile);
- // .initernalKindStr() is a convenience string version of it
- QCOMPARE(qmlFile.internalKindStr(), u"QmlFile");
- // the object attributes (fields) can be reached using .field(u"filedName")
- // normally one should not use a string, but the Fields:: constant
- DomItem qmlFile2 = tFile.field(Fields::currentItem);
- // all the available fields can be listed via fields()
- // qDebug() << "The" << qmlObj.internalKindStr() << "at" << qmlObj.canonicalPath()
- // << "has the following fields:" << qmlObj.fields();
- // we can access the underlying C++ object with as<>
- // if (const QmlFile *qmlFilePtr = qmlFile.as<QmlFile>())
- // qDebug() << "The QmlFile lives at the address" << qmlFilePtr;
- //// We can get the shared pointer of the owner type (which for the file is the QmlFile
- /// itself
- // if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>())
- // qDebug() << "QmlFile uses shared pointers as ownership method, the underlying
- // address "
- // "is the same"
- // << qmlFilePtr.get();
-
- // 2. a (Cbor-) Value, i.e a string, number,...
- DomItem fPath = qmlFile.field(Fields::canonicalFilePath);
- QCOMPARE(fPath.domKind(), DomKind::Value);
- // the Cbor representation of a value can be extracted with .value(), and in this case
- // we can then call toString
- // qDebug() << "The filePath DomItem is " << fPath << " and it still 'knows' its path "
- // << fPath.canonicalPath() << " but can have it also as value:" <<
- // fPath.value()
- // << "or even better as string." <<
- // fPath.value().toString(QLatin1String("*none*"));
- // a DomItem might have a valid value() even if it is not of type DomKind::Value, indeed
- // CBor maps and lists are mapped to DomKind::Map and DomKind::List, and can be
- // traversed thought that but also have a valid value().
-
- // 3. a list
- QCOMPARE(imports.domKind(), DomKind::List);
- // the number of elements can be sound with .indexes() and with .index(n) we access each
- // element
- // qDebug() << "We have " << imports.indexes() << " imports, and the first is "
- // << imports.index(0);
- // If we want to just loop on the elements .values() is the most convenient way
- // technically values *always* works even for objects and maps, iterating on the values
- // for (DomItem import : imports.values()) {
- // if (const Import *importPtr = import.as<Import>()) {
- // if (importPtr->implicit)
- // qDebug() << importPtr->uri << importPtr->version.stringValue();
- // }
- //}
-
- // 4. a map
- DomItem bindings = qmlObj.field(Fields::bindings);
- QCOMPARE(bindings.domKind(), DomKind::Map);
- // The keys of the map can be reached either with .keys() or .sortedKeys(), each element
- // with .key(k)
- // qDebug() << "bindings";
- // for (QString k : bindings.sortedKeys()) {
- // for (DomItem b : bindings.key(k).values()) {
- // qDebug() << k << ":" << b;
- // }
- //}
-
- // 5 The empty element
- DomItem empty;
- QCOMPARE(empty.domKind(), DomKind::Empty);
- // The empty element is the only DomItem that casted to bool returns false, so checking
- // for it can be just an implicit cast to bool
- QVERIFY(bindings && !empty);
- // the empty element supports all the previus operations so that one can traverse a non
- // existing path without checking at every element, but only check the result
- DomItem nonExisting = qmlFile.field(u"no-existing").key(u"a").index(0);
- QVERIFY(!nonExisting);
-
- // the index operator [] can be used instead of .index/.key/.field, it might be slightly
- // less efficient but works
-
- // find type
- // access type
-
- // ### write out
- // it is possible to write out a qmlFile (actually also parts of it), which will
- // automatically reformat it
- QString reformattedFilePath =
- QDir(QDir::tempPath())
- .filePath(QFileInfo(testFilePath).baseName() + QLatin1String(".qml"));
- DomItem newFile = qmlFile.writeOut(reformattedFilePath);
- // qDebug() << "reformatted written at " << reformattedFilePath;
-
- // ## Jumping around
- // ### Generic Methods
- // from a DomItem you do no have just deeper in the tree, you can also go up the
- // hierarch toward the root .container() just goes up one step in the canonicalPath of
- // the object
- QCOMPARE(imports, firstImport.container());
- // .containingObject() goes up to the containing DomKind::Object, skipping over all Maps
- // and Lists
- QCOMPARE(qmlFile, firstImport.containingObject());
- // .owner() returns the shared pointer based "owner" object, qmlFile and
- // ScriptExpression are owningItems
- QCOMPARE(qmlFile, bindings.owner());
- // .top() goes to the top of the tree, i.e the environment (or the universe)
- QCOMPARE(env, bindings.top());
- // environment is normally the same as top, but making sure it is a actually a
- // DomEnvironment
- QCOMPARE(env, bindings.environment());
- // the universe is a cache of loaded files which for each file keeps two versions: the
- // latest and the latest valid it can be reached with .universe(), from the universe you
- // cannot get back to the environment.
- QCOMPARE(env.universe().internalKind(), DomType::DomUniverse);
-
- // ## QML Oriented Methods
- // The Dom model is not for generic json-like structures, so there are methods tailored
- // for Qml and its structure The methods can succeed if there is a clearly defined
- // unique result. sometime there is an obivious, but not necessarily unique choice
- // (tipically going up the hierarchy), for example given a qml file the obvious choice
- // for a component is the root component, but the file might contain other inline
- // components, and for an object with different version exposed (C++ property
- // versioning) the latest version is the natural choice, but other might be available.
- // In these case passing GoTo::MostLikely as argument makes the method to this obivious
- // choice (or possibly even only choice if no other versions/components are actually
- // defined), instead of refusing any potentially ambiguous situation and returning the
- // empty element.
-
- // .fileObject() goes to the object representing the whole file
- // (from either the external object returned by load or from inside the file)
- DomItem fileObject = tFile.fileObject();
- DomItem fileObject2 = imports.fileObject();
- QCOMPARE(fileObject, fileObject2);
- QCOMPARE(fileObject.internalKind(), DomType::QmlFile);
- // .component() goes to the component object.
- QCOMPARE(qmlObj.component(), qmlFile.component(GoTo::MostLikely));
- // .pragmas gives access to the pragmas of the current component
- QCOMPARE(qmlFile.pragmas(), qmlFile.field(Fields::pragmas));
-
- // QmlObject
- // QmlObject if the main to represent the type information (methods, bindings,
- // properties,...) of qml. Please note that QmlObject -> component operation is
- // potentially lossy, when multiple version are exposed, so we represent a type through
- // its root object, not through a component.
-
- // .qmlObject() goes to the current QmlObject
- QCOMPARE(qmlObj, bindings.qmlObject());
-
- // Given the centrality of QmlObject several of its attributes have convenience methods
- // to access them:
-
- // .children() makes subObjects contained inside a QmlObject accessible
- // note that it is possible to add objects also by directly binding the children or data
- // attribute, those children are not listed here, this accesses only those listed inside
- // the QmlObject
- QCOMPARE(qmlObj.children(), qmlObj.field(Fields::children));
- DomItem subObj0 = qmlObj.children().index(0);
- // .child(<i>) is a shortcut for .children.index(<i>)
- QCOMPARE(subObj0, qmlObj.child(0));
- // rootQmlObject goes to the root qmlObject (unless one reaches an empty element)
- QVERIFY(bool(subObj0));
- QCOMPARE(subObj0.rootQmlObject(), qmlObj);
- // .bindings() returns the bindings defined in the current object
- QCOMPARE(bindings, qmlObj.bindings());
- DomItem mCompObj = qmlObj.child(0)
- .child(0)
- .bindings()
- .key(u"delegate")
- .index(0)
- .field(Fields::value)
- .child(1);
- // .methods() gives methods definitions and signals
- DomItem methods = mCompObj.methods();
- // qDebug() << "mCompObj methods:";
- for (QString methodName : methods.sortedKeys()) {
- for (DomItem method : methods.key(methodName).values()) {
- if (const MethodInfo *methodPtr = method.as<MethodInfo>()) {
- QCOMPARE(methodName, methodPtr->name);
- // qDebug() << " " << methodPtr->name << methodPtr->methodType;
- }
- }
- }
- // qDebug() << "mCompObj propertyDefs:";
- // .propertyDefs() returns the properties defined in the current object
- DomItem pDefs = mCompObj.propertyDefs();
- for (QString pDefName : pDefs.sortedKeys()) {
- for (DomItem pDef : pDefs.key(pDefName).values()) {
- if (const PropertyDefinition *pDefPtr = pDef.as<PropertyDefinition>()) {
- QCOMPARE(pDefName, pDefPtr->name);
- // qDebug() << " " << pDefPtr->name << pDefPtr->typeName;
- }
- }
- }
- // binding and property definitions are about the ones defined in the current object
- // often one is interested also to the inherited properties.
- // Here PropertyInfo helps, it list all the definitions and bindings for a given
- // property in the inheritance order (local definitions, parent definitions, parent
- // parent definitions,...) .propertyInfos() gives access in the usual way (through a
- // DomItem)
- DomItem propertyInfos = mCompObj.propertyInfos();
- // .propertyInfoWithName(<name>) directly accesses one
- PropertyInfo pInfo = mCompObj.propertyInfoWithName(QStringLiteral(u"a"));
- // qDebug() << "bindings" << pInfo.bindings;
- // .propertyInfoNames() gives the names of the properties
- QCOMPARE(propertyInfos.keys(), mCompObj.propertyInfoNames());
-
- // .globalScope() goes to the globa scope object
- QCOMPARE(qmlObj.globalScope().internalKind(), DomType::GlobalScope);
- // and scope to the containing scope
- QCOMPARE(bindings.scope(), qmlObj);
- }
- // mutate & edit
- {
- // DomItem handles read-only access, but if one wants to change something it cannot be
- // used. MutableDomItem can be initialized with a DomItem, and provides also the methods
- // to modify the item. It keeps the OwningItem and the path to the current item.
- // Mutability can invalidate pointers to non owning items (and thus DomItem).
- // For this reason one should not modify something that other code can have a DomItem
- // pointer to, the best practice is to make shared object immutable and never change
- // them. One should modify only a copy that is used only by a single thread, and do not
- // shared untils all modifications are done. A MutableItem stays valid (or becomes
- // Empty), but stays safe to use
- //
- // Assuming one guarantees that editing is ok, doing it in practice is just about using
- // MutableDomItem instead of DomItem
- // It is possible to simply initialize a mutable item with a DomItem
- DomItem origFile = tFile.fileObject();
- MutableDomItem myFile0(origFile);
- // Normally it is better to have a separate environment. Is possible to avoid re-reading
- // the files already read by sharing the Universe between two environments.
- // But normally it is better and just as safe to work on a copy, so that one can be sure
- // that no DomItem is kept by other code gets invalidated. The .makeCopy creates a deep
- // copy, and by default (DomItem::CopyOption::EnvConnected) creates an environment which
- // to takes all non local elements from the current environment (its parent environment)
- // but replaces the file object with the copy. When finished one can replace the file
- // object of the parent with the new one using .commitToBase().
- MutableDomItem myFile = origFile.makeCopy();
- QVERIFY(myFile.ownerAs<QmlFile>()
- && myFile.ownerAs<QmlFile>() != myFile0.ownerAs<QmlFile>());
- QVERIFY(myFile.environment().ownerAs<DomEnvironment>()
- && myFile.environment().ownerAs<DomEnvironment>()
- != myFile0.environment().ownerAs<DomEnvironment>());
- // we can check that the two files are really identical (.item() give back the DomItem
- // of a MutableDomItem
- Q_ASSERT(domCompareStrList(origFile, myFile, FieldFilter::compareFilter()).isEmpty());
- // MutableDomItem has the same methods as DomItem
- MutableDomItem qmlObj = myFile.qmlObject(GoTo::MostLikely);
- MutableDomItem qmlObj2 = myFile.field(Fields::components)
- .key(QString())
- .index(0)
- .field(Fields::objects)
- .index(0);
- QVERIFY(bool(qmlObj));
- QCOMPARE(qmlObj, qmlObj2);
- // qDebug() << "mutable qmlObj has canonicalPath " << qmlObj.canonicalPath();
- // but it adds methods to add
- // * new PropertyDefinitions
- PropertyDefinition b;
- b.name = QLatin1String("xx");
- b.typeName = QLatin1String("int");
- // if we make t true we also have to give a value...
- MutableDomItem addedPDef = qmlObj.addPropertyDef(b);
- // qDebug() << "added property definition at:" << addedPDef.pathFromOwner();
- // * new bindings
- MutableDomItem addedBinding0 = qmlObj.addBinding(
- Binding("height",
- std::shared_ptr<ScriptExpression>(new ScriptExpression(
- QStringLiteral(u"243"),
- ScriptExpression::ExpressionType::BindingExpression))));
- // by default addBinding, addPropertyDef and addMethod have the AddOption::Override
- // to make it more difficult to create invalid documents, so that only the
- // following binding remains (where we use the convenience constructor that constucts
- // the ScriptExpression internally
- MutableDomItem addedBinding =
- qmlObj.addBinding(Binding("height", QStringLiteral(u"242")));
- // qDebug() << "added binding at:" << addedBinding.pathFromOwner();
- // * new methods
- MethodInfo mInfo;
- mInfo.name = QLatin1String("foo2");
- MethodParameter param;
- param.name = QLatin1String("x");
- mInfo.parameters.append(param);
- mInfo.setCode(QLatin1String("return 4*10+2 - x"));
- // we can change the added binding
- addedBinding.setCode(QLatin1String("245"));
- MutableDomItem addedMethod = qmlObj.addMethod(mInfo);
- // qDebug() << "added method at:" << addedMethod.pathFromOwner();
- // * new QmlObjects
- QmlObject subObj;
- subObj.setName(QLatin1String("Item"));
- MutableDomItem addedSubObj = qmlObj.addChild(subObj);
- // qDebug() << "added subObject at:" << addedMethod.pathFromOwner();
- // It is possible to modify the content of objects, using the mutableAs method
- if (PropertyDefinition *addedPDefPtr = addedPDef.mutableAs<PropertyDefinition>()) {
- addedPDefPtr->isRequired = true;
- }
- MutableDomItem firstChild = qmlObj.child(0);
- // qDebug() << "firstChild:" << firstChild;
- // It is possible remove objects
- if (QmlObject *qmlObjPtr = qmlObj.mutableAs<QmlObject>()) {
- QList<QmlObject> children = qmlObjPtr->children();
- children.removeAt(0);
- qmlObjPtr->setChildren(children);
- }
- // But as MutableDomItem does not keep the identity, just the same position, the
- // addedSubObj becomes invalid (and firstChild changes)
- // qDebug() << "after removal firstChild:" << firstChild;
- // qDebug() << "addedSubObj becomes invalid:" << addedSubObj;
- // qDebug() << "But the last object is the added one:"
- // << qmlObj.child(qmlObj.children().indexes() - 1);
-
- // now origFile are different
- Q_ASSERT(!domCompareStrList(origFile, myFile, FieldFilter::compareFilter()).isEmpty());
- // and we can look at the places where they differ
- // qDebug().noquote().nospace()
- // << "Edits introduced the following diffs (ignoring file locations"
- // << " and thus whitespace/reformatting changes):\n"
- // << domCompareStrList(origFile, myFile, FieldFilter::noLocationFilter(),
- // DomCompareStrList::AllDiffs)
- // .join(QString());
-
- QString reformattedFilePath =
- QDir(QDir::tempPath())
- .filePath(QStringLiteral(u"edited") + QFileInfo(testFilePath).baseName()
- + QLatin1String(".qml"));
- Q_ASSERT(myFile.as<QmlFile>()
- && myFile.as<QmlFile>() == myFile.fileObject().as<QmlFile>());
- MutableDomItem reformattedEditedFile = myFile.writeOut(reformattedFilePath);
- // the reformatted edited file might be different from the edited file
- // but the differences are just in file location/formatting
- Q_ASSERT(domCompareStrList(myFile, reformattedEditedFile,
- FieldFilter::noLocationFilter())
- .isEmpty());
-
- // qDebug() << "The edited file was written at " << reformattedFilePath;
- QString dumpFilePath = QDir(QDir::tempPath())
- .filePath(QStringLiteral(u"edited0")
- + QFileInfo(testFilePath).baseName()
- + QLatin1String(".dump.json"));
- myFile.dump(dumpFilePath);
- // qDebug() << "The non reformatted edited file was dumped at " << dumpFilePath;
- QString reformattedDumpFilePath =
- QDir(QDir::tempPath())
- .filePath(QStringLiteral(u"edited") + QFileInfo(testFilePath).baseName()
- + QLatin1String(".dump.json"));
- reformattedEditedFile.dump(reformattedDumpFilePath);
- // qDebug() << "The edited file was dumped at " << reformattedDumpFilePath;
- // The top environment still contains the original loaded file
- Q_ASSERT(origFile.ownerAs<QmlFile>() != reformattedEditedFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>());
- QCOMPARE(tFile.fileObject().refreshed().ownerAs<QmlFile>(),
- origFile.ownerAs<QmlFile>());
- QCOMPARE(tFile.fileObject().ownerAs<QmlFile>(), origFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>()
- != reformattedEditedFile.ownerAs<QmlFile>());
- // we can commit the reformatted file
- if (!reformattedEditedFile.commitToBase()) {
- qWarning() << "No reformatted file to commit";
- }
- // myFile might not be the same (If and updated check is requested, not the case here)
- if (myFile.ownerAs<QmlFile>() != reformattedEditedFile.ownerAs<QmlFile>()
- && !myFile.commitToBase()) {
- qWarning() << "Could not commit edited file";
- }
- // but refreshing it (looking up its canonical path) we always find the updated file
- QCOMPARE(myFile.refreshed().ownerAs<QmlFile>(),
- reformattedEditedFile.ownerAs<QmlFile>());
- Q_ASSERT(tFile.fileObject().refreshed().ownerAs<QmlFile>()
- == reformattedEditedFile.ownerAs<QmlFile>());
- }
- }
-};
-
-} // Dom
-} // QQmlJS
-QT_END_NAMESPACE
diff --git a/tests/auto/qmldom/stringdumper/CMakeLists.txt b/tests/auto/qmldom/stringdumper/CMakeLists.txt
index a67041d433..2b09281302 100644
--- a/tests/auto/qmldom/stringdumper/CMakeLists.txt
+++ b/tests/auto/qmldom/stringdumper/CMakeLists.txt
@@ -7,6 +7,12 @@
## 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.h
diff --git a/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp b/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp
index bc25070838..fab3bc5be8 100644
--- a/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp
+++ b/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmldomstringdumper.h"
QTEST_MAIN(QQmlJS::Dom::TestStringDumper)
diff --git a/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h b/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h
index 9d72b589c1..28e844db73 100644
--- a/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h
+++ b/tests/auto/qmldom/stringdumper/tst_qmldomstringdumper.h
@@ -1,5 +1,5 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QMLDOMSTRINGDUMPER_H
#define TST_QMLDOMSTRINGDUMPER_H