1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Fix #181 and #225 - resolve problems with self in comments/return statements

This commit is contained in:
Matthew Brown 2017-10-07 10:22:52 -04:00
parent 1cc13bdeeb
commit 6b4bd47ddb
9 changed files with 120 additions and 17 deletions

View File

@ -535,7 +535,7 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
}
$uninitialized_variables[] = '$this->' . $property_name;
$uninitialized_properties[$property_name] = $property;
$uninitialized_properties[$property_name] = $property;
}
if ($uninitialized_properties) {

View File

@ -497,7 +497,7 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
) {
if (IssueBuffer::accepts(
new InvalidDocblock(
'Return type has wrong type \'' . $storage->return_type .
'Docblock has incorrect return type \'' . $storage->return_type .
'\', should be \'' . $storage->signature_return_type . '\'',
$storage->return_type_location
),

View File

@ -56,6 +56,8 @@ class ForeachChecker
$iterator_type = null;
}
$project_checker = $statements_checker->getFileChecker()->project_checker;
if ($iterator_type) {
if ($iterator_type->isNull()) {
if (IssueBuffer::accepts(
@ -79,8 +81,6 @@ class ForeachChecker
}
}
$project_checker = $statements_checker->getFileChecker()->project_checker;
foreach ($iterator_type->types as $iterator_type) {
// if it's an empty array, we cannot iterate over it
if ((string) $iterator_type === 'array<empty, empty>') {
@ -235,7 +235,13 @@ class ForeachChecker
);
if ($var_comment && $var_comment->var_id) {
$foreach_context->vars_in_scope[$var_comment->var_id] = Type::parseString($var_comment->type);
$comment_type = ExpressionChecker::fleshOutType(
$project_checker,
Type::parseString($var_comment->type),
$context->self
);
$foreach_context->vars_in_scope[$var_comment->var_id] = $comment_type;
}
}

View File

@ -77,6 +77,7 @@ class AssignmentChecker
);
$var_comment = null;
$comment_type = null;
if ($doc_comment) {
$var_comment = CommentChecker::getTypeFromComment(
@ -88,8 +89,18 @@ class AssignmentChecker
$came_from_line_number
);
if ($var_comment && $var_comment->var_id && $var_comment->var_id !== $var_id) {
$context->vars_in_scope[$var_comment->var_id] = Type::parseString($var_comment->type);
if ($var_comment) {
$comment_type = ExpressionChecker::fleshOutType(
$statements_checker->getFileChecker()->project_checker,
Type::parseString($var_comment->type),
$context->self
);
$comment_type->setFromDocblock();
if ($var_comment->var_id && $var_comment->var_id !== $var_id) {
$context->vars_in_scope[$var_comment->var_id] = $comment_type;
}
}
}
@ -101,16 +112,16 @@ class AssignmentChecker
// if we're not exiting immediately, make everything mixed
$context->vars_in_scope[$var_id] =
$var_comment && (!$var_comment->var_id || $var_comment->var_id === $var_id)
? Type::parseString($var_comment->type)
$var_comment && (!$var_comment->var_id || $var_comment->var_id === $var_id) && $comment_type
? $comment_type
: Type::getMixed();
}
return false;
}
if ($var_comment && (!$var_comment->var_id || $var_comment->var_id === $var_id)) {
$assign_value_type = $var_comment->type;
if ($var_comment && (!$var_comment->var_id || $var_comment->var_id === $var_id) && $comment_type) {
$assign_value_type = $comment_type;
} elseif (!$assign_value_type) {
if (isset($assign_value->inferredType)) {
/** @var Type\Union */

View File

@ -1827,7 +1827,13 @@ class ExpressionChecker
);
if ($var_comment && $var_comment->var_id) {
$context->vars_in_scope[$var_comment->var_id] = Type::parseString($var_comment->type);
$comment_type = ExpressionChecker::fleshOutType(
$statements_checker->getFileChecker()->project_checker,
Type::parseString($var_comment->type),
$context->self
);
$context->vars_in_scope[$var_comment->var_id] = $comment_type;
}
}

View File

@ -159,7 +159,7 @@ class StatementsChecker extends SourceChecker implements StatementsSource
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\Return_) {
$has_returned = true;
$this->analyzeReturn($stmt, $context);
$this->analyzeReturn($project_checker, $stmt, $context);
} elseif ($stmt instanceof PhpParser\Node\Stmt\Throw_) {
$has_returned = true;
$this->analyzeThrow($stmt, $context);
@ -320,7 +320,13 @@ class StatementsChecker extends SourceChecker implements StatementsSource
);
if ($var_comment && $var_comment->var_id) {
$context->vars_in_scope[$var_comment->var_id] = Type::parseString($var_comment->type);
$comment_type = ExpressionChecker::fleshOutType(
$project_checker,
Type::parseString($var_comment->type),
$context->self
);
$context->vars_in_scope[$var_comment->var_id] = $comment_type;
}
}
} elseif ($stmt instanceof PhpParser\Node\Stmt\Goto_) {
@ -832,8 +838,11 @@ class StatementsChecker extends SourceChecker implements StatementsSource
*
* @return false|null
*/
private function analyzeReturn(PhpParser\Node\Stmt\Return_ $stmt, Context $context)
{
private function analyzeReturn(
ProjectChecker $project_checker,
PhpParser\Node\Stmt\Return_ $stmt,
Context $context
) {
$doc_comment_text = (string)$stmt->getDocComment();
$var_comment = null;
@ -847,7 +856,13 @@ class StatementsChecker extends SourceChecker implements StatementsSource
);
if ($var_comment && $var_comment->var_id) {
$context->vars_in_scope[$var_comment->var_id] = Type::parseString($var_comment->type);
$comment_type = ExpressionChecker::fleshOutType(
$project_checker,
Type::parseString($var_comment->type),
$context->self
);
$context->vars_in_scope[$var_comment->var_id] = $comment_type;
}
}

View File

@ -715,6 +715,14 @@ class TypeChecker
return true;
}
if ($input_type_part instanceof TNamedObject
&& $input_type_part->value === 'static'
&& $container_type_part instanceof TNamedObject
&& $container_type_part->value === 'self'
) {
return true;
}
if ($input_type_part instanceof TNamedObject &&
$input_type_part->value === 'Closure' &&
$container_type_part instanceof TCallable

View File

@ -221,6 +221,19 @@ class AnnotationTest extends TestCase
public function foo($a) : void {}
}',
],
'varSelf' => [
'<?php
class A
{
public function foo() : void {}
public function getMeAgain() : void {
/** @var self */
$me = $this;
$me->foo();
}
}',
],
];
}

View File

@ -368,6 +368,50 @@ class ReturnTypeTest extends TestCase
function doSomething($res) : void {
}',
],
'selfReturnNoTypehints' => [
'<?php
class A {
/**
* @return static
*/
public function getMe()
{
return $this;
}
}
class B extends A
{
/**
* @return static
*/
public function getMeAgain() {
return $this->getMe();
}
}',
],
'selfReturnTypehints' => [
'<?php
class A {
/**
* @return static
*/
public function getMe() : self
{
return $this;
}
}
class B extends A
{
/**
* @return static
*/
public function getMeAgain() : self {
return $this->getMe();
}
}',
],
];
}