1
0
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:
Brown 2018-10-03 18:16:33 -04:00
parent f2cea0325f
commit 6707672528
6 changed files with 128 additions and 67 deletions

View File

@ -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;

View File

@ -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]);
}
}
}

View File

@ -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];
}
}

View File

@ -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) ?: [];

View File

@ -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'],
[]
],
];

View File

@ -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' => [],
]
],
];
}