mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Invalidate signature types when use changes
This commit is contained in:
parent
b6dce59e9e
commit
67859ed19b
@ -720,7 +720,10 @@ class ReturnTypeAnalyzer
|
||||
$storage->return_type_location,
|
||||
$storage->suppressed_issues,
|
||||
[],
|
||||
false
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
$context->calling_method_id
|
||||
);
|
||||
|
||||
return null;
|
||||
|
@ -3390,13 +3390,15 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements FileSour
|
||||
$hint = $hint->type;
|
||||
}
|
||||
|
||||
if ($hint instanceof PhpParser\Node\Identifier) {
|
||||
$type_string = $hint->name;
|
||||
} elseif ($hint instanceof PhpParser\Node\Name\FullyQualified) {
|
||||
$type_string = (string)$hint;
|
||||
$type_string = null;
|
||||
|
||||
$this->codebase->scanner->queueClassLikeForScanning($type_string);
|
||||
$this->file_storage->referenced_classlikes[strtolower($type_string)] = $type_string;
|
||||
if ($hint instanceof PhpParser\Node\Identifier) {
|
||||
$fq_type_string = $hint->name;
|
||||
} elseif ($hint instanceof PhpParser\Node\Name\FullyQualified) {
|
||||
$fq_type_string = (string)$hint;
|
||||
|
||||
$this->codebase->scanner->queueClassLikeForScanning($fq_type_string);
|
||||
$this->file_storage->referenced_classlikes[strtolower($fq_type_string)] = $fq_type_string;
|
||||
} else {
|
||||
$lower_hint = strtolower($hint->parts[0]);
|
||||
|
||||
@ -3404,27 +3406,32 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements FileSour
|
||||
&& ($lower_hint === 'self' || $lower_hint === 'static')
|
||||
&& !end($this->classlike_storages)->is_trait
|
||||
) {
|
||||
$type_string = $this->fq_classlike_names[count($this->fq_classlike_names) - 1];
|
||||
$fq_type_string = $this->fq_classlike_names[count($this->fq_classlike_names) - 1];
|
||||
|
||||
if ($lower_hint === 'static') {
|
||||
$type_string .= '&static';
|
||||
$fq_type_string .= '&static';
|
||||
}
|
||||
} else {
|
||||
$type_string = ClassLikeAnalyzer::getFQCLNFromNameObject($hint, $this->aliases);
|
||||
}
|
||||
$type_string = implode('\\', $hint->parts);
|
||||
$fq_type_string = ClassLikeAnalyzer::getFQCLNFromNameObject($hint, $this->aliases);
|
||||
|
||||
if (!in_array($lower_hint, ['self', 'static', 'parent'], true)) {
|
||||
$this->codebase->scanner->queueClassLikeForScanning($type_string);
|
||||
$this->file_storage->referenced_classlikes[strtolower($type_string)] = $type_string;
|
||||
$this->codebase->scanner->queueClassLikeForScanning($fq_type_string);
|
||||
$this->file_storage->referenced_classlikes[strtolower($fq_type_string)] = $fq_type_string;
|
||||
}
|
||||
}
|
||||
|
||||
$type = Type::parseString(
|
||||
$type_string,
|
||||
$fq_type_string,
|
||||
[$this->php_major_version, $this->php_minor_version],
|
||||
[]
|
||||
);
|
||||
|
||||
if ($type_string) {
|
||||
$atomic_types = $type->getAtomicTypes();
|
||||
$atomic_type = reset($atomic_types);
|
||||
$atomic_type->text = $type_string;
|
||||
}
|
||||
|
||||
if ($is_nullable) {
|
||||
$type->addType(new Type\Atomic\TNull);
|
||||
}
|
||||
|
@ -21,6 +21,11 @@ class Value extends \Psalm\Internal\Type\ParseTree
|
||||
*/
|
||||
public $offset_end;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
public $text;
|
||||
|
||||
/**
|
||||
* @param \Psalm\Internal\Type\ParseTree|null $parent
|
||||
*/
|
||||
@ -28,11 +33,13 @@ class Value extends \Psalm\Internal\Type\ParseTree
|
||||
string $value,
|
||||
int $offset_start,
|
||||
int $offset_end,
|
||||
?string $text,
|
||||
\Psalm\Internal\Type\ParseTree $parent = null
|
||||
) {
|
||||
$this->offset_start = $offset_start;
|
||||
$this->offset_end = $offset_end;
|
||||
$this->value = $value;
|
||||
$this->parent = $parent;
|
||||
$this->text = $text === $value ? null : $text;
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ class ParseTreeCreator
|
||||
private $t = 0;
|
||||
|
||||
/**
|
||||
* @param list<array{0: string, 1: int}> $type_tokens
|
||||
* @param list<array{0: string, 1: int, 2?: string}> $type_tokens
|
||||
*/
|
||||
public function __construct(array $type_tokens)
|
||||
{
|
||||
@ -715,7 +715,7 @@ class ParseTreeCreator
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array{0: string, 1: int} $type_token */
|
||||
/** @param array{0: string, 1: int, 2?: string} $type_token */
|
||||
private function handleValue(array $type_token) : void
|
||||
{
|
||||
$new_parent = !$this->current_leaf instanceof ParseTree\Root ? $this->current_leaf : null;
|
||||
@ -793,6 +793,7 @@ class ParseTreeCreator
|
||||
$type_token[0] . '::' . $nexter_token[0],
|
||||
$type_token[1],
|
||||
$type_token[1] + 2 + strlen($nexter_token[0]),
|
||||
$type_token[2] ?? null,
|
||||
$new_parent
|
||||
);
|
||||
|
||||
@ -809,6 +810,7 @@ class ParseTreeCreator
|
||||
$type_token[0],
|
||||
$type_token[1],
|
||||
$type_token[1] + strlen($type_token[0]),
|
||||
$type_token[2] ?? null,
|
||||
$new_parent
|
||||
);
|
||||
break;
|
||||
|
@ -52,7 +52,7 @@ class TypeParser
|
||||
/**
|
||||
* Parses a string type representation
|
||||
*
|
||||
* @param list<array{0: string, 1: int}> $type_tokens
|
||||
* @param list<array{0: string, 1: int, 2?: string}> $type_tokens
|
||||
* @param array{int,int}|null $php_version
|
||||
* @param array<string, array<string, array{Union}>> $template_type_map
|
||||
* @param array<string, TypeAlias> $type_aliases
|
||||
@ -81,6 +81,7 @@ class TypeParser
|
||||
$atomic = Atomic::create($only_token[0], $php_version, $template_type_map, $type_aliases);
|
||||
$atomic->offset_start = 0;
|
||||
$atomic->offset_end = strlen($only_token[0]);
|
||||
$atomic->text = isset($only_token[2]) && $only_token[2] !== $only_token[0] ? $only_token[2] : null;
|
||||
|
||||
return new Union([$atomic]);
|
||||
}
|
||||
@ -186,7 +187,7 @@ class TypeParser
|
||||
$traversable = new TGenericObject('Traversable', $generic_params);
|
||||
$array_acccess = new TGenericObject('ArrayAccess', $generic_params);
|
||||
$countable = new TNamedObject('Countable');
|
||||
|
||||
|
||||
$traversable->extra_types[$array_acccess->getKey()] = $array_acccess;
|
||||
$traversable->extra_types[$countable->getKey()] = $countable;
|
||||
|
||||
@ -935,6 +936,7 @@ class TypeParser
|
||||
|
||||
$atomic_type->offset_start = $parse_tree->offset_start;
|
||||
$atomic_type->offset_end = $parse_tree->offset_end;
|
||||
$atomic_type->text = $parse_tree->text;
|
||||
|
||||
return $atomic_type;
|
||||
}
|
||||
|
@ -328,7 +328,7 @@ class TypeTokenizer
|
||||
* @param array<string, mixed>|null $template_type_map
|
||||
* @param array<string, TypeAlias>|null $type_aliases
|
||||
*
|
||||
* @return list<array{0: string, 1: int}>
|
||||
* @return list<array{0: string, 1: int, 2?: string}>
|
||||
*/
|
||||
public static function getFullyQualifiedTokens(
|
||||
string $string_type,
|
||||
@ -448,6 +448,8 @@ class TypeTokenizer
|
||||
continue;
|
||||
}
|
||||
|
||||
$type_tokens[$i][2] = $string_type_token[0];
|
||||
|
||||
if (isset($type_aliases[$string_type_token[0]])) {
|
||||
$type_alias = $type_aliases[$string_type_token[0]];
|
||||
|
||||
@ -472,7 +474,7 @@ class TypeTokenizer
|
||||
}
|
||||
}
|
||||
|
||||
/** @var list<array{0: string, 1: int}> */
|
||||
/** @var list<array{0: string, 1: int, 2?: string}> */
|
||||
return $type_tokens;
|
||||
}
|
||||
|
||||
|
@ -149,6 +149,15 @@ class TypeChecker extends NodeVisitor
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->calling_method_id
|
||||
&& $atomic->text !== null
|
||||
) {
|
||||
$codebase->file_reference_provider->addMethodReferenceToClassMember(
|
||||
$this->calling_method_id,
|
||||
'use:' . $atomic->text . ':' . \md5($this->source->getFilePath())
|
||||
);
|
||||
}
|
||||
|
||||
if (!isset($this->phantom_classes[\strtolower($atomic->value)]) &&
|
||||
ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
|
||||
$this->source,
|
||||
|
@ -82,6 +82,11 @@ abstract class Atomic implements TypeNode
|
||||
*/
|
||||
public $offset_end;
|
||||
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
public $text;
|
||||
|
||||
/**
|
||||
* @param array{int,int}|null $php_version
|
||||
* @param array<string, array<string, array{Union}>> $template_type_map
|
||||
|
@ -788,6 +788,80 @@ class TemporaryUpdateTest extends \Psalm\Tests\TestCase
|
||||
],
|
||||
'error_positions' => [[197], []],
|
||||
],
|
||||
'changeUseShouldInvalidateBadNew' => [
|
||||
[
|
||||
[
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
|
||||
namespace Foo {
|
||||
use Baz\B;
|
||||
|
||||
class A {
|
||||
public function foo() : void {
|
||||
new B();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Bar {
|
||||
class B {}
|
||||
}',
|
||||
],
|
||||
[
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
|
||||
namespace Foo {
|
||||
use Bar\B;
|
||||
|
||||
class A {
|
||||
public function foo() : void {
|
||||
new B();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Bar {
|
||||
class B {}
|
||||
}',
|
||||
],
|
||||
],
|
||||
'error_positions' => [[247], []],
|
||||
],
|
||||
'changeUseShouldInvalidateBadReturn' => [
|
||||
[
|
||||
[
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
|
||||
namespace Foo {
|
||||
use Baz\B;
|
||||
|
||||
class A {
|
||||
public function foo() : ?B {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Bar {
|
||||
class B {}
|
||||
}',
|
||||
],
|
||||
[
|
||||
getcwd() . DIRECTORY_SEPARATOR . 'A.php' => '<?php
|
||||
namespace Foo {
|
||||
use Bar\B;
|
||||
|
||||
class A {
|
||||
public function foo() : ?B {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Bar {
|
||||
class B {}
|
||||
}',
|
||||
],
|
||||
],
|
||||
'error_positions' => [[196], []],
|
||||
],
|
||||
'fixMissingProperty' => [
|
||||
[
|
||||
[
|
||||
|
Loading…
Reference in New Issue
Block a user