mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 12:55:26 +01:00
Merge pull request #6764 from orklah/promoted-property-docblock
allow documenting promoted properties with @var
This commit is contained in:
commit
61a7cbe9c5
@ -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 [
|
||||
|
@ -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',
|
||||
],
|
||||
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user