diff options
Diffstat (limited to 'tests/auto/qml/qmllint')
85 files changed, 1839 insertions, 101 deletions
diff --git a/tests/auto/qml/qmllint/CMakeLists.txt b/tests/auto/qml/qmllint/CMakeLists.txt index 422e7a08b3..5a3e2d9c0d 100644 --- a/tests/auto/qml/qmllint/CMakeLists.txt +++ b/tests/auto/qml/qmllint/CMakeLists.txt @@ -7,6 +7,12 @@ ## tst_qmllint Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmllint LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -42,9 +48,15 @@ qt_internal_extend_target(tst_qmllint CONDITION NOT ANDROID AND NOT IOS QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) +if (TARGET qmllint) + add_dependencies(tst_qmllint Qt::qmllint) +endif() + if (TARGET qmljsrootgen) qt_internal_extend_target(tst_qmllint DEFINES QT_QMLJSROOTGEN_PRESENT ) + + add_dependencies(tst_qmllint Qt::qmljsrootgen) endif() diff --git a/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/A.qml b/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/A.qml new file mode 100644 index 0000000000..4141884af9 --- /dev/null +++ b/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/A.qml @@ -0,0 +1,5 @@ +import QtQuick + +Item { + property string myProperty +} diff --git a/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/qmldir b/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/qmldir new file mode 100644 index 0000000000..b6e958d657 --- /dev/null +++ b/tests/auto/qml/qmllint/data/ImportPath/ModuleInImportPath/qmldir @@ -0,0 +1,2 @@ +module ModuleInImportPath +A 1.0 A.qml diff --git a/tests/auto/qml/qmllint/data/IsNotAnEntryOfEnum.qml b/tests/auto/qml/qmllint/data/IsNotAnEntryOfEnum.qml new file mode 100644 index 0000000000..8b20fe9ae9 --- /dev/null +++ b/tests/auto/qml/qmllint/data/IsNotAnEntryOfEnum.qml @@ -0,0 +1,14 @@ +import QtQuick + +Item { + id: item + visible: true + + enum Mode { + Hours, + Minutes + } + + property int mode: item.Mode.Hours + property string s: item.mode === IsNotAnEntryOfEnum.Mode.Hour ? "green" : "tomato" +} diff --git a/tests/auto/qml/qmllint/data/LocaleTest/localeTest.qmltypes b/tests/auto/qml/qmllint/data/LocaleTest/localeTest.qmltypes new file mode 100644 index 0000000000..b5baa22357 --- /dev/null +++ b/tests/auto/qml/qmllint/data/LocaleTest/localeTest.qmltypes @@ -0,0 +1,31 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by qmltyperegistrar. + +Module { + Component { + file: "AppManager.h" + name: "AppManager" + accessSemantics: "reference" + prototype: "QObject" + exports: ["LocaleTest/AppManager 1.0"] + isCreatable: false + isSingleton: true + exportMetaObjectRevisions: [256] + Property { + name: "primaryLocale" + type: "QLocale" + read: "getPrimaryLocale" + notify: "primaryLocaleChanged" + index: 0 + isReadonly: true + } + Signal { + name: "primaryLocaleChanged" + Parameter { type: "QLocale" } + } + } +} diff --git a/tests/auto/qml/qmllint/data/LocaleTest/qmldir b/tests/auto/qml/qmllint/data/LocaleTest/qmldir new file mode 100644 index 0000000000..2dc0799d5b --- /dev/null +++ b/tests/auto/qml/qmllint/data/LocaleTest/qmldir @@ -0,0 +1,3 @@ +module LocaleTest +typeinfo localeTest.qmltypes +depends QtQml diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/Outer.qml b/tests/auto/qml/qmllint/data/MultiDirectory/Outer.qml new file mode 100644 index 0000000000..5dda9a4d12 --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/Outer.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + property int o: 12 +} diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/multi.qrc b/tests/auto/qml/qmllint/data/MultiDirectory/multi.qrc new file mode 100644 index 0000000000..b6df21ec4c --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/multi.qrc @@ -0,0 +1,10 @@ +<RCC> + <qresource prefix="/qt/qml/MultiDirectory"> + <file>qmldir</file> + <file>Outer.qml</file> + <file>qml/qmldir</file> + <file>qml/Inner.qml</file> + <file>qml/pages/qmldir</file> + <file>qml/pages/Page.qml</file> + </qresource> +</RCC> diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qml/Inner.qml b/tests/auto/qml/qmllint/data/MultiDirectory/qml/Inner.qml new file mode 100644 index 0000000000..01b7c331d9 --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/qml/Inner.qml @@ -0,0 +1,5 @@ +import QtQml + +Outer { + o: 25 +} diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/Page.qml b/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/Page.qml new file mode 100644 index 0000000000..7efc889fb1 --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/Page.qml @@ -0,0 +1,5 @@ +import QtQml + +Inner { + o: 32 +} diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/qmldir b/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/qmldir new file mode 100644 index 0000000000..56ef19d41b --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/qml/pages/qmldir @@ -0,0 +1 @@ +prefer :/qt/qml/MultiDirectory/ diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qml/qmldir b/tests/auto/qml/qmllint/data/MultiDirectory/qml/qmldir new file mode 100644 index 0000000000..56ef19d41b --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/qml/qmldir @@ -0,0 +1 @@ +prefer :/qt/qml/MultiDirectory/ diff --git a/tests/auto/qml/qmllint/data/MultiDirectory/qmldir b/tests/auto/qml/qmllint/data/MultiDirectory/qmldir new file mode 100644 index 0000000000..699132dc4f --- /dev/null +++ b/tests/auto/qml/qmllint/data/MultiDirectory/qmldir @@ -0,0 +1,5 @@ +module MultiDirectory +prefer :/qt/qml/MultiDirectory +Outer 1.0 Outer.qml +Inner 1.0 qml/Inner.qml +Page 1.0 qml/pages/Page.qml diff --git a/tests/auto/qml/qmllint/data/MyStyle/MyStyle.qmltypes b/tests/auto/qml/qmllint/data/MyStyle/MyStyle.qmltypes new file mode 100644 index 0000000000..92c1b0953e --- /dev/null +++ b/tests/auto/qml/qmllint/data/MyStyle/MyStyle.qmltypes @@ -0,0 +1,112 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by qmltyperegistrar. + +Module { + Component { + file: "mystyle.h" + name: "MyStyle" + accessSemantics: "reference" + prototype: "QQuickAttachedPropertyPropagator" + exports: ["MyStyle/MyStyle 1.0", "MyStyle/MyStyle 254.0"] + isCreatable: false + exportMetaObjectRevisions: [256, 65024] + attachedType: "MyStyle" + Enum { + name: "Theme" + values: ["Light", "Dark"] + } + Property { + name: "theme" + type: "Theme" + read: "theme" + write: "setTheme" + reset: "resetTheme" + notify: "themeChanged" + index: 0 + isFinal: true + } + Property { + name: "windowColor" + type: "QColor" + read: "windowColor" + notify: "themeChanged" + index: 1 + isReadonly: true + isFinal: true + } + Property { + name: "windowTextColor" + type: "QColor" + read: "windowTextColor" + notify: "themeChanged" + index: 2 + isReadonly: true + isFinal: true + } + Property { + name: "buttonColor" + type: "QColor" + read: "buttonColor" + notify: "themeChanged" + index: 3 + isReadonly: true + isFinal: true + } + Property { + name: "buttonTextColor" + type: "QColor" + read: "buttonTextColor" + notify: "themeChanged" + index: 4 + isReadonly: true + isFinal: true + } + Property { + name: "toolBarColor" + type: "QColor" + read: "toolBarColor" + notify: "themeChanged" + index: 5 + isReadonly: true + isFinal: true + } + Property { + name: "popupColor" + type: "QColor" + read: "popupColor" + notify: "themeChanged" + index: 6 + isReadonly: true + isFinal: true + } + Property { + name: "popupBorderColor" + type: "QColor" + read: "popupBorderColor" + notify: "themeChanged" + index: 7 + isReadonly: true + isFinal: true + } + Property { + name: "backgroundDimColor" + type: "QColor" + read: "backgroundDimColor" + notify: "themeChanged" + index: 8 + isReadonly: true + isFinal: true + } + Signal { name: "themeChanged" } + } + Component { + file: "qquickattachedpropertypropagator.h" + name: "QQuickAttachedPropertyPropagator" + accessSemantics: "reference" + prototype: "QObject" + } +} diff --git a/tests/auto/qml/qmllint/data/MyStyle/ToolBar.qml b/tests/auto/qml/qmllint/data/MyStyle/ToolBar.qml new file mode 100644 index 0000000000..5920797d9b --- /dev/null +++ b/tests/auto/qml/qmllint/data/MyStyle/ToolBar.qml @@ -0,0 +1,12 @@ +import QtQuick +import QtQuick.Templates as T +import MyStyle + +T.ToolBar { + id: control + + property color c: MyStyle.toolBarColor + background: Rectangle { + color: MyStyle.toolBarColor + } +} diff --git a/tests/auto/qml/qmllint/data/MyStyle/qmldir b/tests/auto/qml/qmllint/data/MyStyle/qmldir new file mode 100644 index 0000000000..8cc4246f7c --- /dev/null +++ b/tests/auto/qml/qmllint/data/MyStyle/qmldir @@ -0,0 +1,4 @@ +module MyStyle +typeinfo MyStyle.qmltypes +ToolBar 254.0 ToolBar.qml +import QtQuick.Controls.Material diff --git a/tests/auto/qml/qmllint/data/NeedImportPath.qml b/tests/auto/qml/qmllint/data/NeedImportPath.qml new file mode 100644 index 0000000000..0a63b58f7c --- /dev/null +++ b/tests/auto/qml/qmllint/data/NeedImportPath.qml @@ -0,0 +1,5 @@ +import ModuleInImportPath + +A { + myProperty: "Hello World" +} diff --git a/tests/auto/qml/qmllint/data/Qtbug111015/qmldir b/tests/auto/qml/qmllint/data/Qtbug111015/qmldir new file mode 100644 index 0000000000..3bf1d48e13 --- /dev/null +++ b/tests/auto/qml/qmllint/data/Qtbug111015/qmldir @@ -0,0 +1,3 @@ +module Qtbug111015 +typeinfo qtbug111015.qmltypes +import QtQml diff --git a/tests/auto/qml/qmllint/data/Qtbug111015/qtbug111015.qmltypes b/tests/auto/qml/qmllint/data/Qtbug111015/qtbug111015.qmltypes new file mode 100644 index 0000000000..7de521a379 --- /dev/null +++ b/tests/auto/qml/qmllint/data/Qtbug111015/qtbug111015.qmltypes @@ -0,0 +1,20 @@ +import QtQuick.tooling 1.2 + +Module { + Component { + file: "typewithjsonobjectlist.h" + name: "TypeWithJsonObjectList" + exports: ["QmlLintTestLib/TypeWithJsonObjectList 1.0"] + accessSemantics: "reference" + prototype: "QObject" + Property { name: "jsonObjectList"; type: "QJsonObject"; isList: true; read: "getJsonObjectList"; index: 0; isReadonly: true } + } + Component { + file: "typewithjsonarray.h" + name: "TypeWithJsonArray" + exports: ["QmlLintTestLib/TypeWithJsonArray 1.0"] + accessSemantics: "reference" + prototype: "QObject" + Property { name: "jsonArray"; type: "QJsonArray"; read: "getJsonArray"; index: 0; isReadonly: true } + } +} diff --git a/tests/auto/qml/qmllint/data/SharedFunctions.js b/tests/auto/qml/qmllint/data/SharedFunctions.js new file mode 100644 index 0000000000..3398d2d6d7 --- /dev/null +++ b/tests/auto/qml/qmllint/data/SharedFunctions.js @@ -0,0 +1,5 @@ +.pragma library + +function setColorAlpha(color, alpha) { + return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha) +} diff --git a/tests/auto/qml/qmllint/data/StringToDateTime/qmldir b/tests/auto/qml/qmllint/data/StringToDateTime/qmldir new file mode 100644 index 0000000000..761f613496 --- /dev/null +++ b/tests/auto/qml/qmllint/data/StringToDateTime/qmldir @@ -0,0 +1,3 @@ +module StringToDateTime +typeinfo stringToDateTime.qmltypes +import QtQml diff --git a/tests/auto/qml/qmllint/data/StringToDateTime/stringToDateTime.qmltypes b/tests/auto/qml/qmllint/data/StringToDateTime/stringToDateTime.qmltypes new file mode 100644 index 0000000000..e0f13fa2ec --- /dev/null +++ b/tests/auto/qml/qmllint/data/StringToDateTime/stringToDateTime.qmltypes @@ -0,0 +1,14 @@ +import QtQuick.tooling 1.2 +Module { + Component { + file: "stringToQDateTime.h" + name: "StringToDateTimeComponent" + exports: ["QmlLintTestLib/StringToDateTime 1.0"] + exportMetaObjectRevisions: [256] + accessSemantics: "reference" + prototype: "QObject" + Property { name: "aDate"; type: "QDate"; read: "getADate"; write: "setADate" } + Property { name: "aTime"; type: "QTime"; read: "getATime"; write: "setATime" } + Property { name: "aDateTime"; type: "QDateTime"; read: "getADateTime"; write: "setADateTime" } + } +} diff --git a/tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes b/tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes index 04ca30c184..77ad6a3cef 100644 --- a/tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes +++ b/tests/auto/qml/qmllint/data/TestTypes/testtypes.qmltypes @@ -178,4 +178,24 @@ Module { exportMetaObjectRevisions: [256] Method { name: "createObject"; isJavaScriptFunction: true } } + Component { + file: "variantMapLookup.h" + name: "VariantMapLookupFoo" + prototype: "QObject" + exports: ["TestTypes/VariantMapLookupFoo 1.0"] + exportMetaObjectRevisions: [256] + Property { + name: "data" + type: "QVariantMap" + read: "data" + index: 0 + } + } + Component { + file: "foo.h" + name: "Foo" + prototype: "QObject" + exports: ["TestTypes/Foo 1.0"] + Method { name: "print" } + } } diff --git a/tests/auto/qml/qmllint/data/Things/plugins.qmltypes b/tests/auto/qml/qmllint/data/Things/plugins.qmltypes index 9d36d91a90..3b0da602ac 100644 --- a/tests/auto/qml/qmllint/data/Things/plugins.qmltypes +++ b/tests/auto/qml/qmllint/data/Things/plugins.qmltypes @@ -12,6 +12,7 @@ Module { exports: ["Things/SomethingEntirelyStrange 1.0"] Enum { name: "AnEnum" + isScoped: true values: { "AAA": 0, "BBB": 1, @@ -20,7 +21,7 @@ Module { } Enum { name: "TheEnum" - scoped: false + isScoped: false values: { "V1": 0, "V2": 1 @@ -96,4 +97,21 @@ Module { Property { name: "foo"; type: "string" } hasCustomParser: true } + Component { + file: "mediaplayer-qml.h" + name: "MediaPlayerStateMachine" + accessSemantics: "value" + exports: ["Mediaplayer/MediaPlayerStateMachine 1.0"] + isCreatable: false + exportMetaObjectRevisions: [256] + } + Component { + file: "constinvokable.h" + name: "ConstInvokable" + accessSemantics: "reference" + prototype: "QObject" + exports: ["Things/ConstInvokable 1.0"] + exportMetaObjectRevisions: [256] + Method { name: "getObject"; type: "QObject"; isPointer: true; isTypeConstant: true } + } } diff --git a/tests/auto/qml/qmllint/data/UnqualifiedInStoreSloppy.qml b/tests/auto/qml/qmllint/data/UnqualifiedInStoreSloppy.qml new file mode 100644 index 0000000000..197b74fa60 --- /dev/null +++ b/tests/auto/qml/qmllint/data/UnqualifiedInStoreSloppy.qml @@ -0,0 +1,12 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 + +Window { + Rectangle { + property real divisor: 0 + + Timer { + onTriggered: divisor = 1 + } + } +} diff --git a/tests/auto/qml/qmllint/data/UnqualifiedInStoreStrict.qml b/tests/auto/qml/qmllint/data/UnqualifiedInStoreStrict.qml new file mode 100644 index 0000000000..198e2146f5 --- /dev/null +++ b/tests/auto/qml/qmllint/data/UnqualifiedInStoreStrict.qml @@ -0,0 +1,12 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 + +Window { + Rectangle { + property real divisor: 0 + + Timer { + onTriggered: function() {"use strict"; divisor = 1 } + } + } +} diff --git a/tests/auto/qml/qmllint/data/addressableValue.qml b/tests/auto/qml/qmllint/data/addressableValue.qml new file mode 100644 index 0000000000..9b93728ed8 --- /dev/null +++ b/tests/auto/qml/qmllint/data/addressableValue.qml @@ -0,0 +1,8 @@ +pragma ValueTypeBehavior: Addressable + +import QtQml +import scripts + +QtObject { + property var v: "red" as vvv +} diff --git a/tests/auto/qml/qmllint/data/attachedImportUse.qml b/tests/auto/qml/qmllint/data/attachedImportUse.qml new file mode 100644 index 0000000000..56b4e2bf7c --- /dev/null +++ b/tests/auto/qml/qmllint/data/attachedImportUse.qml @@ -0,0 +1,7 @@ +import QtQml +import TestTypes + +QtObject { + id: control + objectName: control.BirthdayParty.objectName +} diff --git a/tests/auto/qml/qmllint/data/badAliasNotAnExpression.qml b/tests/auto/qml/qmllint/data/badAliasNotAnExpression.qml new file mode 100644 index 0000000000..cdc2b28c07 --- /dev/null +++ b/tests/auto/qml/qmllint/data/badAliasNotAnExpression.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +Item { + + property alias innerObj: { + id: inner + } + +} diff --git a/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes b/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes new file mode 100644 index 0000000000..96f98dd14e --- /dev/null +++ b/tests/auto/qml/qmllint/data/bad_builtins/builtins.qmltypes @@ -0,0 +1,513 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -builtins' + +Module { + dependencies: [] + Component { + name: "Qt" + Enum { + name: "GlobalColor" + values: { + "color0": 0, + "color1": 1, + "black": 2, + "white": 3, + "darkGray": 4, + "gray": 5, + "lightGray": 6, + "red": 7, + "green": 8, + "blue": 9, + "cyan": 10, + "magenta": 11, + "yellow": 12, + "darkRed": 13, + "darkGreen": 14, + "darkBlue": 15, + "darkCyan": 16, + "darkMagenta": 17, + "darkYellow": 18, + "transparent": 19 + } + } + Enum { + name: "KeyboardModifiers" + values: { + "NoModifier": 0, + "ShiftModifier": 33554432, + "ControlModifier": 67108864, + "AltModifier": 134217728, + "MetaModifier": 268435456, + "KeypadModifier": 536870912, + "GroupSwitchModifier": 1073741824, + "KeyboardModifierMask": -33554432 + } + } + Enum { + name: "MouseButtons" + values: { + "NoButton": 0, + "LeftButton": 1, + "RightButton": 2, + "MidButton": 4, // For backwards compatibility + "MiddleButton": 4, + "BackButton": 8, + "XButton1": 8, + "ExtraButton1": 8, + "ForwardButton": 16, + "XButton2": 16, + "ExtraButton2": 16, + "TaskButton": 32, + "ExtraButton3": 32, + "ExtraButton4": 64, + "ExtraButton5": 128, + "ExtraButton6": 256, + "ExtraButton7": 512, + "ExtraButton8": 1024, + "ExtraButton9": 2048, + "ExtraButton10": 4096, + "ExtraButton11": 8192, + "ExtraButton12": 16384, + "ExtraButton13": 32768, + "ExtraButton14": 65536, + "ExtraButton15": 131072, + "ExtraButton16": 262144, + "ExtraButton17": 524288, + "ExtraButton18": 1048576, + "ExtraButton19": 2097152, + "ExtraButton20": 4194304, + "ExtraButton21": 8388608, + "ExtraButton22": 16777216, + "ExtraButton23": 33554432, + "ExtraButton24": 67108864, + "AllButtons": 134217727, + "MaxMouseButton": 67108864, + "MouseButtonMask": -1 + } + } + Enum { + name: "Orientation" + values: { + "Horizontal": 1, + "Vertical": 2 + } + } + Enum { + name: "Orientations" + values: { + "Horizontal": 1, + "Vertical": 2 + } + } + Enum { + name: "FocusPolicy" + values: { + "NoFocus": 0, + "TabFocus": 1, + "ClickFocus": 2, + "StrongFocus": 11, + "WheelFocus": 15 + } + } + Enum { + name: "TabFocusBehavior" + values: { + "NoTabFocus": 0, + "TabFocusTextControls": 1, + "TabFocusListControls": 2, + "TabFocusAllControls": 255 + } + } + Enum { + name: "SortOrder" + values: { + "AscendingOrder": 0, + "DescendingOrder": 1 + } + } + Enum { + name: "SplitBehavior" + values: { + "KeepEmptyParts": 0, + "SkipEmptyParts": 1 + } + } + Enum { + name: "Alignment" + values: { + "AlignLeft": 1, + "AlignLeading": 1, + "AlignRight": 2, + "AlignTrailing": 2, + "AlignHCenter": 4, + "AlignJustify": 8, + "AlignAbsolute": 16, + "AlignHorizontal_Mask": 31, + "AlignTop": 32, + "AlignBottom": 64, + "AlignVCenter": 128, + "AlignBaseline": 256, + "AlignVertical_Mask": 480, + "AlignCenter": 132 + } + } + Enum { + name: "TextFlag" + values: { + "TextSingleLine": 256, + "TextDontClip": 512, + "TextExpandTabs": 1024, + "TextShowMnemonic": 2048, + "TextWordWrap": 4096, + "TextWrapAnywhere": 8192, + "TextDontPrint": 16384, + "TextIncludeTrailingSpaces": 134217728, + "TextHideMnemonic": 32768, + "TextJustificationForced": 65536, + "TextForceLeftToRight": 131072, + "TextForceRightToLeft": 262144, + "TextLongestVariant": 524288, + "TextBypassShaping": 1048576 + } + } + Enum { + name: "TextElideMode" + values: { + "ElideLeft": 0, + "ElideRight": 1, + "ElideMiddle": 2, + "ElideNone": 3 + } + } + Enum { + name: "WindowType" + values: { + "Widget": 0, + "Window": 1, + "Dialog": 3, + "Sheet": 5, + "Drawer": 7, + "Popup": 9, + "Tool": 11, + "ToolTip": 13, + "SplashScreen": 15, + "Desktop": 17, + "SubWindow": 18, + "ForeignWindow": 33, + "CoverWindow": 65, + "WindowType_Mask": 255, + "MSWindowsFixedSizeDialogHint": 256, + "MSWindowsOwnDC": 512, + "BypassWindowManagerHint": 1024, + "X11BypassWindowManagerHint": 1024, + "FramelessWindowHint": 2048, + "WindowTitleHint": 4096, + "WindowSystemMenuHint": 8192, + "WindowMinimizeButtonHint": 16384, + "WindowMaximizeButtonHint": 32768, + "WindowMinMaxButtonsHint": 49152, + "WindowContextHelpButtonHint": 65536, + "WindowShadeButtonHint": 131072, + "WindowStaysOnTopHint": 262144, + "WindowTransparentForInput": 524288, + "WindowOverridesSystemGestures": 1048576, + "WindowDoesNotAcceptFocus": 2097152, + "MaximizeUsingFullscreenGeometryHint": 4194304, + "CustomizeWindowHint": 33554432, + "WindowStaysOnBottomHint": 67108864, + "WindowCloseButtonHint": 134217728, + "MacWindowToolBarButtonHint": 268435456, + "BypassGraphicsProxyWidget": 536870912, + "NoDropShadowWindowHint": 1073741824, + "WindowFullscreenButtonHint": -2147483648 + } + } + Enum { + name: "WindowFlags" + values: { + "Widget": 0, + "Window": 1, + "Dialog": 3, + "Sheet": 5, + "Drawer": 7, + "Popup": 9, + "Tool": 11, + "ToolTip": 13, + "SplashScreen": 15, + "Desktop": 17, + "SubWindow": 18, + "ForeignWindow": 33, + "CoverWindow": 65, + "WindowType_Mask": 255, + "MSWindowsFixedSizeDialogHint": 256, + "MSWindowsOwnDC": 512, + "BypassWindowManagerHint": 1024, + "X11BypassWindowManagerHint": 1024, + "FramelessWindowHint": 2048, + "WindowTitleHint": 4096, + "WindowSystemMenuHint": 8192, + "WindowMinimizeButtonHint": 16384, + "WindowMaximizeButtonHint": 32768, + "WindowMinMaxButtonsHint": 49152, + "WindowContextHelpButtonHint": 65536, + "WindowShadeButtonHint": 131072, + "WindowStaysOnTopHint": 262144, + "WindowTransparentForInput": 524288, + "WindowOverridesSystemGestures": 1048576, + "WindowDoesNotAcceptFocus": 2097152, + "MaximizeUsingFullscreenGeometryHint": 4194304, + "CustomizeWindowHint": 33554432, + "WindowStaysOnBottomHint": 67108864, + "WindowCloseButtonHint": 134217728, + "MacWindowToolBarButtonHint": 268435456, + "BypassGraphicsProxyWidget": 536870912, + "NoDropShadowWindowHint": 1073741824, + "WindowFullscreenButtonHint": -2147483648 + } + } + Enum { + name: "WindowState" + values: { + "WindowNoState": 0, + "WindowMinimized": 1, + "WindowMaximized": 2, + "WindowFullScreen": 4, + "WindowActive": 8 + } + } + Enum { + name: "WindowStates" + values: { + "WindowNoState": 0, + "WindowMinimized": 1, + "WindowMaximized": 2, + "WindowFullScreen": 4, + "WindowActive": 8 + } + } + Enum { + name: "ApplicationState" + values: { + "ApplicationSuspended": 0, + "ApplicationHidden": 1, + "ApplicationInactive": 2, + "ApplicationActive": 4 + } + } + Enum { + name: "ScreenOrientation" + values: { + "PrimaryOrientation": 0, + "PortraitOrientation": 1, + "LandscapeOrientation": 2, + "InvertedPortraitOrientation": 4, + "InvertedLandscapeOrientation": 8 + } + } + Enum { + name: "ScreenOrientations" + values: { + "PrimaryOrientation": 0, + "PortraitOrientation": 1, + "LandscapeOrientation": 2, + "InvertedPortraitOrientation": 4, + "InvertedLandscapeOrientation": 8 + } + } + Enum { + name: "WidgetAttribute" + values: { + "WA_Disabled": 0, + "WA_UnderMouse": 1, + "WA_MouseTracking": 2, + "WA_ContentsPropagated": 3, + "WA_OpaquePaintEvent": 4, + "WA_NoBackground": 4, + "WA_StaticContents": 5, + "WA_LaidOut": 7, + "WA_PaintOnScreen": 8, + "WA_NoSystemBackground": 9, + "WA_UpdatesDisabled": 10, + "WA_Mapped": 11, + "WA_MacNoClickThrough": 12, + "WA_InputMethodEnabled": 14, + "WA_WState_Visible": 15, + "WA_WState_Hidden": 16, + "WA_ForceDisabled": 32, + "WA_KeyCompression": 33, + "WA_PendingMoveEvent": 34, + "WA_PendingResizeEvent": 35, + "WA_SetPalette": 36, + "WA_SetFont": 37, + "WA_SetCursor": 38, + "WA_NoChildEventsFromChildren": 39, + "WA_WindowModified": 41, + "WA_Resized": 42, + "WA_Moved": 43, + "WA_PendingUpdate": 44, + "WA_InvalidSize": 45, + "WA_MacBrushedMetal": 46, + "WA_MacMetalStyle": 46, + "WA_CustomWhatsThis": 47, + "WA_LayoutOnEntireRect": 48, + "WA_OutsideWSRange": 49, + "WA_GrabbedShortcut": 50, + "WA_TransparentForMouseEvents": 51, + "WA_PaintUnclipped": 52, + "WA_SetWindowIcon": 53, + "WA_NoMouseReplay": 54, + "WA_DeleteOnClose": 55, + "WA_RightToLeft": 56, + "WA_SetLayoutDirection": 57, + "WA_NoChildEventsForParent": 58, + "WA_ForceUpdatesDisabled": 59, + "WA_WState_Created": 60, + "WA_WState_CompressKeys": 61, + "WA_WState_InPaintEvent": 62, + "WA_WState_Reparented": 63, + "WA_WState_ConfigPending": 64, + "WA_WState_Polished": 66, + "WA_WState_DND": 67, + "WA_WState_OwnSizePolicy": 68, + "WA_WState_ExplicitShowHide": 69, + "WA_ShowModal": 70, + "WA_MouseNoMask": 71, + "WA_GroupLeader": 72, + "WA_NoMousePropagation": 73, + "WA_Hover": 74, + "WA_InputMethodTransparent": 75, + "WA_QuitOnClose": 76, + "WA_KeyboardFocusChange": 77, + "WA_AcceptDrops": 78, + "WA_DropSiteRegistered": 79, + "WA_ForceAcceptDrops": 79, + "WA_WindowPropagation": 80, + "WA_NoX11EventCompression": 81, + "WA_TintedBackground": 82, + "WA_X11OpenGLOverlay": 83, + "WA_AlwaysShowToolTips": 84, + "WA_MacOpaqueSizeGrip": 85, + "WA_SetStyle": 86, + "WA_SetLocale": 87, + "WA_MacShowFocusRect": 88, + "WA_MacNormalSize": 89, + "WA_MacSmallSize": 90, + "WA_MacMiniSize": 91, + "WA_LayoutUsesWidgetRect": 92, + "WA_StyledBackground": 93, + "WA_MSWindowsUseDirect3D": 94, + "WA_CanHostQMdiSubWindowTitleBar": 95, + "WA_MacAlwaysShowToolWindow": 96, + "WA_StyleSheet": 97, + "WA_ShowWithoutActivating": 98, + "WA_X11BypassTransientForHint": 99, + "WA_NativeWindow": 100, + "WA_DontCreateNativeAncestors": 101, + "WA_MacVariableSize": 102, + "WA_DontShowOnScreen": 103, + "WA_X11NetWmWindowTypeDesktop": 104, + "WA_X11NetWmWindowTypeDock": 105, + "WA_X11NetWmWindowTypeToolBar": 106, + "WA_X11NetWmWindowTypeMenu": 107, + "WA_X11NetWmWindowTypeUtility": 108, + "WA_X11NetWmWindowTypeSplash": 109, + "WA_X11NetWmWindowTypeDialog": 110, + "WA_X11NetWmWindowTypeDropDownMenu": 111, + "WA_X11NetWmWindowTypePopupMenu": 112, + "WA_X11NetWmWindowTypeToolTip": 113, + "WA_X11NetWmWindowTypeNotification": 114, + "WA_X11NetWmWindowTypeCombo": 115, + "WA_X11NetWmWindowTypeDND": 116, + "WA_MacFrameworkScaled": 117, + "WA_SetWindowModality": 118, + "WA_WState_WindowOpacitySet": 119, + "WA_TranslucentBackground": 120, + "WA_AcceptTouchEvents": 121, + "WA_WState_AcceptedTouchBeginEvent": 122, + "WA_TouchPadAcceptSingleTouchEvents": 123, + "WA_X11DoNotAcceptFocus": 126, + "WA_MacNoShadow": 127, + "WA_AlwaysStackOnTop": 128, + "WA_TabletTracking": 129, + "WA_ContentsMarginsRespectsSafeArea": 130, + "WA_StyleSheetTarget": 131, + "WA_AttributeCount": 132 + } + } + Enum { + name: "ApplicationAttribute" + values: { + "AA_ImmediateWidgetCreation": 0, + "AA_MSWindowsUseDirect3DByDefault": 1, + "AA_DontShowIconsInMenus": 2, + "AA_NativeWindows": 3, + "AA_DontCreateNativeWidgetSiblings": 4, + "AA_PluginApplication": 5, + "AA_MacPluginApplication": 5, + "AA_DontUseNativeMenuBar": 6, + "AA_MacDontSwapCtrlAndMeta": 7, + "AA_Use96Dpi": 8, + "AA_X11InitThreads": 10, + "AA_SynthesizeTouchForUnhandledMouseEvents": 11, + "AA_SynthesizeMouseForUnhandledTouchEvents": 12, + "AA_UseHighDpiPixmaps": 13, + "AA_ForceRasterWidgets": 14, + "AA_UseDesktopOpenGL": 15, + "AA_UseOpenGLES": 16, + "AA_UseSoftwareOpenGL": 17, + "AA_ShareOpenGLContexts": 18, + "AA_SetPalette": 19, + "AA_EnableHighDpiScaling": 20, + "AA_DisableHighDpiScaling": 21, + "AA_UseStyleSheetPropagationInWidgetStyles": 22, + "AA_DontUseNativeDialogs": 23, + "AA_SynthesizeMouseForUnhandledTabletEvents": 24, + "AA_CompressHighFrequencyEvents": 25, + "AA_DontCheckOpenGLContextThreadAffinity": 26, + "AA_DisableShaderDiskCache": 27, + "AA_DontShowShortcutsInContextMenus": 28, + "AA_CompressTabletEvents": 29, + "AA_DisableWindowContextHelpButton": 30, + "AA_DisableSessionManager": 31, + "AA_AttributeCount": 32 + } + } + Enum { + name: "ImageConversionFlags" + values: { + "ColorMode_Mask": 3, + "AutoColor": 0, + "ColorOnly": 3, + "MonoOnly": 2, + "AlphaDither_Mask": 12, + "ThresholdAlphaDither": 0, + "OrderedAlphaDither": 4, + "DiffuseAlphaDither": 8, + "NoAlpha": 12, + "Dither_Mask": 48, + "DiffuseDither": 0, + "OrderedDither": 16, + "ThresholdDither": 32, + "DitherMode_Mask": 192, + "AutoDither": 0, + "PreferDither": 64, + "AvoidDither": 128, + "NoOpaqueDetection": 256, + "NoFormatConversion": 512 + } + } + Enum { + name: "BGMode" + values: { + "TransparentMode": 0, + "OpaqueMode": 1 + } + } + } + Component { name: "QEasingCurve"; prototype: "QQmlEasingValueType" } +} diff --git a/tests/auto/qml/qmllint/data/coercetovoid.qml b/tests/auto/qml/qmllint/data/coercetovoid.qml new file mode 100644 index 0000000000..f1b593ea3a --- /dev/null +++ b/tests/auto/qml/qmllint/data/coercetovoid.qml @@ -0,0 +1,8 @@ +pragma Strict +import QtQml + +QtObject { + function touchThisAndReturnSomething(x: int) { + return x + 1; + } +} diff --git a/tests/auto/qml/qmllint/data/customParser.qml b/tests/auto/qml/qmllint/data/customParser.qml index a83ae7e823..324ca20953 100644 --- a/tests/auto/qml/qmllint/data/customParser.qml +++ b/tests/auto/qml/qmllint/data/customParser.qml @@ -7,11 +7,11 @@ Rectangle { states: [ State { name: "red_color" - PropertyChanges { target: root; color: "red" } + PropertyChanges { root.color: "red" } }, State { name: "blue_color" - PropertyChanges { target: root; color: "blue" } + PropertyChanges { root.color: "blue" } } ] } diff --git a/tests/auto/qml/qmllint/data/dontCheckJSTypes.qml b/tests/auto/qml/qmllint/data/dontCheckJSTypes.qml new file mode 100644 index 0000000000..dc582936b9 --- /dev/null +++ b/tests/auto/qml/qmllint/data/dontCheckJSTypes.qml @@ -0,0 +1,11 @@ +import QtQuick + +import "SharedFunctions.js" as Functions + +Item { + Rectangle { + color: Functions.setColorAlpha(Qt.color("orange"), 0.15) + x: Functions.setColorAlpha.asdfg + y: Functions.asdfg + } +} diff --git a/tests/auto/qml/qmllint/data/findMemberPrint.qml b/tests/auto/qml/qmllint/data/findMemberPrint.qml new file mode 100644 index 0000000000..69146eb06b --- /dev/null +++ b/tests/auto/qml/qmllint/data/findMemberPrint.qml @@ -0,0 +1,13 @@ +import QtQml + +import TestTypes + +QtObject { + property var foooooooo: Foo { + id: foo + } + + function f(): void { + foo.print() + } +} diff --git a/tests/auto/qml/qmllint/data/generalizedGroupHint.qml b/tests/auto/qml/qmllint/data/generalizedGroupHint.qml new file mode 100644 index 0000000000..5fec03f7f3 --- /dev/null +++ b/tests/auto/qml/qmllint/data/generalizedGroupHint.qml @@ -0,0 +1,23 @@ +import QtQuick + +Window { + width: 640 + height: 480 + visible: true + title: 'Resolve my color type' + + Item { + id: foo + + states: [ + State { + PropertyChanges { + target: foo + myColor: Qt.rgba(0.5, 0.5, 0.5, 0.16) + } + } + ] + + property color myColor: 'black' + } +} diff --git a/tests/auto/qml/qmllint/data/groupedAttachedLayout.qml b/tests/auto/qml/qmllint/data/groupedAttachedLayout.qml new file mode 100644 index 0000000000..7cfe98d4f8 --- /dev/null +++ b/tests/auto/qml/qmllint/data/groupedAttachedLayout.qml @@ -0,0 +1,20 @@ +import QtQuick +import QtQuick.Layouts + +Window { + id: root + + Rectangle { + id: redRect + } + + Item { + states: [ + State { + PropertyChanges { + redRect.Layout.fillWidth: true + } + } + ] + } +} diff --git a/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/main.qml b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/main.qml new file mode 100644 index 0000000000..d018110b86 --- /dev/null +++ b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/main.qml @@ -0,0 +1,11 @@ +import QtQuick +import QtQuick.Controls + +Window { + width: 640; height: 480 + visible: true + + Button { + text: "a" + } +} diff --git a/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/qmldir b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/qmldir new file mode 100644 index 0000000000..acb456ffd9 --- /dev/null +++ b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc/qmldir @@ -0,0 +1,3 @@ +module moduleWithQrc +prefer :/qt/qml/moduleWithQrc/ + diff --git a/tests/auto/qml/qmllint/data/hidden/moduleWithQrc_raw_qml_0.qrc b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc_raw_qml_0.qrc new file mode 100644 index 0000000000..43d3ec805c --- /dev/null +++ b/tests/auto/qml/qmllint/data/hidden/moduleWithQrc_raw_qml_0.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/qt/qml/qmllint65/"> + <file alias="main.qml">moduleWithQrc/main.qml</file> + </qresource> +</RCC> + diff --git a/tests/auto/qml/qmllint/data/hidden/qmake_moduleWithQrc.qrc b/tests/auto/qml/qmllint/data/hidden/qmake_moduleWithQrc.qrc new file mode 100644 index 0000000000..cb362e2d55 --- /dev/null +++ b/tests/auto/qml/qmllint/data/hidden/qmake_moduleWithQrc.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/qt/qml/qmllint65"> + <file alias="qmldir">moduleWithQrc/qmldir</file> + </qresource> +</RCC> + diff --git a/tests/auto/qml/qmllint/data/importNonexistentFile.qml b/tests/auto/qml/qmllint/data/importNonexistentFile.qml new file mode 100644 index 0000000000..847023936a --- /dev/null +++ b/tests/auto/qml/qmllint/data/importNonexistentFile.qml @@ -0,0 +1,3 @@ +import "¯\(ツ)/¯:/invalid/url" + +QtObject {} diff --git a/tests/auto/qml/qmllint/data/importNullDevice.qml b/tests/auto/qml/qmllint/data/importNullDevice.qml new file mode 100644 index 0000000000..5ff3090d75 --- /dev/null +++ b/tests/auto/qml/qmllint/data/importNullDevice.qml @@ -0,0 +1,3 @@ +import "/dev/null" + +QtObject {} diff --git a/tests/auto/qml/qmllint/data/initReadonly.qml b/tests/auto/qml/qmllint/data/initReadonly.qml index 5a3cafff23..a9a2a0016b 100644 --- a/tests/auto/qml/qmllint/data/initReadonly.qml +++ b/tests/auto/qml/qmllint/data/initReadonly.qml @@ -2,4 +2,5 @@ import QtQml QtObject { readonly property int i: 14 + readonly property int j: i + 20 } diff --git a/tests/auto/qml/qmllint/data/inlineComponent.qml b/tests/auto/qml/qmllint/data/inlineComponent.qml index ce6998a980..364d5319de 100644 --- a/tests/auto/qml/qmllint/data/inlineComponent.qml +++ b/tests/auto/qml/qmllint/data/inlineComponent.qml @@ -1,6 +1,7 @@ import QtQuick 2.0 Item { + component MyIC: IC {} component IC : QtObject {} QtObject { component IC2: QtObject {} diff --git a/tests/auto/qml/qmllint/data/invalidIdLookup.qml b/tests/auto/qml/qmllint/data/invalidIdLookup.qml new file mode 100644 index 0000000000..b351e5cfea --- /dev/null +++ b/tests/auto/qml/qmllint/data/invalidIdLookup.qml @@ -0,0 +1,10 @@ +import Things +import QtQml + +QtObject { + property MediaPlayerStateMachine m: MediaPlayerStateMachine { + id: stateMachine + } + + objectName: stateMachine.objectName +} diff --git a/tests/auto/qml/qmllint/data/jsonArrayIsRecognized.qml b/tests/auto/qml/qmllint/data/jsonArrayIsRecognized.qml new file mode 100644 index 0000000000..89c52e0e52 --- /dev/null +++ b/tests/auto/qml/qmllint/data/jsonArrayIsRecognized.qml @@ -0,0 +1,8 @@ +import QtQuick +import Qtbug111015 1.0 + +Item { + TypeWithJsonArray { + jsonArray: [] + } +} diff --git a/tests/auto/qml/qmllint/data/jsonObjectIsRecognized.qml b/tests/auto/qml/qmllint/data/jsonObjectIsRecognized.qml new file mode 100644 index 0000000000..0a96fa9f92 --- /dev/null +++ b/tests/auto/qml/qmllint/data/jsonObjectIsRecognized.qml @@ -0,0 +1,8 @@ +import QtQuick +import Qtbug111015 1.0 + +Item { + TypeWithJsonObjectList { + jsonObjectList: [] + } +} diff --git a/tests/auto/qml/qmllint/data/locale.qml b/tests/auto/qml/qmllint/data/locale.qml new file mode 100644 index 0000000000..4ff9e6392c --- /dev/null +++ b/tests/auto/qml/qmllint/data/locale.qml @@ -0,0 +1,6 @@ +import QtQml +import LocaleTest + +QtObject { + property int monday: AppManager.primaryLocale.firstDayOfWeek +} diff --git a/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport.qml b/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport.qml new file mode 100644 index 0000000000..14c716c35d --- /dev/null +++ b/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport.qml @@ -0,0 +1,9 @@ +import QtQuick as test + +test.Rectangle { // crashed qqmljsimportvisitor + id: mainRect + width: 100 + height: 100 + visible: true + rotation: 11 +} diff --git a/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport2.qml b/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport2.qml new file mode 100644 index 0000000000..4e03d8091d --- /dev/null +++ b/tests/auto/qml/qmllint/data/lowerCaseQualifiedImport2.qml @@ -0,0 +1,9 @@ +import QtQuick as test + +test.Item { + property test.color c + + property var p: test.Grid {} + + component IC: test.Rectangle {} +} diff --git a/tests/auto/qml/qmllint/data/multifix.fixed.qml b/tests/auto/qml/qmllint/data/multifix.fixed.qml new file mode 100644 index 0000000000..d2188f2318 --- /dev/null +++ b/tests/auto/qml/qmllint/data/multifix.fixed.qml @@ -0,0 +1,14 @@ +pragma ComponentBehavior: Bound +import QtQml + +QtObject { + id: root + + property Component cursorDelegate: QtObject { + objectName: root.objectName + } + + property Component background: QtObject { + objectName: root.objectName + } +} diff --git a/tests/auto/qml/qmllint/data/multifix.qml b/tests/auto/qml/qmllint/data/multifix.qml new file mode 100644 index 0000000000..5f05ae7e62 --- /dev/null +++ b/tests/auto/qml/qmllint/data/multifix.qml @@ -0,0 +1,13 @@ +import QtQml + +QtObject { + id: root + + property Component cursorDelegate: QtObject { + objectName: root.objectName + } + + property Component background: QtObject { + objectName: root.objectName + } +} diff --git a/tests/auto/qml/qmllint/data/notQmlRootMethods.qml b/tests/auto/qml/qmllint/data/notQmlRootMethods.qml new file mode 100644 index 0000000000..6d067d572d --- /dev/null +++ b/tests/auto/qml/qmllint/data/notQmlRootMethods.qml @@ -0,0 +1,8 @@ +import QtQml + +QtObject { + id: self + + function a() { self.deleteLater(); } + function b() { self.destroyed(); } +} diff --git a/tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml b/tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml index 2a51cceac3..b6c0f59c7f 100644 --- a/tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml +++ b/tests/auto/qml/qmllint/data/pluginQuick_anchorsUndefined.qml @@ -5,5 +5,6 @@ Item { anchors.horizontalCenter: undefined anchors.verticalCenter: undefined anchors.baseline: undefined + Component.onCompleted: anchors.bottom = undefined } } diff --git a/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesInvalidTarget.qml b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesInvalidTarget.qml new file mode 100644 index 0000000000..fa7d4ef1ac --- /dev/null +++ b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesInvalidTarget.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Rectangle { + State { + name: "test" + PropertyChanges { target: root; color: "blue" } + } +} diff --git a/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml new file mode 100644 index 0000000000..94873463f1 --- /dev/null +++ b/tests/auto/qml/qmllint/data/pluginQuick_propertyChangesParsed.qml @@ -0,0 +1,19 @@ +import QtQuick + +Window { + Item { + id: foo + property color myColor: 'black' + + states: [ + State { + PropertyChanges { + target: foo + myColor: Qt.rgba(0.5, 0.5, 0.5, 0.16) + notThere: "a" + } + } + ] + + } +} diff --git a/tests/auto/qml/qmllint/data/qEventPoint.qml b/tests/auto/qml/qmllint/data/qEventPoint.qml new file mode 100644 index 0000000000..086e710b48 --- /dev/null +++ b/tests/auto/qml/qmllint/data/qEventPoint.qml @@ -0,0 +1,9 @@ +import QtQuick + +TapHandler { + acceptedButtons: Qt.LeftButton | Qt.RightButton + onSingleTapped: (eventPoint, button) => { + console.log("Single tap at", eventPoint, "with button", button) + } + onTapped: console.log("tapped") +} diff --git a/tests/auto/qml/qmllint/data/qmlRootMethods.qml b/tests/auto/qml/qmllint/data/qmlRootMethods.qml new file mode 100644 index 0000000000..5541de3a32 --- /dev/null +++ b/tests/auto/qml/qmllint/data/qmlRootMethods.qml @@ -0,0 +1,12 @@ +import QtQml + +QtObject { + id: self + + objectName: self.toString() + + Component.onCompleted: { + self.destroy(); + self.destroy(25); + } +} diff --git a/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml b/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml index 4847fc9196..ad88f1c58c 100644 --- a/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml +++ b/tests/auto/qml/qmllint/data/qmldirAndQmltypes.qml @@ -2,5 +2,5 @@ import Things 1.0 Something { property var a: SomethingEntirelyStrange {} - property var b: SomethingEntirelyStrange.AAA + property var b: SomethingEntirelyStrange.AnEnum.AAA } diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml new file mode 100644 index 0000000000..de142337b4 --- /dev/null +++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_component.qml @@ -0,0 +1,9 @@ +import QtQml +import QtQuick + +QtObject { + id: root + component Comp : Item { } + property Comp c: Comp{ } + function comp() { return root.c } +} diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml new file mode 100644 index 0000000000..0585ceceb2 --- /dev/null +++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_enum.qml @@ -0,0 +1,5 @@ +import QtQuick + +QtObject { + function enumeration() { return Text.AlignRight } +} diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml new file mode 100644 index 0000000000..dc03311e73 --- /dev/null +++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_method.qml @@ -0,0 +1,6 @@ +import QtQml + +QtObject { + function f() { } + function method() { return f } +} diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml new file mode 100644 index 0000000000..bb79978d85 --- /dev/null +++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_property.qml @@ -0,0 +1,7 @@ +import QtQml + +QtObject { + id: root + property int i: 1 + function prop() { return root.i } +} diff --git a/tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml b/tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml new file mode 100644 index 0000000000..78f02a8b67 --- /dev/null +++ b/tests/auto/qml/qmllint/data/returnTypeAnnotation_type.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + function type() { return 1 + 1 } +} diff --git a/tests/auto/qml/qmllint/data/scriptInTemplate.qml b/tests/auto/qml/qmllint/data/scriptInTemplate.qml new file mode 100644 index 0000000000..ba333dcd3e --- /dev/null +++ b/tests/auto/qml/qmllint/data/scriptInTemplate.qml @@ -0,0 +1,6 @@ +import QtQml +import scripts + +QtObject { + objectName: `Result: ${Foo.getText()}` +} diff --git a/tests/auto/qml/qmllint/data/scripts/Foo.js b/tests/auto/qml/qmllint/data/scripts/Foo.js new file mode 100644 index 0000000000..1d5106f733 --- /dev/null +++ b/tests/auto/qml/qmllint/data/scripts/Foo.js @@ -0,0 +1,6 @@ +.pragma library + +function getText() { + return "whohoooooo" +} + diff --git a/tests/auto/qml/qmllint/data/scripts/qmldir b/tests/auto/qml/qmllint/data/scripts/qmldir new file mode 100644 index 0000000000..b4bf844348 --- /dev/null +++ b/tests/auto/qml/qmllint/data/scripts/qmldir @@ -0,0 +1,4 @@ +module scripts +typeinfo scripts.qmltypes +Foo 1.0 Foo.js + diff --git a/tests/auto/qml/qmllint/data/scripts/scripts.qmltypes b/tests/auto/qml/qmllint/data/scripts/scripts.qmltypes new file mode 100644 index 0000000000..ebbfc41eb2 --- /dev/null +++ b/tests/auto/qml/qmllint/data/scripts/scripts.qmltypes @@ -0,0 +1,22 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by qmltyperegistrar. + +Module { + Component { + file: "value.h" + name: "ValueType" + accessSemantics: "value" + exports: ["scripts/vvv 1.0"] + exportMetaObjectRevisions: [256] + Method { + name: "ValueType" + isConstructor: true + Parameter { name: "v"; type: "QString" } + } + Method { name: "ValueType"; isCloned: true; isConstructor: true } + } +} diff --git a/tests/auto/qml/qmllint/data/scriptstring.qml b/tests/auto/qml/qmllint/data/scriptstring.qml new file mode 100644 index 0000000000..733434e924 --- /dev/null +++ b/tests/auto/qml/qmllint/data/scriptstring.qml @@ -0,0 +1,73 @@ +import QtQuick + +Window { + id: root + + Rectangle { + id: main + + MouseArea { + id: mouse + property int clickCount: 0 + onClicked: { + clickCount++ + + switch ( clickCount % 3 ) { + case 1 : + main.state = "middleState" + break + case 2 : + main.state = "rightState" + break + default : + main.state = "leftState" + } + } + } + + Rectangle { + id: mover + anchors { + left: undefined + right: undefined + horizontalCenter: undefined + top: main.top + bottom: main.bottom + } + } + + states: [ + State { + name: "leftState" + AnchorChanges { + target: mover + anchors.left: main.left + anchors.right: undefined + anchors.horizontalCenter: undefined + } + }, + State { + name: "middleState" + AnchorChanges { + target: mover + anchors { + left: undefined + right: undefined + horizontalCenter: main.horizontalCenter + } + } + }, + State { + name: "rightState" + AnchorChanges { + target: mover + anchors { + left: undefined + right: main.right + horizontalCenter: undefined + } + } + } + ] + } +} diff --git a/tests/auto/qml/qmllint/data/settings/plugin/.qmllint.ini b/tests/auto/qml/qmllint/data/settings/plugin/.qmllint.ini new file mode 100644 index 0000000000..a72e0e29e2 --- /dev/null +++ b/tests/auto/qml/qmllint/data/settings/plugin/.qmllint.ini @@ -0,0 +1,2 @@ +[Warnings] +TestWarning=disable diff --git a/tests/auto/qml/qmllint/data/settings/plugin/elemenpass_pluginSettingTest.qml b/tests/auto/qml/qmllint/data/settings/plugin/elemenpass_pluginSettingTest.qml new file mode 100644 index 0000000000..b9250a2d11 --- /dev/null +++ b/tests/auto/qml/qmllint/data/settings/plugin/elemenpass_pluginSettingTest.qml @@ -0,0 +1,7 @@ +import QtQuick + +Item { + Rectangle { + radius: 5 + } +} diff --git a/tests/auto/qml/qmllint/data/something.qml b/tests/auto/qml/qmllint/data/something.qml new file mode 100644 index 0000000000..38998f606d --- /dev/null +++ b/tests/auto/qml/qmllint/data/something.qml @@ -0,0 +1,2 @@ +import ModuleInImportPath +A {} diff --git a/tests/auto/qml/qmllint/data/storeNameMethod.qml b/tests/auto/qml/qmllint/data/storeNameMethod.qml new file mode 100644 index 0000000000..fca02b288f --- /dev/null +++ b/tests/auto/qml/qmllint/data/storeNameMethod.qml @@ -0,0 +1,12 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 + +Window { + Rectangle { + + Timer { + function foo() {} + onTriggered: foo = 1 + } + } +} diff --git a/tests/auto/qml/qmllint/data/stringToDateTime.qml b/tests/auto/qml/qmllint/data/stringToDateTime.qml new file mode 100644 index 0000000000..dc4bd6cda5 --- /dev/null +++ b/tests/auto/qml/qmllint/data/stringToDateTime.qml @@ -0,0 +1,7 @@ +import StringToDateTime + +StringToDateTime { + aDate: "2023-03-29" + aTime: "15:10:41" + aDateTime: "2023-03-29 15:10:41" +} diff --git a/tests/auto/qml/qmllint/data/untitled/components/Foo.qml b/tests/auto/qml/qmllint/data/untitled/components/Foo.qml new file mode 100644 index 0000000000..10e5741900 --- /dev/null +++ b/tests/auto/qml/qmllint/data/untitled/components/Foo.qml @@ -0,0 +1,5 @@ +import QtQuick + +Text { + text: "Here I am!" +} diff --git a/tests/auto/qml/qmllint/data/untitled/main.qml b/tests/auto/qml/qmllint/data/untitled/main.qml new file mode 100644 index 0000000000..cf8980ab55 --- /dev/null +++ b/tests/auto/qml/qmllint/data/untitled/main.qml @@ -0,0 +1,9 @@ +pragma Strict + +import QtQuick +import 'qrc:/untitled/components' as C + +Window { + id: root + C.Foo {} +} diff --git a/tests/auto/qml/qmllint/data/untitled/qrcUrlImport.qrc b/tests/auto/qml/qmllint/data/untitled/qrcUrlImport.qrc new file mode 100644 index 0000000000..0ad0d57fbb --- /dev/null +++ b/tests/auto/qml/qmllint/data/untitled/qrcUrlImport.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/untitled/"> + <file alias="main.qml">main.qml</file> + <file alias="components/Foo.qml">components/Foo.qml</file> + </qresource> +</RCC> diff --git a/tests/auto/qml/qmllint/data/useConstInvokable.qml b/tests/auto/qml/qmllint/data/useConstInvokable.qml new file mode 100644 index 0000000000..4f89e89918 --- /dev/null +++ b/tests/auto/qml/qmllint/data/useConstInvokable.qml @@ -0,0 +1,5 @@ +import Things + +ConstInvokable { + objectName: getObject().objectName +} diff --git a/tests/auto/qml/qmllint/data/validLiterals.qml b/tests/auto/qml/qmllint/data/validLiterals.qml index 4f8c575cd3..55792eaae2 100644 --- a/tests/auto/qml/qmllint/data/validLiterals.qml +++ b/tests/auto/qml/qmllint/data/validLiterals.qml @@ -29,9 +29,9 @@ QtObject { property date date1: "2021-08-13T14:16:21.435Z" - property point point1: "1,2" + property point point1: ({ x: 1, y: 2 }) - property size size1: "50x50" + property size size1: ({ width: 50, height: 50 }) - property rect rect1: "10,20,30x30" + property rect rect1: ({ x: 10, y: 20, width: 30, height: 30 }) } diff --git a/tests/auto/qml/qmllint/data/valueTypesFromString.qml b/tests/auto/qml/qmllint/data/valueTypesFromString.qml new file mode 100644 index 0000000000..fc013f858f --- /dev/null +++ b/tests/auto/qml/qmllint/data/valueTypesFromString.qml @@ -0,0 +1,7 @@ +import QtQuick + +Item { + property point p: "30,50" + property rect p3: "10, 20, 30x50" + property size p4: "30x50" +} diff --git a/tests/auto/qml/qmllint/data/variantMapLookup.qml b/tests/auto/qml/qmllint/data/variantMapLookup.qml new file mode 100644 index 0000000000..7f50879932 --- /dev/null +++ b/tests/auto/qml/qmllint/data/variantMapLookup.qml @@ -0,0 +1,13 @@ +pragma Strict +import TestTypes +import QtQuick + +Item { + VariantMapLookupFoo { + id: moo + } + Component.onCompleted: { + moo.data.value = 5 + moo.data["value"] = 6 + } +} diff --git a/tests/auto/qml/qmllint/data/writeListProperty.qml b/tests/auto/qml/qmllint/data/writeListProperty.qml new file mode 100644 index 0000000000..8015fdf51b --- /dev/null +++ b/tests/auto/qml/qmllint/data/writeListProperty.qml @@ -0,0 +1,7 @@ +import QtQuick + +Item { + id: self + property Item a: Item { id: a } + Component.onCompleted: self.data = [ a ] +} diff --git a/tests/auto/qml/qmllint/lintplugin.cpp b/tests/auto/qml/qmllint/lintplugin.cpp index 47279adcaa..58b174cb6b 100644 --- a/tests/auto/qml/qmllint/lintplugin.cpp +++ b/tests/auto/qml/qmllint/lintplugin.cpp @@ -1,11 +1,11 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "lintplugin.h" using namespace Qt::StringLiterals; -static constexpr LoggerWarningId plugin { "testPlugin.test" }; +static constexpr QQmlSA::LoggerWarningId plugin{ "testPlugin.test" }; class ElementTest : public QQmlSA::ElementPass { @@ -17,25 +17,25 @@ public: bool shouldRun(const QQmlSA::Element &element) override { - return element->baseType() == m_rectangle; + return element.baseType() == m_rectangle; } void run(const QQmlSA::Element &element) override { - auto property = element->property(u"radius"_s); - if (!property.isValid() || element->property(u"radius"_s).typeName() != u"double") { - emitWarning(u"Failed to verify radius property", plugin, element->sourceLocation()); + auto property = element.property(u"radius"_s); + if (!property.isValid() || element.property(u"radius"_s).typeName() != u"double") { + emitWarning(u"Failed to verify radius property", plugin, element.sourceLocation()); return; } - auto bindings = element->propertyBindings(u"radius"_s); + auto bindings = element.propertyBindings(u"radius"_s); if (bindings.isEmpty() || bindings.constFirst().numberValue() != 5) { emitWarning(u"Failed to verify radius property binding", plugin, - element->sourceLocation()); + element.sourceLocation()); return; } - emitWarning(u"ElementTest OK", plugin, element->sourceLocation()); + emitWarning(u"ElementTest OK", plugin, element.sourceLocation()); } private: @@ -48,37 +48,37 @@ public: PropertyTest(QQmlSA::PassManager *manager) : QQmlSA::PropertyPass(manager) { } void onBinding(const QQmlSA::Element &element, const QString &propertyName, - const QQmlJSMetaPropertyBinding &binding, const QQmlSA::Element &bindingScope, + const QQmlSA::Binding &binding, const QQmlSA::Element &bindingScope, const QQmlSA::Element &value) override { emitWarning(u"Saw binding on %1 property %2 with value %3 (and type %4) in scope %5"_s - .arg(element->baseTypeName(), propertyName, + .arg(element.baseTypeName(), propertyName, value.isNull() ? u"NULL"_s - : (value->internalName().isNull() ? value->baseTypeName() - : value->baseTypeName())) - .arg(binding.bindingType()) - .arg(bindingScope->baseTypeName()), - plugin, bindingScope->sourceLocation()); + : (value.name().isNull() ? value.baseTypeName() + : value.name())) + .arg(qToUnderlying(binding.bindingType())) + .arg(bindingScope.baseTypeName()), + plugin, bindingScope.sourceLocation()); } void onRead(const QQmlSA::Element &element, const QString &propertyName, - const QQmlSA::Element &readScope, QQmlJS::SourceLocation location) override + const QQmlSA::Element &readScope, QQmlSA::SourceLocation location) override { emitWarning(u"Saw read on %1 property %2 in scope %3"_s.arg( - element->baseTypeName(), propertyName, readScope->baseTypeName()), + element.baseTypeName(), propertyName, readScope.baseTypeName()), plugin, location); } void onWrite(const QQmlSA::Element &element, const QString &propertyName, const QQmlSA::Element &value, const QQmlSA::Element &writeScope, - QQmlJS::SourceLocation location) override + QQmlSA::SourceLocation location) override { emitWarning(u"Saw write on %1 property %2 with value %3 in scope %4"_s.arg( - element->baseTypeName(), propertyName, - (value->internalName().isNull() ? value->baseTypeName() - : value->internalName()), - writeScope->baseTypeName()), + element.baseTypeName(), propertyName, + (value.name().isNull() ? value.baseTypeName() + : value.name()), + writeScope.baseTypeName()), plugin, location); } }; @@ -109,7 +109,7 @@ private: void LintPlugin::registerPasses(QQmlSA::PassManager *manager, const QQmlSA::Element &rootElement) { - if (!rootElement->filePath().endsWith(u"_pluginTest.qml")) + if (!rootElement.filePath().endsWith(u"_pluginTest.qml")) return; manager->registerElementPass(std::make_unique<ElementTest>(manager)); diff --git a/tests/auto/qml/qmllint/lintplugin.h b/tests/auto/qml/qmllint/lintplugin.h index 76733ca7a7..c121657456 100644 --- a/tests/auto/qml/qmllint/lintplugin.h +++ b/tests/auto/qml/qmllint/lintplugin.h @@ -1,12 +1,12 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef LINTPLUGIN_H #define LINTPLUGIN_H #include <QtPlugin> #include <QtCore/qobject.h> -#include <QtQmlCompiler/private/qqmlsa_p.h> +#include <QtQmlCompiler/qqmlsa.h> class LintPlugin : public QObject, public QQmlSA::LintPlugin { diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index a5e906ce9a..7f7b5317cc 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -1,6 +1,6 @@ // Copyright (C) 2016 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sergio Martins <sergio.martins@kdab.com> // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QProcess> @@ -43,6 +43,11 @@ public: Flags flags = {}; }; + struct Environment : public QList<QPair<QString, QString>> + { + using QList<QPair<QString, QString>>::QList; + }; + private Q_SLOTS: void initTestCase() override; @@ -73,12 +78,19 @@ private Q_SLOTS: void autoqmltypes(); void resources(); + void multiDirectory(); + void requiredProperty(); void settingsFile(); void additionalImplicitImport(); + void qrcUrlImport(); + + void incorrectImportFromHost_data(); + void incorrectImportFromHost(); + void attachedPropertyReuse(); void missingBuiltinsNoCrash(); @@ -90,11 +102,21 @@ private Q_SLOTS: void lintModule(); void testLineEndings(); + void valueTypesFromString(); + + void ignoreSettingsNotCommandLineOptions(); + void backslashedQmldirPath(); + + void environment_data(); + void environment(); + + void maxWarnings(); #if QT_CONFIG(library) void testPlugin(); void quickPlugin(); #endif + private: enum DefaultImportOption { NoDefaultImports, UseDefaultImports }; enum ContainOption { StringNotContained, StringContained }; @@ -105,17 +127,24 @@ private: enum LintType { LintFile, LintModule }; + static QStringList warningsShouldFailArgs() { + static QStringList args {"-W", "0"}; + return args; + } + QString runQmllint(const QString &fileToLint, std::function<void(QProcess &)> handleResult, const QStringList &extraArgs = QStringList(), bool ignoreSettings = true, - bool addImportDirs = true, bool absolutePath = true); + bool addImportDirs = true, bool absolutePath = true, + const Environment &env = {}); QString runQmllint(const QString &fileToLint, bool shouldSucceed, const QStringList &extraArgs = QStringList(), bool ignoreSettings = true, - bool addImportDirs = true, bool absolutePath = true); + bool addImportDirs = true, bool absolutePath = true, + const Environment &env = {}); void callQmllint(const QString &fileToLint, bool shouldSucceed, QJsonArray *warnings = nullptr, QStringList importDirs = {}, QStringList qmltypesFiles = {}, QStringList resources = {}, DefaultImportOption defaultImports = UseDefaultImports, - QList<QQmlJSLogger::Category> *categories = nullptr, bool autoFixable = false, + QList<QQmlJS::LoggerCategory> *categories = nullptr, bool autoFixable = false, LintType type = LintFile); void searchWarnings(const QJsonArray &warnings, const QString &string, @@ -137,7 +166,7 @@ private: void runTest(const QString &testFile, const Result &result, QStringList importDirs = {}, QStringList qmltypesFiles = {}, QStringList resources = {}, DefaultImportOption defaultImports = UseDefaultImports, - QList<QQmlJSLogger::Category> *categories = nullptr); + QList<QQmlJS::LoggerCategory> *categories = nullptr); QString m_qmllintPath; QString m_qmljsrootgenPath; @@ -258,6 +287,12 @@ void TestQmllint::testUnqualified_data() Message { QStringLiteral("index is implicitly injected into this delegate. " "Add a required property instead.") } } }; + QTest::newRow("storeSloppy") + << QStringLiteral("UnqualifiedInStoreSloppy.qml") + << Result{ { Message{ QStringLiteral("Unqualified access"), 9, 26} } }; + QTest::newRow("storeStrict") + << QStringLiteral("UnqualifiedInStoreStrict.qml") + << Result{ { Message{ QStringLiteral("Unqualified access"), 9, 52} } }; } void TestQmllint::testUnknownCausesFail() @@ -355,12 +390,12 @@ void TestQmllint::verifyJsRoot() QString currentJsRootContent, generatedJsRootContent; QFile currentJsRoot(currentJsRootPath); - QVERIFY(currentJsRoot.open(QFile::ReadOnly)); + QVERIFY(currentJsRoot.open(QFile::ReadOnly | QIODevice::Text)); currentJsRootContent = QString::fromUtf8(currentJsRoot.readAll()); currentJsRoot.close(); QFile generatedJsRoot(dir.path() + QDir::separator() + "jsroot.qmltypes"); - QVERIFY(generatedJsRoot.open(QFile::ReadOnly)); + QVERIFY(generatedJsRoot.open(QFile::ReadOnly | QIODevice::Text)); generatedJsRootContent = QString::fromUtf8(generatedJsRoot.readAll()); generatedJsRoot.close(); @@ -383,7 +418,7 @@ void TestQmllint::autoqmltypes() { QProcess process; process.setWorkingDirectory(testFile("autoqmltypes")); - process.start(m_qmllintPath, { QStringLiteral("test.qml") }); + process.start(m_qmllintPath, warningsShouldFailArgs() << QStringLiteral("test.qml") ); process.waitForFinished(); @@ -393,6 +428,21 @@ void TestQmllint::autoqmltypes() QVERIFY(process.readAllStandardError() .contains("is not a qmldir file. Assuming qmltypes")); QVERIFY(process.readAllStandardOutput().isEmpty()); + + { + QProcess bare; + bare.setWorkingDirectory(testFile("autoqmltypes")); + bare.start(m_qmllintPath, warningsShouldFailArgs() << QStringLiteral("--bare") << QStringLiteral("test.qml") ); + bare.waitForFinished(); + + const QByteArray errors = bare.readAllStandardError(); + QVERIFY(!errors.contains("is not a qmldir file. Assuming qmltypes")); + QVERIFY(errors.contains("Failed to import TestTest.")); + QVERIFY(bare.readAllStandardOutput().isEmpty()); + + QCOMPARE(bare.exitStatus(), QProcess::NormalExit); + QVERIFY(bare.exitCode() != 0); + } } void TestQmllint::resources() @@ -422,6 +472,17 @@ void TestQmllint::resources() } } +void TestQmllint::multiDirectory() +{ + callQmllint( + testFile("MultiDirectory/qml/Inner.qml"), true, nullptr, + {}, {}, { testFile("MultiDirectory/multi.qrc") }); + + callQmllint( + testFile("MultiDirectory/qml/pages/Page.qml"), true, nullptr, + {}, {}, { testFile("MultiDirectory/multi.qrc") }); +} + void TestQmllint::dirtyQmlCode_data() { QTest::addColumn<QString>("filename"); @@ -444,12 +505,12 @@ void TestQmllint::dirtyQmlCode_data() QTest::newRow("MemberNotFound") << QStringLiteral("memberNotFound.qml") << Result { { Message { - QStringLiteral("Property \"foo\" not found on type \"QtObject\""), 6, + QStringLiteral("Member \"foo\" not found on type \"QtObject\""), 6, 31 } } }; QTest::newRow("UnknownJavascriptMethd") << QStringLiteral("unknownJavascriptMethod.qml") << Result { { Message { - QStringLiteral("Property \"foo2\" not found on type \"Methods\""), 5, + QStringLiteral("Member \"foo2\" not found on type \"Methods\""), 5, 25 } } }; QTest::newRow("badAlias") << QStringLiteral("badAlias.qml") @@ -463,6 +524,12 @@ void TestQmllint::dirtyQmlCode_data() QStringLiteral("Invalid alias expression. Only IDs and field member " "expressions can be aliased"), 5, 26 } } }; + QTest::newRow("badAliasNotAnExpression") + << QStringLiteral("badAliasNotAnExpression.qml") + << Result { { Message { + QStringLiteral("Invalid alias expression. Only IDs and field member " + "expressions can be aliased"), + 4, 30 } } }; QTest::newRow("aliasCycle1") << QStringLiteral("aliasCycle.qml") << Result { { Message { QStringLiteral("Alias \"b\" is part of an alias cycle"), @@ -485,17 +552,17 @@ void TestQmllint::dirtyQmlCode_data() 9, 34 } } }; QTest::newRow("badParent") << QStringLiteral("badParent.qml") - << Result { { Message { QStringLiteral("Property \"rrr\" not found on type \"Item\""), + << Result { { Message { QStringLiteral("Member \"rrr\" not found on type \"Item\""), 5, 34 } } }; QTest::newRow("parentIsComponent") << QStringLiteral("parentIsComponent.qml") << Result { { Message { - QStringLiteral("Property \"progress\" not found on type \"QQuickItem\""), 7, + QStringLiteral("Member \"progress\" not found on type \"QQuickItem\""), 7, 39 } } }; QTest::newRow("badTypeAssertion") << QStringLiteral("badTypeAssertion.qml") << Result { { Message { - QStringLiteral("Property \"rrr\" not found on type \"QQuickItem\""), 5, + QStringLiteral("Member \"rrr\" not found on type \"QQuickItem\""), 5, 39 } } }; QTest::newRow("incompleteQmltypes") << QStringLiteral("incompleteQmltypes.qml") @@ -504,7 +571,7 @@ void TestQmllint::dirtyQmlCode_data() 26 } } }; QTest::newRow("incompleteQmltypes2") << QStringLiteral("incompleteQmltypes2.qml") - << Result { { Message { QStringLiteral("Property \"weDontKnowIt\" " + << Result { { Message { QStringLiteral("Member \"weDontKnowIt\" " "not found on type \"CustomPalette\""), 5, 35 } } }; QTest::newRow("incompleteQmltypes3") @@ -526,11 +593,11 @@ void TestQmllint::dirtyQmlCode_data() QTest::newRow("javascriptMethodsInModule") << QStringLiteral("javascriptMethodsInModuleBad.qml") << Result { { Message { - QStringLiteral("Property \"unknownFunc\" not found on type \"Foo\""), 5, + QStringLiteral("Member \"unknownFunc\" not found on type \"Foo\""), 5, 21 } } }; QTest::newRow("badEnumFromQtQml") << QStringLiteral("badEnumFromQtQml.qml") - << Result { { Message { QStringLiteral("Property \"Linear123\" not " + << Result { { Message { QStringLiteral("Member \"Linear123\" not " "found on type \"QQmlEasingEnums\""), 4, 30 } } }; QTest::newRow("anchors3") @@ -548,13 +615,13 @@ void TestQmllint::dirtyQmlCode_data() "unknown grouped property scope nanchors.") } } }; QTest::newRow("badAliasObject") << QStringLiteral("badAliasObject.qml") - << Result { { Message { QStringLiteral("Property \"wrongwrongwrong\" not " + << Result { { Message { QStringLiteral("Member \"wrongwrongwrong\" not " "found on type \"QtObject\""), 8, 40 } } }; QTest::newRow("badScript") << QStringLiteral("badScript.qml") << Result { { Message { QStringLiteral( - "Property \"stuff\" not found on type \"Empty\""), + "Member \"stuff\" not found on type \"Empty\""), 5, 21 } } }; QTest::newRow("badScriptOnAttachedProperty") << QStringLiteral("badScript.attached.qml") @@ -565,7 +632,7 @@ void TestQmllint::dirtyQmlCode_data() QTest::newRow("segFault (bad)") << QStringLiteral("SegFault.bad.qml") << Result { { Message { QStringLiteral( - "Property \"foobar\" not found on type \"QQuickScreenAttached\"") } } }; + "Member \"foobar\" not found on type \"QQuickScreenAttached\"") } } }; QTest::newRow("VariableUsedBeforeDeclaration") << QStringLiteral("useBeforeDeclaration.qml") << Result { { Message { @@ -585,7 +652,7 @@ void TestQmllint::dirtyQmlCode_data() "than the signal it handles.") } } }; QTest::newRow("OnAssignment") << QStringLiteral("onAssignment.qml") << Result { { Message { QStringLiteral( - "Property \"loops\" not found on type \"bool\"") } } }; + "Member \"loops\" not found on type \"bool\"") } } }; QTest::newRow("BadAttached") << QStringLiteral("badAttached.qml") << Result { { Message { QStringLiteral( "unknown attached property scope WrongAttached.") } } }; @@ -702,13 +769,13 @@ void TestQmllint::dirtyQmlCode_data() QTest::newRow("badAttachedProperty") << QStringLiteral("badAttachedProperty.qml") << Result { { Message { - QStringLiteral("Property \"progress\" not found on type \"TestType\"") } } }; + QStringLiteral("Member \"progress\" not found on type \"TestType\"") } } }; QTest::newRow("badAttachedPropertyNested") << QStringLiteral("badAttachedPropertyNested.qml") << Result { { Message { QStringLiteral( - "Property \"progress\" not found on type \"QObject\""), + "Member \"progress\" not found on type \"QObject\""), 12, 41 } }, - { Message { QString("Property \"progress\" not found on type \"QObject\""), + { Message { QString("Member \"progress\" not found on type \"QObject\""), 6, 37 } } }; QTest::newRow("badAttachedPropertyTypeString") << QStringLiteral("badAttachedPropertyTypeString.qml") @@ -780,6 +847,14 @@ singleTicks: ' \\' \\\\' expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", 16, 27 } }, { Result::ExitsNormally, Result::AutoFixable } }; + QTest::addRow("multifix") + << QStringLiteral("multifix.qml") + << Result { { + Message { QStringLiteral("Unqualified access"), 7, 19, QtWarningMsg}, + Message { QStringLiteral("Unqualified access"), 11, 19, QtWarningMsg}, + }, {}, { + Message { QStringLiteral("pragma ComponentBehavior: Bound\n"), 1, 1 } + }, { Result::AutoFixable }}; QTest::newRow("unresolvedType") << QStringLiteral("unresolvedType.qml") << Result { { Message { QStringLiteral( @@ -812,7 +887,7 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", QTest::newRow("QtQuick.Window 2.0") << QStringLiteral("qtquickWindow20.qml") << Result { { Message { QStringLiteral( - "Property \"window\" not found on type \"QQuickWindow\"") } } }; + "Member \"window\" not found on type \"QQuickWindow\"") } } }; QTest::newRow("unresolvedAttachedType") << QStringLiteral("unresolvedAttachedType.qml") << Result { { Message { QStringLiteral( @@ -879,22 +954,22 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", QTest::newRow("enumInvalid") << QStringLiteral("enumInvalid.qml") << Result { { Message { - QStringLiteral("Property \"red\" not found on type \"QtObject\"") } } }; + QStringLiteral("Member \"red\" not found on type \"QtObject\"") } } }; QTest::newRow("inaccessibleId") << QStringLiteral("inaccessibleId.qml") << Result { { Message { - QStringLiteral("Property \"objectName\" not found on type \"int\"") } } }; + QStringLiteral("Member \"objectName\" not found on type \"int\"") } } }; QTest::newRow("inaccessibleId2") << QStringLiteral("inaccessibleId2.qml") << Result { { Message { - QStringLiteral("Property \"objectName\" not found on type \"int\"") } } }; + QStringLiteral("Member \"objectName\" not found on type \"int\"") } } }; QTest::newRow("unknownTypeCustomParser") << QStringLiteral("unknownTypeCustomParser.qml") << Result { { Message { QStringLiteral("TypeDoesNotExist was not found.") } } }; QTest::newRow("nonNullStored") << QStringLiteral("nonNullStored.qml") << Result { { Message { QStringLiteral( - "Property \"objectName\" not found on type \"Foozle\"") } }, + "Member \"objectName\" not found on type \"Foozle\"") } }, { Message { QStringLiteral("Unqualified access") } } }; QTest::newRow("cppPropertyChangeHandlers-wrong-parameters-size-bindable") << QStringLiteral("badCppPropertyChangeHandlers1.qml") @@ -936,13 +1011,13 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", QTest::newRow("didYouMean(property)") << QStringLiteral("didYouMeanProperty.qml") << Result { { Message { QStringLiteral( - "Property \"hoight\" not found on type \"Rectangle\"") }, + "Member \"hoight\" not found on type \"Rectangle\"") }, {}, { Message { QStringLiteral("height") } } } }; QTest::newRow("didYouMean(propertyCall)") << QStringLiteral("didYouMeanPropertyCall.qml") << Result { - { Message { QStringLiteral("Property \"lgg\" not found on type \"Console\"") }, + { Message { QStringLiteral("Member \"lgg\" not found on type \"Console\"") }, {}, { Message { QStringLiteral("log") } } } }; @@ -955,7 +1030,7 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", QTest::newRow("didYouMean(enum)") << QStringLiteral("didYouMeanEnum.qml") << Result { { Message { QStringLiteral( - "Property \"Readx\" not found on type \"QQuickImage\"") }, + "Member \"Readx\" not found on type \"QQuickImage\"") }, {}, { Message { QStringLiteral("Ready") } } } }; QTest::newRow("nullBinding") << QStringLiteral("nullBinding.qml") @@ -1020,9 +1095,8 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", QTest::newRow("NotScopedEnumCpp") << QStringLiteral("NotScopedEnumCpp.qml") << Result{ { Message{ - QStringLiteral( - "Type is an unscoped enum. You cannot access \"V1\" from here."), - 5, 57 } } }; + QStringLiteral("You cannot access unscoped enum \"TheEnum\" from here."), 5, + 49 } } }; QTest::newRow("unresolvedArrayBinding") << QStringLiteral("unresolvedArrayBinding.qml") @@ -1044,6 +1118,56 @@ expression: \${expr} \${expr} \\\${expr} \\\${expr}`)", "in nested components."), 0, 0, QtInfoMsg } }, Result::AutoFixable }; + QTest::newRow("IsNotAnEntryOfEnum") + << QStringLiteral("IsNotAnEntryOfEnum.qml") + << Result{ { + Message { + QStringLiteral("Member \"Mode\" not found on type \"Item\""), 12, + 29, QtWarningMsg }, + Message{ + QStringLiteral("\"Hour\" is not an entry of enum \"Mode\"."), 13, + 62, QtInfoMsg } + }, + {}, + { Message{ QStringLiteral("Hours") } } + }; + + QTest::newRow("StoreNameMethod") + << QStringLiteral("storeNameMethod.qml") + << Result { { Message { QStringLiteral("Cannot assign to method foo") } } }; + + QTest::newRow("CoerceToVoid") + << QStringLiteral("coercetovoid.qml") + << Result { { Message { + QStringLiteral("Function without return type annotation returns double") + } } }; + + QTest::newRow("lowerCaseQualifiedImport") + << QStringLiteral("lowerCaseQualifiedImport.qml") + << Result{ { + Message{ u"Import qualifier 'test' must start with a capital letter."_s }, + Message{ + u"Namespace 'test' of 'test.Rectangle' must start with an upper case letter."_s }, + } }; + QTest::newRow("lowerCaseQualifiedImport2") + << QStringLiteral("lowerCaseQualifiedImport2.qml") + << Result{ { + Message{ u"Import qualifier 'test' must start with a capital letter."_s }, + Message{ + u"Namespace 'test' of 'test.Item' must start with an upper case letter."_s }, + Message{ + u"Namespace 'test' of 'test.Rectangle' must start with an upper case letter."_s }, + Message{ + u"Namespace 'test' of 'test.color' must start with an upper case letter."_s }, + Message{ + u"Namespace 'test' of 'test.Grid' must start with an upper case letter."_s }, + } }; + QTest::newRow("notQmlRootMethods") + << QStringLiteral("notQmlRootMethods.qml") + << Result{ { + Message{ u"Member \"deleteLater\" not found on type \"QtObject\""_s }, + Message{ u"Member \"destroyed\" not found on type \"QtObject\""_s }, + } }; } void TestQmllint::dirtyQmlCode() @@ -1167,6 +1291,7 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("QVariant") << QStringLiteral("qvariant.qml"); QTest::newRow("Accessible") << QStringLiteral("accessible.qml"); QTest::newRow("qjsroot") << QStringLiteral("qjsroot.qml"); + QTest::newRow("qmlRootMethods") << QStringLiteral("qmlRootMethods.qml"); QTest::newRow("InlineComponent") << QStringLiteral("inlineComponent.qml"); QTest::newRow("InlineComponentWithComponents") << QStringLiteral("inlineComponentWithComponents.qml"); QTest::newRow("InlineComponentsChained") << QStringLiteral("inlineComponentsChained.qml"); @@ -1216,6 +1341,21 @@ void TestQmllint::cleanQmlCode_data() QTest::newRow("propertyWithOn") << QStringLiteral("switcher.qml"); QTest::newRow("constructorProperty") << QStringLiteral("constructorProperty.qml"); QTest::newRow("onlyMajorVersion") << QStringLiteral("onlyMajorVersion.qml"); + QTest::newRow("attachedImportUse") << QStringLiteral("attachedImportUse.qml"); + QTest::newRow("VariantMapGetPropertyLookup") << QStringLiteral("variantMapLookup.qml"); + QTest::newRow("StringToDateTime") << QStringLiteral("stringToDateTime.qml"); + QTest::newRow("ScriptInTemplate") << QStringLiteral("scriptInTemplate.qml"); + QTest::newRow("AddressableValue") << QStringLiteral("addressableValue.qml"); + QTest::newRow("WriteListProperty") << QStringLiteral("writeListProperty.qml"); + QTest::newRow("dontConfuseMemberPrintWithGlobalPrint") << QStringLiteral("findMemberPrint.qml"); + QTest::newRow("groupedAttachedLayout") << QStringLiteral("groupedAttachedLayout.qml"); + QTest::newRow("QQmlScriptString") << QStringLiteral("scriptstring.qml"); + QTest::newRow("QEventPoint") << QStringLiteral("qEventPoint.qml"); + QTest::newRow("locale") << QStringLiteral("locale.qml"); + QTest::newRow("constInvokable") << QStringLiteral("useConstInvokable.qml"); + QTest::newRow("dontCheckJSTypes") << QStringLiteral("dontCheckJSTypes.qml"); + QTest::newRow("jsonObjectIsRecognized") << QStringLiteral("jsonObjectIsRecognized.qml"); + QTest::newRow("jsonArrayIsRecognized") << QStringLiteral("jsonArrayIsRecognized.qml"); } void TestQmllint::cleanQmlCode() @@ -1239,10 +1379,10 @@ void TestQmllint::compilerWarnings_data() QTest::newRow("qQmlV4Function") << QStringLiteral("varargs.qml") << Result::clean() << true; QTest::newRow("multiGrouped") << QStringLiteral("multiGrouped.qml") << Result::clean() << true; - QTest::newRow("shadowable") << QStringLiteral("shadowable.qml") - << Result { { Message { QStringLiteral( - "with type NotSoSimple can be shadowed") } } } - << true; + QTest::newRow("shadowable") + << QStringLiteral("shadowable.qml") + << Result { { Message {QStringLiteral("with type NotSoSimple can be shadowed") } } } + << true; QTest::newRow("tooFewParameters") << QStringLiteral("tooFewParams.qml") << Result { { Message { QStringLiteral("No matching override found") } } } << true; @@ -1261,6 +1401,51 @@ void TestQmllint::compilerWarnings_data() << Result { { { QStringLiteral( "Functions without type annotations won't be compiled") } } } << true; + QTest::newRow("generalizedGroupHint") + << QStringLiteral("generalizedGroupHint.qml") + << Result { { { QStringLiteral( + "Cannot resolve property type for binding on myColor. " + "You may want use ID-based grouped properties here.") } } } + << true; + QTest::newRow("invalidIdLookup") + << QStringLiteral("invalidIdLookup.qml") + << Result { { { + QStringLiteral("Cannot retrieve a non-object type by ID: stateMachine") + } } } + << true; + QTest::newRow("returnTypeAnnotation-component") + << QStringLiteral("returnTypeAnnotation_component.qml") + << Result{ { { "Could not compile function comp: function without return type " + "annotation returns (component in" }, + { "returnTypeAnnotation_component.qml)::c with type Comp. " + "This may prevent proper compilation to Cpp." } } } + << true; + QTest::newRow("returnTypeAnnotation-enum") + << QStringLiteral("returnTypeAnnotation_enum.qml") + << Result{ { { "Could not compile function enumeration: function without return type " + "annotation returns QQuickText::HAlignment::AlignRight. " + "This may prevent proper compilation to Cpp." } } } + << true; + QTest::newRow("returnTypeAnnotation-method") + << QStringLiteral("returnTypeAnnotation_method.qml") + << Result{ { { "Could not compile function method: function without return type " + "annotation returns (component in " }, // Don't check the build folder path + { "returnTypeAnnotation_method.qml)::f(...). This may " + "prevent proper compilation to Cpp." } } } + << true; + QTest::newRow("returnTypeAnnotation-property") + << QStringLiteral("returnTypeAnnotation_property.qml") + << Result{ { { "Could not compile function prop: function without return type " + "annotation returns (component in " }, // Don't check the build folder path + { "returnTypeAnnotation_property.qml)::i with type int. This may prevent " + "proper compilation to Cpp." } } } + << true; + QTest::newRow("returnTypeAnnotation-type") + << QStringLiteral("returnTypeAnnotation_type.qml") + << Result{ { { "Could not compile function type: function without return type " + "annotation returns double. This may prevent proper compilation to " + "Cpp." } } } + << true; } void TestQmllint::compilerWarnings() @@ -1273,11 +1458,15 @@ void TestQmllint::compilerWarnings() auto categories = QQmlJSLogger::defaultCategories(); - auto category = std::find(categories.begin(), categories.end(), qmlCompiler); + auto category = std::find_if(categories.begin(), categories.end(), [](const QQmlJS::LoggerCategory& category) { + return category.id() == qmlCompiler; + }); Q_ASSERT(category != categories.end()); - if (enableCompilerWarnings) - category->setLevel(u"warning"_s); + if (enableCompilerWarnings) { + category->setLevel(QtWarningMsg); + category->setIgnored(false); + } runTest(filename, result, {}, {}, {}, UseDefaultImports, &categories); } @@ -1285,7 +1474,7 @@ void TestQmllint::compilerWarnings() QString TestQmllint::runQmllint(const QString &fileToLint, std::function<void(QProcess &)> handleResult, const QStringList &extraArgs, bool ignoreSettings, - bool addImportDirs, bool absolutePath) + bool addImportDirs, bool absolutePath, const Environment &env) { auto qmlImportDir = QLibraryInfo::path(QLibraryInfo::QmlImportsPath); QStringList args; @@ -1311,6 +1500,11 @@ QString TestQmllint::runQmllint(const QString &fileToLint, QString errors; auto verify = [&](bool isSilent) { QProcess process; + QProcessEnvironment processEnv = QProcessEnvironment::systemEnvironment(); + for (const auto &entry : env) + processEnv.insert(entry.first, entry.second); + + process.setProcessEnvironment(processEnv); process.setWorkingDirectory(QFileInfo(absoluteFilePath).absolutePath()); process.start(m_qmllintPath, args); handleResult(process); @@ -1353,7 +1547,7 @@ QString TestQmllint::runQmllint(const QString &fileToLint, QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed, const QStringList &extraArgs, bool ignoreSettings, - bool addImportDirs, bool absolutePath) + bool addImportDirs, bool absolutePath, const Environment &env) { return runQmllint( fileToLint, @@ -1366,13 +1560,13 @@ QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed, else QVERIFY(process.exitCode() != 0); }, - extraArgs, ignoreSettings, addImportDirs, absolutePath); + extraArgs, ignoreSettings, addImportDirs, absolutePath, env); } void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJsonArray *warnings, QStringList importPaths, QStringList qmldirFiles, QStringList resources, DefaultImportOption defaultImports, - QList<QQmlJSLogger::Category> *categories, bool autoFixable, + QList<QQmlJS::LoggerCategory> *categories, bool autoFixable, LintType type) { QJsonArray jsonOutput; @@ -1382,18 +1576,22 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs QQmlJSLinter::LintResult lintResult; + const QStringList resolvedImportPaths = defaultImports == UseDefaultImports + ? m_defaultImportPaths + importPaths + : importPaths; if (type == LintFile) { + const QList<QQmlJS::LoggerCategory> resolvedCategories = + categories != nullptr ? *categories : QQmlJSLogger::defaultCategories(); lintResult = m_linter.lintFile( - lintedFile, nullptr, true, &jsonOutput, - defaultImports == UseDefaultImports ? m_defaultImportPaths + importPaths - : importPaths, - qmldirFiles, resources, - categories != nullptr ? *categories : QQmlJSLogger::defaultCategories()); + lintedFile, nullptr, true, &jsonOutput, resolvedImportPaths, qmldirFiles, + resources, resolvedCategories); } else { - lintResult = m_linter.lintModule(fileToLint, true, &jsonOutput); + lintResult = + m_linter.lintModule(fileToLint, true, &jsonOutput, resolvedImportPaths, resources); } bool success = lintResult == QQmlJSLinter::LintSuccess; + QEXPECT_FAIL("qtquickdialog", "Will fail until QTBUG-104091 is implemented", Abort); QVERIFY2(success == shouldSucceed, QJsonDocument(jsonOutput).toJson()); if (warnings) { @@ -1425,7 +1623,7 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs if (QFileInfo(fixedPath).exists()) { QFile fixedFile(fixedPath); - fixedFile.open(QFile::ReadOnly); + QVERIFY(fixedFile.open(QFile::ReadOnly)); QString fixedFileContents = QString::fromUtf8(fixedFile.readAll()); #ifdef Q_OS_WIN fixedCode = fixedCode.replace(u"\r\n"_s, u"\n"_s); @@ -1447,7 +1645,7 @@ void TestQmllint::callQmllint(const QString &fileToLint, bool shouldSucceed, QJs void TestQmllint::runTest(const QString &testFile, const Result &result, QStringList importDirs, QStringList qmltypesFiles, QStringList resources, DefaultImportOption defaultImports, - QList<QQmlJSLogger::Category> *categories) + QList<QQmlJS::LoggerCategory> *categories) { QJsonArray warnings; callQmllint(testFile, result.flags.testFlag(Result::Flag::ExitsNormally), &warnings, importDirs, @@ -1604,17 +1802,17 @@ void TestQmllint::requiredProperty() void TestQmllint::settingsFile() { - QVERIFY(runQmllint("settings/unqualifiedSilent/unqualified.qml", true, QStringList(), false) + QVERIFY(runQmllint("settings/unqualifiedSilent/unqualified.qml", true, warningsShouldFailArgs(), false) .isEmpty()); - QVERIFY(runQmllint("settings/unusedImportWarning/unused.qml", false, QStringList(), false) + QVERIFY(runQmllint("settings/unusedImportWarning/unused.qml", false, warningsShouldFailArgs(), false) .contains(QStringLiteral("Warning: %1:2:1: Unused import") .arg(testFile("settings/unusedImportWarning/unused.qml")))); - QVERIFY(runQmllint("settings/bare/bare.qml", false, {}, false, false) + QVERIFY(runQmllint("settings/bare/bare.qml", false, warningsShouldFailArgs(), false, false) .contains(QStringLiteral("Failed to find the following builtins: " - "builtins.qmltypes, jsroot.qmltypes"))); - QVERIFY(runQmllint("settings/qmltypes/qmltypes.qml", false, QStringList(), false) + "jsroot.qmltypes, builtins.qmltypes"))); + QVERIFY(runQmllint("settings/qmltypes/qmltypes.qml", false, warningsShouldFailArgs(), false) .contains(QStringLiteral("not a qmldir file. Assuming qmltypes."))); - QVERIFY(runQmllint("settings/qmlimports/qmlimports.qml", true, QStringList(), false).isEmpty()); + QVERIFY(runQmllint("settings/qmlimports/qmlimports.qml", true, warningsShouldFailArgs(), false).isEmpty()); } void TestQmllint::additionalImplicitImport() @@ -1623,17 +1821,56 @@ void TestQmllint::additionalImplicitImport() const auto guard = qScopeGuard([this]() {m_linter.clearCache(); }); runTest("additionalImplicitImport.qml", Result::clean(), {}, {}, { testFile("implicitImportResource.qrc") }); +} + +void TestQmllint::qrcUrlImport() +{ + const auto guard = qScopeGuard([this]() { m_linter.clearCache(); }); + QJsonArray warnings; + callQmllint(testFile("untitled/main.qml"), true, &warnings, {}, {}, + { testFile("untitled/qrcUrlImport.qrc") }); + checkResult(warnings, Result::clean()); } -void TestQmllint::attachedPropertyReuse() +void TestQmllint::incorrectImportFromHost_data() { + QTest::addColumn<QString>("filename"); + QTest::addColumn<Result>("result"); + + QTest::newRow("NonexistentFile") + << QStringLiteral("importNonexistentFile.qml") + << Result{ { Message{ + QStringLiteral("File or directory you are trying to import does not exist"), + 1, 1 } } }; +#ifndef Q_OS_WIN + // there is no /dev/null device on Win + QTest::newRow("NullDevice") + << QStringLiteral("importNullDevice.qml") + << Result{ { Message{ QStringLiteral("is neither a file nor a directory. Are sure the " + "import path is correct?"), + 1, 1 } } }; +#endif +} + +void TestQmllint::incorrectImportFromHost() +{ + QFETCH(QString, filename); + QFETCH(Result, result); + + runTest(filename, result); +} +void TestQmllint::attachedPropertyReuse() +{ auto categories = QQmlJSLogger::defaultCategories(); - auto category = std::find(categories.begin(), categories.end(), qmlAttachedPropertyReuse); + auto category = std::find_if(categories.begin(), categories.end(), [](const QQmlJS::LoggerCategory& category) { + return category.id() == qmlAttachedPropertyReuse; + }); Q_ASSERT(category != categories.end()); - category->setLevel(u"warning"_s); + category->setLevel(QtWarningMsg); + category->setIgnored(false); runTest("attachedPropNotReused.qml", Result { { Message { QStringLiteral("Using attached type QQuickKeyNavigationAttached " "already initialized in a parent " @@ -1641,6 +1878,24 @@ void TestQmllint::attachedPropertyReuse() {}, {}, {}, UseDefaultImports, &categories); runTest("attachedPropEnum.qml", Result::clean(), {}, {}, {}, UseDefaultImports, &categories); + runTest("MyStyle/ToolBar.qml", Result { + { + Message { + "Using attached type MyStyle already initialized in a parent scope"_L1, + 10, + 16 + } + }, + {}, + { + Message { + "Reference it by id instead"_L1, + 10, + 16 + } + }, + Result::AutoFixable + }); } void TestQmllint::missingBuiltinsNoCrash() @@ -1662,13 +1917,13 @@ void TestQmllint::missingBuiltinsNoCrash() checkResult(warnings, Result { { Message { QStringLiteral("Failed to find the following builtins: " - "builtins.qmltypes, jsroot.qmltypes") } } }); + "jsroot.qmltypes, builtins.qmltypes") } } }); } void TestQmllint::absolutePath() { - QString absPathOutput = runQmllint("memberNotFound.qml", false, {}, true, true, true); - QString relPathOutput = runQmllint("memberNotFound.qml", false, {}, true, true, false); + QString absPathOutput = runQmllint("memberNotFound.qml", false, warningsShouldFailArgs(), true, true, true); + QString relPathOutput = runQmllint("memberNotFound.qml", false, warningsShouldFailArgs(), true, true, false); const QString absolutePath = QFileInfo(testFile("memberNotFound.qml")).absoluteFilePath(); QVERIFY(absPathOutput.contains(absolutePath)); @@ -1683,10 +1938,14 @@ void TestQmllint::importMultipartUri() void TestQmllint::lintModule_data() { QTest::addColumn<QString>("module"); + QTest::addColumn<QStringList>("importPaths"); + QTest::addColumn<QStringList>("resources"); QTest::addColumn<Result>("result"); QTest::addRow("Things") << u"Things"_s + << QStringList() + << QStringList() << Result { { Message { u"Type \"QPalette\" not found. Used in SomethingEntirelyStrange.palette"_s, @@ -1696,16 +1955,30 @@ void TestQmllint::lintModule_data() }; QTest::addRow("missingQmltypes") << u"Fake5Compat.GraphicalEffects.private"_s + << QStringList() + << QStringList() << Result { { Message { u"QML types file does not exist"_s } } }; + + QTest::addRow("moduleWithQrc") + << u"moduleWithQrc"_s + << QStringList({ testFile("hidden") }) + << QStringList({ + testFile("hidden/qmake_moduleWithQrc.qrc"), + testFile("hidden/moduleWithQrc_raw_qml_0.qrc") + }) + << Result::clean(); } void TestQmllint::lintModule() { QFETCH(QString, module); + QFETCH(QStringList, importPaths); + QFETCH(QStringList, resources); QFETCH(Result, result); QJsonArray warnings; - callQmllint(module, false, &warnings, {}, {}, {}, {}, nullptr, false, LintModule); + callQmllint(module, result.flags & Result::ExitsNormally, &warnings, importPaths, {}, resources, + UseDefaultImports, nullptr, false, LintModule); checkResult(warnings, result); } @@ -1739,6 +2012,25 @@ void TestQmllint::testLineEndings() } } +void TestQmllint::valueTypesFromString() +{ + runTest("valueTypesFromString.qml", + Result{ { + Message{ + u"Binding is not supported: Type QSizeF should be constructed using QML_STRUCTURED_VALUE's construction mechanism, instead of a string."_s }, + Message{ + u"Binding is not supported: Type QRectF should be constructed using QML_STRUCTURED_VALUE's construction mechanism, instead of a string."_s }, + Message{ + u"Binding is not supported: Type QPointF should be constructed using QML_STRUCTURED_VALUE's construction mechanism, instead of a string."_s }, + }, + { /*bad messages */ }, + { + Message{ u"({ width: 30, height: 50 })"_s }, + Message{ u"({ x: 10, y: 20, width: 30, height: 50 })"_s }, + Message{ u"({ x: 30, y: 50 })"_s }, + } }); +} + #if QT_CONFIG(library) void TestQmllint::testPlugin() { @@ -1780,6 +2072,9 @@ void TestQmllint::testPlugin() Result { { Message { u"QtQuick.Controls and NO QtQuick present"_s } } }); // Verify that none of the passes do anything when they're not supposed to runTest("nothing_pluginTest.qml", Result::clean()); + + QVERIFY(runQmllint("settings/plugin/elemenpass_pluginSettingTest.qml", true, QStringList(), false) + .isEmpty()); } // TODO: Eventually tests for (real) plugins need to be moved into a separate file @@ -1835,7 +2130,7 @@ void TestQmllint::quickPlugin() Message { u"SplitView attached property only works with Items"_s }, Message { u"ScrollIndicator must be attached to a Flickable"_s }, Message { u"ScrollBar must be attached to a Flickable or ScrollView"_s }, - Message { u"Accessible must be attached to an Item"_s }, + Message { u"Accessible must be attached to an Item or an Action"_s }, Message { u"EnterKey attached property only works with Items"_s }, Message { u"LayoutDirection attached property only works with Items and Windows"_s }, @@ -1885,8 +2180,108 @@ void TestQmllint::quickPlugin() runTest("pluginQuick_attachedClean.qml", Result::clean()); runTest("pluginQuick_attachedIgnore.qml", Result::clean()); runTest("pluginQuick_noCrashOnUneresolved.qml", Result {}); // we don't care about the specific warnings + + runTest("pluginQuick_propertyChangesParsed.qml", + Result { { + Message { + u"Property \"myColor\" is custom-parsed in PropertyChanges. " + "You should phrase this binding as \"foo.myColor: Qt.rgba(0.5, ...\""_s, + 12, 30 + }, + Message { + u"You should remove any bindings on the \"target\" property and avoid " + "custom-parsed bindings in PropertyChanges."_s, + 11, 29 + }, + Message { + u"Unknown property \"notThere\" in PropertyChanges."_s, + 13, 31 + } + } }); + runTest("pluginQuick_propertyChangesInvalidTarget.qml", Result {}); // we don't care about the specific warnings +} + +void TestQmllint::environment_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<bool>("shouldSucceed"); + QTest::addColumn<QStringList>("extraArgs"); + QTest::addColumn<Environment>("env"); + QTest::addColumn<QString>("expectedWarning"); + + const QString fileThatNeedsImportPath = testFile(u"NeedImportPath.qml"_s); + const QString importPath = testFile(u"ImportPath"_s); + const QString invalidImportPath = testFile(u"ImportPathThatDoesNotExist"_s); + const QString noWarningExpected; + + QTest::addRow("missing-import-dir") + << fileThatNeedsImportPath << false << warningsShouldFailArgs() + << Environment{ { u"QML_IMPORT_PATH"_s, importPath } } << noWarningExpected; + + QTest::addRow("import-dir-via-arg") + << fileThatNeedsImportPath << true << QStringList{ u"-I"_s, importPath } + << Environment{ { u"QML_IMPORT_PATH"_s, invalidImportPath } } << noWarningExpected; + + QTest::addRow("import-dir-via-env") + << fileThatNeedsImportPath << true << QStringList{ u"-E"_s } + << Environment{ { u"QML_IMPORT_PATH"_s, importPath } } + << u"Using import directories passed from environment variable \"QML_IMPORT_PATH\": \"%1\"."_s + .arg(importPath); + + QTest::addRow("import-dir-via-env2") + << fileThatNeedsImportPath << true << QStringList{ u"-E"_s } + << Environment{ { u"QML2_IMPORT_PATH"_s, importPath } } + << u"Using import directories passed from the deprecated environment variable \"QML2_IMPORT_PATH\": \"%1\"."_s + .arg(importPath); } + +void TestQmllint::environment() +{ + QFETCH(QString, file); + QFETCH(bool, shouldSucceed); + QFETCH(QStringList, extraArgs); + QFETCH(Environment, env); + QFETCH(QString, expectedWarning); + + const QString output = runQmllint(file, shouldSucceed, extraArgs, false, true, false, env); + if (!expectedWarning.isEmpty()) { + QVERIFY(output.contains(expectedWarning)); + } +} + +void TestQmllint::maxWarnings() +{ + // warnings are not fatal by default + runQmllint(testFile("badScript.qml"), true); + // or when max-warnings is set to -1 + runQmllint(testFile("badScript.qml"), true, {"-W", "-1"}); + // 1 warning => should fail + runQmllint(testFile("badScript.qml"), false, {"--max-warnings", "0"}); + // only 1 warning => should exit normally + runQmllint(testFile("badScript.qml"), true, {"--max-warnings", "1"}); +} + #endif -QTEST_MAIN(TestQmllint) +void TestQmllint::ignoreSettingsNotCommandLineOptions() +{ + const QString importPath = testFile(u"ImportPath"_s); + // makes sure that ignore settings only ignores settings and not command line options like + // "-I". + const QString output = runQmllint(testFile(u"NeedImportPath.qml"_s), true, + QStringList{ u"-I"_s, importPath }, true); + // should not complain about not finding the module that is in importPath + QCOMPARE(output, QString()); +} + +void TestQmllint::backslashedQmldirPath() +{ + const QString qmldirPath + = testFile(u"ImportPath/ModuleInImportPath/qmldir"_s).replace('/', QDir::separator()); + const QString output = runQmllint( + testFile(u"something.qml"_s), true, QStringList{ u"-i"_s, qmldirPath }); + QVERIFY(output.isEmpty()); +} + +QTEST_GUILESS_MAIN(TestQmllint) #include "tst_qmllint.moc" |