1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-09 06:28:36 +01:00
psalm/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php

264 lines
9.5 KiB
PHP
Raw Normal View History

2020-05-18 21:13:27 +02:00
<?php
2020-05-18 21:13:27 +02:00
namespace Psalm\Internal\Analyzer\Statements\Expression;
use PhpParser;
2021-06-08 04:55:21 +02:00
use Psalm\CodeLocation;
use Psalm\CodeLocation\DocblockTypeLocation;
2021-06-08 04:55:21 +02:00
use Psalm\Context;
use Psalm\Exception\DocblockParseException;
2020-05-18 21:13:27 +02:00
use Psalm\Internal\Analyzer\CommentAnalyzer;
use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
2022-10-13 13:42:35 +02:00
use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\AtomicPropertyFetchAnalyzer;
2021-06-08 04:55:21 +02:00
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
2020-05-18 21:13:27 +02:00
use Psalm\Internal\Analyzer\StatementsAnalyzer;
2021-06-08 04:55:21 +02:00
use Psalm\Internal\Analyzer\TraitAnalyzer;
2020-05-18 21:13:27 +02:00
use Psalm\Internal\FileManipulation\FileManipulationBuffer;
2021-12-03 20:11:20 +01:00
use Psalm\Internal\Type\TypeExpander;
2020-05-18 21:13:27 +02:00
use Psalm\Issue\InvalidDocblock;
use Psalm\Issue\UnnecessaryVarAnnotation;
use Psalm\IssueBuffer;
use Psalm\Type;
2021-12-13 04:45:57 +01:00
use Psalm\Type\Atomic\TGenericObject;
use Psalm\Type\Atomic\TNamedObject;
2020-05-18 21:13:27 +02:00
2022-10-13 13:42:35 +02:00
use function array_values;
2022-01-03 07:55:32 +01:00
/**
* @internal
*/
2020-05-18 21:13:27 +02:00
class YieldAnalyzer
{
public static function analyze(
StatementsAnalyzer $statements_analyzer,
PhpParser\Node\Expr\Yield_ $stmt,
Context $context
): bool {
2020-05-18 21:13:27 +02:00
$doc_comment = $stmt->getDocComment();
$var_comments = [];
$var_comment_type = null;
$codebase = $statements_analyzer->getCodebase();
if ($doc_comment) {
try {
$var_comments = CommentAnalyzer::getTypeFromComment(
$doc_comment,
$statements_analyzer,
2022-12-18 17:15:15 +01:00
$statements_analyzer->getAliases(),
2020-05-18 21:13:27 +02:00
);
} catch (DocblockParseException $e) {
IssueBuffer::maybeAdd(
2020-05-18 21:13:27 +02:00
new InvalidDocblock(
2020-12-02 00:26:15 +01:00
$e->getMessage(),
2022-12-18 17:15:15 +01:00
new CodeLocation($statements_analyzer->getSource(), $stmt),
),
);
2020-05-18 21:13:27 +02:00
}
foreach ($var_comments as $var_comment) {
if (!$var_comment->type) {
continue;
}
2021-12-03 20:11:20 +01:00
$comment_type = TypeExpander::expandUnion(
2020-05-18 21:13:27 +02:00
$codebase,
$var_comment->type,
$context->self,
2021-12-13 04:45:57 +01:00
$context->self ? new TNamedObject($context->self) : null,
2022-12-18 17:15:15 +01:00
$statements_analyzer->getParentFQCLN(),
2020-05-18 21:13:27 +02:00
);
$type_location = null;
if ($var_comment->type_start
&& $var_comment->type_end
&& $var_comment->line_number
) {
$type_location = new DocblockTypeLocation(
2020-05-18 21:13:27 +02:00
$statements_analyzer,
$var_comment->type_start,
$var_comment->type_end,
2022-12-18 17:15:15 +01:00
$var_comment->line_number,
2020-05-18 21:13:27 +02:00
);
}
if (!$var_comment->var_id) {
$var_comment_type = $comment_type;
continue;
}
if ($codebase->find_unused_variables
&& $type_location
&& isset($context->vars_in_scope[$var_comment->var_id])
&& $context->vars_in_scope[$var_comment->var_id]->getId() === $comment_type->getId()
) {
$project_analyzer = $statements_analyzer->getProjectAnalyzer();
if ($codebase->alter_code
&& isset($project_analyzer->getIssuesToFix()['UnnecessaryVarAnnotation'])
) {
FileManipulationBuffer::addVarAnnotationToRemove($type_location);
2022-12-17 05:00:34 +01:00
} else {
IssueBuffer::maybeAdd(
new UnnecessaryVarAnnotation(
'The @var annotation for ' . $var_comment->var_id . ' is unnecessary',
2022-12-18 17:15:15 +01:00
$type_location,
2022-12-17 05:00:34 +01:00
),
$statements_analyzer->getSuppressedIssues(),
2022-12-18 17:15:15 +01:00
true,
2022-12-17 05:00:34 +01:00
);
2020-05-18 21:13:27 +02:00
}
}
if (isset($context->vars_in_scope[$var_comment->var_id])) {
$comment_type = $comment_type->setParentNodes(
2022-12-18 17:15:15 +01:00
$context->vars_in_scope[$var_comment->var_id]->parent_nodes,
);
}
2020-05-18 21:13:27 +02:00
$context->vars_in_scope[$var_comment->var_id] = $comment_type;
}
}
if ($stmt->key) {
$context->inside_call = true;
if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->key, $context) === false) {
return false;
}
$context->inside_call = false;
}
if ($stmt->value) {
$context->inside_call = true;
if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->value, $context) === false) {
return false;
}
$context->inside_call = false;
if ($var_comment_type) {
$expression_type = $var_comment_type;
2020-05-18 21:13:27 +02:00
} elseif ($stmt_var_type = $statements_analyzer->node_data->getType($stmt->value)) {
$expression_type = $stmt_var_type;
2020-05-18 21:13:27 +02:00
} else {
$expression_type = Type::getMixed();
}
} else {
2021-10-13 19:37:47 +02:00
$expression_type = Type::getNever();
2020-05-18 21:13:27 +02:00
}
2020-05-20 15:12:24 +02:00
$yield_type = null;
2020-05-18 21:13:27 +02:00
foreach ($expression_type->getAtomicTypes() as $expression_atomic_type) {
2022-10-13 13:42:35 +02:00
if (!$expression_atomic_type instanceof TNamedObject) {
continue;
}
if (!$codebase->classlikes->classOrInterfaceExists($expression_atomic_type->value)) {
continue;
}
$classlike_storage = $codebase->classlike_storage_provider->get($expression_atomic_type->value);
2022-10-13 13:42:35 +02:00
if (!$classlike_storage->yield) {
continue;
}
$declaring_classlike_storage = $classlike_storage->declaring_yield_fqcn
? $codebase->classlike_storage_provider->get($classlike_storage->declaring_yield_fqcn)
: $classlike_storage;
$yield_candidate_type = $classlike_storage->yield;
2022-10-13 13:42:35 +02:00
$yield_candidate_type = !$yield_candidate_type->isMixed()
? TypeExpander::expandUnion(
$codebase,
$yield_candidate_type,
$expression_atomic_type->value,
$expression_atomic_type->value,
null,
true,
false,
)
: $yield_candidate_type;
$class_template_params = ClassTemplateParamCollector::collect(
$codebase,
$declaring_classlike_storage,
$classlike_storage,
null,
2022-10-13 13:53:03 +02:00
$expression_atomic_type,
2022-12-18 17:15:15 +01:00
true,
2022-10-13 13:42:35 +02:00
);
if ($class_template_params) {
if (!$expression_atomic_type instanceof TGenericObject) {
$type_params = [];
foreach ($class_template_params as $type_map) {
$type_params[] = array_values($type_map)[0];
2020-05-18 21:13:27 +02:00
}
2022-10-13 13:42:35 +02:00
$expression_atomic_type = new TGenericObject($expression_atomic_type->value, $type_params);
2020-05-18 21:13:27 +02:00
}
2022-10-13 13:42:35 +02:00
$yield_candidate_type = AtomicPropertyFetchAnalyzer::localizePropertyType(
$codebase,
$yield_candidate_type,
$expression_atomic_type,
$classlike_storage,
2022-12-18 17:15:15 +01:00
$declaring_classlike_storage,
2022-10-13 13:42:35 +02:00
);
2020-05-18 21:13:27 +02:00
}
2022-10-13 13:42:35 +02:00
$yield_type = Type::combineUnionTypes(
$yield_type,
$yield_candidate_type,
2022-12-18 17:15:15 +01:00
$codebase,
2022-10-13 13:42:35 +02:00
);
2020-05-18 21:13:27 +02:00
}
2020-05-20 15:12:24 +02:00
if ($yield_type) {
2022-10-03 10:45:36 +02:00
$expression_type = $expression_type->getBuilder()->substitute($expression_type, $yield_type)->freeze();
2020-05-20 15:12:24 +02:00
}
2020-05-18 21:13:27 +02:00
$statements_analyzer->node_data->setType($stmt, $expression_type);
$source = $statements_analyzer->getSource();
if ($source instanceof FunctionLikeAnalyzer
&& !($source->getSource() instanceof TraitAnalyzer)
) {
$source->examineParamTypes($statements_analyzer, $context, $codebase, $stmt);
$storage = $source->getFunctionLikeStorage($statements_analyzer);
if ($storage->return_type && !$yield_type) {
2020-05-18 21:13:27 +02:00
foreach ($storage->return_type->getAtomicTypes() as $atomic_return_type) {
2021-12-13 04:45:57 +01:00
if ($atomic_return_type instanceof TNamedObject
2020-05-18 21:13:27 +02:00
&& $atomic_return_type->value === 'Generator'
) {
2021-12-13 04:45:57 +01:00
if ($atomic_return_type instanceof TGenericObject) {
2020-05-18 21:13:27 +02:00
if (!$atomic_return_type->type_params[2]->isVoid()) {
$statements_analyzer->node_data->setType(
$stmt,
2022-12-18 17:15:15 +01:00
$atomic_return_type->type_params[2],
2020-05-18 21:13:27 +02:00
);
}
} else {
$statements_analyzer->node_data->setType(
$stmt,
Type::combineUnionTypes(
Type::getMixed(),
2022-12-18 17:15:15 +01:00
$expression_type,
),
2020-05-18 21:13:27 +02:00
);
}
}
}
}
}
return true;
}
}