mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Improve API for inference
This commit is contained in:
parent
11aa3558b7
commit
9f9e8a1b30
@ -29,6 +29,7 @@ use Psalm\Issue\UnusedVariable;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\Mutator\FileMutator;
|
||||
use Psalm\StatementsSource;
|
||||
use Psalm\Storage\FunctionLikeStorage;
|
||||
use Psalm\Storage\MethodStorage;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TNamedObject;
|
||||
@ -364,6 +365,11 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
||||
|
||||
$statements_checker = new StatementsChecker($this);
|
||||
|
||||
// this increases memory, so only do it if running under this flag
|
||||
if ($project_checker->infer_types_from_usage) {
|
||||
$this->statements_checker = $statements_checker;
|
||||
}
|
||||
|
||||
$template_types = $storage->template_types;
|
||||
|
||||
if ($class_storage && $class_storage->template_types) {
|
||||
@ -700,6 +706,32 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
||||
return $this->getFilePath() . ':' . $this->function->getLine() . ':' . 'closure';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FunctionLikeStorage
|
||||
*/
|
||||
public function getFunctionLikeStorage()
|
||||
{
|
||||
$function_id = $this->getMethodId();
|
||||
|
||||
$project_checker = $this->getFileChecker()->project_checker;
|
||||
|
||||
if (strpos($function_id, '::')) {
|
||||
$declaring_method_id = MethodChecker::getDeclaringMethodId($project_checker, $function_id);
|
||||
|
||||
if (!$declaring_method_id) {
|
||||
throw new \UnexpectedValueException('This should never happen');
|
||||
}
|
||||
|
||||
return MethodChecker::getStorage($project_checker, $declaring_method_id);
|
||||
}
|
||||
|
||||
if (!$this->statements_checker) {
|
||||
throw new \UnexpectedValueException('This should not happen either');
|
||||
}
|
||||
|
||||
return FunctionChecker::getStorage($this->statements_checker, $function_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
|
@ -2043,46 +2043,14 @@ class CallChecker
|
||||
$method_identifier = $cased_method_id ? ' of ' . $cased_method_id : '';
|
||||
|
||||
if ($project_checker->infer_types_from_usage
|
||||
&& $input_expr
|
||||
&& $input_expr instanceof PhpParser\Node\Expr\Variable
|
||||
&& is_string($input_expr->name)
|
||||
&& $context
|
||||
&& !isset($context->assigned_vars['$' . $input_expr->name])
|
||||
&& $input_expr
|
||||
&& $input_expr->inferredType
|
||||
) {
|
||||
$source_checker = $statements_checker->getSource();
|
||||
|
||||
if ($source_checker instanceof FunctionLikeChecker) {
|
||||
$function_id = $source_checker->getMethodId();
|
||||
|
||||
if (strpos($function_id, '::')) {
|
||||
$declaring_method_id = MethodChecker::getDeclaringMethodId($project_checker, $function_id);
|
||||
|
||||
if (!$declaring_method_id) {
|
||||
throw new \UnexpectedValueException('This should never happen');
|
||||
}
|
||||
|
||||
$function_storage = MethodChecker::getStorage($project_checker, $declaring_method_id);
|
||||
} else {
|
||||
$function_storage = FunctionChecker::getStorage($statements_checker, $function_id);
|
||||
}
|
||||
|
||||
if ($function_storage->param_types
|
||||
&& array_key_exists($input_expr->name, $function_storage->param_types)
|
||||
&& !$function_storage->param_types[$input_expr->name]
|
||||
) {
|
||||
if (isset($context->possible_param_types[$input_expr->name])) {
|
||||
$context->possible_param_types[$input_expr->name] = Type::combineUnionTypes(
|
||||
$context->possible_param_types[$input_expr->name],
|
||||
$param_type
|
||||
);
|
||||
} else {
|
||||
$context->possible_param_types[$input_expr->name] = $param_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($input_type->isMixed()) {
|
||||
$context->vars_in_scope['$' . $input_expr->name] = clone $param_type;
|
||||
$context->inferType($input_expr, $source_checker->getFunctionLikeStorage(), $param_type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ use Psalm\Checker\ClassChecker;
|
||||
use Psalm\Checker\ClassLikeChecker;
|
||||
use Psalm\Checker\ClosureChecker;
|
||||
use Psalm\Checker\CommentChecker;
|
||||
use Psalm\Checker\FunctionChecker;
|
||||
use Psalm\Checker\FunctionLikeChecker;
|
||||
use Psalm\Checker\MethodChecker;
|
||||
use Psalm\Checker\ProjectChecker;
|
||||
@ -1324,59 +1323,10 @@ class ExpressionChecker
|
||||
$source_checker = $statements_checker->getSource();
|
||||
|
||||
if ($source_checker instanceof FunctionLikeChecker) {
|
||||
$function_id = $source_checker->getMethodId();
|
||||
$function_storage = $source_checker->getFunctionLikeStorage();
|
||||
|
||||
if (strpos($function_id, '::')) {
|
||||
$declaring_method_id = MethodChecker::getDeclaringMethodId($project_checker, $function_id);
|
||||
|
||||
if (!$declaring_method_id) {
|
||||
throw new \UnexpectedValueException('This should never happen');
|
||||
}
|
||||
|
||||
$function_storage = MethodChecker::getStorage($project_checker, $declaring_method_id);
|
||||
} else {
|
||||
$function_storage = FunctionChecker::getStorage($statements_checker, $function_id);
|
||||
}
|
||||
|
||||
if ($function_storage->param_types) {
|
||||
if ($left_type
|
||||
&& $left_type->isMixed()
|
||||
&& $left instanceof PhpParser\Node\Expr\Variable
|
||||
&& is_string($left->name)
|
||||
&& !isset($context->assigned_vars['$' . $left->name])
|
||||
&& array_key_exists($left->name, $function_storage->param_types)
|
||||
&& !$function_storage->param_types[$left->name]
|
||||
) {
|
||||
if (isset($context->possible_param_types[$left->name])) {
|
||||
$context->possible_param_types[$left->name] = Type::combineUnionTypes(
|
||||
$context->possible_param_types[$left->name],
|
||||
Type::getString()
|
||||
);
|
||||
} else {
|
||||
$context->possible_param_types[$left->name] = Type::getString();
|
||||
$context->vars_in_scope['$' . $left->name] = Type::getString();
|
||||
}
|
||||
}
|
||||
|
||||
if ($right_type
|
||||
&& $right_type->isMixed()
|
||||
&& $right instanceof PhpParser\Node\Expr\Variable
|
||||
&& is_string($right->name)
|
||||
&& !isset($context->assigned_vars['$' . $right->name])
|
||||
&& array_key_exists($right->name, $function_storage->param_types)
|
||||
&& !$function_storage->param_types[$right->name]
|
||||
) {
|
||||
if (isset($context->possible_param_types[$right->name])) {
|
||||
$context->possible_param_types[$right->name] = Type::combineUnionTypes(
|
||||
$context->possible_param_types[$right->name],
|
||||
Type::getString()
|
||||
);
|
||||
} else {
|
||||
$context->possible_param_types[$right->name] = Type::getString();
|
||||
$context->vars_in_scope['$' . $right->name] = Type::getString();
|
||||
}
|
||||
}
|
||||
}
|
||||
$context->inferType($left, $function_storage, Type::getString());
|
||||
$context->inferType($right, $function_storage, Type::getString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -2021,19 +1971,7 @@ class ExpressionChecker
|
||||
$source_checker = $statements_checker->getSource();
|
||||
|
||||
if ($source_checker instanceof FunctionLikeChecker) {
|
||||
$function_id = $source_checker->getMethodId();
|
||||
|
||||
if (strpos($function_id, '::')) {
|
||||
$declaring_method_id = MethodChecker::getDeclaringMethodId($project_checker, $function_id);
|
||||
|
||||
if (!$declaring_method_id) {
|
||||
throw new \UnexpectedValueException('This should never happen');
|
||||
}
|
||||
|
||||
$function_storage = MethodChecker::getStorage($project_checker, $declaring_method_id);
|
||||
} else {
|
||||
$function_storage = FunctionChecker::getStorage($statements_checker, $function_id);
|
||||
}
|
||||
$function_storage = $source_checker->getFunctionLikeStorage();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2043,27 +1981,8 @@ class ExpressionChecker
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($function_storage
|
||||
&& $function_storage->param_types
|
||||
&& $part->inferredType
|
||||
&& $part->inferredType->isMixed()
|
||||
) {
|
||||
if ($part instanceof PhpParser\Node\Expr\Variable
|
||||
&& is_string($part->name)
|
||||
&& !isset($context->assigned_vars['$' . $part->name])
|
||||
&& array_key_exists($part->name, $function_storage->param_types)
|
||||
&& !$function_storage->param_types[$part->name]
|
||||
) {
|
||||
if (isset($context->possible_param_types[$part->name])) {
|
||||
$context->possible_param_types[$part->name] = Type::combineUnionTypes(
|
||||
$context->possible_param_types[$part->name],
|
||||
Type::getString()
|
||||
);
|
||||
} else {
|
||||
$context->possible_param_types[$part->name] = Type::getString();
|
||||
$context->vars_in_scope['$' . $part->name] = Type::getString();
|
||||
}
|
||||
}
|
||||
if ($function_storage) {
|
||||
$context->inferType($part, $function_storage, Type::getString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
<?php
|
||||
namespace Psalm;
|
||||
|
||||
use PhpParser;
|
||||
use Psalm\Checker\StatementsChecker;
|
||||
use Psalm\Storage\FunctionLikeStorage;
|
||||
use Psalm\Type\Union;
|
||||
|
||||
class Context
|
||||
@ -248,6 +250,40 @@ class Context
|
||||
return $redefined_vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function inferType(
|
||||
PhpParser\Node\Expr $expr,
|
||||
FunctionLikeStorage $function_storage,
|
||||
Type\Union $inferred_type
|
||||
) {
|
||||
if (!isset($expr->inferredType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$expr_type = $expr->inferredType;
|
||||
|
||||
if ($expr_type
|
||||
&& ($expr_type->isMixed() || (string)$expr_type === (string)$inferred_type)
|
||||
&& $expr instanceof PhpParser\Node\Expr\Variable
|
||||
&& is_string($expr->name)
|
||||
&& !isset($this->assigned_vars['$' . $expr->name])
|
||||
&& array_key_exists($expr->name, $function_storage->param_types)
|
||||
&& !$function_storage->param_types[$expr->name]
|
||||
) {
|
||||
if (isset($this->possible_param_types[$expr->name])) {
|
||||
$this->possible_param_types[$expr->name] = Type::combineUnionTypes(
|
||||
$this->possible_param_types[$expr->name],
|
||||
$inferred_type
|
||||
);
|
||||
} else {
|
||||
$this->possible_param_types[$expr->name] = $inferred_type;
|
||||
$this->vars_in_scope['$' . $expr->name] = clone $inferred_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Context $original_context
|
||||
* @param Context $new_context
|
||||
|
Loading…
x
Reference in New Issue
Block a user