mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +01:00
Fix #81 - interface inheritance checks were broken
This commit is contained in:
parent
4114456bf4
commit
8c68861cc3
@ -944,7 +944,14 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
||||
|
||||
$return_types_different = false;
|
||||
|
||||
if (!TypeChecker::isContainedBy($inferred_return_type, $declared_return_type, $this->getFileChecker())) {
|
||||
if (!TypeChecker::isContainedBy(
|
||||
$inferred_return_type,
|
||||
$declared_return_type,
|
||||
$this->getFileChecker(),
|
||||
false,
|
||||
$has_scalar_match,
|
||||
$type_coerced
|
||||
)) {
|
||||
if ($update_docblock) {
|
||||
if (!in_array('InvalidReturnType', $this->suppressed_issues)) {
|
||||
FileChecker::addDocblockReturnType(
|
||||
@ -968,9 +975,7 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
||||
}
|
||||
|
||||
// is the declared return type more specific than the inferred one?
|
||||
if ($declared_return_type->isNullable() === $inferred_return_type->isNullable() &&
|
||||
TypeChecker::isContainedBy($declared_return_type, $inferred_return_type, $this->getFileChecker())
|
||||
) {
|
||||
if ($declared_return_type->isNullable() === $inferred_return_type->isNullable() && $type_coerced) {
|
||||
if (IssueBuffer::accepts(
|
||||
new MoreSpecificReturnType(
|
||||
'The given return type \'' . $declared_return_type . '\' for ' . $cased_method_id .
|
||||
|
@ -790,6 +790,12 @@ class TypeChecker
|
||||
$container_type_part->value
|
||||
)
|
||||
) ||
|
||||
(InterfaceChecker::interfaceExists($input_type_part->value, $file_checker) &&
|
||||
InterfaceChecker::interfaceExtends(
|
||||
$input_type_part->value,
|
||||
$container_type_part->value
|
||||
)
|
||||
) ||
|
||||
ExpressionChecker::isMock($input_type_part->value)
|
||||
)
|
||||
)
|
||||
@ -914,11 +920,19 @@ class TypeChecker
|
||||
return true;
|
||||
} elseif ($container_type_part instanceof TNamedObject &&
|
||||
$input_type_part instanceof TNamedObject &&
|
||||
ClassChecker::classExists($container_type_part->value, $file_checker) &&
|
||||
ClassChecker::classOrInterfaceExists($input_type_part->value, $file_checker) &&
|
||||
ClassChecker::classExtendsOrImplements(
|
||||
$container_type_part->value,
|
||||
$input_type_part->value
|
||||
((
|
||||
ClassChecker::classExists($container_type_part->value, $file_checker) &&
|
||||
ClassChecker::classExtendsOrImplements(
|
||||
$container_type_part->value,
|
||||
$input_type_part->value
|
||||
)) ||
|
||||
(InterfaceChecker::interfaceExists($container_type_part->value, $file_checker) &&
|
||||
InterfaceChecker::interfaceExtends(
|
||||
$container_type_part->value,
|
||||
$input_type_part->value
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
$type_coerced = true;
|
||||
|
@ -566,4 +566,24 @@ class ClassTest extends PHPUnit_Framework_TestCase
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Psalm\Exception\CodeException
|
||||
* @expectedExceptionMessage MoreSpecificReturnType
|
||||
* @return void
|
||||
*/
|
||||
public function testMoreSpecificReturnType()
|
||||
{
|
||||
$stmts = self::$parser->parse('<?php
|
||||
class A {}
|
||||
class B extends A {}
|
||||
|
||||
function foo(A $a) : B {
|
||||
return $a;
|
||||
}
|
||||
');
|
||||
$file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts);
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods($context);
|
||||
}
|
||||
}
|
||||
|
@ -544,4 +544,42 @@ class InterfaceTest extends PHPUnit_Framework_TestCase
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testInterfaceExtendsReturnType()
|
||||
{
|
||||
$stmts = self::$parser->parse('<?php
|
||||
interface A {}
|
||||
interface B extends A {}
|
||||
|
||||
function foo(B $a) : A {
|
||||
return $a;
|
||||
}
|
||||
');
|
||||
$file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts);
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Psalm\Exception\CodeException
|
||||
* @expectedExceptionMessage MoreSpecificReturnType
|
||||
* @return void
|
||||
*/
|
||||
public function testMoreSpecificReturnType()
|
||||
{
|
||||
$stmts = self::$parser->parse('<?php
|
||||
interface A {}
|
||||
interface B extends A {}
|
||||
|
||||
function foo(A $a) : B {
|
||||
return $a;
|
||||
}
|
||||
');
|
||||
$file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts);
|
||||
$context = new Context();
|
||||
$file_checker->visitAndAnalyzeMethods($context);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user