From 409c0959f476e1ede7b68884cc8ea3650d0befac Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 29 Jun 2026 14:41:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(type-check):=20=E6=B7=BB=E5=8A=A0=E5=AF=B9?= =?UTF-8?q?=E8=81=94=E5=90=88=E7=B1=BB=E5=9E=8B=E5=92=8C=E4=BA=A4=E9=9B=86?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E7=9A=84=E8=87=AA=E6=88=91=E7=88=B6=E9=9D=99?= =?UTF-8?q?=E6=80=81=E7=B1=BB=E5=9E=8B=E6=A3=80=E6=9F=A5=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现 self 类型作为联合类型成员的功能测试 - 实现 parent 类型作为联合类型成员的功能测试 - 添加 self 类型不能作为交集类型成员的错误检查 - 添加 parent 类型不能作为交集类型成员的错误检查 - 添加 static 类型不能作为交集类型成员的错误检查 - 重构类型检查可调用名称生成逻辑以支持类方法上下文 - 修复函数命名空间名称获取在类方法中的问题 --- phpunit/src/ClassTest.php | 39 ++++++++++++++++++++++++ src/Php/Generator/TypeCheckGenerator.php | 19 ++++++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/phpunit/src/ClassTest.php b/phpunit/src/ClassTest.php index b74146c2..9332bf01 100644 --- a/phpunit/src/ClassTest.php +++ b/phpunit/src/ClassTest.php @@ -26,4 +26,43 @@ class ClassTest extends \BaseTest { $this->exec('Cannot override private method `Base::doWork()`', 'override-private-method.php'); } + + public function testSelfCanBePartOfUnionType() + { + global $translator; + $compiler = \PhpAot\Php\CompilerTest::create(ROOT_PATH); + $translator = $compiler; + $testFile = __DIR__ . '/../code/union_type_self_allowed.php'; + $compiler->addFiles([$testFile]); + $compiler->prepareFile($testFile); + $compiler->convertFile($testFile); + $this->addToAssertionCount(1); + } + + public function testParentCanBePartOfUnionType() + { + global $translator; + $compiler = \PhpAot\Php\CompilerTest::create(ROOT_PATH); + $translator = $compiler; + $testFile = __DIR__ . '/../code/union_type_parent_allowed.php'; + $compiler->addFiles([$testFile]); + $compiler->prepareFile($testFile); + $compiler->convertFile($testFile); + $this->addToAssertionCount(1); + } + + public function testSelfCannotBePartOfIntersectionType() + { + $this->exec("Type 'self' cannot be part of an intersection type", 'intersection_type_self_not_allowed.php'); + } + + public function testParentCannotBePartOfIntersectionType() + { + $this->exec("Type 'parent' cannot be part of an intersection type", 'intersection_type_parent_not_allowed.php'); + } + + public function testStaticCannotBePartOfIntersectionType() + { + $this->exec("Type 'static' cannot be part of an intersection type", 'intersection_type_static_not_allowed.php'); + } } diff --git a/src/Php/Generator/TypeCheckGenerator.php b/src/Php/Generator/TypeCheckGenerator.php index c8bb9d09..eb706d49 100644 --- a/src/Php/Generator/TypeCheckGenerator.php +++ b/src/Php/Generator/TypeCheckGenerator.php @@ -37,6 +37,12 @@ trait TypeCheckGenerator $check[] = count($clause) === 1 ? $clause[0] : ['kind' => 'allOf', 'types' => $clause]; } } elseif ($typeNode instanceof IntersectionType) { + foreach ($typeNode->types as $subType) { + $nameLower = strtolower($this->parseIdentifier($subType)); + if ($nameLower === 'self' || $nameLower === 'parent' || $nameLower === 'static') { + $this->fatalError($subType, "Type '{$nameLower}' cannot be part of an intersection type"); + } + } $clause = $this->buildTypeCheckClause($typeNode); if (!empty($clause)) { $check[] = count($clause) === 1 ? $clause[0] : ['kind' => 'allOf', 'types' => $clause]; @@ -180,6 +186,15 @@ trait TypeCheckGenerator return '(' . implode(' && ', $conditions) . ')'; } + protected function getTypeCheckCallableName(): string + { + if ($this->classDef) { + return $this->classDef->getNamespacedName(false) . '::' . $this->functionDef->name; + } + + return $this->functionDef->getNamespacedName(); + } + protected function genUnionParamCheck(ArgInfo $argInfo, int $argIndex): string { if (empty($argInfo->typeCheck)) { @@ -251,7 +266,7 @@ trait TypeCheckGenerator protected function genUnionParamTypeErrorExpr(ArgInfo $argInfo, string $valueExpr, string $argNoExpr): string { - $fnName = $this->functionDef->getNamespacedName(); + $fnName = $this->getTypeCheckCallableName(); return 'php::concat({' . 'php::Str(' . $this->genCharPtr($fnName . '(): Argument #', true) . '), ' . 'php::toString(' . $argNoExpr . '), ' @@ -282,7 +297,7 @@ trait TypeCheckGenerator } $orExpr = implode(' || ', $conditions); - $fnName = $this->functionDef->getNamespacedName(); + $fnName = $this->getTypeCheckCallableName(); $typeStr = $this->functionDef->returnTypeStr; $msgExpr = 'php::concat(php::concat(php::Str(' . $this->genCharPtr($fnName, true) . ' "(): Return value must be of type " '