mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +01:00
Support @property-read and @property-write as new tags. (#317)
In combination with `@psalm-seal-properties`, this can be used to have in-depth checking of magic properties.
This commit is contained in:
parent
52c414f1d2
commit
136d48f77c
@ -307,9 +307,24 @@ class CommentChecker
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($comments['specials']['property'])) {
|
||||
/** @var string $property */
|
||||
foreach ($comments['specials']['property'] as $line_number => $property) {
|
||||
self::addMagicPropertyToInfo($info, $comments['specials'], 'property');
|
||||
self::addMagicPropertyToInfo($info, $comments['specials'], 'property-read');
|
||||
self::addMagicPropertyToInfo($info, $comments['specials'], 'property-write');
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassLikeDocblockComment $info
|
||||
* @param array<string,array<mixed,string>> $specials
|
||||
* @param string $property_tag ('property', 'property-read', or 'property-write')
|
||||
* @return void
|
||||
* @throws DocblockParseException
|
||||
*/
|
||||
protected static function addMagicPropertyToInfo(ClassLikeDocblockComment $info, array $specials, $property_tag)
|
||||
{
|
||||
$magic_property_comments = isset($specials[$property_tag]) ? $specials[$property_tag] : [];
|
||||
foreach ($magic_property_comments as $line_number => $property) {
|
||||
try {
|
||||
$line_parts = self::splitDocLine($property);
|
||||
} catch (DocblockParseException $e) {
|
||||
@ -340,18 +355,18 @@ class CommentChecker
|
||||
$info->properties[] = [
|
||||
'name' => $line_parts[1],
|
||||
'type' => $line_parts[0],
|
||||
'line_number' => (int)$line_number,
|
||||
'line_number' => $line_number,
|
||||
'tag' => $property_tag,
|
||||
];
|
||||
} else {
|
||||
throw new DocblockParseException('Badly-formatted @property');
|
||||
}
|
||||
} else {
|
||||
throw new DocblockParseException('Badly-formatted @param');
|
||||
throw new DocblockParseException('Badly-formatted @property');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $return_block
|
||||
*
|
||||
|
@ -16,7 +16,7 @@ class ClassLikeDocblockComment
|
||||
public $template_types = [];
|
||||
|
||||
/**
|
||||
* @var array<int, array{name:string, type:string, line_number: int}>
|
||||
* @var array<int, array{name:string, type:string, tag:string, line_number:int}>
|
||||
*/
|
||||
public $properties = [];
|
||||
|
||||
|
@ -222,10 +222,14 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P
|
||||
$pseudo_property_type = Type::parseString($property['type']);
|
||||
$pseudo_property_type->setFromDocblock();
|
||||
|
||||
if ($property['tag'] !== 'property-read') {
|
||||
$storage->pseudo_property_set_types[$property['name']] = $pseudo_property_type;
|
||||
}
|
||||
if ($property['tag'] !== 'property-write') {
|
||||
$storage->pseudo_property_get_types[$property['name']] = $pseudo_property_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$storage->deprecated = $docblock_info->deprecated;
|
||||
|
||||
|
@ -445,6 +445,27 @@ class AnnotationTest extends TestCase
|
||||
$a->foo = 5;',
|
||||
'error_message' => 'InvalidPropertyAssignment',
|
||||
],
|
||||
'propertyWriteDocblockInvalidAssignment' => [
|
||||
'<?php
|
||||
/**
|
||||
* @property-write string $foo
|
||||
*/
|
||||
class A {
|
||||
public function __get(string $name) : ?string {
|
||||
if ($name === "foo") {
|
||||
return "hello";
|
||||
}
|
||||
}
|
||||
|
||||
/** @param mixed $value */
|
||||
public function __set(string $name, $value) : void {
|
||||
}
|
||||
}
|
||||
|
||||
$a = new A();
|
||||
$a->foo = 5;',
|
||||
'error_message' => 'InvalidPropertyAssignment',
|
||||
],
|
||||
'propertySealedDocblockUndefinedPropertyAssignment' => [
|
||||
'<?php
|
||||
/**
|
||||
@ -489,6 +510,24 @@ class AnnotationTest extends TestCase
|
||||
$a->foo = 5;',
|
||||
'error_message' => 'InvalidPropertyAssignment',
|
||||
],
|
||||
'propertyReadInvalidFetch' => [
|
||||
'<?php
|
||||
/**
|
||||
* @property-read string $foo
|
||||
*/
|
||||
class A {
|
||||
/** @return mixed */
|
||||
public function __get(string $name) {
|
||||
if ($name === "foo") {
|
||||
return "hello";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$a = new A();
|
||||
echo count($a->foo);',
|
||||
'error_message' => 'InvalidArgument',
|
||||
],
|
||||
'propertySealedDocblockUndefinedPropertyFetch' => [
|
||||
'<?php
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user