diff options
| author | Sami Shalayel <sami.shalayel@qt.io> | 2025-03-14 12:42:41 +0100 |
|---|---|---|
| committer | Sami Shalayel <sami.shalayel@qt.io> | 2025-04-09 09:41:00 +0200 |
| commit | e904fc15d96b4aa041b5691b18d8e7e765d06626 (patch) | |
| tree | 0dce82ab93a1fad5b8d271969a9d9badb4bbd830 | |
| parent | 163819b19494a3ae830c9f705f5b5ae0c208f78f (diff) | |
qmllint: warn about variable redeclaration
Add a warning about variable redeclaration for 'let' and 'const'
variables: they are not allowed in JS.
Fixes: QTBUG-127107
Change-Id: I44ba50e436b0b396a089bc8aedcef31d5455e48a
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
(cherry picked from commit e24b705dfb9b67ad344694582828f9346ca1438d)
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
| -rw-r--r-- | src/qmlcompiler/qqmljsimportvisitor.cpp | 24 | ||||
| -rw-r--r-- | tests/auto/qml/qmllint/tst_qmllint.cpp | 36 |
2 files changed, 47 insertions, 13 deletions
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp index 5a2fd8e38c..42e840d4f4 100644 --- a/src/qmlcompiler/qqmljsimportvisitor.cpp +++ b/src/qmlcompiler/qqmljsimportvisitor.cpp @@ -2752,13 +2752,23 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::VariableDeclarationList *vdl) if (Type *type = annotation->type) typeName = type->toString(); - m_currentScope->insertJSIdentifier( - vdl->declaration->bindingIdentifier.toString(), - { (vdl->declaration->scope == QQmlJS::AST::VariableScope::Var) - ? QQmlJSScope::JavaScriptIdentifier::FunctionScoped - : QQmlJSScope::JavaScriptIdentifier::LexicalScoped, - vdl->declaration->firstSourceLocation(), typeName, - vdl->declaration->scope == QQmlJS::AST::VariableScope::Const }); + using Kind = QQmlJSScope::JavaScriptIdentifier::Kind; + const Kind kind = (vdl->declaration->scope == QQmlJS::AST::VariableScope::Var) + ? Kind::FunctionScoped + : Kind::LexicalScoped; + const QString name = vdl->declaration->bindingIdentifier.toString(); + const QQmlJS::SourceLocation location = vdl->declaration->firstSourceLocation(); + if (kind == Kind::LexicalScoped) { + if (auto previousDeclaration = m_currentScope->ownJSIdentifier(name)) { + m_logger->log("Identifier '%1' has already been declared"_L1.arg(name), qmlSyntax, + location); + m_logger->log("Note: previous declaration of '%1' here"_L1.arg(name), qmlSyntax, + previousDeclaration->location); + } + } + + const bool isConst = vdl->declaration->scope == QQmlJS::AST::VariableScope::Const; + m_currentScope->insertJSIdentifier(name, { kind, location, typeName, isConst }); vdl = vdl->next; } return true; diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp index c2cd938df4..67e91fa3b5 100644 --- a/tests/auto/qml/qmllint/tst_qmllint.cpp +++ b/tests/auto/qml/qmllint/tst_qmllint.cpp @@ -1298,7 +1298,27 @@ void TestQmllint::dirtyJsSnippet_data() QTest::addColumn<Result>("result"); QTest::newRow("doubleLet") << u"let x = 4; let x = 4;"_s - << Result{ { { "Identifier 'x' has already been declared"_L1 } } }; + << Result{ + { { "Identifier 'x' has already been declared"_L1, 1, 16 }, + { "Note: previous declaration of 'x' here"_L1, 1, 5 } } + }; + QTest::newRow("doubleConst") << u"const x = 4; const x = 4;"_s + << Result{ + { { "Identifier 'x' has already been declared"_L1, 1, 20 }, + { "Note: previous declaration of 'x' here"_L1, 1, 7 } } + }; +} + +static void addLocationOffsetTo(TestQmllint::Result *result, qsizetype lineOffset, + qsizetype columnOffset = 0) +{ + for (auto *messages : + { &result->expectedMessages, &result->badMessages, &result->expectedReplacements }) { + for (auto &message : *messages) { + message.line += lineOffset; + message.column += columnOffset; + } + } } void TestQmllint::dirtyJsSnippet() @@ -1306,17 +1326,19 @@ void TestQmllint::dirtyJsSnippet() QFETCH(QString, code); QFETCH(Result, result); - const QString qmlCode = "import QtQuick\nItem { function f() {%1}}"_L1.arg(code); + static constexpr QLatin1StringView templateString = + "import QtQuick\nItem { function f() {\n%1}}"_L1; + + // templateString adds 2 newlines before snippet + addLocationOffsetTo(&result, 2); + const QString qmlCode = templateString.arg(code); QJsonArray warnings; - QEXPECT_FAIL("doubleLet", "Not implemented yet", Abort); callQmllint(QString(), result.flags.testFlag(Result::ExitsNormally), &warnings, {}, {}, {}, UseDefaultImports, nullptr, result.flags.testFlag(Result::Flag::AutoFixable), LintFile, &qmlCode); - checkResult( - warnings, result, [] { QEXPECT_FAIL("doubleLet", "Not implemented yet", Abort); }, - [] {}, [] {}); + checkResult(warnings, result, [] { }, [] { }, [] { }); } void TestQmllint::cleanJsSnippet_data() @@ -1324,6 +1346,8 @@ void TestQmllint::cleanJsSnippet_data() QTest::addColumn<QString>("code"); QTest::newRow("testSnippet") << u"let x = 5"_s; + QTest::newRow("doubleVar") << u"var x = 5; var y = 5"_s; + QTest::newRow("doubleInDifferentScopes") << u"const a = 42; for (let a = 1; a < 10; ++a) {}"_s; } void TestQmllint::cleanJsSnippet() |
