refactor(constructor): enhance constructor property validation with existing method checks

- Add detection of pre-existing __construct methods in class
- Throw CompileTimeAttributeError when constructor property conflicts with existing method
- Update test to use proper exception expectation pattern
- Add new test case for parameter order independence
- Create helper method to find declared constructors in class statements
- Improve error messaging with specific class and method information
pull/34/head
韩天峰 1 month ago
parent 89799b9b8c
commit 2287695b44
  1. 16
      phpunit/code/constructor-existing-reordered.php
  2. 15
      phpunit/src/ClassTest.php
  3. 19
      src/Transform/ConstructorLowering.php

@ -0,0 +1,16 @@
<?php
class ReorderedConstructor
{
#[Constructor]
private string $name;
#[Constructor]
private int $id;
public function __construct(int $id, string $name)
{
$this->id = $id;
$this->name = $name;
}
}

@ -523,7 +523,20 @@ class ClassTest extends \BaseTest
public function testConstructorRejectsExistingConstructor(): void
{
$this->exec('Duplicate method `__construct`', 'constructor-existing.php');
$this->expectException(\TypePhp\Exception\SyntaxError::class);
$this->expectExceptionMessage(
'Constructor cannot generate InvalidConstructor::__construct(): method is already declared',
);
$this->compile('constructor-existing.php');
}
public function testConstructorRejectsExistingConstructorRegardlessOfParameterOrder(): void
{
$this->expectException(\TypePhp\Exception\SyntaxError::class);
$this->expectExceptionMessage(
'Constructor cannot generate ReorderedConstructor::__construct(): method is already declared',
);
$this->compile('constructor-existing-reordered.php');
}
public function testConstructorRejectsStaticProperties(): void

@ -33,6 +33,14 @@ final class ConstructorLowering
public static function lowerClassLike(Stmt\Class_|Stmt\Trait_|Stmt\Enum_ $class): void
{
$declaredConstructor = null;
foreach ($class->stmts as $stmt) {
if ($stmt instanceof Stmt\ClassMethod && $stmt->name->toLowerString() === '__construct') {
$declaredConstructor = $stmt;
break;
}
}
$properties = [];
$target = null;
foreach ($class->stmts as $stmt) {
@ -43,6 +51,17 @@ final class ConstructorLowering
throw new SyntaxError('Constructor properties can only be declared in classes');
}
$attribute = CompileTimeAttribute::find($stmt, 'Constructor');
if ($declaredConstructor !== null) {
$className = $class->name?->toString() ?? 'anonymous class';
throw new CompileTimeAttributeError(
"Constructor cannot generate {$className}::__construct(): method is already declared",
$stmt,
'Constructor',
$attribute ?? $stmt,
null,
$declaredConstructor,
);
}
CompileTimeAttribute::consume($stmt, 'Constructor');
$target ??= $stmt;
foreach ($stmt->props as $property) {

Loading…
Cancel
Save