aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2020-06-30 15:25:51 +0200
committerUlf Hermann <ulf.hermann@qt.io>2020-07-08 09:46:46 +0200
commit6aa4c83bdbd9a8df05c14ac1aaad2f1d44053ec4 (patch)
tree8186599d1366b3bfbb7e65ab3d5a129e903a4c37
parente52e7ad76edc6012285db6c83eb12e8724a234ac (diff)
qmlimportscanner: Use QmlDirParser
This gives us a reliable way to parse imports and versions. Apparently we can also have multiple classname entries in the same qmldir file. Reflect that in the parser. Also, drop the version field from the output. Nobody uses it and maintaining it while allowing partial, missing and auto versions would be rather difficult. Fixes: QTBUG-85304 Change-Id: Iab89a6d505a3c58174e623f02f0418899cb5fa2f Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qml/qmldirparser/qqmldirparser.cpp8
-rw-r--r--src/qml/qmldirparser/qqmldirparser_p.h4
-rw-r--r--tests/auto/qml/qml.pro6
-rw-r--r--tests/auto/qml/qmlimportscanner/CMakeLists.txt35
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json36
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeSingleton/Singleton.qml6
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeSingleton/qmldir3
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json29
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeWithEnum/Animal.qml15
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeWithEnum/qmldir3
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json36
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton/Composite.qml5
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton/Singleton.qml6
-rw-r--r--tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton/qmldir4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Drawer.qml5
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Drawer.qml.json23
-rw-r--r--tests/auto/qml/qmlimportscanner/data/ImportWithPrefix.qml5
-rw-r--r--tests/auto/qml/qmlimportscanner/data/ImportWithPrefix.qml.json6
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Imports.json36
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Imports/CompositeImports.qml5
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Imports/Derived.qml6
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Imports/qmldir3
-rw-r--r--tests/auto/qml/qmlimportscanner/data/ListProperty.qml6
-rw-r--r--tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json36
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Methods.js3
-rw-r--r--tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js6
-rw-r--r--tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json30
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Simple.qml4
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Simple.qml.json30
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Singleton.json36
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Singleton/CompositeSingleton.qml6
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Singleton/qmldir2
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Things.json37
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Things/SomethingElse.qml2
-rw-r--r--tests/auto/qml/qmlimportscanner/data/Things/qmldir5
-rw-r--r--tests/auto/qml/qmlimportscanner/data/importing_js.qml5
-rw-r--r--tests/auto/qml/qmlimportscanner/data/importing_js.qml.json6
-rw-r--r--tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml6
-rw-r--r--tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json27
-rw-r--r--tests/auto/qml/qmlimportscanner/data/localImport.qml6
-rw-r--r--tests/auto/qml/qmlimportscanner/data/localImport.qml.json36
-rw-r--r--tests/auto/qml/qmlimportscanner/data/parentEnum.qml7
-rw-r--r--tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json30
-rw-r--r--tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml10
-rw-r--r--tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json36
-rw-r--r--tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml5
-rw-r--r--tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json23
-rw-r--r--tests/auto/qml/qmlimportscanner/data/rootPath.json54
-rw-r--r--tests/auto/qml/qmlimportscanner/qmlimportscanner.pro11
-rw-r--r--tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp169
-rw-r--r--tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp26
-rw-r--r--tools/qmlimportscanner/main.cpp162
52 files changed, 1033 insertions, 74 deletions
diff --git a/src/qml/qmldirparser/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp
index c8589431b8..e774f471cb 100644
--- a/src/qml/qmldirparser/qqmldirparser.cpp
+++ b/src/qml/qmldirparser/qqmldirparser.cpp
@@ -84,7 +84,7 @@ void QQmlDirParser::clear()
_plugins.clear();
_designerSupported = false;
_typeInfos.clear();
- _className.clear();
+ _classNames.clear();
}
inline static void scanSpace(const QChar *&ch) {
@@ -212,7 +212,7 @@ bool QQmlDirParser::parse(const QString &source)
continue;
}
- _className = sections[1];
+ _classNames.append(sections[1]);
} else if (sections[0] == QLatin1String("internal")) {
if (sectionCount != 3) {
@@ -410,9 +410,9 @@ bool QQmlDirParser::designerSupported() const
return _designerSupported;
}
-QString QQmlDirParser::className() const
+QStringList QQmlDirParser::classNames() const
{
- return _className;
+ return _classNames;
}
QDebug &operator<< (QDebug &debug, const QQmlDirParser::Component &component)
diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h
index 68a0d6d5e2..1500d0c653 100644
--- a/src/qml/qmldirparser/qqmldirparser_p.h
+++ b/src/qml/qmldirparser/qqmldirparser_p.h
@@ -161,7 +161,7 @@ public:
QList<TypeInfo> typeInfos() const;
- QString className() const;
+ QStringList classNames() const;
private:
bool maybeAddComponent(const QString &typeName, const QString &fileName, const QString &version, QHash<QString,Component> &hash, int lineNumber = -1, bool multi = true);
@@ -177,7 +177,7 @@ private:
QList<Plugin> _plugins;
bool _designerSupported = false;
QList<TypeInfo> _typeInfos;
- QString _className;
+ QStringList _classNames;
};
using QQmlDirComponents = QMultiHash<QString,QQmlDirParser::Component>;
diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro
index bc90d6df03..714d0a6c84 100644
--- a/tests/auto/qml/qml.pro
+++ b/tests/auto/qml/qml.pro
@@ -90,7 +90,11 @@ SUBDIRS += $$METATYPETESTS
qtConfig(process) {
qtConfig(qml-debug): SUBDIRS += debugger
!boot2qt {
- SUBDIRS += qmlformat qmllint qmlplugindump
+ SUBDIRS += \
+ qmlformat \
+ qmlimportscanner \
+ qmllint \
+ qmlplugindump
}
}
diff --git a/tests/auto/qml/qmlimportscanner/CMakeLists.txt b/tests/auto/qml/qmlimportscanner/CMakeLists.txt
new file mode 100644
index 0000000000..58c6620ff0
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/CMakeLists.txt
@@ -0,0 +1,35 @@
+# Generated from qmlimportscanner.pro.
+
+#####################################################################
+## tst_qmlimportscanner Test:
+#####################################################################
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_add_test(tst_qmlimportscanner
+ SOURCES
+ ../../shared/util.cpp ../../shared/util.h
+ tst_qmlimportscanner.cpp
+ INCLUDE_DIRECTORIES
+ ../../shared
+ PUBLIC_LIBRARIES
+ Qt::Gui
+ TESTDATA ${test_data}
+)
+
+## Scopes:
+#####################################################################
+
+qt_extend_target(tst_qmlimportscanner CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=\\\":/data\\\"
+)
+
+qt_extend_target(tst_qmlimportscanner CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\"
+)
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json b/tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json
new file mode 100644
index 0000000000..3283b0d036
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeSingleton.json
@@ -0,0 +1,36 @@
+[
+ {
+ "name": "CompositeSingleton",
+ "relativePath": "CompositeSingleton",
+ "type": "module"
+ },
+ {
+ "classname": "QtQuick2Plugin",
+ "name": "QtQuick",
+ "plugin": "qtquick2plugin",
+ "relativePath": "QtQuick",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
+
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeSingleton/Singleton.qml b/tests/auto/qml/qmlimportscanner/data/CompositeSingleton/Singleton.qml
new file mode 100644
index 0000000000..b47d2e98f4
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeSingleton/Singleton.qml
@@ -0,0 +1,6 @@
+pragma Singleton
+import QtQuick 2.0
+
+QtObject {
+ property int test: 0
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeSingleton/qmldir b/tests/auto/qml/qmlimportscanner/data/CompositeSingleton/qmldir
new file mode 100644
index 0000000000..897ed1c625
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeSingleton/qmldir
@@ -0,0 +1,3 @@
+module CompositeSingleton
+singleton Singleton 1.0 Singleton.qml
+depends QtQuick 2.0
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json b/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json
new file mode 100644
index 0000000000..13ed122797
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum.json
@@ -0,0 +1,29 @@
+[
+ {
+ "name": "CompositeWithEnum",
+ "relativePath": "CompositeWithEnum",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
+
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum/Animal.qml b/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum/Animal.qml
new file mode 100644
index 0000000000..5bd7788a8c
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum/Animal.qml
@@ -0,0 +1,15 @@
+pragma Singleton
+import QtQml 2.0
+
+QtObject {
+ property string name
+ property string category
+ property string sound
+ property int size: Animal.SizeSmall
+
+ enum SizeType {
+ SizeSmall,
+ SizeMedium,
+ SizeLarge
+ }
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum/qmldir b/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum/qmldir
new file mode 100644
index 0000000000..bc3fc4b62b
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeWithEnum/qmldir
@@ -0,0 +1,3 @@
+module CompositeWithEnum
+singleton Animal 1.0 Animal.qml
+depends QtQml 2.0
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json b/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json
new file mode 100644
index 0000000000..1020588e20
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton.json
@@ -0,0 +1,36 @@
+[
+ {
+ "name": "CompositeWithinSingleton",
+ "relativePath": "CompositeWithinSingleton",
+ "type": "module"
+ },
+ {
+ "classname": "QtQuick2Plugin",
+ "name": "QtQuick",
+ "plugin": "qtquick2plugin",
+ "relativePath": "QtQuick",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
+
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton/Composite.qml b/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton/Composite.qml
new file mode 100644
index 0000000000..b1055b6992
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton/Composite.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+QtObject {
+ property int test: 0
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton/Singleton.qml b/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton/Singleton.qml
new file mode 100644
index 0000000000..e81b2b6cb5
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton/Singleton.qml
@@ -0,0 +1,6 @@
+pragma Singleton
+import QtQuick 2.0
+
+QtObject {
+ property Composite test: Composite {}
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton/qmldir b/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton/qmldir
new file mode 100644
index 0000000000..e0e945cd0a
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/CompositeWithinSingleton/qmldir
@@ -0,0 +1,4 @@
+module CompositeWithinSingleton
+singleton Singleton 1.0 Singleton.qml
+Composite 1.0 Composite.qml
+depends QtQuick 2.0
diff --git a/tests/auto/qml/qmlimportscanner/data/Drawer.qml b/tests/auto/qml/qmlimportscanner/data/Drawer.qml
new file mode 100644
index 0000000000..db1d785c6c
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Drawer.qml
@@ -0,0 +1,5 @@
+import QtQml 2.12 as T
+
+T.QtObject {
+ objectName: T.Component.objectName
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/Drawer.qml.json b/tests/auto/qml/qmlimportscanner/data/Drawer.qml.json
new file mode 100644
index 0000000000..3bbbb1a130
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Drawer.qml.json
@@ -0,0 +1,23 @@
+[
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
diff --git a/tests/auto/qml/qmlimportscanner/data/ImportWithPrefix.qml b/tests/auto/qml/qmlimportscanner/data/ImportWithPrefix.qml
new file mode 100644
index 0000000000..6d070da21a
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/ImportWithPrefix.qml
@@ -0,0 +1,5 @@
+import "." as MyStuff
+
+MyStuff.Simple {
+ property bool something: contains(Qt.point(12, 34))
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/ImportWithPrefix.qml.json b/tests/auto/qml/qmlimportscanner/data/ImportWithPrefix.qml.json
new file mode 100644
index 0000000000..d228522ede
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/ImportWithPrefix.qml.json
@@ -0,0 +1,6 @@
+[
+ {
+ "name": ".",
+ "type": "directory"
+ }
+]
diff --git a/tests/auto/qml/qmlimportscanner/data/Imports.json b/tests/auto/qml/qmlimportscanner/data/Imports.json
new file mode 100644
index 0000000000..97f696cc08
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Imports.json
@@ -0,0 +1,36 @@
+[
+ {
+ "name": "Imports",
+ "relativePath": "Imports",
+ "type": "module"
+ },
+ {
+ "classname": "QtQuick2Plugin",
+ "name": "QtQuick",
+ "plugin": "qtquick2plugin",
+ "relativePath": "QtQuick",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
+
diff --git a/tests/auto/qml/qmlimportscanner/data/Imports/CompositeImports.qml b/tests/auto/qml/qmlimportscanner/data/Imports/CompositeImports.qml
new file mode 100644
index 0000000000..b1055b6992
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Imports/CompositeImports.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+QtObject {
+ property int test: 0
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/Imports/Derived.qml b/tests/auto/qml/qmlimportscanner/data/Imports/Derived.qml
new file mode 100644
index 0000000000..863c11ecd8
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Imports/Derived.qml
@@ -0,0 +1,6 @@
+pragma Singleton
+import Imports 1.0
+
+Imports {
+ property int something: 2
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/Imports/qmldir b/tests/auto/qml/qmlimportscanner/data/Imports/qmldir
new file mode 100644
index 0000000000..1a46fcb9fd
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Imports/qmldir
@@ -0,0 +1,3 @@
+module Imports
+CompositeImports 1.0 CompositeImports.qml
+singleton Derived 1.0 Derived.qml
diff --git a/tests/auto/qml/qmlimportscanner/data/ListProperty.qml b/tests/auto/qml/qmlimportscanner/data/ListProperty.qml
new file mode 100644
index 0000000000..d2bbc58cc5
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/ListProperty.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.12
+import Things 1.0
+
+Frame {
+ contentWidth: contentChildren.length === 1 ? contentChildren[0].implicitWidth : 0
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json b/tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json
new file mode 100644
index 0000000000..5debf07b8a
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/ListProperty.qml.json
@@ -0,0 +1,36 @@
+[
+ {
+ "classname": "QtQuick2Plugin",
+ "name": "QtQuick",
+ "plugin": "qtquick2plugin",
+ "relativePath": "QtQuick",
+ "type": "module"
+ },
+ {
+ "name": "Things",
+ "plugin": "doesNotExistPlugin",
+ "relativePath": "Things",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
diff --git a/tests/auto/qml/qmlimportscanner/data/Methods.js b/tests/auto/qml/qmlimportscanner/data/Methods.js
new file mode 100644
index 0000000000..52ab857e38
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Methods.js
@@ -0,0 +1,3 @@
+function foo() {
+ return "ttt"
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js b/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js
new file mode 100644
index 0000000000..1947b5aa80
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js
@@ -0,0 +1,6 @@
+.pragma library
+.import QtQuick 2.4 as JSQtQuick
+
+function foo(url)
+{
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json b/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json
new file mode 100644
index 0000000000..3d46f89a90
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/QTBUG-45916.js.json
@@ -0,0 +1,30 @@
+[
+ {
+ "classname": "QtQuick2Plugin",
+ "name": "QtQuick",
+ "plugin": "qtquick2plugin",
+ "relativePath": "QtQuick",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
diff --git a/tests/auto/qml/qmlimportscanner/data/Simple.qml b/tests/auto/qml/qmlimportscanner/data/Simple.qml
new file mode 100644
index 0000000000..db54cff477
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Simple.qml
@@ -0,0 +1,4 @@
+import QtQuick 2.2
+
+Item {
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/Simple.qml.json b/tests/auto/qml/qmlimportscanner/data/Simple.qml.json
new file mode 100644
index 0000000000..3d46f89a90
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Simple.qml.json
@@ -0,0 +1,30 @@
+[
+ {
+ "classname": "QtQuick2Plugin",
+ "name": "QtQuick",
+ "plugin": "qtquick2plugin",
+ "relativePath": "QtQuick",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
diff --git a/tests/auto/qml/qmlimportscanner/data/Singleton.json b/tests/auto/qml/qmlimportscanner/data/Singleton.json
new file mode 100644
index 0000000000..a285fdf250
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Singleton.json
@@ -0,0 +1,36 @@
+[
+ {
+ "name": "Singleton",
+ "relativePath": "Singleton",
+ "type": "module"
+ },
+ {
+ "classname": "QtQuick2Plugin",
+ "name": "QtQuick",
+ "plugin": "qtquick2plugin",
+ "relativePath": "QtQuick",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
+
diff --git a/tests/auto/qml/qmlimportscanner/data/Singleton/CompositeSingleton.qml b/tests/auto/qml/qmlimportscanner/data/Singleton/CompositeSingleton.qml
new file mode 100644
index 0000000000..b47d2e98f4
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Singleton/CompositeSingleton.qml
@@ -0,0 +1,6 @@
+pragma Singleton
+import QtQuick 2.0
+
+QtObject {
+ property int test: 0
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/Singleton/qmldir b/tests/auto/qml/qmlimportscanner/data/Singleton/qmldir
new file mode 100644
index 0000000000..04c686b065
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Singleton/qmldir
@@ -0,0 +1,2 @@
+module Singleton
+singleton CompositeSingleton 1.0 CompositeSingleton.qml
diff --git a/tests/auto/qml/qmlimportscanner/data/Things.json b/tests/auto/qml/qmlimportscanner/data/Things.json
new file mode 100644
index 0000000000..68430d158e
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Things.json
@@ -0,0 +1,37 @@
+[
+ {
+ "name": "Things",
+ "plugin": "doesNotExistPlugin",
+ "relativePath": "Things",
+ "type": "module"
+ },
+ {
+ "classname": "QtQuick2Plugin",
+ "name": "QtQuick",
+ "plugin": "qtquick2plugin",
+ "relativePath": "QtQuick",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
+
diff --git a/tests/auto/qml/qmlimportscanner/data/Things/SomethingElse.qml b/tests/auto/qml/qmlimportscanner/data/Things/SomethingElse.qml
new file mode 100644
index 0000000000..0e69012662
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Things/SomethingElse.qml
@@ -0,0 +1,2 @@
+import QtQml 2.0
+QtObject {}
diff --git a/tests/auto/qml/qmlimportscanner/data/Things/qmldir b/tests/auto/qml/qmlimportscanner/data/Things/qmldir
new file mode 100644
index 0000000000..d91d4afc92
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/Things/qmldir
@@ -0,0 +1,5 @@
+module Things
+Something 1.0 SomethingElse.qml
+plugin doesNotExistPlugin
+depends QtQuick 2.0
+import QtQml
diff --git a/tests/auto/qml/qmlimportscanner/data/importing_js.qml b/tests/auto/qml/qmlimportscanner/data/importing_js.qml
new file mode 100644
index 0000000000..fa6cf797db
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/importing_js.qml
@@ -0,0 +1,5 @@
+import "QTBUG-45916.js" as JSTest
+
+Item {
+ id: root
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/importing_js.qml.json b/tests/auto/qml/qmlimportscanner/data/importing_js.qml.json
new file mode 100644
index 0000000000..08ccb622b5
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/importing_js.qml.json
@@ -0,0 +1,6 @@
+[
+ {
+ "name": "QTBUG-45916.js",
+ "type": "javascript"
+ }
+]
diff --git a/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml b/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml
new file mode 100644
index 0000000000..62ddcfda60
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml
@@ -0,0 +1,6 @@
+import QtQml 2.0
+import "Methods.js" as Methods
+
+QtObject {
+ objectName: Methods.foo()
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json b/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json
new file mode 100644
index 0000000000..48029ebf01
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/javascriptMethods.qml.json
@@ -0,0 +1,27 @@
+[
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "name": "Methods.js",
+ "type": "javascript"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
diff --git a/tests/auto/qml/qmlimportscanner/data/localImport.qml b/tests/auto/qml/qmlimportscanner/data/localImport.qml
new file mode 100644
index 0000000000..4847fc9196
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/localImport.qml
@@ -0,0 +1,6 @@
+import Things 1.0
+
+Something {
+ property var a: SomethingEntirelyStrange {}
+ property var b: SomethingEntirelyStrange.AAA
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/localImport.qml.json b/tests/auto/qml/qmlimportscanner/data/localImport.qml.json
new file mode 100644
index 0000000000..1a1e096359
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/localImport.qml.json
@@ -0,0 +1,36 @@
+[
+ {
+ "name": "Things",
+ "plugin": "doesNotExistPlugin",
+ "relativePath": "Things",
+ "type": "module"
+ },
+ {
+ "classname": "QtQuick2Plugin",
+ "name": "QtQuick",
+ "plugin": "qtquick2plugin",
+ "relativePath": "QtQuick",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
diff --git a/tests/auto/qml/qmlimportscanner/data/parentEnum.qml b/tests/auto/qml/qmlimportscanner/data/parentEnum.qml
new file mode 100644
index 0000000000..313760f59a
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/parentEnum.qml
@@ -0,0 +1,7 @@
+import QtQuick
+
+Item {
+ width: 640
+ height: 480
+ property int t: PointerDevice.Position
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json b/tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json
new file mode 100644
index 0000000000..3d46f89a90
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/parentEnum.qml.json
@@ -0,0 +1,30 @@
+[
+ {
+ "classname": "QtQuick2Plugin",
+ "name": "QtQuick",
+ "plugin": "qtquick2plugin",
+ "relativePath": "QtQuick",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
diff --git a/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml b/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml
new file mode 100644
index 0000000000..275b531845
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml
@@ -0,0 +1,10 @@
+import Things
+
+QtObject {
+ objectName: "QtQml was imported from Things/qmldir"
+
+ ItemDerived {
+ objectName: "QQuickItem is depended upon and we know its properties"
+ x: 4
+ }
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json b/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json
new file mode 100644
index 0000000000..1a1e096359
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/qmldirImportAndDepend.qml.json
@@ -0,0 +1,36 @@
+[
+ {
+ "name": "Things",
+ "plugin": "doesNotExistPlugin",
+ "relativePath": "Things",
+ "type": "module"
+ },
+ {
+ "classname": "QtQuick2Plugin",
+ "name": "QtQuick",
+ "plugin": "qtquick2plugin",
+ "relativePath": "QtQuick",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
diff --git a/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml b/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml
new file mode 100644
index 0000000000..a0d2bde562
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml
@@ -0,0 +1,5 @@
+import QtQml 2.0
+
+QtObject {
+ Component.onCompleted: console.log("a")
+}
diff --git a/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json b/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json
new file mode 100644
index 0000000000..3bbbb1a130
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/qtQmlOnly.qml.json
@@ -0,0 +1,23 @@
+[
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ }
+]
diff --git a/tests/auto/qml/qmlimportscanner/data/rootPath.json b/tests/auto/qml/qmlimportscanner/data/rootPath.json
new file mode 100644
index 0000000000..8947b44706
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/data/rootPath.json
@@ -0,0 +1,54 @@
+[
+ {
+ "classname": "QtQuick2Plugin",
+ "name": "QtQuick",
+ "plugin": "qtquick2plugin",
+ "relativePath": "QtQuick",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlPlugin",
+ "name": "QtQml",
+ "plugin": "qmlplugin",
+ "relativePath": "QtQml",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlModelsPlugin",
+ "name": "QtQml.Models",
+ "plugin": "modelsplugin",
+ "relativePath": "QtQml/Models",
+ "type": "module"
+ },
+ {
+ "classname": "QtQmlWorkerScriptPlugin",
+ "name": "QtQml.WorkerScript",
+ "plugin": "workerscriptplugin",
+ "relativePath": "QtQml/WorkerScript",
+ "type": "module"
+ },
+ {
+ "name": "QTBUG-45916.js",
+ "type": "javascript"
+ },
+ {
+ "name": ".",
+ "type": "directory"
+ },
+ {
+ "name": "Methods.js",
+ "type": "javascript"
+ },
+ {
+ "name": "Things",
+ "plugin": "doesNotExistPlugin",
+ "relativePath": "Things",
+ "type": "module"
+ },
+ {
+ "name": "Imports",
+ "relativePath": "Imports",
+ "type": "module"
+ }
+]
+
diff --git a/tests/auto/qml/qmlimportscanner/qmlimportscanner.pro b/tests/auto/qml/qmlimportscanner/qmlimportscanner.pro
new file mode 100644
index 0000000000..1e8d13a327
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/qmlimportscanner.pro
@@ -0,0 +1,11 @@
+CONFIG += testcase
+TARGET = tst_qmlimportscanner
+macos:CONFIG -= app_bundle
+
+SOURCES += tst_qmlimportscanner.cpp
+
+include (../../shared/util.pri)
+
+TESTDATA = data/*
+
+QT += testlib
diff --git a/tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp b/tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp
new file mode 100644
index 0000000000..f4df7e9ac6
--- /dev/null
+++ b/tests/auto/qml/qmlimportscanner/tst_qmlimportscanner.cpp
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QProcess>
+#include <QString>
+
+#include <util.h>
+
+class TestQmlimportscanner: public QQmlDataTest
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase() override;
+
+ void cleanQmlCode_data();
+ void cleanQmlCode();
+ void rootPath();
+ void modules_data();
+ void modules();
+
+private:
+ void runQmlimportscanner(const QString &mode, const QString &fileToScan,
+ const QString &resultFile);
+
+ QString m_qmlimportscannerPath;
+};
+
+void TestQmlimportscanner::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+ m_qmlimportscannerPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/qmlimportscanner");
+#ifdef Q_OS_WIN
+ m_qmlimportscannerPath += QLatin1String(".exe");
+#endif
+ if (!QFileInfo(m_qmlimportscannerPath).exists()) {
+ QString message = QStringLiteral("qmlimportscanner executable not found (looked for %0)").arg(m_qmlimportscannerPath);
+ QFAIL(qPrintable(message));
+ }
+}
+
+void TestQmlimportscanner::cleanQmlCode_data()
+{
+ QTest::addColumn<QString>("filename");
+ QTest::newRow("Simple_QML") << QStringLiteral("Simple.qml");
+ QTest::newRow("QML_importing_JS") << QStringLiteral("importing_js.qml");
+ QTest::newRow("JS_with_pragma_and_import") << QStringLiteral("QTBUG-45916.js");
+ QTest::newRow("qtQmlOnly") << QStringLiteral("qtQmlOnly.qml");
+ QTest::newRow("directoryImportWithPrefix") << QStringLiteral("ImportWithPrefix.qml");
+ QTest::newRow("localImport") << QStringLiteral("localImport.qml");
+ QTest::newRow("methodsInJavascript") << QStringLiteral("javascriptMethods.qml");
+ QTest::newRow("moduleImportWithPrefix") << QStringLiteral("Drawer.qml");
+ QTest::newRow("localAndModuleImport") << QStringLiteral("ListProperty.qml");
+ QTest::newRow("versionLessLocalImport") << QStringLiteral("qmldirImportAndDepend.qml");
+ QTest::newRow("versionLessModuleImport") << QStringLiteral("parentEnum.qml");
+}
+
+void TestQmlimportscanner::cleanQmlCode()
+{
+ QFETCH(QString, filename);
+ runQmlimportscanner("-qmlFiles", testFile(filename), testFile(filename + ".json"));
+}
+
+void TestQmlimportscanner::rootPath()
+{
+ runQmlimportscanner("-rootPath", dataDirectory(), testFile("rootPath.json"));
+}
+
+void TestQmlimportscanner::modules_data()
+{
+ QTest::addColumn<QString>("name");
+ QTest::newRow("CompositeSingleton") << QStringLiteral("CompositeSingleton");
+ QTest::newRow("CompositeWithEnum") << QStringLiteral("CompositeWithEnum");
+ QTest::newRow("CompositeWithinSingleton") << QStringLiteral("CompositeWithinSingleton");
+ QTest::newRow("Imports") << QStringLiteral("Imports");
+ QTest::newRow("Singleton") << QStringLiteral("Singleton");
+ QTest::newRow("Things") << QStringLiteral("Things");
+}
+
+void TestQmlimportscanner::modules()
+{
+ QFETCH(QString, name);
+
+ QTemporaryFile qmlFile(QDir::tempPath() + "/tst_qmlimportscanner_XXXXXX.qml");
+ QVERIFY(qmlFile.open());
+
+ qmlFile.write("import " + name.toUtf8() + "\nQtObject {}");
+ qmlFile.close();
+ runQmlimportscanner("-qmlFiles", qmlFile.fileName(), testFile(name + ".json"));
+}
+
+void TestQmlimportscanner::runQmlimportscanner(const QString &mode, const QString &pathToScan,
+ const QString &resultFile)
+{
+ const QString file(pathToScan);
+ QStringList args {
+ mode, file,
+ "-importPath", QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath), dataDirectory()
+ };
+ QString errors;
+ QProcess process;
+ process.start(m_qmlimportscannerPath, args);
+ QVERIFY(process.waitForFinished());
+ QCOMPARE(process.exitStatus(), QProcess::NormalExit);
+ QCOMPARE(process.exitCode(), 0);
+ QVERIFY(process.readAllStandardError().isEmpty());
+
+ QJsonParseError error;
+ const QJsonDocument generated = QJsonDocument::fromJson(process.readAllStandardOutput(),
+ &error);
+ QCOMPARE(error.error, QJsonParseError::NoError);
+ QVERIFY(generated.isArray());
+
+ QFile imports(resultFile);
+ imports.open(QIODevice::ReadOnly);
+ QJsonDocument expected = QJsonDocument::fromJson(imports.readAll(), &error);
+ QCOMPARE(error.error, QJsonParseError::NoError);
+ QVERIFY(expected.isArray());
+
+ const QJsonArray generatedArray = generated.array();
+ QJsonArray expectedArray = expected.array();
+ for (const QJsonValue &value : generatedArray) {
+ QVERIFY(value.isObject());
+ QJsonObject object = value.toObject();
+
+ // Path is omitted because it's an absolute path, dependent on host system.
+ object["path"] = QJsonValue::Undefined;
+
+ bool found = false;
+ for (auto it = expectedArray.begin(), end = expectedArray.end(); it != end; ++it) {
+ if (*it == object) {
+ expectedArray.erase(it);
+ found = true;
+ break;
+ }
+ }
+ QVERIFY(found);
+ }
+ QVERIFY(expectedArray.isEmpty());
+}
+
+QTEST_MAIN(TestQmlimportscanner)
+#include "tst_qmlimportscanner.moc"
diff --git a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
index 627347df06..d019285c5b 100644
--- a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
+++ b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
@@ -133,6 +133,7 @@ void tst_qqmldirparser::parse_data()
QTest::addColumn<QString>("file");
QTest::addColumn<QStringList>("errors");
QTest::addColumn<QStringList>("plugins");
+ QTest::addColumn<QStringList>("classnames");
QTest::addColumn<QStringList>("components");
QTest::addColumn<QStringList>("scripts");
QTest::addColumn<QStringList>("dependencies");
@@ -145,6 +146,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("no-content")
@@ -154,6 +156,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("one-section")
@@ -163,6 +166,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("four-sections")
@@ -172,6 +176,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("incomplete-module")
@@ -181,6 +186,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("excessive-module")
@@ -190,6 +196,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("repeated-module")
@@ -199,6 +206,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("non-first-module")
@@ -208,6 +216,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("incomplete-plugin")
@@ -217,6 +226,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("excessive-plugin")
@@ -226,6 +236,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("name-plugin")
@@ -235,6 +246,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("name-path-plugin")
@@ -244,12 +256,14 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("unversioned-component")
<< "unversioned-component/qmldir"
<< QStringList()
<< QStringList()
+ << QStringList()
<< (QStringList() << "foo|bar|255|255|false")
<< QStringList()
<< QStringList()
@@ -262,12 +276,14 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("versioned-component")
<< "versioned-component/qmldir"
<< QStringList()
<< QStringList()
+ << QStringList()
<< (QStringList() << "foo|bar|33|66|false")
<< QStringList()
<< QStringList()
@@ -280,6 +296,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("versioned-script")
@@ -287,6 +304,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< (QStringList() << "foo|bar.js|33|66")
<< QStringList()
<< false;
@@ -295,6 +313,7 @@ void tst_qqmldirparser::parse_data()
<< "multiple/qmldir"
<< QStringList()
<< (QStringList() << "PluginA|plugina.so")
+ << QStringList()
<< (QStringList() << "ComponentA|componenta-1_0.qml|1|0|false"
<< "ComponentA|componenta-1_5.qml|1|5|false"
<< "ComponentB|componentb-1_5.qml|1|5|false")
@@ -309,6 +328,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< true;
QTest::newRow("designersupported-no")
@@ -318,6 +338,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("invalid-versioned-dependency")
@@ -327,6 +348,7 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< QStringList()
<< QStringList()
+ << QStringList()
<< false;
QTest::newRow("dependency")
@@ -335,6 +357,7 @@ void tst_qqmldirparser::parse_data()
<< (QStringList() << "foo|")
<< QStringList()
<< QStringList()
+ << QStringList()
<< (QStringList() << "bar||1|0|true")
<< false;
@@ -342,6 +365,7 @@ void tst_qqmldirparser::parse_data()
<< "classname/qmldir"
<< QStringList()
<< (QStringList() << "qtquick2plugin|")
+ << (QStringList() << "QtQuick2Plugin")
<< QStringList()
<< QStringList()
<< QStringList()
@@ -353,6 +377,7 @@ void tst_qqmldirparser::parse()
QFETCH(QString, file);
QFETCH(QStringList, errors);
QFETCH(QStringList, plugins);
+ QFETCH(QStringList, classnames);
QFETCH(QStringList, components);
QFETCH(QStringList, scripts);
QFETCH(QStringList, dependencies);
@@ -372,6 +397,7 @@ void tst_qqmldirparser::parse()
}
QCOMPARE(toStringList(p.plugins()), plugins);
+ QCOMPARE(p.classNames(), classnames);
QCOMPARE(toStringList(p.components()), components);
QCOMPARE(toStringList(p.scripts()), scripts);
QCOMPARE(toStringList(p.dependencies()), dependencies);
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index 348328f6a4..d1fa0991e8 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -33,6 +33,7 @@
#include <private/qv4staticvalue_p.h>
#include <private/qqmlirbuilder_p.h>
#include <private/qqmljsdiagnosticmessage_p.h>
+#include <private/qqmldirparser_p.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
@@ -128,7 +129,8 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con
+ QLatin1Char('.')
+ QString::number(importNode->version->version.minorVersion())
: QString();
- import[versionLiteral()] = versionString;
+ if (!versionString.isEmpty())
+ import[versionLiteral()] = versionString;
}
imports.append(import);
@@ -137,43 +139,90 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con
return imports;
}
+QVariantList findQmlImportsInQmlFile(const QString &filePath);
+QVariantList findQmlImportsInJavascriptFile(const QString &filePath);
+
+static QString versionSuffix(QTypeRevision version)
+{
+ return QLatin1Char(' ') + QString::number(version.majorVersion()) + QLatin1Char('.')
+ + QString::number(version.minorVersion());
+}
+
// Read the qmldir file, extract a list of plugins by
-// parsing the "plugin" and "classname" lines.
-QVariantMap pluginsForModulePath(const QString &modulePath) {
+// parsing the "plugin", "import", and "classname" directives.
+QVariantMap pluginsForModulePath(const QString &modulePath, const QString &version) {
QFile qmldirFile(modulePath + QLatin1String("/qmldir"));
- if (!qmldirFile.exists())
+ if (!qmldirFile.exists()) {
+ qWarning() << "qmldir file not found at" << modulePath;
return QVariantMap();
+ }
- qmldirFile.open(QIODevice::ReadOnly | QIODevice::Text);
-
- // A qml import may contain several plugins
- QString plugins;
- QString classnames;
- QStringList dependencies;
- QByteArray line;
- do {
- line = qmldirFile.readLine();
- if (line.startsWith("plugin")) {
- plugins += QString::fromUtf8(line.split(' ').at(1));
- plugins += QLatin1Char(' ');
- } else if (line.startsWith("classname")) {
- classnames += QString::fromUtf8(line.split(' ').at(1));
- classnames += QLatin1Char(' ');
- } else if (line.startsWith("depends")) {
- const QList<QByteArray> dep = line.split(' ');
- if (dep.length() != 3)
- std::cerr << "depends: expected 2 arguments: module identifier and version" << std::endl;
- else
- dependencies << QString::fromUtf8(dep[1]) + QLatin1Char(' ') + QString::fromUtf8(dep[2]).simplified();
- }
+ if (!qmldirFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning() << "qmldir file not found at" << modulePath;
+ return QVariantMap();
+ }
- } while (line.length() > 0);
+ QQmlDirParser parser;
+ parser.parse(QString::fromUtf8(qmldirFile.readAll()));
+ if (parser.hasError()) {
+ qWarning() << "qmldir file malformed at" << modulePath;
+ for (const auto error : parser.errors(QLatin1String("qmldir")))
+ qWarning() << error.message;
+ return QVariantMap();
+ }
QVariantMap pluginInfo;
- pluginInfo[pluginsLiteral()] = plugins.simplified();
- pluginInfo[classnamesLiteral()] = classnames.simplified();
- if (dependencies.length())
- pluginInfo[dependenciesLiteral()] = dependencies;
+
+ QStringList pluginNameList;
+ const auto plugins = parser.plugins();
+ for (const auto plugin : plugins)
+ pluginNameList.append(plugin.name);
+ pluginInfo[pluginsLiteral()] = pluginNameList.join(QLatin1Char(' '));
+
+ pluginInfo[classnamesLiteral()] = parser.classNames().join(QLatin1Char(' '));
+
+ QStringList importsAndDependencies;
+ const auto dependencies = parser.dependencies();
+ for (const auto &dependency : dependencies)
+ importsAndDependencies.append(dependency.typeName + versionSuffix(dependency.version));
+
+ const auto imports = parser.imports();
+ for (const auto &import : imports) {
+ if (import.isAutoImport) {
+ importsAndDependencies.append(
+ import.module + QLatin1Char(' ')
+ + (version.isEmpty() ? QString::fromLatin1("auto") : version));
+ } else if (import.version.isValid()) {
+ importsAndDependencies.append(import.module + versionSuffix(import.version));
+ } else {
+ importsAndDependencies.append(import.module);
+ }
+ }
+
+ QVariantList importsFromFiles;
+ const auto components = parser.components();
+ for (const auto component : components) {
+ importsFromFiles
+ += findQmlImportsInQmlFile(modulePath + QLatin1Char('/') + component.fileName);
+ }
+ const auto scripts = parser.scripts();
+ for (const auto script : scripts) {
+ importsFromFiles
+ += findQmlImportsInJavascriptFile(modulePath + QLatin1Char('/') + script.fileName);
+ }
+
+ for (const QVariant &import : importsFromFiles) {
+ const QVariantMap details = qvariant_cast<QVariantMap>(import);
+ if (details.value(typeLiteral()) != moduleLiteral())
+ continue;
+ const QString name = details.value(nameLiteral()).toString();
+ const QString version = details.value(versionLiteral()).toString();
+ importsAndDependencies.append(
+ version.isEmpty() ? name : (name + QLatin1Char(' ') + version));
+ }
+
+ if (!importsAndDependencies.isEmpty())
+ pluginInfo[dependenciesLiteral()] = importsAndDependencies;
return pluginInfo;
}
@@ -238,13 +287,15 @@ QVariantList findPathsForModuleImports(const QVariantList &imports)
for (int i = 0; i < importsCopy.length(); ++i) {
QVariantMap import = qvariant_cast<QVariantMap>(importsCopy.at(i));
if (import.value(typeLiteral()) == moduleLiteral()) {
+ const QString version = import.value(versionLiteral()).toString();
const QPair<QString, QString> paths =
- resolveImportPath(import.value(nameLiteral()).toString(), import.value(versionLiteral()).toString());
+ resolveImportPath(import.value(nameLiteral()).toString(), version);
+ QVariantMap plugininfo;
if (!paths.first.isEmpty()) {
import.insert(pathLiteral(), paths.first);
import.insert(relativePathLiteral(), paths.second);
+ plugininfo = pluginsForModulePath(paths.first, version);
}
- QVariantMap plugininfo = pluginsForModulePath(import.value(pathLiteral()).toString());
QString plugins = plugininfo.value(pluginsLiteral()).toString();
QString classnames = plugininfo.value(classnamesLiteral()).toString();
if (!plugins.isEmpty())
@@ -254,15 +305,20 @@ QVariantList findPathsForModuleImports(const QVariantList &imports)
if (plugininfo.contains(dependenciesLiteral())) {
const QStringList dependencies = plugininfo.value(dependenciesLiteral()).toStringList();
for (const QString &line : dependencies) {
- const auto dep = QStringView{line}.split(QLatin1Char(' '));
+ const auto dep = QStringView{line}.split(QLatin1Char(' '), Qt::SkipEmptyParts);
+ const QString name = dep[0].toString();
QVariantMap depImport;
depImport[typeLiteral()] = moduleLiteral();
- depImport[nameLiteral()] = dep[0].toString();
- depImport[versionLiteral()] = dep[1].toString();
- importsCopy.append(depImport);
+ depImport[nameLiteral()] = name;
+ if (dep.length() > 1)
+ depImport[versionLiteral()] = dep[1].toString();
+
+ if (!importsCopy.contains(depImport))
+ importsCopy.append(depImport);
}
}
}
+ import.remove(versionLiteral());
done.append(import);
}
return done;
@@ -326,7 +382,8 @@ struct ImportCollector : public QQmlJS::Directives
} else {
entry[typeLiteral()] = moduleLiteral();
entry[nameLiteral()] = uri;
- entry[versionLiteral()] = version;
+ if (!version.isEmpty())
+ entry[versionLiteral()] = version;
}
imports << entry;
@@ -452,18 +509,6 @@ QVariantList findQmlImportsInDirectory(const QString &qmlDir)
return ret;
}
-QSet<QString> importModulePaths(const QVariantList &imports) {
- QSet<QString> ret;
- for (const QVariant &importVariant : imports) {
- QVariantMap import = qvariant_cast<QVariantMap>(importVariant);
- QString path = import.value(pathLiteral()).toString();
- QString type = import.value(typeLiteral()).toString();
- if (type == moduleLiteral() && !path.isEmpty())
- ret.insert(QDir(path).canonicalPath());
- }
- return ret;
-}
-
// Find qml imports recursively from a root set of qml files.
// The directories in qmlDirs are searched recursively.
// The files in qmlFiles parsed directly.
@@ -483,23 +528,6 @@ QVariantList findQmlImportsRecursively(const QStringList &qmlDirs, const QString
ret = mergeImports(ret, imports);
}
- // Get the paths to the imports found in the app qml
- QSet<QString> toVisit = importModulePaths(ret);
-
- // Recursively scan for import dependencies.
- QSet<QString> visited;
- while (!toVisit.isEmpty()) {
- QString qmlDir = *toVisit.begin();
- toVisit.erase(toVisit.begin());
- visited.insert(qmlDir);
-
- QVariantList imports = findQmlImportsInDirectory(qmlDir);
- ret = mergeImports(ret, imports);
-
- QSet<QString> candidatePaths = importModulePaths(ret);
- candidatePaths.subtract(visited);
- toVisit.unite(candidatePaths);
- }
return ret;
}