mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +01:00
Make Psalm a bit faster
This commit is contained in:
parent
95de80b7f1
commit
c7b0f6685f
@ -589,7 +589,6 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
foreach ($storage->appearing_property_ids as $property_name => $appearing_property_id) {
|
foreach ($storage->appearing_property_ids as $property_name => $appearing_property_id) {
|
||||||
$property_class_name = self::getDeclaringClassForProperty($appearing_property_id);
|
$property_class_name = self::getDeclaringClassForProperty($appearing_property_id);
|
||||||
$property_class_storage = self::$storage[strtolower((string)$property_class_name)];
|
$property_class_storage = self::$storage[strtolower((string)$property_class_name)];
|
||||||
@ -675,6 +674,7 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$class_context->include_location = $previous_context_include_location;
|
$class_context->include_location = $previous_context_include_location;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -692,7 +692,20 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
|||||||
|
|
||||||
$property = $property_class_storage->properties[$property_name];
|
$property = $property_class_storage->properties[$property_name];
|
||||||
|
|
||||||
if (!$property->has_default &&
|
$constructor_class_storage = null;
|
||||||
|
|
||||||
|
if (isset($property_class_storage->methods['__construct'])
|
||||||
|
&& $property_class_storage !== $storage
|
||||||
|
) {
|
||||||
|
$constructor_class_storage = $property_class_storage;
|
||||||
|
} elseif (!empty($property_class_storage->overridden_method_ids['__construct'])) {
|
||||||
|
list($construct_fqcln) =
|
||||||
|
explode('::', $property_class_storage->overridden_method_ids['__construct'][0]);
|
||||||
|
$constructor_class_storage = self::$storage[strtolower($construct_fqcln)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!$constructor_class_storage || !$constructor_class_storage->all_properties_set_in_constructor) &&
|
||||||
|
!$property->has_default &&
|
||||||
$property->type &&
|
$property->type &&
|
||||||
!$property->type->isMixed() &&
|
!$property->type->isMixed() &&
|
||||||
!$property->type->isNullable() &&
|
!$property->type->isNullable() &&
|
||||||
@ -704,10 +717,64 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($uninitialized_properties
|
if ($uninitialized_properties && !($this instanceof TraitChecker)) {
|
||||||
&& !($this instanceof TraitChecker)
|
if (!$storage->abstract
|
||||||
&& !$storage->abstract
|
&& !$constructor_checker
|
||||||
) {
|
&& isset($storage->declaring_method_ids['__construct'])
|
||||||
|
) {
|
||||||
|
list($construct_fqcln) = explode('::', $storage->declaring_method_ids['__construct']);
|
||||||
|
|
||||||
|
$constructor_class_storage = self::$storage[strtolower($construct_fqcln)];
|
||||||
|
|
||||||
|
// ignore oldstyle constructors and classes without any declared properties
|
||||||
|
if (isset($constructor_class_storage->methods['__construct'])) {
|
||||||
|
$constructor_storage = self::$storage[strtolower($construct_fqcln)]->methods['__construct'];
|
||||||
|
|
||||||
|
$fake_constructor_params = array_map(
|
||||||
|
/** @return PhpParser\Node\Param */
|
||||||
|
function (\Psalm\FunctionLikeParameter $param) {
|
||||||
|
return (new PhpParser\Builder\Param($param->name))
|
||||||
|
->setTypehint((string)$param->signature_type)
|
||||||
|
->getNode();
|
||||||
|
},
|
||||||
|
$constructor_storage->params
|
||||||
|
);
|
||||||
|
|
||||||
|
$fake_constructor_stmt_args = array_map(
|
||||||
|
/** @return PhpParser\Node\Arg */
|
||||||
|
function (\Psalm\FunctionLikeParameter $param) {
|
||||||
|
return new PhpParser\Node\Arg(new PhpParser\Node\Expr\Variable($param->name));
|
||||||
|
},
|
||||||
|
$constructor_storage->params
|
||||||
|
);
|
||||||
|
|
||||||
|
$fake_constructor_stmts = [
|
||||||
|
new PhpParser\Node\Expr\StaticCall(
|
||||||
|
new PhpParser\Node\Name(['parent']),
|
||||||
|
'__construct',
|
||||||
|
$fake_constructor_stmt_args
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
$fake_stmt = new PhpParser\Node\Stmt\ClassMethod(
|
||||||
|
'__construct',
|
||||||
|
[
|
||||||
|
'type' => PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC,
|
||||||
|
'params' => $fake_constructor_params,
|
||||||
|
'stmts' => $fake_constructor_stmts,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$constructor_checker = $this->analyzeClassMethod(
|
||||||
|
$fake_stmt,
|
||||||
|
$this,
|
||||||
|
$class_context,
|
||||||
|
$global_context,
|
||||||
|
$update_docblocks
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($constructor_checker) {
|
if ($constructor_checker) {
|
||||||
$method_context = clone $class_context;
|
$method_context = clone $class_context;
|
||||||
$method_context->collect_initializations = true;
|
$method_context->collect_initializations = true;
|
||||||
@ -716,6 +783,8 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
|||||||
|
|
||||||
$constructor_checker->analyze($method_context, $global_context, true);
|
$constructor_checker->analyze($method_context, $global_context, true);
|
||||||
|
|
||||||
|
$all_properties_set_in_constructor = true;
|
||||||
|
|
||||||
foreach ($uninitialized_properties as $property_name => $property) {
|
foreach ($uninitialized_properties as $property_name => $property) {
|
||||||
if (!isset($method_context->vars_in_scope['$this->' . $property_name])) {
|
if (!isset($method_context->vars_in_scope['$this->' . $property_name])) {
|
||||||
throw new \UnexpectedValueException('$this->' . $property_name . ' should be in scope');
|
throw new \UnexpectedValueException('$this->' . $property_name . ' should be in scope');
|
||||||
@ -723,6 +792,10 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
|||||||
|
|
||||||
$end_type = $method_context->vars_in_scope['$this->' . $property_name];
|
$end_type = $method_context->vars_in_scope['$this->' . $property_name];
|
||||||
|
|
||||||
|
if (!$end_type->initialized) {
|
||||||
|
$all_properties_set_in_constructor = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!$end_type->initialized && $property->location) {
|
if (!$end_type->initialized && $property->location) {
|
||||||
$property_id = $this->fq_class_name . '::$' . $property_name;
|
$property_id = $this->fq_class_name . '::$' . $property_name;
|
||||||
|
|
||||||
@ -738,7 +811,9 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
$storage->all_properties_set_in_constructor = $all_properties_set_in_constructor;
|
||||||
|
} elseif (!$storage->abstract) {
|
||||||
$first_uninitialized_property = array_shift($uninitialized_properties);
|
$first_uninitialized_property = array_shift($uninitialized_properties);
|
||||||
|
|
||||||
if ($first_uninitialized_property->location) {
|
if ($first_uninitialized_property->location) {
|
||||||
@ -826,7 +901,7 @@ abstract class ClassLikeChecker extends SourceChecker implements StatementsSourc
|
|||||||
$global_context ? clone $global_context : null
|
$global_context ? clone $global_context : null
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($config->reportIssueInFile('InvalidReturnType', $source->getFilePath())) {
|
if ($stmt->name !== '__construct' && $config->reportIssueInFile('InvalidReturnType', $source->getFilePath())) {
|
||||||
$return_type_location = null;
|
$return_type_location = null;
|
||||||
$secondary_return_type_location = null;
|
$secondary_return_type_location = null;
|
||||||
|
|
||||||
|
@ -332,8 +332,11 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
|||||||
$this->getMethodId()
|
$this->getMethodId()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$context->vars_in_scope['$' . $function_param->name] = $param_type;
|
||||||
|
$context->vars_possibly_in_scope['$' . $function_param->name] = true;
|
||||||
|
|
||||||
if (!$function_param->location) {
|
if (!$function_param->location) {
|
||||||
throw new \UnexpectedValueException('We should know where this code is');
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$parser_param = $this->function->getParams()[$offset];
|
$parser_param = $this->function->getParams()[$offset];
|
||||||
@ -386,9 +389,6 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$context->vars_in_scope['$' . $function_param->name] = $param_type;
|
|
||||||
$context->vars_possibly_in_scope['$' . $function_param->name] = true;
|
|
||||||
|
|
||||||
if ($function_param->by_ref && !$param_type->isMixed()) {
|
if ($function_param->by_ref && !$param_type->isMixed()) {
|
||||||
$context->byref_constraints['$' . $function_param->name] = new \Psalm\ReferenceConstraint($param_type);
|
$context->byref_constraints['$' . $function_param->name] = new \Psalm\ReferenceConstraint($param_type);
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,11 @@ class ClassLikeStorage
|
|||||||
*/
|
*/
|
||||||
public $reflected = false;
|
public $reflected = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $all_properties_set_in_constructor = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
|
@ -390,18 +390,20 @@ class PropertyTypeTest extends TestCase
|
|||||||
}
|
}
|
||||||
}',
|
}',
|
||||||
],
|
],
|
||||||
'SKIPPED-abstractClassConstructorAndImplicitChildConstructor' => [
|
'abstractClassConstructorAndImplicitChildConstructor' => [
|
||||||
'<?php
|
'<?php
|
||||||
abstract class A {
|
abstract class A {
|
||||||
/** @var string */
|
/** @var string */
|
||||||
public $foo;
|
public $foo;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct(int $bar) {
|
||||||
$this->foo = "";
|
$this->foo = (string)$bar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class B extends A {}',
|
class B extends A {}
|
||||||
|
|
||||||
|
class E extends \Exception{}',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -657,6 +659,16 @@ class PropertyTypeTest extends TestCase
|
|||||||
}',
|
}',
|
||||||
'error_message' => 'MissingConstructor',
|
'error_message' => 'MissingConstructor',
|
||||||
],
|
],
|
||||||
|
'abstractClassInheritsNoConstructor' => [
|
||||||
|
'<?php
|
||||||
|
abstract class A {
|
||||||
|
/** @var string */
|
||||||
|
public $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B extends A {}',
|
||||||
|
'error_message' => 'MissingConstructor',
|
||||||
|
],
|
||||||
'notSetInAllBranchesOfIf' => [
|
'notSetInAllBranchesOfIf' => [
|
||||||
'<?php
|
'<?php
|
||||||
class A {
|
class A {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user