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";
',