diff --git a/ChangeLog.md b/ChangeLog.md index ce98002c..7425109f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,9 @@ XP Compiler ChangeLog ## ?.?.? / ????-??-?? +* Fixed `private(set)` not being implicitely marked as *final*, see + https://wiki.php.net/rfc/asymmetric-visibility-v2#inheritance + (@thekid) * Fixed enclosing scopes when using references - @thekid ## 9.3.0 / 2024-08-31 diff --git a/src/main/php/lang/ast/emit/AsymmetricVisibility.class.php b/src/main/php/lang/ast/emit/AsymmetricVisibility.class.php index 9bf53ce0..721c4c3c 100755 --- a/src/main/php/lang/ast/emit/AsymmetricVisibility.class.php +++ b/src/main/php/lang/ast/emit/AsymmetricVisibility.class.php @@ -50,6 +50,7 @@ protected function emitProperty($result, $property) { } else if ($modifiers & 0x0001000) { $checks= [$this->private($property->name, 'modify private(set)')]; $modifiers & MODIFIER_PRIVATE && $modifiers&= ~0x0001000; + $modifiers|= MODIFIER_FINAL; } // Emit XP meta information for the reflection API diff --git a/src/test/php/lang/ast/unittest/emit/AsymmetricVisibilityTest.class.php b/src/test/php/lang/ast/unittest/emit/AsymmetricVisibilityTest.class.php index 1007425e..9de9d07c 100755 --- a/src/test/php/lang/ast/unittest/emit/AsymmetricVisibilityTest.class.php +++ b/src/test/php/lang/ast/unittest/emit/AsymmetricVisibilityTest.class.php @@ -100,19 +100,31 @@ public function rename() { $t->newInstance()->rename(); } - #[Test, Values(['private', 'protected'])] - public function reflection($modifier) { + #[Test] + public function protected_set_reflection() { $t= $this->declare('class %T { - public '.$modifier.'(set) string $fixture= "Test"; + public protected(set) string $fixture= "Test"; }'); Assert::equals( - 'public '.$modifier.'(set) string $fixture', + 'public protected(set) string $fixture', $t->property('fixture')->toString() ); } - #[Test, Values(['private', 'protected', 'public'])] + #[Test] + public function private_set_implicitely_final_in_reflection() { + $t= $this->declare('class %T { + public private(set) string $fixture= "Test"; + }'); + + Assert::equals( + 'public final private(set) string $fixture', + $t->property('fixture')->toString() + ); + } + + #[Test, Values(['protected', 'public'])] public function same_modifier_for_get_and_set($modifier) { $t= $this->declare('class %T { '.$modifier.' '.$modifier.'(set) string $fixture= "Test"; @@ -124,6 +136,18 @@ public function same_modifier_for_get_and_set($modifier) { ); } + #[Test] + public function private_modifier_for_get_and_set() { + $t= $this->declare('class %T { + private private(set) string $fixture= "Test"; + }'); + + Assert::equals( + 'private final string $fixture', + $t->property('fixture')->toString() + ); + } + #[Test] public function interaction_with_hooks() { $t= $this->declare('class %T {