1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-27 04:45:20 +01:00
psalm/tests/TypeReconciliation/ScopeTest.php
Daniil Gentili 1986c8b4a8
Add support for strict arrays, fix type alias intersection, fix array_is_list assertion on non-lists (#8395)
* Immutable CodeLocation

* Remove excess clones

* Remove external clones

* Remove leftover clones

* Fix final clone issue

* Immutable storages

* Refactoring

* Fixes

* Fixes

* Fix

* Fix

* Fixes

* Simplify

* Fixes

* Fix

* Fixes

* Update

* Fix

* Cache global types

* Fix

* Update

* Update

* Fixes

* Fixes

* Refactor

* Fixes

* Fix

* Fix

* More caching

* Fix

* Fix

* Update

* Update

* Fix

* Fixes

* Update

* Refactor

* Update

* Fixes

* Break one more test

* Fix

* FIx

* Fix

* Fix

* Fix

* Fix

* Improve performance and readability

* Equivalent logic

* Fixes

* Revert

* Revert "Revert"

This reverts commit f9175100c8452c80559234200663fd4c4f4dd889.

* Fix

* Fix reference bug

* Make default TypeVisitor immutable

* Bugfix

* Remove clones

* Partial refactoring

* Refactoring

* Fixes

* Fix

* Fixes

* Fixes

* cs-fix

* Fix final bugs

* Add test

* Misc fixes

* Update

* Fixes

* Experiment with removing different property

* revert "Experiment with removing different property"

This reverts commit ac1156e077fc4ea633530d51096d27b6e88bfdf9.

* Uniform naming

* Uniform naming

* Hack hotfix

* Clean up $_FILES ref #8621

* Undo hack, try fixing properly

* Helper method

* Remove redundant call

* Partially fix bugs

* Cleanup

* Change defaults

* Fix bug

* Fix (?, hope this doesn't break anything else)

* cs-fix

* Review fixes

* Bugfix

* Bugfix

* Improve logic

* Add support for list{} and callable-list{} types, properly implement array_is_list assertions (fixes #8389)

* Default to sealed arrays

* Fix array_merge bug

* Fixes

* Fix

* Sealed type checks

* Properly infer properties-of and get_object_vars on final classes

* Fix array_map zipping

* Fix tests

* Fixes

* Fixes

* Fix more stuff

* Recursively resolve type aliases

* Fix typo

* Fixes

* Fix array_is_list assertion on keyed array

* Add BC docs

* Fixes

* fix

* Update

* Update

* Update

* Update

* Seal arrays with count assertions

* Fix #8528

* Fix

* Update

* Improve sealed array foreach logic

* get_object_vars on template properties

* Fix sealed array assertion reconciler logic

* Improved reconciler

* Add tests

* Single source of truth for test types

* Fix tests

* Fixup tests

* Fixup tests

* Fixup tests

* Update

* Fix tests

* Fix tests

* Final fixes

* Fixes

* Use list syntax only when needed

* Fix tests

* Cs-fix

* Update docs

* Update docs

* Update docs

* Update docs

* Update docs

* Document missing types

* Update docs

* Improve class-string-map docs

* Update

* Update

* I love working on psalm :)

* Keep arrays unsealed by default

* Fixup tests

* Fix syntax mistake

* cs-fix

* Fix typo

* Re-import missing types

* Keep strict types only in return types

* argc/argv fixes

* argc/argv fixes

* Fix test

* Comment-out valinor code, pinging @romm pls merge https://github.com/CuyZ/Valinor/pull/246 so we can add valinor to the psalm docs :)
2022-11-05 22:34:42 +01:00

314 lines
9.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Psalm\Tests\TypeReconciliation;
use Psalm\Tests\TestCase;
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
use const DIRECTORY_SEPARATOR;
class ScopeTest extends TestCase
{
use InvalidCodeAnalysisTestTrait;
use ValidCodeAnalysisTestTrait;
/**
*
*/
public function providerValidCodeParse(): iterable
{
return [
'newVarInIf' => [
'code' => '<?php
if (rand(0,100) === 10) {
$badge = "hello";
}
else {
$badge = "goodbye";
}
echo $badge;',
],
'newVarInIfWithElseReturn' => [
'code' => '<?php
if (rand(0,100) === 10) {
$badge = "hello";
}
else {
throw new \Exception();
}
echo $badge;',
],
'passByRefInVarWithBoolean' => [
'code' => '<?php
$a = preg_match("/bad/", "badger", $matches) > 0;
if ($a) {
echo $matches[1];
}',
],
'functionExists' => [
'code' => '<?php
if (rand(0,1) && function_exists("flabble")) {
flabble();
}',
],
'nestedPropertyFetchInElseif' => [
'code' => '<?php
class A {
/** @var A|null */
public $foo;
public function __toString(): string {
return "boop";
}
}
$a = rand(0, 10) === 5 ? new A() : null;
if (rand(0, 1)) {
} elseif ($a && $a->foo) {
echo $a;
}',
],
'globalReturn' => [
'code' => '<?php
$foo = "foo";
function a(): string {
global $foo;
return $foo;
}',
],
'globalReturnWithAnnotation' => [
'code' => '<?php
/**
* @global string $foo
*/
function a(): string {
global $foo;
return $foo;
}',
],
'negateAssertionAndOther' => [
'code' => '<?php
$a = rand(0, 10) ? "hello" : null;
if (rand(0, 10) > 1 && is_string($a)) {
throw new \Exception("bad");
}',
'assertions' => [
'$a' => 'null|string',
],
],
'repeatAssertionWithOther' => [
'code' => '<?php
function getString() : string {
return "hello";
}
$a = rand(0, 10) ? getString() : null;
if (rand(0, 10) > 1 || is_string($a)) {
if (is_string($a)) {
echo strpos($a, "e");
}
}',
'assertions' => [
'$a' => 'null|string',
],
'ignored_issues' => ['PossiblyFalseArgument'],
],
'refineOredType' => [
'code' => '<?php
class A {
public function doThing(): void
{
if ($this instanceof B || $this instanceof C) {
if ($this instanceof B) {
}
}
}
}
class B extends A {}
class C extends A {}',
],
'instanceOfSubtraction' => [
'code' => '<?php
class Foo {}
class FooBar extends Foo{}
class FooBarBat extends FooBar{}
class FooMoo extends Foo{}
$a = new Foo();
if ($a instanceof FooBar && !$a instanceof FooBarBat) {
} elseif ($a instanceof FooMoo) {
}',
],
'staticNullRef' => [
'code' => '<?php
/** @return void */
function foo() {
static $bar = null;
if ($bar !== null) {
// do something
}
$bar = 5;
}',
],
'suppressInvalidThis' => [
'code' => '<?php
/** @psalm-suppress InvalidScope */
if (!isset($this->value)) {
$this->value = ["x", "y"];
echo count($this->value) - 2;
}',
'assertions' => [],
'ignored_issues' => ['MixedPropertyAssignment', 'MixedArgument'],
],
'typedStatic' => [
'code' => '<?php
function a(): ?int {
/** @var ?int */
static $foo = 5;
if (rand(0, 1)) {
return $foo;
}
$foo = null;
return $foo;
}',
],
'psalmScopeThisInTemplate' => [
'code' => '<?php
$e = new Exception(); // necessary to trick Psalms scanner for test
/** @psalm-scope-this Exception */
?>
<h1><?= $this->getMessage() ?></h1>',
],
'psalmVarThisInTemplate' => [
'code' => '<?php
$e = new Exception(); // necessary to trick Psalms scanner for test
/** @var Exception $this */
?>
<h1><?= $this->getMessage() ?></h1>',
],
'psalmVarThisAbsoluteClassInTemplate' => [
'code' => '<?php
$e = new Exception(); // necessary to trick Psalms scanner for test
/** @var \Exception $this */
?>
<h1><?= $this->getMessage() ?></h1>',
],
];
}
/**
*
*/
public function providerInvalidCodeParse(): iterable
{
return [
'possiblyUndefinedVarInIf' => [
'code' => '<?php
if (rand(0,100) === 10) {
$b = "s";
}
echo $b;',
'error_message' => 'PossiblyUndefinedGlobalVariable - src' . DIRECTORY_SEPARATOR . 'somefile.php:6:26 - Possibly undefined global '
. 'variable $b, first seen on line 3',
],
'possiblyUndefinedArrayInIf' => [
'code' => '<?php
if (rand(0,100) === 10) {
$array[] = "hello";
}
echo $array;',
'error_message' => 'PossiblyUndefinedGlobalVariable - src' . DIRECTORY_SEPARATOR . 'somefile.php:3:25 - Possibly undefined global '
. 'variable $array, first seen on line 3',
],
'invalidGlobal' => [
'code' => '<?php
$a = "heli";
global $a;',
'error_message' => 'InvalidGlobal',
],
'thisInStatic' => [
'code' => '<?php
class A {
public static function fooFoo() {
echo $this;
}
}',
'error_message' => 'InvalidScope',
],
'static' => [
'code' => '<?php
function a(): string {
static $foo = "foo";
return $foo;
}',
'error_message' => 'MixedReturnStatement',
],
'staticNullRef' => [
'code' => '<?php
/** @return void */
function foo() {
/** @var int */
static $bar = 5;
if ($bar === null) {
// do something
}
$bar = 4;
}',
'error_message' => 'DocblockTypeContradiction',
],
'typedStaticCannotHaveNullDefault' => [
'code' => '<?php
function a(): void {
/** @var string */
static $foo = null;
}',
'error_message' => 'ReferenceConstraintViolation',
],
'typedStaticCannotBeAssignedInt' => [
'code' => '<?php
function a(): void {
/** @var string */
static $foo = "foo";
$foo = 5;
}',
'error_message' => 'ReferenceConstraintViolation',
],
'typedStaticCannotBeAssignedNull' => [
'code' => '<?php
function a(): void {
/** @var string */
static $foo = "foo";
$foo = null;
}',
'error_message' => 'ReferenceConstraintViolation',
],
];
}
}