Source code
Revision control
Copy as Markdown
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmlls_utils.h"
#include <algorithm>
#include <optional>
#include <QtCore/private/qduplicatetracker_p.h>
#include <QtQmlLS/private/qdochtmlparser_p.h>
// some helper constants for the tests
const static int positionAfterOneIndent = 5;
const static QString noResultExpected;
// constants for resultIndex
const static int firstResult = 0;
const static int secondResult = 1;
// constants for expectedItemsCount
const static int outOfOne = 1;
const static int outOfTwo = 2;
// enable/disable additional debug output
constexpr static bool enable_debug_output = true;
static QString readFileContent(const QString &testFileName) {
QFile file(testFileName);
if (file.open(QIODeviceBase::ReadOnly))
return QString::fromUtf8(file.readAll());
return QString{};
};
std::tuple<QQmlJS::Dom::DomItem, QQmlJS::Dom::DomItem>
tst_qmlls_utils::createEnvironmentAndLoadFile(const QString &filePath, const QStringList &extraBuildDir)
{
CacheKey cacheKey = QDir::cleanPath(filePath + u"/.."_s);
if (auto entry = cache.find(cacheKey); entry != cache.end()) {
QQmlJS::Dom::DomItem env{ *entry };
return { env, env.field(QQmlJS::Dom::Fields::qmlFileWithPath).key(filePath) };
};
const QStringList qmltypeDirs =
QStringList({ dataDirectory(), QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath),
dataDirectory() + u"/sophisticatedBuildFolder"_s,
}) += extraBuildDir;
auto envPtr = QQmlJS::Dom::DomEnvironment::create(
qmltypeDirs, QQmlJS::Dom::DomEnvironment::Option::SingleThreaded,
QQmlJS::Dom::Extended);
QQmlJS::Dom::DomItem file;
QQmlJS::Dom::DomItem env(envPtr);
envPtr->loadFile(QQmlJS::Dom::FileToLoad::fromFileSystem(envPtr, filePath),
[&file](QQmlJS::Dom::Path, const QQmlJS::Dom::DomItem &,
const QQmlJS::Dom::DomItem &newIt) { file = newIt; });
envPtr->loadPendingDependencies();
cache[cacheKey] = envPtr;
return std::make_tuple(env, file);
}
void tst_qmlls_utils::textOffsetRowColumnConversions_data()
{
QTest::addColumn<QString>("code");
QTest::addColumn<int>("line");
QTest::addColumn<int>("character");
QTest::addColumn<qsizetype>("expectedOffset");
QTest::addColumn<QChar>("expectedChar");
// in case they differ from line and character, e.g. when accessing non-existing line or rows
// set to -1 when same as before
QTest::addColumn<int>("expectedLine");
QTest::addColumn<int>("expectedCharacter");
QTest::newRow("oneline") << u"Hello World!"_s << 0 << 6 << 6ll << QChar('W') << -1 << -1;
QTest::newRow("multi-line") << u"Hello World!\n How are you? \n Bye!\n"_s << 0 << 6 << 6ll
<< QChar('W') << -1 << -1;
QTest::newRow("multi-line2") << u"Hello World!\n How are you? \n Bye!\n"_s << 1 << 5 << 18ll
<< QChar('a') << -1 << -1;
QTest::newRow("multi-line3") << u"Hello World!\n How are you? \n Bye!\n"_s << 2 << 1 << 29ll
<< QChar('B') << -1 << -1;
QTest::newRow("newlines") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 0 << 0 << 0ll << QChar('A') << -1
<< -1;
QTest::newRow("newlines2") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 1 << 0 << 2ll << QChar('B') << -1
<< -1;
// try to access '\r'
QTest::newRow("newlines3") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 1 << 1 << 3ll << QChar('\r') << -1
<< -1;
// try to access '\n', should return the last character of the line (which is '\n' in this case)
QTest::newRow("newlines4") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 1 << 2 << 4ll << QChar('\n') << -1
<< -1;
// try to access after the end of the line, should return the last character of the line (which
// is '\n' in this case)
QTest::newRow("afterLineEnd") << u"A\nB\r\nC\n\r\nD\r\n\r"_s << 1 << 42 << 4ll << QChar('\n')
<< -1 << 2;
// try to access an inexisting column, seems to return the last character of the last line.
QTest::newRow("afterColumnEnd")
<< u"A\nB\r\nC\n\r\nD\r\n\rAX"_s << 42 << 0 << 15ll << QChar('X') << 6 << 1;
// \n\r are two newlines
QTest::newRow("\\n\\r")
<< u"A\n\rB"_s << 2 << 0 << 3ll << QChar('B') << -1 << -1;
// \r\n is a newline
QTest::newRow("\\r\\n")
<< u"A\r\nB"_s << 1 << 0 << 3ll << QChar('B') << -1 << -1;
QTest::newRow("windowsNewlineAfterColumnEnd")
<< u"A\r\nB\r\n"_s << 0 << 42 << 2ll << QChar('\n') << 0 << 2;
QTest::newRow("windowsNewlineAfterLineEnd")
<< u"A\r\nB\r\n"_s << 42 << 0 << 5ll << QChar('\n') << 1 << 2;
QTest::newRow("windowsNewlineR")
<< u"A\r\nB\r\n"_s << 0 << 1 << 1ll << QChar('\r') << 0 << 1;
}
void tst_qmlls_utils::textOffsetRowColumnConversions()
{
QFETCH(QString, code);
QFETCH(int, line);
QFETCH(int, character);
QFETCH(qsizetype, expectedOffset);
QFETCH(QChar, expectedChar);
QFETCH(int, expectedLine);
QFETCH(int, expectedCharacter);
qsizetype offset = QQmlLSUtils::textOffsetFrom(code, line, character);
QCOMPARE(offset, expectedOffset);
if (offset < code.size())
QCOMPARE(code[offset], expectedChar);
auto [computedRow, computedColumn] = QQmlLSUtils::textRowAndColumnFrom(code, expectedOffset);
if (expectedLine == -1)
expectedLine = line;
if (expectedCharacter == -1)
expectedCharacter = character;
QCOMPARE(computedRow, expectedLine);
QCOMPARE(computedColumn, expectedCharacter);
}
void tst_qmlls_utils::findItemFromLocation_data()
{
QTest::addColumn<QString>("filePath");
// keep in mind that line and character are starting at 1!
QTest::addColumn<int>("line");
QTest::addColumn<int>("character");
// in case there are multiple items to be found (e.g. for a location between two objects), the
// item to be checked against
QTest::addColumn<int>("resultIndex");
QTest::addColumn<int>("expectedItemsCount");
QTest::addColumn<QQmlJS::Dom::DomType>("expectedType");
// set to -1 when unchanged from above line and character, starts at 1
QTest::addColumn<int>("expectedLine");
QTest::addColumn<int>("expectedCharacter");
const QString file1Qml = testFile(u"file1.qml"_s);
QTest::addRow("findIntProperty") << file1Qml << 9 << 18 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::PropertyDefinition
// start of the "property"-token of the "a" property
<< -1 << positionAfterOneIndent;
QTest::addRow("findIntProperty2") << file1Qml << 9 << 10 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::PropertyDefinition
// start of the "property"-token of the "a" property
<< -1 << positionAfterOneIndent;
QTest::addRow("findIntBinding")
<< file1Qml << 30 << positionAfterOneIndent << firstResult << outOfOne
<< QQmlJS::Dom::DomType::ScriptIdentifierExpression
// start of the a identifier of the "a" binding
<< -1 << positionAfterOneIndent;
QTest::addRow("findIntBinding2") << file1Qml << 30 << 8 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::ScriptLiteral
<< -1 << 8;
QTest::addRow("colorBinding") << file1Qml << 39 << 13 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1
<< 2 * positionAfterOneIndent - 1;
QTest::addRow("findVarProperty") << file1Qml << 12 << 12 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::PropertyDefinition
// start of the "property"-token of the "d" property
<< -1 << positionAfterOneIndent;
QTest::addRow("findVarBinding") << file1Qml << 31 << 8 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::ScriptLiteral << -1 << 8;
QTest::addRow("beforeEProperty")
<< file1Qml << 13 << positionAfterOneIndent << firstResult << outOfOne
<< QQmlJS::Dom::DomType::PropertyDefinition
// start of the "property"-token of the "e" property
<< -1 << -1;
QTest::addRow("onEProperty") << file1Qml << 13 << 24 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::PropertyDefinition
// start of the "property"-token of the "e" property
<< -1 << positionAfterOneIndent;
QTest::addRow("afterEProperty") << file1Qml << 13 << 25 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::PropertyDefinition
// start of the "property"-token of the "e" property
<< -1 << positionAfterOneIndent;
QTest::addRow("property-in-ic") << file1Qml << 28 << 38 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::PropertyDefinition << -1 << 26;
QTest::addRow("onCChild") << file1Qml << 16 << positionAfterOneIndent << firstResult << outOfOne
<< QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1
<< positionAfterOneIndent;
// check for off-by-one/overlapping items
QTest::addRow("closingBraceOfC")
<< file1Qml << 16 << 19 << firstResult << outOfOne << QQmlJS::Dom::DomType::QmlObject
<< -1 << positionAfterOneIndent;
QTest::addRow("beforeClosingBraceOfC")
<< file1Qml << 16 << 18 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 12;
QTest::addRow("firstBetweenCandD")
<< file1Qml << 16 << 20 << secondResult << outOfTwo << QQmlJS::Dom::DomType::QmlObject
<< -1 << positionAfterOneIndent;
QTest::addRow("secondBetweenCandD")
<< file1Qml << 16 << 20 << firstResult << outOfTwo
<< QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << -1;
QTest::addRow("afterD") << file1Qml << 16 << 21 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 20;
// check what happens between items (it should not crash)
QTest::addRow("onWhitespaceBeforeC")
<< file1Qml << 16 << 1 << firstResult << outOfOne << QQmlJS::Dom::DomType::Map << 9
<< positionAfterOneIndent;
QTest::addRow("onWhitespaceAfterC")
<< file1Qml << 17 << 8 << firstResult << outOfOne << QQmlJS::Dom::DomType::QmlObject
<< -1 << positionAfterOneIndent;
QTest::addRow("onWhitespaceBetweenCAndD") << file1Qml << 17 << 23 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::Map << 16 << 8;
QTest::addRow("onWhitespaceBetweenCAndD2") << file1Qml << 17 << 24 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::Map << 16 << 8;
QTest::addRow("ic") << file1Qml << 15 << 5 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::QmlComponent << -1 << 5;
QTest::addRow("ic2") << file1Qml << 15 << 20 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 18;
QTest::addRow("ic3") << file1Qml << 15 << 33 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 29;
QTest::addRow("function") << file1Qml << 33 << 5 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::MethodInfo << -1 << positionAfterOneIndent;
QTest::addRow("function-parameter")
<< file1Qml << 33 << 20 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 19;
// The return type of a function has no own DomItem. Instead, the return type of a function
// is saved into the MethodInfo.
QTest::addRow("function-return")
<< file1Qml << 33 << 41 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 41;
QTest::addRow("function2") << file1Qml << 36 << 17 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::MethodInfo << -1
<< positionAfterOneIndent;
// check rectangle property
QTest::addRow("rectangle-property")
<< file1Qml << 44 << 31 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::ScriptIdentifierExpression << -1 << 29;
// check comment
QTest::addRow("pre-comment")
<< file1Qml << 62 << 15 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::Comment << -1 << 5;
QTest::addRow("post-comment")
<< file1Qml << 64 << 15 << firstResult << outOfOne
<< QQmlJS::Dom::DomType::Comment << -1 << 5;
}
void tst_qmlls_utils::findItemFromLocation()
{
QFETCH(QString, filePath);
QFETCH(int, line);
QFETCH(int, character);
QFETCH(int, resultIndex);
QFETCH(int, expectedItemsCount);
QFETCH(QQmlJS::Dom::DomType, expectedType);
QFETCH(int, expectedLine);
QFETCH(int, expectedCharacter);
if (expectedLine == -1)
expectedLine = line;
if (expectedCharacter == -1)
expectedCharacter = character;
// they all start at 1.
Q_ASSERT(line > 0);
Q_ASSERT(character > 0);
Q_ASSERT(expectedLine > 0);
Q_ASSERT(expectedCharacter > 0);
auto [env, file] = createEnvironmentAndLoadFile(filePath);
auto locations = QQmlLSUtils::itemsFromTextLocation(
file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
QVERIFY(resultIndex < locations.size());
QCOMPARE(locations.size(), expectedItemsCount);
QQmlJS::Dom::DomItem itemToTest = locations[resultIndex].domItem;
// ask for the type in the args
if constexpr (enable_debug_output) {
if (itemToTest.internalKind() != expectedType) {
qDebug() << itemToTest.internalKindStr() << " has not the expected kind "
<< expectedType << " for item " << itemToTest.toString();
}
}
QCOMPARE(itemToTest.internalKind(), expectedType);
QQmlJS::Dom::FileLocations::Tree locationToTest = locations[resultIndex].fileLocation;
QCOMPARE(locationToTest->info().fullRegion.startLine, quint32(expectedLine));
QCOMPARE(locationToTest->info().fullRegion.startColumn, quint32(expectedCharacter));
}
void tst_qmlls_utils::findTypeDefinitionFromLocation_data()
{
QTest::addColumn<QString>("filePath");
// keep in mind that line and character are starting at 1!
QTest::addColumn<int>("line");
QTest::addColumn<int>("character");
// in case there are multiple items to be found (e.g. for a location between two objects), the
// item to be checked against
QTest::addColumn<int>("resultIndex");
QTest::addColumn<int>("expectedItemsCount");
QTest::addColumn<QString>("expectedFilePath");
// set to -1 when unchanged from above line and character. 0-based.
QTest::addColumn<int>("expectedLine");
QTest::addColumn<int>("expectedCharacter");
const QString file1Qml = testFile(u"file1.qml"_s);
const QString TypeQml = testFile(u"Type.qml"_s);
// pass this as file when no result is expected, e.g. for type definition of "var".
QTest::addRow("onCProperty") << file1Qml << 11 << 16 << firstResult << outOfOne << file1Qml << 7
<< 18;
QTest::addRow("onCProperty2") << file1Qml << 28 << 37 << firstResult << outOfOne << file1Qml
<< 7 << 18;
QTest::addRow("onCProperty3") << file1Qml << 28 << 35 << firstResult << outOfOne << file1Qml
<< 7 << 18;
QTest::addRow("onCBinding") << file1Qml << 46 << 8 << firstResult << outOfOne << file1Qml << 7
<< 18;
QTest::addRow("onDefaultBinding") << file1Qml << 16 << positionAfterOneIndent << firstResult
<< outOfOne << file1Qml << 7 << 18;
QTest::addRow("onDefaultBindingId")
<< file1Qml << 16 << 28 << firstResult << outOfOne << file1Qml << 16 << 20;
QTest::addRow("findIntProperty") << file1Qml << 9 << 18 << firstResult << outOfOne << file1Qml
<< -1 << positionAfterOneIndent;
QTest::addRow("colorBinding") << file1Qml << 39 << 8 << firstResult << outOfOne << file1Qml
<< -1 << positionAfterOneIndent;
// check what happens between items (it should not crash)
QTest::addRow("onWhitespaceBeforeC")
<< file1Qml << 16 << 1 << firstResult << outOfOne << noResultExpected << -1 << -1;
QTest::addRow("onWhitespaceAfterC")
<< file1Qml << 17 << 23 << firstResult << outOfOne << noResultExpected << -1 << -1;
QTest::addRow("onWhitespaceBetweenCAndD")
<< file1Qml << 17 << 24 << firstResult << outOfOne << noResultExpected << -1 << -1;
QTest::addRow("ic") << file1Qml << 15 << 15 << firstResult << outOfOne << file1Qml << 15 << 18;
QTest::addRow("icBase") << file1Qml << 15 << 20 << firstResult << outOfOne
<< u"TODO: file location for C++ defined types?"_s << -1 << -1;
QTest::addRow("ic3") << file1Qml << 15 << 33 << firstResult << outOfOne << file1Qml << -1 << 18;
QTest::addRow("function-parameter-builtin")
<< file1Qml << 33 << 20 << firstResult << outOfOne << file1Qml << -1 << -1;
QTest::addRow("function-parameter-item")
<< file1Qml << 33 << 36 << firstResult << outOfOne << file1Qml << 7 << 18;
QTest::addRow("function-return")
<< file1Qml << 33 << 41 << firstResult << outOfOne << file1Qml << 7 << 18;
QTest::addRow("rectangle-property") << file1Qml << 44 << 31 << firstResult << outOfOne
<< "TODO: c++ type location" << -1 << -1;
QTest::addRow("functionParameterICUsage")
<< file1Qml << 34 << 16 << firstResult << outOfOne << file1Qml << 7 << 18;
QTest::addRow("ICBindingUsage")
<< file1Qml << 47 << 21 << firstResult << outOfOne << file1Qml << 7 << 18;
QTest::addRow("ICBindingUsage2")
<< file1Qml << 49 << 11 << firstResult << outOfOne << file1Qml << 7 << 18;
QTest::addRow("ICBindingUsage3")
<< file1Qml << 52 << 17 << firstResult << outOfOne << file1Qml << 7 << 18;
QTest::addRow("ICBindingUsageFromLambda")
<< file1Qml << 58 << 23 << firstResult << outOfOne << file1Qml << 7 << 18;
}
void tst_qmlls_utils::findTypeDefinitionFromLocation()
{
QFETCH(QString, filePath);
QFETCH(int, line);
QFETCH(int, character);
QFETCH(int, resultIndex);
QFETCH(int, expectedItemsCount);
QFETCH(QString, expectedFilePath);
QFETCH(int, expectedLine);
QFETCH(int, expectedCharacter);
if (expectedLine == -1)
expectedLine = line;
if (expectedCharacter == -1)
expectedCharacter = character;
// they all start at 1.
Q_ASSERT(line > 0);
Q_ASSERT(character > 0);
Q_ASSERT(expectedLine > 0);
Q_ASSERT(expectedCharacter > 0);
auto [env, file] = createEnvironmentAndLoadFile(filePath);
auto locations = QQmlLSUtils::itemsFromTextLocation(
file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
QCOMPARE(locations.size(), expectedItemsCount);
auto base = QQmlLSUtils::findTypeDefinitionOf(locations[resultIndex].domItem);
// if expectedFilePath is empty, we probably just want to make sure that it does
// not crash
if (expectedFilePath == noResultExpected) {
QVERIFY(!base);
return;
}
QEXPECT_FAIL("findIntProperty", "Builtins not supported yet", Abort);
QEXPECT_FAIL("function-parameter-builtin", "Base types defined in C++ are not supported yet",
Abort);
QEXPECT_FAIL("colorBinding", "Types from C++ bases not supported yet", Abort);
QEXPECT_FAIL("rectangle-property", "Types from C++ bases not supported yet", Abort);
QEXPECT_FAIL("icBase", "Base types defined in C++ are not supported yet", Abort);
QVERIFY(base);
auto fileObject =
locations[resultIndex].domItem.goToFile(base->filename()).as<QQmlJS::Dom::QmlFile>();
// print some debug message when failing, instead of using QVERIFY2
// (printing the type every time takes a lot of time).
if constexpr (enable_debug_output) {
if (!fileObject)
qDebug() << "Could not find the file" << base->filename() << "in the Dom.";
}
QVERIFY(fileObject);
QCOMPARE(base->filename(), expectedFilePath);
QCOMPARE(fileObject->canonicalFilePath(), expectedFilePath);
QCOMPARE(base->sourceLocation().startLine, quint32(expectedLine));
QCOMPARE(base->sourceLocation().startColumn, quint32(expectedCharacter));
}
void tst_qmlls_utils::findLocationOfItem_data()
{
QTest::addColumn<QString>("filePath");
QTest::addColumn<int>("line");
QTest::addColumn<int>("character");
QTest::addColumn<int>("expectedLine");
QTest::addColumn<int>("expectedCharacter");
const QString file1Qml = testFile(u"file1.qml"_s);
QTest::addRow("root-element") << file1Qml << 6 << 2 << -1 << 1;
QTest::addRow("property-a") << file1Qml << 9 << 18 << -1 << positionAfterOneIndent;
QTest::addRow("property-a2") << file1Qml << 9 << 10 << -1 << positionAfterOneIndent;
QTest::addRow("nested-C") << file1Qml << 20 << 9 << -1 << -1;
QTest::addRow("nested-C2") << file1Qml << 23 << 13 << -1 << -1;
QTest::addRow("D") << file1Qml << 17 << 33 << -1 << 32;
QTest::addRow("property-d-var-type") << file1Qml << 12 << 15 << -1 << 14;
QTest::addRow("import") << file1Qml << 4 << 6 << -1 << 1;
}
void tst_qmlls_utils::findLocationOfItem()
{
QFETCH(QString, filePath);
QFETCH(int, line);
QFETCH(int, character);
QFETCH(int, expectedLine);
QFETCH(int, expectedCharacter);
if (expectedLine == -1)
expectedLine = line;
if (expectedCharacter == -1)
expectedCharacter = character;
// they all start at 1.
Q_ASSERT(line > 0);
Q_ASSERT(character > 0);
Q_ASSERT(expectedLine > 0);
Q_ASSERT(expectedCharacter > 0);
auto [env, file] = createEnvironmentAndLoadFile(filePath);
// grab item using already tested QQmlLSUtils::findLastItemsContaining
auto locations = QQmlLSUtils::itemsFromTextLocation(
file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
QCOMPARE(locations.size(), 1);
// once the item is grabbed, make sure its line/character position can be obtained back
auto t = QQmlJS::Dom::FileLocations::treeOf(locations.front().domItem);
QCOMPARE(t->info().fullRegion.startLine, quint32(expectedLine));
QCOMPARE(t->info().fullRegion.startColumn, quint32(expectedCharacter));
}
/*! \internal
\brief Wrapper for findUsages data.
*/
struct UsageData
{
QString testFileName;
QQmlLSUtils::Usages expectedUsages;
};
void tst_qmlls_utils::findUsages_data()
{
QTest::addColumn<int>("line");
QTest::addColumn<int>("character");
QTest::addColumn<UsageData>("data");
const auto readFileContent = [](const QString &testFileName) {
QFile file(testFileName);
if (file.open(QIODeviceBase::ReadOnly))
return QString::fromUtf8(file.readAll());
return QString{};
};
const auto makeUsages = [](const QString &fileName, QList<QQmlLSUtils::Location> &locations) {
UsageData data;
std::sort(locations.begin(), locations.end());
data.expectedUsages = { locations, {} };
data.testFileName = fileName;
return data;
};
{
QList<QQmlLSUtils::Location> expectedUsages;
const auto testFileName = testFile("findUsages/jsIdentifier/jsIdentifier.qml");
const auto testFileContent = readFileContent(testFileName);
{
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 13,
strlen("sum"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 13,
strlen("sum"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 19,
strlen("sum"));
const auto sumUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findSumFromDeclaration") << 8 << 13 << sumUsages;
QTest::addRow("findSumFromUsage") << 10 << 20 << sumUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 17,
strlen("i"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 24,
strlen("i"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 32,
strlen("i"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 36,
strlen("i"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 25,
strlen("i"));
const auto iUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findIFromDeclaration") << 9 << 17 << iUsages;
QTest::addRow("findIFromUsage") << 9 << 24 << iUsages;
QTest::addRow("findIFromUsage2") << 10 << 25 << iUsages;
}
}
{
const auto testFileName = testFile("findUsages/property/property.qml");
const auto otherFile = testFile("findUsages/property/PropertyFromAnotherFile.qml");
const auto testFileContent = readFileContent(testFileName);
const auto otherFileContent = readFileContent(otherFile);
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 18,
strlen("helloProperty"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 13, 13,
strlen("helloProperty"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 13, 29,
strlen("helloProperty"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 20, 9,
strlen("helloProperty"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 21, 9,
strlen("helloPropertyChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 5,
strlen("onHelloPropertyChanged"));
const auto helloPropertyUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findPropertyFromDeclaration") << 8 << 18 << helloPropertyUsages;
QTest::addRow("findPropertyFromUsage") << 13 << 13 << helloPropertyUsages;
QTest::addRow("findPropertyFromUsage2") << 13 << 29 << helloPropertyUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 36, 20,
strlen("helloProperty"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 38, 25,
strlen("helloProperty"));
const auto subItemHelloPropertyUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findPropertyFromDeclarationInSubItem") << 38 << 25 << subItemHelloPropertyUsages;
QTest::addRow("findPropertyFromUsageInSubItem") << 36 << 20 << subItemHelloPropertyUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 27, 22,
strlen("helloProperty"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 29, 20,
strlen("helloProperty"));
const auto ICHelloPropertyUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findPropertyFromDeclarationInIC") << 27 << 22 << ICHelloPropertyUsages;
QTest::addRow("findPropertyFromUsageInIC") << 29 << 20 << ICHelloPropertyUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(otherFile, otherFileContent, 4, 18,
strlen("helloProperty"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 42, 9,
strlen("helloProperty"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 44, 20,
strlen("helloProperty"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 46, 9,
strlen("OnHelloPropertyChanged"));
const auto helloPropertyUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findPropertyFromOtherFile") << 42 << 13 << helloPropertyUsages;
}
}
{
const auto testFileName = testFile("findUsages/propertyInNested/propertyInNested.qml");
const auto testFileContent = readFileContent(testFileName);
const auto componentFileName =
testFile("findUsages/propertyInNested/NestedComponentInFile.qml");
const auto componentFileContent = readFileContent(componentFileName);
const auto componentFileName3 =
testFile("findUsages/propertyInNested/NestedComponentInFile3.qml");
const auto componentFileContent3 = readFileContent(componentFileName);
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 7, 18,
strlen("p2"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 31,
strlen("p2"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 34, 37,
strlen("p2"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 35, 43,
strlen("p2"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 36, 49,
strlen("p2"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 42, 26,
strlen("p2"));
const auto p2Usages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findPropertyFromDeclaration2") << 7 << 18 << p2Usages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 29, 13,
strlen("myNested"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 32, 17,
strlen("myNested"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 17,
strlen("myNested"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 34, 17,
strlen("myNested"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 35, 17,
strlen("myNested"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 36, 17,
strlen("myNested"));
const auto nestedUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findIdFromUsage") << 36 << 20 << nestedUsages;
QTest::addRow("findIdFromDefinition") << 29 << 17 << nestedUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 35,
strlen("inner"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 32, 32,
strlen("inner"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 35, 32,
strlen("inner"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 36, 32,
strlen("inner"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 16, 9,
strlen("inner"));
const auto nestedComponent3Usages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findPropertyFromUsageInFieldMemberExpression")
<< 36 << 34 << nestedComponent3Usages;
QTest::addRow("findFieldMemberExpressionUsageFromPropertyDefinition")
<< 14 << 38 << nestedComponent3Usages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(componentFileName, componentFileContent,
4, 37, strlen("inner"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 50, 32,
strlen("inner"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 52, 32,
strlen("inner"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 53, 32,
strlen("inner"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 54, 32,
strlen("inner"));
const auto nestedComponent3Usages = makeUsages(testFileName, expectedUsages);
const auto nestedComponent3UsagesFromOtherFile =
makeUsages(componentFileName, expectedUsages);
QTest::addRow("findPropertyFromUsageInFieldMemberExpressionFromOtherFile")
<< 50 << 33 << nestedComponent3Usages;
QTest::addRow("findFieldMemberExpressionUsageFromPropertyDefinitionFromOtherFile")
<< 4 << 38 << nestedComponent3UsagesFromOtherFile;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 35, 38,
strlen("p2"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 20, 22,
strlen("p2"));
const auto nestedComponent3P2Usages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findProperty2FromUsageInFieldMemberExpression")
<< 35 << 39 << nestedComponent3P2Usages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(componentFileName3, componentFileContent3,
5, 18, strlen("p2"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 53, 44,
strlen("p2"));
const auto nestedComponent3P2Usages = makeUsages(testFileName, expectedUsages);
const auto nestedComponent3P2UsagesFromOtherFile = makeUsages(componentFileName3, expectedUsages);
QTest::addRow("findProperty2FromUsageInFieldMemberExpressionInOtherFile")
<< 53 << 44 << nestedComponent3P2Usages;
QTest::addRow("findProperty2FromUsageInDefinitionInOtherFile")
<< 5 << 19 << nestedComponent3P2UsagesFromOtherFile;
}
}
{
QList<QQmlLSUtils::Location> expectedUsages;
const auto testFileName = testFile("findUsages/idUsages/idUsages.qml");
const auto testFileContent = readFileContent(testFileName);
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 7, 9,
strlen("rootId"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 11, 17,
strlen("rootId"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 12, 20,
strlen("rootId"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 17, 9,
strlen("rootId"));
const auto rootIdUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findIdFromUsageInChild") << 12 << 20 << rootIdUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
const auto testFileName = testFile("findUsages/recursive/recursive.qml");
const auto testFileContent = readFileContent(testFileName);
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 14,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 24,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 34,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 51,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 68,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 12, 20,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 15, 34,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 19, 27,
strlen("recursive"));
const auto recursiveUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findFunctionUsage") << 10 << 30 << recursiveUsages;
QTest::addRow("findFunctionUsage2") << 12 << 24 << recursiveUsages;
QTest::addRow("findQualifiedFunctionUsage") << 19 << 31 << recursiveUsages;
QTest::addRow("findFunctionUsageFromDefinition") << 8 << 17 << recursiveUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
const auto testFileName = testFile("findUsages/recursive/recursive.qml");
const auto testFileContent = readFileContent(testFileName);
const auto otherFileName = testFile("findUsages/recursive/RecursiveInOtherFile.qml");
const auto otherFileContent = readFileContent(otherFileName);
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 27, 61,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 4, 14,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 6, 24,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 6, 34,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 6, 51,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 6, 68,
strlen("recursive"));
expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 8, 20,
strlen("recursive"));
const auto recursiveUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findFunctionUsageFromOtherFile") << 27 << 64 << recursiveUsages;
const auto recursiveUsagesFromOtherFile = makeUsages(otherFileName, expectedUsages);
QTest::addRow("findFunctionUsageFromSameFile") << 6 << 39 << recursiveUsagesFromOtherFile;
QTest::addRow("findFunctionUsageFromDefinitionInOtherFile")
<< 4 << 14 << recursiveUsagesFromOtherFile;
}
{
const auto testFileName = testFile("findUsages/signalsAndHandlers/signalsAndHandlers.qml");
const auto testFileContent = readFileContent(testFileName);
const auto otherFileName = testFile("findUsages/signalsAndHandlers/widthChangedInAnotherFile.qml");
const auto otherFileContent = readFileContent(otherFileName);
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 12,
strlen("helloSignal"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 11, 9,
strlen("helloSignal"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 13, 13,
strlen("helloSignal"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 17, 17,
strlen("helloSignal"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 21, 9,
strlen("helloSignal"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 39, 5,
strlen("onHelloSignal"));
const auto helloSignalUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findQmlSignalUsageFromDefinition") << 8 << 17 << helloSignalUsages;
QTest::addRow("findQmlSignalUsageFromUsage") << 13 << 17 << helloSignalUsages;
QTest::addRow("findQmlSignalUsageFromHandler") << 39 << 11 << helloSignalUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 5, 5,
strlen("onWidthChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 13,
strlen("widthChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 27, 17,
strlen("widthChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 28, 20,
strlen("widthChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 34, 20,
strlen("widthChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 13,
strlen("widthChanged"));
const auto widthChangedUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findCppSignalUsageFromUsage") << 27 << 23 << widthChangedUsages;
QTest::addRow("findCppSignalUsageFromQualifiedUsage") << 28 << 23 << widthChangedUsages;
QTest::addRow("findCppSignalUsageFromQualifiedUsage2") << 34 << 24 << widthChangedUsages;
}
}
{
const auto testFileName = testFile("findUsages/binding/binding.qml");
const auto testFileContent = readFileContent(testFileName);
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 18,
strlen("helloPropertyBinding"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 5,
strlen("helloPropertyBinding"));
const auto helloPropertyBindingUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findBindingUsagesFromDefinition") << 9 << 21 << helloPropertyBindingUsages;
QTest::addRow("findBindingUsagesFromBinding") << 10 << 19 << helloPropertyBindingUsages;
}
{
const auto testFileName = testFile("findUsages/signalsAndHandlers/signalAndHandlers2.qml");
const auto testFileContent = readFileContent(testFileName);
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 7, 14,
strlen("myHelloHandler"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 20,
strlen("myHelloHandler"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 29,
strlen("myHelloHandler"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 15, 24,
strlen("myHelloHandler"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 17,
strlen("myHelloHandler"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 24, 24,
strlen("myHelloHandler"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 25, 21,
strlen("myHelloHandler"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 19,
strlen("myHelloHandler"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 42, 29,
strlen("myHelloHandler"));
const auto myHelloHandlerUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findJSMethodFromUsageInBinding") << 8 << 27 << myHelloHandlerUsages;
QTest::addRow("findJSMethodFromDefinition") << 7 << 22 << myHelloHandlerUsages;
QTest::addRow("findJSMethodFromDefinition2") << 7 << 9 << myHelloHandlerUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 13, 18,
strlen("checkHandlers"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 5,
strlen("onCheckHandlersChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 17, 9,
strlen("checkHandlersChanged"));
const auto checkHandlersUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findQmlPropertyHandlerFromDefinition") << 13 << 18 << checkHandlersUsages;
QTest::addRow("findQmlPropertyHandlerFromHandler") << 14 << 5 << checkHandlersUsages;
QTest::addRow("findQmlPropertyHandlerFromSignalCall") << 17 << 9 << checkHandlersUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 15, 5,
strlen("onChildrenChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 18, 9,
strlen("childrenChanged"));
const auto checkCppHandlersUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findCppPropertyHandlerFromHandler") << 15 << 5 << checkCppHandlersUsages;
QTest::addRow("findCppPropertyHandlerFromSignalCall") << 18 << 9 << checkCppHandlersUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 20, 18,
strlen("_"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 5,
strlen("on_Changed"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 27, 9,
strlen("_Changed"));
const auto checkHandlersUsages2 = makeUsages(testFileName, expectedUsages);
QTest::addRow("findQmlPropertyHandler2FromDefinition") << 20 << 18 << checkHandlersUsages2;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 21, 18,
strlen("______42"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 24, 5,
strlen("on______42Changed"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 28, 9,
strlen("______42Changed"));
const auto checkHandlersUsages3 = makeUsages(testFileName, expectedUsages);
QTest::addRow("findQmlPropertyHandler3FromDefinition") << 21 << 18 << checkHandlersUsages3;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 22, 18,
strlen("_123a"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 25, 5,
strlen("on_123AChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 29, 9,
strlen("_123aChanged"));
const auto checkHandlersUsages4 = makeUsages(testFileName, expectedUsages);
QTest::addRow("findQmlPropertyHandler4FromDefinition") << 22 << 18 << checkHandlersUsages4;
}
}
{
QList<QQmlLSUtils::Location> expectedUsages;
const auto testFileName = testFile("findUsages/connections/connections.qml");
const auto testFileContent = readFileContent(testFileName);
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 9,
strlen("onClicked"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 17, 23,
strlen("clicked"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 15,
strlen("clicked"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 16, 22,
strlen("onClicked"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 34, 15,
strlen("clicked"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 18, 23,
strlen("clicked"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 28, 9,
strlen("onClicked"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 35, 15,
strlen("clicked"));
const auto signalInConnection = makeUsages(testFileName, expectedUsages);
QTest::addRow("findSignalsInConnectionFromSignal") << 33 << 15 << signalInConnection;
QTest::addRow("findSignalsInConnectionFromHandler") << 9 << 9 << signalInConnection;
QTest::addRow("findSignalsInConnectionFromFunction") << 16 << 22 << signalInConnection;
}
{
const auto testFileName =
testFile("findUsages/parametersAndDeconstruction/parametersAndDeconstruction.qml");
const auto testFileContent = readFileContent(testFileName);
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 30,
strlen("a"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 16,
strlen("a"));
const auto aParamUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findMethodParameterA") << 9 << 16 << aParamUsages;
QTest::addRow("findMethodParameterAFromUsage") << 8 << 30 << aParamUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 50,
strlen("x"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 28,
strlen("x"));
const auto xParamUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findMethodParameterXDeconstructed") << 8 << 50 << xParamUsages;
QTest::addRow("findMethodParameterXDeconstructedFromUsage") << 9 << 28 << xParamUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 53,
strlen("y"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 32,
strlen("y"));
const auto yParamUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findMethodParameterYDeconstructed") << 8 << 53 << yParamUsages;
QTest::addRow("findMethodParameterYDeconstructedFromUsage") << 9 << 32 << yParamUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 59,
strlen("z"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 36,
strlen("z"));
const auto zParamUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("findMethodParameterZDeconstructed") << 8 << 59 << zParamUsages;
QTest::addRow("findMethodParameterZDeconstructedFromUsage") << 9 << 36 << zParamUsages;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 13, 14,
strlen("a"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 17,
strlen("a"));
const auto deconstructedAUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("deconstructed") << 14 << 17 << deconstructedAUsages;
QTest::addRow("deconstructedFromDefinition") << 13 << 14 << deconstructedAUsages;
}
}
{
const auto testFileName = testFile("findUsages/groupedPropertyUsage/groupedPropertyUsage.qml");
const auto testFileContent = readFileContent(testFileName);
const auto otherFileName = testFile("findUsages/groupedPropertyUsage/fontFamilyUsage.qml");
const auto otherFileContent = readFileContent(otherFileName);
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 5, 34,
strlen("family"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 17,
strlen("family"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 35,
strlen("family"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 10,
strlen("family"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 48,
strlen("family"));
const auto groupedPropertyUsages1 = makeUsages(testFileName, expectedUsages);
QTest::addRow("groupedPropertyUsages1") << 14 << 17 << groupedPropertyUsages1;
const auto groupedPropertyUsages1FromOtherFile =
makeUsages(otherFileName, expectedUsages);
QTest::addRow("groupedPropertyUsages1FromOtherFile")
<< 5 << 37 << groupedPropertyUsages1FromOtherFile;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 5,
strlen("font"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 24, 5,
strlen("font"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 12, 13,
strlen("font"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 30,
strlen("font"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 32, 41,
strlen("font"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 33, 43,
strlen("font"));
const auto groupedPropertyUsages2 = makeUsages(testFileName, expectedUsages);
QTest::addRow("groupedPropertyUsages2") << 23 << 5 << groupedPropertyUsages2;
}
}
{
const auto testFileName =
testFile("findUsages/attachedPropertyUsage/attachedPropertyUsage.qml");
const auto testFileContent = readFileContent(testFileName);
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 5,
strlen("Keys"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 12, 25,
strlen("Keys"));
const auto attachedPropertyUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("attachedPropertyUsages") << 12 << 25 << attachedPropertyUsages;
}
{
const auto testFileName = testFile("findUsages/inlineComponents/inlineComponents.qml");
const auto testFileContent = readFileContent(testFileName);
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 22,
strlen("foo"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 44,
strlen("foo"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 27,
strlen("foo"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 20, 20,
strlen("foo"));
const auto inlineUsages = makeUsages(testFileName, expectedUsages);
QTest::addRow("inlineUsagesFromProperty") << 9 << 22 << inlineUsages;
QTest::addRow("inlineUsagesFromUsageOfBaseProperty") << 14 << 27 << inlineUsages;
QTest::addRow("inlineUsagesFromJsScope") << 20 << 20 << inlineUsages;
}
{
const auto testFileName = testFile("findUsages/propertyChanges/propertyChanges.qml");
const auto testFileContent = readFileContent(testFileName);
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 9,
strlen("onClicked"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 16, 21,
strlen("onClicked"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 19, 25,
strlen("onClicked"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 25, 17,
strlen("onClicked"));
const auto propertyChanges = makeUsages(testFileName, expectedUsages);
QTest::addRow("propertyChanges1") << 16 << 21 << propertyChanges;
}
{
const auto testFileName = testFile("findUsages/bindings/bindings.qml");
const auto testFileContent = readFileContent(testFileName);
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 11, 23,
strlen("patronChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 14, 27,
strlen("patronChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 21, 27,
strlen("patronChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 27, 19,
strlen("patronChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 27, 41,
strlen("patronChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 34, 17,
strlen("patronChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 13, 20,
strlen("patronChanged"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 20, 23,
strlen("\"patronChanged\""));
const auto bindings = makeUsages(testFileName, expectedUsages);
QTest::addRow("propertyInBindingsFromDecl") << 11 << 23 << bindings;
QTest::addRow("generalizedGroupedPropertyBindings") << 27 << 19 << bindings;
}
{
const auto testFileName = testFile("findUsages/enums/Enums.qml");
const auto testFileContent = readFileContent(testFileName);
const auto otherFileName = testFile("findUsages/enums/EnumsFromAnotherFile.qml");
const auto otherFileContent = readFileContent(otherFileName);
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 9,
strlen("Patron"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 22, 35,
strlen("Patron"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 23, 34,
strlen("Patron"));
const auto enums = makeUsages(testFileName, expectedUsages);
QTest::addRow("enumValuesFromDeclaration") << 9 << 9 << enums;
QTest::addRow("enumValuesFromUsage") << 22 << 35 << enums;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 10,
strlen("Cats"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 22, 30,
strlen("Cats"));
const auto enums = makeUsages(testFileName, expectedUsages);
QTest::addRow("enumNameFromDeclaration") << 8 << 10 << enums;
QTest::addRow("enumNameFromUsage") << 22 << 30 << enums;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 26, 46,
strlen("FromAnotherUniverse"));
expectedUsages << QQmlLSUtils::Location::from(otherFileName, otherFileContent, 4, 68,
strlen("FromAnotherUniverse"));
const auto enums = makeUsages(testFileName, expectedUsages);
QTest::addRow("enumNameFromDeclarationInOtherFile") << 26 << 50 << enums;
const auto enumsFromOtherFile = makeUsages(otherFileName, expectedUsages);
QTest::addRow("enumNameFromUsageFromOtherFile") << 4 << 81 << enumsFromOtherFile;
}
}
{
const auto testFileName = testFile("findUsages/inlineComponents/inlineComponents2.qml");
const auto testFileContent = readFileContent(testFileName);
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 4, 15,
strlen("MyIC"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 5, 5,
strlen("MyIC"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 5, 12,
strlen("MyIC"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 5, 19,
strlen("MyIC"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 6, 19,
strlen("MyIC"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 6, 26,
strlen("MyIC"));
const auto inlineComponents = makeUsages(testFileName, expectedUsages);
QTest::addRow("findICUsagesFromDefinition") << 4 << 16 << inlineComponents;
QTest::addRow("findICUsagesFromDefinition2") << 4 << 9 << inlineComponents;
QTest::addRow("findICUsagesFromUsage") << 5 << 19 << inlineComponents;
QTest::addRow("findICUsagesFromTypeUsage") << 6 << 19 << inlineComponents;
}
}
{
const auto testFileName = testFile("findUsages/lambdas/lambdas.qml");
const auto testFileContent = readFileContent(testFileName);
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 6, 29,
strlen("a"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 7, 17,
strlen("a"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 46,
strlen("a"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 9, 21,
strlen("a"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 10, 17,
strlen("a"));
const auto usagesForTestFile = makeUsages(testFileName, expectedUsages);
QTest::addRow("lambdaParameter") << 7 << 17 << usagesForTestFile;
QTest::addRow("lambdaParameter2") << 6 << 29 << usagesForTestFile;
QTest::addRow("lambdaParameterFromInnerArrow") << 8 << 46 << usagesForTestFile;
}
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 28,
strlen("x"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 8, 38,
strlen("x"));
const auto usagesForTestFile = makeUsages(testFileName, expectedUsages);
QTest::addRow("arrowParameter") << 8 << 28 << usagesForTestFile;
QTest::addRow("arrowParameter2") << 8 << 38 << usagesForTestFile;
}
}
{
const auto testFileName = testFile("findUsages/inlineComponents/inlineComponents.qml");
const auto testFileContent = readFileContent(testFileName);
const auto providerFileName =
testFile("findUsages/inlineComponents/InlineComponentProvider.qml");
const auto providerFileContent = readFileContent(providerFileName);
{
QList<QQmlLSUtils::Location> expectedUsages;
expectedUsages << QQmlLSUtils::Location::from(providerFileName, providerFileContent, 4,
15, strlen("IC1"));
expectedUsages << QQmlLSUtils::Location::from(providerFileName, providerFileContent, 5,
36, strlen("IC1"));
expectedUsages << QQmlLSUtils::Location::from(providerFileName, providerFileContent, 7,
5, strlen("IC1"));
expectedUsages << QQmlLSUtils::Location::from(providerFileName, providerFileContent, 17,
13, strlen("IC1"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 25, 38,
strlen("IC1"));
expectedUsages << QQmlLSUtils::Location::from(testFileName, testFileContent, 25, 84,
strlen("IC1"));
{
const auto usagesForTestFile = makeUsages(testFileName, expectedUsages);
QTest::addRow("findICUsagesFromTypeAnnotationInOtherFiles")
<< 25 << 39 << usagesForTestFile;
QTest::addRow("findICUsagesFromInstantiationInOtherFiles")
<< 25 << 84 << usagesForTestFile;
}
{
const auto usagesInProviderFile = makeUsages(providerFileName, expectedUsages);
QTest::addRow("findICUsagesFromDefinitionInOtherFiles")
<< 4 << 16 << usagesInProviderFile;
QTest::addRow("findICUsagesFromInstantiationInOtherFiles2")
<< 17 << 14 << usagesInProviderFile;
}
}
}
}
void tst_qmlls_utils::findUsages()
{
QFETCH(int, line);
QFETCH(int, character);
QFETCH(UsageData, data);
{
auto usagesInFilename = data.expectedUsages.usagesInFilename();
QVERIFY(std::is_sorted(usagesInFilename.begin(), usagesInFilename.end()));
auto usagesInFile = data.expectedUsages.usagesInFile();
QVERIFY(std::is_sorted(usagesInFile.begin(), usagesInFile.end()));
}
auto [env, file] = createEnvironmentAndLoadFile(data.testFileName);
auto locations = QQmlLSUtils::itemsFromTextLocation(
file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
if constexpr (enable_debug_output) {
if (locations.size() > 1) {
for (auto &x : locations)
qDebug() << x.domItem.toString();
}
}
QCOMPARE(locations.size(), 1);
auto usages = QQmlLSUtils::findUsagesOf(locations.front().domItem);
if constexpr (enable_debug_output) {
if (usages != data.expectedUsages) {
qDebug() << "Got:\n";
for (auto &x : usages.usagesInFile()) {
qDebug() << x.filename() << "(" << x.sourceLocation().startLine << ", "
<< x.sourceLocation().startColumn << "), " << x.sourceLocation().offset << "+"
<< x.sourceLocation().length;
}
qDebug() << "with usages in filenames:" << usages.usagesInFilename();
qDebug() << "But expected: \n";
for (auto &x : data.expectedUsages.usagesInFile()) {
qDebug() << x.filename() << "(" << x.sourceLocation().startLine << ", "
<< x.sourceLocation().startColumn << "), " << x.sourceLocation().offset << "+"
<< x.sourceLocation().length;
}
qDebug() << "with usages in filenames:" << data.expectedUsages.usagesInFilename();
}
}
QCOMPARE(usages, data.expectedUsages);
}
void tst_qmlls_utils::renameUsages_data()
{
QTest::addColumn<QString>("filePath");
QTest::addColumn<int>("line");
QTest::addColumn<int>("character");
QTest::addColumn<QString>("newName");
QTest::addColumn<QQmlLSUtils::RenameUsages>("expectedRenames");
QTest::addColumn<QString>("expectedError");
const QString testFileName = testFile(u"JSUsages.qml"_s);
const QString testFileNameFromAnotherFile = testFile(u"JSUsagesFromAnotherFile.qml"_s);
const QString testFileContent = readFileContent(testFileName);
const QString testFileFromAnotherFileContent = readFileContent(testFileNameFromAnotherFile);
const QString noError;
const QQmlLSUtils::RenameUsages noRenames;
QQmlLSUtils::RenameUsages methodFRename{
{
QQmlLSUtils::Edit::from(testFileName, testFileContent, 72, 14, strlen("recursive"),
u"newNameNewMe"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 74, 24, strlen("recursive"),
u"newNameNewMe"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 74, 34, strlen("recursive"),
u"newNameNewMe"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 74, 51, strlen("recursive"),
u"newNameNewMe"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 74, 68, strlen("recursive"),
u"newNameNewMe"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 76, 20, strlen("recursive"),
u"newNameNewMe"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 79, 34, strlen("recursive"),
u"newNameNewMe"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 84, 27, strlen("recursive"),
u"newNameNewMe"_s),
},
{}
};
QQmlLSUtils::RenameUsages JSIdentifierSumRename{
{
QQmlLSUtils::Edit::from(testFileName, testFileContent, 8, 13, strlen("sum"),
u"sumsumsum123"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 10, 13, strlen("sum"),
u"sumsumsum123"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 10, 19, strlen("sum"),
u"sumsumsum123"_s),
},
{}
};
QQmlLSUtils::RenameUsages qmlSignalRename{
{
QQmlLSUtils::Edit::from(testFileName, testFileContent, 88, 12,
strlen("helloSignal"), u"finalSignal"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 91, 9, strlen("helloSignal"),
u"finalSignal"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 93, 13,
strlen("helloSignal"), u"finalSignal"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 97, 17,
strlen("helloSignal"), u"finalSignal"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 101, 9,
strlen("helloSignal"), u"finalSignal"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 119, 5,
strlen("onHelloSignal"), u"onFinalSignal"_s),
},
{}
};
QQmlLSUtils::RenameUsages helloPropertyRename{
{
QQmlLSUtils::Edit::from(testFileName, testFileContent, 17, 18,
strlen("helloProperty"), u"freshPropertyName"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 24, 13,
strlen("helloProperty"), u"freshPropertyName"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 24, 29,
strlen("helloProperty"), u"freshPropertyName"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 65, 60,
strlen("helloProperty"), u"freshPropertyName"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 151, 9,
strlen("helloPropertyChanged"),
u"freshPropertyNameChanged"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 153, 5,
strlen("onHelloPropertyChanged"),
u"onFreshPropertyNameChanged"_s),
QQmlLSUtils::Edit::from(testFileNameFromAnotherFile, testFileFromAnotherFileContent,
12, 16, strlen("helloProperty"), u"freshPropertyName"_s),
},
{}
};
QQmlLSUtils::RenameUsages nestedComponentRename{
{
QQmlLSUtils::Edit::from(testFileName, testFileContent, 42, 15,
strlen("NestedComponent"), u"SuperInlineComponent"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 61, 5,
strlen("NestedComponent"), u"SuperInlineComponent"_s),
},
{}
};
QQmlLSUtils::RenameUsages myNestedIdRename{
{
QQmlLSUtils::Edit::from(testFileName, testFileContent, 62, 13, strlen("myNested"),
u"freshNewIdForMyNested"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 65, 17, strlen("myNested"),
u"freshNewIdForMyNested"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 66, 17, strlen("myNested"),
u"freshNewIdForMyNested"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 67, 17, strlen("myNested"),
u"freshNewIdForMyNested"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 68, 17, strlen("myNested"),
u"freshNewIdForMyNested"_s),
QQmlLSUtils::Edit::from(testFileName, testFileContent, 69, 17, strlen("myNested"),
u"freshNewIdForMyNested"_s),
},
{}
};
const QString renameFileQml = testFile("renaming/main.qml");
const QString renameFileQmlContent = readFileContent(renameFileQml);
const QQmlLSUtils::RenameUsages renameComponent1{
{
QQmlLSUtils::Edit::from(renameFileQml, renameFileQmlContent, 4, 5,
strlen("RenameMe"), u"FreshNewComponentName"_s),
},
{
{ testFile("renaming/RenameMe.qml"),
testFile(u"renaming/FreshNewComponentName.qml"_s) },
}
};
const QQmlLSUtils::RenameUsages renameComponent2{
{
QQmlLSUtils::Edit::from(renameFileQml, renameFileQmlContent, 5, 5,
strlen("RenameMe2"), u"AnotherOneThankYou"_s),
},
{
{ testFile("renaming/RenameMe2.ui.qml"),
testFile(u"renaming/AnotherOneThankYou.ui.qml"_s) },
}
};
const QQmlLSUtils::RenameUsages renameComponentNamedByQmldir{
{
QQmlLSUtils::Edit::from(renameFileQml, renameFileQmlContent, 6, 5,
strlen("HelloWorld"), u"AnotherOneThankYou"_s),
},
// make sure that the file itself does not get renamed
{}
};
const QString parserError = u"Invalid EcmaScript identifier!"_s;
QTest::addRow("renameMethod") << testFileName << 72 << 19 << u"newNameNewMe"_s << methodFRename
<< noError;
QTest::addRow("renameJSIdentifier")
<< testFileName << 10 << 19 << u"sumsumsum123"_s << JSIdentifierSumRename << noError;
QTest::addRow("renameQmlSignal")
<< testFileName << 93 << 19 << u"finalSignal"_s << qmlSignalRename << noError;
QTest::addRow("renameQmlSignalHandler")
<< testFileName << 119 << 10 << u"onFinalSignal"_s << qmlSignalRename << noError;
QTest::addRow("renameQmlProperty")
<< testFileName << 17 << 20 << u"freshPropertyName"_s << helloPropertyRename << noError;
QTest::addRow("renameQmlPropertyChanged")
<< testFileName << 151 << 18 << u"freshPropertyNameChanged"_s << helloPropertyRename
<< noError;
QTest::addRow("renameQmlPropertyChangedHandler")
<< testFileName << 153 << 22 << u"onFreshPropertyNameChanged"_s << helloPropertyRename
<< noError;
QTest::addRow("renameQmlObjectId") << testFileName << 65 << 21 << u"freshNewIdForMyNested"_s
<< myNestedIdRename << noError;
// rename forbidden stuff
QTest::addRow("renameCPPDefinedItem") << testFileName << 144 << 13 << u"onHelloWorld"_s
<< noRenames << u"defined in non-QML files."_s;
QTest::addRow("renameFunctionKeyword") << testFileName << 8 << 10 << u"HelloWorld"_s
<< noRenames << "Requested item cannot be renamed";
QTest::addRow("invalidCharactersInIdentifier")
<< testFileName << 12 << 22 << u"\""_s << noRenames << parserError;
QTest::addRow("invalidCharactersInIdentifier2")
<< testFileName << 12 << 22 << u"hello world"_s << noRenames << parserError;
QTest::addRow("invalidCharactersInIdentifier3")
<< testFileName << 12 << 22 << u"sum.sum.sum"_s << noRenames << parserError;
QTest::addRow("emptyIdentifier")
<< testFileName << 12 << 22 << QString() << noRenames << parserError;
QTest::addRow("usingKeywordAsIdentifier")
<< testFileName << 12 << 22 << u"function"_s << noRenames << parserError;
QTest::addRow("changedSignalHandlerMissingOnChanged")
<< testFileName << 134 << 9 << u"___"_s << noRenames
<< u"Invalid name for a property changed handler identifier"_s;
QTest::addRow("changedSignalHandlerMissingChanged")
<< testFileName << 134 << 9 << u"on___"_s << noRenames
<< u"Invalid name for a property changed handler identifier"_s;
QTest::addRow("changedSignalHandlerMissingOn")
<< testFileName << 134 << 9 << u"___Changed"_s << noRenames
<< u"Invalid name for a property changed handler identifier"_s;
QTest::addRow("changedSignalHandlerTypoInChanged")
<< testFileName << 134 << 9 << u"on___Chänged"_s << noRenames
<< u"Invalid name for a property changed handler identifier"_s;
QTest::addRow("signalHandlerMissingOn")
<< testFileName << 119 << 10 << u"helloSuperSignal"_s << noRenames
<< u"Invalid name for a signal handler identifier"_s;
QTest::addRow("signalHandlerMissingCapitalization")
<< testFileName << 119 << 10 << u"onhelloSuperSignal"_s << noRenames
<< u"Invalid name for a signal handler identifier"_s;
QTest::addRow("JSIdentifierStartsWithNumber")
<< testFileName << 67 << 13 << u"123"_s << noRenames << parserError;
QTest::addRow("renameQmlFile") << testFile(u"renaming/main.qml"_s) << 4 << 9
<< u"FreshNewComponentName"_s << renameComponent1 << noError;
QTest::addRow("renameUiQmlFile") << testFile(u"renaming/main.qml"_s) << 5 << 9
<< u"AnotherOneThankYou"_s << renameComponent2 << noError;
QTest::addRow("renameQmlFileRenamedByQmldir")
<< testFile(u"renaming/main.qml"_s) << 6 << 8 << u"AnotherOneThankYou"_s
<< renameComponentNamedByQmldir << noError;
}
void tst_qmlls_utils::renameUsages()
{
// findAndRenameUsages() already tests if all usages will be renamed
// now test that the new name is correctly passed
QFETCH(QString, filePath);
QFETCH(int, line);
QFETCH(int, character);
QFETCH(QString, newName);
QFETCH(QQmlLSUtils::RenameUsages, expectedRenames);
QFETCH(QString, expectedError);
{
const auto renameInFile = expectedRenames.renameInFile();
QVERIFY(std::is_sorted(renameInFile.constBegin(), renameInFile.constEnd()));
const auto renameInFilename = expectedRenames.renameInFilename();
QVERIFY(std::is_sorted(renameInFilename.begin(), renameInFilename.end()));
}
auto [env, file] = createEnvironmentAndLoadFile(filePath);
auto locations = QQmlLSUtils::itemsFromTextLocation(
file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
if constexpr (enable_debug_output) {
if (locations.size() > 1) {
for (auto &x : locations)
qDebug() << x.domItem.toString();
}
}
QCOMPARE(locations.size(), 1);
if (auto errors = QQmlLSUtils::checkNameForRename(locations.front().domItem, newName)) {
if constexpr (enable_debug_output) {
if (expectedError.isEmpty())
qDebug() << "Expected no error but got" << errors->message;
if (!errors->message.contains(expectedError))
qDebug() << "Cannot find" << expectedError << "in" << errors->message;
}
QVERIFY(!expectedError.isEmpty());
QVERIFY(errors->message.contains(expectedError));
return;
}
auto edits = QQmlLSUtils::renameUsagesOf(locations.front().domItem, newName);
if constexpr (enable_debug_output) {
if (edits != expectedRenames) {
qDebug() << "Got:\n";
for (auto &x : edits.renameInFile()) {
qDebug() << x.replacement << x.location.filename() << "("
<< x.location.sourceLocation().startLine << ", "
<< x.location.sourceLocation().startColumn << "), "
<< x.location.sourceLocation().offset << "+"
<< x.location.sourceLocation().length;
}
qDebug() << "with renames in filenames:";
for (auto &x : edits.renameInFilename()) {
qDebug() << x.oldFilename << "->" << x.newFilename;
}
qDebug() << "But expected: \n";
for (auto &x : expectedRenames.renameInFile()) {
qDebug() << x.replacement << x.location.filename() << "("
<< x.location.sourceLocation().startLine << ", "
<< x.location.sourceLocation().startColumn << "), "
<< x.location.sourceLocation().offset << "+"
<< x.location.sourceLocation().length;
}
qDebug() << "with renames in filenames:";
for (auto &x : expectedRenames.renameInFilename()) {
qDebug() << x.oldFilename << "->" << x.newFilename;
}
}
}
QCOMPARE(edits, expectedRenames);
}
void tst_qmlls_utils::findDefinitionFromLocation_data()
{
QTest::addColumn<QString>("filePath");
// keep in mind that line and character are starting at 1!
QTest::addColumn<int>("line");
QTest::addColumn<int>("character");
QTest::addColumn<QString>("expectedFilePath");
// set to -1 when unchanged from above line and character. 0-based.
QTest::addColumn<int>("expectedLine");
QTest::addColumn<int>("expectedCharacter");
QTest::addColumn<size_t>("expectedLength");
QTest::addColumn<QStringList>("extraBuildDirs");
const QString JSDefinitionsQml = testFile(u"JSDefinitions.qml"_s);
const QString BaseTypeQml = testFile(u"BaseType.qml"_s);
const QStringList noExtraBuildDir;
QTest::addRow("JSIdentifierX")
<< JSDefinitionsQml << 14 << 11 << JSDefinitionsQml << 13 << 13 << strlen("x") << noExtraBuildDir;
QTest::addRow("JSIdentifierX2")
<< JSDefinitionsQml << 15 << 11 << JSDefinitionsQml << 13 << 13 << strlen("x") << noExtraBuildDir;
QTest::addRow("propertyI") << JSDefinitionsQml << 14 << 14 << JSDefinitionsQml << 9 << 18
<< strlen("i") << noExtraBuildDir;
QTest::addRow("qualifiedPropertyI")
<< JSDefinitionsQml << 15 << 21 << JSDefinitionsQml << 9 << 18 << strlen("i") << noExtraBuildDir;
QTest::addRow("inlineComponentProperty")
<< JSDefinitionsQml << 62 << 21 << JSDefinitionsQml << 54 << 22 << strlen("data") << noExtraBuildDir;
QTest::addRow("parameterA") << JSDefinitionsQml << 10 << 16 << JSDefinitionsQml << 10 << 16
<< strlen("a") << noExtraBuildDir;
QTest::addRow("parameterAUsage")
<< JSDefinitionsQml << 10 << 39 << JSDefinitionsQml << -1 << 16 << strlen("a") << noExtraBuildDir;
QTest::addRow("parameterB") << JSDefinitionsQml << 10 << 28 << JSDefinitionsQml << 10 << 28
<< strlen("b") << noExtraBuildDir;
QTest::addRow("parameterBUsage")
<< JSDefinitionsQml << 10 << 86 << JSDefinitionsQml << -1 << 28 << strlen("b") << noExtraBuildDir;
QTest::addRow("comment") << JSDefinitionsQml << 10 << 21 << noResultExpected << -1 << -1
<< size_t{} << noExtraBuildDir;
QTest::addRow("scopedX") << JSDefinitionsQml << 22 << 18 << JSDefinitionsQml << 21 << 17
<< strlen("scoped") << noExtraBuildDir;
QTest::addRow("scopedX2") << JSDefinitionsQml << 25 << 22 << JSDefinitionsQml << 21 << 17
<< strlen("scoped") << noExtraBuildDir;
QTest::addRow("scopedX3") << JSDefinitionsQml << 28 << 14 << JSDefinitionsQml << 19 << 13
<< strlen("scoped") << noExtraBuildDir;
QTest::addRow("normalI") << JSDefinitionsQml << 22 << 23 << JSDefinitionsQml << 9 << 18
<< strlen("i") << noExtraBuildDir;
QTest::addRow("scopedI") << JSDefinitionsQml << 25 << 27 << JSDefinitionsQml << 24 << 32
<< strlen("i") << noExtraBuildDir;
QTest::addRow("shadowingProperty")
<< JSDefinitionsQml << 37 << 21 << JSDefinitionsQml << 34 << 22 << strlen("i") << noExtraBuildDir;
QTest::addRow("shadowingQualifiedProperty")
<< JSDefinitionsQml << 37 << 35 << JSDefinitionsQml << 34 << 22 << strlen("i") << noExtraBuildDir;
QTest::addRow("shadowedProperty")
<< JSDefinitionsQml << 37 << 49 << JSDefinitionsQml << 9 << 18 << strlen("i") << noExtraBuildDir;
QTest::addRow("propertyInBinding")
<< JSDefinitionsQml << 64 << 37 << JSDefinitionsQml << 9 << 18 << strlen("i") << noExtraBuildDir;
QTest::addRow("propertyInBinding2")
<< JSDefinitionsQml << 65 << 38 << JSDefinitionsQml << 9 << 18 << strlen("i") << noExtraBuildDir;
QTest::addRow("propertyInBinding3")
<< JSDefinitionsQml << 66 << 51 << JSDefinitionsQml << 9 << 18 << strlen("i") << noExtraBuildDir;
QTest::addRow("propertyFromDifferentFile")
<< JSDefinitionsQml << 72 << 20 << BaseTypeQml << 24 << 18 << strlen("helloProperty") << noExtraBuildDir;
QTest::addRow("id") << JSDefinitionsQml << 15 << 17 << JSDefinitionsQml << 7 << 9
<< strlen("rootId") << noExtraBuildDir;
QTest::addRow("onId") << JSDefinitionsQml << 32 << 16 << JSDefinitionsQml << 32 << 13
<< strlen("nested") << noExtraBuildDir;
QTest::addRow("parentId") << JSDefinitionsQml << 37 << 44 << JSDefinitionsQml << 7 << 9
<< strlen("rootId") << noExtraBuildDir;
QTest::addRow("currentId") << JSDefinitionsQml << 37 << 30 << JSDefinitionsQml << 32 << 13
<< strlen("nested") << noExtraBuildDir;
QTest::addRow("inlineComponentId")
<< JSDefinitionsQml << 56 << 35 << JSDefinitionsQml << 52 << 13 << strlen("helloIC") << noExtraBuildDir;
QTest::addRow("recursiveFunction")
<< JSDefinitionsQml << 39 << 28 << JSDefinitionsQml << 36 << 18 << strlen("f") << noExtraBuildDir;
QTest::addRow("recursiveFunction2")
<< JSDefinitionsQml << 39 << 39 << JSDefinitionsQml << 36 << 18 << strlen("f") << noExtraBuildDir;
QTest::addRow("functionFromFunction")
<< JSDefinitionsQml << 44 << 20 << JSDefinitionsQml << 36 << 18 << strlen("f") << noExtraBuildDir;
QTest::addRow("qualifiedFunctionName")
<< JSDefinitionsQml << 48 << 23 << JSDefinitionsQml << 36 << 18 << strlen("f") << noExtraBuildDir;
QTest::addRow("functionInParent")
<< JSDefinitionsQml << 44 << 37 << JSDefinitionsQml << 18 << 14 << strlen("ffff") << noExtraBuildDir;
QTest::addRow("functionFromDifferentFile")
<< JSDefinitionsQml << 72 << 47 << BaseTypeQml << 25 << 14 << strlen("helloFunction") << noExtraBuildDir;
QTest::addRow("componentFromFile")
<< JSDefinitionsQml << 68 << 28 << BaseTypeQml << 6 << 1 << strlen("Item") << noExtraBuildDir;
QTest::addRow("inlineComponentFromDifferentFile")
<< JSDefinitionsQml << 75 << 27 << BaseTypeQml << 9 << 38 << strlen("Item") << noExtraBuildDir;
QTest::addRow("lambdaParameter")
<< JSDefinitionsQml << 78 << 33 << JSDefinitionsQml << 77 << 34 << strlen("c") << noExtraBuildDir;
QTest::addRow("callFromLambda")
<< JSDefinitionsQml << 79 << 21 << JSDefinitionsQml << 47 << 14 << strlen("abc") << noExtraBuildDir;
QTest::addRow("callFromLambda2")
<< JSDefinitionsQml << 81 << 32 << JSDefinitionsQml << 76 << 14 << strlen("helloLambda") << noExtraBuildDir;
QTest::addRow("lambdaRecursion")
<< JSDefinitionsQml << 80 << 29 << JSDefinitionsQml << 77 << 24 << strlen("function") << noExtraBuildDir;
QTest::addRow("arrowParameter")
<< JSDefinitionsQml << 84 << 37 << JSDefinitionsQml << 83 << 32 << strlen("c") << noExtraBuildDir;
QTest::addRow("callFromArrow")
<< JSDefinitionsQml << 85 << 21 << JSDefinitionsQml << 47 << 14 << strlen("abc") << noExtraBuildDir;
QTest::addRow("callFromArrow2")
<< JSDefinitionsQml << 87 << 32 << JSDefinitionsQml << 76 << 14 << strlen("helloLambda") << noExtraBuildDir;
QTest::addRow("arrowRecursion")
<< JSDefinitionsQml << 86 << 29 << JSDefinitionsQml << 77 << 24 << strlen("function") << noExtraBuildDir;
{
const QString definitionFile =
testFile(u"findDefinition/MyApplicationWindowModule/MyApplicationWindow.qml"_s);
const QString qmlComponents = testFile(u"findDefinition/QmlComponents.qml"_s);
const QString qualifiedQmlComponents = testFile(u"findDefinition/QualifiedQmlComponents.qml"_s);
QTest::addRow("component") << qmlComponents << 7 << 11 << definitionFile << 7 << 1
<< strlen("ApplicationWindow") << noExtraBuildDir;
QTest::addRow("enumValue") << qmlComponents << 10 << 42 << definitionFile << 7 << 1
<< strlen("ApplicationWindow") << noExtraBuildDir;
QTest::addRow("enumName") << qmlComponents << 11 << 42 << definitionFile << 7 << 1
<< strlen("ApplicationWindow") << noExtraBuildDir;
QTest::addRow("qualifiedComponent")
<< qualifiedQmlComponents << 7 << 11 << definitionFile << 7 << 1
<< strlen("ApplicationWindow") << noExtraBuildDir;
QTest::addRow("qualifiedEnumValue") << qualifiedQmlComponents << 10 << 42 << definitionFile
<< 7 << 1 << strlen("ApplicationWindow") << noExtraBuildDir;
QTest::addRow("qualifiedEnumName") << qualifiedQmlComponents << 11 << 42 << definitionFile
<< 7 << 1 << strlen("ApplicationWindow") << noExtraBuildDir;
QTest::addRow("qualifiedModuleName")
<< qualifiedQmlComponents << 9 << 42 << qualifiedQmlComponents << 5 << 52
<< strlen("MAWM") << noExtraBuildDir;
}
{
const QString mainQml = testFile(u"findDefinition/mymodule-source/MyModule/Main.qml"_s);
const QString myComponentQml = testFile(u"findDefinition/mymodule-source/MyModule/X/Y/Z/MyComponent.qml"_s);
QTest::addRow("nestedFromOwnModule")
<< mainQml << 4 << 5 << myComponentQml << 3 << 1 << strlen("Item")
<< QStringList { testFile(u"findDefinition/mymodule-build"_s) };
}
{
const QString mainQml = testFile(u"findDefinition/mymodule-without-qmldir-prefer-source/MyModuleWithoutQmldirPrefer/Main.qml"_s);
const QString myComponentQml = testFile(u"findDefinition/mymodule-without-qmldir-prefer-source/MyModuleWithoutQmldirPrefer/X/Y/Z/MyComponent.qml"_s);
QTest::addRow("nestedFromOwnModuleWithoutQmldirPrefer")
<< mainQml << 4 << 5 << myComponentQml << 3 << 1 << strlen("Item")
<< QStringList { testFile(u"findDefinition/mymodule-without-qmldir-prefer-build"_s) };
}
{
const QString mainQml = testFile(u"findDefinition/TestAppWithBuildFolder/TestApp/Main.qml"_s);
const QString myComponentQml = testFile(u"findDefinition/TestAppWithBuildFolder/TestApp/somesubfolder/anothersubfolder/MyModule/MyItem.qml"_s);
QTest::addRow("componentFromOtherModule")
<< mainQml << 5 << 8 << myComponentQml << 3 << 1 << strlen("Item")
<< QStringList{
testFile(u"findDefinition/TestAppWithBuildFolder/build"_s),
testFile(
u"findDefinition/TestAppWithBuildFolder/build/somesubfolder/anothersubfolder"_s)
};
}
QTest::addRow("singletonFromQml") << testFile("findDefinition/UseMySingletons.qml"_L1) << 5
<< 31 << testFile("ModuleWithSingleton/MySingleton.qml"_L1)
<< 4 << 1 << strlen("Item") << noExtraBuildDir;
QTest::addRow("singletonFromCpp")
<< testFile("findDefinition/UseMySingletons.qml"_L1) << 6 << 31
<< testFile("findDefinition/SomeIncludeFolder/mysingletonfromcppheader.h") << 42 << 1
<< strlen("") << QStringList{ testFile("findDefinition"_L1) };
const QString componentFromCppHeaderPath =
testFile("findDefinition/SomeIncludeFolder/mycomponentfromcppheader.h"_L1);
QTest::addRow("componentFromCpp") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 3
<< 1 << componentFromCppHeaderPath << 42 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("componentFromJava")
<< testFile("findDefinition/UseMyCppComponent.qml"_L1) << 16 << 44
<< testFile("findDefinition/SomeIncludeFolder/mycomponentfromnoncppheader.java"_L1)
<< 42 << 1 << strlen("") << QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("propertyFromCpp") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 4
<< 6 << componentFromCppHeaderPath << 12 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("propertyFromCpp2") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 5
<< 47 << componentFromCppHeaderPath << 12 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("propertyFromCppInNonUserFolder")
<< testFile("findDefinition/UseMyCppComponent.qml"_L1) << 6 << 12 << noResultExpected
<< 1 << 1 << strlen("") << QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("signalFromCpp") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 7 << 34
<< componentFromCppHeaderPath << 654321 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("slotFromCpp") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 7 << 46
<< componentFromCppHeaderPath << 29 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("invokableFromCpp") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 7
<< 60 << componentFromCppHeaderPath << 23 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("groupedFromCpp") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 8 << 8
<< componentFromCppHeaderPath << 13 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("groupedFromCpp2") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 9
<< 8 << componentFromCppHeaderPath << 13 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("groupedPropertyFromCpp")
<< testFile("findDefinition/UseMyCppComponent.qml"_L1) << 8 << 18
<< componentFromCppHeaderPath << 37 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("groupedPropertyFromCpp2")
<< testFile("findDefinition/UseMyCppComponent.qml"_L1) << 9 << 18
<< componentFromCppHeaderPath << 38 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("enumFromCpp") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 10 << 52
<< componentFromCppHeaderPath << 150 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("enumFromCpp2") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 11 << 60
<< componentFromCppHeaderPath << 150 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("scopedEnumFromCpp") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 11
<< 56 << componentFromCppHeaderPath << 150 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("enumFromQml") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 14 << 51
<< "UseMyCppComponent.qml" << 13 << 42 << strlen("ByeEnumFromQml")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("enumFromQml2") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 15 << 68
<< "UseMyCppComponent.qml" << 13 << 42 << strlen("ByeEnumFromQml")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("scopedEnumFromQml")
<< testFile("findDefinition/UseMyCppComponent.qml"_L1) << 15 << 59
<< "UseMyCppComponent.qml" << 13 << 10 << strlen("EnumFromQml")
<< QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("flagFromCpp") << testFile("findDefinition/UseMyCppComponent.qml"_L1) << 12 << 53
<< componentFromCppHeaderPath << 555 << 1 << strlen("")
<< QStringList{ testFile("findDefinition"_L1) };
const QString attachedHeaderPath = testFile("findDefinition/SomeIncludeFolder/attached.h"_L1);
QTest::addRow("attachedTypeFromCpp")
<< testFile("findDefinition/UseMyAttachedType.qml"_L1) << 5 << 11 << attachedHeaderPath
<< 42 << 1 << strlen("") << QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("attachedPropertyFromCpp")
<< testFile("findDefinition/UseMyAttachedType.qml"_L1) << 5 << 21 << attachedHeaderPath
<< 88 << 1 << strlen("") << QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("attachedSignalFromCpp")
<< testFile("findDefinition/UseMyAttachedType.qml"_L1) << 7 << 25 << attachedHeaderPath
<< 12345 << 1 << strlen("") << QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("attachedTypeFromCpp2")
<< testFile("findDefinition/UseMyAttachedType.qml"_L1) << 8 << 23 << attachedHeaderPath
<< 42 << 1 << strlen("") << QStringList{ testFile("findDefinition"_L1) };
QTest::addRow("attachedPropertyFromCpp2")
<< testFile("findDefinition/UseMyAttachedType.qml"_L1) << 8 << 30 << attachedHeaderPath
<< 88 << 1 << strlen("") << QStringList{ testFile("findDefinition"_L1) };
}
void tst_qmlls_utils::findDefinitionFromLocation()
{
QFETCH(QString, filePath);
QFETCH(int, line);
QFETCH(int, character);
QFETCH(QString, expectedFilePath);
QFETCH(int, expectedLine);
QFETCH(int, expectedCharacter);
QFETCH(size_t, expectedLength);
QFETCH(QStringList, extraBuildDirs);
if (expectedLine == -1)
expectedLine = line;
if (expectedCharacter == -1)
expectedCharacter = character;
// they all start at 1.
Q_ASSERT(line > 0);
Q_ASSERT(character > 0);
Q_ASSERT(expectedLine > 0);
Q_ASSERT(expectedCharacter > 0);
auto [env, file] = createEnvironmentAndLoadFile(filePath, extraBuildDirs);
auto locations = QQmlLSUtils::itemsFromTextLocation(
file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
QCOMPARE(locations.size(), 1);
auto definition = QQmlLSUtils::findDefinitionOf(locations.front().domItem, extraBuildDirs);
// if expectedFilePath is empty, we probably just want to make sure that it does
// not crash
if (expectedFilePath == noResultExpected) {
QCOMPARE(definition.size(), 0);
return;
}
QCOMPARE(definition.size(), 1);
// don't work with absolute paths, and only compare the end of the file path
QCOMPARE(QStringView(definition.front().filename()).last(expectedFilePath.size()),
expectedFilePath);
QCOMPARE(definition.front().sourceLocation().startLine, quint32(expectedLine));
QCOMPARE(definition.front().sourceLocation().startColumn, quint32(expectedCharacter));
QCOMPARE(definition.front().sourceLocation().length, quint32(expectedLength));
}
struct ExpectedDefinition
{
QString filePath;
int line;
int character;
size_t length;
};
void tst_qmlls_utils::findMultipleDefinitionsFromLocation_data()
{
QTest::addColumn<QString>("filePath");
// keep in mind that line and character are starting at 1!
QTest::addColumn<int>("line");
QTest::addColumn<int>("character");
QTest::addColumn<QList<ExpectedDefinition>>("expected");
QTest::addColumn<QStringList>("extraBuildDirs");
QTest::addRow("componentFromCpp")
<< testFile("findDefinition/UseMyCppComponent.qml"_L1) << 17 << 29
<< QList<ExpectedDefinition>{
{ testFile("findDefinition/SomeIncludeFolder/mytype.h"), 222, 1, strlen(""), },
{ testFile("findDefinition/SomeIncludeFolder/mytypeforeign.h"), 111, 1, strlen(""), },
} << QStringList{ testFile("findDefinition"_L1) };
}
void tst_qmlls_utils::findMultipleDefinitionsFromLocation()
{
QFETCH(QString, filePath);
QFETCH(int, line);
QFETCH(int, character);
QFETCH(QList<ExpectedDefinition>, expected);
QFETCH(QStringList, extraBuildDirs);
// they all start at 1.
Q_ASSERT(line > 0);
Q_ASSERT(character > 0);
auto [env, file] = createEnvironmentAndLoadFile(filePath, extraBuildDirs);
auto locations = QQmlLSUtils::itemsFromTextLocation(
file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
QCOMPARE(locations.size(), 1);
auto definitions = QQmlLSUtils::findDefinitionOf(locations.front().domItem, extraBuildDirs);
QCOMPARE(definitions.size(), expected.size());
for (int i = 0; i < definitions.size(); ++i) {
Q_ASSERT(expected[i].line > 0);
Q_ASSERT(expected[i].character > 0);
// don't work with absolute paths, and only compare the end of the file path
QCOMPARE(QStringView(definitions[i].filename()).last(expected[i].filePath.size()),
expected[i].filePath);
QCOMPARE(definitions[i].sourceLocation().startLine, quint32(expected[i].line));
QCOMPARE(definitions[i].sourceLocation().startColumn, quint32(expected[i].character));
QCOMPARE(definitions[i].sourceLocation().length, quint32(expected[i].length));
}
}
void tst_qmlls_utils::findDefinitionFromAbsoluteLocation()
{
QTemporaryDir build;
QVERIFY(build.isValid());
QDir(build.path()).mkdir("ModuleWithComponent");
QFile::copy(testFile("ModuleWithComponent/qmldir"),
build.filePath("ModuleWithComponent/qmldir"));
QFile headerFile(build.filePath("ModuleWithComponent/mycomponentfromnoncppheader.java"));
QVERIFY(headerFile.open(QFile::WriteOnly | QFile::Text));
QFile qmltypes(testFile("ModuleWithComponent/plugin.qmltypes"));
QVERIFY(qmltypes.open(QFile::ReadOnly | QFile::Text));
QByteArray qmltypesContent = qmltypes.readAll();
const QByteArray expectedFilePath = headerFile.fileName().toUtf8();
qmltypesContent.replace("mycomponentfromnoncppheader.java", expectedFilePath);
QFile newQmltypes(build.filePath("ModuleWithComponent/plugin.qmltypes"));
QVERIFY(newQmltypes.open(QFile::WriteOnly | QFile::Text));
newQmltypes.write(qmltypesContent);
auto [env, file] = createEnvironmentAndLoadFile(
testFile("findDefinition/UseMyCppComponent.qml"_L1), { build.path() });
auto locations = QQmlLSUtils::itemsFromTextLocation(
file.field(QQmlJS::Dom::Fields::currentItem), 15, 43);
QCOMPARE(locations.size(), 1);
auto definition = QQmlLSUtils::findDefinitionOf(locations.front().domItem, { build.path() });
QCOMPARE(definition.size(), 1);
QCOMPARE(definition.front().filename(), expectedFilePath);
QCOMPARE(definition.front().sourceLocation().startLine, quint32(42));
QCOMPARE(definition.front().sourceLocation().startColumn, quint32(1));
QCOMPARE(definition.front().sourceLocation().length, quint32(0));
}
void tst_qmlls_utils::resolveExpressionType_data()
{
QTest::addColumn<QString>("filePath");
// keep in mind that line and character are starting at 1!
QTest::addColumn<int>("line");
QTest::addColumn<int>("character");
QTest::addColumn<QQmlLSUtils::ResolveOptions>("resolveOption");
QTest::addColumn<QString>("expectedFile");
// startline of the owners definition
QTest::addColumn<int>("expectedLine");
QTest::addColumn<QQmlLSUtils::IdentifierType>("expectedType");
QTest::addColumn<QString>("expectedName");
using namespace QQmlLSUtils;
const int noLine = -1;
const QString noFile;
const QString unnamed;
{
const QString JSDefinitionsQml = testFile(u"JSDefinitions.qml"_s);
const int parentLine = 6;
const int childLine = 31;
QTest::addRow("id") << JSDefinitionsQml << 15 << 17 << ResolveOwnerType << JSDefinitionsQml
<< parentLine << QmlObjectIdIdentifier << u"rootId"_s;
QTest::addRow("childIddInChild")
<< JSDefinitionsQml << 37 << 30 << ResolveOwnerType << JSDefinitionsQml << childLine
<< QmlObjectIdIdentifier << u"nested"_s;
QTest::addRow("parentIdInChild")
<< JSDefinitionsQml << 37 << 43 << ResolveOwnerType << JSDefinitionsQml
<< parentLine << QmlObjectIdIdentifier << u"rootId"_s;
QTest::addRow("propertyI")
<< JSDefinitionsQml << 14 << 14 << ResolveOwnerType << JSDefinitionsQml
<< parentLine << PropertyIdentifier << u"i"_s;
QTest::addRow("qualifiedPropertyI")
<< JSDefinitionsQml << 15 << 21 << ResolveOwnerType << JSDefinitionsQml
<< parentLine << PropertyIdentifier << u"i"_s;
QTest::addRow("propertyIInChild")
<< JSDefinitionsQml << 37 << 21 << ResolveOwnerType << JSDefinitionsQml << childLine
<< PropertyIdentifier << u"i"_s;
QTest::addRow("qualifiedChildPropertyIInChild")
<< JSDefinitionsQml << 37 << 35 << ResolveOwnerType << JSDefinitionsQml << childLine
<< PropertyIdentifier << u"i"_s;
QTest::addRow("qualifiedParentPropertyIInChild")
<< JSDefinitionsQml << 37 << 49 << ResolveOwnerType << JSDefinitionsQml
<< parentLine << PropertyIdentifier << u"i"_s;
QTest::addRow("childMethod") << JSDefinitionsQml << 48 << 23 << ResolveOwnerType
<< JSDefinitionsQml << childLine << MethodIdentifier << u"f"_s;
QTest::addRow("childMethod2")
<< JSDefinitionsQml << 44 << 20 << ResolveOwnerType << JSDefinitionsQml << childLine
<< MethodIdentifier << u"f"_s;
QTest::addRow("parentMethod")
<< JSDefinitionsQml << 14 << 9 << ResolveOwnerType << JSDefinitionsQml << parentLine
<< MethodIdentifier << u"f"_s;
}
{
const QString bindingsOnDeferredQml =
testFile(u"resolveExpressionType/BindingsOnDeferred.qml"_s);
const QString qQuickControl = u"private/qquickcontrol_p.h"_s;
const QString qQuickKeysAttachedType = u"private/qquickitem_p.h"_s;
QTest::addRow("bindingOnId")
<< bindingsOnDeferredQml << 12 << 14 << ResolveOwnerType << bindingsOnDeferredQml
<< 8 << QmlObjectIdIdentifier << u"inner"_s;
QTest::addRow("bindingOnQualifiedDeferredProperty")
<< bindingsOnDeferredQml << 12 << 24 << ResolveOwnerType << qQuickControl << noLine
<< PropertyIdentifier << u"background"_s;
QTest::addRow("groupedPropertyBindingOnId")
<< bindingsOnDeferredQml << 14 << 14 << ResolveOwnerType << bindingsOnDeferredQml
<< 8 << QmlObjectIdIdentifier << u"inner"_s;
QTest::addRow("someDeferredProperty")
<< bindingsOnDeferredQml << 15 << 22 << ResolveOwnerType << qQuickControl << noLine
<< PropertyIdentifier << u"background"_s;
}
{
const QString JSUsagesQml = testFile(u"JSUsages.qml"_s);
const int rootLine = 6;
const int nestedComponent2Line = 46;
const int nestedComponent3Line = 51;
const int nestedComponent4Line = 57;
QTest::addRow("propertyAccess:inner.inner")
<< JSUsagesQml << 68 << 34 << ResolveOwnerType << JSUsagesQml
<< nestedComponent2Line << PropertyIdentifier << u"inner"_s;
QTest::addRow("propertyAccess:inner.inner2")
<< JSUsagesQml << 69 << 34 << ResolveOwnerType << JSUsagesQml
<< nestedComponent2Line << PropertyIdentifier << u"inner"_s;
QTest::addRow("propertyAccess:inner.inner.inner")
<< JSUsagesQml << 69 << 40 << ResolveOwnerType << JSUsagesQml
<< nestedComponent3Line << PropertyIdentifier << u"inner"_s;
QTest::addRow("propertyAccess:inner.inner.inner.p2")
<< JSUsagesQml << 69 << 44 << ResolveOwnerType << JSUsagesQml
<< nestedComponent4Line << PropertyIdentifier << u"p2"_s;
QTest::addRow("propertyAccess:helloProperty")
<< JSUsagesQml << 65 << 68 << ResolveOwnerType << JSUsagesQml << rootLine
<< PropertyIdentifier << u"helloProperty"_s;
QTest::addRow("propertyAccess:nestedHelloProperty")
<< JSUsagesQml << 65 << 46 << ResolveOwnerType << JSUsagesQml
<< nestedComponent4Line << PropertyIdentifier << u"helloProperty"_s;
}
{
const QString derivedType = testFile(u"resolveExpressionType/DerivedType.qml"_s);
const QString derived2Type = testFile(u"resolveExpressionType/Derived2.qml"_s);
const QString baseType = testFile(u"resolveExpressionType/BaseType.qml"_s);
const QString qQuickValueTypes = u"private/qquickvaluetypes_p.h"_s;
const QString qQuickKeysAttachedType = u"private/qquickitem_p.h"_s;
const int baseTypeLine = 6;
const int derivedTypeLine = 6;
const int keysLine = 29;
QTest::addRow("ownerOfMethod") << derivedType << 9 << 13 << ResolveOwnerType << baseType
<< baseTypeLine << MethodIdentifier << u"helloMethod"_s;
QTest::addRow("ownerOfMethod2") << derivedType << 15 << 33 << ResolveOwnerType << baseType
<< baseTypeLine << MethodIdentifier << u"helloMethod"_s;
QTest::addRow("ownerOfQualifiedMethod")
<< derivedType << 22 << 46 << ResolveOwnerType << baseType << baseTypeLine
<< MethodIdentifier << u"helloMethod"_s;
QTest::addRow("ownerOfProperty")
<< derivedType << 10 << 22 << ResolveOwnerType << baseType << baseTypeLine
<< PropertyIdentifier << u"helloProperty"_s;
QTest::addRow("ownerOfProperty2")
<< derivedType << 16 << 37 << ResolveOwnerType << baseType << baseTypeLine
<< PropertyIdentifier << u"helloProperty"_s;
QTest::addRow("ownerOfQualifiedProperty")
<< derivedType << 23 << 46 << ResolveOwnerType << baseType << baseTypeLine
<< PropertyIdentifier << u"helloProperty"_s;
QTest::addRow("ownerOfOwnProperty")
<< derivedType << 16 << 23 << ResolveOwnerType << derivedType << derivedTypeLine
<< PropertyIdentifier << u"someProperty"_s;
QTest::addRow("ownerOfSignal") << derivedType << 11 << 13 << ResolveOwnerType << baseType
<< baseTypeLine << SignalIdentifier << u"helloSignal"_s;
QTest::addRow("ownerOfSignal2") << derivedType << 18 << 37 << ResolveOwnerType << baseType
<< baseTypeLine << SignalIdentifier << u"helloSignal"_s;
QTest::addRow("ownerOfSignalHandler")
<< derivedType << 19 << 10 << ResolveOwnerType << baseType << baseTypeLine
<< SignalHandlerIdentifier << u"onHelloSignal"_s;
QTest::addRow("ownerOfQualifiedSignal")
<< derivedType << 25 << 22 << ResolveOwnerType << baseType << baseTypeLine
<< SignalIdentifier << u"helloSignal"_s;
QTest::addRow("ownerOfGroupedProperty")
<< derivedType << 28 << 7 << ResolveOwnerType << baseType << baseTypeLine
<< GroupedPropertyIdentifier << u"helloFont"_s;
QTest::addRow("ownerOfGroupedProperty2")
<< derivedType << 28 << 17 << ResolveOwnerType << qQuickValueTypes << noLine
<< PropertyIdentifier << u"family"_s;
QTest::addRow("ownerOfAttachedProperty")
<< derivedType << 29 << 6 << ResolveOwnerType << derivedType << keysLine
<< AttachedTypeIdentifierInBindingTarget << u"Keys"_s;
QTest::addRow("ownerOfAttachedProperty2")
<< derivedType << 29 << 14 << ResolveOwnerType << qQuickKeysAttachedType << noLine
<< SignalHandlerIdentifier << u"onBackPressed"_s;
QTest::addRow("actualTypeOfAttachedProperty")
<< derivedType << 29 << 14 << ResolveActualTypeForFieldMemberExpression << noFile
<< noLine << SignalHandlerIdentifier << u"onBackPressed"_s;
QTest::addRow("id2") << derivedType << 7 << 10 << ResolveOwnerType << derivedType << 6
<< QmlObjectIdIdentifier << u"self"_s;
QTest::addRow("propertyBinding")
<< derivedType << 31 << 13 << ResolveOwnerType << baseType << baseTypeLine
<< PropertyIdentifier << u"helloProperty"_s;
QTest::addRow("qmlObject") << derivedType << 6 << 4 << ResolveOwnerType << derived2Type << 4
<< QmlComponentIdentifier << u"Derived2"_s;
}
{
const QString file = testFile(u"resolveExpressionType/JSObjects.qml"_s);
QTest::addRow("jsObject") << file << 11 << 17 << ResolveOwnerType << file << 4
<< JavaScriptIdentifier << u"object"_s;
QTest::addRow("jsObjectMember") << file << 11 << 23 << ResolveOwnerType << noFile << noLine
<< JavaScriptIdentifier << u"a"_s;
QTest::addRow("jsObjectForFieldMemberExpression")
<< file << 11 << 17 << ResolveActualTypeForFieldMemberExpression << noFile << noLine
<< JavaScriptIdentifier << u"object"_s;
QTest::addRow("jsObjectMemberForFieldMemberExpression")
<< file << 11 << 23 << ResolveActualTypeForFieldMemberExpression << noFile << noLine
<< JavaScriptIdentifier << u"a"_s;
QTest::addRow("jsNewMemberExpression")
<< file << 15 << 26 << ResolveActualTypeForFieldMemberExpression << noFile << noLine
<< JavaScriptIdentifier << u"Date"_s;
}
{
const QString file = testFile(u"resolveExpressionType/lambdas.qml"_s);
QTest::addRow("lambda") << file << 6 << 24 << ResolveOwnerType << file << 6
<< LambdaMethodIdentifier << unnamed;
QTest::addRow("lambdaParameter") << file << 7 << 28 << ResolveOwnerType << file << 6
<< JavaScriptIdentifier << u"b"_s;
QTest::addRow("arrow") << file << 8 << 36 << ResolveOwnerType << file << 8
<< LambdaMethodIdentifier << unnamed;
QTest::addRow("arrowParameter")
<< file << 8 << 42 << ResolveOwnerType << file << 8 << JavaScriptIdentifier << u"y"_s;
QTest::addRow("letWithLambda")
<< file << 9 << 13 << ResolveOwnerType << file << 6 << JavaScriptIdentifier << u"moreLambdas"_s;
}
{
const QString myHeader = u"private/myfile_p.h"_s;
const QString file = testFile(u"resolveExpressionType/AttachedComponentMixup.qml"_s);
QTest::addRow("ComponentAsComponent")
<< file << 6 << 9 << ResolveOwnerType << myHeader << noLine
<< QmlComponentIdentifier << u"TypeWithAttachedAndEnum"_s;
QTest::addRow("ComponentAsAttachedType")
<< file << 8 << 56 << ResolveOwnerType << myHeader << noLine
<< AttachedTypeIdentifier << u"TypeWithAttachedAndEnum"_s;
QTest::addRow("ComponentAsEnumName")
<< file << 9 << 33 << ResolveOwnerType << myHeader << noLine
<< QmlComponentIdentifier << u"TypeWithAttachedAndEnum"_s;
}
{
const QString myHeader = u"private/myfile_p.h"_s;
const QString file = testFile(u"resolveExpressionType/QualifiedAttachedComponentMixup.qml"_s);
QTest::addRow("QualifiedComponentAsComponent")
<< file << 6 << 9 << ResolveOwnerType << myHeader << noLine
<< QmlComponentIdentifier << u"TypeWithAttachedAndEnum"_s;
QTest::addRow("QualifiedComponentAsAttachedType")
<< file << 8 << 56 << ResolveOwnerType << myHeader << noLine
<< AttachedTypeIdentifier << u"TypeWithAttachedAndEnum"_s;
QTest::addRow("QualifiedComponentAsEnumName")
<< file << 9 << 35 << ResolveOwnerType << myHeader << noLine
<< QmlComponentIdentifier << u"TypeWithAttachedAndEnum"_s;
QTest::addRow("qualifiedModule") << file << 6 << 1 << ResolveOwnerType << noFile << noLine
<< QualifiedModuleIdentifier << u"RETM"_s;
QTest::addRow("qualifiedModule2") << file << 8 << 41 << ResolveOwnerType << noFile
<< noLine << QualifiedModuleIdentifier << u"RETM"_s;
QTest::addRow("qualifiedModule3") << file << 9 << 30 << ResolveOwnerType << noFile
<< noLine << QualifiedModuleIdentifier << u"RETM"_s;
QTest::addRow("qualifiedModule4") << file << 4 << 42 << ResolveOwnerType << noFile
<< noLine << QualifiedModuleIdentifier << u"RETM"_s;
}
{
const QString myHeader = u"private/myfile_p.h"_s;
const QString file = testFile(u"resolveExpressionType/parameterTypeFromBinding.qml"_s);
QTest::addRow("invalidPropertyChangedHandlerParameter")
<< file << 9 << 23 << ResolveActualTypeForFieldMemberExpression << noFile << noLine
<< JavaScriptIdentifier << u"invalid"_s;
QTest::addRow("invalidPropertyChangedHandlerParameter2")
<< file << 9 << 49 << ResolveActualTypeForFieldMemberExpression << noFile << noLine
<< JavaScriptIdentifier << u"invalid"_s;
QTest::addRow("signalHandlerParameter")
<< file << 12 << 30 << ResolveActualTypeForFieldMemberExpression << myHeader
<< noLine << JavaScriptIdentifier << u"parameter"_s;
QTest::addRow("signalHandlerParameter2")
<< file << 12 << 63 << ResolveActualTypeForFieldMemberExpression << myHeader
<< noLine << JavaScriptIdentifier << u"parameter"_s;
QTest::addRow("invalidSignalParameter")
<< file << 12 << 39 << ResolveActualTypeForFieldMemberExpression << noFile << noLine
<< JavaScriptIdentifier << u"invalid"_s;
QTest::addRow("invalidSignalParameter2")
<< file << 12 << 85 << ResolveActualTypeForFieldMemberExpression << noFile << noLine
<< JavaScriptIdentifier << u"invalid"_s;
QTest::addRow("unrelatedToHandlerParameter") << file << 16 << 17 << ResolveOwnerType << file
<< 15 << JavaScriptIdentifier << u"xxx"_s;
QTest::addRow("unrelatedToHandlerParameter2")
<< file << 17 << 26 << ResolveOwnerType << file << 15 << JavaScriptIdentifier
<< u"xxx"_s;
QTest::addRow("signalHandlerParameterNonArrow")
<< file << 21 << 37 << ResolveActualTypeForFieldMemberExpression << myHeader
<< noLine << JavaScriptIdentifier << u"parameter"_s;
QTest::addRow("onColorChangedHandlerParameter")
<< file << 12 << 30 << ResolveActualTypeForFieldMemberExpression << myHeader
<< noLine << JavaScriptIdentifier << u"parameter"_s;
QTest::addRow("onFakePropertyChangedSignal")
<< file << 26 << 18 << ResolveOwnerType << myHeader << noLine
<< SignalHandlerIdentifier << u"onMySomeTypeChanged"_s;
}
{
const QString file = testFile(u"resolveExpressionType/identifiers.qml"_s);
QTest::addRow("identifierJSVariableFirst")
<< file << 12 << 26 << ResolveActualTypeForFieldMemberExpression << noFile << noLine
<< JavaScriptIdentifier << u"colliding"_s;
QTest::addRow("identifierIdSecond")
<< file << 15 << 22 << ResolveActualTypeForFieldMemberExpression << file << 5
<< QmlObjectIdIdentifier << u"colliding"_s;
QTest::addRow("identifierPropertyThird") << file << 23 << 22 << ResolveOwnerType << file
<< 18 << PropertyIdentifier << u"colliding"_s;
QTest::addRow("identifierPropertyThird2") << file << 31 << 22 << ResolveOwnerType << file
<< 26 << PropertyIdentifier << u"colliding"_s;
QTest::addRow("identifierMethodFourth") << file << 38 << 22 << ResolveOwnerType << file
<< 34 << MethodIdentifier << u"colliding"_s;
}
}
void tst_qmlls_utils::resolveExpressionType()
{
QFETCH(QString, filePath);
QFETCH(int, line);
QFETCH(int, character);
QFETCH(QQmlLSUtils::ResolveOptions, resolveOption);
QFETCH(QString, expectedFile);
QFETCH(int, expectedLine);
QFETCH(QQmlLSUtils::IdentifierType, expectedType);
QFETCH(QString, expectedName);
// they all start at 1.
Q_ASSERT(line > 0);
Q_ASSERT(character > 0);
auto [env, file] = createEnvironmentAndLoadFile(filePath);
auto locations = QQmlLSUtils::itemsFromTextLocation(
file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
QCOMPARE(locations.size(), 1);
auto definition = QQmlLSUtils::resolveExpressionType(locations.front().domItem, resolveOption);
QVERIFY(definition);
QCOMPARE(definition->type, expectedType);
if (expectedName.isEmpty())
QVERIFY(!definition->name);
else
QCOMPARE(definition->name, expectedName);
if (expectedFile.isEmpty()) {
QVERIFY(!definition->semanticScope);
return;
}
QVERIFY(definition->semanticScope);
QCOMPARE(definition->semanticScope->filePath(), expectedFile);
if (expectedLine != -1) {
QQmlJS::SourceLocation location = definition->semanticScope->sourceLocation();
QCOMPARE((int)location.startLine, expectedLine);
}
}
void tst_qmlls_utils::isValidEcmaScriptIdentifier_data()
{
QTest::addColumn<QString>("identifier");
QTest::addColumn<bool>("isValid");
QTest::addRow("f") << u"f"_s << true;
QTest::addRow("f-unicode") << u"\\u0046"_s << true;
QTest::addRow("starts-with-digit") << u"8helloWorld"_s << false;
QTest::addRow("starts-with-unicode-digit") << u"\\u0038helloWorld"_s << false; // \u0038 == '8'
QTest::addRow("keyword") << u"return"_s << false;
QTest::addRow("not-keyword") << u"returny"_s << true;
}
void tst_qmlls_utils::isValidEcmaScriptIdentifier()
{
QFETCH(QString, identifier);
QFETCH(bool, isValid);
QCOMPARE(QQmlLSUtils::isValidEcmaScriptIdentifier(identifier), isValid);
}
using namespace QLspSpecification;
enum InsertOption { None, InsertColon };
tst_qmlls_utils::ExpectedCompletions
tst_qmlls_utils::quickSnippets(const QStringView firstPrefix, const QStringView secondPrefix) const
{
const ExpectedCompletions result{
{ u"%1BorderImage snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1BorderImage {\n"
u"\tid: ${1:name}\n"
u"\tsource: \"${2:file}\"\n"
u"\twidth: ${3:100}; height: ${4:100}\n"
u"\tborder.left: ${5: 5}; border.top: ${5}\n"
u"\tborder.right: ${5}; border.bottom: ${5}\n"
u"}"_s.arg(firstPrefix) },
{ u"%1ColorAnimation snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1ColorAnimation {\n"
u"\tfrom: \"${1:white}\"\n"
u"\tto: \"${2:black}\"\n"
u"\tduration: ${3:200}\n"
u"}"_s.arg(firstPrefix) },
{ u"%1Image snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1Image {\n"
u"\tid: ${1:name}\n"
u"\tsource: \"${2:file}\"\n"
u"}"_s.arg(firstPrefix) },
{ u"%1Item snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1Item {\n"
u"\tid: ${1:name}\n"
u"}"_s.arg(firstPrefix) },
{ u"%1NumberAnimation snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1NumberAnimation {\n"
u"\ttarget: ${1:object}\n"
u"\tproperty: \"${2:name}\"\n"
u"\tduration: ${3:200}\n"
u"\teasing.type: %2Easing.${4:InOutQuad}\n"
u"}"_s.arg(firstPrefix, secondPrefix) },
{ u"%1NumberAnimation with targets snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1NumberAnimation {\n"
u"\ttargets: [${1:object}]\n"
u"\tproperties: \"${2:name}\"\n"
u"\tduration: ${3:200}\n"
u"}"_s.arg(firstPrefix) },
{ u"%1PauseAnimation snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1PauseAnimation {\n"
u"\tduration: ${1:200}\n"
u"}"_s.arg(firstPrefix) },
{ u"%1PropertyAction snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1PropertyAction {\n"
u"\ttarget: ${1:object}\n"
u"\tproperty: \"${2:name}\"\n"
"}"_s.arg(firstPrefix) },
{ u"%1PropertyAction with targets snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1PropertyAction {\n"
u"\ttargets: [${1:object}]\n"
u"\tproperties: \"${2:name}\"\n"
u"}"_s.arg(firstPrefix) },
{ u"%1PropertyChanges snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1PropertyChanges {\n"
u"\ttarget: ${1:object}\n"
u"}"_s.arg(firstPrefix) },
{ u"%1State snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1State {\n"
u"\tname: ${1:name}\n"
u"\t%2PropertyChanges {\n"
u"\t\ttarget: ${2:object}\n"
u"\t}\n"
u"}"_s.arg(firstPrefix, secondPrefix) },
{ u"%1Text snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1Text {\n"
u"\tid: ${1:name}\n"
u"\ttext: qsTr(\"${2:text}\")\n"
u"}"_s.arg(firstPrefix) },
{ u"%1Transition snippet"_s.arg(firstPrefix), CompletionItemKind::Snippet,
u"%1Transition {\n"
u"\tfrom: \"${1:fromState}\"\n"
u"\tto: \"${2:toState}\"\n"
u"}"_s.arg(firstPrefix) }
};
return result;
}
tst_qmlls_utils::ExpectedCompletions
tst_qmlls_utils::quickBindingSnippets(const QStringView firstPrefix) const
{
const ExpectedCompletions result{
{ { u"states binding with PropertyChanges in State"_s, CompletionItemKind::Snippet,
u"states: [\n"
u"\t%1State {\n"
u"\t\tname: \"${1:name}\"\n"
u"\t\t%1PropertyChanges {\n"
u"\t\t\ttarget: ${2:object}\n"
u"\t\t}\n"
u"\t}\n"
u"]"_s.arg(firstPrefix) },
{ u"transitions binding with Transition"_s, CompletionItemKind::Snippet,
u"transitions: [\n"
u"\t%1Transition {\n"
u"\t\tfrom: \"${1:fromState}\"\n"
u"\t\tto: \"${2:fromState}\"\n"
u"\t}\n"
u"]"_s.arg(firstPrefix) } }
};
return result;
}
void tst_qmlls_utils::completions_data()
{
QTest::addColumn<QString>("filePath");
QTest::addColumn<int>("line");
QTest::addColumn<int>("character");
QTest::addColumn<ExpectedCompletions>("expected");
//TODO(QTBUG-142394)
QTest::addColumn<QStringList>("notExpected");
const QString file = testFile(u"Yyy.qml"_s);
const QString emptyFile = testFile(u"emptyFile.qml"_s);
const QString pragmaFile = testFile(u"pragmas.qml"_s);
const QString singletonName = u"SystemInformation"_s;
const QString attachedTypeName = u"Component"_s;
const QString attachedTypeName2 = u"Keys"_s;
const auto attachedTypes = ExpectedCompletions({
{ attachedTypeName, CompletionItemKind::Class },
{ attachedTypeName2, CompletionItemKind::Class },
});
const auto keywords = ExpectedCompletions({
{ u"function"_s, CompletionItemKind::Keyword },
{ u"required"_s, CompletionItemKind::Keyword },
{ u"enum"_s, CompletionItemKind::Keyword },
{ u"component"_s, CompletionItemKind::Keyword },
});
const auto mixedTypes = ExpectedCompletions({
{ u"Zzz"_s, CompletionItemKind::Class },
{ u"Item"_s, CompletionItemKind::Class },
{ u"int"_s, CompletionItemKind::Class },
{ u"date"_s, CompletionItemKind::Class },
});
const auto constructorTypes = ExpectedCompletions({
{ u"Rectangle"_s, CompletionItemKind::Constructor },
{ u"MyRectangle"_s, CompletionItemKind::Constructor },
{ u"Zzz"_s, CompletionItemKind::Constructor },
{ u"Item"_s, CompletionItemKind::Constructor },
{ u"QtObject"_s, CompletionItemKind::Constructor },
});
const auto rectangleTypes = ExpectedCompletions({
{ u"Rectangle"_s, CompletionItemKind::Constructor },
{ u"MyRectangle"_s, CompletionItemKind::Constructor },
});
QTest::newRow("objEmptyLine") << file << 9 << 1
<< ExpectedCompletions({
{ u"Rectangle"_s, CompletionItemKind::Constructor },
{ u"width"_s, CompletionItemKind::Property },
})
<< QStringList({ u"QtQuick"_s, u"vector4d"_s });
const QString propertyCompletion = u"property type name: value;"_s;
const QString functionCompletion = u"function name(args...): returnType { statements...}"_s;
const ExpectedCompletions quickSnippetsWithQualifier = quickSnippets(u"QQ.", u"QQ.");
const ExpectedCompletions quickSnippetsWithQualifierInside = quickSnippets(u"", u"QQ.");
const ExpectedCompletions quickSnippetsWithoutQualifier = quickSnippets(u"", u"");
const ExpectedCompletions quickSnippetsWithoutQualifierWithBindings =
quickBindingSnippets(u"") += quickSnippetsWithoutQualifier;
const ExpectedCompletions quickSnippetsWithQualifierWithBindings =
quickBindingSnippets(u"QQ.") += quickSnippetsWithQualifier;
const ExpectedCompletions quickSnippetsWithQualifierInsideWithBindings =
quickBindingSnippets(u"QQ.") += quickSnippetsWithQualifierInside;
// TODO(QTBUG-138020), TODO(QTBUG-142394)
QTest::newRow("objEmptyLineSnippets")
<< file << 9 << 1
<< (ExpectedCompletions({
{ propertyCompletion, CompletionItemKind::Snippet,
u"property ${1:type} ${2:name}: ${0:value};"_s },
{ u"readonly property type name: value;"_s, CompletionItemKind::Snippet,
u"readonly property ${1:type} ${2:name}: ${0:value};"_s },
{ u"default property type name: value;"_s, CompletionItemKind::Snippet,
u"default property ${1:type} ${2:name}: ${0:value};"_s },
{ u"property type name;"_s, CompletionItemKind::Snippet,
u"property ${1:type} ${0:name};"_s },
{ u"required property type name;"_s, CompletionItemKind::Snippet,
u"required property ${1:type} ${0:name};"_s },
{ u"default property type name;"_s, CompletionItemKind::Snippet,
u"default property ${1:type} ${0:name};"_s },
{ u"default required property type name;"_s, CompletionItemKind::Snippet,
u"default required property ${1:type} ${0:name};"_s },
{ u"final property type name;"_s, CompletionItemKind::Snippet,
u"final property ${1:type} ${0:name};"_s },
{ u"final property type name: value;"_s, CompletionItemKind::Snippet,
u"final property ${1:type} ${2:name}: ${0:value};"_s },
{ u"virtual property type name;"_s, CompletionItemKind::Snippet,
u"virtual property ${1:type} ${0:name};"_s },
{ u"virtual property type name: value;"_s, CompletionItemKind::Snippet,
u"virtual property ${1:type} ${2:name}: ${0:value};"_s },
{ u"override property type name;"_s, CompletionItemKind::Snippet,
u"override property ${1:type} ${0:name};"_s },
{ u"override property type name: value;"_s, CompletionItemKind::Snippet,
u"override property ${1:type} ${2:name}: ${0:value};"_s },
{ u"default final property type name;"_s, CompletionItemKind::Snippet,
u"default final property ${1:type} ${0:name};"_s },
{ u"default final property type name: value;"_s, CompletionItemKind::Snippet,
u"default final property ${1:type} ${2:name}: ${0:value};"_s },
{ u"default virtual property type name;"_s, CompletionItemKind::Snippet,
u"default virtual property ${1:type} ${0:name};"_s },
{ u"default virtual property type name: value;"_s, CompletionItemKind::Snippet,
u"default virtual property ${1:type} ${2:name}: ${0:value};"_s },
{ u"default override property type name;"_s, CompletionItemKind::Snippet,
u"default override property ${1:type} ${0:name};"_s },
{ u"default override property type name: value;"_s, CompletionItemKind::Snippet,
u"default override property ${1:type} ${2:name}: ${0:value};"_s },
{ u"final required property type name;"_s, CompletionItemKind::Snippet,
u"final required property ${1:type} ${0:name};"_s },
{ u"final readonly property type name: value;"_s, CompletionItemKind::Snippet,
u"final readonly property ${1:type} ${2:name}: ${0:value};"_s },
{ u"virtual required property type name;"_s, CompletionItemKind::Snippet,
u"virtual required property ${1:type} ${0:name};"_s },
{ u"virtual readonly property type name: value;"_s, CompletionItemKind::Snippet,
u"virtual readonly property ${1:type} ${2:name}: ${0:value};"_s },
{ u"override required property type name;"_s, CompletionItemKind::Snippet,
u"override required property ${1:type} ${0:name};"_s },
{ u"override readonly property type name: value;"_s, CompletionItemKind::Snippet,
u"override readonly property ${1:type} ${2:name}: ${0:value};"_s },
{ u"default final required property type name;"_s,
CompletionItemKind::Snippet,
u"default final required property ${1:type} ${0:name};"_s },
{ u"default virtual required property type name;"_s,
CompletionItemKind::Snippet,
u"default virtual required property ${1:type} ${0:name};"_s },
{ u"default override required property type name;"_s,
CompletionItemKind::Snippet,
u"default override required property ${1:type} ${0:name};"_s },
{ u"signal name(arg1:type1, ...)"_s, CompletionItemKind::Snippet,
u"signal ${1:name}($0)"_s },
{ u"signal name;"_s, CompletionItemKind::Snippet, u"signal ${0:name};"_s },
{ u"required name;"_s, CompletionItemKind::Snippet,
u"required ${0:name};"_s },
{ functionCompletion, CompletionItemKind::Snippet,
u"function ${1:name}($2): ${3:returnType} {\n\t$0\n}"_s },
{ u"enum name { Values...}"_s, CompletionItemKind::Snippet,
u"enum ${1:name} {\n\t${0:values}\n}"_s },
{ u"component Name: BaseType { ... }"_s, CompletionItemKind::Snippet,
u"component ${1:name}: ${2:baseType} {\n\t$0\n}"_s },
}) += quickSnippetsWithoutQualifierWithBindings)
// not allowed because required properties need an initializer
<< QStringList({ u"readonly property type name;"_s });
QTest::newRow("quickSnippetsForQualifiedQuickImport")
<< testFile("qualifiedModule.qml") << 5 << 1
<< quickSnippetsWithQualifier
// not allowed because required properties need an initializer
<< QStringList({ u"readonly property type name;"_s });
QTest::newRow("quickSnippetsForQualifiedQuickImportBeforeDot")
<< testFile("qualifiedModule.qml") << 5 << 6
<< quickSnippetsWithQualifierWithBindings
// not allowed because required properties need an initializer
<< QStringList({ u"readonly property type name;"_s });
QTest::newRow("quickSnippetsForQualifiedQuickImportAfterDot")
<< testFile("qualifiedModule.qml") << 5 << 7
<< quickSnippetsWithQualifierInsideWithBindings
// not allowed because required properties need an initializer
<< QStringList({ u"readonly property type name;"_s });
QTest::newRow("quickSnippetsForQualifiedQuickImportBeforeDotInBinding")
<< testFile("qualifiedModule.qml") << 4 << 32 << quickSnippetsWithQualifier
<< QStringList({ u"states binding with PropertyChanges in State"_s,
u"transitions binding with Transition"_s });
QTest::newRow("quickSnippetsForQualifiedQuickImportAfterDotInBinding")
<< testFile("qualifiedModule.qml") << 4 << 33 << quickSnippetsWithQualifierInside
<< QStringList({ u"states binding with PropertyChanges in State"_s,
u"transitions binding with Transition"_s });
// forbid transitions and states because QtObject does not inherit from Item
QTest::newRow("qtObjectEmptyLineSnippets")
<< file << 141 << 8
<< ExpectedCompletions{ { u"Item"_s, CompletionItemKind::Constructor } }
<< QStringList({ u"transitions binding with Transition"_s,
u"states binding with PropertyChanges in State"_s });
QTest::newRow("handlers") << file << 5 << 1
<< ExpectedCompletions{ {
{ u"onHandleMe"_s, CompletionItemKind::Method },
{ u"onDefaultPropertyChanged"_s,
CompletionItemKind::Method },
} }
<< QStringList({ u"QtQuick"_s, u"vector4d"_s });
QTest::newRow("attachedTypes")
<< file << 9 << 1 << attachedTypes << QStringList{ u"QtQuick"_s, u"vector4d"_s };
QTest::newRow("attachedTypesInScript")
<< file << 6 << 12 << attachedTypes << QStringList{ u"QtQuick"_s, u"vector4d"_s };
QTest::newRow("attachedTypesInLongScript")
<< file << 10 << 16 << attachedTypes << QStringList{ u"QtQuick"_s, u"vector4d"_s };
QTest::newRow("completionFromRootId") << file << 10 << 21
<< ExpectedCompletions({
{ u"width"_s, CompletionItemKind::Property },
{ u"lala"_s, CompletionItemKind::Method },
{ u"foo"_s, CompletionItemKind::Property },
})
<< QStringList{ u"QtQuick"_s, u"vector4d"_s };
QTest::newRow("attachedProperties") << file << 89 << 15
<< ExpectedCompletions({
{ u"onCompleted"_s, CompletionItemKind::Method },
})
<< QStringList{ u"QtQuick"_s,
u"vector4d"_s,
attachedTypeName,
u"Rectangle"_s,
u"property"_s,
u"foo"_s,
u"onActiveFocusOnTabChanged"_s };
QTest::newRow("inBindingLabel") << file << 6 << 10
<< ExpectedCompletions({
{ u"Rectangle"_s, CompletionItemKind::Constructor },
{ u"width"_s, CompletionItemKind::Property },
})
<< QStringList({ u"QtQuick"_s, u"vector4d"_s, u"property"_s });
QTest::newRow("afterBinding") << file << 6 << 11
<< (ExpectedCompletions({
{ u"height"_s, CompletionItemKind::Property },
{ u"width"_s, CompletionItemKind::Property },
{ u"Rectangle"_s, CompletionItemKind::Constructor },
{ singletonName, CompletionItemKind::Class },
})
+ attachedTypes)
<< QStringList({ u"QtQuick"_s, u"property"_s, u"vector4d"_s });
QTest::newRow("jsGlobals") << file << 6 << 11
<< ExpectedCompletions{ {
{ u"console"_s, CompletionItemKind::Property },
{ u"Math"_s, CompletionItemKind::Property },
} }
<< QStringList({ u"QtQuick"_s, u"property"_s, u"vector4d"_s });
QTest::newRow("jsGlobals2") << file << 100 << 32
<< ExpectedCompletions{ {
{ u"abs"_s, CompletionItemKind::Method },
{ u"log"_s, CompletionItemKind::Method },
{ u"E"_s, CompletionItemKind::Property },
} }
<< QStringList({ u"QtQuick"_s, u"property"_s, u"vector4d"_s,
u"foo"_s, u"lala"_s });
QTest::newRow("afterLongBinding")
<< file << 10 << 16
<< ExpectedCompletions({
{ u"height"_s, CompletionItemKind::Property },
{ u"width"_s, CompletionItemKind::Property },
{ u"Rectangle"_s, CompletionItemKind::Constructor },
})
<< QStringList({ u"QtQuick"_s, u"property"_s, u"vector4d"_s });
QTest::newRow("afterId") << file << 5 << 8 << ExpectedCompletions({})
<< QStringList({
u"QtQuick"_s,
u"property"_s,
u"Rectangle"_s,
u"width"_s,
u"vector4d"_s,
u"import"_s,
});
QTest::newRow("emptyFile") << emptyFile << 1 << 1
<< ExpectedCompletions({
{ u"import"_s, CompletionItemKind::Keyword },
{ u"pragma"_s, CompletionItemKind::Keyword },
})
<< QStringList({ u"QtQuick"_s, u"vector4d"_s, u"width"_s });
QTest::newRow("importImport") << file << 1 << 4
<< ExpectedCompletions({
{ u"import"_s, CompletionItemKind::Keyword },
})
<< QStringList({ u"QtQuick"_s, u"vector4d"_s, u"width"_s,
u"Rectangle"_s });
{
const QString importFile = testFile(u"completions/imports.qml"_s);
QTest::newRow("importAfterDot") << importFile << 1 << 16
<< ExpectedCompletions({
{ u"Controls"_s, CompletionItemKind::Module },
})
<< QStringList({ u"QtQuick"_s, u"vector4d"_s, u"width"_s,
u"Rectangle"_s, u"as"_s });
QTest::newRow("importAfterDot2") << importFile << 1 << 25
<< ExpectedCompletions({
{ u"Basic"_s, CompletionItemKind::Module },
})
<< QStringList({ u"QtQuick"_s, u"vector4d"_s, u"width"_s,
u"Rectangle"_s, u"as"_s });
QTest::newRow("importAfterDotMissing")
<< importFile << 2 << 25
<< ExpectedCompletions({
{ u"Basic"_s, CompletionItemKind::Module },
})
<< QStringList(
{ u"QtQuick"_s, u"vector4d"_s, u"width"_s, u"Rectangle"_s, u"as"_s });
}
QTest::newRow("importModuleStart")
<< file << 1 << 8
<< ExpectedCompletions({
{ u"QtQuick"_s, CompletionItemKind::Module },
})
<< QStringList({ u"vector4d"_s, u"width"_s, u"Rectangle"_s, u"import"_s });
QTest::newRow("importVersionStart")
<< file << 1 << 16
<< ExpectedCompletions({
{ u"2"_s, CompletionItemKind::Constant },
{ u"as"_s, CompletionItemKind::Keyword },
})
<< QStringList({ u"Rectangle"_s, u"import"_s, u"vector4d"_s, u"width"_s });
// QTest::newRow("importVersionMinor")
// << uri << 1 << 18
// << ExpectedCompletions({
// { u"15"_s, CompletionItemKind::Constant },
// })
// << QStringList({ u"as"_s, u"Rectangle"_s, u"import"_s, u"vector4d"_s, u"width"_s });
QTest::newRow("expandBase1") << file << 10 << 24
<< ExpectedCompletions({
{ u"width"_s, CompletionItemKind::Property },
{ u"foo"_s, CompletionItemKind::Property },
})
<< QStringList({ u"import"_s, u"Rectangle"_s });
QTest::newRow("expandBase2") << file << 11 << 30
<< ExpectedCompletions({
{ u"width"_s, CompletionItemKind::Property },
{ u"color"_s, CompletionItemKind::Property },
})
<< QStringList({ u"foo"_s, u"import"_s, u"Rectangle"_s });
QTest::newRow("qualifiedTypeCompletionBeforeDot")
<< testFile(u"qualifiedModule.qml"_s) << 4 << 31
<< ExpectedCompletions({
{ u"QQ.Rectangle"_s, CompletionItemKind::Constructor },
})
<< QStringList({ u"foo"_s, u"import"_s, u"lala"_s, });
QTest::newRow("qualifiedTypeCompletionAfterDot")
<< testFile(u"qualifiedModule.qml"_s) << 4 << 35
<< ExpectedCompletions({
{ u"Rectangle"_s, CompletionItemKind::Constructor },
})
<< QStringList({ u"foo"_s, u"import"_s, u"lala"_s, u"width"_s });
QTest::newRow("qualifiedTypeCompletionBeforeDotInDefaultBinding")
<< testFile(u"qualifiedModule.qml"_s) << 5 << 5
<< ExpectedCompletions({
{ u"QQ.Rectangle"_s, CompletionItemKind::Constructor },
})
<< QStringList({ u"foo"_s, u"import"_s, u"lala"_s });
QTest::newRow("qualifiedTypeCompletionAfterDotInDefaultBinding")
<< testFile(u"qualifiedModule.qml"_s) << 5 << 7
<< ExpectedCompletions({
{ u"Rectangle"_s, CompletionItemKind::Constructor },
})
<< QStringList({ u"foo"_s, u"import"_s, u"lala"_s, u"width"_s });
QTest::newRow("parameterCompletion")
<< file << 36 << 24
<< ExpectedCompletions({
{ u"helloWorld"_s, CompletionItemKind::Variable },
{ u"helloMe"_s, CompletionItemKind::Variable },
})
<< QStringList();
QTest::newRow("inMethodName") << file << 15 << 14 << ExpectedCompletions({})
<< QStringList{ u"QtQuick"_s, u"vector4d"_s, u"foo"_s,
u"root"_s, u"Item"_s, singletonName };
QTest::newRow("inMethodReturnType") << file << 17 << 54 << mixedTypes
<< QStringList{
u"QtQuick"_s,
u"foo"_s,
u"root"_s,
};
QTest::newRow("letStatement") << file << 95 << 13 << ExpectedCompletions({})
<< QStringList{ u"QtQuick"_s, u"vector4d"_s, u"root"_s };
QTest::newRow("inParameterCompletion") << file << 35 << 39 << ExpectedCompletions({})
<< QStringList{
u"helloWorld"_s,
u"helloMe"_s,
};
QTest::newRow("parameterTypeCompletion") << file << 35 << 55 << mixedTypes
<< QStringList{
u"helloWorld"_s,
u"helloMe"_s,
};
QTest::newRow("propertyTypeCompletion") << file << 16 << 14 << mixedTypes
<< QStringList{
u"helloWorld"_s,
u"helloMe"_s,
};
QTest::newRow("propertyTypeCompletion2") << file << 16 << 23 << mixedTypes
<< QStringList{
u"helloWorld"_s,
u"helloMe"_s,
};
QTest::newRow("propertyNameCompletion")
<< file << 16 << 24 << ExpectedCompletions({})
<< QStringList{
u"helloWorld"_s, u"helloMe"_s, u"Zzz"_s, u"Item"_s, u"int"_s, u"date"_s,
};
QTest::newRow("propertyNameCompletion2")
<< file << 16 << 25 << ExpectedCompletions({})
<< QStringList{
u"helloWorld"_s, u"helloMe"_s, u"Zzz"_s, u"Item"_s, u"int"_s, u"date"_s,
};
QTest::newRow("propertyDefinitionBinding")
<< file << 90 << 27
<< (ExpectedCompletions({
{ u"lala"_s, CompletionItemKind::Method },
{ u"createRectangle"_s, CompletionItemKind::Method },
{ u"createItem"_s, CompletionItemKind::Method },
{ u"createAnything"_s, CompletionItemKind::Method },
}) += constructorTypes)
<< QStringList{
u"helloWorld"_s,
u"helloMe"_s,
u"int"_s,
u"date"_s,
};
QTest::newRow("ignoreNonRelatedTypesForPropertyDefinitionBinding")
<< file << 16 << 28
<< (ExpectedCompletions({
{ u"createRectangle"_s, CompletionItemKind::Method },
{ u"createItem"_s, CompletionItemKind::Method },
{ u"createAnything"_s, CompletionItemKind::Method },
}) += rectangleTypes)
<< QStringList{
u"Item"_s, u"Zzz"_s, u"helloWorld"_s, u"helloMe"_s,
u"int"_s, u"date"_s, u"Item"_s, u"QtObject"_s,
};
QTest::newRow("inBoundObject")
<< file << 16 << 40
<< (ExpectedCompletions({
{ u"objectName"_s, CompletionItemKind::Property },
{ u"width"_s, CompletionItemKind::Property },
{ propertyCompletion, CompletionItemKind::Snippet },
{ functionCompletion, CompletionItemKind::Snippet },
}) += constructorTypes)
<< QStringList{
u"helloWorld"_s, u"helloMe"_s, u"int"_s, u"date"_s, u"QtQuick"_s, u"vector4d"_s,
};
QTest::newRow("qualifiedIdentifierCompletion")
<< file << 37 << 36
<< ExpectedCompletions({
{ u"helloProperty"_s, CompletionItemKind::Property },
{ u"childAt"_s, CompletionItemKind::Method },
})
<< QStringList{ u"helloVar"_s, u"someItem"_s, u"color"_s, u"helloWorld"_s,
u"propertyOfZZZ"_s };
QTest::newRow("scriptExpressionCompletion")
<< file << 60 << 16
<< ExpectedCompletions({
// parameters
{ u"jsParameterInChild"_s, CompletionItemKind::Variable },
// own properties
{ u"jsIdentifierInChild"_s, CompletionItemKind::Variable },
{ u"functionInChild"_s, CompletionItemKind::Method },
{ u"propertyInChild"_s, CompletionItemKind::Property },
// inherited properties from QML
{ u"functionInBase"_s, CompletionItemKind::Method },
{ u"propertyInBase"_s, CompletionItemKind::Property },
// inherited properties (transitive) from C++
{ u"objectName"_s, CompletionItemKind::Property },
{ u"someItem"_s, CompletionItemKind::Value },
{ u"true"_s, CompletionItemKind::Value },
{ u"false"_s, CompletionItemKind::Value },
{ u"null"_s, CompletionItemKind::Value },
})
<< QStringList{
u"helloVar"_s,
u"color"_s,
u"helloWorld"_s,
u"propertyOfZZZ"_s,
u"propertyInDerived"_s,
u"functionInDerived"_s,
u"jsIdentifierInDerived"_s,
u"jsIdentifierInBase"_s,
u"lala"_s,
u"foo"_s,
u"jsParameterInBase"_s,
u"jsParameterInDerived"_s,
};
QTest::newRow("qualifiedScriptExpressionCompletion")
<< file << 60 << 34
<< ExpectedCompletions({
// own properties
{ u"helloProperty"_s, CompletionItemKind::Property },
// inherited properties (transitive) from C++
{ u"width"_s, CompletionItemKind::Property },
})
<< QStringList{
u"helloVar"_s,
u"color"_s,
u"helloWorld"_s,
u"propertyOfZZZ"_s,
u"propertyInDerived"_s,
u"functionInDerived"_s,
u"jsIdentifierInDerived"_s,
u"jsIdentifierInBase"_s,
u"jsIdentifierInChild"_s,
u"lala"_s,
u"foo"_s,
u"jsParameterInBase"_s,
u"jsParameterInDerived"_s,
u"jsParameterInChild"_s,
u"functionInChild"_s,
};
QTest::newRow("pragma") << pragmaFile << 1 << 8
<< ExpectedCompletions({
{ u"NativeMethodBehavior"_s, CompletionItemKind::Value },
{ u"ComponentBehavior"_s, CompletionItemKind::Value },
{ u"ListPropertyAssignBehavior"_s,
CompletionItemKind::Value },
{ u"Singleton"_s, CompletionItemKind::Value },
// note: only complete the Addressible/Inaddressible part of
// ValueTypeBehavior!
{ u"ValueTypeBehavior"_s, CompletionItemKind::Value },
})
<< QStringList{
u"int"_s,
u"Rectangle"_s,
u"FunctionSignatureBehavior"_s,
u"Strict"_s,
};
QTest::newRow("pragmaValue") << pragmaFile << 2 << 30
<< ExpectedCompletions({
{ u"AcceptThisObject"_s, CompletionItemKind::Value },
{ u"RejectThisObject"_s, CompletionItemKind::Value },
})
<< QStringList{
u"int"_s,
u"Rectangle"_s,
u"FunctionSignatureBehavior"_s,
u"Strict"_s,
u"NativeMethodBehavior"_s,
u"ComponentBehavior"_s,
u"ListPropertyAssignBehavior"_s,
u"Singleton"_s,
u"ValueTypeBehavior"_s,
u"Unbound"_s,
};
QTest::newRow("pragmaMultiValue")
<< pragmaFile << 3 << 43
<< ExpectedCompletions({
{ u"ReplaceIfNotDefault"_s, CompletionItemKind::Value },
{ u"Append"_s, CompletionItemKind::Value },
{ u"Replace"_s, CompletionItemKind::Value },
})
<< QStringList{
u"int"_s,
u"Rectangle"_s,
u"FunctionSignatureBehavior"_s,
u"Strict"_s,
u"NativeMethodBehavior"_s,
u"ComponentBehavior"_s,
u"ListPropertyAssignBehavior"_s,
u"Singleton"_s,
u"ValueTypeBehavior"_s,
u"Unbound"_s,
};
QTest::newRow("pragmaWithoutValue")
<< pragmaFile << 1 << 17
<< ExpectedCompletions({
{ u"NativeMethodBehavior"_s, CompletionItemKind::Value },
{ u"ComponentBehavior"_s, CompletionItemKind::Value },
{ u"ListPropertyAssignBehavior"_s, CompletionItemKind::Value },
{ u"Singleton"_s, CompletionItemKind::Value },
// note: only complete the Addressible/Inaddressible part of
// ValueTypeBehavior!
{ u"ValueTypeBehavior"_s, CompletionItemKind::Value },
})
<< QStringList{
u"int"_s,
u"Rectangle"_s,
u"FunctionSignatureBehavior"_s,
u"Strict"_s,
};
QTest::newRow("non-block-scoped-variable")
<< file << 69 << 21
<< ExpectedCompletions({
{ u"helloVarVariable"_s, CompletionItemKind::Variable },
})
<< QStringList{};
QTest::newRow("block-scoped-variable")
<< file << 76 << 21 << ExpectedCompletions{ { u"test2"_s, CompletionItemKind::Method } }
<< QStringList{ u"helloLetVariable"_s, u"helloVarVariable"_s };
QTest::newRow("singleton") << file << 78 << 33
<< ExpectedCompletions({
{ singletonName, CompletionItemKind::Class },
})
<< QStringList{};
QTest::newRow("singletonPropertyAndEnums")
<< file << 78 << 52
<< ExpectedCompletions({
{ u"byteOrder"_s, CompletionItemKind::Property },
{ u"Little"_s, CompletionItemKind::EnumMember },
{ u"Endian"_s, CompletionItemKind::Enum },
})
<< QStringList{
u"int"_s,
u"Rectangle"_s,
u"foo"_s,
};
QTest::newRow("enumsFromItem") << file << 86 << 33
<< ExpectedCompletions({
{ u"World"_s, CompletionItemKind::EnumMember },
{ u"ValueOne"_s, CompletionItemKind::EnumMember },
{ u"ValueTwo"_s, CompletionItemKind::EnumMember },
{ u"Hello"_s, CompletionItemKind::Enum },
{ u"MyEnum"_s, CompletionItemKind::Enum },
})
<< QStringList{
u"int"_s,
u"Rectangle"_s,
};
QTest::newRow("enumsFromEnumName")
<< file << 87 << 40
<< ExpectedCompletions({
{ u"World"_s, CompletionItemKind::EnumMember },
})
<< QStringList{
u"int"_s, u"Rectangle"_s, u"foo"_s, u"ValueOne"_s,
u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
};
QTest::newRow("requiredProperty")
<< file << 97 << 14
<< ExpectedCompletions({
{ u"property"_s, CompletionItemKind::Keyword },
{ u"default"_s, CompletionItemKind::Keyword },
{ u"virtual"_s, CompletionItemKind::Keyword },
{ u"final"_s, CompletionItemKind::Keyword },
{ u"override"_s, CompletionItemKind::Keyword },
})
<< QStringList{
u"readonly"_s, u"required"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
};
QTest::newRow("readonlyProperty")
<< file << 98 << 13
<< ExpectedCompletions({
{ u"property"_s, CompletionItemKind::Keyword },
{ u"default"_s, CompletionItemKind::Keyword },
{ u"virtual"_s, CompletionItemKind::Keyword },
{ u"final"_s, CompletionItemKind::Keyword },
{ u"override"_s, CompletionItemKind::Keyword },
})
<< QStringList{
u"required"_s, u"readonly"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
};
QTest::newRow("defaultProperty")
<< file << 99 << 12
<< ExpectedCompletions({
{ u"property"_s, CompletionItemKind::Keyword },
{ u"readonly"_s, CompletionItemKind::Keyword },
{ u"required"_s, CompletionItemKind::Keyword },
{ u"virtual"_s, CompletionItemKind::Keyword },
{ u"final"_s, CompletionItemKind::Keyword },
{ u"override"_s, CompletionItemKind::Keyword },
})
<< QStringList{
u"default"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
};
QTest::newRow("defaultProperty2")
<< file << 99 << 20
<< ExpectedCompletions({
{ u"property"_s, CompletionItemKind::Keyword },
{ u"readonly"_s, CompletionItemKind::Keyword },
{ u"required"_s, CompletionItemKind::Keyword },
{ u"virtual"_s, CompletionItemKind::Keyword },
{ u"final"_s, CompletionItemKind::Keyword },
{ u"override"_s, CompletionItemKind::Keyword },
})
<< QStringList{
u"default"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
};
QTest::newRow("defaultProperty3")
<< file << 99 << 21 << ExpectedCompletions{ { u"int"_s, CompletionItemKind::Class } }
<< QStringList{
u"property"_s,
u"readonly"_s,
u"required"_s,
};
const auto virtSpecifiersTest = [&file](QString keyword, int lineNum){
const auto testName = keyword + "Property";
QTest::newRow(qPrintable(testName))
<< file << lineNum << 17 /*after keyword before end of property token*/
<< ExpectedCompletions({
{ u"property"_s, CompletionItemKind::Keyword },
{ u"readonly"_s, CompletionItemKind::Keyword },
{ u"required"_s, CompletionItemKind::Keyword },
{ u"default"_s, CompletionItemKind::Keyword },
})
<< QStringList{
u"virtual"_s, u"final"_s, u"override"_s, u"int"_s, u"Rectangle"_s, u"foo"_s,
u"ValueOne"_s, u"ValueTwo"_s, u"Hello"_s, u"MyEnum"_s,
};
};
virtSpecifiersTest("virtual", 143);
virtSpecifiersTest("override", 144);
virtSpecifiersTest("final", 145);
const QString forStatementCompletion = u"for (initializer; condition; increment) { statements... }"_s;
const QString ifStatementCompletion = u"if (condition) statement"_s;
const QString letStatementCompletion = u"let variable = value;"_s;
const QString constStatementCompletion = u"const variable = value;"_s;
const QString varStatementCompletion = u"var variable = value;"_s;
// for the for loop
const QString letStatementCompletionWithoutSemicolon = letStatementCompletion.chopped(1);
const QString constStatementCompletionWithoutSemicolon = constStatementCompletion.chopped(1);
const QString varStatementCompletionWithoutSemicolon = varStatementCompletion.chopped(1);
const QString caseStatementCompletion = u"case value: statements..."_s;
const QString caseStatement2Completion = u"case value: { statements... }"_s;
const QString defaultStatementCompletion = u"default: statements..."_s;
const QString defaultStatement2Completion = u"default: { statements... }"_s;
// warning: the completion strings in the test below were all tested by hand in VS Code to
// make sure they are easy to use. Make sure to check the code snippets by hand before changing
// them.
QTest::newRow("jsStatements")
<< file << 104 << 1
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet,
u"let ${1:variable} = $0;"_s },
{ u"const variable = value;"_s, CompletionItemKind::Snippet,
u"const ${1:variable} = $0;"_s },
{ u"var variable = value;"_s, CompletionItemKind::Snippet,
u"var ${1:variable} = $0;"_s },
{ u"{ statements... }"_s, CompletionItemKind::Snippet,
u"{\n\t$0\n}"_s },
{ u"if (condition) { statements }"_s,
CompletionItemKind::Snippet, u"if ($1) {\n\t$0\n}"_s },
{ u"do { statements } while (condition);"_s,
CompletionItemKind::Snippet, u"do {\n\t$1\n} while ($0);"_s },
{ u"while (condition) { statements...}"_s,
CompletionItemKind::Snippet, u"while ($1) {\n\t$0\n}"_s },
{ forStatementCompletion,
CompletionItemKind::Snippet, u"for ($1;$2;$3) {\n\t$0\n}"_s },
{ u"try { statements... } catch(error) { statements... }"_s,
CompletionItemKind::Snippet, u"try {\n\t$1\n} catch($2) {\n\t$0\n}"_s },
{ u"try { statements... } finally { statements... }"_s,
CompletionItemKind::Snippet, u"try {\n\t$1\n} finally {\n\t$0\n}"_s },
{ u"try { statements... } catch(error) { statements... } finally { statements... }"_s,
CompletionItemKind::Snippet, u"try {\n\t$1\n} catch($2) {\n\t$3\n} finally {\n\t$0\n}"_s },
{ u"for (property in object) { statements... }"_s,
CompletionItemKind::Snippet, u"for ($1 in $2) {\n\t$0\n}"_s },
{ u"for (element of array) { statements... }"_s,
CompletionItemKind::Snippet, u"for ($1 of $2) {\n\t$0\n}"_s },
{ u"continue"_s, CompletionItemKind::Keyword },
{ u"break"_s, CompletionItemKind::Keyword },
}
<< QStringList{ caseStatementCompletion,
caseStatement2Completion,
defaultStatementCompletion,
defaultStatement2Completion,
};
QTest::newRow("forStatementLet")
<< file << 103 << 13
<< ExpectedCompletions{ { letStatementCompletionWithoutSemicolon,
CompletionItemKind::Snippet, u"let ${1:variable} = $0"_s },
{ constStatementCompletionWithoutSemicolon,
CompletionItemKind::Snippet, u"const ${1:variable} = $0"_s },
{ varStatementCompletionWithoutSemicolon,
CompletionItemKind::Snippet, u"var ${1:variable} = $0"_s },
{ u"helloJSStatements"_s, CompletionItemKind::Method } }
<< QStringList{ u"property"_s,
u"readonly"_s,
u"required"_s,
forStatementCompletion,
ifStatementCompletion,
letStatementCompletion,
constStatementCompletion,
varStatementCompletion };
QTest::newRow("forStatementCondition")
<< file << 103 << 25
<< ExpectedCompletions{
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ u"i"_s, CompletionItemKind::Variable },
}
<< QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
forStatementCompletion, ifStatementCompletion, varStatementCompletion,
letStatementCompletion, constStatementCompletion, }
;
QTest::newRow("forStatementIncrement")
<< file << 103 << 30
<< ExpectedCompletions{
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ u"i"_s, CompletionItemKind::Variable },
}
<< QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
forStatementCompletion, ifStatementCompletion, varStatementCompletion,
letStatementCompletion, constStatementCompletion, }
;
QTest::newRow("forStatementIncrement2")
<< file << 103 << 33
<< ExpectedCompletions{ { u"helloJSStatements"_s, CompletionItemKind::Method } }
<< QStringList{
u"property"_s, u"readonly"_s,
u"required"_s, forStatementCompletion,
ifStatementCompletion, varStatementCompletion,
letStatementCompletion, constStatementCompletion,
};
QTest::newRow("forStatementWithoutBlock")
<< file << 107 << 12
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind::Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ u"j"_s, CompletionItemKind::Variable },
{ forStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ propertyCompletion };
QTest::newRow("blockStatementBeforeBracket")
<< file << 103 << 36
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind::Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ u"i"_s, CompletionItemKind::Variable },
{ forStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ propertyCompletion };
QTest::newRow("blockStatementAfterBracket")
<< file << 103 << 37
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind::Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ forStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ propertyCompletion };
QTest::newRow("ifStatementCondition")
<< file << 110 << 15
<< ExpectedCompletions{
{ u"hello"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion, constStatementCompletion }
;
QTest::newRow("ifStatementConsequence")
<< file << 111 << 12
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind::Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"hello"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion }
;
QTest::newRow("ifStatementAlternative")
<< file << 113 << 12
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind::Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"hello"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion }
;
QTest::newRow("binaryExpressionCompletionInsideStatement")
<< file << 113 << 21
<< ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable }, }
<< QStringList{ propertyCompletion, forStatementCompletion }
;
QTest::newRow("elseIfStatement")
<< file << 121 << 18
<< ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable }, }
<< QStringList{ propertyCompletion, letStatementCompletion, ifStatementCompletion }
;
QTest::newRow("returnStatement")
<< file << 125 << 16
<< ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("returnStatement2")
<< testFile("completions/returnStatement.qml") << 8 << 15
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable }, }
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("whileCondition")
<< file << 128 << 16
<< ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable }, }
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("whileConsequence")
<< file << 128 << 22
<< ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable },
{ letStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ propertyCompletion };
QTest::newRow("doWhileCondition")
<< file << 131 << 30
<< ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable }, }
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("doWhileConsequence")
<< file << 131 << 12
<< ExpectedCompletions{ { u"hello"_s, CompletionItemKind::Variable },
{ letStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ propertyCompletion };
QTest::newRow("forInStatementLet")
<< file << 134 << 13
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind::Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"helloJSStatements"_s, CompletionItemKind::Method } }
<< QStringList{
u"property"_s, u"readonly"_s, u"required"_s,
forStatementCompletion, ifStatementCompletion,
};
QTest::newRow("forOfStatementLet")
<< file << 135 << 13
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind::Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"helloJSStatements"_s, CompletionItemKind::Method } }
<< QStringList{
u"property"_s, u"readonly"_s, u"required"_s,
forStatementCompletion, ifStatementCompletion,
};
QTest::newRow("forInStatementTarget")
<< file << 134 << 25
<< ExpectedCompletions{
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ u"hello"_s, CompletionItemKind::Variable },
}
<< QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
forStatementCompletion, ifStatementCompletion, varStatementCompletion,
letStatementCompletion, constStatementCompletion, }
;
QTest::newRow("forOfStatementTarget")
<< file << 135 << 24
<< ExpectedCompletions{
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ u"hello"_s, CompletionItemKind::Variable },
}
<< QStringList{ u"property"_s, u"readonly"_s, u"required"_s,
forStatementCompletion, ifStatementCompletion, varStatementCompletion,
letStatementCompletion, constStatementCompletion, }
;
QTest::newRow("forInStatementConsequence")
<< file << 134 << 31
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind::Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ u"hello"_s, CompletionItemKind::Variable },
{ forStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ propertyCompletion };
QTest::newRow("forOfStatementConsequence")
<< file << 135 << 30
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ constStatementCompletion, CompletionItemKind::Snippet },
{ varStatementCompletion, CompletionItemKind::Snippet },
{ u"helloJSStatements"_s, CompletionItemKind::Method },
{ u"hello"_s, CompletionItemKind::Variable },
{ forStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ propertyCompletion };
QTest::newRow("binaryExpressionRHS") << file << 138 << 17
<< ExpectedCompletions{
{ u"log"_s, CompletionItemKind::Method },
{ u"error"_s, CompletionItemKind::Method },
}
<< QStringList{ propertyCompletion, u"helloVarVariable"_s,
u"test1"_s, u"width"_s,
u"height"_s, u"layer"_s,
u"left"_s, forStatementCompletion }
;
QTest::newRow("binaryExpressionLHS") << file << 138 << 12
<< ExpectedCompletions{
{ u"qualifiedScriptIdentifiers"_s, CompletionItemKind::Method },
{ u"width"_s, CompletionItemKind::Property },
{ u"layer"_s, CompletionItemKind::Property },
}
<< QStringList{ u"log"_s, u"error"_s, forStatementCompletion}
;
const QString missingRHSFile = testFile(u"completions/missingRHS.qml"_s);
QTest::newRow("binaryExpressionMissingRHS") << missingRHSFile << 12 << 25
<< ExpectedCompletions{
{ u"good"_s, CompletionItemKind::Property },
}
<< QStringList{ propertyCompletion, u"bad"_s }
;
QTest::newRow("binaryExpressionMissingRHSWithDefaultProperty") << missingRHSFile << 14 << 33
<< ExpectedCompletions{
{ u"good"_s, CompletionItemKind::Property },
}
<< QStringList{ propertyCompletion, u"bad"_s, u"helloSubItem"_s }
;
QTest::newRow("binaryExpressionMissingRHSWithSemicolon")
<< testFile(u"completions/missingRHS.parserfail.qml"_s)
<< 5 << 22
<< ExpectedCompletions{
{ u"good"_s, CompletionItemKind::Property },
}
<< QStringList{ propertyCompletion, u"bad"_s, u"helloSubItem"_s }
;
QTest::newRow("binaryExpressionMissingRHSWithStatement") <<
testFile(u"completions/missingRHS.parserfail.qml"_s)
<< 6 << 22
<< ExpectedCompletions{
{ u"good"_s, CompletionItemKind::Property },
}
<< QStringList{ propertyCompletion, u"bad"_s, u"helloSubItem"_s }
;
QTest::newRow("tryStatements")
<< testFile(u"completions/tryStatements.qml"_s) << 5 << 14
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ forStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{};
QTest::newRow("tryStatementsCatchParameter")
<< testFile(u"completions/tryStatements.qml"_s) << 5 << 23 << ExpectedCompletions{}
<< QStringList{ letStatementCompletion, forStatementCompletion };
QTest::newRow("tryStatementsCatchBlock")
<< testFile(u"completions/tryStatements.qml"_s) << 5 << 27
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ forStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{};
QTest::newRow("tryStatementsFinallyBlock")
<< testFile(u"completions/tryStatements.qml"_s) << 5 << 39
<< ExpectedCompletions{ { letStatementCompletion, CompletionItemKind::Snippet },
{ forStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{};
QTest::newRow("inSwitchExpression")
<< testFile(u"completions/switchStatements.qml"_s) << 10 << 16
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"myProperty"_s, CompletionItemKind::Property } }
<< QStringList{
letStatementCompletion,
propertyCompletion,
};
QTest::newRow("beforeCaseStatement")
<< testFile(u"completions/switchStatements.qml"_s) << 11 << 1
<< ExpectedCompletions{ { caseStatementCompletion, CompletionItemKind::Snippet },
{ caseStatement2Completion, CompletionItemKind::Snippet },
{ defaultStatementCompletion, CompletionItemKind::Snippet },
{ defaultStatement2Completion, CompletionItemKind::Snippet } }
<< QStringList{ letStatementCompletion, propertyCompletion, u"x"_s, u"myProperty"_s };
QTest::newRow("inCaseExpression")
<< testFile(u"completions/switchStatements.qml"_s) << 12 << 14
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ u"myProperty"_s, CompletionItemKind::Property } }
<< QStringList{ letStatementCompletion, propertyCompletion, caseStatementCompletion };
QTest::newRow("inCaseStatementList")
<< testFile(u"completions/switchStatements.qml"_s) << 13 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ caseStatementCompletion, CompletionItemKind::Snippet },
{ defaultStatementCompletion, CompletionItemKind::Snippet },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ u"break"_s, CompletionItemKind::Keyword },
{ u"return"_s, CompletionItemKind::Keyword },
{ u"myProperty"_s, CompletionItemKind::Property } }
<< QStringList{ propertyCompletion };
QTest::newRow("inDefaultStatementList")
<< testFile(u"completions/switchStatements.qml"_s) << 24 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ caseStatementCompletion, CompletionItemKind::Snippet },
{ defaultStatementCompletion, CompletionItemKind::Snippet },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ u"break"_s, CompletionItemKind::Keyword },
{ u"return"_s, CompletionItemKind::Keyword },
{ u"myProperty"_s, CompletionItemKind::Property } }
<< QStringList{ propertyCompletion };
QTest::newRow("inMoreCasesStatementList")
<< testFile(u"completions/switchStatements.qml"_s) << 26 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ caseStatementCompletion, CompletionItemKind::Snippet },
{ defaultStatementCompletion, CompletionItemKind::Snippet },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ u"break"_s, CompletionItemKind::Keyword },
{ u"return"_s, CompletionItemKind::Keyword },
{ u"myProperty"_s, CompletionItemKind::Property } }
<< QStringList{ propertyCompletion };
QTest::newRow("inCaseBeforeBlock")
<< testFile(u"completions/switchStatements.qml"_s) << 14 << 23
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ caseStatementCompletion, CompletionItemKind::Snippet },
{ defaultStatementCompletion, CompletionItemKind::Snippet },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ u"myProperty"_s, CompletionItemKind::Property } }
<< QStringList{
propertyCompletion,
};
QTest::newRow("inCaseBeforeBlock2")
<< testFile(u"completions/switchStatements.qml"_s) << 14 << 24
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ u"myProperty"_s, CompletionItemKind::Property } }
<< QStringList{
propertyCompletion,
};
QTest::newRow("inCaseNestedStatement")
<< testFile(u"completions/switchStatements.qml"_s) << 16 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ u"myProperty"_s, CompletionItemKind::Property } }
<< QStringList{ propertyCompletion, caseStatementCompletion,
defaultStatementCompletion };
QTest::newRow("inCaseAfterBlock")
<< testFile(u"completions/switchStatements.qml"_s) << 22 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ caseStatementCompletion, CompletionItemKind::Snippet },
{ defaultStatementCompletion, CompletionItemKind::Snippet },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ u"myProperty"_s, CompletionItemKind::Property } }
<< QStringList{
propertyCompletion,
};
QTest::newRow("inCaseBeforeDefault")
<< testFile(u"completions/switchStatements.qml"_s) << 23 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ caseStatementCompletion, CompletionItemKind::Snippet },
{ defaultStatementCompletion, CompletionItemKind::Snippet },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ u"myProperty"_s, CompletionItemKind::Property } }
<< QStringList{
propertyCompletion,
};
QTest::newRow("inCaseAfterDefault")
<< testFile(u"completions/switchStatements.qml"_s) << 25 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ caseStatementCompletion, CompletionItemKind::Snippet },
{ defaultStatementCompletion, CompletionItemKind::Snippet },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ u"myProperty"_s, CompletionItemKind::Property } }
<< QStringList{
propertyCompletion,
};
QTest::newRow("beforeAnyCase")
<< testFile(u"completions/switchStatements.qml"_s) << 20 << 1
<< ExpectedCompletions{ { caseStatementCompletion, CompletionItemKind::Snippet },
{ defaultStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ propertyCompletion, letStatementCompletion, u"myProperty"_s, u"x"_s,
u"f"_s };
QTest::newRow("beforeAnyDefault")
<< testFile(u"completions/switchStatements.qml"_s) << 32 << 1
<< ExpectedCompletions{ { caseStatementCompletion, CompletionItemKind::Snippet },
{ defaultStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ propertyCompletion, letStatementCompletion, u"myProperty"_s, u"x"_s,
u"f"_s };
QTest::newRow("inDefaultAfterDefault")
<< testFile(u"completions/switchStatements.qml"_s) << 33 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ caseStatementCompletion, CompletionItemKind::Snippet },
{ defaultStatementCompletion, CompletionItemKind::Snippet },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ u"myProperty"_s, CompletionItemKind::Property } }
<< QStringList{
propertyCompletion,
};
// variableDeclaration.qml tests for let/const/var statements + destructuring
QTest::newRow("letStatement") << testFile(u"completions/variableDeclaration.qml"_s) << 7 << 13
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion,
u"x"_s, u"data"_s };
QTest::newRow("letStatement2")
<< testFile(u"completions/variableDeclaration.qml"_s) << 7 << 26
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
QTest::newRow("letStatementBehindEqual")
<< testFile(u"completions/variableDeclaration.qml"_s) << 7 << 28
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("letStatementBehindEqual2")
<< testFile(u"completions/variableDeclaration.qml"_s) << 7 << 33
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("constStatement")
<< testFile(u"completions/variableDeclaration.qml"_s) << 8 << 19
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
QTest::newRow("constStatementBehindEqual")
<< testFile(u"completions/variableDeclaration.qml"_s) << 8 << 32
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("varStatement") << testFile(u"completions/variableDeclaration.qml"_s) << 9 << 17
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion,
u"x"_s, u"data"_s };
QTest::newRow("varStatementBehindEqual")
<< testFile(u"completions/variableDeclaration.qml"_s) << 9 << 28
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("objectDeconstruction")
<< testFile(u"completions/variableDeclaration.qml"_s) << 13 << 20
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
QTest::newRow("objectDeconstructionAloneBehindEqual")
<< testFile(u"completions/variableDeclaration.qml"_s) << 14 << 51
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("objectDeconstructionAloneBehindEqual2")
<< testFile(u"completions/variableDeclaration.qml"_s) << 14 << 58
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("objectDeconstruction2BehindEqual")
<< testFile(u"completions/variableDeclaration.qml"_s) << 15 << 83
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("objectDeconstruction2BehindEqual2")
<< testFile(u"completions/variableDeclaration.qml"_s) << 15 << 90
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("objectDeconstruction3BehindEqual")
<< testFile(u"completions/variableDeclaration.qml"_s) << 15 << 140
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("objectDeconstructionBehindComma")
<< testFile(u"completions/variableDeclaration.qml"_s) << 15 << 143
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
QTest::newRow("objectDeconstructionBetweenObjects")
<< testFile(u"completions/variableDeclaration.qml"_s) << 15 << 50
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
QTest::newRow("objectDeconstructionBetweenDeconstructions")
<< testFile(u"completions/variableDeclaration.qml"_s) << 15 << 97
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
QTest::newRow("arrayDeconstructionAlone")
<< testFile(u"completions/variableDeclaration.qml"_s) << 19 << 24
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
QTest::newRow("arrayDeconstructionAloneBehindEqual")
<< testFile(u"completions/variableDeclaration.qml"_s) << 19 << 33
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("arrayDeconstruction2")
<< testFile(u"completions/variableDeclaration.qml"_s) << 21 << 71
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
QTest::newRow("arrayDeconstruction2BehindEqual")
<< testFile(u"completions/variableDeclaration.qml"_s) << 21 << 83
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("arrayDeconstruction3")
<< testFile(u"completions/variableDeclaration.qml"_s) << 21 << 125
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
QTest::newRow("arrayDeconstruction3BehindEqual")
<< testFile(u"completions/variableDeclaration.qml"_s) << 21 << 139
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("arrayDeconstructionIn_Wildcard")
<< testFile(u"completions/variableDeclaration.qml"_s) << 25 << 64
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
QTest::newRow("arrayDeconstructionBehind+")
<< testFile(u"completions/variableDeclaration.qml"_s) << 25 << 132
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("objectDeconstructionForNeedle")
<< testFile(u"completions/variableDeclaration.qml"_s) << 29 << 111
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("arrayInObjectDeconstructionInObjectInitializer")
<< testFile(u"completions/variableDeclaration.qml"_s) << 33 << 44
<< ExpectedCompletions{ {u"x"_s, CompletionItemKind::Variable},
{u"data"_s, CompletionItemKind::Method},
}
<< QStringList{ propertyCompletion, letStatementCompletion }
;
QTest::newRow("arrayInObjectDeconstructionInObjectPropertyName")
<< testFile(u"completions/variableDeclaration.qml"_s) << 33 << 26
<< ExpectedCompletions{}
<< QStringList{ propertyCompletion, letStatementCompletion, u"x"_s, u"data"_s };
QTest::newRow("throwStatement")
<< testFile(u"completions/throwStatement.qml"_s) << 8 << 15
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method } }
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("throwStatement2")
<< testFile(u"completions/throwStatement.qml"_s) << 9 << 20
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method } }
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("labelledStatement")
<< testFile(u"completions/labelledStatement.qml"_s) << 5 << 16
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ forStatementCompletion, CompletionItemKind::Snippet },
}
<< QStringList{ propertyCompletion, };
QTest::newRow("nestedLabel")
<< testFile(u"completions/labelledStatement.qml"_s) << 7 << 22
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ forStatementCompletion, CompletionItemKind::Snippet },
}
<< QStringList{ propertyCompletion, };
QTest::newRow("nestedLabel2")
<< testFile(u"completions/labelledStatement.qml"_s) << 8 << 26
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ forStatementCompletion, CompletionItemKind::Snippet },
}
<< QStringList{ propertyCompletion, };
QTest::newRow("multiLabel")
<< testFile(u"completions/labelledStatement.qml"_s) << 15 << 21
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ forStatementCompletion, CompletionItemKind::Snippet },
}
<< QStringList{ propertyCompletion, };
QTest::newRow("multiLabel2")
<< testFile(u"completions/labelledStatement.qml"_s) << 16 << 21
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"f"_s, CompletionItemKind::Method },
{ letStatementCompletion, CompletionItemKind::Snippet },
{ forStatementCompletion, CompletionItemKind::Snippet },
}
<< QStringList{ propertyCompletion, };
QTest::newRow("continueNested")
<< testFile(u"completions/continueAndBreakStatement.qml"_s) << 12 << 26
<< ExpectedCompletions{ { u"nestedLabel1"_s, CompletionItemKind::Value },
{ u"nestedLabel2"_s, CompletionItemKind::Value }, }
<< QStringList{ propertyCompletion, u"x"_s, u"f"_s, u"multiLabel1"_s };
QTest::newRow("breakNested")
<< testFile(u"completions/continueAndBreakStatement.qml"_s) << 13 << 23
<< ExpectedCompletions{ { u"nestedLabel1"_s, CompletionItemKind::Value },
{ u"nestedLabel2"_s, CompletionItemKind::Value }, }
<< QStringList{ propertyCompletion, u"x"_s, u"f"_s, u"multiLabel1"_s };
QTest::newRow("continueMulti")
<< testFile(u"completions/continueAndBreakStatement.qml"_s) << 20 << 22
<< ExpectedCompletions{ { u"multiLabel1"_s, CompletionItemKind::Value },
{ u"multiLabel2"_s, CompletionItemKind::Value }, }
<< QStringList{ propertyCompletion, u"x"_s, u"f"_s, u"nestedLabel1"_s };
QTest::newRow("breakMulti")
<< testFile(u"completions/continueAndBreakStatement.qml"_s) << 21 << 19
<< ExpectedCompletions{ { u"multiLabel1"_s, CompletionItemKind::Value },
{ u"multiLabel2"_s, CompletionItemKind::Value }, }
<< QStringList{ propertyCompletion, u"x"_s, u"f"_s, u"nestedLabel1"_s };
QTest::newRow("continueNoLabel") << testFile(u"completions/continueAndBreakStatement.qml"_s)
<< 25 << 22 << ExpectedCompletions{}
<< QStringList{ propertyCompletion, u"x"_s, u"f"_s,
u"nestedLabel1"_s, u"multiLabel1"_s };
QTest::newRow("breakNoLabel") << testFile(u"completions/continueAndBreakStatement.qml"_s) << 26
<< 19 << ExpectedCompletions{}
<< QStringList{ propertyCompletion, u"x"_s, u"f"_s,
u"nestedLabel1"_s, u"multiLabel1"_s };
QTest::newRow("insideMethodBody")
<< testFile(u"completions/functionBody.qml"_s) << 5 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ forStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ propertyCompletion };
QTest::newRow("insideMethodBody2")
<< testFile(u"completions/functionBody.qml"_s) << 11 << 11
<< ExpectedCompletions{ { u"helloProperty"_s, CompletionItemKind::Property }, }
<< QStringList{ u"badProperty"_s, forStatementCompletion };
QTest::newRow("insideArrow")
<< testFile(u"completions/functionBody.qml"_s) << 12 << 22
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable }, }
<< QStringList{ propertyCompletion, forStatementCompletion };
QTest::newRow("insideArrow2")
<< testFile(u"completions/functionBody.qml"_s) << 12 << 26
<< ExpectedCompletions{ { u"helloProperty"_s, CompletionItemKind::Property }, }
<< QStringList{ u"badProperty"_s, forStatementCompletion };
QTest::newRow("insideMethodBodyStart")
<< testFile(u"completions/functionBody.qml"_s) << 11 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ forStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ u"helloProperty"_s };
QTest::newRow("insideMethodBodyEnd")
<< testFile(u"completions/functionBody.qml"_s) << 12 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ forStatementCompletion, CompletionItemKind::Snippet } }
<< QStringList{ u"helloProperty"_s };
QTest::newRow("insideArrowBody")
<< testFile(u"completions/functionBody.qml"_s) << 14 << 24
<< ExpectedCompletions{ { u"xxx"_s, CompletionItemKind::Variable } }
<< QStringList{ u"helloProperty"_s };
QTest::newRow("noBreakInMethodBody")
<< testFile(u"completions/suggestContinueAndBreak.qml"_s) << 8 << 8
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable } }
<< QStringList{ u"break"_s, u"continue"_s };
QTest::newRow("breakAndContinueInForLoop")
<< testFile(u"completions/suggestContinueAndBreak.qml"_s) << 11 << 12
<< ExpectedCompletions{ { u"break"_s, CompletionItemKind::Keyword },
{ u"continue"_s, CompletionItemKind::Keyword },
}
<< QStringList{};
QTest::newRow("noBreakInSwitch")
<< testFile(u"completions/suggestContinueAndBreak.qml"_s) << 15 << 12
<< ExpectedCompletions{ { caseStatementCompletion, CompletionItemKind::Snippet }, }
<< QStringList{ u"continue"_s, u"break"_s };
QTest::newRow("breakInSwitchCase")
<< testFile(u"completions/suggestContinueAndBreak.qml"_s) << 17 << 12
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"break"_s, CompletionItemKind::Keyword },
}
<< QStringList{ u"continue"_s };
QTest::newRow("breakInSwitchDefault")
<< testFile(u"completions/suggestContinueAndBreak.qml"_s) << 19 << 12
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"break"_s, CompletionItemKind::Keyword },
}
<< QStringList{ u"continue"_s };
QTest::newRow("breakInSwitchSecondCase")
<< testFile(u"completions/suggestContinueAndBreak.qml"_s) << 21 << 12
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"break"_s, CompletionItemKind::Keyword },
}
<< QStringList{ u"continue"_s };
QTest::newRow("breakInLabel")
<< testFile(u"completions/suggestContinueAndBreak.qml"_s) << 25 << 12
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"break"_s, CompletionItemKind::Keyword },
}
<< QStringList{ u"continue"_s };
QTest::newRow("forLoopInsideOfLabel")
<< testFile(u"completions/suggestContinueAndBreak.qml"_s) << 33 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"break"_s, CompletionItemKind::Keyword },
{ u"continue"_s, CompletionItemKind::Keyword },
}
<< QStringList{ };
QTest::newRow("switchInsideForLoopInsideOfLabel")
<< testFile(u"completions/suggestContinueAndBreak.qml"_s) << 36 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"break"_s, CompletionItemKind::Keyword },
{ u"continue"_s, CompletionItemKind::Keyword },
}
<< QStringList{ };
QTest::newRow("switchInsideOfLabel")
<< testFile(u"completions/suggestContinueAndBreak.qml"_s) << 45 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"break"_s, CompletionItemKind::Keyword },
}
<< QStringList{ u"continue"_s };
QTest::newRow("forLoopInSwitchInsideOfLabel")
<< testFile(u"completions/suggestContinueAndBreak.qml"_s) << 47 << 1
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
{ u"break"_s, CompletionItemKind::Keyword },
{ u"continue"_s, CompletionItemKind::Keyword },
}
<< QStringList{ };
QTest::newRow("commaExpression")
<< testFile(u"completions/commaExpression.qml"_s) << 5 << 18
<< ExpectedCompletions{ { u"a"_s, CompletionItemKind::Variable },
{ u"b"_s, CompletionItemKind::Variable },
{ u"c"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion };
QTest::newRow("conditionalExpressionConsequence")
<< testFile(u"completions/conditionalExpression.qml"_s) << 5 << 17
<< ExpectedCompletions{ { u"a"_s, CompletionItemKind::Variable },
{ u"b"_s, CompletionItemKind::Variable },
{ u"c"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("conditionalExpressionAlternative")
<< testFile(u"completions/conditionalExpression.qml"_s) << 5 << 30
<< ExpectedCompletions{ { u"a"_s, CompletionItemKind::Variable },
{ u"b"_s, CompletionItemKind::Variable },
{ u"c"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("unaryMinus")
<< testFile(u"completions/unaryExpression.qml"_s) << 5 << 10
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("unaryPlus")
<< testFile(u"completions/unaryExpression.qml"_s) << 6 << 10
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("unaryTilde")
<< testFile(u"completions/unaryExpression.qml"_s) << 7 << 10
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("unaryNot")
<< testFile(u"completions/unaryExpression.qml"_s) << 8 << 10
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("typeof")
<< testFile(u"completions/unaryExpression.qml"_s) << 9 << 16
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("delete")
<< testFile(u"completions/unaryExpression.qml"_s) << 10 << 16
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("void")
<< testFile(u"completions/unaryExpression.qml"_s) << 11 << 14
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("postDecrement")
<< testFile(u"completions/unaryExpression.qml"_s) << 12 << 9
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("postIncrement")
<< testFile(u"completions/unaryExpression.qml"_s) << 13 << 9
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("preDecrement")
<< testFile(u"completions/unaryExpression.qml"_s) << 14 << 11
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("preIncrement")
<< testFile(u"completions/unaryExpression.qml"_s) << 15 << 11
<< ExpectedCompletions{ { u"x"_s, CompletionItemKind::Variable },
}
<< QStringList{ propertyCompletion, letStatementCompletion };
QTest::newRow("attachedPropertyAfterDot")
<< testFile("completions/attachedAndGroupedProperty.qml") << 8 << 15
<< ExpectedCompletions({
{ u"onCompleted"_s, CompletionItemKind::Method },
})
<< QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
u"bad"_s, u"x"_s };
QTest::newRow("attachedPropertyAfterDot2")
<< testFile("completions/attachedAndGroupedProperty.qml") << 13 << 34
<< ExpectedCompletions({
{ u"completed"_s, CompletionItemKind::Method },
})
<< QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
u"bad"_s, u"progress"_s, u"status"_s, u"createObject"_s, u"x"_s };
QTest::newRow("groupedPropertyAfterDot")
<< testFile("completions/attachedAndGroupedProperty.qml") << 10 << 14
<< ExpectedCompletions({
{ u"family"_s, CompletionItemKind::Property },
})
<< QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
u"bad"_s, u"onCompleted"_s };
QTest::newRow("attachedPropertyAfterDotMissingRHS")
<< testFile("completions/attachedPropertyMissingRHS.qml") << 7 << 16
<< ExpectedCompletions({
{ u"onCompleted"_s, CompletionItemKind::Method },
})
<< QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
u"bad"_s };
QTest::newRow("QTBUG-127586")
<< testFile("completions/attachedPropertyQTBUG127586.qml") << 4 << 31
<< ExpectedCompletions({
{ u"completed"_s, CompletionItemKind::Method },
{ u"destruction"_s, CompletionItemKind::Method },
{ u"Asynchronous"_s, CompletionItemKind::EnumMember },
{ u"loadFromModule"_s, CompletionItemKind::Method},
})
<< QStringList{ u"ItemClipsChildrenToState"_s, u"Top"_s, u"parent"_s };
QTest::newRow("groupedPropertyAfterDotMissingRHS")
<< testFile("completions/groupedPropertyMissingRHS.qml") << 7 << 11
<< ExpectedCompletions({
{ u"family"_s, CompletionItemKind::Property },
})
<< QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
u"bad"_s, u"onCompleted"_s };
QTest::newRow("dotFollowedByDefaultBinding")
<< testFile("completions/afterDots.qml") << 11 << 31
<< ExpectedCompletions({
{ u"good"_s, CompletionItemKind::Property },
})
<< QStringList{ u"bad"_s, u"QtQuick"_s, u"vector4d"_s,
attachedTypeName, u"Rectangle"_s, u"onCompleted"_s };
QTest::newRow("dotFollowedByBinding")
<< testFile("completions/afterDots.qml") << 13 << 32
<< ExpectedCompletions({
{ u"good"_s, CompletionItemKind::Property },
})
<< QStringList{ u"bad"_s, u"QtQuick"_s, u"vector4d"_s,
attachedTypeName, u"Rectangle"_s, u"onCompleted"_s };
QTest::newRow("dotFollowedByForStatement")
<< testFile("completions/afterDots.qml") << 16 << 16
<< ExpectedCompletions({
{ u"good"_s, CompletionItemKind::Property },
})
<< QStringList{
u"bad"_s, u"QtQuick"_s, u"vector4d"_s, attachedTypeName,
u"Rectangle"_s, u"onCompleted"_s, forStatementCompletion
};
QTest::newRow("qualifiedTypeCompletionWithoutQualifier")
<< testFile("completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml")
<< 9 << 5
<< ExpectedCompletions({
{ u"T.Button"_s, CompletionItemKind::Constructor },
{ u"Button"_s, CompletionItemKind::Constructor },
{ u"Rectangle"_s, CompletionItemKind::Constructor },
})
<< QStringList{ u"QtQuick"_s, u"vector4d"_s, u"bad"_s, u"onCompleted"_s };
QTest::newRow("qualifiedTypeCompletionWithoutQualifier2")
<< testFile("completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml")
<< 10 << 19
<< ExpectedCompletions({
{ u"T.Button"_s, CompletionItemKind::Class },
{ u"Button"_s, CompletionItemKind::Class },
{ u"Rectangle"_s, CompletionItemKind::Class },
})
<< QStringList{ u"QtQuick"_s, u"bad"_s, u"onCompleted"_s };
QTest::newRow("qualifiedTypeCompletionWithQualifier")
<< testFile("completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml")
<< 9 << 7
<< ExpectedCompletions({
{ u"Button"_s, CompletionItemKind::Constructor },
})
<< QStringList{ u"QtQuick"_s, u"vector4d"_s, attachedTypeName, u"Rectangle"_s,
u"bad"_s, u"onCompleted"_s, u"T.Button"_s };
QTest::newRow("qualifiedTypeCompletionWithQualifier2")
<< testFile("completions/quickcontrols_and_quicktemplates/qualifiedTypesCompletion.qml")
<< 10 << 21
<< ExpectedCompletions({
{ u"Button"_s, CompletionItemKind::Class },
})
<< QStringList{ u"QtQuick"_s, attachedTypeName, u"Rectangle"_s,
u"bad"_s, u"onCompleted"_s, u"T.Button"_s };
QTest::newRow("parenthesizedExpression")
<< testFile("completions/parenthesizedExpression.qml") << 8 << 10
<< ExpectedCompletions({
{ u"x"_s, CompletionItemKind::Variable },
})
<< QStringList{ u"QtQuick"_s, u"Rectangle"_s, forStatementCompletion };
QTest::newRow("behindParenthesizedExpression")
<< testFile("completions/parenthesizedExpression.qml") << 8 << 16
<< ExpectedCompletions({})
<< QStringList{ u"QtQuick"_s, attachedTypeName, u"Rectangle"_s, forStatementCompletion,
u"x"_s };
QTest::newRow("assumeBoundComponentsIdFromParent")
<< testFile("completions/boundComponents.qml") << 14 << 33
<< ExpectedCompletions{ { u"rootId"_s, CompletionItemKind::Value } }
<< QStringList{ u"inRoot"_s };
QTest::newRow("assumeBoundComponentsPropertyFromParent")
<< testFile("completions/boundComponents.qml") << 14 << 40
<< ExpectedCompletions{ { u"inRoot"_s, CompletionItemKind::Property } }
<< QStringList{ u"root"_s };
QTest::newRow("insideTemplateLiteralExpression")
<< testFile("completions/templateLiterals.qml") << 9 << 27
<< ExpectedCompletions{ { u"helloProperty"_s, CompletionItemKind::Property } }
<< QStringList{ forStatementCompletion };
QTest::newRow("insideTemplateLiteralString")
<< testFile("completions/templateLiterals.qml") << 9 << 40
<< ExpectedCompletions{ }
<< QStringList{ forStatementCompletion };
QTest::newRow("insideTaggedLiteralExpression")
<< testFile("completions/templateLiterals.qml") << 10 << 27
<< ExpectedCompletions{ { u"helloProperty"_s, CompletionItemKind::Property } }
<< QStringList{ forStatementCompletion };
QTest::newRow("insideTaggedLiteralString")
<< testFile("completions/templateLiterals.qml") << 10 << 40
<< ExpectedCompletions{ }
<< QStringList{ forStatementCompletion };
QTest::newRow("insideRegularExpression")
<< testFile("completions/regularExpressions.qml") << 5 << 20
<< ExpectedCompletions{ }
<< QStringList{};
QTest::newRow("insideRegularExpressionFlag")
<< testFile("completions/regularExpressions.qml") << 5 << 25
<< ExpectedCompletions{ }
<< QStringList{};
QTest::newRow("insideNewMemberExpression")
<< testFile("completions/newExpressions.qml") << 5 << 13
<< ExpectedCompletions{ { u"f"_s, CompletionItemKind::Method } }
<< QStringList{ forStatementCompletion };
QTest::newRow("insideNewMemberExpressionArgument")
<< testFile("completions/newExpressions.qml") << 5 << 15
<< ExpectedCompletions{ { u"f"_s, CompletionItemKind::Method } }
<< QStringList{ forStatementCompletion };
QTest::newRow("insideNewExpression")
<< testFile("completions/newExpressions.qml") << 6 << 13
<< ExpectedCompletions{ { u"f"_s, CompletionItemKind::Method } }
<< QStringList{ forStatementCompletion };
QTest::newRow("insideSuperLiteral")
<< testFile("completions/superLiteral.qml") << 5 << 11 << ExpectedCompletions{}
<< QStringList{ forStatementCompletion, u"f"_s };
QTest::newRow("insideSuperParameter")
<< testFile("completions/superLiteral.qml") << 5 << 15
<< ExpectedCompletions{ { u"f"_s, CompletionItemKind::Method } }
<< QStringList{ forStatementCompletion };
QTest::newRow("insideSuperField")
<< testFile("completions/superLiteral.qml") << 6 << 15 << ExpectedCompletions{}
<< QStringList{ forStatementCompletion, u"f"_s };
QTest::newRow("insideSuperParameter2")
<< testFile("completions/superLiteral.qml") << 6 << 17
<< ExpectedCompletions{ { u"f"_s, CompletionItemKind::Method } }
<< QStringList{ forStatementCompletion };
QTest::newRow("insideThisExpression")
<< testFile("completions/thisExpression.qml") << 5 << 10
<< ExpectedCompletions{ }
<< QStringList{ forStatementCompletion, u"f"_s };
QTest::newRow("insideThisField")
<< testFile("completions/thisExpression.qml") << 5 << 14
<< ExpectedCompletions{ }
<< QStringList{ forStatementCompletion, u"f"_s };
QTest::newRow("unexistingSignalParameter")
<< testFile("completions/parameterTypeFromBinding.qml") << 8 << 46
<< ExpectedCompletions{} << QStringList{};
QTest::newRow("signalParameter")
<< testFile("completions/parameterTypeFromBinding.qml") << 11 << 59
<< ExpectedCompletions{ { u"helloData"_s, CompletionItemKind::Property } }
<< QStringList{};
QTest::newRow("signalParameter2")
<< testFile("completions/parameterTypeFromBinding.qml") << 11 << 38
<< ExpectedCompletions{ { u"console"_s, CompletionItemKind::Property } }
<< QStringList{};
QTest::newRow("fakePropertyChangedSignal")
<< testFile("completions/parameterTypeFromBinding.qml") << 14 << 64
<< ExpectedCompletions{ { u"helloData"_s, CompletionItemKind::Property } }
<< QStringList{};
QTest::newRow("enumMemberFromRoot")
<< testFile("completions/Enumerations.qml") << 12 << 52
<< ExpectedCompletions{ { u"World"_s, CompletionItemKind::EnumMember } }
<< QStringList{};
QTest::newRow("enumMemberInRoot")
<< testFile("completions/Enumerations.qml") << 18 << 40
<< ExpectedCompletions{ { u"World"_s, CompletionItemKind::EnumMember } }
<< QStringList{};
QTest::newRow("enumFromRoot")
<< testFile("completions/Enumerations.qml") << 12 << 46
<< ExpectedCompletions{ { u"World"_s, CompletionItemKind::EnumMember },
{ u"Hello"_s, CompletionItemKind::Enum } }
<< QStringList{};
QTest::newRow("enumInRoot")
<< testFile("completions/Enumerations.qml") << 18 << 34
<< ExpectedCompletions{ { u"World"_s, CompletionItemKind::EnumMember },
{ u"Hello"_s, CompletionItemKind::Enum } }
<< QStringList{};
QTest::newRow("viaResourceFile")
<< testFile("AdminDialogFromSource.qml") << 4 << 33
<< ExpectedCompletions{ { u"LabelTitle"_s, CompletionItemKind::Constructor } }
<< QStringList{};
QTest::newRow("Qt.Point") << testFile("completions/QtPoint.qml") << 5 << 34
<< ExpectedCompletions{ { u"y"_s, CompletionItemKind::Property } }
<< QStringList{ u"f"_s, forStatementCompletion };
QTest::newRow("insideComment1")
<< testFile("completions/Comments.qml") << 4 << 9 << ExpectedCompletions{}
<< QStringList{ u"f"_s, forStatementCompletion };
QTest::newRow("insideComment2")
<< testFile("completions/Comments.qml") << 6 << 9 << ExpectedCompletions{}
<< QStringList{ u"x"_s, u"Item"_s, forStatementCompletion };
QTest::newRow("attachedSignalHandler")
<< testFile("completions/attachedSignalHandler.qml") << 5 << 9
<< ExpectedCompletions{ { "event", CompletionItemKind::Variable } } << QStringList{};
QTest::newRow("idMethodMixup")
<< testFile("completions/idMethodMixup.qml") << 10 << 15
<< ExpectedCompletions{ { "myProperty"_L1, CompletionItemKind::Property } }
<< QStringList{ };
QTest::newRow("opaqueType")
<< testFile("completions/opaqueUsage.qml") << 6 << 56
<< ExpectedCompletions { {"x"_L1, CompletionItemKind::Property },
{"y"_L1, CompletionItemKind::Property } }
<< QStringList {};
}
void tst_qmlls_utils::completions()
{
QFETCH(QString, filePath);
QFETCH(int, line);
QFETCH(int, character);
QFETCH(ExpectedCompletions, expected);
//TODO(QTBUG-142394)
QFETCH(QStringList, notExpected);
auto [env, file] = createEnvironmentAndLoadFile(filePath);
auto locations = QQmlLSUtils::itemsFromTextLocation(
file.field(QQmlJS::Dom::Fields::currentItem), line - 1, character - 1);
QEXPECT_FAIL("binaryExpressionMissingRHSWithSemicolon",
"Current parser cannot recover from this error yet!", Abort);
QEXPECT_FAIL("binaryExpressionMissingRHSWithStatement",
"Current parser cannot recover from this error yet!", Abort);
QCOMPARE(locations.size(), 1);
QString code;
{
QFile file(filePath);
QVERIFY(file.open(QIODeviceBase::ReadOnly));
code = QString::fromUtf8(file.readAll());
}
qsizetype pos = QQmlLSUtils::textOffsetFrom(code, line - 1, character - 1);
CompletionContextStrings ctxt{ code, pos };
QQmlLSCompletion completionEngine(m_pluginLoader);
QList<CompletionItem> completions =
completionEngine.completions(locations.front().domItem, ctxt);
if (expected.isEmpty()) {
if constexpr (enable_debug_output) {
if (!completions.isEmpty()) {
QStringList unexpected;
for (const auto ¤t : completions) {
unexpected << current.label;
}
qDebug() << "Received unexpected completions:" << unexpected.join(u", ");
}
}
QEXPECT_FAIL("singleton", "completion not implemented yet!", Abort);
QVERIFY(completions.isEmpty());
return;
}
QSet<QString> labels;
QStringList sortedLabels;
QDuplicateTracker<QByteArray> modulesTracker;
QDuplicateTracker<QByteArray> keywordsTracker;
QDuplicateTracker<QByteArray> classesTracker;
QDuplicateTracker<QByteArray> fieldsTracker;
QDuplicateTracker<QByteArray> propertiesTracker;
QDuplicateTracker<QByteArray> snippetTracker;
// avoid QEXPECT_FAIL tests to XPASS when completion order changes
std::sort(completions.begin(), completions.end(),
[](const CompletionItem&a, const CompletionItem&b) {return a.label < b.label;});
for (const CompletionItem &c : completions) {
// explicitly forbid marker structs created by QQmlJSImporter
QVERIFY(!c.label.contains("$internal$."));
QVERIFY(!c.label.contains("$module$."));
QVERIFY(!c.label.contains("$anonymous$."));
if (c.kind == CompletionItemKind::Module) {
QVERIFY2(!modulesTracker.hasSeen(c.label), "Duplicate module: " + c.label);
} else if (c.kind == CompletionItemKind::Keyword) {
QVERIFY2(!keywordsTracker.hasSeen(c.label), "Duplicate keyword: " + c.label);
} else if (c.kind == CompletionItemKind::Class) {
QVERIFY2(!classesTracker.hasSeen(c.label), "Duplicate class: " + c.label);
} else if (c.kind == CompletionItemKind::Field) {
QVERIFY2(!fieldsTracker.hasSeen(c.label), "Duplicate field: " + c.label);
} else if (c.kind == CompletionItemKind::Snippet) {
QVERIFY2(!snippetTracker.hasSeen(c.label), "Duplicate field: " + c.label);
if (c.insertText->contains('\n') || c.insertText->contains('\r')) {
QCOMPARE(c.insertTextMode, InsertTextMode::AdjustIndentation);
}
} else if (c.kind == CompletionItemKind::Property) {
QVERIFY2(!propertiesTracker.hasSeen(c.label), "Duplicate property: " + c.label);
QCOMPARE(c.insertText, std::nullopt);
}
labels << c.label;
sortedLabels << c.label;
}
const QString labelsForPrinting = sortedLabels.join(u", "_s);
for (const ExpectedCompletion &exp : expected) {
QEXPECT_FAIL("letStatementAfterEqual", "Completion not implemented yet!", Abort);
QEXPECT_FAIL("importAfterDot", "Imports need to be reworked via QTBUG-126766", Abort);
QEXPECT_FAIL("importAfterDot2", "Imports need to be reworked via QTBUG-126766", Abort);
QEXPECT_FAIL("importAfterDotMissing", "Imports need to be reworked via QTBUG-126766",
Abort);
QEXPECT_FAIL("importModuleStart", "Imports need to be reworked via QTBUG-126766", Abort);
QEXPECT_FAIL("importVersionStart", "Imports need to be reworked via QTBUG-126766", Abort);
QVERIFY2(labels.contains(exp.label),
u"no %1 in %2"_s.arg(exp.label, labelsForPrinting).toUtf8());
if (labels.contains(exp.label)) {
bool foundEntry = false;
bool hasCorrectKind = false;
CompletionItemKind foundKind;
for (const CompletionItem &c : completions) {
if (c.label == exp.label) {
foundKind = *c.kind;
foundEntry = true;
if (foundKind == exp.kind) {
hasCorrectKind = true;
if (!exp.snippet.isEmpty()) {
QCOMPARE(QString::fromUtf8(c.insertText.value_or(QByteArray())),
exp.snippet);
}
break;
}
}
}
// Ignore QVERIFY for those completions not in the expected list.
if (!foundEntry)
continue;
QVERIFY2(hasCorrectKind,
qPrintable(QString::fromLatin1("Completion item '%1' has wrong kind '%2'")
.arg(exp.label)
.arg(QMetaEnum::fromType<CompletionItemKind>().valueToKey(
int(foundKind)))));
}
}
for (const QString &nexp : notExpected) {
QEXPECT_FAIL("ignoreNonRelatedTypesForPropertyDefinitionBinding",
"Filtering by Type not implemented yet, for example to avoid proposing "
"binding Items to Rectangle properties.",
Abort);
QVERIFY2(!labels.contains(nexp), u"found unexpected completion %1"_s.arg(nexp).toUtf8());
}
}
void tst_qmlls_utils::cmakeBuildCommand()
{
const QString path = u"helloWorldPath"_s;
const std::pair<QString, QStringList> expected{
u"cmake"_s, { u"--build"_s, path, u"-t"_s, u"all_qmltyperegistrations"_s }
};
QCOMPARE(QQmlLSUtils::cmakeBuildCommand(path), expected);
}
void tst_qmlls_utils::maxFilesToSearch()
{
qputenv("QMLLS_MAX_FILES_TO_SEARCH", "1111");
QTest::ignoreMessage(QtInfoMsg,
"Aborting search for \"qt\", \"qwer\" inside "
"\"" QT_QMLLS_BIG_FOLDER
"\" after reaching QMLLS_MAX_FILES_TO_SEARCH (currently set to 1111). Set "
"the environment variable \"QMLLS_MAX_FILES_TO_SEARCH\" to a higher value "
"to spend more time on searching.");
QQmlLSUtils::findFilePathsFromFileNames(QT_QMLLS_BIG_FOLDER ""_L1, { "qt"_L1, "qwer"_L1 }, {});
}
void tst_qmlls_utils::findFilePathsFromFileNames_data()
{
QTest::addColumn<QSet<QString>>("ignored");
QTest::addColumn<QStringList>("expected");
QTest::addRow("all") << QSet<QString>()
<< QStringList{
testFile("findFilePathsFromFileNames/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/b/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/b/c/HelloWorld.txt"_L1),
};
QTest::addRow("ignore3")
<< QSet<QString> {
testFile("findFilePathsFromFileNames/a/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/b/HelloWorld.txt"_L1),
}
<< QStringList{
testFile("findFilePathsFromFileNames/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/b/c/HelloWorld.txt"_L1),
};
}
void tst_qmlls_utils::findFilePathsFromFileNames()
{
QFETCH(QSet<QString>, ignored);
QFETCH(QStringList, expected);
QStringList filePaths = QQmlLSUtils::findFilePathsFromFileNames(
testFile("findFilePathsFromFileNames"_L1), { "HelloWorld.txt"_L1 }, ignored);
std::sort(filePaths.begin(), filePaths.end());
std::sort(expected.begin(), expected.end());
QCOMPARE(filePaths, expected);
}
void tst_qmlls_utils::findFilePathFromFileName_data()
{
QTest::addColumn<QSet<QString>>("ignored");
QTest::addColumn<QString>("expected");
QTest::addRow("withoutIgnore")
<< QSet<QString>() << testFile("findFilePathsFromFileNames/HelloWorld.txt"_L1);
QTest::addRow("withIgnore") << QSet<QString>{
testFile("findFilePathsFromFileNames/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/b/HelloWorld.txt"_L1),
} << testFile("findFilePathsFromFileNames/a/b/c/HelloWorld.txt"_L1);
}
void tst_qmlls_utils::findFilePathFromFileName()
{
QFETCH(QSet<QString>, ignored);
QFETCH(QString, expected);
QString filePaths = QQmlLSUtils::findFilePathFromFileName(
{ testFile("findFilePathsFromFileNames"_L1) }, { "HelloWorld.txt"_L1 }, ignored);
QCOMPARE(filePaths, expected);
}
QTEST_MAIN(tst_qmlls_utils)