diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2019-01-08 16:27:56 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2019-07-09 16:52:01 +0200 |
commit | a06736cab722c2a4283cdcbda8d003d8e91d7f54 (patch) | |
tree | f083fe5bca85a06254e63729802d6c081c88cf87 /tests | |
parent | d9f115327cd36cef370172bc87706cca6e57f912 (diff) |
Add basic type annotation support for QML functions to the grammar
Inspired by TypeScript syntax, allow optional type annotations in the
style of ": <name of type>" in for the parameters of functions and their
return type.
These annotations are not used at the moment, so by default we produce
an error message when encountering them in the AST.
In addition their usage is limited to functions declared in the QML
scope. All other uses attempt to produce readable syntax errors. So for
example this is okay:
Item {
function blah(param: string) string { ... }
}
And this is not okay:
// some file.js
function blah(param: string) : string { ... }
Change-Id: I12d20b4e2ce92e01108132975a06cfd13ba4a254
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tests')
19 files changed, 185 insertions, 2 deletions
diff --git a/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.errors.txt b/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.errors.txt new file mode 100644 index 0000000000..b316acae30 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.errors.txt @@ -0,0 +1 @@ +5:14:Type annotations are not permitted in variable declarations diff --git a/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml b/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml new file mode 100644 index 0000000000..655fe4c226 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 + +QtObject { + function notYet() { + var x: string = "ko" + return x + } +} diff --git a/tests/auto/qml/qqmllanguage/data/typeAnnotations.errors.txt b/tests/auto/qml/qqmllanguage/data/typeAnnotations.errors.txt new file mode 100644 index 0000000000..3db89a47d5 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/typeAnnotations.errors.txt @@ -0,0 +1 @@ +4:5:Type annotations are not supported (yet). diff --git a/tests/auto/qml/qqmllanguage/data/typeAnnotations.qml b/tests/auto/qml/qqmllanguage/data/typeAnnotations.qml new file mode 100644 index 0000000000..d8cc4535eb --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/typeAnnotations.qml @@ -0,0 +1,7 @@ +import QtQml 2.0 + +QtObject { + function notYet() : string { + return "ko" + } +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 8cbb39974e..cc7935f383 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -622,6 +622,9 @@ void tst_qqmllanguage::errors_data() QTest::newRow("fuzzed.2") << "fuzzed.2.qml" << "fuzzed.2.errors.txt" << false; QTest::newRow("bareQmlImport") << "bareQmlImport.qml" << "bareQmlImport.errors.txt" << false; + + QTest::newRow("typeAnnotations") << "typeAnnotations.qml" << "typeAnnotations.errors.txt" << false; + QTest::newRow("typeAnnotations.2") << "typeAnnotations.2.qml" << "typeAnnotations.2.errors.txt" << false; } diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/anonfunctionexpr.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/anonfunctionexpr.js new file mode 100644 index 0000000000..f660edb69e --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/anonfunctionexpr.js @@ -0,0 +1,3 @@ +(function () : string { + return "ko" +}) diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classmemberparam.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classmemberparam.js new file mode 100644 index 0000000000..ab85d90880 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classmemberparam.js @@ -0,0 +1,6 @@ + +class Foo { + member(param: string) { + return "ko" + } +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classreturnvalue.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classreturnvalue.js new file mode 100644 index 0000000000..a7da9e0ca7 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classreturnvalue.js @@ -0,0 +1,6 @@ + +class Foo { + member(): string { + return "ko" + } +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/function.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/function.js new file mode 100644 index 0000000000..4d6021e835 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/function.js @@ -0,0 +1,4 @@ + +function x() : string { + return "ok" +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionexpr.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionexpr.js new file mode 100644 index 0000000000..33f2abbb61 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionexpr.js @@ -0,0 +1,3 @@ +(function x() : string { + return "ko" +}) diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionparams.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionparams.js new file mode 100644 index 0000000000..2c23628e3f --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionparams.js @@ -0,0 +1,3 @@ + +function test(x: string, y: string) { +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration.js new file mode 100644 index 0000000000..a6cc00e38a --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration.js @@ -0,0 +1,3 @@ + +for (var i: int = 0; i < 100; ++i) { +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration2.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration2.js new file mode 100644 index 0000000000..24d5acce98 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration2.js @@ -0,0 +1,5 @@ + +let y = [1, 2, 3]; + +for (let x: int of y) { +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml new file mode 100644 index 0000000000..f435bf1b25 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml @@ -0,0 +1,9 @@ +import QtQuick 2.12 as MyQuick +MyQuick.Item { + function factory(param: string) : MyQuick.Item { + function nested(foo: string) { + return this + } + return nested() + } +} diff --git a/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/variables.js b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/variables.js new file mode 100644 index 0000000000..bf332ac7a8 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/disallowedtypeannotations/variables.js @@ -0,0 +1,2 @@ + +var x: string = "ko" diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/parametrized.qml b/tests/auto/qml/qqmlparser/data/typeannotations/parametrized.qml new file mode 100644 index 0000000000..d80b5e3f87 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/typeannotations/parametrized.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 as MyQuick +MyQuick.Item { + function factory() : list<MyQuick.Item> { + } +} diff --git a/tests/auto/qml/qqmlparser/data/typeannotations/qmlfunction.qml b/tests/auto/qml/qqmlparser/data/typeannotations/qmlfunction.qml new file mode 100644 index 0000000000..cd5c1f51e5 --- /dev/null +++ b/tests/auto/qml/qqmlparser/data/typeannotations/qmlfunction.qml @@ -0,0 +1,6 @@ +import QtQuick 2.12 as MyQuick +MyQuick.Item { + function factory(param: string) : MyQuick.Item { + return this + } +} diff --git a/tests/auto/qml/qqmlparser/qqmlparser.pro b/tests/auto/qml/qqmlparser/qqmlparser.pro index 74cb620f06..d8e4b0dd06 100644 --- a/tests/auto/qml/qqmlparser/qqmlparser.pro +++ b/tests/auto/qml/qqmlparser/qqmlparser.pro @@ -7,3 +7,7 @@ SOURCES += tst_qqmlparser.cpp DEFINES += SRCDIR=\\\"$$PWD\\\" cross_compile: DEFINES += QTEST_CROSS_COMPILED + +TESTDATA = data/* + +include (../../shared/util.pri) diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp index 5f58df75d4..9d8818d01e 100644 --- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp +++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp @@ -32,12 +32,14 @@ #include <private/qqmljsastvisitor_p.h> #include <private/qqmljsast_p.h> +#include "../../shared/util.h" + #include <qtest.h> #include <QDir> #include <QDebug> #include <cstdlib> -class tst_qqmlparser : public QObject +class tst_qqmlparser : public QQmlDataTest { Q_OBJECT public: @@ -56,6 +58,10 @@ private slots: void leadingSemicolonInClass(); void templatedReadonlyProperty(); void qmlImportInJSRequiresFullVersion(); + void typeAnnotations_data(); + void typeAnnotations(); + void disallowedTypeAnnotations_data(); + void disallowedTypeAnnotations(); private: QStringList excludedDirs; @@ -88,7 +94,7 @@ public: qDebug() << "first source loc failed: node:" << node->kind << "at" << node->firstSourceLocation().startLine << "/" << node->firstSourceLocation().startColumn << "parent" << parent->kind << "at" << parent->firstSourceLocation().startLine << "/" << parent->firstSourceLocation().startColumn; if (node->lastSourceLocation().end() > parentEnd) - qDebug() << "first source loc failed: node:" << node->kind << "at" << node->lastSourceLocation().startLine << "/" << node->lastSourceLocation().startColumn + qDebug() << "last source loc failed: node:" << node->kind << "at" << node->lastSourceLocation().startLine << "/" << node->lastSourceLocation().startColumn << "parent" << parent->kind << "at" << parent->lastSourceLocation().startLine << "/" << parent->lastSourceLocation().startColumn; QVERIFY(node->firstSourceLocation().begin() >= parentBegin); @@ -114,6 +120,27 @@ public: } }; +struct TypeAnnotationObserver: public AST::Visitor +{ + bool typeAnnotationSeen = false; + + void operator()(AST::Node *node) + { + AST::Node::accept(node, this); + } + + virtual bool visit(AST::TypeAnnotation *) + { + typeAnnotationSeen = true; + return true; + } + + void throwRecursionDepthError() final + { + QFAIL("Maximum statement or expression depth exceeded"); + } +}; + } tst_qqmlparser::tst_qqmlparser() @@ -122,6 +149,7 @@ tst_qqmlparser::tst_qqmlparser() void tst_qqmlparser::initTestCase() { + QQmlDataTest::initTestCase(); // Add directories you want excluded here // These snippets are not expected to run on their own. @@ -334,6 +362,82 @@ void tst_qqmlparser::qmlImportInJSRequiresFullVersion() } } +void tst_qqmlparser::typeAnnotations_data() +{ + QTest::addColumn<QString>("file"); + + QString tests = dataDirectory() + "/typeannotations/"; + + QStringList files; + files << findFiles(QDir(tests)); + + for (const QString &file: qAsConst(files)) + QTest::newRow(qPrintable(file)) << file; +} + +void tst_qqmlparser::typeAnnotations() +{ + using namespace QQmlJS; + + QFETCH(QString, file); + + QString code; + + QFile f(file); + if (f.open(QFile::ReadOnly)) + code = QString::fromUtf8(f.readAll()); + + const bool qmlMode = file.endsWith(QLatin1String(".qml")); + + Engine engine; + Lexer lexer(&engine); + lexer.setCode(code, 1, qmlMode); + Parser parser(&engine); + bool ok = qmlMode ? parser.parse() : parser.parseProgram(); + QVERIFY(ok); + + check::TypeAnnotationObserver observer; + observer(parser.rootNode()); + + QVERIFY(observer.typeAnnotationSeen); +} + +void tst_qqmlparser::disallowedTypeAnnotations_data() +{ + QTest::addColumn<QString>("file"); + + QString tests = dataDirectory() + "/disallowedtypeannotations/"; + + QStringList files; + files << findFiles(QDir(tests)); + + for (const QString &file: qAsConst(files)) + QTest::newRow(qPrintable(file)) << file; +} + +void tst_qqmlparser::disallowedTypeAnnotations() +{ + using namespace QQmlJS; + + QFETCH(QString, file); + + QString code; + + QFile f(file); + if (f.open(QFile::ReadOnly)) + code = QString::fromUtf8(f.readAll()); + + const bool qmlMode = file.endsWith(QLatin1String(".qml")); + + Engine engine; + Lexer lexer(&engine); + lexer.setCode(code, 1, qmlMode); + Parser parser(&engine); + bool ok = qmlMode ? parser.parse() : parser.parseProgram(); + QVERIFY(!ok); + QVERIFY2(parser.errorMessage().startsWith("Type annotations are not permitted "), qPrintable(parser.errorMessage())); +} + QTEST_MAIN(tst_qqmlparser) #include "tst_qqmlparser.moc" |