mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Improve handling of traits further
This commit is contained in:
parent
f2cea0325f
commit
6707672528
@ -886,9 +886,15 @@ class ClassChecker extends ClassLikeChecker
|
||||
}
|
||||
}
|
||||
|
||||
$trait_safe_method_id = strtolower($analyzed_method_id);
|
||||
|
||||
if (strtolower($actual_method_id) !== $trait_safe_method_id) {
|
||||
$trait_safe_method_id .= '&' . strtolower($actual_method_id);
|
||||
}
|
||||
|
||||
$is_method_correct = $codebase->analyzer->isMethodCorrect(
|
||||
$included_file_path,
|
||||
strtolower($analyzed_method_id)
|
||||
$trait_safe_method_id
|
||||
);
|
||||
|
||||
if ($is_method_correct
|
||||
@ -1006,7 +1012,7 @@ class ClassChecker extends ClassLikeChecker
|
||||
&& !$class_context->collect_mutations
|
||||
&& !$is_fake
|
||||
) {
|
||||
$codebase->analyzer->setCorrectMethod($included_file_path, strtolower($analyzed_method_id));
|
||||
$codebase->analyzer->setCorrectMethod($included_file_path, $trait_safe_method_id);
|
||||
}
|
||||
|
||||
return $method_checker;
|
||||
|
@ -301,19 +301,10 @@ class Analyzer
|
||||
|
||||
$statements_provider = $project_checker->codebase->statements_provider;
|
||||
|
||||
$unchanged_members = $statements_provider->getUnchangedMembers();
|
||||
$changed_members = $statements_provider->getChangedMembers();
|
||||
|
||||
$diff_map = $statements_provider->getDiffMap();
|
||||
|
||||
$method_map = [];
|
||||
|
||||
foreach ($this->correct_methods as $file_path => $correct_methods) {
|
||||
foreach ($correct_methods as $correct_method_id => $_) {
|
||||
$method_map[$correct_method_id] = $file_path;
|
||||
}
|
||||
}
|
||||
|
||||
$all_referencing_methods = $project_checker->file_reference_provider->getMethodsReferencing();
|
||||
|
||||
foreach ($all_referencing_methods as $member_id => $referencing_method_ids) {
|
||||
@ -338,6 +329,8 @@ class Analyzer
|
||||
|
||||
foreach ($changed_members as $file_changed_members) {
|
||||
foreach ($file_changed_members as $member_id => $_) {
|
||||
$newly_invalidated_methods[$member_id] = true;
|
||||
|
||||
if (isset($all_referencing_methods[$member_id])) {
|
||||
$newly_invalidated_methods = array_merge(
|
||||
$all_referencing_methods[$member_id],
|
||||
@ -358,17 +351,17 @@ class Analyzer
|
||||
|
||||
foreach ($this->correct_methods as $file_path => $correct_methods) {
|
||||
foreach ($correct_methods as $correct_method_id => $_) {
|
||||
$correct_method_stub = preg_replace('/::.*$/', '::*', $correct_method_id);
|
||||
$trait_safe_method_id = $correct_method_id;
|
||||
|
||||
if (isset($newly_invalidated_methods[$correct_method_id])) {
|
||||
unset($this->correct_methods[$file_path][$correct_method_id]);
|
||||
}
|
||||
$correct_method_ids = explode('&', $correct_method_id);
|
||||
|
||||
if (isset($unchanged_members[$file_path])
|
||||
&& !isset($unchanged_members[$file_path][$correct_method_id])
|
||||
&& !isset($unchanged_members[$file_path][$correct_method_stub])
|
||||
$correct_method_id = $correct_method_ids[0];
|
||||
|
||||
if (isset($newly_invalidated_methods[$correct_method_id])
|
||||
|| (isset($correct_method_ids[1])
|
||||
&& isset($newly_invalidated_methods[$correct_method_ids[1]]))
|
||||
) {
|
||||
unset($this->correct_methods[$file_path][$correct_method_id]);
|
||||
unset($this->correct_methods[$file_path][$trait_safe_method_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ class ClassStatementsDiffer extends Differ
|
||||
|
||||
$keep = [];
|
||||
$keep_signature = [];
|
||||
$delete = [];
|
||||
$add_or_delete = [];
|
||||
|
||||
foreach ($diff as $diff_elem) {
|
||||
if ($diff_elem->type === DiffElem::TYPE_KEEP) {
|
||||
@ -167,25 +167,27 @@ class ClassStatementsDiffer extends Differ
|
||||
$keep_signature[] = strtolower($name) . '::$' . $prop->name;
|
||||
}
|
||||
}
|
||||
} elseif ($diff_elem->type === DiffElem::TYPE_REMOVE) {
|
||||
if ($diff_elem->old instanceof PhpParser\Node\Stmt\ClassMethod) {
|
||||
$delete[] = strtolower($name) . '::' . strtolower((string) $diff_elem->old->name);
|
||||
} elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\Property) {
|
||||
foreach ($diff_elem->old->props as $prop) {
|
||||
$delete[] = strtolower($name) . '::$' . $prop->name;
|
||||
} elseif ($diff_elem->type === DiffElem::TYPE_REMOVE || $diff_elem->type === DiffElem::TYPE_ADD) {
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
$affected_elem = $diff_elem->type === DiffElem::TYPE_REMOVE ? $diff_elem->old : $diff_elem->new;
|
||||
if ($affected_elem instanceof PhpParser\Node\Stmt\ClassMethod) {
|
||||
$add_or_delete[] = strtolower($name) . '::' . strtolower((string) $affected_elem->name);
|
||||
} elseif ($affected_elem instanceof PhpParser\Node\Stmt\Property) {
|
||||
foreach ($affected_elem->props as $prop) {
|
||||
$add_or_delete[] = strtolower($name) . '::$' . $prop->name;
|
||||
}
|
||||
} elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\ClassConst) {
|
||||
foreach ($diff_elem->old->consts as $const) {
|
||||
$delete[] = strtolower($name) . '::' . $const->name;
|
||||
} elseif ($affected_elem instanceof PhpParser\Node\Stmt\ClassConst) {
|
||||
foreach ($affected_elem->consts as $const) {
|
||||
$add_or_delete[] = strtolower($name) . '::' . $const->name;
|
||||
}
|
||||
} elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\TraitUse) {
|
||||
foreach ($diff_elem->old->traits as $trait) {
|
||||
$delete[] = strtolower((string) $trait->getAttribute('resolvedName')) . '::*';
|
||||
} elseif ($affected_elem instanceof PhpParser\Node\Stmt\TraitUse) {
|
||||
foreach ($affected_elem->traits as $trait) {
|
||||
$add_or_delete[] = strtolower((string) $trait->getAttribute('resolvedName')) . '::*';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return [$keep, $keep_signature, $delete, $diff_map];
|
||||
return [$keep, $keep_signature, $add_or_delete, $diff_map];
|
||||
}
|
||||
}
|
||||
|
@ -205,14 +205,6 @@ class StatementsProvider
|
||||
return $stmts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, bool>>
|
||||
*/
|
||||
public function getUnchangedMembers()
|
||||
{
|
||||
return $this->unchanged_members;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, bool>>
|
||||
*/
|
||||
@ -276,7 +268,7 @@ class StatementsProvider
|
||||
}
|
||||
|
||||
$error_handler = new \PhpParser\ErrorHandler\Collecting();
|
||||
|
||||
|
||||
/** @var array<int, \PhpParser\Node\Stmt> */
|
||||
$stmts = self::$parser->parse($file_contents, $error_handler) ?: [];
|
||||
|
||||
|
@ -197,7 +197,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
['foo\a::foo'],
|
||||
[],
|
||||
['foo\a::bar'],
|
||||
['foo\a::bar', 'foo\a::bar'],
|
||||
[[0, 0]]
|
||||
],
|
||||
'propertyChange' => [
|
||||
@ -215,7 +215,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
[],
|
||||
[],
|
||||
['foo\a::$a'],
|
||||
['foo\a::$a', 'foo\a::$b'],
|
||||
[]
|
||||
],
|
||||
'propertyDefaultChange' => [
|
||||
@ -271,7 +271,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
[],
|
||||
[],
|
||||
['foo\a::$a'],
|
||||
['foo\a::$a', 'foo\a::$a'],
|
||||
[]
|
||||
],
|
||||
'propertyStaticChange' => [
|
||||
@ -291,7 +291,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
[],
|
||||
[],
|
||||
['foo\a::$a'],
|
||||
['foo\a::$a', 'foo\a::$a'],
|
||||
[]
|
||||
],
|
||||
'propertyVisibilityChange' => [
|
||||
@ -311,7 +311,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
[],
|
||||
[],
|
||||
['foo\a::$a'],
|
||||
['foo\a::$a', 'foo\a::$a'],
|
||||
[]
|
||||
],
|
||||
'addDocblockToFirst' => [
|
||||
@ -342,7 +342,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
['foo\a::bar'],
|
||||
[],
|
||||
['foo\a::foo'],
|
||||
['foo\a::foo', 'foo\a::foo'],
|
||||
[[84, 3]]
|
||||
],
|
||||
'addDocblockToSecond' => [
|
||||
@ -373,7 +373,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
['foo\a::foo'],
|
||||
[],
|
||||
['foo\a::bar'],
|
||||
['foo\a::bar', 'foo\a::bar'],
|
||||
[[0, 0]]
|
||||
],
|
||||
'removeDocblock' => [
|
||||
@ -404,7 +404,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
['foo\a::foo'],
|
||||
[],
|
||||
['foo\a::bar'],
|
||||
['foo\a::bar', 'foo\a::bar'],
|
||||
[[0, 0]]
|
||||
],
|
||||
'changeDocblock' => [
|
||||
@ -438,7 +438,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
['foo\a::foo'],
|
||||
[],
|
||||
['foo\a::bar'],
|
||||
['foo\a::bar', 'foo\a::bar'],
|
||||
[[0, 0]]
|
||||
],
|
||||
'changeMethodVisibility' => [
|
||||
@ -466,7 +466,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
['foo\a::foo'],
|
||||
[],
|
||||
['foo\a::bar'],
|
||||
['foo\a::bar', 'foo\a::bar'],
|
||||
[[0, 0]]
|
||||
],
|
||||
'removeFunctionAtEnd' => [
|
||||
@ -638,7 +638,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
['foo\a::foo', 'foo\a::bar'],
|
||||
[],
|
||||
[],
|
||||
['foo\a::bat'],
|
||||
[[0, 0], [0, 0]]
|
||||
],
|
||||
'newFunctionAtBeginning' => [
|
||||
@ -669,7 +669,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
['foo\a::foo', 'foo\a::bar'],
|
||||
[],
|
||||
[],
|
||||
['foo\a::bat'],
|
||||
[[98, 3], [98, 3]]
|
||||
],
|
||||
'newFunctionInMiddle' => [
|
||||
@ -700,7 +700,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
['foo\a::foo', 'foo\a::bar'],
|
||||
[],
|
||||
[],
|
||||
['foo\a::bat'],
|
||||
[[0, 0], [98, 3]]
|
||||
],
|
||||
'SKIPPED-whiteSpaceOnly' => [
|
||||
@ -764,7 +764,7 @@ class FileDiffTest extends TestCase
|
||||
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' => [
|
||||
@ -818,7 +818,7 @@ class FileDiffTest extends TestCase
|
||||
}',
|
||||
[],
|
||||
[],
|
||||
['foo\t::$a'],
|
||||
['foo\t::$a', 'foo\t::$b'],
|
||||
[]
|
||||
],
|
||||
];
|
||||
|
@ -472,7 +472,7 @@ class FileUpdateTest extends TestCase
|
||||
],
|
||||
]
|
||||
],
|
||||
'SKIPPED-dontInvalidateTraitMethods' => [
|
||||
'dontInvalidateTraitMethods' => [
|
||||
'start_files' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
|
||||
namespace Foo;
|
||||
@ -539,7 +539,7 @@ class FileUpdateTest extends TestCase
|
||||
],
|
||||
'initial_correct_methods' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => [
|
||||
'foo\a::barbar' => true,
|
||||
'foo\a::barbar&foo\t::barbar' => true,
|
||||
'foo\a::foofoo' => true,
|
||||
],
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'B.php' => [
|
||||
@ -550,7 +550,7 @@ class FileUpdateTest extends TestCase
|
||||
],
|
||||
'unaffected_correct_methods' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => [
|
||||
'foo\a::barbar' => true,
|
||||
'foo\a::barbar&foo\t::barbar' => true,
|
||||
],
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'B.php' => [
|
||||
'foo\b::bar' => true,
|
||||
@ -622,7 +622,7 @@ class FileUpdateTest extends TestCase
|
||||
],
|
||||
'initial_correct_methods' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => [
|
||||
'foo\a::barbar' => true,
|
||||
'foo\a::barbar&foo\t::barbar' => true,
|
||||
'foo\a::foofoo' => true,
|
||||
],
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'B.php' => [
|
||||
@ -631,7 +631,9 @@ class FileUpdateTest extends TestCase
|
||||
],
|
||||
],
|
||||
'unaffected_correct_methods' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => [],
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => [
|
||||
'foo\a::barbar&foo\t::barbar' => true, // this doesn't exist, so we don't care
|
||||
],
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'B.php' => [],
|
||||
]
|
||||
],
|
||||
@ -700,7 +702,7 @@ class FileUpdateTest extends TestCase
|
||||
],
|
||||
'initial_correct_methods' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => [
|
||||
'foo\a::barbar' => true,
|
||||
'foo\a::barbar&foo\t::barbar' => true,
|
||||
'foo\a::foofoo' => true,
|
||||
],
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'B.php' => [
|
||||
@ -713,7 +715,7 @@ class FileUpdateTest extends TestCase
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'B.php' => [],
|
||||
]
|
||||
],
|
||||
'SKIPPED-invalidateTraitMethodsWhenMethodChanged' => [
|
||||
'invalidateTraitMethodsWhenMethodChanged' => [
|
||||
'start_files' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
|
||||
namespace Foo;
|
||||
@ -784,8 +786,8 @@ class FileUpdateTest extends TestCase
|
||||
],
|
||||
'initial_correct_methods' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => [
|
||||
'foo\a::barbar' => true,
|
||||
'foo\a::bat' => true,
|
||||
'foo\a::barbar&foo\t::barbar' => true,
|
||||
'foo\a::bat&foo\t::bat' => true,
|
||||
'foo\a::foofoo' => true,
|
||||
],
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'B.php' => [
|
||||
@ -795,11 +797,77 @@ class FileUpdateTest extends TestCase
|
||||
],
|
||||
'unaffected_correct_methods' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => [
|
||||
'foo\a::bat' => true,
|
||||
'foo\a::bat&foo\t::bat' => true,
|
||||
],
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'B.php' => [],
|
||||
]
|
||||
],
|
||||
'invalidateTraitMethodsWhenMethodSuperimposed' => [
|
||||
'start_files' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
|
||||
namespace Foo;
|
||||
|
||||
class A {
|
||||
use T;
|
||||
}',
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'B.php' => '<?php
|
||||
namespace Foo;
|
||||
|
||||
class B {
|
||||
public function bar() : string {
|
||||
return (new A)->barBar();
|
||||
}
|
||||
}',
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'T.php' => '<?php
|
||||
namespace Foo;
|
||||
|
||||
trait T {
|
||||
public function barBar(): string {
|
||||
return "hello";
|
||||
}
|
||||
}',
|
||||
],
|
||||
'end_files' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
|
||||
namespace Foo;
|
||||
|
||||
class A {
|
||||
use T;
|
||||
|
||||
public function barBar(): int {
|
||||
return 5;
|
||||
}
|
||||
}',
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'B.php' => '<?php
|
||||
namespace Foo;
|
||||
|
||||
class B {
|
||||
public function bar() : string {
|
||||
return (new A)->barBar();
|
||||
}
|
||||
}',
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'T.php' => '<?php
|
||||
namespace Foo;
|
||||
|
||||
trait T {
|
||||
public function barBar(): string {
|
||||
return "hello";
|
||||
}
|
||||
}',
|
||||
],
|
||||
'initial_correct_methods' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => [
|
||||
'foo\a::barbar&foo\t::barbar' => true,
|
||||
],
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'B.php' => [
|
||||
'foo\b::bar' => true,
|
||||
],
|
||||
],
|
||||
'unaffected_correct_methods' => [
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => [],
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'B.php' => [],
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user