diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 0ce40af3ff5f16d7b945d41e7498d36fc5d29973..9fa83ca1b3f0162fa394c25036400c50a4b87ec9 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -423,7 +423,7 @@ checker::Type *ETSAnalyzer::Check(ir::SpreadElement *expr) const return expr->SetTsType(exprType->AsETSObjectType()->TypeArguments().front()); } - if (!exprType->IsETSArrayType() && !exprType->IsETSTupleType() && !exprType->IsETSReadonlyArrayType()) { + if (!exprType->IsAnyETSArrayOrTupleType()) { if (!exprType->IsTypeError()) { // Don't duplicate error messages for the same error checker->LogError(diagnostic::SPREAD_OF_INVALID_TYPE, {exprType}, expr->Start()); diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index 1fd04a4a9e88ed3d4b7489ecfbe585db545fbe43..0ff022f42d722e52c2229a6853475b34777b0687 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -1799,8 +1799,9 @@ SignatureInfo *ETSChecker::ComposeSignatureInfo(ir::TSTypeParameterDeclaration * return nullptr; } ES2PANDA_ASSERT(restParamType != nullptr); - if (!restParamType->IsAnyETSArrayOrTupleType()) { - LogError(diagnostic::ONLY_ARRAY_OR_TUPLE_FOR_REST, {}, param->Start()); + if (!restParamType->IsAnySpreadOrRestCompatibleType()) { + LogError(diagnostic::REST_PARAMETER_OF_INVALID_TYPE, {restParamType->MaybeBaseTypeOfGradualType()}, + param->Start()); restParamType = GlobalTypeError(); } signatureInfo->restVar = SetupSignatureParameter(param, restParamType); diff --git a/ets2panda/checker/types/type.cpp b/ets2panda/checker/types/type.cpp index 694499259016b06b0bea17986aa1a3dc766f89e3..c8d3095e4744aa6f13dbe9b67ef255728d9fbc82 100644 --- a/ets2panda/checker/types/type.cpp +++ b/ets2panda/checker/types/type.cpp @@ -60,6 +60,22 @@ bool Type::IsBuiltinNumeric() const noexcept return IsETSObjectType() && AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_NUMERIC); } +bool Type::IsETSIterableType() const +{ + bool retVal = false; + if (IsETSObjectType() && AsETSObjectType()->HasObjectFlag(ETSObjectFlags::CLASS)) { + auto *iter = AsETSObjectType()->GetProperty(compiler::Signatures::ITERATOR_METHOD, + checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD | + checker::PropertySearchFlags::SEARCH_IN_INTERFACES | + checker::PropertySearchFlags::SEARCH_IN_BASE); + + if (iter != nullptr) { + retVal = true; + } + } + return retVal; +} + bool Type::IsLambdaObject() const { if (IsETSObjectType() && (AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL_INTERFACE) || diff --git a/ets2panda/checker/types/type.h b/ets2panda/checker/types/type.h index b0a50a0b51d31f85953003e00e86e147ff312481..0b7197c4037a8778159cdf985c459c96cf9396e2 100644 --- a/ets2panda/checker/types/type.h +++ b/ets2panda/checker/types/type.h @@ -113,6 +113,7 @@ public: bool IsETSAsyncFuncReturnType() const; bool IsETSUnboxableObject() const; bool IsETSPrimitiveOrEnumType() const; + bool IsETSIterableType() const; bool IsETSDoubleEnumType() const; bool PossiblyETSNull() const; @@ -176,6 +177,11 @@ public: return HasTypeFlag(checker::TypeFlag::CONSTANT); } + bool IsAnySpreadOrRestCompatibleType() const + { + return IsAnyETSArrayOrTupleType() || IsETSIterableType(); + } + bool IsAnyETSArrayOrTupleType() const { return IsETSArrayType() || IsETSResizableArrayType() || IsETSReadonlyArrayType() || IsETSTupleType(); diff --git a/ets2panda/test/ast/compiler/ets/invalid_param_pack.ets b/ets2panda/test/ast/compiler/ets/invalid_param_pack.ets index 0ef867033bb85c3592807d5eb5e3dc68aa48014a..0eb75d4c88d40c4716310db30770d423e247b612 100644 --- a/ets2panda/test/ast/compiler/ets/invalid_param_pack.ets +++ b/ets2panda/test/ast/compiler/ets/invalid_param_pack.ets @@ -37,8 +37,8 @@ class Derived2 extends Base2{ } } -/* @@? 17:17 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 17:17 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but '*ERROR_TYPE*' is provided. */ /* @@? 17:24 Error SyntaxError: Parameter declaration should have an explicit type annotation. */ -/* @@? 29:13 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 29:13 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but '*ERROR_TYPE*' is provided. */ /* @@? 29:20 Error SyntaxError: Parameter declaration should have an explicit type annotation. */ /* @@? 36:3 Error TypeError: Property 'my_func' must be accessed through 'this' */ diff --git a/ets2panda/test/ast/compiler/ets/null_pointer_error.ets b/ets2panda/test/ast/compiler/ets/null_pointer_error.ets index 4297f3c7fee22992dee5f2dd988bfc3f17f44834..b57521a84ff254e9c743e6c31ce804601dfd1641 100644 --- a/ets2panda/test/ast/compiler/ets/null_pointer_error.ets +++ b/ets2panda/test/ast/compiler/ets/null_pointer_error.ets @@ -23,6 +23,6 @@ export class AbstractDaoSession{ /* @@? 1:3 Error TypeError: Cannot find type 'dataRdb'. */ /* @@? 1:3 Error TypeError: 'ValueType' type does not exist. */ -/* @@? 17:62 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 17:62 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but '*ERROR_TYPE*' is provided. */ /* @@? 19:17 Error TypeError: Cannot find type 'AbstractDao'. */ /* @@? 20:16 Error TypeError: Type '*ERROR_TYPE*' can not be awaited, it is not a Promise. */ diff --git a/ets2panda/test/ast/compiler/ets/rest_compatible_type_check.ets b/ets2panda/test/ast/compiler/ets/rest_compatible_type_check.ets new file mode 100644 index 0000000000000000000000000000000000000000..d52c59a34943985798a6773b96110c7bdc7f2f1b --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/rest_compatible_type_check.ets @@ -0,0 +1,121 @@ +/* +* Copyright (c) 2025 Huawei Device Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable low or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + +/* + Test Description: Following codes are to check if + argument type is compatible with rest parameter in + function, method decleration. +*/ + +// type alias +type TEntity = Record + +// aliased itarable type check +function foo(... args: TEntity){ + let it = args.$_iterator() + let count = 0 + for(let i of it){ + count++ + } + return count +} + +// Record type check +function foo1(... args: Record): number { + let it = args.$_iterator() + let count = 0 + for(let i of it){ + count++ + } + return count +} + +// Set type check +function foo2(... args: Set): number { + let it = args.$_iterator() + let count = 0 + for(let i of it){ + count++ + } + return count +} + +// Aliased array type check +function foo3(... args: TEntity[]): number { + let it = args.$_iterator() + let count = 0 + for(let i of it){ + count++ + } + return count +} + +// fixed array type check +function foo4(... args: FixedArray){ + return args.length +} + +// array type check +function foo5(... args: double[]){ + return args.length +} + +// tuple type check +function foo6(... args: [string, number]){ + return args[0] +} + +// derived iterable class +class A implements Iterable { + public data: string[] = ['1','2','3'] + public $_iterator(): AIterator { + return new AIterator(this) + } + + // array type check for a class method + public foo7(... args: string[]){ + this.data[0] = args[0] + } + + // Record type check for a class method + public foo8(key: number, ... args: Record){ + return args[key] + } +} + +class AIterator implements Iterator { + index = 0 + base: A + constructor(base: A){ + this.base = base + } + + next() : IteratorResult { + return { + done: this.index >= this.base.data.length, + value: this.index >= this.base.data.length ? undefined: this.base.data[this.index++] + } + } +} + +// derived iterable class type check +function foo9(... args: A): boolean { + return true +} + +function main() { + let entity: TEntity = {"a":"b", "c":"d"} +} \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/restvar_type_infer.ets b/ets2panda/test/ast/compiler/ets/restvar_type_infer.ets index b24788ce952c404b8cba82b6471b3187d8868fe4..b0fdeab69b14cb74fbb391ffd607bd204dd38ff7 100644 --- a/ets2panda/test/ast/compiler/ets/restvar_type_infer.ets +++ b/ets2panda/test/ast/compiler/ets/restvar_type_infer.ets @@ -19,8 +19,8 @@ function main() { } /* @@? 17:14 Error TypeError: Cannot use type 'void' as value. */ -/* @@? 17:16 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 17:16 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but '*ERROR_TYPE*' is provided. */ /* @@? 17:16 Error TypeError: The type of parameter 'args' cannot be inferred */ /* @@? 18:14 Error TypeError: Cannot use type 'void' as value. */ -/* @@? 18:26 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 18:26 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but '*ERROR_TYPE*' is provided. */ /* @@? 18:26 Error TypeError: The type of parameter 'args' cannot be inferred */ diff --git a/ets2panda/test/ast/compiler/ets/type_error_test2.ets b/ets2panda/test/ast/compiler/ets/type_error_test2.ets index 08c72c921ab06fa7752e36ba967dfb6a2c01800c..9577786104413018765f7276c8e134f565b23136 100644 --- a/ets2panda/test/ast/compiler/ets/type_error_test2.ets +++ b/ets2panda/test/ast/compiler/ets/type_error_test2.ets @@ -15,7 +15,7 @@ let f:(c:string, ...abe])=>void = (c:be ...abe])=>{} -/* @@? 16:18 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 16:18 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but '*ERROR_TYPE*' is provided. */ /* @@? 16:24 Error SyntaxError: Parameter declaration should have an explicit type annotation. */ /* @@? 16:24 Error SyntaxError: Rest parameter must be the last formal parameter. */ /* @@? 16:35 Error TypeError: Type '(c: *ERROR_TYPE*) => void' cannot be assigned to type '(c: String, ...abe: *ERROR_TYPE*) => void' */ diff --git a/ets2panda/test/ast/parser/ets/FixedArray/MultipleParserErrors.ets b/ets2panda/test/ast/parser/ets/FixedArray/MultipleParserErrors.ets index 4b647af6a8464a13a0f8fc8a66329c457f7c0786..38a4bddf8ca829a30f4041ef8f464d7f6a1401ec 100644 --- a/ets2panda/test/ast/parser/ets/FixedArray/MultipleParserErrors.ets +++ b/ets2panda/test/ast/parser/ets/FixedArray/MultipleParserErrors.ets @@ -210,9 +210,9 @@ function main(): void { /* @@? 52:12 Error TypeError: All return statements in the function should be empty or have a value. */ /* @@? 52:12 Error TypeError: Unresolved reference q */ /* @@? 52:23 Error TypeError: Unresolved reference p */ -/* @@? 55:14 Error SyntaxError: Rest parameter should be either array or tuple type. */ -/* @@? 59:14 Error SyntaxError: Rest parameter should be either array or tuple type. */ -/* @@? 63:22 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 55:14 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but 'Object' is provided. */ +/* @@? 59:14 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but 'Int' is provided. */ +/* @@? 63:22 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but '*ERROR_TYPE*' is provided. */ /* @@? 63:26 Error SyntaxError: Parameter declaration should have an explicit type annotation. */ /* @@? 67:7 Error TypeError: Merging declarations is not supported, please keep all definitions of classes, interfaces and enums compact in the codebase! */ /* @@? 67:7 Error TypeError: Variable 'A' has already been declared. */ @@ -229,8 +229,8 @@ function main(): void { /* @@? 92:7 Error TypeError: Merging declarations is not supported, please keep all definitions of classes, interfaces and enums compact in the codebase! */ /* @@? 92:7 Error TypeError: Variable 'A' has already been declared. */ /* @@? 93:3 Error SyntaxError: Unexpected token. A constructor, method, accessor, or property was expected. */ -/* @@? 103:14 Error SyntaxError: Rest parameter should be either array or tuple type. */ -/* @@? 104:21 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 103:14 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but 'Byte' is provided. */ +/* @@? 104:21 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but 'Byte' is provided. */ /* @@? 115:26 Error SyntaxError: Parameter declaration should have an explicit type annotation. */ /* @@? 115:26 Error SyntaxError: Unexpected token, expected ',' or ')'. */ /* @@? 115:26 Error SyntaxError: Unexpected token 'case'. */ diff --git a/ets2panda/test/ast/parser/ets/FixedArray/unexpected_token_31.ets b/ets2panda/test/ast/parser/ets/FixedArray/unexpected_token_31.ets index 37de6fe998ab731177911580d49d617a788b01c8..834b2feb5a4c7beb8afb212f84ce75876e0fdb24 100644 --- a/ets2panda/test/ast/parser/ets/FixedArray/unexpected_token_31.ets +++ b/ets2panda/test/ast/parser/ets/FixedArray/unexpected_token_31.ets @@ -18,7 +18,7 @@ function foo(...^number: FixedArray): int { } /* @@? 16:10 Error TypeError: Only abstract or native methods can't have body. */ -/* @@? 16:14 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 16:14 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but '*ERROR_TYPE*' is provided. */ /* @@? 16:17 Error SyntaxError: Unexpected token, expected an identifier. */ /* @@? 16:18 Error SyntaxError: Parameter declaration should have an explicit type annotation. */ /* @@? 16:18 Error SyntaxError: Rest parameter must be the last formal parameter. */ diff --git a/ets2panda/test/ast/parser/ets/MultipleParserErrors.ets b/ets2panda/test/ast/parser/ets/MultipleParserErrors.ets index 257ff2bd18f6d3494ee020147d6c318bb66eeab7..7dcf7df61e4879514134da59bf10fdecccb69f11 100644 --- a/ets2panda/test/ast/parser/ets/MultipleParserErrors.ets +++ b/ets2panda/test/ast/parser/ets/MultipleParserErrors.ets @@ -207,9 +207,9 @@ function main(): void { /* @@? 52:12 Error TypeError: All return statements in the function should be empty or have a value. */ /* @@? 52:12 Error TypeError: Unresolved reference q */ /* @@? 52:23 Error TypeError: Unresolved reference p */ -/* @@? 55:14 Error SyntaxError: Rest parameter should be either array or tuple type. */ -/* @@? 59:14 Error SyntaxError: Rest parameter should be either array or tuple type. */ -/* @@? 63:22 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 55:14 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but 'Object' is provided. */ +/* @@? 59:14 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but 'Int' is provided. */ +/* @@? 63:22 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but '*ERROR_TYPE*' is provided. */ /* @@? 63:26 Error SyntaxError: Parameter declaration should have an explicit type annotation. */ /* @@? 67:7 Error TypeError: Merging declarations is not supported, please keep all definitions of classes, interfaces and enums compact in the codebase! */ /* @@? 67:7 Error TypeError: Variable 'A' has already been declared. */ @@ -226,8 +226,8 @@ function main(): void { /* @@? 92:7 Error TypeError: Variable 'A' has already been declared. */ /* @@? 92:7 Error TypeError: Merging declarations is not supported, please keep all definitions of classes, interfaces and enums compact in the codebase! */ /* @@? 93:3 Error SyntaxError: Unexpected token. A constructor, method, accessor, or property was expected. */ -/* @@? 103:14 Error SyntaxError: Rest parameter should be either array or tuple type. */ -/* @@? 104:21 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 103:14 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but 'Byte' is provided. */ +/* @@? 104:21 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but 'Byte' is provided. */ /* @@? 115:26 Error SyntaxError: Unexpected token, expected an identifier. */ /* @@? 115:26 Error SyntaxError: Parameter declaration should have an explicit type annotation. */ /* @@? 115:26 Error SyntaxError: Unexpected token, expected ',' or ')'. */ diff --git a/ets2panda/test/ast/parser/ets/annotations_tests/annotationUsage_bad_param07.ets b/ets2panda/test/ast/parser/ets/annotations_tests/annotationUsage_bad_param07.ets index d5077544840f5baec9e408280c5c401c4a4c1374..81a06a690b6d9304f477dcf3634db13e3abef84c 100644 --- a/ets2panda/test/ast/parser/ets/annotations_tests/annotationUsage_bad_param07.ets +++ b/ets2panda/test/ast/parser/ets/annotations_tests/annotationUsage_bad_param07.ets @@ -21,5 +21,5 @@ function foo(@MyAnno ...a){} /* @@? 22:15 Error TypeError: The required field 'testProperty1' must be specified. Fields without default values cannot be omitted. */ -/* @@? 22:22 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 22:22 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but '*ERROR_TYPE*' is provided. */ /* @@? 22:26 Error SyntaxError: Parameter declaration should have an explicit type annotation. */ diff --git a/ets2panda/test/ast/parser/ets/rest_parameter_14.ets b/ets2panda/test/ast/parser/ets/rest_parameter_14.ets index 73bbbb9eb079a4ae9c0701e4ac1a4f98d577a2e1..d548498baab74374bf1ecc249ea90b71e4e29c0c 100644 --- a/ets2panda/test/ast/parser/ets/rest_parameter_14.ets +++ b/ets2panda/test/ast/parser/ets/rest_parameter_14.ets @@ -21,5 +21,5 @@ class B { moo(/* @@ label2 */...i: A) {} } -/* @@@ label1 Error SyntaxError: Rest parameter should be either array or tuple type. */ -/* @@@ label2 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@@ label1 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but 'A' is provided. */ +/* @@@ label2 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but 'A' is provided. */ diff --git a/ets2panda/test/ast/parser/ets/unexpected_token_31.ets b/ets2panda/test/ast/parser/ets/unexpected_token_31.ets index 01374410c399cf735780f40ecf9857927f864423..38ef344262d618d90ec364f14973b0a2307c363a 100644 --- a/ets2panda/test/ast/parser/ets/unexpected_token_31.ets +++ b/ets2panda/test/ast/parser/ets/unexpected_token_31.ets @@ -18,7 +18,7 @@ function foo(...^number: int[]): int { } /* @@? 16:10 Error TypeError: Only abstract or native methods can't have body. */ -/* @@? 16:14 Error SyntaxError: Rest parameter should be either array or tuple type. */ +/* @@? 16:14 Error SyntaxError: Rest parameter can be applied only to array, tuple or any iterable type, but '*ERROR_TYPE*' is provided. */ /* @@? 16:17 Error SyntaxError: Unexpected token, expected an identifier. */ /* @@? 16:18 Error SyntaxError: Parameter declaration should have an explicit type annotation. */ /* @@? 16:18 Error SyntaxError: Rest parameter must be the last formal parameter. */ diff --git a/ets2panda/util/diagnostic/syntax.yaml b/ets2panda/util/diagnostic/syntax.yaml index 178787feee8c6a2d6b9dd71f1ac3e5cf33a9a079..50d71537459c734c3d4fcd22daad84a0829c3bd7 100644 --- a/ets2panda/util/diagnostic/syntax.yaml +++ b/ets2panda/util/diagnostic/syntax.yaml @@ -927,10 +927,6 @@ syntax: id: 181 message: "Only ambient modules can use quoted names." -- name: ONLY_ARRAY_OR_TUPLE_FOR_REST - id: 193 - message: "Rest parameter should be either array or tuple type." - - name: ONLY_SPREAD_AT_LAST_INDEX id: 143 message: "Only one spread type declaration allowed, at the last index." @@ -1051,6 +1047,10 @@ syntax: id: 15 message: "Both optional and rest parameters are not allowed in function's parameter list." +- name: REST_PARAMETER_OF_INVALID_TYPE + id: 193 + message: "Rest parameter can be applied only to array, tuple or any iterable type, but '{}' is provided." + - name: REST_PARAM_NOT_LAST id: 67 message: "Rest parameter must be the last formal parameter."