feat(type-check): 添加对联合类型和交集类型的自我父静态类型检查支持

- 实现 self 类型作为联合类型成员的功能测试
- 实现 parent 类型作为联合类型成员的功能测试
- 添加 self 类型不能作为交集类型成员的错误检查
- 添加 parent 类型不能作为交集类型成员的错误检查
- 添加 static 类型不能作为交集类型成员的错误检查
- 重构类型检查可调用名称生成逻辑以支持类方法上下文
- 修复函数命名空间名称获取在类方法中的问题
pull/5/head
韩天峰 2 months ago
parent a0c9591e81
commit 409c0959f4
  1. 39
      phpunit/src/ClassTest.php
  2. 19
      src/Php/Generator/TypeCheckGenerator.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');
}
}

@ -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 " '

Loading…
Cancel
Save