mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
remove support for allowPhpstormGenerics
This commit is contained in:
parent
0f2ae281ae
commit
4f7c1d05c1
@ -51,14 +51,6 @@
|
|||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
|
||||||
<xs:attribute name="allowFileIncludes" type="xs:boolean" default="true" />
|
<xs:attribute name="allowFileIncludes" type="xs:boolean" default="true" />
|
||||||
<xs:attribute name="allowPhpStormGenerics" type="xs:boolean" default="false">
|
|
||||||
<xs:annotation>
|
|
||||||
<!-- note: for PHPStorm to mark the attribute as deprecated the doc entry has to be *single line* and start with the word `deprecated` -->
|
|
||||||
<xs:documentation xml:lang="en">
|
|
||||||
Deprecated. PHPStorm now supports generics for the most part and @psalm- annotations can be used
|
|
||||||
</xs:documentation>
|
|
||||||
</xs:annotation>
|
|
||||||
</xs:attribute>
|
|
||||||
<xs:attribute name="allowStringToStandInForClass" type="xs:boolean" default="false" />
|
<xs:attribute name="allowStringToStandInForClass" type="xs:boolean" default="false" />
|
||||||
<xs:attribute name="checkForThrowsDocblock" type="xs:boolean" default="false" />
|
<xs:attribute name="checkForThrowsDocblock" type="xs:boolean" default="false" />
|
||||||
<xs:attribute name="checkForThrowsInGlobalScope" type="xs:boolean" default="false" />
|
<xs:attribute name="checkForThrowsInGlobalScope" type="xs:boolean" default="false" />
|
||||||
|
@ -134,17 +134,6 @@ If true we force strict typing on numerical and string operations (see https://g
|
|||||||
```
|
```
|
||||||
Setting this to `false` means that any function calls will cause Psalm to forget anything it knew about object properties within the scope of the function it's currently analysing. This duplicates functionality that Hack has. Defaults to `true`.
|
Setting this to `false` means that any function calls will cause Psalm to forget anything it knew about object properties within the scope of the function it's currently analysing. This duplicates functionality that Hack has. Defaults to `true`.
|
||||||
|
|
||||||
#### allowPhpStormGenerics
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<psalm
|
|
||||||
allowPhpStormGenerics="[bool]"
|
|
||||||
>
|
|
||||||
```
|
|
||||||
Allows you to specify whether or not to use the typed iterator docblock format supported by PHP Storm e.g. `ArrayIterator|string[]`, which Psalm transforms to `ArrayIterator<string>`. Defaults to `false`.
|
|
||||||
|
|
||||||
This flag is deprecated and will be removed in Psalm 5
|
|
||||||
|
|
||||||
#### allowStringToStandInForClass
|
#### allowStringToStandInForClass
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
|
@ -315,11 +315,6 @@ class Config
|
|||||||
/** @var bool */
|
/** @var bool */
|
||||||
public $use_igbinary = false;
|
public $use_igbinary = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
public $allow_phpstorm_generics = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
@ -887,7 +882,6 @@ class Config
|
|||||||
'allowFileIncludes' => 'allow_includes',
|
'allowFileIncludes' => 'allow_includes',
|
||||||
'strictBinaryOperands' => 'strict_binary_operands',
|
'strictBinaryOperands' => 'strict_binary_operands',
|
||||||
'rememberPropertyAssignmentsAfterCall' => 'remember_property_assignments_after_call',
|
'rememberPropertyAssignmentsAfterCall' => 'remember_property_assignments_after_call',
|
||||||
'allowPhpStormGenerics' => 'allow_phpstorm_generics',
|
|
||||||
'allowStringToStandInForClass' => 'allow_string_standin_for_class',
|
'allowStringToStandInForClass' => 'allow_string_standin_for_class',
|
||||||
'usePhpDocMethodsWithoutMagicCall' => 'use_phpdoc_method_without_magic_or_parent',
|
'usePhpDocMethodsWithoutMagicCall' => 'use_phpdoc_method_without_magic_or_parent',
|
||||||
'usePhpDocPropertiesWithoutMagicCall' => 'use_phpdoc_property_without_magic_or_parent',
|
'usePhpDocPropertiesWithoutMagicCall' => 'use_phpdoc_property_without_magic_or_parent',
|
||||||
|
@ -14,11 +14,6 @@ use Psalm\IssueBuffer;
|
|||||||
use Psalm\Progress\Progress;
|
use Psalm\Progress\Progress;
|
||||||
use Psalm\Storage\ClassLikeStorage;
|
use Psalm\Storage\ClassLikeStorage;
|
||||||
use Psalm\Storage\FileStorage;
|
use Psalm\Storage\FileStorage;
|
||||||
use Psalm\Type;
|
|
||||||
use Psalm\Type\Atomic\TArray;
|
|
||||||
use Psalm\Type\Atomic\TGenericObject;
|
|
||||||
use Psalm\Type\Atomic\TIterable;
|
|
||||||
use Psalm\Type\Atomic\TNamedObject;
|
|
||||||
use Psalm\Type\Atomic\TTemplateParam;
|
use Psalm\Type\Atomic\TTemplateParam;
|
||||||
use Psalm\Type\Union;
|
use Psalm\Type\Union;
|
||||||
|
|
||||||
@ -110,26 +105,6 @@ class Populator
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->classlike_storage_provider->getNew() as $class_storage) {
|
foreach ($this->classlike_storage_provider->getNew() as $class_storage) {
|
||||||
if ($this->config->allow_phpstorm_generics) {
|
|
||||||
foreach ($class_storage->properties as $property_storage) {
|
|
||||||
if ($property_storage->type) {
|
|
||||||
$this->convertPhpStormGenericToPsalmGeneric($property_storage->type, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($class_storage->methods as $method_storage) {
|
|
||||||
if ($method_storage->return_type) {
|
|
||||||
$this->convertPhpStormGenericToPsalmGeneric($method_storage->return_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($method_storage->params as $param_storage) {
|
|
||||||
if ($param_storage->type) {
|
|
||||||
$this->convertPhpStormGenericToPsalmGeneric($param_storage->type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($class_storage->dependent_classlikes as $dependent_classlike_lc => $_) {
|
foreach ($class_storage->dependent_classlikes as $dependent_classlike_lc => $_) {
|
||||||
try {
|
try {
|
||||||
$dependee_storage = $this->classlike_storage_provider->get($dependent_classlike_lc);
|
$dependee_storage = $this->classlike_storage_provider->get($dependent_classlike_lc);
|
||||||
@ -141,22 +116,6 @@ class Populator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->config->allow_phpstorm_generics) {
|
|
||||||
foreach ($all_file_storage as $file_storage) {
|
|
||||||
foreach ($file_storage->functions as $function_storage) {
|
|
||||||
if ($function_storage->return_type) {
|
|
||||||
$this->convertPhpStormGenericToPsalmGeneric($function_storage->return_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($function_storage->params as $param_storage) {
|
|
||||||
if ($param_storage->type) {
|
|
||||||
$this->convertPhpStormGenericToPsalmGeneric($param_storage->type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->progress->debug('FileStorage is populated' . "\n");
|
$this->progress->debug('FileStorage is populated' . "\n");
|
||||||
|
|
||||||
ClassLikeStorageProvider::populated();
|
ClassLikeStorageProvider::populated();
|
||||||
@ -991,65 +950,6 @@ class Populator
|
|||||||
$storage->populated = true;
|
$storage->populated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function convertPhpStormGenericToPsalmGeneric(Union $candidate, bool $is_property = false): void
|
|
||||||
{
|
|
||||||
if (!$candidate->from_docblock) {
|
|
||||||
//never convert a type that comes from a signature
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$atomic_types = $candidate->getAtomicTypes();
|
|
||||||
|
|
||||||
if (isset($atomic_types['array']) && count($atomic_types) > 1 && !isset($atomic_types['null'])) {
|
|
||||||
$iterator_name = null;
|
|
||||||
$generic_params = null;
|
|
||||||
$iterator_key = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
foreach ($atomic_types as $type_key => $type) {
|
|
||||||
if ($type instanceof TIterable
|
|
||||||
|| ($type instanceof TNamedObject
|
|
||||||
&& (!$type->from_docblock || $is_property)
|
|
||||||
&& (
|
|
||||||
strtolower($type->value) === 'traversable'
|
|
||||||
|| $this->classlikes->interfaceExtends(
|
|
||||||
$type->value,
|
|
||||||
'Traversable'
|
|
||||||
)
|
|
||||||
|| $this->classlikes->classImplements(
|
|
||||||
$type->value,
|
|
||||||
'Traversable'
|
|
||||||
)
|
|
||||||
))
|
|
||||||
) {
|
|
||||||
$iterator_name = $type->value;
|
|
||||||
$iterator_key = $type_key;
|
|
||||||
} elseif ($type instanceof TArray) {
|
|
||||||
$generic_params = $type->type_params;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InvalidArgumentException $e) {
|
|
||||||
// ignore class-not-found issues
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($iterator_name && $iterator_key && $generic_params) {
|
|
||||||
if ($iterator_name === 'iterable') {
|
|
||||||
$generic_iterator = new TIterable($generic_params);
|
|
||||||
} else {
|
|
||||||
if (strtolower($iterator_name) === 'generator') {
|
|
||||||
$generic_params[] = Type::getMixed();
|
|
||||||
$generic_params[] = Type::getMixed();
|
|
||||||
}
|
|
||||||
$generic_iterator = new TGenericObject($iterator_name, $generic_params);
|
|
||||||
}
|
|
||||||
|
|
||||||
$candidate->removeType('array');
|
|
||||||
$candidate->removeType($iterator_key);
|
|
||||||
$candidate->addType($generic_iterator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function inheritMethodsFromParent(
|
protected function inheritMethodsFromParent(
|
||||||
ClassLikeStorage $storage,
|
ClassLikeStorage $storage,
|
||||||
ClassLikeStorage $parent_storage
|
ClassLikeStorage $parent_storage
|
||||||
|
@ -180,8 +180,7 @@ class TypeCombiner
|
|||||||
&& (isset($combination->named_object_types['Traversable'])
|
&& (isset($combination->named_object_types['Traversable'])
|
||||||
|| isset($combination->builtin_type_params['Traversable']))
|
|| isset($combination->builtin_type_params['Traversable']))
|
||||||
&& (
|
&& (
|
||||||
($codebase && $codebase->config->allow_phpstorm_generics)
|
isset($combination->builtin_type_params['Traversable'])
|
||||||
|| isset($combination->builtin_type_params['Traversable'])
|
|
||||||
|| (isset($combination->named_object_types['Traversable'])
|
|| (isset($combination->named_object_types['Traversable'])
|
||||||
&& $combination->named_object_types['Traversable']->from_docblock)
|
&& $combination->named_object_types['Traversable']->from_docblock)
|
||||||
)
|
)
|
||||||
|
@ -22,149 +22,6 @@ class AnnotationTest extends TestCase
|
|||||||
$codebase->reportUnusedVariables();
|
$codebase->reportUnusedVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPhpStormGenericsWithValidArrayIteratorArgument(): void
|
|
||||||
{
|
|
||||||
Config::getInstance()->allow_phpstorm_generics = true;
|
|
||||||
|
|
||||||
$this->addFile(
|
|
||||||
'somefile.php',
|
|
||||||
'<?php
|
|
||||||
function takesString(string $_s): void {}
|
|
||||||
|
|
||||||
/** @param ArrayIterator|string[] $i */
|
|
||||||
function takesArrayIteratorOfString(ArrayIterator $i): void {
|
|
||||||
$s = $i->offsetGet("a");
|
|
||||||
takesString($s);
|
|
||||||
|
|
||||||
foreach ($i as $s2) {
|
|
||||||
takesString($s2);
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->analyzeFile('somefile.php', new Context());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPhpStormGenericsWithTypeInSignature(): void
|
|
||||||
{
|
|
||||||
Config::getInstance()->allow_phpstorm_generics = true;
|
|
||||||
|
|
||||||
$this->addFile(
|
|
||||||
'somefile.php',
|
|
||||||
'<?php
|
|
||||||
function a(array|\ArrayObject $_meta = []): void {}'
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->analyzeFile('somefile.php', new Context());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPhpStormGenericsWithValidTraversableArgument(): void
|
|
||||||
{
|
|
||||||
Config::getInstance()->allow_phpstorm_generics = true;
|
|
||||||
|
|
||||||
$this->addFile(
|
|
||||||
'somefile.php',
|
|
||||||
'<?php
|
|
||||||
function takesString(string $_s): void {}
|
|
||||||
|
|
||||||
/** @param Traversable|string[] $i */
|
|
||||||
function takesTraversableOfString(Traversable $i): void {
|
|
||||||
foreach ($i as $s2) {
|
|
||||||
takesString($s2);
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->analyzeFile('somefile.php', new Context());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPhpStormGenericsWithClassProperty(): void
|
|
||||||
{
|
|
||||||
Config::getInstance()->allow_phpstorm_generics = true;
|
|
||||||
|
|
||||||
$this->addFile(
|
|
||||||
'somefile.php',
|
|
||||||
'<?php
|
|
||||||
/** @psalm-suppress MissingConstructor */
|
|
||||||
class Foo {
|
|
||||||
/** @var \stdClass[]|\ArrayObject */
|
|
||||||
public $bar;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return \stdClass[]|\ArrayObject
|
|
||||||
*/
|
|
||||||
public function getBar(): \ArrayObject {
|
|
||||||
return $this->bar;
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->analyzeFile('somefile.php', new Context());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPhpStormGenericsWithGeneratorArray(): void
|
|
||||||
{
|
|
||||||
Config::getInstance()->allow_phpstorm_generics = true;
|
|
||||||
|
|
||||||
$this->addFile(
|
|
||||||
'somefile.php',
|
|
||||||
'<?php
|
|
||||||
class A {
|
|
||||||
/**
|
|
||||||
* @return stdClass[]|Generator
|
|
||||||
*/
|
|
||||||
function getCollection(): Generator
|
|
||||||
{
|
|
||||||
yield new stdClass;
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->analyzeFile('somefile.php', new Context());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPhpStormGenericsWithValidIterableArgument(): void
|
|
||||||
{
|
|
||||||
Config::getInstance()->allow_phpstorm_generics = true;
|
|
||||||
|
|
||||||
$this->addFile(
|
|
||||||
'somefile.php',
|
|
||||||
'<?php
|
|
||||||
function takesString(string $_s): void {}
|
|
||||||
|
|
||||||
/** @param iterable|string[] $i */
|
|
||||||
function takesArrayIteratorOfString(iterable $i): void {
|
|
||||||
foreach ($i as $s2) {
|
|
||||||
takesString($s2);
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->analyzeFile('somefile.php', new Context());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPhpStormGenericsInvalidArgument(): void
|
|
||||||
{
|
|
||||||
$this->expectException(CodeException::class);
|
|
||||||
$this->expectExceptionMessage('InvalidScalarArgument');
|
|
||||||
|
|
||||||
Config::getInstance()->allow_phpstorm_generics = true;
|
|
||||||
|
|
||||||
$this->addFile(
|
|
||||||
'somefile.php',
|
|
||||||
'<?php
|
|
||||||
function takesInt(int $_s): void {}
|
|
||||||
|
|
||||||
/** @param ArrayIterator|string[] $i */
|
|
||||||
function takesArrayIteratorOfString(ArrayIterator $i): void {
|
|
||||||
$s = $i->offsetGet("a");
|
|
||||||
takesInt($s);
|
|
||||||
}'
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->analyzeFile('somefile.php', new Context());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testLessSpecificImplementedReturnTypeWithDocblockOnMultipleLines(): void
|
public function testLessSpecificImplementedReturnTypeWithDocblockOnMultipleLines(): void
|
||||||
{
|
{
|
||||||
$this->expectException(CodeException::class);
|
$this->expectException(CodeException::class);
|
||||||
@ -236,25 +93,6 @@ class AnnotationTest extends TestCase
|
|||||||
$this->analyzeFile('somefile.php', new Context());
|
$this->analyzeFile('somefile.php', new Context());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPhpStormGenericsNoTypehint(): void
|
|
||||||
{
|
|
||||||
$this->expectException(CodeException::class);
|
|
||||||
$this->expectExceptionMessage('PossiblyInvalidMethodCall');
|
|
||||||
|
|
||||||
Config::getInstance()->allow_phpstorm_generics = true;
|
|
||||||
|
|
||||||
$this->addFile(
|
|
||||||
'somefile.php',
|
|
||||||
'<?php
|
|
||||||
/** @param ArrayIterator|string[] $i */
|
|
||||||
function takesArrayIteratorOfString($i): void {
|
|
||||||
$s = $i->offsetGet("a");
|
|
||||||
}'
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->analyzeFile('somefile.php', new Context());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testInvalidParamDefault(): void
|
public function testInvalidParamDefault(): void
|
||||||
{
|
{
|
||||||
$this->expectException(CodeException::class);
|
$this->expectException(CodeException::class);
|
||||||
|
Loading…
Reference in New Issue
Block a user