1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Failed and regression tests for magic static methods

List failed tests:
```
1) Psalm\Tests\MagicMethodAnnotationTest::testNoSealAllMethodsWithStatic
Failed asserting that exception of type "Psalm\Exception\CodeException" is thrown.

2) Psalm\Tests\MagicMethodAnnotationTest::testSealAllMethodsWithoutFooWithStatic
Failed asserting that exception of type "Psalm\Exception\CodeException" is thrown.

3) Psalm\Tests\MagicMethodAnnotationTest::testInvalidCode with data set "inheritSealedMethodsWithStatic"
Failed asserting that exception of type "Psalm\Exception\CodeException" is thrown.
```
This commit is contained in:
Ivan Sidorov 2024-02-07 08:59:49 +00:00
parent fe2c67ec89
commit 6e361aa9e6

View File

@ -46,6 +46,35 @@ class MagicMethodAnnotationTest extends TestCase
$this->analyzeFile('somefile.php', new Context());
}
public function testPhpDocMethodWhenUndefinedWithStatic(): void
{
Config::getInstance()->use_phpdoc_method_without_magic_or_parent = true;
$this->addFile(
'somefile.php',
'<?php
/**
* @method static string getString()
* @method static void setInteger(int $integer)
* @method static mixed setString(int $integer)
* @method static bool getBool(string $foo)
* @method static (string|int)[] getArray()
* @method static (callable() : string) getCallable()
*/
class Child {}
$a = Child::getString();
Child::setInteger(4);
/** @psalm-suppress MixedAssignment */
$b = Child::setString(5);
$c = Child::getBool("hello");
$d = Child::getArray();
$e = Child::getCallable();',
);
$this->analyzeFile('somefile.php', new Context());
}
public function testPhpDocMethodWhenTemplated(): void
{
Config::getInstance()->use_phpdoc_method_without_magic_or_parent = true;
@ -99,6 +128,28 @@ class MagicMethodAnnotationTest extends TestCase
$this->analyzeFile('somefile.php', $context);
}
public function testAnnotationWithoutCallConfigWithStatic(): void
{
$this->expectExceptionMessage('UndefinedMethod');
$this->expectException(CodeException::class);
Config::getInstance()->use_phpdoc_method_without_magic_or_parent = false;
$this->addFile(
'somefile.php',
'<?php
/**
* @method static string getString()
*/
class Child {}
Child::getString();',
);
$context = new Context();
$this->analyzeFile('somefile.php', $context);
}
public function testOverrideParentClassRetunType(): void
{
Config::getInstance()->use_phpdoc_method_without_magic_or_parent = true;
@ -193,6 +244,48 @@ class MagicMethodAnnotationTest extends TestCase
'$e' => 'callable():string',
],
],
'validSimpleAnnotationsWithStatic' => [
'code' => '<?php
class ParentClass {
public function __callStatic(string $name, array $args) {}
}
/**
* @method static string getString() dsa sada
* @method static void setInteger(int $integer) dsa sada
* @method static mixed setString(int $integer) dsa sada
* @method static mixed setMixed(mixed $foo) dsa sada
* @method static mixed setImplicitMixed($foo) dsa sada
* @method static mixed setAnotherImplicitMixed( $foo, $bar,$baz) dsa sada
* @method static mixed setYetAnotherImplicitMixed( $foo ,$bar, $baz ) dsa sada
* @method static bool getBool(string $foo) dsa sada
* @method static (string|int)[] getArray() with some text dsa sada
* @method static (callable() : string) getCallable() dsa sada
* @method static static getInstance() dsa sada
*/
class Child extends ParentClass {}
$a = Child::getString();
Child::setInteger(4);
/** @psalm-suppress MixedAssignment */
$b = Child::setString(5);
$c = Child::getBool("hello");
$d = Child::getArray();
$e = Child::getCallable();
$f = Child::getInstance();
Child::setMixed("hello");
Child::setMixed(4);
Child::setImplicitMixed("hello");
Child::setImplicitMixed(4);',
'assertions' => [
'$a' => 'string',
'$b' => 'mixed',
'$c' => 'bool',
'$d' => 'array<array-key, int|string>',
'$e' => 'callable():string',
'$f' => 'Child',
],
],
'validAnnotationWithDefault' => [
'code' => '<?php
class ParentClass {
@ -1116,6 +1209,20 @@ class MagicMethodAnnotationTest extends TestCase
$b->foo();',
'error_message' => 'UndefinedMagicMethod',
],
'inheritSealedMethodsWithStatic' => [
'code' => '<?php
/**
* @psalm-seal-methods
*/
class A {
public static function __callStatic(string $method, array $args) {}
}
class B extends A {}
B::foo();',
'error_message' => 'UndefinedMagicMethod',
],
'lonelyMethod' => [
'code' => '<?php
/**
@ -1174,6 +1281,29 @@ class MagicMethodAnnotationTest extends TestCase
$this->analyzeFile('somefile.php', new Context());
}
public function testSealAllMethodsWithoutFooWithStatic(): void
{
Config::getInstance()->seal_all_methods = true;
$this->addFile(
'somefile.php',
'<?php
class A {
public static function __callStatic(string $method, array $args) {}
}
class B extends A {}
B::foo();
',
);
$error_message = 'UndefinedMagicMethod';
$this->expectException(CodeException::class);
$this->expectExceptionMessage($error_message);
$this->analyzeFile('somefile.php', new Context());
}
public function testNoSealAllMethods(): void
{
Config::getInstance()->seal_all_methods = true;
@ -1199,6 +1329,30 @@ class MagicMethodAnnotationTest extends TestCase
$this->analyzeFile('somefile.php', new Context());
}
public function testNoSealAllMethodsWithStatic(): void
{
Config::getInstance()->seal_all_methods = true;
$this->addFile(
'somefile.php',
'<?php
/** @psalm-no-seal-properties */
class A {
public static function __callStatic(string $method, array $args) {}
}
class B extends A {}
B::foo();
',
);
$error_message = 'UndefinedMagicMethod';
$this->expectException(CodeException::class);
$this->expectExceptionMessage($error_message);
$this->analyzeFile('somefile.php', new Context());
}
public function testSealAllMethodsWithFoo(): void
{
Config::getInstance()->seal_all_methods = true;
@ -1221,6 +1375,27 @@ class MagicMethodAnnotationTest extends TestCase
$this->analyzeFile('somefile.php', new Context());
}
public function testSealAllMethodsWithFooWithStatic(): void
{
Config::getInstance()->seal_all_methods = true;
$this->addFile(
'somefile.php',
'<?php
class A {
public static function __callStatic(string $method, array $args) {}
public static function foo(): void {}
}
class B extends A {}
B::foo();
',
);
$this->analyzeFile('somefile.php', new Context());
}
public function testSealAllMethodsWithFooInSubclass(): void
{
Config::getInstance()->seal_all_methods = true;
@ -1244,6 +1419,28 @@ class MagicMethodAnnotationTest extends TestCase
$this->analyzeFile('somefile.php', new Context());
}
public function testSealAllMethodsWithFooInSubclassWithStatic(): void
{
Config::getInstance()->seal_all_methods = true;
$this->addFile(
'somefile.php',
'<?php
class A {
public static function __callStatic(string $method, array $args) {}
}
class B extends A {
public static function foo(): void {}
}
B::foo();
',
);
$this->analyzeFile('somefile.php', new Context());
}
public function testSealAllMethodsWithFooAnnotated(): void
{
Config::getInstance()->seal_all_methods = true;
@ -1266,6 +1463,27 @@ class MagicMethodAnnotationTest extends TestCase
$this->analyzeFile('somefile.php', new Context());
}
public function testSealAllMethodsWithFooAnnotatedWithStatic(): void
{
Config::getInstance()->seal_all_methods = true;
$this->addFile(
'somefile.php',
'<?php
/** @method static int foo() */
class A {
public static function __callStatic(string $method, array $args) {}
}
class B extends A {}
B::foo();
',
);
$this->analyzeFile('somefile.php', new Context());
}
public function testSealAllMethodsSetToFalse(): void
{
Config::getInstance()->seal_all_methods = false;
@ -1287,6 +1505,26 @@ class MagicMethodAnnotationTest extends TestCase
$this->analyzeFile('somefile.php', new Context());
}
public function testSealAllMethodsSetToFalseWithStatic(): void
{
Config::getInstance()->seal_all_methods = false;
$this->addFile(
'somefile.php',
'<?php
class A {
public static function __callStatic(string $method, array $args) {}
}
class B extends A {}
B::foo();
',
);
$this->analyzeFile('somefile.php', new Context());
}
public function testIntersectionTypeWhenMagicMethodDoesNotExistButIsProvidedBySecondType(): void
{
$this->addFile(