aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/compiler/qv4codegen.cpp8
-rw-r--r--src/qml/compiler/qv4codegen_p.h2
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp5
-rw-r--r--src/qml/parser/qqmljs.g213
-rw-r--r--src/qml/parser/qqmljsast.cpp35
-rw-r--r--src/qml/parser/qqmljsast_p.h181
-rw-r--r--src/qml/parser/qqmljsastfwd_p.h3
-rw-r--r--src/qml/parser/qqmljsastvisitor_p.h9
-rw-r--r--tests/auto/qml/qqmllanguage/data/typeAnnotations.2.errors.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/typeAnnotations.2.qml8
-rw-r--r--tests/auto/qml/qqmllanguage/data/typeAnnotations.errors.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/typeAnnotations.qml7
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp3
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/anonfunctionexpr.js3
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classmemberparam.js6
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/classreturnvalue.js6
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/function.js4
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionexpr.js3
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/functionparams.js3
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration.js3
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/iteration2.js5
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/qmlnestedfunction.qml9
-rw-r--r--tests/auto/qml/qqmlparser/data/disallowedtypeannotations/variables.js2
-rw-r--r--tests/auto/qml/qqmlparser/data/typeannotations/parametrized.qml5
-rw-r--r--tests/auto/qml/qqmlparser/data/typeannotations/qmlfunction.qml6
-rw-r--r--tests/auto/qml/qqmlparser/qqmlparser.pro4
-rw-r--r--tests/auto/qml/qqmlparser/tst_qqmlparser.cpp108
27 files changed, 545 insertions, 98 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 453963c1dd..6248348d8e 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -610,6 +610,8 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con
if (hasError())
return;
+ accept(e->typeAnnotation);
+
if (e->initializer) {
if (!baseRef.isValid()) {
// assignment
@@ -885,6 +887,12 @@ bool Codegen::visit(ExportDeclaration *ast)
return false;
}
+bool Codegen::visit(TypeAnnotation *ast)
+{
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Type annotations are not supported (yet)."));
+ return false;
+}
+
bool Codegen::visit(StatementList *)
{
Q_UNREACHABLE();
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 51b821aafe..ef710b5648 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -593,6 +593,8 @@ protected:
bool visit(AST::ExportDeclaration *ast) override;
+ bool visit(AST::TypeAnnotation *ast) override;
+
// expressions
bool visit(AST::Expression *ast) override;
bool visit(AST::ArrayPattern *ast) override;
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index 416a0edee0..11da6605c3 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -679,6 +679,11 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete
_context->isArrowFunction = true;
else if (expr->isGenerator)
_context->isGenerator = true;
+
+ if (expr->typeAnnotation) {
+ _cg->throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Type annotations are not supported (yet)."));
+ return false;
+ }
}
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index daaa402ef1..28566e21ea 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -299,6 +299,9 @@ public:
AST::ExportsList *ExportsList;
AST::ExportClause *ExportClause;
AST::ExportDeclaration *ExportDeclaration;
+ AST::TypeAnnotation *TypeAnnotation;
+ AST::TypeArgumentList *TypeArgumentList;
+ AST::Type *Type;
AST::UiProgram *UiProgram;
AST::UiHeaderItemList *UiHeaderItemList;
@@ -424,6 +427,8 @@ protected:
diagnostic_messages.append(compileError(location, message));
}
+ bool ensureNoFunctionTypeAnnotations(AST::TypeAnnotation *returnTypeAnnotation, AST::FormalParameterList *formals);
+
protected:
Engine *driver;
MemoryPool *pool;
@@ -594,6 +599,21 @@ int Parser::lookaheadToken(Lexer *lexer)
return yytoken;
}
+bool Parser::ensureNoFunctionTypeAnnotations(AST::TypeAnnotation *returnValueAnnotation, AST::FormalParameterList *formals)
+{
+ for (auto formal = formals; formal; formal = formal->next) {
+ if (formal->element && formal->element->typeAnnotation) {
+ syntaxError(formal->element->typeAnnotation->firstSourceLocation(), "Type annotations are not permitted in function parameters in JavaScript functions");
+ return false;
+ }
+ }
+ if (returnValueAnnotation) {
+ syntaxError(returnValueAnnotation->firstSourceLocation(), "Type annotations are not permitted for the return value of JavaScript functions");
+ return false;
+ }
+ return true;
+}
+
//#define PARSER_DEBUG
bool Parser::parse(int startToken)
@@ -1355,7 +1375,7 @@ UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON Expre
} break;
./
-UiObjectMember: FunctionDeclaration;
+UiObjectMember: FunctionDeclarationWithTypes;
/.
case $rule_number: {
sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
@@ -1471,6 +1491,54 @@ IdentifierReference: JsIdentifier;
BindingIdentifier: IdentifierReference;
--------------------------------------------------------------------------------------------------------
+-- Types
+--------------------------------------------------------------------------------------------------------
+
+TypeArguments: Type;
+/.
+ case $rule_number: {
+ sym(1).TypeArgumentList = new (pool) AST::TypeArgumentList(sym(1).Type);
+ } break;
+./
+
+TypeArguments: TypeArguments T_COMMA Type;
+/.
+ case $rule_number: {
+ sym(1).TypeArgumentList = new (pool) AST::TypeArgumentList(sym(1).TypeArgumentList, sym(3).Type);
+ } break;
+./
+
+Type: UiQualifiedId T_LT TypeArguments T_GT;
+/.
+ case $rule_number: {
+ sym(1).Type = new (pool) AST::Type(sym(1).UiQualifiedId, sym(3).TypeArgumentList->finish());
+ } break;
+./
+
+Type: UiQualifiedId;
+/.
+ case $rule_number: {
+ sym(1).Type = new (pool) AST::Type(sym(1).UiQualifiedId);
+ } break;
+./
+
+TypeAnnotation: T_COLON Type;
+/.
+ case $rule_number: {
+ sym(1).TypeAnnotation = new (pool) AST::TypeAnnotation(sym(2).Type);
+ sym(1).TypeAnnotation->colonToken = loc(1);
+ } break;
+./
+
+TypeAnnotationOpt: TypeAnnotation;
+TypeAnnotationOpt: ;
+/.
+ case $rule_number: {
+ sym(1).TypeAnnotation = nullptr;
+ } break;
+./
+
+--------------------------------------------------------------------------------------------------------
-- Expressions
--------------------------------------------------------------------------------------------------------
@@ -2851,7 +2919,14 @@ VarDeclaration: Var VariableDeclarationList;
VarDeclaration_In: Var VariableDeclarationList_In;
/.
case $rule_number: {
- AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(sym(1).scope));
+ AST::VariableDeclarationList *declarations = sym(2).VariableDeclarationList->finish(sym(1).scope);
+ for (auto it = declarations; it; it = it->next) {
+ if (it->declaration && it->declaration->typeAnnotation) {
+ syntaxError(it->declaration->typeAnnotation->firstSourceLocation(), "Type annotations are not permitted in variable declarations");
+ return false;
+ }
+ }
+ AST::VariableStatement *node = new (pool) AST::VariableStatement(declarations);
node->declarationKindToken = loc(1);
sym(1).Node = node;
} break;
@@ -2888,22 +2963,22 @@ VariableDeclarationList_In: VariableDeclarationList_In T_COMMA VariableDeclarati
} break;
./
-LexicalBinding: BindingIdentifier InitializerOpt;
+LexicalBinding: BindingIdentifier TypeAnnotationOpt InitializerOpt;
/. case $rule_number: Q_FALLTHROUGH(); ./
-LexicalBinding_In: BindingIdentifier InitializerOpt_In;
+LexicalBinding_In: BindingIdentifier TypeAnnotationOpt InitializerOpt_In;
/. case $rule_number: Q_FALLTHROUGH(); ./
-VariableDeclaration: BindingIdentifier InitializerOpt;
+VariableDeclaration: BindingIdentifier TypeAnnotationOpt InitializerOpt;
/. case $rule_number: Q_FALLTHROUGH(); ./
-VariableDeclaration_In: BindingIdentifier InitializerOpt_In;
+VariableDeclaration_In: BindingIdentifier TypeAnnotationOpt InitializerOpt_In;
/.
case $rule_number: {
- auto *node = new (pool) AST::PatternElement(stringRef(1), sym(2).Expression);
+ auto *node = new (pool) AST::PatternElement(stringRef(1), sym(2).TypeAnnotation, sym(3).Expression);
node->identifierToken = loc(1);
sym(1).Node = node;
// if initializer is an anonymous function expression, we need to assign identifierref as it's name
- if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
+ if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression))
f->name = stringRef(1);
- if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
+ if (auto *c = asAnonymousClassDefinition(sym(3).Expression))
c->name = stringRef(1);
} break;
./
@@ -3053,15 +3128,15 @@ BindingProperty: PropertyName T_COLON BindingPattern InitializerOpt_In;
} break;
./
-BindingElement: BindingIdentifier InitializerOpt_In;
+BindingElement: BindingIdentifier TypeAnnotationOpt InitializerOpt_In;
/.
case $rule_number: {
- AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(1), sym(2).Expression);
+ AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(1), sym(2).TypeAnnotation, sym(3).Expression);
node->identifierToken = loc(1);
// if initializer is an anonymous function expression, we need to assign identifierref as it's name
- if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
+ if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression))
f->name = stringRef(1);
- if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
+ if (auto *c = asAnonymousClassDefinition(sym(3).Expression))
c->name = stringRef(1);
sym(1).Node = node;
} break;
@@ -3078,7 +3153,7 @@ BindingElement: BindingPattern InitializerOpt_In;
BindingRestElement: T_ELLIPSIS BindingIdentifier;
/.
case $rule_number: {
- AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(2), nullptr, AST::PatternElement::RestElement);
+ AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(2), /*type annotation*/nullptr, nullptr, AST::PatternElement::RestElement);
node->identifierToken = loc(2);
sym(1).Node = node;
} break;
@@ -3268,12 +3343,16 @@ IterationStatement: T_FOR T_LPAREN ForDeclaration InOrOf Expression_In T_RPAREN
} break;
./
-ForDeclaration: LetOrConst BindingIdentifier;
+ForDeclaration: LetOrConst BindingIdentifier TypeAnnotationOpt;
/. case $rule_number: Q_FALLTHROUGH(); ./
-ForDeclaration: Var BindingIdentifier;
+ForDeclaration: Var BindingIdentifier TypeAnnotationOpt;
/.
case $rule_number: {
- auto *node = new (pool) AST::PatternElement(stringRef(2), nullptr);
+ if (auto typeAnnotation = sym(3).TypeAnnotation) {
+ syntaxError(typeAnnotation->firstSourceLocation(), "Type annotations are not permitted in variable declarations");
+ return false;
+ }
+ auto *node = new (pool) AST::PatternElement(stringRef(2), sym(3).TypeAnnotation, nullptr);
node->identifierToken = loc(2);
node->scope = sym(1).scope;
node->isForDeclaration = true;
@@ -3560,59 +3639,85 @@ DebuggerStatement: T_DEBUGGER T_SEMICOLON;
-- otherwise conflict.
Function: T_FUNCTION %prec REDUCE_HERE;
-FunctionDeclaration: Function BindingIdentifier T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+FunctionDeclaration: Function BindingIdentifier T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(6).TypeAnnotation, sym(4).FormalParameterList))
+ return false;
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList,
+ /*type annotation*/nullptr);
node->functionToken = loc(1);
node->identifierToken = loc(2);
node->lparenToken = loc(3);
node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
+ node->lbraceToken = loc(7);
+ node->rbraceToken = loc(9);
sym(1).Node = node;
} break;
./
+FunctionDeclarationWithTypes: Function BindingIdentifier T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
+/.
+ case $rule_number: {
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList,
+ sym(6).TypeAnnotation);
+ node->functionToken = loc(1);
+ node->identifierToken = loc(2);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(7);
+ node->rbraceToken = loc(9);
+ sym(1).Node = node;
+ } break;
+./
FunctionDeclaration_Default: FunctionDeclaration;
-FunctionDeclaration_Default: Function T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+FunctionDeclaration_Default: Function T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(QStringRef(), sym(3).FormalParameterList, sym(6).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(5).TypeAnnotation, sym(3).FormalParameterList))
+ return false;
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(QStringRef(), sym(3).FormalParameterList, sym(7).StatementList,
+ /*type annotation*/nullptr);
node->functionToken = loc(1);
node->lparenToken = loc(2);
node->rparenToken = loc(4);
- node->lbraceToken = loc(5);
- node->rbraceToken = loc(7);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
sym(1).Node = node;
} break;
./
-FunctionExpression: T_FUNCTION BindingIdentifier T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+FunctionExpression: T_FUNCTION BindingIdentifier T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(6).TypeAnnotation, sym(4).FormalParameterList))
+ return false;
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList,
+ /*type annotation*/nullptr);
node->functionToken = loc(1);
if (! stringRef(2).isNull())
node->identifierToken = loc(2);
node->lparenToken = loc(3);
node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
+ node->lbraceToken = loc(7);
+ node->rbraceToken = loc(9);
sym(1).Node = node;
} break;
./
-FunctionExpression: T_FUNCTION T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+FunctionExpression: T_FUNCTION T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(5).TypeAnnotation, sym(3).FormalParameterList))
+ return false;
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(7).StatementList,
+ /*type annotation*/nullptr);
node->functionToken = loc(1);
node->lparenToken = loc(2);
node->rparenToken = loc(4);
- node->lbraceToken = loc(5);
- node->rbraceToken = loc(7);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
sym(1).Node = node;
} break;
./
@@ -3722,7 +3827,7 @@ ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead T_FORCE_BLOCK Fun
ArrowParameters: BindingIdentifier;
/.
case $rule_number: {
- AST::PatternElement *e = new (pool) AST::PatternElement(stringRef(1), nullptr, AST::PatternElement::Binding);
+ AST::PatternElement *e = new (pool) AST::PatternElement(stringRef(1), /*type annotation*/nullptr, nullptr, AST::PatternElement::Binding);
e->identifierToken = loc(1);
sym(1).FormalParameterList = (new (pool) AST::FormalParameterList(nullptr, e))->finish(pool);
} break;
@@ -3756,30 +3861,34 @@ ConciseBodyLookahead: ;
} break;
./
-MethodDefinition: PropertyName T_LPAREN StrictFormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+MethodDefinition: PropertyName T_LPAREN StrictFormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(1), sym(3).FormalParameterList, sym(6).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(5).TypeAnnotation, sym(3).FormalParameterList))
+ return false;
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(1), sym(3).FormalParameterList, sym(7).StatementList);
f->functionToken = sym(1).PropertyName->firstSourceLocation();
f->lparenToken = loc(2);
f->rparenToken = loc(4);
- f->lbraceToken = loc(5);
- f->rbraceToken = loc(7);
+ f->lbraceToken = loc(6);
+ f->rbraceToken = loc(8);
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, f, AST::PatternProperty::Method);
node->colonToken = loc(2);
sym(1).Node = node;
} break;
./
-MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace;
+MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace GeneratorBody GeneratorRBrace;
/.
case $rule_number: {
- AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(6).TypeAnnotation, sym(4).FormalParameterList))
+ return false;
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList);
f->functionToken = sym(2).PropertyName->firstSourceLocation();
f->lparenToken = loc(3);
f->rparenToken = loc(5);
- f->lbraceToken = loc(6);
- f->rbraceToken = loc(8);
+ f->lbraceToken = loc(7);
+ f->rbraceToken = loc(9);
f->isGenerator = true;
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Method);
node->colonToken = loc(2);
@@ -3788,30 +3897,34 @@ MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_R
./
-MethodDefinition: T_GET PropertyName T_LPAREN T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+MethodDefinition: T_GET PropertyName T_LPAREN T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), nullptr, sym(6).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(5).TypeAnnotation, /*formals*/nullptr))
+ return false;
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), nullptr, sym(7).StatementList);
f->functionToken = sym(2).PropertyName->firstSourceLocation();
f->lparenToken = loc(3);
f->rparenToken = loc(4);
- f->lbraceToken = loc(5);
- f->rbraceToken = loc(7);
+ f->lbraceToken = loc(6);
+ f->rbraceToken = loc(8);
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Getter);
node->colonToken = loc(2);
sym(1).Node = node;
} break;
./
-MethodDefinition: T_SET PropertyName T_LPAREN PropertySetParameterList T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+MethodDefinition: T_SET PropertyName T_LPAREN PropertySetParameterList T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace;
/.
case $rule_number: {
- AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ if (!ensureNoFunctionTypeAnnotations(sym(6).TypeAnnotation, sym(4).FormalParameterList))
+ return false;
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList);
f->functionToken = sym(2).PropertyName->firstSourceLocation();
f->lparenToken = loc(3);
f->rparenToken = loc(5);
- f->lbraceToken = loc(6);
- f->rbraceToken = loc(8);
+ f->lbraceToken = loc(7);
+ f->rbraceToken = loc(9);
AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Setter);
node->colonToken = loc(2);
sym(1).Node = node;
diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp
index 1bc0e6e364..de5db73d3f 100644
--- a/src/qml/parser/qqmljsast.cpp
+++ b/src/qml/parser/qqmljsast.cpp
@@ -131,7 +131,7 @@ FormalParameterList *ExpressionNode::reparseAsFormalParameterList(MemoryPool *po
}
AST::PatternElement *binding = nullptr;
if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(expr)) {
- binding = new (pool) AST::PatternElement(idExpr->name, rhs);
+ binding = new (pool) AST::PatternElement(idExpr->name, /*type annotation*/nullptr, rhs);
binding->identifierToken = idExpr->identifierToken;
} else if (AST::Pattern *p = expr->patternCast()) {
SourceLocation loc;
@@ -961,6 +961,7 @@ void FunctionDeclaration::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(formals, visitor);
+ accept(typeAnnotation, visitor);
accept(body, visitor);
}
@@ -971,6 +972,7 @@ void FunctionExpression::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(formals, visitor);
+ accept(typeAnnotation, visitor);
accept(body, visitor);
}
@@ -1271,6 +1273,35 @@ void UiQualifiedId::accept0(Visitor *visitor)
visitor->endVisit(this);
}
+void Type::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(typeId, visitor);
+ accept(typeArguments, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void TypeArgumentList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (TypeArgumentList *it = this; it; it = it->next)
+ accept(it->typeId, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void TypeAnnotation::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(type, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
void UiImport::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
@@ -1339,6 +1370,7 @@ void PatternElement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(bindingTarget, visitor);
+ accept(typeAnnotation, visitor);
accept(initializer, visitor);
}
@@ -1382,6 +1414,7 @@ void PatternProperty::accept0(Visitor *visitor)
if (visitor->visit(this)) {
accept(name, visitor);
accept(bindingTarget, visitor);
+ accept(typeAnnotation, visitor);
accept(initializer, visitor);
}
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index c62c11885f..21d143edf1 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -234,6 +234,9 @@ public:
Kind_PatternElementList,
Kind_PatternProperty,
Kind_PatternPropertyList,
+ Kind_Type,
+ Kind_TypeArgumentList,
+ Kind_TypeAnnotation,
Kind_UiArrayBinding,
Kind_UiImport,
@@ -304,6 +307,128 @@ public:
int kind = Kind_Undefined;
};
+
+class QML_PARSER_EXPORT UiQualifiedId: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(UiQualifiedId)
+
+ UiQualifiedId(const QStringRef &name)
+ : next(this), name(name)
+ { kind = K; }
+
+ UiQualifiedId(UiQualifiedId *previous, const QStringRef &name)
+ : name(name)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ UiQualifiedId *finish()
+ {
+ UiQualifiedId *head = next;
+ next = nullptr;
+ return head;
+ }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return identifierToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return next ? next->lastSourceLocation() : identifierToken; }
+
+// attributes
+ UiQualifiedId *next;
+ QStringRef name;
+ SourceLocation identifierToken;
+};
+
+class QML_PARSER_EXPORT Type: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(Type)
+
+ Type(UiQualifiedId *typeId, Node *typeArguments = nullptr)
+ : typeId(typeId)
+ , typeArguments(typeArguments)
+ { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return typeId->firstSourceLocation(); }
+
+ SourceLocation lastSourceLocation() const override
+ { return typeArguments ? typeArguments->lastSourceLocation() : typeId->lastSourceLocation(); }
+
+// attributes
+ UiQualifiedId *typeId;
+ Node *typeArguments; // TypeArgumentList
+};
+
+
+class QML_PARSER_EXPORT TypeArgumentList: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(TypeArgumentList)
+
+ TypeArgumentList(Type *typeId)
+ : typeId(typeId)
+ , next(nullptr)
+ { kind = K; }
+
+ TypeArgumentList(TypeArgumentList *previous, Type *typeId)
+ : typeId(typeId)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return typeId->firstSourceLocation(); }
+
+ SourceLocation lastSourceLocation() const override
+ { return next ? next->lastSourceLocation() : typeId->lastSourceLocation(); }
+
+ inline TypeArgumentList *finish()
+ {
+ TypeArgumentList *front = next;
+ next = nullptr;
+ return front;
+ }
+
+// attributes
+ Type *typeId;
+ TypeArgumentList *next;
+};
+
+class QML_PARSER_EXPORT TypeAnnotation: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(TypeAnnotation)
+
+ TypeAnnotation(Type *type)
+ : type(type)
+ { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return colonToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return type->lastSourceLocation(); }
+
+// attributes
+ Type *type;
+ SourceLocation colonToken;
+};
class QML_PARSER_EXPORT ExpressionNode: public Node
{
public:
@@ -729,8 +854,9 @@ public:
: initializer(i), type(t)
{ kind = K; }
- PatternElement(const QStringRef &n, ExpressionNode *i = nullptr, Type t = Binding)
+ PatternElement(const QStringRef &n, TypeAnnotation *typeAnnotation = nullptr, ExpressionNode *i = nullptr, Type t = Binding)
: bindingIdentifier(n), initializer(i), type(t)
+ , typeAnnotation(typeAnnotation)
{
Q_ASSERT(t >= RestElement);
kind = K;
@@ -750,7 +876,7 @@ public:
{ return identifierToken.isValid() ? identifierToken : (bindingTarget ? bindingTarget->firstSourceLocation() : initializer->firstSourceLocation()); }
SourceLocation lastSourceLocation() const override
- { return initializer ? initializer->lastSourceLocation() : (bindingTarget ? bindingTarget->lastSourceLocation() : identifierToken); }
+ { return initializer ? initializer->lastSourceLocation() : (bindingTarget ? bindingTarget->lastSourceLocation() : (typeAnnotation ? typeAnnotation->lastSourceLocation() : identifierToken)); }
ExpressionNode *destructuringTarget() const { return bindingTarget; }
Pattern *destructuringPattern() const { return bindingTarget ? bindingTarget->patternCast() : nullptr; }
@@ -768,6 +894,7 @@ public:
ExpressionNode *bindingTarget = nullptr;
ExpressionNode *initializer = nullptr;
Type type = Literal;
+ TypeAnnotation *typeAnnotation = nullptr;
// when used in a VariableDeclarationList
VariableScope scope = VariableScope::NoScope;
bool isForDeclaration = false;
@@ -820,7 +947,7 @@ public:
{ kind = K; }
PatternProperty(PropertyName *name, const QStringRef &n, ExpressionNode *i = nullptr)
- : PatternElement(n, i), name(name)
+ : PatternElement(n, /*type annotation*/nullptr, i), name(name)
{ kind = K; }
PatternProperty(PropertyName *name, Pattern *pattern, ExpressionNode *i = nullptr)
@@ -2155,8 +2282,9 @@ class QML_PARSER_EXPORT FunctionExpression: public ExpressionNode
public:
QQMLJS_DECLARE_AST_NODE(FunctionExpression)
- FunctionExpression(const QStringRef &n, FormalParameterList *f, StatementList *b):
- name (n), formals (f), body (b)
+ FunctionExpression(const QStringRef &n, FormalParameterList *f, StatementList *b, TypeAnnotation *typeAnnotation = nullptr):
+ name (n), formals (f), body (b),
+ typeAnnotation(typeAnnotation)
{ kind = K; }
void accept0(Visitor *visitor) override;
@@ -2175,6 +2303,7 @@ public:
bool isGenerator = false;
FormalParameterList *formals;
StatementList *body;
+ TypeAnnotation *typeAnnotation;
SourceLocation functionToken;
SourceLocation identifierToken;
SourceLocation lparenToken;
@@ -2188,8 +2317,8 @@ class QML_PARSER_EXPORT FunctionDeclaration: public FunctionExpression
public:
QQMLJS_DECLARE_AST_NODE(FunctionDeclaration)
- FunctionDeclaration(const QStringRef &n, FormalParameterList *f, StatementList *b):
- FunctionExpression(n, f, b)
+ FunctionDeclaration(const QStringRef &n, FormalParameterList *f, StatementList *b, TypeAnnotation *typeAnnotation = nullptr):
+ FunctionExpression(n, f, b, typeAnnotation)
{ kind = K; }
void accept0(Visitor *visitor) override;
@@ -2810,44 +2939,6 @@ public:
SourceLocation semicolonToken;
};
-class QML_PARSER_EXPORT UiQualifiedId: public Node
-{
-public:
- QQMLJS_DECLARE_AST_NODE(UiQualifiedId)
-
- UiQualifiedId(const QStringRef &name)
- : next(this), name(name)
- { kind = K; }
-
- UiQualifiedId(UiQualifiedId *previous, const QStringRef &name)
- : name(name)
- {
- kind = K;
- next = previous->next;
- previous->next = this;
- }
-
- UiQualifiedId *finish()
- {
- UiQualifiedId *head = next;
- next = nullptr;
- return head;
- }
-
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return identifierToken; }
-
- SourceLocation lastSourceLocation() const override
- { return next ? next->lastSourceLocation() : identifierToken; }
-
-// attributes
- UiQualifiedId *next;
- QStringRef name;
- SourceLocation identifierToken;
-};
-
class QML_PARSER_EXPORT UiImport: public Node
{
public:
diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h
index 6fe108e425..05226fd043 100644
--- a/src/qml/parser/qqmljsastfwd_p.h
+++ b/src/qml/parser/qqmljsastfwd_p.h
@@ -158,6 +158,9 @@ class NestedExpression;
class ClassExpression;
class ClassDeclaration;
class ClassElementList;
+class TypeArgumentList;
+class Type;
+class TypeAnnotation;
// ui elements
class UiProgram;
diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h
index f3732cbba8..7146cd00ac 100644
--- a/src/qml/parser/qqmljsastvisitor_p.h
+++ b/src/qml/parser/qqmljsastvisitor_p.h
@@ -403,6 +403,15 @@ public:
virtual bool visit(DebuggerStatement *) { return true; }
virtual void endVisit(DebuggerStatement *) {}
+ virtual bool visit(Type *) { return true; }
+ virtual void endVisit(Type *) {}
+
+ virtual bool visit(TypeArgumentList *) { return true; }
+ virtual void endVisit(TypeArgumentList *) {}
+
+ virtual bool visit(TypeAnnotation *) { return true; }
+ virtual void endVisit(TypeAnnotation *) {}
+
virtual void throwRecursionDepthError() = 0;
quint16 recursionDepth() const { return m_recursionDepth; }
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"