Skip to content

Commit

Permalink
Merge pull request #10344 from robchett/seal_method_seal_properties_w…
Browse files Browse the repository at this point in the history
…ithout_prefix

Support @(no-)seal-(properties|methods) annotations without prefix
  • Loading branch information
orklah committed Nov 4, 2023
2 parents f147344 + 54999ab commit d041b65
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 15 deletions.
8 changes: 4 additions & 4 deletions docs/annotating_code/supported_annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ takesFoo(getFoo());

This provides the same, but for `false`. Psalm uses this internally for functions like `preg_replace`, which can return false if the given input has encoding errors, but where 99.9% of the time the function operates as expected.

### `@psalm-seal-properties`, `@psalm-no-seal-properties`
### `@psalm-seal-properties`, `@psalm-no-seal-properties`, `@seal-properties`, `@no-seal-properties`

If you have a magic property getter/setter, you can use `@psalm-seal-properties` to instruct Psalm to disallow getting and setting any properties not contained in a list of `@property` (or `@property-read`/`@property-write`) annotations.
This is automatically enabled with the configuration option `sealAllProperties` and can be disabled for a class with `@psalm-no-seal-properties`
Expand All @@ -211,7 +211,7 @@ This is automatically enabled with the configuration option `sealAllProperties`
<?php
/**
* @property string $foo
* @psalm-seal-properties
* @seal-properties
*/
class A {
public function __get(string $name): ?string {
Expand All @@ -227,7 +227,7 @@ $a = new A();
$a->bar = 5; // this call fails
```

### `@psalm-seal-methods`, `@psalm-no-seal-methods`
### `@psalm-seal-methods`, `@psalm-no-seal-methods`, `@seal-methods`, `@no-seal-methods`

If you have a magic method caller, you can use `@psalm-seal-methods` to instruct Psalm to disallow calling any methods not contained in a list of `@method` annotations.
This is automatically enabled with the configuration option `sealAllMethods` and can be disabled for a class with `@psalm-no-seal-methods`
Expand All @@ -236,7 +236,7 @@ This is automatically enabled with the configuration option `sealAllMethods` and
<?php
/**
* @method foo(): string
* @psalm-seal-methods
* @seal-methods
*/
class A {
public function __call(string $name, array $args) {
Expand Down
24 changes: 13 additions & 11 deletions src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,18 +236,20 @@ public static function parse(
}
}

if (isset($parsed_docblock->tags['psalm-seal-properties'])) {
$info->sealed_properties = true;
}
if (isset($parsed_docblock->tags['psalm-no-seal-properties'])) {
$info->sealed_properties = false;
}
foreach (['', 'psalm-'] as $prefix) {
if (isset($parsed_docblock->tags[$prefix . 'seal-properties'])) {
$info->sealed_properties = true;
}
if (isset($parsed_docblock->tags[$prefix . 'no-seal-properties'])) {
$info->sealed_properties = false;
}

if (isset($parsed_docblock->tags['psalm-seal-methods'])) {
$info->sealed_methods = true;
}
if (isset($parsed_docblock->tags['psalm-no-seal-methods'])) {
$info->sealed_methods = false;
if (isset($parsed_docblock->tags[$prefix . 'seal-methods'])) {
$info->sealed_methods = true;
}
if (isset($parsed_docblock->tags[$prefix . 'no-seal-methods'])) {
$info->sealed_methods = false;
}
}

if (isset($parsed_docblock->tags['psalm-inheritors'])) {
Expand Down
15 changes: 15 additions & 0 deletions tests/MagicMethodAnnotationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,21 @@ class B extends A {}
$b->foo();',
'error_message' => 'UndefinedMagicMethod',
],
'inheritSealedMethodsWithoutPrefix' => [
'code' => '<?php
/**
* @seal-methods
*/
class A {
public function __call(string $method, array $args) {}
}
class B extends A {}
$b = new B();
$b->foo();',
'error_message' => 'UndefinedMagicMethod',
],
'lonelyMethod' => [
'code' => '<?php
/**
Expand Down
32 changes: 32 additions & 0 deletions tests/MagicPropertyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,38 @@ class A {
}',
'error_message' => 'InvalidDocblock',
],
'sealedWithNoProperties' => [
'code' => '<?php
/**
* @psalm-seal-properties
*/
final class OrganizationObject {
public function __get(string $key)
{
return [];
}
}
echo (new OrganizationObject)->errors;',
'error_message' => 'UndefinedMagicPropertyFetch',
],
'sealedWithNoPropertiesNoPrefix' => [
'code' => '<?php
/**
* @seal-properties
*/
final class OrganizationObject {
public function __get(string $key)
{
return [];
}
}
echo (new OrganizationObject)->errors;',
'error_message' => 'UndefinedMagicPropertyFetch',
],
];
}

Expand Down

0 comments on commit d041b65

Please sign in to comment.