Skip to content

Commit bf57d59

Browse files
authored
Merge pull request #10618 from weirdan/update-master
2 parents 9520223 + e87fd86 commit bf57d59

17 files changed

+115
-20
lines changed

dictionaries/ImpureFunctionsList.php

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
'ob_end_clean' => true,
8686
'ob_get_clean' => true,
8787
'readfile' => true,
88+
'readgzfile' => true,
8889
'printf' => true,
8990
'var_dump' => true,
9091
'phpinfo' => true,

src/Psalm/CodeLocation.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public function __construct(
147147
$this->preview_start = $this->docblock_start ?: $this->file_start;
148148

149149
/** @psalm-suppress ImpureMethodCall Actually mutation-free just not marked */
150-
$this->raw_line_number = $stmt->getLine();
150+
$this->raw_line_number = $stmt->getStartLine();
151151

152152
$this->docblock_line_number = $comment_line;
153153
}

src/Psalm/Internal/Analyzer/ClassAnalyzer.php

+6-3
Original file line numberDiff line numberDiff line change
@@ -1091,8 +1091,11 @@ private function checkPropertyInitialization(
10911091
$uninitialized_variables[] = '$this->' . $property_name;
10921092
$uninitialized_properties[$property_class_name . '::$' . $property_name] = $property;
10931093

1094-
if ($property->type && !$property->type->isMixed()) {
1095-
$uninitialized_typed_properties[$property_class_name . '::$' . $property_name] = $property;
1094+
if ($property->type) {
1095+
// Complain about all natively typed properties and all non-mixed docblock typed properties
1096+
if (!$property->type->from_docblock || !$property->type->isMixed()) {
1097+
$uninitialized_typed_properties[$property_class_name . '::$' . $property_name] = $property;
1098+
}
10961099
}
10971100
}
10981101

@@ -1192,7 +1195,7 @@ static function (FunctionLikeParameter $param): PhpParser\Node\Arg {
11921195
$fake_stmt = new VirtualClassMethod(
11931196
new VirtualIdentifier('__construct'),
11941197
[
1195-
'type' => PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC,
1198+
'flags' => PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC,
11961199
'params' => $fake_constructor_params,
11971200
'stmts' => $fake_constructor_stmts,
11981201
],

src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -2042,7 +2042,9 @@ private static function hasNonEmptyCountCheck(PhpParser\Node\Expr\FuncCall $stmt
20422042

20432043
private static function hasArrayKeyExistsCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
20442044
{
2045-
return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->getFirst()) === 'array_key_exists';
2045+
return $stmt->name instanceof PhpParser\Node\Name
2046+
&& (strtolower($stmt->name->getFirst()) === 'array_key_exists'
2047+
|| strtolower($stmt->name->getFirst()) === 'key_exists');
20462048
}
20472049

20482050
/**

src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php

+1
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,7 @@ private static function verifyExplicitParam(
13471347
} else {
13481348
if (!$param_type->hasString()
13491349
&& !$param_type->hasArray()
1350+
&& $context->check_functions
13501351
&& CallAnalyzer::checkFunctionExists(
13511352
$statements_analyzer,
13521353
$function_id,

src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php

+10-1
Original file line numberDiff line numberDiff line change
@@ -1133,6 +1133,8 @@ private static function handleNonExistentClass(
11331133

11341134
$override_property_visibility = $interface_storage->override_property_visibility;
11351135

1136+
$intersects_with_enum = false;
1137+
11361138
foreach ($intersection_types as $intersection_type) {
11371139
if ($intersection_type instanceof TNamedObject
11381140
&& $codebase->classExists($intersection_type->value)
@@ -1141,12 +1143,19 @@ private static function handleNonExistentClass(
11411143
$class_exists = true;
11421144
return;
11431145
}
1146+
if ($intersection_type instanceof TNamedObject
1147+
&& (in_array($intersection_type->value, ['UnitEnum', 'BackedEnum'], true)
1148+
|| in_array('UnitEnum', $codebase->getParentInterfaces($intersection_type->value)))
1149+
) {
1150+
$intersects_with_enum = true;
1151+
}
11441152
}
11451153

11461154
if (!$class_exists &&
11471155
//interfaces can't have properties. Except when they do... In PHP Core, they can
11481156
!in_array($fq_class_name, ['UnitEnum', 'BackedEnum'], true) &&
1149-
!in_array('UnitEnum', $codebase->getParentInterfaces($fq_class_name))
1157+
!in_array('UnitEnum', $codebase->getParentInterfaces($fq_class_name)) &&
1158+
!$intersects_with_enum
11501159
) {
11511160
if (IssueBuffer::accepts(
11521161
new NoInterfaceProperties(

src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public function findUnusedAssignment(
6767
$traverser->addVisitor($visitor);
6868
$traverser->traverse([$rhs_exp]);
6969

70-
$rhs_exp_trivial = (count($visitor->getNonTrivialExpr()) === 0);
70+
$rhs_exp_trivial = !$visitor->hasNonTrivialExpr();
7171

7272
if ($rhs_exp_trivial) {
7373
$treat_as_expr = false;

src/Psalm/Internal/Fork/PsalmRestarter.php

+9
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ protected function requiresRestart($default): bool
9999
}
100100
}
101101

102+
// opcache.save_comments is required for json mapper (used in language server) to work
103+
if ($opcache_loaded && in_array(ini_get('opcache.save_comments'), ['0', 'false', 0, false])) {
104+
return true;
105+
}
106+
102107
return $default || $this->required;
103108
}
104109

@@ -163,6 +168,10 @@ protected function restart($command): void
163168
}
164169
}
165170

171+
if ($opcache_loaded) {
172+
$additional_options[] = '-dopcache.save_comments=1';
173+
}
174+
166175
array_splice(
167176
$command,
168177
1,

src/Psalm/Internal/LanguageServer/LanguageServer.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ public function emitVersionedIssues(array $files, ?int $version = null): void
697697
$diagnostics = array_map(
698698
function (IssueData $issue_data): Diagnostic {
699699
//$check_name = $issue->check_name;
700-
$description = $issue_data->message;
700+
$description = '[' . $issue_data->type . '] ' . $issue_data->message;
701701
$severity = $issue_data->severity;
702702

703703
$start_line = max($issue_data->line_from, 1);

src/Psalm/Internal/PhpVisitor/CheckTrivialExprVisitor.php

+4-10
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@
1111
*/
1212
final class CheckTrivialExprVisitor extends PhpParser\NodeVisitorAbstract
1313
{
14-
/**
15-
* @var array<int, PhpParser\Node\Expr>
16-
*/
17-
private array $non_trivial_expr = [];
14+
private bool $has_non_trivial_expr = false;
1815

1916
private function checkNonTrivialExpr(PhpParser\Node\Expr $node): bool
2017
{
@@ -57,7 +54,7 @@ public function enterNode(PhpParser\Node $node): ?int
5754
if ($node instanceof PhpParser\Node\Expr) {
5855
// Check for Non-Trivial Expression first
5956
if ($this->checkNonTrivialExpr($node)) {
60-
$this->non_trivial_expr[] = $node;
57+
$this->has_non_trivial_expr = true;
6158
return PhpParser\NodeTraverser::STOP_TRAVERSAL;
6259
}
6360

@@ -72,11 +69,8 @@ public function enterNode(PhpParser\Node $node): ?int
7269
return null;
7370
}
7471

75-
/**
76-
* @return array<int, PhpParser\Node\Expr>
77-
*/
78-
public function getNonTrivialExpr(): array
72+
public function hasNonTrivialExpr(): bool
7973
{
80-
return $this->non_trivial_expr;
74+
return $this->has_non_trivial_expr;
8175
}
8276
}

src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ public function leaveNode(PhpParser\Node $node)
533533
}
534534

535535
throw new UnexpectedValueException(
536-
'There should be function storages for line ' . $this->file_path . ':' . $node->getLine(),
536+
'There should be function storages for line ' . $this->file_path . ':' . $node->getStartLine(),
537537
);
538538
}
539539

src/Psalm/Internal/Type/TypeExpander.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ public static function expandAtomic(
278278
$declaring_fq_classlike_name = $self_class;
279279
}
280280

281-
if (!($evaluate_class_constants && $codebase->classOrInterfaceExists($declaring_fq_classlike_name))) {
281+
if (!($evaluate_class_constants && $codebase->classOrInterfaceOrEnumExists($declaring_fq_classlike_name))) {
282282
return [$return_type];
283283
}
284284

tests/EnumTest.php

+27
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,33 @@ enum Bar: int
679679
'ignored_issues' => [],
680680
'php_version' => '8.1',
681681
],
682+
'allowPropertiesOnIntersectionsWithEnumInterfaces' => [
683+
'code' => <<<'PHP'
684+
<?php
685+
interface I {}
686+
687+
interface UE extends UnitEnum {}
688+
interface BE extends BackedEnum {}
689+
690+
function f(I $i): void {
691+
if ($i instanceof BackedEnum) {
692+
echo $i->name;
693+
}
694+
if ($i instanceof UnitEnum) {
695+
echo $i->name;
696+
}
697+
if ($i instanceof UE) {
698+
echo $i->name;
699+
}
700+
if ($i instanceof BE) {
701+
echo $i->name;
702+
}
703+
}
704+
PHP,
705+
'assertions' => [],
706+
'ignored_issues' => [],
707+
'php_version' => '8.1',
708+
],
682709
];
683710
}
684711

tests/FunctionCallTest.php

+8
Original file line numberDiff line numberDiff line change
@@ -1649,6 +1649,14 @@ function in_array($a, $b) {
16491649
}
16501650
}',
16511651
],
1652+
'callableArgumentWithFunctionExists' => [
1653+
'code' => <<<'PHP'
1654+
<?php
1655+
if (function_exists('foo')) {
1656+
register_shutdown_function('foo');
1657+
}
1658+
PHP,
1659+
],
16521660
'pregMatch' => [
16531661
'code' => '<?php
16541662
function takesInt(int $i) : void {}

tests/PropertyTypeTest.php

+9
Original file line numberDiff line numberDiff line change
@@ -3829,6 +3829,15 @@ class A {
38293829
',
38303830
'error_message' => 'UndefinedPropertyAssignment',
38313831
],
3832+
'nativeMixedPropertyWithNoConstructor' => [
3833+
'code' => <<< 'PHP'
3834+
<?php
3835+
class A {
3836+
public mixed $foo;
3837+
}
3838+
PHP,
3839+
'error_message' => 'MissingConstructor',
3840+
],
38323841
];
38333842
}
38343843
}

tests/TypeAnnotationTest.php

+19
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,25 @@ public function doesNotWork($_doesNotWork): void {
889889
}
890890
}',
891891
],
892+
'importFromEnum' => [
893+
'code' => <<<'PHP'
894+
<?php
895+
/** @psalm-type _Foo = array{foo: string} */
896+
enum E {}
897+
/**
898+
* @psalm-import-type _Foo from E
899+
*/
900+
class C {
901+
/** @param _Foo $foo */
902+
public function f(array $foo): void {
903+
echo $foo['foo'];
904+
}
905+
}
906+
PHP,
907+
'assertions' => [],
908+
'ignored_issues' => [],
909+
'php_version' => '8.1',
910+
],
892911
];
893912
}
894913

tests/TypeReconciliation/ArrayKeyExistsTest.php

+13
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,19 @@ public function isCriticalError(int|string $key): bool {
509509
'ignored_issues' => [],
510510
'php_version' => '8.0',
511511
],
512+
'keyExistsAsAliasForArrayKeyExists' => [
513+
'code' => <<<'PHP'
514+
<?php
515+
/**
516+
* @param array<string, string> $arr
517+
*/
518+
function foo(array $arr): void {
519+
if (key_exists("a", $arr)) {
520+
echo $arr["a"];
521+
}
522+
}
523+
PHP,
524+
],
512525
];
513526
}
514527

0 commit comments

Comments
 (0)