diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 3aa4109c2eb8eb2d86c89482000a32026599fdd0..5028e1770eda1b56748edca36155deab4ec0f749 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -1516,6 +1516,11 @@ checker::Type *ETSAnalyzer::Check(ir::CallExpression *expr) const CheckCallee(checker, expr); + checker::TypeStackElement tse(checker, expr, {{diagnostic::CYCLIC_CALLEE, {}}}, expr->Start()); + if (tse.HasTypeError()) { + return checker->GlobalTypeError(); + } + checker::Type *const returnType = GetCallExpressionReturnType(expr, calleeType); expr->SetTsType(returnType); if (returnType->IsTypeError()) { diff --git a/ets2panda/checker/ets/utilityTypeHandlers.cpp b/ets2panda/checker/ets/utilityTypeHandlers.cpp index c267defa3c8f07b568c3720c739da8bc9d218139..1bfc781d84df6fcc344b577375408683c3aee19f 100644 --- a/ets2panda/checker/ets/utilityTypeHandlers.cpp +++ b/ets2panda/checker/ets/utilityTypeHandlers.cpp @@ -858,6 +858,10 @@ Type *ETSChecker::CreatePartialTypeClassDef(ir::ClassDefinition *const partialCl ? GlobalETSObjectType() : classDef->Super()->TsType()); + if (partialSuper == partialType) { + LogError(diagnostic::CYCLIC_CLASS_SUPER_TYPE, {}, classDef->Start()); + return partialType; + } partialType->SetSuperType(partialSuper->AsETSObjectType()); } diff --git a/ets2panda/compiler/lowering/ets/localClassLowering.cpp b/ets2panda/compiler/lowering/ets/localClassLowering.cpp index febb95ab47b37460f58f61183d64b182f764a3a8..53e9ecdb7110e1f53535add40eb311e072006289 100644 --- a/ets2panda/compiler/lowering/ets/localClassLowering.cpp +++ b/ets2panda/compiler/lowering/ets/localClassLowering.cpp @@ -199,7 +199,13 @@ void LocalClassConstructionPhase::RemapReferencesFromCapturedVariablesToClassPro if (it->IsMethodDefinition() && !it->AsMethodDefinition()->IsConstructor()) { LOG(DEBUG, ES2PANDA) << " - Rebinding variable rerferences in: " << it->AsMethodDefinition()->Id()->Name().Mutf8().c_str(); - it->AsMethodDefinition()->Function()->Body()->IterateRecursively(remapCapturedVariables); + if (it->AsMethodDefinition()->Function()->Body() == nullptr && + it->AsMethodDefinition()->AsyncPairMethod() != nullptr) { + it->AsMethodDefinition()->AsyncPairMethod()->Function()->Body()->IterateRecursively( + remapCapturedVariables); + } else { + it->AsMethodDefinition()->Function()->Body()->IterateRecursively(remapCapturedVariables); + } } } // Since the constructor with zero parameter is not listed in the class_def body the constructors diff --git a/ets2panda/ir/ets/etsKeyofType.cpp b/ets2panda/ir/ets/etsKeyofType.cpp index d266214e5bf495de704a4e7d552ea5450dedc295..6407914ab78eb420a69567514f91e8aa3da39a4b 100644 --- a/ets2panda/ir/ets/etsKeyofType.cpp +++ b/ets2panda/ir/ets/etsKeyofType.cpp @@ -75,6 +75,11 @@ checker::Type *ETSKeyofType::GetType(checker::ETSChecker *checker) return checker->GlobalTypeError(); } + checker::TypeStackElement tse(checker, GetHistoryNode(), {{diagnostic::CYCLIC_TYPE_OF, {}}}, Start()); + if (tse.HasTypeError()) { + return checker->GlobalTypeError(); + } + SetTsType(checker->CreateUnionFromKeyofType(typeReference->AsETSObjectType())); return TsType(); } diff --git a/ets2panda/ir/ets/etsTypeReferencePart.cpp b/ets2panda/ir/ets/etsTypeReferencePart.cpp index 9dbef072a275a2e41a81acddb471b649f2ec868a..8ebd5258b695592122eed98bf6dac3b2d4c308d6 100644 --- a/ets2panda/ir/ets/etsTypeReferencePart.cpp +++ b/ets2panda/ir/ets/etsTypeReferencePart.cpp @@ -173,6 +173,29 @@ static checker::Type *CheckPredefinedBuiltinTypes(checker::ETSChecker *const che return nullptr; } +[[maybe_unused]] static bool CheckTypeAliaLoop(ETSTypeReferencePart *ref, varbinder::Variable *variable) +{ + auto typeAliasDecl = variable->Declaration()->Node()->AsTSTypeAliasDeclaration(); + auto typeDeclaration = typeAliasDecl->TypeParams(); + if (typeDeclaration == nullptr) { + return false; + } + + for (auto *param : typeDeclaration->Params()) { + auto constraint = param->Constraint(); + if (constraint == nullptr || !constraint->IsETSTypeReference()) { + continue; + } + + auto part = constraint->AsETSTypeReference()->Part(); + if (part == ref) { + return true; + } + } + + return false; +} + checker::Type *ETSTypeReferencePart::HandleInternalTypes(checker::ETSChecker *const checker) { auto const name = Name(); @@ -193,6 +216,10 @@ checker::Type *ETSTypeReferencePart::HandleInternalTypes(checker::ETSChecker *co } if (variable != nullptr && variable->Declaration()->IsTypeAliasDecl()) { + if (CheckTypeAliaLoop(this, variable)) { + checker->LogError(diagnostic::CYCLIC_ALIAS, {}, Start()); + return checker->GlobalTypeError(); + } return checker->HandleTypeAlias(name, TypeParams(), variable->Declaration()->AsTypeAliasDecl()->Node()->AsTSTypeAliasDeclaration()); } diff --git a/ets2panda/test/ast/parser/ets/circular_class_extends.ets b/ets2panda/test/ast/parser/ets/circular_class_extends.ets new file mode 100644 index 0000000000000000000000000000000000000000..4cac2e3719f03516fa733c496a5644d10b11e338 --- /dev/null +++ b/ets2panda/test/ast/parser/ets/circular_class_extends.ets @@ -0,0 +1,27 @@ +/* + * 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. + */ + +namespace m { + export class c { + } +} + +// Instead of assigning namespace to variable, export the namespace directly +export { m }; + +// If you want to re-export the class from the namespace: +export class c extends m.c {} + +/* @@? 25:28 Error TypeError: Class's super type is itself */ diff --git a/ets2panda/test/ast/parser/ets/circular_in_class_functions.ets b/ets2panda/test/ast/parser/ets/circular_in_class_functions.ets new file mode 100644 index 0000000000000000000000000000000000000000..d128f8bccee0a32e9b22da38a5be5a09aa1c7e0b --- /dev/null +++ b/ets2panda/test/ast/parser/ets/circular_in_class_functions.ets @@ -0,0 +1,33 @@ +/* + * 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 Test { + foo() {} + + bar() { + this.baz(this, "*1*"); + + const t = new Test() + this.baz(t, "*2*"); + } + + baz(a: Test, k: keyof Test) {} +} + +/* @@? 20:9 Error TypeError: No matching call signature for baz(Test, "*1*") */ +/* @@? 20:24 Error TypeError: Type '"*1*"' is not compatible with type '"foo"|"bar"|"baz"' at index 2 */ +/* @@? 23:9 Error TypeError: No matching call signature for baz(Test, "*2*") */ +/* @@? 23:21 Error TypeError: Type '"*2*"' is not compatible with type '"foo"|"bar"|"baz"' at index 2 */ +/* @@? 26:31 Error TypeError: Circular type of reference */ diff --git a/ets2panda/test/ast/parser/ets/circular_type_in_alias.ets b/ets2panda/test/ast/parser/ets/circular_type_in_alias.ets new file mode 100644 index 0000000000000000000000000000000000000000..920263f091680b1a7e4356a37f2f30b36b682b12 --- /dev/null +++ b/ets2panda/test/ast/parser/ets/circular_type_in_alias.ets @@ -0,0 +1,38 @@ +/* + * 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. + */ + +type Loop> = { + [P in keyof T]: U[P] extends boolean ? number : string; +}; + +/* @@? 16:24 Error TypeError: Circular type alias reference */ +/* @@? 16:38 Error SyntaxError: Invalid Type. */ +/* @@? 17:6 Error TypeError: Unresolved reference P */ +/* @@? 17:8 Error SyntaxError: Unexpected token, expected ',' or ']'. */ +/* @@? 17:8 Error SyntaxError: Unexpected token 'in'. */ +/* @@? 17:8 Error TypeError: Unresolved reference in */ +/* @@? 17:11 Error SyntaxError: Unexpected token 'keyof'. */ +/* @@? 17:11 Error TypeError: Unresolved reference keyof */ +/* @@? 17:17 Error SyntaxError: Unexpected token 'T'. */ +/* @@? 17:17 Error TypeError: Unresolved reference T */ +/* @@? 17:18 Error SyntaxError: Unexpected token ']'. */ +/* @@? 17:19 Error SyntaxError: Unexpected token ':'. */ +/* @@? 17:21 Error SyntaxError: Unexpected token 'U'. */ +/* @@? 17:21 Error TypeError: Unresolved reference U */ +/* @@? 17:21 Error TypeError: Indexed access is not supported for such expression type. */ +/* @@? 17:26 Error SyntaxError: Unexpected token 'extends'. */ +/* @@? 17:34 Error SyntaxError: Unexpected token 'boolean'. */ +/* @@? 17:44 Error TypeError: Type name 'number' used in the wrong context */ +/* @@? 17:53 Error TypeError: Type name 'string' used in the wrong context */ diff --git a/ets2panda/util/diagnostic/semantic.yaml b/ets2panda/util/diagnostic/semantic.yaml index 568a3b73128a4625368f0bb392f21c0762ebe352..5fd093da74fe192bfe450b01ec78c363d13a63b4 100644 --- a/ets2panda/util/diagnostic/semantic.yaml +++ b/ets2panda/util/diagnostic/semantic.yaml @@ -1496,5 +1496,17 @@ semantic: message: "Class field '{}' defined by the parent class is not accessible in the child class via super." - name: INTERFACE_EXTENDS_CLASS - id: 373 + id: 378 message: "Interfaces cannot extend classes, only other interfaces." + +- name: CYCLIC_TYPE_OF + id: 379 + message: "Circular type of reference" + +- name: CYCLIC_CLASS_SUPER_TYPE + id: 380 + message: "Class's super type is itself" + +- name: CYCLIC_CALLEE + id: 381 + message: "Circular call function"