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

1442 lines
50 KiB
PHP
Raw Permalink Normal View History

2016-12-12 05:41:11 +01:00
<?php
2016-12-12 05:41:11 +01:00
namespace Psalm\Tests;
2021-12-04 21:55:53 +01:00
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
use const DIRECTORY_SEPARATOR;
class ClassTest extends TestCase
2016-12-12 05:41:11 +01:00
{
2021-12-04 21:55:53 +01:00
use InvalidCodeAnalysisTestTrait;
use ValidCodeAnalysisTestTrait;
public function testExtendsMysqli(): void
{
$this->addFile(
'somefile.php',
'<?php
class db extends mysqli {
public function close()
{
return true;
}
public function prepare(string $sql)
{
return false;
}
public function commit(?int $flags = null, ?string $name = null)
{
return true;
}
public function real_escape_string(string $string)
{
return "escaped";
}
2022-12-18 17:15:15 +01:00
}',
);
}
public function providerValidCodeParse(): iterable
{
return [
'overrideProtectedAccessLevelToPublic' => [
'code' => '<?php
class A {
2018-01-11 21:50:45 +01:00
protected function fooFoo(): void {}
}
2017-06-29 16:22:49 +02:00
class B extends A {
2018-01-11 21:50:45 +01:00
public function fooFoo(): void {}
2017-05-27 02:05:57 +02:00
}',
],
'reflectedParents' => [
'code' => '<?php
$e = rand(0, 10)
? new RuntimeException("m")
: null;
2017-06-29 16:22:49 +02:00
if ($e instanceof Exception) {
echo "good";
2017-05-27 02:05:57 +02:00
}',
],
'namespacedAliasedClassCall' => [
'code' => '<?php
namespace Aye {
class Foo {}
}
namespace Bee {
use Aye as A;
2017-06-29 16:22:49 +02:00
new A\Foo();
2017-05-27 02:05:57 +02:00
}',
],
'abstractExtendsAbstract' => [
'code' => '<?php
abstract class A {
/** @return void */
abstract public function foo();
}
2017-06-29 16:22:49 +02:00
abstract class B extends A {
/** @return void */
public function bar() {
$this->foo();
}
2017-05-27 02:05:57 +02:00
}',
],
'missingParentWithFunction' => [
'code' => '<?php
class B extends C {
public function fooA() { }
}',
'assertions' => [],
'ignored_issues' => [
'UndefinedClass',
2017-05-27 02:05:57 +02:00
'MissingReturnType',
],
],
'subclassWithSimplerArg' => [
'code' => '<?php
class A {}
class B extends A {}
2017-06-29 16:22:49 +02:00
class E1 {
/**
* @param A|B|null $a
*/
public function __construct($a) {
}
}
2017-06-29 16:22:49 +02:00
class E2 extends E1 {
/**
* @param A|null $a
*/
public function __construct($a) {
parent::__construct($a);
}
2017-05-27 02:05:57 +02:00
}',
],
'subclassOfInvalidArgumentExceptionWithSimplerArg' => [
'code' => '<?php
class A extends InvalidArgumentException {
/**
* @param string $message
* @param int $code
* @param Throwable|null $previous_exception
*/
public function __construct($message, $code, $previous_exception) {
parent::__construct($message, $code, $previous_exception);
}
2017-05-27 02:05:57 +02:00
}',
],
'classStringInstantiation' => [
'code' => '<?php
class Foo {}
class Bar {}
$class = mt_rand(0, 1) === 1 ? Foo::class : Bar::class;
$object = new $class();',
'assertions' => [
'$object' => 'Bar|Foo',
],
],
'instantiateClassAndIsA' => [
'code' => '<?php
/**
* @psalm-consistent-constructor
*/
class Foo {
public function bar() : void{}
}
/**
* @return string|null
*/
function getFooClass() {
return mt_rand(0, 1) === 1 ? Foo::class : null;
}
$foo_class = getFooClass();
if (is_string($foo_class) && is_a($foo_class, Foo::class, true)) {
$foo = new $foo_class();
$foo->bar();
}',
],
'returnStringAfterIsACheckWithClassConst' => [
'code' => '<?php
class Foo{}
function bar(string $maybeBaz) : string {
if (!is_a($maybeBaz, Foo::class, true)) {
throw new Exception("not Foo");
}
return $maybeBaz;
}',
],
'returnStringAfterIsACheckWithString' => [
'code' => '<?php
class Foo{}
2021-10-12 20:13:04 +02:00
/** @param class-string $maybeBaz */
function bar(string $maybeBaz) : string {
2021-10-12 20:13:04 +02:00
if (!is_a($maybeBaz, Foo::class, true)) {
throw new Exception("not Foo");
}
return $maybeBaz;
}',
],
'assignAnonymousClassToArray' => [
'code' => '<?php
/**
* @param array<string, object> $array
*/
function foo(array $array, string $key) : void {
foreach ($array as $i => $item) {
$array[$key] = new class() {};
if ($array[$i] === $array[$key]) {}
}
}',
],
'getClassSelfClass' => [
'code' => '<?php
class C {
public function work(object $obj): string {
if (get_class($obj) === self::class) {
return $obj->baz();
}
return "";
}
public function baz(): string {
return "baz";
}
}',
],
'staticClassComparison' => [
'code' => '<?php
class C {
public function foo1(): string {
if (static::class === D::class) {
return $this->baz();
}
return "";
}
public static function foo2(): string {
if (static::class === D::class) {
return static::bat();
}
return "";
}
}
class D extends C {
public function baz(): string {
return "baz";
}
public static function bat(): string {
return "baz";
}
}',
],
'isAStaticClass' => [
'code' => '<?php
class C {
public function foo1(): string {
if (is_a(static::class, D::class, true)) {
return $this->baz();
}
return "";
}
public static function foo2(): string {
if (is_a(static::class, D::class, true)) {
return static::bat();
}
return "";
}
}
class D extends C {
public function baz(): string {
return "baz";
}
public static function bat(): string {
return "baz";
}
}',
],
'typedMagicCall' => [
'code' => '<?php
class B {
public function __call(string $methodName, array $args) : string {
return __METHOD__;
}
}
class A {
public function __call(string $methodName, array $args) : B {
return new B;
}
}
$a = (new A)->zugzug();
$b = (new A)->bar()->baz();',
'assertions' => [
'$a' => 'B',
'$b' => 'string',
],
],
'abstractCallToInterfaceMethod' => [
'code' => '<?php
interface I {
public function fooBar(): array;
}
abstract class A implements I
{
public function g(): array {
return $this->fooBar();
}
}',
],
'noCrashWhenIgnoringUndefinedClass' => [
'code' => '<?php
class A extends B {
public function foo() {
parent::bar();
}
}',
'assertions' => [],
'ignored_issues' => [
2019-03-23 19:27:54 +01:00
'UndefinedClass',
],
],
'noCrashWhenIgnoringUndefinedParam' => [
'code' => '<?php
function bar(iterable $_i) : void {}
function foo(C $c) : void {
bar($c);
}',
'assertions' => [],
'ignored_issues' => [
'UndefinedClass',
'InvalidArgument',
],
],
'noCrashWhenIgnoringUndefinedReturnIterableArg' => [
'code' => '<?php
function bar(iterable $_i) : void {}
function foo() : D {
return new D();
}
bar(foo());',
'assertions' => [],
'ignored_issues' => [
'UndefinedClass',
'MixedInferredReturnType',
'InvalidArgument',
],
],
'noCrashWhenIgnoringUndefinedReturnClassArg' => [
'code' => '<?php
class Exists {}
function bar(Exists $_i) : void {}
function foo() : D {
return new D();
}
bar(foo());',
'assertions' => [],
'ignored_issues' => [
'UndefinedClass',
'MixedInferredReturnType',
'InvalidArgument',
],
],
'allowAbstractInstantiationOnPossibleChild' => [
'code' => '<?php
/**
* @psalm-consistent-constructor
*/
abstract class A {}
function foo(string $a_class) : void {
if (is_a($a_class, A::class, true)) {
new $a_class();
}
}',
],
'markInferredMutationFreeDuringPropertyTypeInferenceAsActuallyInferred' => [
'code' => '<?php
class A {}
/**
* @psalm-consistent-constructor
*/
abstract class AbstractClass
{
protected $renderer;
public function __construct(A $r)
{
$this->renderer = $r;
}
}
class ConcreteClass extends AbstractClass
{
public function __construct(A $r)
{
parent::__construct($r);
}
}
',
],
'interfaceExistsCreatesClassString' => [
'code' => '<?php
function funB(string $className) : ?ReflectionClass {
if (class_exists($className)) {
return new ReflectionClass($className);
}
if (interface_exists($className)) {
return new ReflectionClass($className);
}
return null;
}',
],
'allowClassExistsAndInterfaceExists' => [
'code' => '<?php
function foo(string $s) : void {
if (class_exists($s) || interface_exists($s)) {}
2019-07-05 22:24:00 +02:00
}',
],
'classExistsWithFalseArgRefinedAsString' => [
'code' => '<?php
/**
* @param class-string $class
2019-05-26 19:34:07 +02:00
* @return string
*/
function autoload(string $class) : string {
if (class_exists($class, false)) {
return $class;
}
return $class;
2019-07-05 22:24:00 +02:00
}',
],
'allowNegatingClassExistsWithoutAutloading' => [
'code' => '<?php
function specifyString(string $className): void{
if (!class_exists($className, false)) {
return;
}
new ReflectionClass($className);
2022-12-18 17:15:15 +01:00
}',
],
'classExistsWithFalseArgInside' => [
'code' => '<?php
function foo(string $s) : void {
if (class_exists($s, false)) {
/** @psalm-suppress MixedMethodCall */
new $s();
}
2019-07-05 22:24:00 +02:00
}',
],
'classAliasOnNonexistantClass' => [
'code' => '<?php
if (!class_exists(\PHPUnit\Framework\TestCase::class)) {
/** @psalm-suppress UndefinedClass */
class_alias(\PHPUnit_Framework_TestCase::class, \PHPUnit\Framework\TestCase::class);
}
class T extends \PHPUnit\Framework\TestCase {
}',
'assertions' => [],
'ignored_issues' => ['PropertyNotSetInConstructor'],
],
'classAliasNoException' => [
'code' => '<?php
class_alias("Bar\F1", "Bar\F2");
namespace Bar {
class F1 {
public static function baz() : void {}
}
}
namespace {
Bar\F2::baz();
}',
],
'classAliasEcho' => [
'code' => '<?php
class A { }
class_alias("A", "A_A");
2022-12-18 17:15:15 +01:00
echo A_A::class;',
],
'classAliasTrait' => [
'code' => '<?php
trait FeatureV1 {}
class_alias(FeatureV1::class, Feature::class);
class App { use Feature; }
2022-12-18 17:15:15 +01:00
',
],
'classAliasParent' => [
'code' => '<?php
class NewA {}
class_alias(NewA::class, OldA::class);
function action(NewA $_m): void {}
class OldAChild extends OldA {}
action(new OldA());
2022-12-18 17:15:15 +01:00
action(new OldAChild());',
],
'classAliasStaticProperty' => [
'code' => '<?php
class A {
/** @var int */
public static $prop = 1;
}
class_alias(A::class, B::class);
2022-12-18 17:15:15 +01:00
B::$prop = 123;',
],
'resourceAndNumericSoftlyReserved' => [
'code' => '<?php
2020-11-04 18:30:02 +01:00
namespace {
class Numeric {}
}
namespace Foo {
class Resource {}
class Numeric {}
}
namespace Bar {
use \Foo\Resource;
use \Foo\Numeric;
new \Foo\Resource();
new \Foo\Numeric();
new Resource();
new Numeric();
/**
* @param Resource $r
* @param Numeric $n
* @return void
*/
2021-05-03 22:59:24 +02:00
function foo(Resource $r, Numeric $n) : void {}
2022-12-18 17:15:15 +01:00
}',
],
'inheritInterfaceFromParent' => [
'code' => '<?php
class A {}
class AChild extends A {}
interface IParent {
public function get(): A;
}
interface IChild extends IParent {
/**
* @psalm-return AChild
*/
public function get(): A;
}
class Concrete implements IChild {
public function get(): A {
return new AChild;
}
}',
],
'noErrorsAfterClassExists' => [
'code' => '<?php
if (class_exists(A::class)) {
if (method_exists(A::class, "method")) {
2020-11-08 17:04:39 +01:00
/** @psalm-suppress MixedArgument */
echo A::method();
}
echo A::class;
/** @psalm-suppress MixedArgument */
echo A::SOME_CONST;
2022-12-18 17:15:15 +01:00
}',
],
'noCrashOnClassExists' => [
'code' => '<?php
if (!class_exists(ReflectionGenerator::class)) {
class ReflectionGenerator {
private $prop;
}
}',
],
'instanceofWithPhantomClass' => [
'code' => '<?php
if (class_exists(NS\UnknonwClass::class)) {
null instanceof NS\UnknonwClass;
}
',
],
'extendException' => [
'code' => '<?php
class ME extends Exception {
protected $message = "hello";
}',
2020-03-24 23:00:20 +01:00
],
'allowFinalReturnerForStatic' => [
'code' => '<?php
/**
* @psalm-consistent-constructor
*/
class A {
/** @return static */
public static function getInstance() {
return new static();
}
}
final class AChild extends A {
public static function getInstance() {
return new AChild();
}
}',
],
'intersectWithStatic' => [
'code' => '<?php
interface M1 {
/** @return M2&static */
function mock();
}
interface M2 {}
class A {}
/** @return A&M1 */
function intersect(A $a) {
assert($a instanceof M1);
if (rand(0, 1)) {
return $a;
}
$b = $a->mock();
return $b;
2022-12-18 17:15:15 +01:00
}',
],
2022-10-14 13:30:57 +02:00
'preventDoubleStaticResolution1' => [
'code' => '<?php
/**
* @template TTKey of array-key
2022-10-14 13:30:57 +02:00
* @template TTValue
*
* @extends ArrayObject<TTKey, TTValue>
*/
class iter extends ArrayObject {
/**
* @return self<TTKey, TTValue>
*/
public function stabilize(): self {
return $this;
}
}
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
2022-10-14 13:30:57 +02:00
class a {
/**
* @return iter<int, static>
*/
public function ret(): iter {
return new iter([$this]);
}
}
class b extends a {
}
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
2022-10-14 13:30:57 +02:00
$a = new b;
$a = $a->ret();
$a = $a->stabilize();',
'assertions' => [
2022-12-18 17:15:15 +01:00
'$a===' => 'iter<int, b&static>',
],
2022-10-14 13:30:57 +02:00
],
'preventDoubleStaticResolution2' => [
'code' => '<?php
/**
* @template TTKey of array-key
2022-10-14 13:30:57 +02:00
* @template TTValue
*
* @extends ArrayObject<TTKey, TTValue>
*/
class iter extends ArrayObject {
/**
* @return self<TTKey, TTValue>
*/
public function stabilize(): self {
return $this;
}
}
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
2022-10-14 13:30:57 +02:00
interface a {
/**
* @return iter<int, static>
*/
public function ret(): iter;
}
class b implements a {
public function ret(): iter {
return new iter([$this]);
}
}
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
2022-10-14 13:30:57 +02:00
/** @var a */
$a = new b;
$a = $a->ret();
$a = $a->stabilize();',
'assertions' => [
2022-12-18 17:15:15 +01:00
'$a===' => 'iter<int, a&static>',
],
2022-10-14 13:30:57 +02:00
],
'preventDoubleStaticResolution3' => [
'code' => '<?php
/**
* @template TTKey of array-key
2022-10-14 13:30:57 +02:00
* @template TTValue
*
* @extends ArrayObject<TTKey, TTValue>
*/
class iter extends ArrayObject {
/**
* @return self<TTKey, TTValue>
*/
public function stabilize(): self {
return $this;
}
}
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
2022-10-14 13:30:57 +02:00
interface a {
/**
* @return iter<int, a&static>
*/
public function ret(): iter;
}
class b implements a {
public function ret(): iter {
return new iter([$this]);
}
}
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
2022-10-14 13:30:57 +02:00
/** @var a */
$a = new b;
$a = $a->ret();
$a = $a->stabilize();',
'assertions' => [
2022-12-18 17:15:15 +01:00
'$a===' => 'iter<int, a&static>',
],
2022-10-14 13:30:57 +02:00
],
'allowTraversableImplementationAlongWithIteratorAggregate' => [
'code' => '<?php
/**
* @implements Traversable<int, 1>
* @implements IteratorAggregate<int, 1>
*/
final class C implements Traversable, IteratorAggregate {
public function getIterator() {
yield 1;
}
}
',
],
'allowTraversableImplementationAlongWithIterator' => [
'code' => '<?php
/**
* @implements Traversable<1, 1>
* @implements Iterator<1, 1>
*/
final class C implements Traversable, Iterator {
public function current() { return 1; }
public function key() { return 1; }
public function next() { }
public function rewind() { }
public function valid() { return false; }
}
',
],
'allowTraversableImplementationOnAbstractClass' => [
'code' => '<?php
/**
* @template TKey
* @template TValue
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
*
* @implements Traversable<TKey, TValue>
*/
abstract class C implements Traversable {}
',
],
'allowIndirectTraversableImplementationOnAbstractClass' => [
'code' => '<?php
/**
* @extends Traversable<int, int>
*/
interface I extends Traversable {}
abstract class C implements I {}
',
],
'newOnNamedObject' => [
'code' => '<?php
$o = new stdClass;
$o2 = new $o;
',
'assertions' => [
'$o2===' => 'stdClass',
],
],
'newOnObjectOfAnonymousClass' => [
'code' => '<?php
function f(): object {
$o = new class {};
return new $o;
}
',
],
'newOnObjectOfAnonymousExtendingNamed' => [
'code' => '<?php
function f(): Exception {
$o = new class extends Exception {};
return new $o;
}
',
],
'newOnObjectOfAnonymousClassImplementingNamed' => [
'code' => '<?php
interface I {}
function f(): I {
$o = new class implements I {};
return new $o;
}
',
],
'throwAnonymousObjects' => [
'code' => '<?php
throw new class extends Exception {};
',
],
'throwTheResultOfNewOnAnAnonymousClass' => [
'code' => '<?php
declare(strict_types=1);
$test = new class extends \Exception { };
throw new $test();
2022-12-18 17:15:15 +01:00
',
],
'privateFinalConstructorsAreAllowed' => [
'code' => <<<'PHP'
<?php
class Foo {
private final function __construct() {}
}
PHP,
],
2023-04-20 18:25:11 +02:00
'singleInheritorIsAllowed' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors FooClass
*/
class BaseClass {}
class FooClass extends BaseClass {}
PHP,
],
'unionInheritorIsAllowed' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors FooClass|BarClass
*/
class BaseClass {}
class FooClass extends BaseClass {}
class BarClass extends FooClass {}
PHP,
],
'multiInheritorIsAllowed' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors FooClass|arClass
*/
class BaseClass {}
class FooClass extends BaseClass {}
class BarClass extends FooClass {}
PHP,
],
'skippedInheritorIsAllowed' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors FooClass|BarClass
*/
class BaseClass {}
class FooClass extends BaseClass {}
class BarClass extends FooClass {}
PHP,
],
'CompositeInheritorIsAllowed' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors BarClass&FooInterface
*/
class BaseClass {}
interface FooInterface {}
class BarClass extends BaseClass implements FooInterface {}
PHP,
],
2023-04-28 18:10:26 +02:00
'InterfaceInheritorIsAllowed' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors FooClass|BarClass
*/
interface BaseInterface {}
class FooClass implements BaseInterface {}
class BarClass implements BaseInterface {}
PHP,
],
'MultiInterfaceInheritorIsAllowed' => [
2023-04-28 18:10:26 +02:00
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors FooClass|BarClass
*/
interface InterfaceA {}
/**
* @psalm-inheritors FooClass|BarClass
*/
interface InterfaceB {}
class FooClass implements InterfaceA, InterfaceB {}
PHP,
],
'InterfaceOfInterfaceInheritorIsAllowed' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors InterfaceB
*/
interface InterfaceA {}
interface InterfaceB extends InterfaceA {}
2023-04-28 18:10:26 +02:00
PHP,
],
];
}
public function providerInvalidCodeParse(): iterable
{
return [
'undefinedClass' => [
'code' => '<?php
(new Foo());',
2017-05-27 02:05:57 +02:00
'error_message' => 'UndefinedClass',
],
'wrongCaseClass' => [
'code' => '<?php
class Foo {}
(new foo());',
2017-05-27 02:05:57 +02:00
'error_message' => 'InvalidClass',
],
'wrongCaseClassWithCall' => [
'code' => '<?php
class A {}
needsA(new A);
2018-01-11 21:50:45 +01:00
function needsA(a $x): void {}',
'error_message' => 'InvalidClass',
],
'invalidThisFetch' => [
'code' => '<?php
echo $this;',
2017-05-27 02:05:57 +02:00
'error_message' => 'InvalidScope',
],
'invalidThisArgument' => [
'code' => '<?php
$this = "hello";',
2017-05-27 02:05:57 +02:00
'error_message' => 'InvalidScope',
],
'undefinedConstant' => [
'code' => '<?php
echo HELLO;',
2017-05-27 02:05:57 +02:00
'error_message' => 'UndefinedConstant',
],
'undefinedClassConstant' => [
'code' => '<?php
class A {}
echo A::HELLO;',
2017-05-27 02:05:57 +02:00
'error_message' => 'UndefinedConstant',
],
'overridePublicAccessLevelToPrivate' => [
'code' => '<?php
class A {
2018-01-11 21:50:45 +01:00
public function fooFoo(): void {}
}
2017-06-29 16:22:49 +02:00
class B extends A {
2018-01-11 21:50:45 +01:00
private function fooFoo(): void {}
}',
2017-05-27 02:05:57 +02:00
'error_message' => 'OverriddenMethodAccess',
],
'overridePublicAccessLevelToProtected' => [
'code' => '<?php
class A {
2018-01-11 21:50:45 +01:00
public function fooFoo(): void {}
}
2017-06-29 16:22:49 +02:00
class B extends A {
2018-01-11 21:50:45 +01:00
protected function fooFoo(): void {}
}',
2017-05-27 02:05:57 +02:00
'error_message' => 'OverriddenMethodAccess',
],
'overrideProtectedAccessLevelToPrivate' => [
'code' => '<?php
class A {
2018-01-11 21:50:45 +01:00
protected function fooFoo(): void {}
}
2017-06-29 16:22:49 +02:00
class B extends A {
2018-01-11 21:50:45 +01:00
private function fooFoo(): void {}
}',
2017-05-27 02:05:57 +02:00
'error_message' => 'OverriddenMethodAccess',
],
'overridePublicPropertyAccessLevelToPrivate' => [
'code' => '<?php
class A {
/** @var string|null */
public $foo;
}
class B extends A {
/** @var string|null */
private $foo;
}',
'error_message' => 'OverriddenPropertyAccess - src' . DIRECTORY_SEPARATOR . 'somefile.php:9:33 - Property B::$foo has different access level than A::$foo',
],
'overridePublicPropertyAccessLevelToProtected' => [
'code' => '<?php
class A {
/** @var string|null */
public $foo;
}
class B extends A {
/** @var string|null */
protected $foo;
}',
'error_message' => 'OverriddenPropertyAccess',
],
'overrideProtectedPropertyAccessLevelToPrivate' => [
'code' => '<?php
class A {
/** @var string|null */
protected $foo;
}
class B extends A {
/** @var string|null */
private $foo;
}',
'error_message' => 'OverriddenPropertyAccess',
],
'classRedefinition' => [
'code' => '<?php
class Foo {}
class Foo {}',
2017-05-27 02:05:57 +02:00
'error_message' => 'DuplicateClass',
],
'classRedefinitionInNamespace' => [
'code' => '<?php
namespace Aye {
class Foo {}
class Foo {}
}',
2017-05-27 02:05:57 +02:00
'error_message' => 'DuplicateClass',
],
'classRedefinitionInSeparateNamespace' => [
'code' => '<?php
namespace Aye {
class Foo {}
}
namespace Aye {
class Foo {}
}',
2017-05-27 02:05:57 +02:00
'error_message' => 'DuplicateClass',
],
'abstractClassInstantiation' => [
'code' => '<?php
abstract class A {}
new A();',
2017-05-27 02:05:57 +02:00
'error_message' => 'AbstractInstantiation',
],
'abstractClassMethod' => [
'code' => '<?php
abstract class A {
abstract public function foo() : void;
}
class B extends A { }',
'error_message' => 'UnimplementedAbstractMethod',
],
'abstractReflectedClassMethod' => [
'code' => '<?php
/**
* @template TKey
* @template TValue
2022-01-31 10:28:47 +01:00
* @extends FilterIterator<TKey, TValue, Iterator<TKey, TValue>>
*/
class DedupeIterator extends FilterIterator {
2022-01-31 10:28:47 +01:00
/**
* @param Iterator<TKey, TValue> $i
*/
public function __construct(Iterator $i) {
parent::__construct($i);
}
}',
'error_message' => 'UnimplementedAbstractMethod',
],
'missingParent' => [
'code' => '<?php
class A extends B { }',
2017-05-27 02:05:57 +02:00
'error_message' => 'UndefinedClass',
],
'lessSpecificReturnStatement' => [
'code' => '<?php
class A {}
class B extends A {}
2017-06-29 16:22:49 +02:00
2018-01-11 21:50:45 +01:00
function foo(A $a): B {
return $a;
}',
'error_message' => 'LessSpecificReturnStatement',
2017-05-27 02:05:57 +02:00
],
'circularReference' => [
'code' => '<?php
class A extends A {}',
'error_message' => 'CircularReference',
],
'preventAbstractInstantiationDefiniteClasss' => [
'code' => '<?php
abstract class A {}
function foo(string $a_class) : void {
if ($a_class === A::class) {
new $a_class();
}
}',
'error_message' => 'AbstractInstantiation',
],
'preventExtendingInterface' => [
'code' => '<?php
interface Foo {}
class Bar extends Foo {}',
'error_message' => 'UndefinedClass',
],
'preventImplementingClass' => [
'code' => '<?php
class Foo {}
class Bar implements Foo {}',
'error_message' => 'UndefinedInterface',
],
'classAliasAlreadyDefinedClass' => [
'code' => '<?php
class A {}
class B {}
if (false) {
class_alias(A::class, B::class);
}
function foo(A $a, B $b) : void {
if ($a === $b) {}
}',
'error_message' => 'TypeDoesNotContainType',
],
'cannotOverrideFinalType' => [
'code' => '<?php
class P {
public final function f() : void {}
}
class C extends P {
public function f() : void {}
}',
'error_message' => 'MethodSignatureMismatch',
],
2020-03-24 23:00:20 +01:00
'preventFinalOverriding' => [
'code' => '<?php
/**
* @psalm-consistent-constructor
*/
2020-03-24 23:00:20 +01:00
class A {
/** @return static */
public static function getInstance() {
return new static();
}
}
class AChild extends A {
public static function getInstance() {
return new AChild();
}
}
class AGrandChild extends AChild {
public function foo() : void {}
}
AGrandChild::getInstance()->foo();',
'error_message' => 'LessSpecificReturnStatement',
],
'preventTraversableImplementation' => [
'code' => '<?php
/**
* @implements Traversable<int, int>
*/
final class C implements Traversable {}
',
'error_message' => 'InvalidTraversableImplementation',
],
'preventIndirectTraversableImplementation' => [
'code' => '<?php
/**
* @extends Traversable<int, int>
*/
interface I extends Traversable {}
final class C implements I {}
',
'error_message' => 'InvalidTraversableImplementation',
],
'detectMissingTemplateExtends' => [
2022-01-26 18:56:35 +01:00
'code' => '<?php
/** @template T */
abstract class A {}
final class B extends A {}
',
'error_message' => 'MissingTemplateParam',
],
'detectMissingTemplateImplements' => [
2022-01-26 18:56:35 +01:00
'code' => '<?php
/** @template T */
interface A {}
final class B implements A {}
',
'error_message' => 'MissingTemplateParam',
],
'detectMissingTemplateUse' => [
2022-01-26 18:56:35 +01:00
'code' => '<?php
/** @template T */
trait A {}
final class B {
use A;
}
',
'error_message' => 'MissingTemplateParam',
],
'detectMissingTemplateExtendsNative' => [
2022-01-26 18:56:35 +01:00
'code' => '<?php
final class C extends ArrayObject {}
',
'error_message' => 'MissingTemplateParam',
],
'detectMissingTemplateImplementsNative' => [
2022-01-26 18:56:35 +01:00
'code' => '<?php
final class C implements Iterator {
public function current(): mixed {
return 0;
}
public function key(): mixed {
return 0;
}
public function next(): void {
}
public function rewind(): void {
}
public function valid(): bool {
return false;
}
}
',
'error_message' => 'MissingTemplateParam',
],
'cannotNameClassConstantClass' => [
2022-10-16 13:59:15 +02:00
'code' => '<?php
class Foo
{
/** @var class-string<Bar> */
protected const CLASS = Bar::class;
}
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
class Bar {}
',
'error_message' => 'ReservedWord',
],
'newOnObject' => [
'code' => '<?php
function f(object $o): object
{
return new $o;
}
',
'error_message' => 'MixedMethodCall',
],
'forbiddenThrowableImplementation' => [
'code' => '<?php
class C implements Throwable {}
',
'error_message' => 'InvalidInterfaceImplementation',
'ignored_issues' => [],
'php_version' => '7.0',
],
'directConstructorCall' => [
'code' => '<?php
class A {
public function __construct() {}
}
$a = new A;
$a->__construct();
',
'error_message' => 'DirectConstructorCall',
],
'directConstructorCallOnThis' => [
'code' => '<?php
class A {
public function __construct() {}
public function f(): void { $this->__construct(); }
}
$a = new A;
$a->f();
',
'error_message' => 'DirectConstructorCall',
],
'privateFinalMethodsAreForbidden' => [
'code' => <<<'PHP'
<?php
class Foo {
final private function baz(): void {}
}
PHP,
'error_message' => 'PrivateFinalMethod',
],
2023-03-03 08:57:56 +01:00
'readonlyClass' => [
'code' => <<<'PHP'
<?php
readonly class Foo {
public int $a = 22;
}
$foo = new Foo;
$foo->a = 33;
PHP,
'error_message' => 'InaccessibleProperty',
'ignored_issues' => [],
'php_version' => '8.2',
],
'readonlyClassRequiresTypedProperties' => [
'code' => <<<'PHP'
<?php
readonly class Foo {
/** @var int */
public $a = 22;
}
PHP,
'error_message' => 'MissingPropertyType',
'ignored_issues' => [],
'php_version' => '8.2',
],
'readonlyClassCannotHaveDynamicProperties' => [
'code' => <<<'PHP'
<?php
#[AllowDynamicProperties]
readonly class Foo {}
PHP,
'error_message' => 'InvalidAttribute',
'ignored_issues' => [],
'php_version' => '8.2',
],
'readonlyClassesCannotBeExtendedByNonReadonlyOnes' => [
'code' => <<<'PHP'
<?php
readonly class Foo {}
class Bar extends Foo {}
PHP,
'error_message' => 'InvalidExtendClass',
'ignored_issues' => [],
'php_version' => '8.2',
],
2023-04-20 18:25:11 +02:00
'classCannotExtendIfNotInInheritors' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors FooClass|BarClass
*/
class BaseClass {}
class BazClass extends BaseClass {} // this is an error
PHP,
'error_message' => 'InheritorViolation',
2023-04-20 18:25:11 +02:00
'ignored_issues' => [],
],
2023-04-28 18:10:26 +02:00
'classCannotImplementIfNotInInheritors' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors FooClass|BarClass
*/
interface BaseInterface {}
class BazClass implements BaseInterface {}
PHP,
'error_message' => 'InheritorViolation',
2023-04-28 18:10:26 +02:00
'ignored_issues' => [],
],
'interfaceCannotImplementIfNotInInheritors' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors FooClass|BarClass
*/
interface BaseInterface {}
interface BazInterface extends BaseInterface {}
PHP,
'error_message' => 'InheritorViolation',
'ignored_issues' => [],
],
2023-04-28 18:10:26 +02:00
'UnfulfilledInterfaceInheritors' => [
'code' => <<<'PHP'
<?php
/**
* @psalm-inheritors FooClass
*/
interface InterfaceA {}
/**
* @psalm-inheritors BarClass
*/
interface InterfaceB {}
class BazClass implements InterFaceA, InterFaceB {}
PHP,
'error_message' => 'InheritorViolation',
2023-04-28 18:10:26 +02:00
'ignored_issues' => [],
],
];
}
2016-12-12 05:41:11 +01:00
}