mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
1711 lines
46 KiB
PHP
1711 lines
46 KiB
PHP
<?php
|
|
namespace Psalm\Tests;
|
|
|
|
use function array_map;
|
|
use function count;
|
|
use function get_class;
|
|
use PhpParser;
|
|
use function strpos;
|
|
use function var_export;
|
|
|
|
class FileDiffTest extends TestCase
|
|
{
|
|
/**
|
|
* @dataProvider getChanges
|
|
*
|
|
* @param string[] $same_methods
|
|
*
|
|
*/
|
|
public function testCode(
|
|
string $a,
|
|
string $b,
|
|
array $same_methods,
|
|
array $same_signatures,
|
|
array $changed_methods,
|
|
array $diff_map_offsets
|
|
): void {
|
|
if (strpos($this->getTestName(), 'SKIPPED-') !== false) {
|
|
$this->markTestSkipped();
|
|
}
|
|
|
|
$a_stmts = \Psalm\Internal\Provider\StatementsProvider::parseStatements($a, '7.4');
|
|
$b_stmts = \Psalm\Internal\Provider\StatementsProvider::parseStatements($b, '7.4');
|
|
|
|
$diff = \Psalm\Internal\Diff\FileStatementsDiffer::diff($a_stmts, $b_stmts, $a, $b);
|
|
|
|
$this->assertSame(
|
|
$same_methods,
|
|
$diff[0]
|
|
);
|
|
|
|
$this->assertSame(
|
|
$same_signatures,
|
|
$diff[1]
|
|
);
|
|
|
|
$this->assertSame(
|
|
$changed_methods,
|
|
$diff[2]
|
|
);
|
|
|
|
$this->assertSame(count($diff_map_offsets), count($diff[3]));
|
|
|
|
$found_offsets = array_map(
|
|
/**
|
|
* @param array{0: int, 1: int, 2: int, 3: int} $arr
|
|
*
|
|
* @return array{0: int, 1: int}
|
|
*/
|
|
function (array $arr): array {
|
|
return [$arr[2], $arr[3]];
|
|
},
|
|
$diff[3]
|
|
);
|
|
|
|
$this->assertSame($diff_map_offsets, $found_offsets);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider getChanges
|
|
*
|
|
* @param string[] $same_methods
|
|
* @param string[] $same_signatures
|
|
* @param string[] $changed_methods
|
|
* @param array<array-key,array{int,int}> $diff_map_offsets
|
|
*
|
|
*/
|
|
public function testPartialAstDiff(
|
|
string $a,
|
|
string $b,
|
|
array $same_methods,
|
|
array $same_signatures,
|
|
array $changed_methods,
|
|
array $diff_map_offsets
|
|
): void {
|
|
if (strpos($this->getTestName(), 'SKIPPED-') !== false) {
|
|
$this->markTestSkipped();
|
|
}
|
|
|
|
$file_changes = \Psalm\Internal\Diff\FileDiffer::getDiff($a, $b);
|
|
|
|
$a_stmts = \Psalm\Internal\Provider\StatementsProvider::parseStatements($a, '7.4');
|
|
|
|
$traverser = new PhpParser\NodeTraverser;
|
|
$traverser->addVisitor(new \Psalm\Internal\PhpVisitor\CloningVisitor);
|
|
// performs a deep clone
|
|
/** @var list<PhpParser\Node\Stmt> */
|
|
$a_stmts_copy = $traverser->traverse($a_stmts);
|
|
|
|
$this->assertTreesEqual($a_stmts, $a_stmts_copy);
|
|
|
|
$b_stmts = \Psalm\Internal\Provider\StatementsProvider::parseStatements($b, '7.4', null, $a, $a_stmts_copy, $file_changes);
|
|
$b_clean_stmts = \Psalm\Internal\Provider\StatementsProvider::parseStatements($b, '7.4');
|
|
|
|
$this->assertTreesEqual($b_clean_stmts, $b_stmts);
|
|
|
|
$diff = \Psalm\Internal\Diff\FileStatementsDiffer::diff($a_stmts, $b_clean_stmts, $a, $b);
|
|
|
|
$this->assertSame(
|
|
$same_methods,
|
|
$diff[0]
|
|
);
|
|
|
|
$this->assertSame(
|
|
$same_signatures,
|
|
$diff[1]
|
|
);
|
|
|
|
$this->assertSame(
|
|
$changed_methods,
|
|
$diff[2]
|
|
);
|
|
|
|
$this->assertSame(count($diff_map_offsets), count($diff[3]));
|
|
|
|
$found_offsets = array_map(
|
|
/**
|
|
* @param array{0: int, 1: int, 2: int, 3: int} $arr
|
|
*
|
|
* @return array{0: int, 1: int}
|
|
*/
|
|
function (array $arr): array {
|
|
return [$arr[2], $arr[3]];
|
|
},
|
|
$diff[3]
|
|
);
|
|
|
|
$this->assertSame($diff_map_offsets, $found_offsets);
|
|
}
|
|
|
|
/**
|
|
* @param array<int, PhpParser\Node\Stmt> $a
|
|
* @param array<int, PhpParser\Node\Stmt> $b
|
|
*
|
|
*/
|
|
private function assertTreesEqual(array $a, array $b): void
|
|
{
|
|
$this->assertSame(count($a), count($b));
|
|
|
|
foreach ($a as $i => $a_stmt) {
|
|
$b_stmt = $b[$i];
|
|
|
|
$this->assertNotSame($a_stmt, $b_stmt);
|
|
|
|
$this->assertSame(get_class($a_stmt), get_class($b_stmt));
|
|
|
|
if ($a_stmt instanceof PhpParser\Node\Stmt\Expression
|
|
&& $b_stmt instanceof PhpParser\Node\Stmt\Expression
|
|
) {
|
|
$this->assertSame(get_class($a_stmt->expr), get_class($b_stmt->expr));
|
|
}
|
|
|
|
if ($a_doc = $a_stmt->getDocComment()) {
|
|
$b_doc = $b_stmt->getDocComment();
|
|
|
|
$this->assertNotNull($b_doc, var_export($a_doc, true));
|
|
|
|
$this->assertNotSame($a_doc, $b_doc);
|
|
|
|
$this->assertSame($a_doc->getStartLine(), $b_doc->getStartLine());
|
|
}
|
|
|
|
$this->assertSame(
|
|
$a_stmt->getAttribute('startFilePos'),
|
|
$b_stmt->getAttribute('startFilePos')
|
|
);
|
|
$this->assertSame(
|
|
$a_stmt->getAttribute('endFilePos'),
|
|
$b_stmt->getAttribute('endFilePos'),
|
|
($a_stmt instanceof PhpParser\Node\Stmt\Expression
|
|
? get_class($a_stmt->expr)
|
|
: get_class($a_stmt))
|
|
. ' on line ' . $a_stmt->getLine()
|
|
);
|
|
$this->assertSame($a_stmt->getLine(), $b_stmt->getLine());
|
|
|
|
if (isset($a_stmt->stmts)) {
|
|
$this->assertTrue(isset($b_stmt->stmts));
|
|
|
|
/**
|
|
* @psalm-suppress MixedArgument
|
|
*/
|
|
$this->assertTreesEqual($a_stmt->stmts, $b_stmt->stmts);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array<string,array{string,string,string[],string[],string[],array<array-key,array{int,int}>}>
|
|
*/
|
|
public function getChanges(): array
|
|
{
|
|
return [
|
|
'sameFile' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public $aB = 5;
|
|
|
|
const F = 1;
|
|
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public $aB = 5;
|
|
|
|
const F = 1;
|
|
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
['foo\a::$aB', 'foo\a::F', 'foo\a::foo', 'foo\a::bar'],
|
|
[],
|
|
[],
|
|
[[0, 0], [0, 0], [0, 0], [0, 0]],
|
|
],
|
|
'sameFileWithDoubleDocblocks' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public $aB = 5;
|
|
|
|
const F = 1;
|
|
|
|
/*
|
|
* another
|
|
*/
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
|
|
// this is one line
|
|
// this is another
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public $aB = 5;
|
|
|
|
const F = 1;
|
|
|
|
/*
|
|
* another
|
|
*/
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
|
|
// this is one line
|
|
// this is another
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
['foo\a::$aB', 'foo\a::F', 'foo\a::foo', 'foo\a::bar'],
|
|
[],
|
|
[],
|
|
[[0, 0], [0, 0], [0, 0], [0, 0]],
|
|
],
|
|
'lineChanges' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public $aB = 5;
|
|
|
|
const F = 1;
|
|
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
|
|
public $aB = 5;
|
|
|
|
|
|
const F = 1;
|
|
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
|
|
|
|
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
['foo\a::$aB', 'foo\a::F', 'foo\a::foo', 'foo\a::bar'],
|
|
[],
|
|
[],
|
|
[[1, 1], [2, 2], [2, 2], [5, 5]],
|
|
],
|
|
'simpleBodyChangeWithoutSignatureChange' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() : void {
|
|
$a = 1;
|
|
}
|
|
public function bar() : void {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() : void {
|
|
$a = 12;
|
|
}
|
|
public function bar() : void {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
['foo\a::bar'],
|
|
['foo\a::foo'],
|
|
[],
|
|
[[1, 0]],
|
|
],
|
|
'simpleBodyChangesWithoutSignatureChange' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() : void {
|
|
$a = 1;
|
|
}
|
|
public function bar() : void {
|
|
$c = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() : void {
|
|
$a = 1;
|
|
$b = 2;
|
|
}
|
|
public function bar() : void {
|
|
$c = 1;
|
|
}
|
|
}',
|
|
['foo\a::bar'],
|
|
['foo\a::foo'],
|
|
[],
|
|
[[32, 1]],
|
|
],
|
|
'simpleBodyChangeWithSignatureChange' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar(string $a) {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
['foo\a::foo'],
|
|
[],
|
|
['foo\a::bar', 'foo\a::bar'],
|
|
[[0, 0]],
|
|
],
|
|
'propertyChange' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public $a;
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public $b;
|
|
}',
|
|
[],
|
|
[],
|
|
['foo\a::$a', 'foo\a::$b'],
|
|
[],
|
|
],
|
|
'propertyDefaultChange' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public $a = 1;
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public $a = 2;
|
|
}',
|
|
[],
|
|
['foo\a::$a'],
|
|
[],
|
|
[],
|
|
],
|
|
'propertyDefaultAddition' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public $a;
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public $a = 2;
|
|
}',
|
|
[],
|
|
['foo\a::$a'],
|
|
[],
|
|
[],
|
|
],
|
|
'propertySignatureChange' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/** @var ?string */
|
|
public $a;
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/** @var ?int */
|
|
public $a;
|
|
}',
|
|
[],
|
|
[],
|
|
['foo\a::$a', 'foo\a::$a'],
|
|
[],
|
|
],
|
|
'propertyStaticChange' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/** @var ?string */
|
|
public static $a;
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/** @var ?string */
|
|
public $a;
|
|
}',
|
|
[],
|
|
[],
|
|
['foo\a::$a', 'foo\a::$a'],
|
|
[],
|
|
],
|
|
'propertyVisibilityChange' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/** @var ?string */
|
|
public $a;
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/** @var ?string */
|
|
private $a;
|
|
}',
|
|
[],
|
|
[],
|
|
['foo\a::$a', 'foo\a::$a'],
|
|
[],
|
|
],
|
|
'addDocblockToFirst' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 2;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 2;
|
|
}
|
|
}',
|
|
['foo\a::bar'],
|
|
[],
|
|
['foo\a::foo', 'foo\a::foo'],
|
|
[[84, 3]],
|
|
],
|
|
'addDocblockToSecond' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 2;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$b = 2;
|
|
}
|
|
}',
|
|
['foo\a::foo'],
|
|
[],
|
|
['foo\a::bar', 'foo\a::bar'],
|
|
[[0, 0]],
|
|
],
|
|
'removeDocblock' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$b = 2;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 2;
|
|
}
|
|
}',
|
|
['foo\a::foo'],
|
|
[],
|
|
['foo\a::bar', 'foo\a::bar'],
|
|
[[0, 0]],
|
|
],
|
|
'changeDocblock' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function bar() {
|
|
$b = 2;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$b = 2;
|
|
}
|
|
}',
|
|
['foo\a::foo'],
|
|
[],
|
|
['foo\a::bar', 'foo\a::bar'],
|
|
[[0, 0]],
|
|
],
|
|
'changeMethodVisibility' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 2;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
private function bar() {
|
|
$b = 2;
|
|
}
|
|
}',
|
|
['foo\a::foo'],
|
|
[],
|
|
['foo\a::bar', 'foo\a::bar'],
|
|
[[0, 0]],
|
|
],
|
|
'removeFunctionAtEnd' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bat() {
|
|
$c = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
['foo\a::foo', 'foo\a::bar'],
|
|
[],
|
|
['foo\a::bat'],
|
|
[[0, 0], [0, 0]],
|
|
],
|
|
'addSpaceInFunction' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
$b = 2;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$c = 3;
|
|
$d = 4;
|
|
|
|
if (true) {
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bat() {
|
|
$e = 5;
|
|
$f = 6;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
$b = 2;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$c = 3;
|
|
|
|
|
|
|
|
|
|
$d = 4;
|
|
|
|
if (true) {
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bat() {
|
|
$e = 5;
|
|
$f = 6;
|
|
}
|
|
}',
|
|
['foo\a::foo', 'foo\a::bat'],
|
|
['foo\a::bar'],
|
|
[],
|
|
[[0, 0], [4, 4]],
|
|
],
|
|
'removeSpaceInFunction' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
$b = 2;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$c = 3;
|
|
|
|
|
|
|
|
|
|
$d = 4;
|
|
|
|
if (true) {
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bat() {
|
|
$e = 5;
|
|
$f = 6;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
$b = 2;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$c = 3;
|
|
$d = 4;
|
|
|
|
if (true) {
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bat() {
|
|
$e = 5;
|
|
$f = 6;
|
|
}
|
|
}',
|
|
['foo\a::foo', 'foo\a::bat'],
|
|
['foo\a::bar'],
|
|
[],
|
|
[[0, 0], [-4, -4]],
|
|
],
|
|
'removeFunctionAtBeginning' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
public function bat() {
|
|
$c = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
public function bat() {
|
|
$c = 1;
|
|
}
|
|
}',
|
|
['foo\a::bar', 'foo\a::bat'],
|
|
[],
|
|
['foo\a::foo'],
|
|
[[-98, -3], [-98, -3]],
|
|
],
|
|
'removeFunctionInMiddle' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
public function bat() {
|
|
$c = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bat() {
|
|
$c = 1;
|
|
}
|
|
}',
|
|
['foo\a::foo', 'foo\a::bat'],
|
|
[],
|
|
['foo\a::bar'],
|
|
[[0, 0], [-98, -3]],
|
|
],
|
|
'changeNamespace' => [
|
|
'<?php
|
|
namespace Bar;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function bar() {
|
|
$b = 2;
|
|
}
|
|
}',
|
|
[],
|
|
[],
|
|
[],
|
|
[],
|
|
],
|
|
'removeNamespace' => [
|
|
'<?php
|
|
namespace Bar;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
class A {
|
|
public function bar() {
|
|
$b = 2;
|
|
}
|
|
}',
|
|
[],
|
|
[],
|
|
[],
|
|
[],
|
|
],
|
|
'newFunctionAtEnd' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bat() {
|
|
$c = 1;
|
|
}
|
|
}',
|
|
['foo\a::foo', 'foo\a::bar'],
|
|
[],
|
|
['foo\a::bat'],
|
|
[[0, 0], [0, 0]],
|
|
],
|
|
'newFunctionAtBeginning' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bat() {
|
|
$c = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
['foo\a::foo', 'foo\a::bar'],
|
|
[],
|
|
['foo\a::bat'],
|
|
[[183, 7], [183, 7]],
|
|
],
|
|
'newFunctionInMiddle' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bat() {
|
|
$c = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
['foo\a::foo', 'foo\a::bar'],
|
|
[],
|
|
['foo\a::bat'],
|
|
[[0, 0], [183, 7]],
|
|
],
|
|
'removeAdditionalComments' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
/**
|
|
* more Comments
|
|
*
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
|
|
/**
|
|
* more Comments
|
|
*
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
use D;
|
|
use E;
|
|
|
|
class A {
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function foo() {
|
|
$c = 1;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function bar() {
|
|
$a = 1;
|
|
}
|
|
}',
|
|
[],
|
|
[],
|
|
['use:D', 'use:E', 'foo\a::foo', 'foo\a::bar', 'foo\a::foo', 'foo\a::bar'],
|
|
[],
|
|
],
|
|
'SKIPPED-whiteSpaceOnly' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
class A {
|
|
public function foo() {
|
|
|
|
$a = 1 ;
|
|
}
|
|
|
|
public function bar() {
|
|
$b = 1;
|
|
|
|
}
|
|
}',
|
|
['foo\a::foo', 'foo\a::bar'],
|
|
[],
|
|
[],
|
|
[],
|
|
],
|
|
'changeDeclaredMethodId' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function __construct() {}
|
|
public static function bar() : void {}
|
|
}
|
|
|
|
class B extends A {
|
|
public static function bat() : void {}
|
|
}
|
|
|
|
class C extends B { }',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function __construct() {}
|
|
public static function bar() : void {}
|
|
}
|
|
|
|
class B extends A {
|
|
public function __construct() {}
|
|
public static function bar() : void {}
|
|
public static function bat() : void {}
|
|
}
|
|
|
|
class C extends B { }',
|
|
['foo\a::__construct', 'foo\a::bar', 'foo\b::bat'],
|
|
[],
|
|
['foo\b::__construct', 'foo\b::bar'],
|
|
[[0, 0], [0, 0], [120, 2]],
|
|
],
|
|
'sameTrait' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
trait T {
|
|
public $aB = 5;
|
|
|
|
const F = 1;
|
|
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
trait T {
|
|
public $aB = 5;
|
|
|
|
const F = 1;
|
|
|
|
public function foo() {
|
|
$a = 1;
|
|
}
|
|
public function bar() {
|
|
$b = 1;
|
|
}
|
|
}',
|
|
['foo\t::$aB', 'foo\t::F', 'foo\t::foo', 'foo\t::bar'],
|
|
[],
|
|
[],
|
|
[[0, 0], [0, 0], [0, 0], [0, 0]],
|
|
],
|
|
'traitPropertyChange' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
trait T {
|
|
public $a;
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
trait T {
|
|
public $b;
|
|
}',
|
|
[],
|
|
[],
|
|
['foo\t::$a', 'foo\t::$b'],
|
|
[],
|
|
],
|
|
'traitMethodReturnTypeChange' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
trait T {
|
|
public function barBar(): string {
|
|
return "hello";
|
|
}
|
|
|
|
public function bat(): string {
|
|
return "hello";
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
trait T {
|
|
public function barBar(): int {
|
|
return 5;
|
|
}
|
|
|
|
public function bat(): string {
|
|
return "hello";
|
|
}
|
|
}',
|
|
['foo\t::bat'],
|
|
[],
|
|
['foo\t::barbar', 'foo\t::barbar'],
|
|
[[-9, 0]],
|
|
],
|
|
'removeManyArguments' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class C {
|
|
public function barBar() {
|
|
foo(
|
|
$a,
|
|
$b
|
|
);
|
|
}
|
|
|
|
public function bat() {
|
|
return "hello";
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class C {
|
|
public function barBar() {
|
|
foo(
|
|
$a
|
|
);
|
|
}
|
|
|
|
public function bat() {
|
|
return "hello";
|
|
}
|
|
}',
|
|
['foo\c::bat'],
|
|
['foo\c::barbar'],
|
|
[],
|
|
[[-36, -1]],
|
|
],
|
|
'docblockTwiceOver' => [
|
|
'<?php
|
|
namespace Bar;
|
|
|
|
class Foo
|
|
{
|
|
public function a()
|
|
{
|
|
return 5;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function c()
|
|
{
|
|
return true;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Bar;
|
|
|
|
class Foo
|
|
{
|
|
public function a()
|
|
{
|
|
return 5;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function b()
|
|
{
|
|
$a = 1;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function c()
|
|
{
|
|
return true;
|
|
}
|
|
}',
|
|
['bar\foo::a', 'bar\foo::c'],
|
|
[],
|
|
['bar\foo::b'],
|
|
[[0, 0], [229, 8]],
|
|
],
|
|
'removeStatementsAbove' => [
|
|
'<?php
|
|
namespace A;
|
|
|
|
class B
|
|
{
|
|
/**
|
|
* @return void
|
|
*/
|
|
public static function foo() {
|
|
echo 4;
|
|
echo 5;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public static function bar() {
|
|
echo 4;
|
|
echo 5;
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace A;
|
|
|
|
class B
|
|
{
|
|
/**
|
|
* @return void
|
|
*/
|
|
public static function foo() {
|
|
echo 5;
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public static function bar() {
|
|
echo 5;
|
|
}
|
|
}',
|
|
[],
|
|
[
|
|
'a\b::foo',
|
|
'a\b::bar',
|
|
],
|
|
[],
|
|
[],
|
|
],
|
|
'removeUse' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
use Exception;
|
|
|
|
class A {
|
|
public function foo() : void {
|
|
throw new Exception();
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A {
|
|
public function foo() : void {
|
|
throw new Exception();
|
|
}
|
|
}',
|
|
['foo\a::foo'],
|
|
[],
|
|
['use:Exception'],
|
|
[[-36, -2]],
|
|
],
|
|
'addDocblockToFirstFunctionStatement' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class C {
|
|
public function foo(array $a) : void {
|
|
foreach ($a as $b) {
|
|
$b->bar();
|
|
}
|
|
}
|
|
}',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class C {
|
|
public function foo(array $a) : void {
|
|
/**
|
|
* @psalm-suppress MixedAssignment
|
|
*/
|
|
foreach ($a as $b) {
|
|
$b->bar();
|
|
}
|
|
}
|
|
}',
|
|
[],
|
|
['foo\c::foo'],
|
|
[],
|
|
[],
|
|
],
|
|
'vimeoDiff' => [
|
|
'<?php
|
|
namespace C;
|
|
|
|
class A extends B
|
|
{
|
|
/**
|
|
* Another thing
|
|
*
|
|
* @return D
|
|
*/
|
|
public function foo() {
|
|
$a = 1;
|
|
$b = 2;
|
|
}
|
|
|
|
/**
|
|
* Other thing
|
|
*
|
|
* @return D
|
|
*/
|
|
public function bar() {
|
|
$a = 1;
|
|
$b = 2;
|
|
}
|
|
|
|
/**
|
|
* Some thing
|
|
*
|
|
* @return D
|
|
*/
|
|
public function zap() {
|
|
$a = 1;
|
|
$b = 2;
|
|
}
|
|
|
|
/**
|
|
* @return Foo
|
|
*/
|
|
private function top(
|
|
D $c
|
|
) {
|
|
return $c;
|
|
}
|
|
}
|
|
',
|
|
'<?php
|
|
namespace C;
|
|
|
|
class A extends B
|
|
{
|
|
/**
|
|
* @return D
|
|
*/
|
|
public function rot() {
|
|
$c = 1;
|
|
}
|
|
|
|
/**
|
|
* @return D
|
|
*/
|
|
public function bar() {
|
|
return $c;
|
|
}
|
|
}',
|
|
[],
|
|
[],
|
|
['c\a::foo', 'c\a::bar', 'c\a::zap', 'c\a::top', 'c\a::rot', 'c\a::bar'],
|
|
[],
|
|
],
|
|
'noUseChange' => [
|
|
'<?php
|
|
namespace A;
|
|
|
|
use PhpParser;
|
|
use Psalm\Aliases;
|
|
|
|
class C
|
|
{
|
|
/**
|
|
* @return D
|
|
*/
|
|
public function foo() {
|
|
$c = 1;
|
|
}
|
|
}
|
|
',
|
|
'<?php
|
|
namespace A;
|
|
|
|
use PhpParser;
|
|
use Psalm\Aliases;
|
|
|
|
class C
|
|
{
|
|
/**
|
|
* @return D
|
|
*/
|
|
public function foo() {
|
|
$d = 1;
|
|
}
|
|
}
|
|
',
|
|
[],
|
|
['a\c::foo'],
|
|
[],
|
|
[],
|
|
],
|
|
'diffMultipleBadDocblocks' => [
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A
|
|
{
|
|
/**
|
|
* @param string $s
|
|
* @param string $t
|
|
* @return Database
|
|
*/
|
|
public static function foo()
|
|
{
|
|
return D::eep();
|
|
}
|
|
|
|
/**
|
|
* @param string $s
|
|
* @param string $t
|
|
* @return bool
|
|
*/
|
|
public static function bar()
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
/**
|
|
* @return C|null
|
|
*/
|
|
public static function bat()
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
',
|
|
'<?php
|
|
namespace Foo;
|
|
|
|
class A
|
|
{
|
|
/**
|
|
* @param string $s
|
|
* @param string
|
|
* @return Database
|
|
*/
|
|
public static function foo()
|
|
{
|
|
return D::eep();
|
|
}
|
|
|
|
/**
|
|
* @param string $s
|
|
* @param string
|
|
* @return bool
|
|
*/
|
|
public static function bar()
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
/**
|
|
* @return C|null
|
|
*/
|
|
public static function bat()
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
',
|
|
['foo\a::bat'],
|
|
[],
|
|
['foo\a::foo', 'foo\a::bar', 'foo\a::foo', 'foo\a::bar'],
|
|
[[-6, 0]],
|
|
],
|
|
];
|
|
}
|
|
}
|