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

Fix edge-case issue with abstract class not fully implementing interface

This commit is contained in:
Matthew Brown 2017-09-13 23:57:11 -04:00
parent 92ec985ac0
commit 313e1c383c
5 changed files with 130 additions and 4 deletions

View File

@ -1457,7 +1457,13 @@ class ProjectChecker
$file_checker = $this->getFileCheckerForClassLike($fq_class_name);
$appearing_method_id = (string)MethodChecker::getAppearingMethodId($this, $original_method_id);
$appearing_method_id = MethodChecker::getAppearingMethodId($this, $original_method_id);
if (!$appearing_method_id) {
// this can happen for some abstract classes implementing (but not fully) interfaces
return;
}
list($appearing_fq_class_name) = explode('::', $appearing_method_id);
$appearing_class_storage = $this->classlike_storage_provider->get($appearing_fq_class_name);

View File

@ -0,0 +1,17 @@
<?php
namespace Psalm\Mutation;
class FileMutation
{
/** @var string */
public $file_path;
/** @var int */
public $start;
/** @var int */
public $end;
/** @var string */
public $insertion_text;
}

View File

@ -0,0 +1,70 @@
<?php
namespace Psalm\Tests;
use Psalm\Checker\FileChecker;
class ParamTypeAdditionTest extends TestCase
{
/** @var \Psalm\Checker\ProjectChecker */
protected $project_checker;
/**
* @return void
*/
public function setUp()
{
FileChecker::clearCache();
$this->file_provider = new Provider\FakeFileProvider();
$this->project_checker = new \Psalm\Checker\ProjectChecker(
$this->file_provider,
new Provider\FakeCacheProvider()
);
$this->project_checker->setConfig(new TestConfig());
$this->project_checker->collect_references = true;
}
/**
* @dataProvider providerTestJsonOutputErrors
*
* @param string $input
* @param string $output
* @param int $line_number
* @param string $error
*
* @return void
*/
public function testParamTypeAdditions($input, $output)
{
$this->addFile('somefile.php', $input);
$file_checker = new FileChecker('somefile.php', $this->project_checker);
$file_checker->visitAndAnalyzeMethods();
}
/**
* @return array
*/
public function providerTestParamTypeAdditions()
{
return [
'addSimpleReturnType' => [
'input' => '<?php
function takesString(string $s) : void {}
function shouldTakeString($s) : void {
takesString($s);
}',
'output' => '<?php
function takesString(string $s) : void {}
function shouldTakeString(string $s) : void {
takesString($s);
}',
],
];
}
}

View File

@ -417,6 +417,32 @@ class PropertyTypeTest extends TestCase
public function __construct() { }
}',
],
'setInAbstractMethod' => [
'<?php
interface I {
public function foo() : void;
}
abstract class A implements I {
/** @var string */
public $bar;
public function __construct() {
$this->foo();
}
}
class B extends A {
public function foo() : void
{
$this->bar = "hello";
}
}',
'assertions' => [],
'error_levels' => [
'PropertyNotSetInConstructor' => Config::REPORT_INFO,
],
],
];
}

View File

@ -18,7 +18,7 @@ trait FileCheckerValidCodeParseTestTrait
*
* @param string $code
* @param array<string, string> $assertions
* @param array<string> $error_levels
* @param array<string|int, string> $error_levels
* @param array<string, Union> $scope_vars
*
* @return void
@ -36,8 +36,15 @@ trait FileCheckerValidCodeParseTestTrait
$this->markTestSkipped('Skipped due to a bug.');
}
foreach ($error_levels as $error_level) {
Config::getInstance()->setCustomErrorLevel($error_level, Config::REPORT_SUPPRESS);
foreach ($error_levels as $error_level_key => $error_level) {
if (is_integer($error_level_key)) {
$issue_name = $error_level;
$error_level = Config::REPORT_SUPPRESS;
} else {
$issue_name = $error_level_key;
}
Config::getInstance()->setCustomErrorLevel($issue_name, $error_level);
}
$context = new Context();