1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Fix #1039 by invalidating all use users

This commit is contained in:
Matthew Brown 2018-10-26 23:04:38 -04:00
parent 00b51627cc
commit 1d77b61ff9
9 changed files with 314 additions and 5 deletions

View File

@ -335,8 +335,10 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
*
* @return string
*/
public static function getFQCLNFromNameObject(PhpParser\Node\Name $class_name, Aliases $aliases)
{
public static function getFQCLNFromNameObject(
PhpParser\Node\Name $class_name,
Aliases $aliases
) {
/** @var string|null */
$resolved_name = $class_name->getAttribute('resolvedName');

View File

@ -43,9 +43,21 @@ class NewChecker extends \Psalm\Checker\Statements\Expression\CallChecker
if ($stmt->class instanceof PhpParser\Node\Name) {
if (!in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)) {
$aliases = $statements_checker->getAliases();
if ($context->calling_method_id
&& !$stmt->class instanceof PhpParser\Node\Name\FullyQualified
&& isset($aliases->uses[strtolower($stmt->class->parts[0])])
) {
$codebase->file_reference_provider->addReferenceToClassMethod(
$context->calling_method_id,
'use:' . $stmt->class->parts[0] . ':' . \md5($statements_checker->getFilePath())
);
}
$fq_class_name = ClassLikeChecker::getFQCLNFromNameObject(
$stmt->class,
$statements_checker->getAliases()
$aliases
);
if ($context->check_classes) {

View File

@ -174,9 +174,21 @@ class StaticCallChecker extends \Psalm\Checker\Statements\Expression\CallChecker
return null;
}
} elseif ($context->check_classes) {
$aliases = $statements_checker->getAliases();
if ($context->calling_method_id
&& !$stmt->class instanceof PhpParser\Node\Name\FullyQualified
&& isset($aliases->uses[strtolower($stmt->class->parts[0])])
) {
$codebase->file_reference_provider->addReferenceToClassMethod(
$context->calling_method_id,
'use:' . $stmt->class->parts[0] . ':' . \md5($statements_checker->getFilePath())
);
}
$fq_class_name = ClassLikeChecker::getFQCLNFromNameObject(
$stmt->class,
$statements_checker->getAliases()
$aliases
);
if ($context->isPhantomClass($fq_class_name)) {

View File

@ -563,9 +563,21 @@ class PropertyFetchChecker
return null;
}
} else {
$aliases = $statements_checker->getAliases();
if ($context->calling_method_id
&& !$stmt->class instanceof PhpParser\Node\Name\FullyQualified
&& isset($aliases->uses[strtolower($stmt->class->parts[0])])
) {
$codebase->file_reference_provider->addReferenceToClassMethod(
$context->calling_method_id,
'use:' . $stmt->class->parts[0] . ':' . \md5($statements_checker->getFilePath())
);
}
$fq_class_name = ClassLikeChecker::getFQCLNFromNameObject(
$stmt->class,
$statements_checker->getAliases()
$aliases
);
if ($context->isPhantomClass($fq_class_name)) {

View File

@ -99,6 +99,20 @@ class FileStatementsDiffer extends AstDiffer
$add_or_delete = array_merge($add_or_delete, $class_keep[2]);
$diff_map = array_merge($diff_map, $class_keep[3]);
}
} elseif ($diff_elem->type === DiffElem::TYPE_REMOVE) {
if ($diff_elem->old instanceof PhpParser\Node\Stmt\Use_
|| $diff_elem->old instanceof PhpParser\Node\Stmt\GroupUse
) {
foreach ($diff_elem->old->uses as $use) {
if ($use->alias) {
$add_or_delete[] = 'use:' . (string) $use->alias;
} else {
$name_parts = $use->name->parts;
$add_or_delete[] = 'use:' . end($name_parts);
}
}
}
}
}

View File

@ -82,6 +82,20 @@ class NamespaceStatementsDiffer extends AstDiffer
$add_or_delete = array_merge($add_or_delete, $class_keep[2]);
$diff_map = array_merge($diff_map, $class_keep[3]);
}
} elseif ($diff_elem->type === DiffElem::TYPE_REMOVE) {
if ($diff_elem->old instanceof PhpParser\Node\Stmt\Use_
|| $diff_elem->old instanceof PhpParser\Node\Stmt\GroupUse
) {
foreach ($diff_elem->old->uses as $use) {
if ($use->alias) {
$add_or_delete[] = 'use:' . (string) $use->alias;
} else {
$name_parts = $use->name->parts;
$add_or_delete[] = 'use:' . end($name_parts);
}
}
}
}
}

View File

@ -169,6 +169,19 @@ class StatementsProvider
array_flip($unchanged_signature_members)
);
$file_path_hash = \md5($file_path);
$changed_members = array_map(
function (string $key) use ($file_path_hash) : string {
if (substr($key, 0, 4) === 'use:') {
return $key . ':' . $file_path_hash;
}
return $key;
},
$changed_members
);
$changed_members = array_map(
/**
* @param int $_

View File

@ -1349,6 +1349,30 @@ class FileDiffTest extends TestCase
[],
[]
],
'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]]
],
];
}
}

View File

@ -472,6 +472,212 @@ class TemporaryUpdateTest extends \Psalm\Tests\TestCase
],
'error_positions' => [[120], [120]],
],
'removeUseShouldInvalidate' => [
[
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
use Exception;
class A {
public function foo() : void {
throw new Exception();
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
public function foo() : void {
throw new Exception();
}
}',
],
],
'error_positions' => [[], [197]],
],
'removeGroupUseShouldInvalidate' => [
[
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
use PhpParser\{Error};
class A {
public function foo() : void {
throw new Error("bad", 5);
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
public function foo() : void {
throw new Error("bad", 5);
}
}',
],
],
'error_positions' => [[], [197]],
],
'removeUseWithAliasShouldInvalidate' => [
[
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
use Exception as E;
class A {
public function foo() : void {
throw new E();
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
public function foo() : void {
throw new E();
}
}',
],
],
'error_positions' => [[], [197]],
],
'removeGroupUseWithAliasShouldInvalidate' => [
[
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
use PhpParser\{Error as E};
class A {
public function foo() : void {
throw new E("bad", 5);
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
public function foo() : void {
throw new E("bad", 5);
}
}',
],
],
'error_positions' => [[], [197]],
],
'removeUseShouldInvalidateNoNamespace' => [
[
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
use PhpParser\Node\Name;
class A {
public function foo() : void {
new Name("Martin");
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
class A {
public function foo() : void {
new Name("Martin");
}
}',
],
],
'error_positions' => [[], [147]],
],
'removeGroupUseShouldInvalidateNoNamespace' => [
[
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
use PhpParser\{Error};
class A {
public function foo() : void {
throw new Error("bad", 5);
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
public function foo() : void {
throw new Error("bad", 5);
}
}',
],
],
'error_positions' => [[], [197]],
],
'removeUseWithAliasShouldInvalidateNoNamespace' => [
[
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
use Exception as E;
class A {
public function foo() : void {
throw new E();
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
class A {
public function foo() : void {
throw new E();
}
}',
],
],
'error_positions' => [[], [153]],
],
'removeGroupUseWithAliasShouldInvalidateNoNamespace' => [
[
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
use PhpParser\{Error as E};
class A {
public function foo() : void {
throw new E("bad", 5);
}
}',
],
[
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
namespace Foo;
class A {
public function foo() : void {
throw new E("bad", 5);
}
}',
],
],
'error_positions' => [[], [197]],
],
];
}
}