diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 3aa4109c2eb8eb2d86c89482000a32026599fdd0..2f4e8ae2ae1aa4825d1601d51e313f6f94e6e078 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -1671,6 +1671,11 @@ static Type *TransformTypeForMethodReference(ETSChecker *checker, ir::Expression checker->LogError(diagnostic::OVERLOADED_METHOD_AS_VALUE, getUseSite()); return checker->GlobalTypeError(); } + + if (signatures.empty()) { + checker->LogError(diagnostic::NO_CALL_SIGNATURE, {"function"}, getUseSite()); + return checker->GlobalTypeError(); + } return functionType->MethodToArrow(checker); } diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 553abd66e2047e601d4e4440b62db65fc8eadec5..adce2577b10f1a2940588833e75554c2fe09157b 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -1492,8 +1492,9 @@ static void CollectAliasParametersForBoxing(Type *expandedAliasType, std::setIsETSObjectType()) { auto objectType = expandedAliasType->AsETSObjectType(); - needToBeBoxed = - objectType->GetDeclNode()->IsClassDefinition() || objectType->GetDeclNode()->IsTSInterfaceDeclaration(); + auto objectTypeNode = objectType->GetDeclNode(); + needToBeBoxed = objectTypeNode != nullptr && + (objectTypeNode->IsClassDefinition() || objectTypeNode->IsTSInterfaceDeclaration()); for (const auto typeArgument : objectType->TypeArguments()) { CollectAliasParametersForBoxing(typeArgument, parametersNeedToBeBoxed, needToBeBoxed); } @@ -2464,6 +2465,10 @@ void ETSChecker::InferTypesForLambda(ir::ScriptFunction *lambda, ir::ETSFunction Signature *maybeSubstitutedFunctionSig) { for (size_t i = 0; i < lambda->Params().size(); ++i) { + if (!lambda->Params().at(i)->IsETSParameterExpression()) { + LogError(diagnostic::NO_SUCH_SIG_WITH_TRAILING_LAMBDA, {}, lambda->Params().at(i)->Start()); + return; + } auto *const lambdaParam = lambda->Params().at(i)->AsETSParameterExpression()->Ident(); if (lambdaParam->TypeAnnotation() == nullptr) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) diff --git a/ets2panda/checker/ets/utilityTypeHandlers.cpp b/ets2panda/checker/ets/utilityTypeHandlers.cpp index c267defa3c8f07b568c3720c739da8bc9d218139..14129de4c3822f79870165dda04861dffbce7202 100644 --- a/ets2panda/checker/ets/utilityTypeHandlers.cpp +++ b/ets2panda/checker/ets/utilityTypeHandlers.cpp @@ -132,6 +132,10 @@ Type *ETSChecker::CreatePartialType(Type *const typeToBePartial) return HandleUnionForPartialType(typeToBePartial->AsETSUnionType()); } + if (typeToBePartial->IsTypeError()) { + return typeToBePartial; + } + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) return CreatePartialTypeClass(typeToBePartial->AsETSObjectType(), typeToBePartial->Variable()->Declaration()->Node()); diff --git a/ets2panda/ir/statements/classDeclaration.cpp b/ets2panda/ir/statements/classDeclaration.cpp index 398e74f231c4a3fad40511c8d6f3c13d1b066703..0659ad3742e77304504a2cfb571a6024bc3ec136 100644 --- a/ets2panda/ir/statements/classDeclaration.cpp +++ b/ets2panda/ir/statements/classDeclaration.cpp @@ -133,4 +133,20 @@ checker::VerifiedType ClassDeclaration::Check(checker::ETSChecker *checker) { return {this, checker->GetAnalyzer()->Check(this)}; } + +ClassDeclaration *ClassDeclaration::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + auto *const clone = allocator->New(def_, allocator); + ArenaVector decorators(allocator->Adapter()); + for (auto *decorator : Decorators()) { + decorators.emplace_back(decorator->Clone(allocator, clone)->AsDecorator()); + } + clone->decorators_ = std::move(decorators); + if (parent != nullptr) { + clone->SetParent(parent); + } + clone->SetRange(range_); + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/statements/classDeclaration.h b/ets2panda/ir/statements/classDeclaration.h index 5ea6d0e8d01f3ca3d9c240541138d807d62e1308..ab3ed24a51f0b73534aee72efc3ea05e48527a78 100644 --- a/ets2panda/ir/statements/classDeclaration.h +++ b/ets2panda/ir/statements/classDeclaration.h @@ -86,6 +86,8 @@ public: void SetDefinition(ClassDefinition *def); + [[nodiscard]] ClassDeclaration *Clone(ArenaAllocator *allocator, AstNode *parent) override; + protected: explicit ClassDeclaration(AstNodeType type, ClassDefinition *const def, ArenaAllocator *const allocator) : Statement(type), def_(def), decorators_(allocator->Adapter()) diff --git a/ets2panda/ir/statements/forOfStatement.cpp b/ets2panda/ir/statements/forOfStatement.cpp index 953d2822686fed5db2a26f1b66b7ea3dd97210e8..c1c8d1f6cc842dde11061722b9357171ffca7a25 100644 --- a/ets2panda/ir/statements/forOfStatement.cpp +++ b/ets2panda/ir/statements/forOfStatement.cpp @@ -29,7 +29,11 @@ checker::Type *ForOfStatement::CreateUnionIteratorTypes(checker::ETSChecker *che if (it->IsETSStringType()) { types.emplace_back(checker->GlobalCharBuiltinType()); } else if (it->IsETSObjectType()) { - types.emplace_back(this->CheckIteratorMethodForObject(checker, it->AsETSObjectType())); + auto *const objType = CheckIteratorMethodForObject(checker, it->AsETSObjectType()); + if (objType == nullptr) { + return nullptr; + } + types.emplace_back(objType); } else if (it->IsETSArrayType()) { types.emplace_back(it->AsETSArrayType()->ElementType()->Clone(checker)); types.back()->RemoveTypeFlag(checker::TypeFlag::CONSTANT); diff --git a/ets2panda/test/ast/parser/ets/invalid_of_testcase.ets b/ets2panda/test/ast/parser/ets/invalid_of_testcase.ets new file mode 100644 index 0000000000000000000000000000000000000000..56cb4703688c4aaadc7559fb82260b01a782c63b --- /dev/null +++ b/ets2panda/test/ast/parser/ets/invalid_of_testcase.ets @@ -0,0 +1,35 @@ +/* + * 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 law 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. + */ + +function processValue(value: string | number): void { + if (typeof value === 'string') { + for (const char of value) { + console.log(char); + } + } else { + console.log(value.toString()); + } +} + +const testValues: (string | number)[] = ['hello', 42, 'world', 7]; + +for (const item of testValues) { + processValue(item); +} + +/* @@? 18:16 Error SyntaxError: Identifier expected, got 'char'. */ +/* @@? 18:24 Error TypeError: Object type doesn't have proper iterator method. */ +/* @@? 18:24 Error TypeError: 'For-of' statement source expression is not of iterable type. */ +/* @@? 19:19 Error SyntaxError: Unexpected token 'char'. */ diff --git a/ets2panda/test/ast/parser/ets/lambda_param_array.ets b/ets2panda/test/ast/parser/ets/lambda_param_array.ets new file mode 100644 index 0000000000000000000000000000000000000000..51caba27abf97084ffc6ab6b63855e8d21b8e5ff --- /dev/null +++ b/ets2panda/test/ast/parser/ets/lambda_param_array.ets @@ -0,0 +1,31 @@ +/* + * 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 law 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. + */ + +interface ResizeObserverEntry {} +class ResizeObserver { + constructor(callback: (entries: ResizeObserverEntry[]) => void) {} + disconnect(): void {} + observe(target: Element): void {} + unobserve(target: Element): void {} +} + +const resizeObserver = new ResizeObserver(([entry]) => { + // Dummy usage +}); + +/* @@? 20:21 Error TypeError: Cannot find type 'Element'. */ +/* @@? 21:23 Error TypeError: Cannot find type 'Element'. */ +/* @@? 24:43 Error TypeError: No matching call signature with trailing lambda */ +/* @@? 24:53 Error SyntaxError: Unexpected token '=>'. */ diff --git a/ets2panda/test/ast/parser/ets/record_generic_error.ets b/ets2panda/test/ast/parser/ets/record_generic_error.ets new file mode 100644 index 0000000000000000000000000000000000000000..39cbe808a713a92a8e22b1879a3c9896bf3e7dd2 --- /dev/null +++ b/ets2panda/test/ast/parser/ets/record_generic_error.ets @@ -0,0 +1,73 @@ +/* + * 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 law 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. + */ + +class C { + x: string = ""; +} + +class D { + x: string = ""; +} + +// Define the assign function with compatible types +function assign>(target: T, source: Partial): void { + Object.assign(target as Record, source as Record); +} + +// Replace BaseObject with Record usage +type BaseObject = Record; + +class X { + x: T; + + constructor(init?: Partial) { + // Initialize x with an empty object or a partial initialization + this.x = {} as T; + if (init) { + assign(this.x, init); // Explicitly specify generic type T + } + } +} + +function foo(t: X, t2: X): Record { + // Manually merge fields from both objects + const merged: Record = {}; + const obj1: Record = t.x as Record; + const obj2: Record = t2.x as Record; + + // Shallow copy properties from obj1 + Object.keys(obj1).forEach((key: string) => { + merged[key] = obj1[key]; + }); + + // Shallow copy properties from obj2 + Object.keys(obj2).forEach((key: string) => { + merged[key] = obj2[key]; + }); + + return merged; +} + +/* @@? 25:42 Error TypeError: Cannot find type 'any'. */ +/* @@? 26:3 Error TypeError: No matching call signature for assign(Record, Record) */ +/* @@? 26:17 Error TypeError: Type 'Record' is not compatible with type 'Record' at index 1 */ +/* @@? 30:34 Error TypeError: Cannot find type 'any'. */ +/* @@? 37:14 Error TypeError: Target type for class composite needs to be an object type, found 'T' */ +/* @@? 52:5 Error TypeError: No matching indexing signature for $_set(String, Object|undefined) */ +/* @@? 52:12 Error TypeError: Cannot find index access method with the required signature. */ +/* @@? 52:19 Error TypeError: Type 'Object|undefined' is not compatible with type 'Object' at index 2 */ +/* @@? 57:5 Error TypeError: No matching indexing signature for $_set(String, Object|undefined) */ +/* @@? 57:12 Error TypeError: Cannot find index access method with the required signature. */ +/* @@? 57:19 Error TypeError: Type 'Object|undefined' is not compatible with type 'Object' at index 2 */