aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2025-03-14 12:42:41 +0100
committerSami Shalayel <sami.shalayel@qt.io>2025-04-09 09:41:00 +0200
commite904fc15d96b4aa041b5691b18d8e7e765d06626 (patch)
tree0dce82ab93a1fad5b8d271969a9d9badb4bbd830
parent163819b19494a3ae830c9f705f5b5ae0c208f78f (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.cpp24
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp36
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()