mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
parent
aaa912374f
commit
5da816f160
@ -1219,7 +1219,7 @@ class AssertionFinder
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null
|
null
|
||||||
)->getId();
|
)->getAssertionString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1284,7 +1284,7 @@ class AssertionFinder
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null
|
null
|
||||||
)->getId();
|
)->getAssertionString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3696,7 +3696,7 @@ class AssertionFinder
|
|||||||
$literal_assertions = [];
|
$literal_assertions = [];
|
||||||
|
|
||||||
foreach ($array_literal_types as $array_literal_type) {
|
foreach ($array_literal_types as $array_literal_type) {
|
||||||
$literal_assertions[] = '=' . $array_literal_type->getId();
|
$literal_assertions[] = '=' . $array_literal_type->getAssertionString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($value_type->isFalsable()) {
|
if ($value_type->isFalsable()) {
|
||||||
@ -3766,11 +3766,11 @@ class AssertionFinder
|
|||||||
|
|
||||||
if ($key_type->allStringLiterals() && !$key_type->possibly_undefined) {
|
if ($key_type->allStringLiterals() && !$key_type->possibly_undefined) {
|
||||||
foreach ($key_type->getLiteralStrings() as $array_literal_type) {
|
foreach ($key_type->getLiteralStrings() as $array_literal_type) {
|
||||||
$literal_assertions[] = '=' . $array_literal_type->getId();
|
$literal_assertions[] = '=' . $array_literal_type->getAssertionString();
|
||||||
}
|
}
|
||||||
} elseif ($key_type->allIntLiterals() && !$key_type->possibly_undefined) {
|
} elseif ($key_type->allIntLiterals() && !$key_type->possibly_undefined) {
|
||||||
foreach ($key_type->getLiteralInts() as $array_literal_type) {
|
foreach ($key_type->getLiteralInts() as $array_literal_type) {
|
||||||
$literal_assertions[] = '=' . $array_literal_type->getId();
|
$literal_assertions[] = '=' . $array_literal_type->getAssertionString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -557,6 +557,7 @@ class FunctionLikeDocblockScanner
|
|||||||
|
|
||||||
foreach ($namespaced_type->getAtomicTypes() as $namespaced_type_part) {
|
foreach ($namespaced_type->getAtomicTypes() as $namespaced_type_part) {
|
||||||
if ($namespaced_type_part instanceof Type\Atomic\TAssertionFalsy
|
if ($namespaced_type_part instanceof Type\Atomic\TAssertionFalsy
|
||||||
|
|| $namespaced_type_part instanceof Type\Atomic\TScalarClassConstant
|
||||||
|| ($namespaced_type_part instanceof Type\Atomic\TList
|
|| ($namespaced_type_part instanceof Type\Atomic\TList
|
||||||
&& $namespaced_type_part->type_param->isMixed())
|
&& $namespaced_type_part->type_param->isMixed())
|
||||||
|| ($namespaced_type_part instanceof Type\Atomic\TArray
|
|| ($namespaced_type_part instanceof Type\Atomic\TArray
|
||||||
|
@ -31,7 +31,13 @@ class TIntMask extends TInt
|
|||||||
|
|
||||||
public function getId(bool $nested = false): string
|
public function getId(bool $nested = false): string
|
||||||
{
|
{
|
||||||
return $this->getKey();
|
$s = '';
|
||||||
|
|
||||||
|
foreach ($this->values as $value) {
|
||||||
|
$s .= $value->getId() . ', ';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'int-mask<' . substr($s, 0, -2) . '>';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,6 +20,11 @@ class TLiteralInt extends TInt
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getId(bool $nested = false): string
|
public function getId(bool $nested = false): string
|
||||||
|
{
|
||||||
|
return (string) $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAssertionString(bool $exact = false): string
|
||||||
{
|
{
|
||||||
return 'int(' . $this->value . ')';
|
return 'int(' . $this->value . ')';
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ class TLiteralString extends TString
|
|||||||
|
|
||||||
public function getKey(bool $include_extra = true) : string
|
public function getKey(bool $include_extra = true) : string
|
||||||
{
|
{
|
||||||
return $this->getId();
|
return 'string(' . $this->value . ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __toString(): string
|
public function __toString(): string
|
||||||
@ -32,10 +32,15 @@ class TLiteralString extends TString
|
|||||||
{
|
{
|
||||||
$no_newline_value = preg_replace("/\n/m", '\n', $this->value);
|
$no_newline_value = preg_replace("/\n/m", '\n', $this->value);
|
||||||
if (strlen($this->value) > 80) {
|
if (strlen($this->value) > 80) {
|
||||||
return 'string(' . substr($no_newline_value, 0, 80) . '...' . ')';
|
return '"' . substr($no_newline_value, 0, 80) . '...' . '"';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'string(' . $no_newline_value . ')';
|
return '"' . $no_newline_value . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAssertionString(bool $exact = false): string
|
||||||
|
{
|
||||||
|
return 'string(' . $this->value . ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,7 +30,12 @@ class TScalarClassConstant extends Scalar
|
|||||||
|
|
||||||
public function getId(bool $nested = false): string
|
public function getId(bool $nested = false): string
|
||||||
{
|
{
|
||||||
return $this->getKey();
|
return $this->fq_classlike_name . '::' . $this->const_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAssertionString(bool $exact = false): string
|
||||||
|
{
|
||||||
|
return 'scalar-class-constant(' . $this->fq_classlike_name . '::' . $this->const_name . ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,9 +74,4 @@ class TScalarClassConstant extends Scalar
|
|||||||
. '::'
|
. '::'
|
||||||
. $this->const_name;
|
. $this->const_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAssertionString(bool $exact = false): string
|
|
||||||
{
|
|
||||||
return 'mixed';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -688,7 +688,7 @@ class ArrayFunctionCallTest extends TestCase
|
|||||||
ARRAY_FILTER_USE_KEY
|
ARRAY_FILTER_USE_KEY
|
||||||
);',
|
);',
|
||||||
'assertions' => [
|
'assertions' => [
|
||||||
'$foo' => 'array<string, pure-Closure():string(baz)>',
|
'$foo' => 'array<string, pure-Closure():"baz">',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'ignoreFalsableCurrent' => [
|
'ignoreFalsableCurrent' => [
|
||||||
|
@ -1260,7 +1260,6 @@ class AssertAnnotationTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function takesA(int $a) : void {
|
function takesA(int $a) : void {
|
||||||
if (A::isValid($a)) {
|
if (A::isValid($a)) {
|
||||||
A::bar($a);
|
A::bar($a);
|
||||||
|
@ -340,7 +340,7 @@ class ClosureTest extends TestCase
|
|||||||
$a = function() : Closure { return function() : string { return "hello"; }; };
|
$a = function() : Closure { return function() : string { return "hello"; }; };
|
||||||
$b = $a()();',
|
$b = $a()();',
|
||||||
'assertions' => [
|
'assertions' => [
|
||||||
'$a' => 'pure-Closure():pure-Closure():string(hello)',
|
'$a' => 'pure-Closure():pure-Closure():"hello"',
|
||||||
'$b' => 'string',
|
'$b' => 'string',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -98,7 +98,7 @@ class JsonOutputTest extends TestCase
|
|||||||
function fooFoo() {
|
function fooFoo() {
|
||||||
return "hello";
|
return "hello";
|
||||||
}',
|
}',
|
||||||
'message' => 'Method fooFoo does not have a return type, expecting string(hello)',
|
'message' => 'Method fooFoo does not have a return type, expecting "hello"',
|
||||||
'line' => 2,
|
'line' => 2,
|
||||||
'error' => 'fooFoo',
|
'error' => 'fooFoo',
|
||||||
],
|
],
|
||||||
@ -110,7 +110,7 @@ class JsonOutputTest extends TestCase
|
|||||||
function fooFoo() {
|
function fooFoo() {
|
||||||
return "hello";
|
return "hello";
|
||||||
}',
|
}',
|
||||||
'message' => "The inferred type 'string(hello)' does not match the declared return type 'int' for fooFoo",
|
'message' => "The inferred type '\"hello\"' does not match the declared return type 'int' for fooFoo",
|
||||||
'line' => 6,
|
'line' => 6,
|
||||||
'error' => '"hello"',
|
'error' => '"hello"',
|
||||||
],
|
],
|
||||||
|
@ -216,13 +216,13 @@ class SymbolLookupTest extends \Psalm\Tests\TestCase
|
|||||||
|
|
||||||
$this->assertNotNull($symbol_at_position);
|
$this->assertNotNull($symbol_at_position);
|
||||||
|
|
||||||
$this->assertSame('213-214:int(1)', $symbol_at_position[0]);
|
$this->assertSame('213-214:1', $symbol_at_position[0]);
|
||||||
|
|
||||||
$symbol_at_position = $codebase->getReferenceAtPosition('somefile.php', new Position(17, 30));
|
$symbol_at_position = $codebase->getReferenceAtPosition('somefile.php', new Position(17, 30));
|
||||||
|
|
||||||
$this->assertNotNull($symbol_at_position);
|
$this->assertNotNull($symbol_at_position);
|
||||||
|
|
||||||
$this->assertSame('425-426:int(2)', $symbol_at_position[0]);
|
$this->assertSame('425-426:2', $symbol_at_position[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetSymbolPositionMissingArg(): void
|
public function testGetSymbolPositionMissingArg(): void
|
||||||
|
@ -59,8 +59,8 @@ class Php56Test extends TestCase
|
|||||||
$bitxor = C::BITXOR;',
|
$bitxor = C::BITXOR;',
|
||||||
'assertions' => [
|
'assertions' => [
|
||||||
'$c1' => 'int',
|
'$c1' => 'int',
|
||||||
'$c2===' => 'int(2)',
|
'$c2===' => '2',
|
||||||
'$c3===' => 'int(3)',
|
'$c3===' => '3',
|
||||||
'$c1_3rd' => 'float|int',
|
'$c1_3rd' => 'float|int',
|
||||||
'$c_sentence' => 'string',
|
'$c_sentence' => 'string',
|
||||||
'$cf' => 'int',
|
'$cf' => 'int',
|
||||||
|
@ -1288,7 +1288,7 @@ class ReturnTypeTest extends TestCase
|
|||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
}',
|
}',
|
||||||
'error_message' => 'InvalidReturnStatement - src' . DIRECTORY_SEPARATOR . 'somefile.php:9:28 - The inferred type \'pure-Closure(iterable<int, T:fn-map as mixed>):int(1)\' does not match the declared return type \'callable(iterable<int, T:fn-map as mixed>):iterable<int, U:fn-map as mixed>\' for map',
|
'error_message' => 'InvalidReturnStatement - src' . DIRECTORY_SEPARATOR . 'somefile.php:9:28 - The inferred type \'pure-Closure(iterable<int, T:fn-map as mixed>):1\' does not match the declared return type \'callable(iterable<int, T:fn-map as mixed>):iterable<int, U:fn-map as mixed>\' for map',
|
||||||
],
|
],
|
||||||
'cannotInferReturnClosureWithDifferentTypes' => [
|
'cannotInferReturnClosureWithDifferentTypes' => [
|
||||||
'<?php
|
'<?php
|
||||||
|
@ -1045,7 +1045,7 @@ class ClassTemplateTest extends TestCase
|
|||||||
|
|
||||||
$c = new C();',
|
$c = new C();',
|
||||||
'assertions' => [
|
'assertions' => [
|
||||||
'$c===' => 'C<string(hello)>',
|
'$c===' => 'C<"hello">',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'SKIPPED-templateDefaultConstant' => [
|
'SKIPPED-templateDefaultConstant' => [
|
||||||
@ -3601,7 +3601,7 @@ class ClassTemplateTest extends TestCase
|
|||||||
$mario = new CharacterRow(["id" => 5, "name" => "Mario", "height" => 3.5]);
|
$mario = new CharacterRow(["id" => 5, "name" => "Mario", "height" => 3.5]);
|
||||||
|
|
||||||
$mario->ame = "Luigi";',
|
$mario->ame = "Luigi";',
|
||||||
'error_message' => 'InvalidArgument - src' . DIRECTORY_SEPARATOR . 'somefile.php:47:29 - Argument 1 of CharacterRow::__set expects string(height)|string(id)|string(name), string(ame) provided',
|
'error_message' => 'InvalidArgument - src' . DIRECTORY_SEPARATOR . 'somefile.php:47:29 - Argument 1 of CharacterRow::__set expects "height"|"id"|"name", "ame" provided',
|
||||||
],
|
],
|
||||||
'specialiseTypeBeforeReturning' => [
|
'specialiseTypeBeforeReturning' => [
|
||||||
'<?php
|
'<?php
|
||||||
|
@ -339,28 +339,28 @@ class TypeCombinationTest extends TestCase
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
'combineObjectTypeWithIntKeyedArray' => [
|
'combineObjectTypeWithIntKeyedArray' => [
|
||||||
'array<int|string(a), int|string>',
|
'array<"a"|int, int|string>',
|
||||||
[
|
[
|
||||||
'array{a: int}',
|
'array{a: int}',
|
||||||
'array<int, string>',
|
'array<int, string>',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'combineNestedObjectTypeWithTKeyedArrayIntKeyedArray' => [
|
'combineNestedObjectTypeWithTKeyedArrayIntKeyedArray' => [
|
||||||
'array{a: array<int|string(a), int|string>}',
|
'array{a: array<"a"|int, int|string>}',
|
||||||
[
|
[
|
||||||
'array{a: array{a: int}}',
|
'array{a: array{a: int}}',
|
||||||
'array{a: array<int, string>}',
|
'array{a: array<int, string>}',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'combineIntKeyedObjectTypeWithNestedIntKeyedArray' => [
|
'combineIntKeyedObjectTypeWithNestedIntKeyedArray' => [
|
||||||
'array<int, array<int|string(a), int|string>>',
|
'array<int, array<"a"|int, int|string>>',
|
||||||
[
|
[
|
||||||
'array<int, array{a:int}>',
|
'array<int, array{a:int}>',
|
||||||
'array<int, array<int, string>>',
|
'array<int, array<int, string>>',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'combineNestedObjectTypeWithNestedIntKeyedArray' => [
|
'combineNestedObjectTypeWithNestedIntKeyedArray' => [
|
||||||
'array<int|string(a), array<int|string(a), int|string>>',
|
'array<"a"|int, array<"a"|int, int|string>>',
|
||||||
[
|
[
|
||||||
'array{a: array{a: int}}',
|
'array{a: array{a: int}}',
|
||||||
'array<int, array<int, string>>',
|
'array<int, array<int, string>>',
|
||||||
@ -431,7 +431,7 @@ class TypeCombinationTest extends TestCase
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
'objectLikePlusArrayEqualsArray' => [
|
'objectLikePlusArrayEqualsArray' => [
|
||||||
'array<string(a)|string(b)|string(c), int(1)|int(2)|int(3)>',
|
'array<"a"|"b"|"c", 1|2|3>',
|
||||||
[
|
[
|
||||||
'array<"a"|"b"|"c", 1|2|3>',
|
'array<"a"|"b"|"c", 1|2|3>',
|
||||||
'array{a: 1|2, b: 2|3, c: 1|3}',
|
'array{a: 1|2, b: 2|3, c: 1|3}',
|
||||||
@ -571,14 +571,14 @@ class TypeCombinationTest extends TestCase
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
'combineZeroAndPositiveInt' => [
|
'combineZeroAndPositiveInt' => [
|
||||||
'int(0)|positive-int',
|
'0|positive-int',
|
||||||
[
|
[
|
||||||
'0',
|
'0',
|
||||||
'positive-int',
|
'positive-int',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'combinePositiveIntAndZero' => [
|
'combinePositiveIntAndZero' => [
|
||||||
'int(0)|positive-int',
|
'0|positive-int',
|
||||||
[
|
[
|
||||||
'positive-int',
|
'positive-int',
|
||||||
'0',
|
'0',
|
||||||
@ -615,7 +615,7 @@ class TypeCombinationTest extends TestCase
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
'combineZeroOneAndPositiveInt' => [
|
'combineZeroOneAndPositiveInt' => [
|
||||||
'int(0)|positive-int',
|
'0|positive-int',
|
||||||
[
|
[
|
||||||
'0',
|
'0',
|
||||||
'1',
|
'1',
|
||||||
@ -623,7 +623,7 @@ class TypeCombinationTest extends TestCase
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
'combinePositiveIntOneAndZero' => [
|
'combinePositiveIntOneAndZero' => [
|
||||||
'int(0)|positive-int',
|
'0|positive-int',
|
||||||
[
|
[
|
||||||
'positive-int',
|
'positive-int',
|
||||||
'1',
|
'1',
|
||||||
|
@ -717,7 +717,7 @@ class TypeParseTest extends TestCase
|
|||||||
public function testCombineLiteralStringWithClassString(): void
|
public function testCombineLiteralStringWithClassString(): void
|
||||||
{
|
{
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
'class-string|string(array)',
|
'"array"|class-string',
|
||||||
Type::parseString('"array"|class-string')->getId()
|
Type::parseString('"array"|class-string')->getId()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -902,26 +902,26 @@ class TypeParseTest extends TestCase
|
|||||||
{
|
{
|
||||||
$docblock_type = Type::parseString('int-mask<0, 1, 2, 4>');
|
$docblock_type = Type::parseString('int-mask<0, 1, 2, 4>');
|
||||||
|
|
||||||
$this->assertSame('int(0)|int(1)|int(2)|int(3)|int(4)|int(5)|int(6)|int(7)', $docblock_type->getId());
|
$this->assertSame('0|1|2|3|4|5|6|7', $docblock_type->getId());
|
||||||
|
|
||||||
$docblock_type = Type::parseString('int-mask<1, 2, 4>');
|
$docblock_type = Type::parseString('int-mask<1, 2, 4>');
|
||||||
|
|
||||||
$this->assertSame('int(1)|int(2)|int(3)|int(4)|int(5)|int(6)|int(7)', $docblock_type->getId());
|
$this->assertSame('1|2|3|4|5|6|7', $docblock_type->getId());
|
||||||
|
|
||||||
$docblock_type = Type::parseString('int-mask<1, 4>');
|
$docblock_type = Type::parseString('int-mask<1, 4>');
|
||||||
|
|
||||||
$this->assertSame('int(1)|int(4)|int(5)', $docblock_type->getId());
|
$this->assertSame('1|4|5', $docblock_type->getId());
|
||||||
|
|
||||||
$docblock_type = Type::parseString('int-mask<PREG_PATTERN_ORDER, PREG_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL>');
|
$docblock_type = Type::parseString('int-mask<PREG_PATTERN_ORDER, PREG_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL>');
|
||||||
|
|
||||||
$this->assertSame('int(1)|int(256)|int(257)|int(512)|int(513)|int(768)|int(769)', $docblock_type->getId());
|
$this->assertSame('1|256|257|512|513|768|769', $docblock_type->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIntMaskWithClassConstant(): void
|
public function testIntMaskWithClassConstant(): void
|
||||||
{
|
{
|
||||||
$docblock_type = Type::parseString('int-mask<0, A::FOO, A::BAR>');
|
$docblock_type = Type::parseString('int-mask<0, A::FOO, A::BAR>');
|
||||||
|
|
||||||
$this->assertSame('int-mask<int(0), scalar-class-constant(A::FOO), scalar-class-constant(A::BAR)>', $docblock_type->getId());
|
$this->assertSame('int-mask<0, A::FOO, A::BAR>', $docblock_type->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIntMaskWithInvalidClassConstant(): void
|
public function testIntMaskWithInvalidClassConstant(): void
|
||||||
|
@ -101,7 +101,7 @@ class ReconcilerTest extends \Psalm\Tests\TestCase
|
|||||||
'falsyWithMyObjectPipeBool' => ['false', 'falsy', 'MyObject|bool'],
|
'falsyWithMyObjectPipeBool' => ['false', 'falsy', 'MyObject|bool'],
|
||||||
'falsyWithMixed' => ['empty-mixed', 'falsy', 'mixed'],
|
'falsyWithMixed' => ['empty-mixed', 'falsy', 'mixed'],
|
||||||
'falsyWithBool' => ['false', 'falsy', 'bool'],
|
'falsyWithBool' => ['false', 'falsy', 'bool'],
|
||||||
'falsyWithStringOrNull' => ['null|string()|string(0)', 'falsy', 'string|null'],
|
'falsyWithStringOrNull' => ['""|"0"|null', 'falsy', 'string|null'],
|
||||||
'falsyWithScalarOrNull' => ['empty-scalar', 'falsy', 'scalar'],
|
'falsyWithScalarOrNull' => ['empty-scalar', 'falsy', 'scalar'],
|
||||||
|
|
||||||
'notMyObjectWithMyObjectPipeBool' => ['bool', '!MyObject', 'MyObject|bool'],
|
'notMyObjectWithMyObjectPipeBool' => ['bool', '!MyObject', 'MyObject|bool'],
|
||||||
|
Loading…
Reference in New Issue
Block a user