diff --git a/composer.json b/composer.json index f3d319883c9..95943a9d28c 100644 --- a/composer.json +++ b/composer.json @@ -33,15 +33,12 @@ "felixfbecker/language-server-protocol": "^1.5.2", "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.16", + "nikic/php-parser": "^5.0.0", "sebastian/diff": "^4.0 || ^5.0", "spatie/array-to-xml": "^2.17.0 || ^3.0", "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" }, - "conflict": { - "nikic/php-parser": "4.17.0" - }, "provide": { "psalm/psalm": "self.version" }, diff --git a/examples/plugins/SafeArrayKeyChecker.php b/examples/plugins/SafeArrayKeyChecker.php index 0360ed79155..fafcc2727da 100644 --- a/examples/plugins/SafeArrayKeyChecker.php +++ b/examples/plugins/SafeArrayKeyChecker.php @@ -2,7 +2,7 @@ namespace Psalm\Example\Plugin; -use PhpParser\Node\Expr\ArrayItem; +use PhpParser\Node\ArrayItem; use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent; use Psalm\Plugin\EventHandler\RemoveTaintsInterface; diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 3ef4c082be2..67e332207d3 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,10 +1,5 @@ - - - - - tags['variablesfrom'][0]]]> @@ -2351,9 +2346,4 @@ $return_type - - - - - diff --git a/psalm.xml.dist b/psalm.xml.dist index 5e8e8ac33d0..744af215afb 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -144,10 +144,31 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Psalm/CodeLocation/ParseErrorLocation.php b/src/Psalm/CodeLocation/ParseErrorLocation.php index d20d96eb8e1..911371c284b 100644 --- a/src/Psalm/CodeLocation/ParseErrorLocation.php +++ b/src/Psalm/CodeLocation/ParseErrorLocation.php @@ -19,9 +19,9 @@ public function __construct( string $file_path, string $file_name, ) { - /** @psalm-suppress PossiblyUndefinedStringArrayOffset, ImpureMethodCall */ + /** @psalm-suppress PossiblyUndefinedStringArrayOffset */ $this->file_start = (int)$error->getAttributes()['startFilePos']; - /** @psalm-suppress PossiblyUndefinedStringArrayOffset, ImpureMethodCall */ + /** @psalm-suppress PossiblyUndefinedStringArrayOffset */ $this->file_end = (int)$error->getAttributes()['endFilePos']; $this->raw_file_start = $this->file_start; $this->raw_file_end = $this->file_end; diff --git a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index 969efe0d2e0..d2da1eb3d7e 100644 --- a/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -1195,7 +1195,7 @@ static function (FunctionLikeParameter $param): PhpParser\Node\Arg { $fake_stmt = new VirtualClassMethod( new VirtualIdentifier('__construct'), [ - 'flags' => PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC, + 'flags' => PhpParser\Modifiers::PUBLIC, 'params' => $fake_constructor_params, 'stmts' => $fake_constructor_stmts, ], diff --git a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php index 21e9d22d1c4..9d6a0bf62d2 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php +++ b/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php @@ -54,7 +54,7 @@ public static function getReturnTypes( $yield_types = array_merge($yield_types, self::getYieldTypeFromExpression($stmt->expr, $nodes)); } elseif ($stmt->expr instanceof PhpParser\Node\Scalar\String_) { $return_types[] = Type::getString(); - } elseif ($stmt->expr instanceof PhpParser\Node\Scalar\LNumber) { + } elseif ($stmt->expr instanceof PhpParser\Node\Scalar\Int_) { $return_types[] = Type::getInt(); } elseif ($stmt->expr instanceof PhpParser\Node\Expr\ConstFetch) { if ((string)$stmt->expr->name === 'true') { @@ -77,14 +77,9 @@ public static function getReturnTypes( break; } - if ($stmt instanceof PhpParser\Node\Stmt\Throw_) { - $return_types[] = Type::getNever(); - - break; - } - if ($stmt instanceof PhpParser\Node\Stmt\Expression) { - if ($stmt->expr instanceof PhpParser\Node\Expr\Exit_) { + if ($stmt->expr instanceof PhpParser\Node\Expr\Exit_ + || $stmt->expr instanceof PhpParser\Node\Expr\Throw_) { $return_types[] = Type::getNever(); break; diff --git a/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php b/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php index 40c85901cba..3187bd05e29 100644 --- a/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php @@ -1186,8 +1186,7 @@ public function setPhpVersion(string $version, string $source): void $analysis_php_version_id = $php_major_version * 10_000 + $php_minor_version * 100; if ($this->codebase->analysis_php_version_id !== $analysis_php_version_id) { - // reset lexer and parser when php version changes - StatementsProvider::clearLexer(); + // reset parser when php version changes StatementsProvider::clearParser(); } diff --git a/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php b/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php index bca1b1aa895..25cca24d9ec 100644 --- a/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php @@ -50,8 +50,9 @@ public static function getControlActions( foreach ($stmts as $stmt) { if ($stmt instanceof PhpParser\Node\Stmt\Return_ || - $stmt instanceof PhpParser\Node\Stmt\Throw_ || - ($stmt instanceof PhpParser\Node\Stmt\Expression && $stmt->expr instanceof PhpParser\Node\Expr\Exit_) + ($stmt instanceof PhpParser\Node\Stmt\Expression + && ($stmt->expr instanceof PhpParser\Node\Expr\Exit_ + || $stmt->expr instanceof PhpParser\Node\Expr\Throw_)) ) { if (!$return_is_exit && $stmt instanceof PhpParser\Node\Stmt\Return_) { $stmt_return_type = null; @@ -85,7 +86,7 @@ public static function getControlActions( if ($stmt instanceof PhpParser\Node\Stmt\Continue_) { $count = !$stmt->num ? 1 - : ($stmt->num instanceof PhpParser\Node\Scalar\LNumber ? $stmt->num->value : null); + : ($stmt->num instanceof PhpParser\Node\Scalar\Int_ ? $stmt->num->value : null); if ($break_types && $count !== null && count($break_types) >= $count) { /** @psalm-suppress InvalidArrayOffset Some int-range improvements are needed */ @@ -102,7 +103,7 @@ public static function getControlActions( if ($stmt instanceof PhpParser\Node\Stmt\Break_) { $count = !$stmt->num ? 1 - : ($stmt->num instanceof PhpParser\Node\Scalar\LNumber ? $stmt->num->value : null); + : ($stmt->num instanceof PhpParser\Node\Scalar\Int_ ? $stmt->num->value : null); if ($break_types && $count !== null && count($break_types) >= $count) { /** @psalm-suppress InvalidArrayOffset Some int-range improvements are needed */ @@ -408,9 +409,9 @@ public static function onlyThrowsOrExits(NodeTypeProvider $type_provider, array for ($i = count($stmts) - 1; $i >= 0; --$i) { $stmt = $stmts[$i]; - if ($stmt instanceof PhpParser\Node\Stmt\Throw_ - || ($stmt instanceof PhpParser\Node\Stmt\Expression - && $stmt->expr instanceof PhpParser\Node\Expr\Exit_) + if ($stmt instanceof PhpParser\Node\Stmt\Expression + && ($stmt->expr instanceof PhpParser\Node\Expr\Exit_ + || $stmt->expr instanceof PhpParser\Node\Expr\Throw_) ) { return true; } @@ -438,7 +439,7 @@ public static function onlyThrows(array $stmts): bool } foreach ($stmts as $stmt) { - if ($stmt instanceof PhpParser\Node\Stmt\Throw_) { + if ($stmt instanceof PhpParser\Node\Stmt\Expression && $stmt->expr instanceof PhpParser\Node\Expr\Throw_) { return true; } } diff --git a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php index a5a9f51e542..33fa2b39dff 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php @@ -26,15 +26,15 @@ use Psalm\Node\Expr\BinaryOp\VirtualEqual; use Psalm\Node\Expr\BinaryOp\VirtualIdentical; use Psalm\Node\Expr\VirtualArray; -use Psalm\Node\Expr\VirtualArrayItem; use Psalm\Node\Expr\VirtualBooleanNot; use Psalm\Node\Expr\VirtualConstFetch; use Psalm\Node\Expr\VirtualFuncCall; use Psalm\Node\Expr\VirtualVariable; use Psalm\Node\Name\VirtualFullyQualified; -use Psalm\Node\Scalar\VirtualLNumber; +use Psalm\Node\Scalar\VirtualInt; use Psalm\Node\Stmt\VirtualIf; use Psalm\Node\VirtualArg; +use Psalm\Node\VirtualArrayItem; use Psalm\Node\VirtualName; use Psalm\Type; use Psalm\Type\Atomic\TDependentGetClass; @@ -249,8 +249,8 @@ public static function analyze( $case_equality_expr = new VirtualFuncCall( new VirtualFullyQualified(['rand']), [ - new VirtualArg(new VirtualLNumber(0)), - new VirtualArg(new VirtualLNumber(1)), + new VirtualArg(new VirtualInt(0)), + new VirtualArg(new VirtualInt(1)), ], $case->getAttributes(), ); @@ -294,8 +294,8 @@ public static function analyze( $case_or_default_equality_expr = new VirtualFuncCall( new VirtualFullyQualified(['rand']), [ - new VirtualArg(new VirtualLNumber(0)), - new VirtualArg(new VirtualLNumber(1)), + new VirtualArg(new VirtualInt(0)), + new VirtualArg(new VirtualInt(1)), ], $case->getAttributes(), ); @@ -690,8 +690,8 @@ private static function simplifyCaseEqualityExpression( } /** - * @param array $in_array_values - * @return ?array + * @param array $in_array_values + * @return ?array */ private static function getOptionsFromNestedOr( PhpParser\Node\Expr $case_equality_expr, diff --git a/src/Psalm/Internal/Analyzer/Statements/BreakAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/BreakAnalyzer.php index 330918a9559..52eede08d3a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/BreakAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/BreakAnalyzer.php @@ -29,7 +29,7 @@ public static function analyze( if ($loop_scope) { if ($context->break_types && end($context->break_types) === 'switch' - && (!$stmt->num instanceof PhpParser\Node\Scalar\LNumber || $stmt->num->value < 2) + && (!$stmt->num instanceof PhpParser\Node\Scalar\Int_ || $stmt->num->value < 2) ) { $loop_scope->final_actions[] = ScopeAnalyzer::ACTION_LEAVE_SWITCH; } else { diff --git a/src/Psalm/Internal/Analyzer/Statements/ContinueAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ContinueAnalyzer.php index b5b0de71f9f..a41a1bca392 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ContinueAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ContinueAnalyzer.php @@ -25,7 +25,7 @@ public static function analyze( PhpParser\Node\Stmt\Continue_ $stmt, Context $context, ): void { - $count = $stmt->num instanceof PhpParser\Node\Scalar\LNumber? $stmt->num->value : 1; + $count = $stmt->num instanceof PhpParser\Node\Scalar\Int_? $stmt->num->value : 1; $loop_scope = $context->loop_scope; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php index 8c6dd5d7ccd..05543abbfad 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php @@ -242,7 +242,7 @@ private static function analyzeArrayItem( StatementsAnalyzer $statements_analyzer, Context $context, ArrayCreationInfo $array_creation_info, - PhpParser\Node\Expr\ArrayItem $item, + PhpParser\Node\ArrayItem $item, Codebase $codebase, ): void { if ($item->unpack) { @@ -519,7 +519,7 @@ private static function analyzeArrayItem( private static function handleUnpackedArray( StatementsAnalyzer $statements_analyzer, ArrayCreationInfo $array_creation_info, - PhpParser\Node\Expr\ArrayItem $item, + PhpParser\Node\ArrayItem $item, Union $unpacked_array_type, Codebase $codebase, ): void { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php index ea8840e4689..cad1f07833b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php @@ -16,7 +16,7 @@ use PhpParser\Node\Expr\BinaryOp\SmallerOrEqual; use PhpParser\Node\Expr\UnaryMinus; use PhpParser\Node\Expr\UnaryPlus; -use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\Int_; use Psalm\CodeLocation; use Psalm\Codebase; use Psalm\FileSource; @@ -1575,7 +1575,7 @@ private static function hasNonEmptyCountEqualityCheck( } // TODO get node type provider here somehow and check literal ints and int ranges - if ($compare_to instanceof PhpParser\Node\Scalar\LNumber + if ($compare_to instanceof PhpParser\Node\Scalar\Int_ && $compare_to->value > (-1 * $comparison_adjustment) ) { $min_count = $compare_to->value + $comparison_adjustment; @@ -1605,7 +1605,7 @@ private static function hasLessThanCountEqualityCheck( if ($left_count && $operator_less_than_or_equal - && $conditional->right instanceof PhpParser\Node\Scalar\LNumber + && $conditional->right instanceof PhpParser\Node\Scalar\Int_ ) { $max_count = $conditional->right->value - ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller ? 1 : 0); @@ -1624,7 +1624,7 @@ private static function hasLessThanCountEqualityCheck( if ($right_count && $operator_greater_than_or_equal - && $conditional->left instanceof PhpParser\Node\Scalar\LNumber + && $conditional->left instanceof PhpParser\Node\Scalar\Int_ ) { $max_count = $conditional->left->value - ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater ? 1 : 0); @@ -1648,7 +1648,7 @@ private static function hasCountEqualityCheck( && in_array(strtolower($conditional->left->name->getFirst()), ['count', 'sizeof']) && $conditional->left->getArgs(); - if ($left_count && $conditional->right instanceof PhpParser\Node\Scalar\LNumber) { + if ($left_count && $conditional->right instanceof PhpParser\Node\Scalar\Int_) { $count = $conditional->right->value; return self::ASSIGNMENT_TO_RIGHT; @@ -1659,7 +1659,7 @@ private static function hasCountEqualityCheck( && in_array(strtolower($conditional->right->name->getFirst()), ['count', 'sizeof']) && $conditional->right->getArgs(); - if ($right_count && $conditional->left instanceof PhpParser\Node\Scalar\LNumber) { + if ($right_count && $conditional->left instanceof PhpParser\Node\Scalar\Int_) { $count = $conditional->left->value; return self::ASSIGNMENT_TO_LEFT; @@ -1685,13 +1685,13 @@ private static function hasSuperiorNumberCheck( ) { $right_assignment = true; $value_right = $type->getSingleIntLiteral()->value; - } elseif ($conditional->right instanceof LNumber) { + } elseif ($conditional->right instanceof Int_) { $right_assignment = true; $value_right = $conditional->right->value; - } elseif ($conditional->right instanceof UnaryMinus && $conditional->right->expr instanceof LNumber) { + } elseif ($conditional->right instanceof UnaryMinus && $conditional->right->expr instanceof Int_) { $right_assignment = true; $value_right = -$conditional->right->expr->value; - } elseif ($conditional->right instanceof UnaryPlus && $conditional->right->expr instanceof LNumber) { + } elseif ($conditional->right instanceof UnaryPlus && $conditional->right->expr instanceof Int_) { $right_assignment = true; $value_right = $conditional->right->expr->value; } @@ -1709,13 +1709,13 @@ private static function hasSuperiorNumberCheck( ) { $left_assignment = true; $value_left = $type->getSingleIntLiteral()->value; - } elseif ($conditional->left instanceof LNumber) { + } elseif ($conditional->left instanceof Int_) { $left_assignment = true; $value_left = $conditional->left->value; - } elseif ($conditional->left instanceof UnaryMinus && $conditional->left->expr instanceof LNumber) { + } elseif ($conditional->left instanceof UnaryMinus && $conditional->left->expr instanceof Int_) { $left_assignment = true; $value_left = -$conditional->left->expr->value; - } elseif ($conditional->left instanceof UnaryPlus && $conditional->left->expr instanceof LNumber) { + } elseif ($conditional->left instanceof UnaryPlus && $conditional->left->expr instanceof Int_) { $left_assignment = true; $value_left = $conditional->left->expr->value; } @@ -1745,13 +1745,13 @@ private static function hasInferiorNumberCheck( ) { $right_assignment = true; $value_right = $type->getSingleIntLiteral()->value; - } elseif ($conditional->right instanceof LNumber) { + } elseif ($conditional->right instanceof Int_) { $right_assignment = true; $value_right = $conditional->right->value; - } elseif ($conditional->right instanceof UnaryMinus && $conditional->right->expr instanceof LNumber) { + } elseif ($conditional->right instanceof UnaryMinus && $conditional->right->expr instanceof Int_) { $right_assignment = true; $value_right = -$conditional->right->expr->value; - } elseif ($conditional->right instanceof UnaryPlus && $conditional->right->expr instanceof LNumber) { + } elseif ($conditional->right instanceof UnaryPlus && $conditional->right->expr instanceof Int_) { $right_assignment = true; $value_right = $conditional->right->expr->value; } @@ -1769,13 +1769,13 @@ private static function hasInferiorNumberCheck( ) { $left_assignment = true; $value_left = $type->getSingleIntLiteral()->value; - } elseif ($conditional->left instanceof LNumber) { + } elseif ($conditional->left instanceof Int_) { $left_assignment = true; $value_left = $conditional->left->value; - } elseif ($conditional->left instanceof UnaryMinus && $conditional->left->expr instanceof LNumber) { + } elseif ($conditional->left instanceof UnaryMinus && $conditional->left->expr instanceof Int_) { $left_assignment = true; $value_left = -$conditional->left->expr->value; - } elseif ($conditional->left instanceof UnaryPlus && $conditional->left->expr instanceof LNumber) { + } elseif ($conditional->left instanceof UnaryPlus && $conditional->left->expr instanceof Int_) { $left_assignment = true; $value_left = $conditional->left->expr->value; } @@ -1799,7 +1799,7 @@ private static function hasReconcilableNonEmptyCountEqualityCheck( && $conditional->left->name instanceof PhpParser\Node\Name && in_array(strtolower($conditional->left->name->getFirst()), ['count', 'sizeof']); - $right_number = $conditional->right instanceof PhpParser\Node\Scalar\LNumber + $right_number = $conditional->right instanceof PhpParser\Node\Scalar\Int_ && $conditional->right->value === ( $conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater ? 0 : 1); @@ -3775,7 +3775,7 @@ private static function getArrayKeyExistsAssertions( if ($first_arg->value instanceof PhpParser\Node\Scalar\String_) { $first_var_name = '\'' . $first_arg->value->value . '\''; - } elseif ($first_arg->value instanceof PhpParser\Node\Scalar\LNumber) { + } elseif ($first_arg->value instanceof PhpParser\Node\Scalar\Int_) { $first_var_name = (string)$first_arg->value->value; } } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php index 4f49634c593..91d28b02088 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php @@ -174,7 +174,7 @@ public static function updateArrayType( if ($value_type instanceof TLiteralString) { $key_values[] = $value_type; } - } elseif ($current_dim instanceof PhpParser\Node\Scalar\LNumber && !$root_is_string) { + } elseif ($current_dim instanceof PhpParser\Node\Scalar\Int_ && !$root_is_string) { $key_values[] = new TLiteralInt($current_dim->value); } elseif ($current_dim && ($key_type = $statements_analyzer->node_data->getType($current_dim)) @@ -1027,7 +1027,7 @@ private static function getDimKeyValues( if ($value_type instanceof TLiteralString) { $key_values[] = $value_type; } - } elseif ($dim instanceof PhpParser\Node\Scalar\LNumber) { + } elseif ($dim instanceof PhpParser\Node\Scalar\Int_) { $key_values[] = new TLiteralInt($dim->value); } else { $key_type = $statements_analyzer->node_data->getType($dim); @@ -1084,12 +1084,12 @@ private static function getArrayAssignmentOffsetType( return [$offset_type, $var_id_addition, true]; } - if ($child_stmt->dim instanceof PhpParser\Node\Scalar\LNumber + if ($child_stmt->dim instanceof PhpParser\Node\Scalar\Int_ || (($child_stmt->dim instanceof PhpParser\Node\Expr\ConstFetch || $child_stmt->dim instanceof PhpParser\Node\Expr\ClassConstFetch) && $child_stmt_dim_type->isSingleIntLiteral()) ) { - if ($child_stmt->dim instanceof PhpParser\Node\Scalar\LNumber) { + if ($child_stmt->dim instanceof PhpParser\Node\Scalar\Int_) { $offset_type = new TLiteralInt($child_stmt->dim->value); } else { $offset_type = $child_stmt_dim_type->getSingleIntLiteral(); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php index c11731e2758..e96263fd83a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php @@ -7,7 +7,7 @@ use PhpParser; use PhpParser\Node\Expr; use PhpParser\Node\Expr\PropertyFetch; -use PhpParser\Node\Stmt\PropertyProperty; +use PhpParser\Node\PropertyItem; use Psalm\CodeLocation; use Psalm\Codebase; use Psalm\Config; @@ -92,8 +92,8 @@ final class InstancePropertyAssignmentAnalyzer { /** - * @param PropertyFetch|PropertyProperty $stmt - * @param bool $direct_assignment whether the variable is assigned explicitly + * @param PropertyFetch|PropertyItem $stmt + * @param bool $direct_assignment whether the variable is assigned explicitly */ public static function analyze( StatementsAnalyzer $statements_analyzer, @@ -106,7 +106,7 @@ public static function analyze( ): void { $codebase = $statements_analyzer->getCodebase(); - if ($stmt instanceof PropertyProperty) { + if ($stmt instanceof PropertyItem) { if (!$context->self || !$stmt->default) { return; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php index 2e28081e8e5..414a8773b5e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php @@ -899,8 +899,9 @@ public static function analyzeAssignmentRef( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr\AssignRef $stmt, Context $context, + ?PhpParser\Node\Stmt $from_stmt, ): bool { - ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context, false, null, false, null, true); + ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context, false, null, null, null, true); $lhs_var_id = ExpressionIdentifier::getExtendedVarId( $stmt->var, @@ -914,7 +915,7 @@ public static function analyzeAssignmentRef( $statements_analyzer, ); - $doc_comment = $stmt->getDocComment(); + $doc_comment = $stmt->getDocComment() ?? $from_stmt?->getDocComment(); if ($doc_comment) { try { $var_comments = CommentAnalyzer::getTypeFromComment( @@ -1412,7 +1413,7 @@ private static function analyzeDestructuringAssignment( $can_be_empty = !$assign_value_atomic_type instanceof TNonEmptyArray; } elseif ($assign_value_atomic_type instanceof TKeyedArray) { if (($assign_var_item->key instanceof PhpParser\Node\Scalar\String_ - || $assign_var_item->key instanceof PhpParser\Node\Scalar\LNumber) + || $assign_var_item->key instanceof PhpParser\Node\Scalar\Int_) && isset($assign_value_atomic_type->properties[$assign_var_item->key->value]) ) { $new_assign_type = diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php index a563b7495f2..7151f232b15 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php @@ -237,7 +237,7 @@ public static function analyze( $context, false, null, - false, + null, $high_order_template_result, ) === false) { $context->inside_call = $was_inside_call; @@ -1171,7 +1171,7 @@ private static function evaluateArbitraryParam( || $arg->value instanceof PhpParser\Node\Expr\Array_ || $arg->value instanceof PhpParser\Node\Expr\BinaryOp || $arg->value instanceof PhpParser\Node\Expr\Ternary - || $arg->value instanceof PhpParser\Node\Scalar\Encapsed + || $arg->value instanceof PhpParser\Node\Scalar\InterpolatedString || $arg->value instanceof PhpParser\Node\Expr\PostInc || $arg->value instanceof PhpParser\Node\Expr\PostDec || $arg->value instanceof PhpParser\Node\Expr\PreInc diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php index 14026d3d1ab..a5048bd2f3b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php @@ -18,9 +18,9 @@ use Psalm\Internal\Type\TemplateResult; use Psalm\Internal\Type\TypeExpander; use Psalm\Node\Expr\VirtualArray; -use Psalm\Node\Expr\VirtualArrayItem; use Psalm\Node\Scalar\VirtualString; use Psalm\Node\VirtualArg; +use Psalm\Node\VirtualArrayItem; use Psalm\Storage\ClassLikeStorage; use Psalm\Storage\MethodStorage; use Psalm\Type; @@ -203,7 +203,7 @@ public static function handleMagicMethod( $result->existent_method_ids[$method_id->__toString()] = true; $array_values = array_map( - static fn(PhpParser\Node\Arg $arg): PhpParser\Node\Expr\ArrayItem => new VirtualArrayItem( + static fn(PhpParser\Node\Arg $arg): PhpParser\Node\ArrayItem => new VirtualArrayItem( $arg->value, null, false, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php index 80c780de50b..f29b75fbb5a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php @@ -25,9 +25,9 @@ use Psalm\Issue\RedundantFunctionCallGivenDocblockType; use Psalm\IssueBuffer; use Psalm\Node\Expr\VirtualArray; -use Psalm\Node\Expr\VirtualArrayItem; use Psalm\Node\Expr\VirtualVariable; use Psalm\Node\Scalar\VirtualString; +use Psalm\Node\VirtualArrayItem; use Psalm\Type; use Psalm\Type\Atomic\TBool; use Psalm\Type\Atomic\TClassString; diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php index 54b93dccbde..85f4d465fa8 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php @@ -34,11 +34,11 @@ use Psalm\Issue\UndefinedClass; use Psalm\IssueBuffer; use Psalm\Node\Expr\VirtualArray; -use Psalm\Node\Expr\VirtualArrayItem; use Psalm\Node\Expr\VirtualMethodCall; use Psalm\Node\Expr\VirtualVariable; use Psalm\Node\Scalar\VirtualString; use Psalm\Node\VirtualArg; +use Psalm\Node\VirtualArrayItem; use Psalm\Storage\ClassLikeStorage; use Psalm\Storage\MethodStorage; use Psalm\Type; @@ -670,7 +670,7 @@ private static function handleNamedCall( } $array_values = array_map( - static fn(PhpParser\Node\Arg $arg): PhpParser\Node\Expr\ArrayItem => new VirtualArrayItem( + static fn(PhpParser\Node\Arg $arg): PhpParser\Node\ArrayItem => new VirtualArrayItem( $arg->value, null, false, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php index 5ee02bef358..88ef13bc054 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php @@ -5,7 +5,8 @@ namespace Psalm\Internal\Analyzer\Statements\Expression; use PhpParser; -use PhpParser\Node\Scalar\EncapsedStringPart; +use PhpParser\Node\Expr; +use PhpParser\Node\InterpolatedStringPart; use Psalm\CodeLocation; use Psalm\Context; use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer; @@ -32,7 +33,7 @@ final class EncapsulatedStringAnalyzer { public static function analyze( StatementsAnalyzer $statements_analyzer, - PhpParser\Node\Scalar\Encapsed $stmt, + PhpParser\Node\Scalar\InterpolatedString $stmt, Context $context, ): bool { $parent_nodes = []; @@ -44,11 +45,13 @@ public static function analyze( $literal_string = ""; foreach ($stmt->parts as $part) { - if (ExpressionAnalyzer::analyze($statements_analyzer, $part, $context) === false) { - return false; + if ($part instanceof Expr) { + if (ExpressionAnalyzer::analyze($statements_analyzer, $part, $context) === false) { + return false; + } } - if ($part instanceof EncapsedStringPart) { + if ($part instanceof InterpolatedStringPart) { if ($literal_string !== null) { $literal_string .= $part->value; } diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php index 9cf2a92f518..660ad0ff087 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php @@ -116,7 +116,7 @@ public static function getExtendedVarId( if ($root_var_id) { if ($stmt->dim instanceof PhpParser\Node\Scalar\String_ - || $stmt->dim instanceof PhpParser\Node\Scalar\LNumber + || $stmt->dim instanceof PhpParser\Node\Scalar\Int_ ) { $offset = $stmt->dim instanceof PhpParser\Node\Scalar\String_ ? '\'' . $stmt->dim->value . '\'' diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php index 70bf0c77412..b6a3ec6d85e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php @@ -501,7 +501,7 @@ public static function getArrayAccessTypeGivenOffset( if ($value_type instanceof TLiteralString) { $key_values[] = $value_type; } - } elseif ($stmt->dim instanceof PhpParser\Node\Scalar\LNumber) { + } elseif ($stmt->dim instanceof PhpParser\Node\Scalar\Int_) { $key_values[] = new TLiteralInt($stmt->dim->value); } elseif ($stmt->dim && ($stmt_dim_type = $statements_analyzer->node_data->getType($stmt->dim))) { $string_literals = $stmt_dim_type->getLiteralStrings(); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php index 08abb627c4c..61728389918 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php @@ -16,7 +16,7 @@ use Psalm\Node\Expr\BinaryOp\VirtualMinus; use Psalm\Node\Expr\BinaryOp\VirtualPlus; use Psalm\Node\Expr\VirtualAssign; -use Psalm\Node\Scalar\VirtualLNumber; +use Psalm\Node\Scalar\VirtualInt; use Psalm\Type; /** @@ -55,7 +55,7 @@ public static function analyze( ) { $return_type = null; - $fake_right_expr = new VirtualLNumber(1, $stmt->getAttributes()); + $fake_right_expr = new VirtualInt(1, $stmt->getAttributes()); $statements_analyzer->node_data->setType($fake_right_expr, Type::getInt()); ArithmeticOpAnalyzer::analyze( @@ -100,7 +100,7 @@ public static function analyze( ); } } else { - $fake_right_expr = new VirtualLNumber(1, $stmt->getAttributes()); + $fake_right_expr = new VirtualInt(1, $stmt->getAttributes()); $operation = $stmt instanceof PostInc || $stmt instanceof PreInc ? new VirtualPlus( diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php index f79479cc530..ce884452f53 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php @@ -332,7 +332,7 @@ public static function getPathTo( $dir_level = 1; if (isset($stmt->getArgs()[1])) { - if ($stmt->getArgs()[1]->value instanceof PhpParser\Node\Scalar\LNumber) { + if ($stmt->getArgs()[1]->value instanceof PhpParser\Node\Scalar\Int_) { $dir_level = $stmt->getArgs()[1]->value->value; } else { if ($statements_analyzer) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php index 38ab1befc28..3d2e242748a 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php @@ -15,7 +15,6 @@ use Psalm\IssueBuffer; use Psalm\Node\Expr\BinaryOp\VirtualIdentical; use Psalm\Node\Expr\VirtualArray; -use Psalm\Node\Expr\VirtualArrayItem; use Psalm\Node\Expr\VirtualConstFetch; use Psalm\Node\Expr\VirtualFuncCall; use Psalm\Node\Expr\VirtualNew; @@ -24,6 +23,7 @@ use Psalm\Node\Expr\VirtualVariable; use Psalm\Node\Name\VirtualFullyQualified; use Psalm\Node\VirtualArg; +use Psalm\Node\VirtualArrayItem; use Psalm\Type; use Psalm\Type\Atomic; use Psalm\Type\Atomic\TEnumCase; @@ -318,6 +318,7 @@ public static function analyze( /** * @param non-empty-list $conds + * @param array $attributes */ private static function convertCondsToConditional( array $conds, @@ -333,7 +334,7 @@ private static function convertCondsToConditional( } $array_items = array_map( - static fn(PhpParser\Node\Expr $cond): PhpParser\Node\Expr\ArrayItem => + static fn(PhpParser\Node\Expr $cond): PhpParser\Node\ArrayItem => new VirtualArrayItem($cond, null, false, $cond->getAttributes()), $conds, ); diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php index cba0730a470..93b6d68ea48 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php @@ -400,11 +400,11 @@ public static function infer( return Type::getString($stmt->value); } - if ($stmt instanceof PhpParser\Node\Scalar\LNumber) { + if ($stmt instanceof PhpParser\Node\Scalar\Int_) { return Type::getInt(false, $stmt->value); } - if ($stmt instanceof PhpParser\Node\Scalar\DNumber) { + if ($stmt instanceof PhpParser\Node\Scalar\Float_) { return Type::getFloat($stmt->value); } @@ -623,7 +623,7 @@ private static function handleArrayItem( Codebase $codebase, NodeDataProvider $nodes, ArrayCreationInfo $array_creation_info, - PhpParser\Node\Expr\ArrayItem $item, + PhpParser\Node\ArrayItem $item, Aliases $aliases, FileSource $file_source = null, ?array $existing_class_constants = null, @@ -730,7 +730,7 @@ private static function handleArrayItem( $array_creation_info->all_list = $array_creation_info->all_list && $item_is_list_item; if ($item->key instanceof PhpParser\Node\Scalar\String_ - || $item->key instanceof PhpParser\Node\Scalar\LNumber + || $item->key instanceof PhpParser\Node\Scalar\Int_ || !$item->key ) { if ($item_key_value !== null diff --git a/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/ThrowAnalyzer.php similarity index 91% rename from src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php rename to src/Psalm/Internal/Analyzer/Statements/Expression/ThrowAnalyzer.php index 890c0637204..2d0c0a07653 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/ThrowAnalyzer.php @@ -2,11 +2,12 @@ declare(strict_types=1); -namespace Psalm\Internal\Analyzer\Statements; +namespace Psalm\Internal\Analyzer\Statements\Expression; use PhpParser; use Psalm\CodeLocation; use Psalm\Context; +use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer; use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\Internal\Type\Comparator\UnionTypeComparator; use Psalm\Issue\InvalidThrow; @@ -20,12 +21,9 @@ */ final class ThrowAnalyzer { - /** - * @param PhpParser\Node\Stmt\Throw_|PhpParser\Node\Expr\Throw_ $stmt - */ public static function analyze( StatementsAnalyzer $statements_analyzer, - PhpParser\Node $stmt, + PhpParser\Node\Expr\Throw_ $stmt, Context $context, ): bool { $context->inside_throw = true; @@ -87,9 +85,7 @@ public static function analyze( } } - if ($stmt instanceof PhpParser\Node\Expr\Throw_) { - $statements_analyzer->node_data->setType($stmt, Type::getNever()); - } + $statements_analyzer->node_data->setType($stmt, Type::getNever()); return true; } diff --git a/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php index e82c928ddbd..9261457eff1 100644 --- a/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php @@ -40,6 +40,7 @@ use Psalm\Internal\Analyzer\Statements\Expression\NullsafeAnalyzer; use Psalm\Internal\Analyzer\Statements\Expression\PrintAnalyzer; use Psalm\Internal\Analyzer\Statements\Expression\TernaryAnalyzer; +use Psalm\Internal\Analyzer\Statements\Expression\ThrowAnalyzer; use Psalm\Internal\Analyzer\Statements\Expression\UnaryPlusMinusAnalyzer; use Psalm\Internal\Analyzer\Statements\Expression\YieldAnalyzer; use Psalm\Internal\Analyzer\Statements\Expression\YieldFromAnalyzer; @@ -50,7 +51,7 @@ use Psalm\Issue\UnsupportedReferenceUsage; use Psalm\IssueBuffer; use Psalm\Node\Expr\VirtualFuncCall; -use Psalm\Node\Scalar\VirtualEncapsed; +use Psalm\Node\Scalar\VirtualInterpolatedString; use Psalm\Node\VirtualArg; use Psalm\Node\VirtualName; use Psalm\Plugin\EventHandler\Event\AfterExpressionAnalysisEvent; @@ -75,7 +76,7 @@ public static function analyze( Context $context, bool $array_assignment = false, ?Context $global_context = null, - bool $from_stmt = false, + PhpParser\Node\Stmt $from_stmt = null, ?TemplateResult $template_result = null, bool $assigned_to_reference = false, ): bool { @@ -146,7 +147,7 @@ private static function handleExpression( Context $context, bool $array_assignment, ?Context $global_context, - bool $from_stmt, + ?PhpParser\Node\Stmt $from_stmt, ?TemplateResult $template_result = null, bool $assigned_to_reference = false, ): bool { @@ -191,23 +192,19 @@ private static function handleExpression( return true; } - if ($stmt instanceof PhpParser\Node\Scalar\EncapsedStringPart) { - return true; - } - if ($stmt instanceof PhpParser\Node\Scalar\MagicConst) { MagicConstAnalyzer::analyze($statements_analyzer, $stmt, $context); return true; } - if ($stmt instanceof PhpParser\Node\Scalar\LNumber) { + if ($stmt instanceof PhpParser\Node\Scalar\Int_) { $statements_analyzer->node_data->setType($stmt, Type::getInt(false, $stmt->value)); return true; } - if ($stmt instanceof PhpParser\Node\Scalar\DNumber) { + if ($stmt instanceof PhpParser\Node\Scalar\Float_) { $statements_analyzer->node_data->setType($stmt, Type::getFloat($stmt->value)); return true; @@ -256,7 +253,7 @@ private static function handleExpression( $stmt, $context, 0, - $from_stmt, + $from_stmt !== null, ); } @@ -276,7 +273,7 @@ private static function handleExpression( return ArrayAnalyzer::analyze($statements_analyzer, $stmt, $context); } - if ($stmt instanceof PhpParser\Node\Scalar\Encapsed) { + if ($stmt instanceof PhpParser\Node\Scalar\InterpolatedString) { return EncapsulatedStringAnalyzer::analyze($statements_analyzer, $stmt, $context); } @@ -343,7 +340,7 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Expr\AssignRef) { - if (!AssignmentAnalyzer::analyzeAssignmentRef($statements_analyzer, $stmt, $context)) { + if (!AssignmentAnalyzer::analyzeAssignmentRef($statements_analyzer, $stmt, $context, $from_stmt)) { IssueBuffer::maybeAdd( new UnsupportedReferenceUsage( "This reference cannot be analyzed by Psalm", @@ -376,7 +373,7 @@ private static function handleExpression( } if ($stmt instanceof PhpParser\Node\Expr\ShellExec) { - $concat = new VirtualEncapsed($stmt->parts, $stmt->getAttributes()); + $concat = new VirtualInterpolatedString($stmt->parts, $stmt->getAttributes()); $virtual_call = new VirtualFuncCall(new VirtualName(['shell_exec']), [ new VirtualArg($concat), ], $stmt->getAttributes()); @@ -420,7 +417,7 @@ private static function handleExpression( return MatchAnalyzer::analyze($statements_analyzer, $stmt, $context); } - if ($stmt instanceof PhpParser\Node\Expr\Throw_ && $analysis_php_version_id >= 8_00_00) { + if ($stmt instanceof PhpParser\Node\Expr\Throw_) { return ThrowAnalyzer::analyze($statements_analyzer, $stmt, $context); } @@ -460,7 +457,7 @@ private static function analyzeAssignment( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr $stmt, Context $context, - bool $from_stmt, + ?PhpParser\Node\Stmt $from_stmt, ): bool { $assignment_type = AssignmentAnalyzer::analyze( $statements_analyzer, @@ -468,7 +465,7 @@ private static function analyzeAssignment( $stmt->expr, null, $context, - $stmt->getDocComment(), + $stmt->getDocComment() ?? $from_stmt?->getDocComment(), [], !$from_stmt ? $stmt : null, ); diff --git a/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php b/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php index 0ccadac662e..ec9e3df80a8 100644 --- a/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php @@ -35,7 +35,6 @@ use Psalm\Internal\Analyzer\Statements\GlobalAnalyzer; use Psalm\Internal\Analyzer\Statements\ReturnAnalyzer; use Psalm\Internal\Analyzer\Statements\StaticAnalyzer; -use Psalm\Internal\Analyzer\Statements\ThrowAnalyzer; use Psalm\Internal\Analyzer\Statements\UnsetAnalyzer; use Psalm\Internal\Analyzer\Statements\UnusedAssignmentRemover; use Psalm\Internal\Codebase\DataFlowGraph; @@ -536,8 +535,6 @@ private static function analyzeStatement( UnsetAnalyzer::analyze($statements_analyzer, $stmt, $context); } elseif ($stmt instanceof PhpParser\Node\Stmt\Return_) { ReturnAnalyzer::analyze($statements_analyzer, $stmt, $context); - } elseif ($stmt instanceof PhpParser\Node\Stmt\Throw_) { - ThrowAnalyzer::analyze($statements_analyzer, $stmt, $context); } elseif ($stmt instanceof PhpParser\Node\Stmt\Switch_) { SwitchAnalyzer::analyze($statements_analyzer, $stmt, $context); } elseif ($stmt instanceof PhpParser\Node\Stmt\Break_) { @@ -559,7 +556,7 @@ private static function analyzeStatement( $context, false, $global_context, - true, + $stmt, ) === false) { return false; } diff --git a/src/Psalm/Internal/PhpTraverser/CustomTraverser.php b/src/Psalm/Internal/PhpTraverser/CustomTraverser.php index f1e2673572d..2f32f5c6379 100644 --- a/src/Psalm/Internal/PhpTraverser/CustomTraverser.php +++ b/src/Psalm/Internal/PhpTraverser/CustomTraverser.php @@ -27,9 +27,8 @@ public function __construct() * Recursively traverse a node. * * @param Node $node node to traverse - * @return Node Result of traversal (may be original node or new one) */ - protected function traverseNode(Node $node): Node + protected function traverseNode(Node $node): void { foreach ($node->getSubNodeNames() as $name) { $subNode = &$node->$name; @@ -60,7 +59,7 @@ protected function traverseNode(Node $node): Node } if ($traverseChildren) { - $subNode = $this->traverseNode($subNode); + $this->traverseNode($subNode); if ($this->stopTraversal) { break; } @@ -88,8 +87,6 @@ protected function traverseNode(Node $node): Node } } } - - return $node; } /** @@ -124,7 +121,7 @@ protected function traverseArray(array $nodes): array } if ($traverseChildren) { - $node = $this->traverseNode($node); + $this->traverseNode($node); if ($this->stopTraversal) { break; } @@ -147,7 +144,7 @@ protected function traverseArray(array $nodes): array } elseif (false === $return) { throw new LogicException( 'bool(false) return from leaveNode() no longer supported. ' . - 'Return NodeTraverser::REMOVE_NODE instead', + 'Return NodeVisitor::REMOVE_NODE instead', ); } else { throw new LogicException( diff --git a/src/Psalm/Internal/PhpVisitor/AssignmentMapVisitor.php b/src/Psalm/Internal/PhpVisitor/AssignmentMapVisitor.php index de750effdf5..440e1aa2db8 100644 --- a/src/Psalm/Internal/PhpVisitor/AssignmentMapVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/AssignmentMapVisitor.php @@ -51,7 +51,7 @@ public function enterNode(PhpParser\Node $node): ?int } } - return PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN; + return PhpParser\NodeVisitor::DONT_TRAVERSE_CHILDREN; } if ($node instanceof PhpParser\Node\Expr\PostInc @@ -66,7 +66,7 @@ public function enterNode(PhpParser\Node $node): ?int $this->assignment_map[$var_id][$var_id] = true; } - return PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN; + return PhpParser\NodeVisitor::DONT_TRAVERSE_CHILDREN; } if ($node instanceof PhpParser\Node\Expr\FuncCall diff --git a/src/Psalm/Internal/PhpVisitor/CheckTrivialExprVisitor.php b/src/Psalm/Internal/PhpVisitor/CheckTrivialExprVisitor.php index 4bad245493c..ce4da4e2191 100644 --- a/src/Psalm/Internal/PhpVisitor/CheckTrivialExprVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/CheckTrivialExprVisitor.php @@ -17,7 +17,6 @@ private function checkNonTrivialExpr(PhpParser\Node\Expr $node): bool { if ($node instanceof PhpParser\Node\Expr\ArrayDimFetch || $node instanceof PhpParser\Node\Expr\Closure - || $node instanceof PhpParser\Node\Expr\ClosureUse || $node instanceof PhpParser\Node\Expr\Eval_ || $node instanceof PhpParser\Node\Expr\Exit_ || $node instanceof PhpParser\Node\Expr\Include_ @@ -55,7 +54,7 @@ public function enterNode(PhpParser\Node $node): ?int // Check for Non-Trivial Expression first if ($this->checkNonTrivialExpr($node)) { $this->has_non_trivial_expr = true; - return PhpParser\NodeTraverser::STOP_TRAVERSAL; + return self::STOP_TRAVERSAL; } if ($node instanceof PhpParser\Node\Expr\ClassConstFetch @@ -63,8 +62,11 @@ public function enterNode(PhpParser\Node $node): ?int || $node instanceof PhpParser\Node\Expr\Error || $node instanceof PhpParser\Node\Expr\PropertyFetch || $node instanceof PhpParser\Node\Expr\StaticPropertyFetch) { - return PhpParser\NodeTraverser::STOP_TRAVERSAL; + return self::STOP_TRAVERSAL; } + } elseif ($node instanceof PhpParser\Node\ClosureUse) { + $this->has_non_trivial_expr = true; + return self::STOP_TRAVERSAL; } return null; } diff --git a/src/Psalm/Internal/PhpVisitor/ParamReplacementVisitor.php b/src/Psalm/Internal/PhpVisitor/ParamReplacementVisitor.php index a10b264916f..a09b2c1eb53 100644 --- a/src/Psalm/Internal/PhpVisitor/ParamReplacementVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/ParamReplacementVisitor.php @@ -43,7 +43,7 @@ public function enterNode(PhpParser\Node $node): ?int } elseif ($node->name === $this->new_name) { if ($this->new_new_name_used) { $this->replacements = []; - return PhpParser\NodeTraverser::STOP_TRAVERSAL; + return self::STOP_TRAVERSAL; } $this->replacements[] = new FileManipulation( @@ -56,7 +56,7 @@ public function enterNode(PhpParser\Node $node): ?int } elseif ($node->name === $this->new_name . '_new') { if ($this->new_name_replaced) { $this->replacements = []; - return PhpParser\NodeTraverser::STOP_TRAVERSAL; + return self::STOP_TRAVERSAL; } $this->new_new_name_used = true; diff --git a/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php b/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php index 19040e27a92..d185eadea03 100644 --- a/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php @@ -103,7 +103,7 @@ public function enterNode(PhpParser\Node $node, bool &$traverseChildren = true): if ($a_e2 > $stmt_end_pos) { $this->must_rescan = true; - return PhpParser\NodeTraverser::STOP_TRAVERSAL; + return self::STOP_TRAVERSAL; } $end_offset = $b_e2 - $a_e2; @@ -139,7 +139,7 @@ public function enterNode(PhpParser\Node $node, bool &$traverseChildren = true): if (!$method_contents) { $this->must_rescan = true; - return PhpParser\NodeTraverser::STOP_TRAVERSAL; + return self::STOP_TRAVERSAL; } $error_handler = new Collecting(); @@ -204,7 +204,7 @@ public function enterNode(PhpParser\Node $node, bool &$traverseChildren = true): ) { $this->must_rescan = true; - return PhpParser\NodeTraverser::STOP_TRAVERSAL; + return self::STOP_TRAVERSAL; } // changes "): {" to ") {" @@ -227,7 +227,7 @@ public function enterNode(PhpParser\Node $node, bool &$traverseChildren = true): ) { $this->must_rescan = true; - return PhpParser\NodeTraverser::STOP_TRAVERSAL; + return self::STOP_TRAVERSAL; } } @@ -286,7 +286,7 @@ public function enterNode(PhpParser\Node $node, bool &$traverseChildren = true): $this->must_rescan = true; - return PhpParser\NodeTraverser::STOP_TRAVERSAL; + return self::STOP_TRAVERSAL; } if ($node->stmts) { @@ -317,7 +317,7 @@ public function enterNode(PhpParser\Node $node, bool &$traverseChildren = true): $this->must_rescan = true; - return PhpParser\NodeTraverser::STOP_TRAVERSAL; + return self::STOP_TRAVERSAL; } if ($start_offset !== 0 || $end_offset !== 0 || $line_offset !== 0) { diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php b/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php index bfcfc996a89..da64d4115b1 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php @@ -218,8 +218,8 @@ public static function getUnresolvedClassConstExpr( } if ($stmt instanceof PhpParser\Node\Scalar\String_ - || $stmt instanceof PhpParser\Node\Scalar\LNumber - || $stmt instanceof PhpParser\Node\Scalar\DNumber + || $stmt instanceof PhpParser\Node\Scalar\Int_ + || $stmt instanceof PhpParser\Node\Scalar\Float_ ) { return new ScalarValue($stmt->value); } @@ -373,11 +373,11 @@ public static function enterConditional( ( $expr->left instanceof PhpParser\Node\Expr\ConstFetch && $expr->left->name->getParts() === ['PHP_VERSION_ID'] - && $expr->right instanceof PhpParser\Node\Scalar\LNumber + && $expr->right instanceof PhpParser\Node\Scalar\Int_ ) || ( $expr->right instanceof PhpParser\Node\Expr\ConstFetch && $expr->right->name->getParts() === ['PHP_VERSION_ID'] - && $expr->left instanceof PhpParser\Node\Scalar\LNumber + && $expr->left instanceof PhpParser\Node\Scalar\Int_ ) ) ) { diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php index 54c42971897..b91b521ec88 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php @@ -6,11 +6,11 @@ use LogicException; use PhpParser; +use PhpParser\Modifiers; use PhpParser\Node\Identifier; use PhpParser\Node\IntersectionType; use PhpParser\Node\Name; use PhpParser\Node\NullableType; -use PhpParser\Node\Stmt\Class_; use PhpParser\Node\UnionType; use Psalm\Aliases; use Psalm\CodeLocation; @@ -100,6 +100,7 @@ public function __construct( public function start( PhpParser\Node\FunctionLike $stmt, bool $fake_method = false, + PhpParser\Comment\Doc $doc_comment = null, ): FunctionStorage|MethodStorage|false { if ($stmt instanceof PhpParser\Node\Expr\Closure || $stmt instanceof PhpParser\Node\Expr\ArrowFunction @@ -411,7 +412,7 @@ public function start( $storage->returns_by_ref = true; } - $doc_comment = $stmt->getDocComment(); + $doc_comment = $stmt->getDocComment() ?? $doc_comment; if ($classlike_storage && !$classlike_storage->is_trait) { @@ -618,7 +619,7 @@ public function start( $property_storage->location = $param_storage->location; $property_storage->stmt_location = new CodeLocation($this->file_scanner, $param); $property_storage->has_default = (bool)$param->default; - $param_type_readonly = (bool)($param->flags & PhpParser\Node\Stmt\Class_::MODIFIER_READONLY); + $param_type_readonly = (bool)($param->flags & PhpParser\Modifiers::READONLY); $property_storage->readonly = $param_type_readonly ?: $var_comment_readonly; $property_storage->allow_private_mutation = $var_comment_allow_private_mutation; $param_storage->promoted_property = true; @@ -626,18 +627,18 @@ public function start( $property_id = $fq_classlike_name . '::$' . $param_storage->name; - switch ($param->flags & Class_::VISIBILITY_MODIFIER_MASK) { - case Class_::MODIFIER_PUBLIC: + switch ($param->flags & Modifiers::VISIBILITY_MASK) { + case Modifiers::PUBLIC: $property_storage->visibility = ClassLikeAnalyzer::VISIBILITY_PUBLIC; $classlike_storage->inheritable_property_ids[$param_storage->name] = $property_id; break; - case Class_::MODIFIER_PROTECTED: + case Modifiers::PROTECTED: $property_storage->visibility = ClassLikeAnalyzer::VISIBILITY_PROTECTED; $classlike_storage->inheritable_property_ids[$param_storage->name] = $property_id; break; - case Class_::MODIFIER_PRIVATE: + case Modifiers::PRIVATE: $property_storage->visibility = ClassLikeAnalyzer::VISIBILITY_PRIVATE; break; } diff --git a/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php b/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php index 54d9ce3d323..b22cb9fc7fc 100644 --- a/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php +++ b/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php @@ -34,6 +34,7 @@ use Psalm\Storage\FileStorage; use Psalm\Storage\MethodStorage; use Psalm\Type; +use SplObjectStorage; use UnexpectedValueException; use function array_pop; @@ -84,6 +85,11 @@ final class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements Fi private array $bad_classes = []; private readonly EventDispatcher $eventDispatcher; + /** + * @var SplObjectStorage + */ + private SplObjectStorage $closure_statements; + public function __construct( private readonly Codebase $codebase, private readonly FileScanner $file_scanner, @@ -93,6 +99,7 @@ public function __construct( $this->scan_deep = $file_scanner->will_analyze; $this->aliases = $this->file_storage->aliases = new Aliases(); $this->eventDispatcher = $this->codebase->config->eventDispatcher; + $this->closure_statements = new SplObjectStorage(); } public function enterNode(PhpParser\Node $node): ?int @@ -143,7 +150,7 @@ public function enterNode(PhpParser\Node $node): ?int if ($classlike_node_scanner->start($node) === false) { $this->bad_classes[spl_object_id($node)] = true; - return PhpParser\NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + return self::DONT_TRAVERSE_CURRENT_AND_CHILDREN; } $this->classlike_node_scanners[] = $classlike_node_scanner; @@ -160,13 +167,34 @@ public function enterNode(PhpParser\Node $node): ?int } } } - } elseif ($node instanceof PhpParser\Node\FunctionLike) { + } elseif ($node instanceof PhpParser\Node\FunctionLike + || $node instanceof PhpParser\Node\Stmt\Expression + && ($node->expr instanceof PhpParser\Node\Expr\ArrowFunction + || $node->expr instanceof PhpParser\Node\Expr\Closure) + || $node instanceof PhpParser\Node\Arg + && ($node->value instanceof PhpParser\Node\Expr\ArrowFunction + || $node->value instanceof PhpParser\Node\Expr\Closure) + ) { + $doc_comment = null; if ($node instanceof PhpParser\Node\Stmt\Function_ || $node instanceof PhpParser\Node\Stmt\ClassMethod ) { if ($this->skip_if_descendants) { return null; } + } elseif ($node instanceof PhpParser\Node\Stmt\Expression) { + $doc_comment = $node->getDocComment(); + /** @var PhpParser\Node\FunctionLike */ + $node = $node->expr; + $this->closure_statements->attach($node); + } elseif ($node instanceof PhpParser\Node\Arg) { + $doc_comment = $node->getDocComment(); + /** @var PhpParser\Node\FunctionLike */ + $node = $node->value; + $this->closure_statements->attach($node); + } elseif ($this->closure_statements->contains($node)) { + // This is a closure that was already processed at the statement level. + return null; } $classlike_storage = null; @@ -193,7 +221,7 @@ public function enterNode(PhpParser\Node $node): ?int $functionlike_types, ); - $functionlike_node_scanner->start($node); + $functionlike_node_scanner->start($node, false, $doc_comment); $this->functionlike_node_scanners[] = $functionlike_node_scanner; @@ -212,7 +240,7 @@ public function enterNode(PhpParser\Node $node): ?int } if (!$this->scan_deep) { - return PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN; + return self::DONT_TRAVERSE_CHILDREN; } } elseif ($node instanceof PhpParser\Node\Stmt\Global_) { $functionlike_node_scanner = end($this->functionlike_node_scanners); diff --git a/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php b/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php index 455a22696df..065fbf1210d 100644 --- a/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php +++ b/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php @@ -93,7 +93,7 @@ public function enterNode(Node $node): ?int if ($attrs['endFilePos'] < $this->start_change || $attrs['startFilePos'] > $this->end_change ) { - return PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN; + return PhpParser\NodeVisitor::DONT_TRAVERSE_CHILDREN; } } @@ -146,7 +146,10 @@ public function enterNode(Node $node): ?int return null; } - private function addAlias(Stmt\UseUse $use, int $type, ?Name $prefix = null): void + /** + * @param Stmt\Use_::TYPE_* $type + */ + private function addAlias(Node\UseItem $use, int $type, ?Name $prefix = null): void { // Add prefix for group uses /** @var Name $name */ diff --git a/src/Psalm/Internal/PhpVisitor/TraitFinder.php b/src/Psalm/Internal/PhpVisitor/TraitFinder.php index 9decb8d8af1..e7640cce7df 100644 --- a/src/Psalm/Internal/PhpVisitor/TraitFinder.php +++ b/src/Psalm/Internal/PhpVisitor/TraitFinder.php @@ -53,7 +53,7 @@ public function enterNode(PhpParser\Node $node, bool &$traverseChildren = true): if ($node instanceof PhpParser\Node\Stmt\ClassLike || $node instanceof PhpParser\Node\FunctionLike ) { - return PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN; + return PhpParser\NodeVisitor::DONT_TRAVERSE_CHILDREN; } return null; diff --git a/src/Psalm/Internal/PhpVisitor/YieldTypeCollector.php b/src/Psalm/Internal/PhpVisitor/YieldTypeCollector.php index 94de49e6619..03912348153 100644 --- a/src/Psalm/Internal/PhpVisitor/YieldTypeCollector.php +++ b/src/Psalm/Internal/PhpVisitor/YieldTypeCollector.php @@ -8,7 +8,6 @@ use PhpParser\Node\Expr\YieldFrom; use PhpParser\Node\Expr\Yield_; use PhpParser\Node\FunctionLike; -use PhpParser\NodeTraverser; use PhpParser\NodeVisitorAbstract; use Psalm\Internal\Provider\NodeDataProvider; use Psalm\Type; @@ -63,7 +62,7 @@ public function enterNode(Node $node): ?int $this->yield_types []= Type::getMixed(); } elseif ($node instanceof FunctionLike) { - return NodeTraverser::DONT_TRAVERSE_CHILDREN; + return self::DONT_TRAVERSE_CHILDREN; } return null; diff --git a/src/Psalm/Internal/Provider/StatementsProvider.php b/src/Psalm/Internal/Provider/StatementsProvider.php index b5bdbef8ac1..d929a8614e7 100644 --- a/src/Psalm/Internal/Provider/StatementsProvider.php +++ b/src/Psalm/Internal/Provider/StatementsProvider.php @@ -6,9 +6,9 @@ use PhpParser; use PhpParser\ErrorHandler\Collecting; -use PhpParser\Lexer\Emulative; use PhpParser\Node\Stmt; use PhpParser\Parser; +use PhpParser\PhpVersion; use Psalm\CodeLocation\ParseErrorLocation; use Psalm\Codebase; use Psalm\Config; @@ -74,8 +74,6 @@ final class StatementsProvider */ private array $deletion_ranges = []; - private static ?Emulative $lexer = null; - private static ?Parser $parser = null; public function __construct( @@ -375,21 +373,11 @@ public static function parseStatements( ?array $existing_statements = null, ?array $file_changes = null, ): array { - $attributes = [ - 'comments', 'startLine', 'startFilePos', 'endFilePos', - ]; - - if (!self::$lexer) { + if (!self::$parser) { $major_version = Codebase::transformPhpVersionId($analysis_php_version_id, 10_000); $minor_version = Codebase::transformPhpVersionId($analysis_php_version_id % 10_000, 100); - self::$lexer = new Emulative([ - 'usedAttributes' => $attributes, - 'phpVersion' => $major_version . '.' . $minor_version, - ]); - } - - if (!self::$parser) { - self::$parser = (new PhpParser\ParserFactory())->create(PhpParser\ParserFactory::ONLY_PHP7, self::$lexer); + $php_version = PhpVersion::fromComponents($major_version, $minor_version); + self::$parser = (new PhpParser\ParserFactory())->createForVersion($php_version); } $used_cached_statements = false; @@ -466,11 +454,6 @@ public static function parseStatements( return $stmts; } - public static function clearLexer(): void - { - self::$lexer = null; - } - public static function clearParser(): void { self::$parser = null; diff --git a/src/Psalm/Internal/RuntimeCaches.php b/src/Psalm/Internal/RuntimeCaches.php index a876f2c5d45..a0a8383d9b9 100644 --- a/src/Psalm/Internal/RuntimeCaches.php +++ b/src/Psalm/Internal/RuntimeCaches.php @@ -40,7 +40,6 @@ public static function clearAll(): void FunctionLikeAnalyzer::clearCache(); ClassLikeStorageProvider::deleteAll(); FileStorageProvider::deleteAll(); - StatementsProvider::clearLexer(); StatementsProvider::clearParser(); ParsedDocblock::resetNewlineBetweenAnnotations(); } diff --git a/src/Psalm/Internal/Scanner/PhpStormMetaScanner.php b/src/Psalm/Internal/Scanner/PhpStormMetaScanner.php index 2ca354dc68f..ed5f7e92800 100644 --- a/src/Psalm/Internal/Scanner/PhpStormMetaScanner.php +++ b/src/Psalm/Internal/Scanner/PhpStormMetaScanner.php @@ -107,7 +107,7 @@ public static function handleOverride(array $args, Codebase $codebase): void if ($args[1]->value->name->getParts() === ['type'] && $args[1]->value->getArgs() - && $args[1]->value->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber + && $args[1]->value->getArgs()[0]->value instanceof PhpParser\Node\Scalar\Int_ ) { $type_offset = $args[1]->value->getArgs()[0]->value->value; } @@ -116,7 +116,7 @@ public static function handleOverride(array $args, Codebase $codebase): void if ($args[1]->value->name->getParts() === ['elementType'] && $args[1]->value->getArgs() - && $args[1]->value->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber + && $args[1]->value->getArgs()[0]->value instanceof PhpParser\Node\Scalar\Int_ ) { $element_type_offset = $args[1]->value->getArgs()[0]->value->value; } @@ -126,7 +126,7 @@ public static function handleOverride(array $args, Codebase $codebase): void && $identifier->name instanceof PhpParser\Node\Identifier && ( $identifier->getArgs() === [] - || $identifier->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber + || $identifier->getArgs()[0]->value instanceof PhpParser\Node\Scalar\Int_ ) ) { $meta_fq_classlike_name = $identifier->class->toString(); @@ -136,7 +136,7 @@ public static function handleOverride(array $args, Codebase $codebase): void if ($map) { $offset = 0; if ($identifier->getArgs() - && $identifier->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber + && $identifier->getArgs()[0]->value instanceof PhpParser\Node\Scalar\Int_ ) { $offset = $identifier->getArgs()[0]->value->value; } @@ -278,7 +278,7 @@ static function ( && $identifier->name instanceof PhpParser\Node\Name\FullyQualified && ( $identifier->getArgs() === [] - || $identifier->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber + || $identifier->getArgs()[0]->value instanceof PhpParser\Node\Scalar\Int_ ) ) { $function_id = strtolower($identifier->name->toString()); @@ -286,7 +286,7 @@ static function ( if ($map) { $offset = 0; if ($identifier->getArgs() - && $identifier->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber + && $identifier->getArgs()[0]->value instanceof PhpParser\Node\Scalar\Int_ ) { $offset = $identifier->getArgs()[0]->value->value; } diff --git a/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php b/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php index 7a110f6a32e..4d938f6a0a9 100644 --- a/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php +++ b/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php @@ -11,9 +11,9 @@ use Psalm\Node\Stmt\VirtualClassMethod; use Psalm\Node\Stmt\VirtualInterface; use Psalm\Node\Stmt\VirtualProperty; -use Psalm\Node\Stmt\VirtualPropertyProperty; use Psalm\Node\Stmt\VirtualTrait; use Psalm\Node\VirtualConst; +use Psalm\Node\VirtualPropertyItem; use Psalm\Storage\ClassLikeStorage; use Psalm\Internal\Analyzer\ClassLikeAnalyzer; use Psalm\Internal\Scanner\ParsedDocblock; @@ -142,10 +142,10 @@ private static function getConstantNodes(Codebase $codebase, ClassLikeStorage $s ) ], $constant_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC - ? PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC + ? PhpParser\Modifiers::PUBLIC : ($constant_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PROTECTED - ? PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED - : PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE) + ? PhpParser\Modifiers::PROTECTED + : PhpParser\Modifiers::PRIVATE) ); } @@ -163,9 +163,9 @@ private static function getPropertyNodes(ClassLikeStorage $storage): array foreach ($storage->properties as $property_name => $property_storage) { $flag = match ($property_storage->visibility) { - ClassLikeAnalyzer::VISIBILITY_PRIVATE => PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE, - ClassLikeAnalyzer::VISIBILITY_PROTECTED => PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED, - default => PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC, + ClassLikeAnalyzer::VISIBILITY_PRIVATE => PhpParser\Modifiers::PRIVATE, + ClassLikeAnalyzer::VISIBILITY_PROTECTED => PhpParser\Modifiers::PROTECTED, + default => PhpParser\Modifiers::PUBLIC, }; $docblock = new ParsedDocblock('', []); @@ -182,9 +182,9 @@ private static function getPropertyNodes(ClassLikeStorage $storage): array } $property_nodes[] = new VirtualProperty( - $flag | ($property_storage->is_static ? PhpParser\Node\Stmt\Class_::MODIFIER_STATIC : 0), + $flag | ($property_storage->is_static ? PhpParser\Modifiers::STATIC : 0), [ - new VirtualPropertyProperty( + new VirtualPropertyItem( $property_name, $property_storage->suggested_type ? StubsGenerator::getExpressionFromType($property_storage->suggested_type) @@ -222,9 +222,9 @@ private static function getMethodNodes(ClassLikeStorage $storage): array { } $flag = match ($method_storage->visibility) { - ReflectionProperty::IS_PRIVATE => PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE, - ReflectionProperty::IS_PROTECTED => PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED, - default => PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC, + ReflectionProperty::IS_PRIVATE => PhpParser\Modifiers::PRIVATE, + ReflectionProperty::IS_PROTECTED => PhpParser\Modifiers::PROTECTED, + default => PhpParser\Modifiers::PUBLIC, }; $docblock = new ParsedDocblock('', []); @@ -276,8 +276,8 @@ private static function getMethodNodes(ClassLikeStorage $storage): array { $method_storage->cased_name, [ 'flags' => $flag - | ($method_storage->is_static ? PhpParser\Node\Stmt\Class_::MODIFIER_STATIC : 0) - | ($method_storage->abstract ? PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT : 0), + | ($method_storage->is_static ? PhpParser\Modifiers::STATIC : 0) + | ($method_storage->abstract ? PhpParser\Modifiers::ABSTRACT : 0), 'params' => StubsGenerator::getFunctionParamNodes($method_storage), 'returnType' => $method_storage->signature_return_type ? StubsGenerator::getParserTypeFromPsalmType($method_storage->signature_return_type) diff --git a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php index 301c2979d0b..19e212854b9 100644 --- a/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php +++ b/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php @@ -23,13 +23,13 @@ use PhpParser; use Psalm\Internal\Scanner\ParsedDocblock; use Psalm\Node\Expr\VirtualArray; -use Psalm\Node\Expr\VirtualArrayItem; +use Psalm\Node\VirtualArrayItem; use Psalm\Node\Expr\VirtualClassConstFetch; use Psalm\Node\Expr\VirtualConstFetch; use Psalm\Node\Expr\VirtualVariable; use Psalm\Node\Name\VirtualFullyQualified; -use Psalm\Node\Scalar\VirtualDNumber; -use Psalm\Node\Scalar\VirtualLNumber; +use Psalm\Node\Scalar\VirtualFloat; +use Psalm\Node\Scalar\VirtualInt; use Psalm\Node\Scalar\VirtualString; use Psalm\Node\Stmt\VirtualFunction; use Psalm\Node\Stmt\VirtualNamespace; @@ -365,11 +365,11 @@ public static function getExpressionFromType(Union $type) : PhpParser\Node\Expr } if ($atomic_type instanceof TLiteralInt) { - return new VirtualLNumber($atomic_type->value); + return new VirtualInt($atomic_type->value); } if ($atomic_type instanceof TLiteralFloat) { - return new VirtualDNumber($atomic_type->value); + return new VirtualFloat($atomic_type->value); } if ($atomic_type instanceof TFalse) { @@ -395,7 +395,7 @@ public static function getExpressionFromType(Union $type) : PhpParser\Node\Expr if ($atomic_type->is_list) { $key_type = null; } elseif (is_int($property_name)) { - $key_type = new VirtualLNumber($property_name); + $key_type = new VirtualInt($property_name); } else { $key_type = new VirtualString($property_name); } diff --git a/src/Psalm/Node/Scalar/VirtualDNumber.php b/src/Psalm/Node/Scalar/VirtualDNumber.php deleted file mode 100644 index 9a95e9b0312..00000000000 --- a/src/Psalm/Node/Scalar/VirtualDNumber.php +++ /dev/null @@ -1,13 +0,0 @@ -expr; } diff --git a/tests/ClosureTest.php b/tests/ClosureTest.php index 7e3b5b77fde..ebdf2532344 100644 --- a/tests/ClosureTest.php +++ b/tests/ClosureTest.php @@ -1008,6 +1008,19 @@ function &(): int { fn &(int &$x): int => $x; ', ], + 'arrowFunctionArg' => [ + 'code' => '}> $existingIssue */ + array_reduce( + $existingIssue, + /** + * @param array{o:int, s:array} $existingIssue + */ + static fn(int $carry, array $existingIssue): int => $carry + $existingIssue["o"], + 0, + ); + ', + ], ]; } @@ -1346,7 +1359,7 @@ function () { );', 'error_message' => 'InvalidArgument', ], - 'undefinedVariableInEncapsedString' => [ + 'undefinedVariableInInterpolatedString' => [ 'code' => ' "$a"; ',