mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Tighten up rules around instantiation a bit more
This commit is contained in:
parent
ccd4eaa8e7
commit
afce2dc66f
@ -18,6 +18,7 @@ use function count;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class FileAnalyzer extends SourceAnalyzer implements StatementsSource
|
||||
{
|
||||
|
@ -134,10 +134,22 @@ class NewAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\CallAna
|
||||
$lhs_type_part->param_name,
|
||||
$lhs_type_part->as_type
|
||||
? new Type\Union([$lhs_type_part->as_type])
|
||||
: Type::parseString($lhs_type_part->as),
|
||||
: Type::getObject(),
|
||||
$lhs_type_part->defining_class
|
||||
);
|
||||
|
||||
if (!$lhs_type_part->as_type) {
|
||||
if (IssueBuffer::accepts(
|
||||
new MixedMethodCall(
|
||||
'Cannot call constructor on an unknown class',
|
||||
new CodeLocation($statements_analyzer->getSource(), $stmt)
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues()
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
|
||||
if ($new_type) {
|
||||
$new_type = Type::combineUnionTypes(
|
||||
$new_type,
|
||||
@ -146,6 +158,28 @@ class NewAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\CallAna
|
||||
} else {
|
||||
$new_type = new Type\Union([$new_type_part]);
|
||||
}
|
||||
|
||||
if ($lhs_type_part->as_type
|
||||
&& $codebase->classlikes->classExists($lhs_type_part->as_type->value)
|
||||
) {
|
||||
$as_storage = $codebase->classlike_storage_provider->get(
|
||||
$lhs_type_part->as_type->value
|
||||
);
|
||||
|
||||
if (!$as_storage->preserve_constructor_signature) {
|
||||
if (IssueBuffer::accepts(
|
||||
new UnsafeInstantiation(
|
||||
'Cannot safely instantiate class ' . $lhs_type_part->as_type->value
|
||||
. ' with "new $class_name" as '
|
||||
. ' its constructor might change in child classes',
|
||||
new CodeLocation($statements_analyzer->getSource(), $stmt)
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues()
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($lhs_type_part->as_type) {
|
||||
@ -174,6 +208,28 @@ class NewAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\CallAna
|
||||
$generated_type = $lhs_type_part->as_type
|
||||
? clone $lhs_type_part->as_type
|
||||
: new Type\Atomic\TObject();
|
||||
|
||||
if ($lhs_type_part->as_type
|
||||
&& $codebase->classlikes->classExists($lhs_type_part->as_type->value)
|
||||
) {
|
||||
$as_storage = $codebase->classlike_storage_provider->get(
|
||||
$lhs_type_part->as_type->value
|
||||
);
|
||||
|
||||
if (!$as_storage->preserve_constructor_signature) {
|
||||
if (IssueBuffer::accepts(
|
||||
new UnsafeInstantiation(
|
||||
'Cannot safely instantiate class ' . $lhs_type_part->as_type->value
|
||||
. ' with "new $class_name" as '
|
||||
. ' its constructor might change in child classes',
|
||||
new CodeLocation($statements_analyzer->getSource(), $stmt)
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues()
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($lhs_type_part instanceof Type\Atomic\GetClassT) {
|
||||
$generated_type = new Type\Atomic\TObject();
|
||||
|
||||
|
@ -12,6 +12,7 @@ use Psalm\Internal\PhpVisitor\ReflectorVisitor;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class FileScanner implements FileSource
|
||||
{
|
||||
|
@ -620,6 +620,9 @@ class ClassStringTest extends TestCase
|
||||
],
|
||||
'createNewObjectFromGetClass' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class Example {
|
||||
static function staticMethod(): string {
|
||||
return "";
|
||||
@ -706,6 +709,9 @@ class ClassStringTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class A
|
||||
{
|
||||
use Factory;
|
||||
@ -717,6 +723,9 @@ class ClassStringTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class B
|
||||
{
|
||||
use Factory;
|
||||
@ -730,6 +739,9 @@ class ClassStringTest extends TestCase
|
||||
],
|
||||
'staticClassReturn' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class A {
|
||||
/** @return static */
|
||||
public static function getInstance() {
|
||||
@ -740,6 +752,9 @@ class ClassStringTest extends TestCase
|
||||
],
|
||||
'getCalledClassIsStaticClass' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class A {
|
||||
/** @return static */
|
||||
public function getStatic() {
|
||||
|
@ -155,6 +155,9 @@ class ClassTest extends TestCase
|
||||
],
|
||||
'instantiateClassAndIsA' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class Foo {
|
||||
public function bar() : void{}
|
||||
}
|
||||
@ -364,6 +367,9 @@ class ClassTest extends TestCase
|
||||
],
|
||||
'allowAbstractInstantiationOnPossibleChild' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
abstract class A {}
|
||||
|
||||
function foo(string $a_class) : void {
|
||||
|
@ -20,7 +20,11 @@ class ClassStringMapTest extends TestCase
|
||||
'<?php
|
||||
namespace Bar;
|
||||
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class Foo {}
|
||||
|
||||
class A {
|
||||
/** @var class-string-map<T as Foo, T> */
|
||||
public static array $map = [];
|
||||
@ -44,7 +48,11 @@ class ClassStringMapTest extends TestCase
|
||||
'<?php
|
||||
namespace Bar;
|
||||
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class Foo {}
|
||||
|
||||
class A {
|
||||
/** @var class-string-map<T as Foo, T> */
|
||||
public static array $map = [];
|
||||
@ -77,7 +85,6 @@ class ClassStringMapTest extends TestCase
|
||||
'<?php
|
||||
namespace Bar;
|
||||
|
||||
class Foo {}
|
||||
class A {
|
||||
/** @var class-string-map<T, T> */
|
||||
public static array $map = [];
|
||||
@ -96,7 +103,6 @@ class ClassStringMapTest extends TestCase
|
||||
'<?php
|
||||
namespace Bar;
|
||||
|
||||
class Foo {}
|
||||
class A {
|
||||
/** @var class-string-map<T, T> */
|
||||
public static array $map = [];
|
||||
|
@ -2201,6 +2201,7 @@ class ClassTemplateExtendsTest extends TestCase
|
||||
* @template T as object
|
||||
* @param class-string<T> $t
|
||||
* @return I<T>
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
function f(string $t) {
|
||||
return new C(new $t);
|
||||
@ -2959,6 +2960,7 @@ class ClassTemplateExtendsTest extends TestCase
|
||||
* @template T2
|
||||
* @param class-string<T2> $t
|
||||
* @return ?T2
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
public function get($t) {
|
||||
return new $t;
|
||||
@ -2970,6 +2972,7 @@ class ClassTemplateExtendsTest extends TestCase
|
||||
* @template T3
|
||||
* @param class-string<T3> $t
|
||||
* @return ?T3
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
public function get($t) {
|
||||
return new $t;
|
||||
|
@ -40,6 +40,7 @@ class ClassTemplateTest extends TestCase
|
||||
|
||||
/**
|
||||
* @return T
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
public function bar() {
|
||||
$t = $this->T;
|
||||
@ -98,6 +99,7 @@ class ClassTemplateTest extends TestCase
|
||||
|
||||
/**
|
||||
* @return T
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
public function bar() {
|
||||
$t = $this->T;
|
||||
@ -169,6 +171,7 @@ class ClassTemplateTest extends TestCase
|
||||
|
||||
/**
|
||||
* @return T
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
public function bar() {
|
||||
$t = $this->T;
|
||||
@ -1239,6 +1242,8 @@ class ClassTemplateTest extends TestCase
|
||||
* @param class-string<Q> $obj2
|
||||
*
|
||||
* @return array<I, V|Q>
|
||||
*
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
private function appender(string $obj2): array
|
||||
{
|
||||
|
@ -278,6 +278,9 @@ class ConditionalReturnTypeTest extends TestCase
|
||||
return new Application();
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
return new $className();
|
||||
}
|
||||
|
||||
|
@ -92,6 +92,7 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
* @template T as Exception
|
||||
* @param T::class $type
|
||||
* @return T
|
||||
* @psalm-suppress UnsafeInstantiation
|
||||
*/
|
||||
function a(string $type): Exception {
|
||||
return new $type;
|
||||
@ -114,6 +115,8 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
* @param class-string<T> $foo
|
||||
*
|
||||
* @return T
|
||||
*
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
function Foo(string $foo) : object {
|
||||
return new $foo;
|
||||
@ -123,6 +126,9 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
],
|
||||
'templatedClassStringParamAsClass' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
abstract class C {
|
||||
public function foo() : void{}
|
||||
}
|
||||
@ -160,6 +166,9 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
],
|
||||
'templatedClassStringParamAsObject' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
abstract class C {
|
||||
public function foo() : void{}
|
||||
}
|
||||
@ -170,6 +179,7 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
* @param class-string<T> $c_class
|
||||
*
|
||||
* @psalm-return T
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
public static function get(string $c_class) {
|
||||
return new $c_class;
|
||||
@ -186,6 +196,9 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
],
|
||||
'templatedClassStringParamMoreSpecific' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
abstract class C {
|
||||
public function foo() : void{}
|
||||
}
|
||||
@ -281,6 +294,7 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
* @psalm-template T of object
|
||||
* @psalm-param T|class-string<T> $someType
|
||||
* @psalm-return T
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
function getObject($someType) {
|
||||
if (is_object($someType)) {
|
||||
@ -304,6 +318,7 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
* @psalm-template T of object
|
||||
* @psalm-param T|class-string<T> $someType
|
||||
* @psalm-return T
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
function getObject($someType) {
|
||||
if (is_object($someType)) {
|
||||
@ -333,6 +348,7 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
* @param-out array<T> $map
|
||||
* @param int $id
|
||||
* @return T
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
function get(string $className, array &$map, int $id) {
|
||||
if(!array_key_exists($id, $map)) {
|
||||
@ -361,6 +377,7 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
* @template T as object
|
||||
* @param T|class-string<T> $s
|
||||
* @return T
|
||||
* @psalm-suppress MixedMethodCall
|
||||
*/
|
||||
function bar($s) {
|
||||
if (is_object($s)) {
|
||||
@ -520,7 +537,14 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
],
|
||||
'templateAsUnionClassStringPassingValidClass' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class A {}
|
||||
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class B {}
|
||||
|
||||
/**
|
||||
@ -618,6 +642,9 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
],
|
||||
'templateFromDifferentClassStrings' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class A {}
|
||||
|
||||
class B extends A {}
|
||||
@ -739,8 +766,16 @@ class FunctionClassStringTemplateTest extends TestCase
|
||||
],
|
||||
'templateAsUnionClassStringPassingInvalidClass' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class A {}
|
||||
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
class B {}
|
||||
|
||||
class C {}
|
||||
|
||||
/**
|
||||
|
@ -1222,6 +1222,7 @@ class FunctionTemplateTest extends TestCase
|
||||
string $className,
|
||||
Closure $outmaker
|
||||
) : object {
|
||||
/** @psalm-suppress MixedMethodCall */
|
||||
$t = new $className();
|
||||
$outmaker($t);
|
||||
return $t;
|
||||
@ -1247,6 +1248,7 @@ class FunctionTemplateTest extends TestCase
|
||||
string $className,
|
||||
callable $outmaker
|
||||
) : object {
|
||||
/** @psalm-suppress MixedMethodCall */
|
||||
$t = new $className();
|
||||
$outmaker($t);
|
||||
return $t;
|
||||
@ -1827,6 +1829,7 @@ class FunctionTemplateTest extends TestCase
|
||||
string $className,
|
||||
Closure $outmaker
|
||||
) : object {
|
||||
/** @psalm-suppress MixedMethodCall */
|
||||
$t = new $className();
|
||||
$outmaker($t);
|
||||
return $t;
|
||||
@ -1855,6 +1858,7 @@ class FunctionTemplateTest extends TestCase
|
||||
string $className,
|
||||
callable $outmaker
|
||||
) : object {
|
||||
/** @psalm-suppress MixedMethodCall */
|
||||
$t = new $className();
|
||||
$outmaker($t);
|
||||
return $t;
|
||||
|
@ -682,6 +682,9 @@ class UnusedCodeTest extends TestCase
|
||||
],
|
||||
'usedThroughNewClassStringOfBase' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-consistent-constructor
|
||||
*/
|
||||
abstract class FooBase {
|
||||
public final function __construct() {}
|
||||
|
||||
|
@ -881,6 +881,7 @@ class UnusedVariableTest extends TestCase
|
||||
function bar(string $type) : ArrayObject {
|
||||
$data = [["foo"], ["bar"]];
|
||||
|
||||
/** @psalm-suppress UnsafeInstantiation */
|
||||
return new $type($data[0]);
|
||||
}',
|
||||
],
|
||||
|
Loading…
x
Reference in New Issue
Block a user