1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-27 04:45:20 +01:00

Merge pull request #6764 from orklah/promoted-property-docblock

allow documenting promoted properties with @var
This commit is contained in:
orklah 2021-10-28 22:19:42 +02:00 committed by GitHub
commit 61a7cbe9c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 5 deletions

View File

@ -14,6 +14,7 @@ use Psalm\Exception\DocblockParseException;
use Psalm\Exception\IncorrectDocblockException;
use Psalm\Internal\Algebra\FormulaGenerator;
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
use Psalm\Internal\Analyzer\CommentAnalyzer;
use Psalm\Internal\Analyzer\NamespaceAnalyzer;
use Psalm\Internal\Analyzer\Statements\Expression\SimpleTypeInferer;
use Psalm\Internal\Scanner\FileScanner;
@ -32,6 +33,7 @@ use Psalm\Storage\FunctionStorage;
use Psalm\Storage\MethodStorage;
use Psalm\Storage\PropertyStorage;
use Psalm\Type;
use UnexpectedValueException;
use function array_pop;
use function count;
@ -404,7 +406,7 @@ class FunctionLikeNodeScanner
if ($parser_return_type) {
$original_type = $parser_return_type;
if ($original_type instanceof PhpParser\Node\IntersectionType) {
throw new \UnexpectedValueException('Intersection types not yet supported');
throw new UnexpectedValueException('Intersection types not yet supported');
}
/** @var Identifier|Name|NullableType|UnionType $original_type */
@ -585,9 +587,45 @@ class FunctionLikeNodeScanner
continue;
}
$doc_comment = $param->getDocComment();
$var_comment_type = null;
if ($doc_comment) {
$var_comments = CommentAnalyzer::getTypeFromComment(
$doc_comment,
$this->file_scanner,
$this->aliases,
$this->existing_function_template_types ?: [],
$this->type_aliases
);
$var_comment = array_pop($var_comments);
if ($var_comment !== null) {
$var_comment_type = $var_comment->type;
}
}
//both way to document type were used
if ($param_storage->type && $var_comment_type) {
if (IssueBuffer::accepts(
new InvalidDocblock(
'Param ' . $param_storage->name . ' of ' . $cased_function_id .
' should be documented as a param or a property, not both',
new CodeLocation($this->file_scanner, $param, null, true)
)
)) {
return false;
}
}
//no docblock type was provided for param but we have one for property
if ($param_storage->type === null && $var_comment_type) {
$param_storage->type = $var_comment_type;
}
$property_storage = $classlike_storage->properties[$param_storage->name] = new PropertyStorage();
$property_storage->is_static = false;
$property_storage->type = $param_storage->type;
$property_storage->type = $param_storage->type ?? $var_comment_type;
$property_storage->signature_type = $param_storage->signature_type;
$property_storage->signature_type_location = $param_storage->signature_type_location;
$property_storage->type_location = $param_storage->type_location;
@ -762,7 +800,7 @@ class FunctionLikeNodeScanner
if ($param_typehint) {
if ($param_typehint instanceof PhpParser\Node\IntersectionType) {
throw new \UnexpectedValueException('Intersection types not yet supported');
throw new UnexpectedValueException('Intersection types not yet supported');
}
/** @var Identifier|Name|NullableType|UnionType $param_typehint */
@ -786,7 +824,7 @@ class FunctionLikeNodeScanner
$is_optional = $param->default !== null;
if ($param->var instanceof PhpParser\Node\Expr\Error || !is_string($param->var->name)) {
throw new \UnexpectedValueException('Not expecting param name to be non-string');
throw new UnexpectedValueException('Not expecting param name to be non-string');
}
$default_type = null;
@ -1074,7 +1112,7 @@ class FunctionLikeNodeScanner
}
}
} else {
throw new \UnexpectedValueException('Unrecognized functionlike');
throw new UnexpectedValueException('Unrecognized functionlike');
}
return [

View File

@ -1303,6 +1303,22 @@ class AnnotationTest extends TestCase
$_b = new A();
echo (string)($a->getConfig()[0]??"");'
],
'promotedPropertiesDocumentationEitherForParamOrForProperty' => [
'<?php
final class UserRole
{
/** @psalm-param stdClass $id */
public function __construct(
protected $id,
/** @psalm-var stdClass */
protected $id2
) {
}
}
new UserRole(new stdClass(), new stdClass());
'
],
];
}
@ -1882,6 +1898,52 @@ class AnnotationTest extends TestCase
",
'error_message' => 'InvalidDocblock',
],
'promotedPropertiesDocumentationFailsWhenSendingBadTypeAgainstParam' => [
'<?php
final class UserRole
{
/** @psalm-param stdClass $id */
public function __construct(
protected $id
) {
}
}
new UserRole("a");
',
'error_message' => 'InvalidArgument',
],
'promotedPropertiesDocumentationFailsWhenSendingBadTypeAgainstProperty' => [
'<?php
final class UserRole
{
public function __construct(
/** @psalm-var stdClass */
protected $id2
) {
}
}
new UserRole("a");
',
'error_message' => 'InvalidArgument',
],
'promotedPropertyDuplicateDoc' => [
'<?php
final class UserRole
{
/** @psalm-param string $id */
public function __construct(
/** @psalm-var stdClass */
protected $id
) {
}
}
',
'error_message' => 'InvalidDocblock',
],
];
}
}