1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-03 10:07:52 +01:00
psalm/tests/FileUpdates/ErrorFixTest.php
Bruce Weirdan d19aad7db1
Display target PHP version
Historically it was often not quite clear to users what PHP version
Psalm assumes, and why. This PR addresses this issue by printing the
version and where we got it from right before scanning the files.
2021-11-27 02:18:09 +02:00

472 lines
18 KiB
PHP

<?php
namespace Psalm\Tests\FileUpdates;
use Psalm\Internal\Analyzer\ProjectAnalyzer;
use Psalm\Internal\Provider\FakeFileProvider;
use Psalm\Internal\Provider\Providers;
use Psalm\Tests\Internal\Provider;
use Psalm\Tests\TestConfig;
use function array_keys;
use function count;
use function getcwd;
use const DIRECTORY_SEPARATOR;
class ErrorFixTest extends \Psalm\Tests\TestCase
{
public function setUp() : void
{
parent::setUp();
$this->file_provider = new FakeFileProvider();
$config = new TestConfig();
$config->throw_exception = false;
$providers = new Providers(
$this->file_provider,
new \Psalm\Tests\Internal\Provider\ParserInstanceCacheProvider(),
null,
null,
new Provider\FakeFileReferenceCacheProvider(),
new \Psalm\Tests\Internal\Provider\ProjectCacheProvider()
);
$this->project_analyzer = new ProjectAnalyzer(
$config,
$providers
);
$this->project_analyzer->setPhpVersion('7.3', 'tests');
}
/**
* @dataProvider providerTestErrorFix
*
* @param array<int, array<string, string>> $files
* @param array<int, int> $error_counts
* @param array<string, string> $error_levels
*
*/
public function testErrorFix(
array $files,
array $error_counts,
array $error_levels = []
): void {
$this->project_analyzer->getCodebase()->diff_methods = true;
$codebase = $this->project_analyzer->getCodebase();
$config = $codebase->config;
foreach ($error_levels as $error_type => $error_level) {
$config->setCustomErrorLevel($error_type, $error_level);
}
$analyzed_files = [];
for ($i = 0; $i < count($files); ++$i) {
$batch = $files[$i];
foreach ($batch as $file_path => $contents) {
$this->file_provider->registerFile($file_path, $contents);
if (!isset($analyzed_files[$file_path])) {
$codebase->addFilesToAnalyze([$file_path => $file_path]);
$analyzed_files[$file_path] = true;
}
}
if ($i === 0) {
$codebase->scanFiles();
} else {
$codebase->reloadFiles($this->project_analyzer, array_keys($batch));
}
$codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false);
$expected_count = 0;
$data = \Psalm\IssueBuffer::clear();
foreach ($data as $file_issues) {
$expected_count += count($file_issues);
}
$this->assertSame($error_counts[$i], $expected_count);
}
}
/**
* @return array<string,array{files: array<int, array<string,string>>,error_counts:array<int,int>,error_levels?:array<string,string>}>
*/
public function providerTestErrorFix(): array
{
return [
'fixMissingColonSyntaxError' => [
'files' => [
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
public function foo() : void {
$a = 5;
echo $a;
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
public function foo() : void {
$a = 5
echo $a;
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
public function foo() : void {
$a = 5;
echo $a;
}
}',
],
],
'error_counts' => [0, 1, 0],
],
'addReturnTypesToSingleMethod' => [
'files' => [
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
public function foo() {
return 5;
}
public function bar() {
return $this->foo();
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
public function foo() : int {
return 5;
}
public function bar() {
return $this->foo();
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
public function foo() : int {
return 5;
}
public function bar() : int {
return $this->foo();
}
}',
],
],
'error_counts' => [2, 1, 0],
[
'MissingReturnType' => \Psalm\Config::REPORT_INFO,
],
],
'traitMethodRenameFirstCorrect' => [
'files' => [
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
use T;
public function foo() : void {
echo $this->bar();
}
}',
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'T.php' => '<?php
namespace Foo;
trait T {
public function bar() : string {
return "hello";
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
use T;
public function foo() : void {
echo $this->bar();
}
}',
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'T.php' => '<?php
namespace Foo;
trait T {
public function bat() : string {
return "hello";
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
use T;
public function foo() : void {
echo $this->bar();
}
}',
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'T.php' => '<?php
namespace Foo;
trait T {
public function bar() : string {
return "hello";
}
}',
],
],
'error_counts' => [0, 2, 0],
],
'traitMethodRenameFirstError' => [
'files' => [
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
use T;
public function foo() : void {
echo $this->bar();
}
}',
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'T.php' => '<?php
namespace Foo;
trait T {
public function bat() : string {
return "hello";
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
use T;
public function foo() : void {
echo $this->bar();
}
}',
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'T.php' => '<?php
namespace Foo;
trait T {
public function bar() : string {
return "hello";
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
use T;
public function foo() : void {
echo $this->bar();
}
}',
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'T.php' => '<?php
namespace Foo;
trait T {
public function bar() : string {
return "hello";
}
}',
],
],
'error_counts' => [2, 0, 0],
],
'addSuppressions' => [
'files' => [
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
class C {
public function foo(array $a) : void {
foreach ($a as $b) {
$b->bar();
}
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
class C {
public function foo(array $a) : void {
/**
* @psalm-suppress MixedAssignment
*/
foreach ($a as $b) {
$b->bar();
}
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
class C {
public function foo(array $a) : void {
/**
* @psalm-suppress MixedAssignment
*/
foreach ($a as $b) {
/**
* @psalm-suppress MixedMethodCall
*/
$b->bar();
}
}
}',
],
],
'error_counts' => [2, 1, 0],
],
'fixDefault' => [
'files' => [
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
class C {
/** @var string */
public $foo = 5;
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
class C {
/** @var string */
public $foo = "hello";
}',
],
],
'error_counts' => [1, 0],
],
'changeContent' => [
'files' => [
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
function add(int $a, int $b): int {
return $a + $b;
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'B.php' => '<?php
function hasMethod(object $input, string $method): bool {
return (new ReflectionClass($input))
->hasMethod($method);
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'C.php' => '<?php
function add(int $a, int $b): int {
return $a + $b;
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'D.php' => '<?php
function hasMethod(object $input, string $method): bool {
return (new ReflectionClass($input))
->hasMethod($method);
}',
],
],
'error_counts' => [0, 0, 0, 0],
],
'missingConstructorForTwoVars' => [
'files' => [
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
class A {
protected int $x;
protected int $y;
}'
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
class A {
protected int $x = 0;
protected int $y;
}'
],
],
'error_counts' => [2, 1],
],
'missingConstructorForInheritedProperties' => [
'files' => [
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
abstract class A {
public int $x;
public int $y;
}
class B extends A {
public function __construct() {}
}'
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
abstract class A {
public int $x = 0;
public int $y;
}
class B extends A {
public function __construct() {}
}'
],
[
getcwd() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'A.php' => '<?php
abstract class A {
public int $x = 0;
public int $y = 0;
}
class B extends A {
public function __construct() {}
}'
],
],
'error_counts' => [2, 1, 0],
],
];
}
}