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:
parent
92ec985ac0
commit
313e1c383c
@ -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);
|
||||
|
17
src/Psalm/Mutation/FileMutation.php
Normal file
17
src/Psalm/Mutation/FileMutation.php
Normal 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;
|
||||
}
|
70
tests/ParamTypeAdditionTest.php
Normal file
70
tests/ParamTypeAdditionTest.php
Normal 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);
|
||||
}',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -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,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user