Skip to content

Commit 9f34449

Browse files
authored
@readonly property cannot be passed by-ref
1 parent da73771 commit 9f34449

File tree

3 files changed

+89
-6
lines changed

3 files changed

+89
-6
lines changed

src/Rules/FunctionCallParametersCheck.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -409,16 +409,25 @@ public function check(
409409
if ($nativePropertyReflection === null) {
410410
continue;
411411
}
412-
if (!$nativePropertyReflection->isReadOnly()) {
413-
continue;
414-
}
415412

416-
if ($nativePropertyReflection->isStatic()) {
417-
$propertyDescription = sprintf('static readonly property %s::$%s', $propertyReflection->getDeclaringClass()->getDisplayName(), $propertyReflection->getName());
413+
if ($nativePropertyReflection->isReadOnly()) {
414+
if ($nativePropertyReflection->isStatic()) {
415+
$errorFormat = 'static readonly property %s::$%s';
416+
} else {
417+
$errorFormat = 'readonly property %s::$%s';
418+
}
419+
} elseif ($nativePropertyReflection->isReadOnlyByPhpDoc()) {
420+
if ($nativePropertyReflection->isStatic()) {
421+
$errorFormat = 'static @readonly property %s::$%s';
422+
} else {
423+
$errorFormat = '@readonly property %s::$%s';
424+
}
418425
} else {
419-
$propertyDescription = sprintf('readonly property %s::$%s', $propertyReflection->getDeclaringClass()->getDisplayName(), $propertyReflection->getName());
426+
continue;
420427
}
421428

429+
$propertyDescription = sprintf($errorFormat, $propertyReflection->getDeclaringClass()->getDisplayName(), $propertyReflection->getName());
430+
422431
$errors[] = RuleErrorBuilder::message(sprintf(
423432
'%s is passed by reference so it does not accept %s.',
424433
$this->describeParameter($parameter, $argumentName === null ? $i + 1 : null),

tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,4 +1982,41 @@ public function testBug3107(): void
19821982
$this->analyse([__DIR__ . '/data/bug-3107.php'], []);
19831983
}
19841984

1985+
public function testBug12676(): void
1986+
{
1987+
$errors = [
1988+
[
1989+
'Parameter #1 $array is passed by reference so it does not accept @readonly property Bug12676\A::$a.',
1990+
15,
1991+
],
1992+
[
1993+
'Parameter #1 $array is passed by reference so it does not accept @readonly property Bug12676\B::$readonlyArr.',
1994+
25,
1995+
],
1996+
[
1997+
'Parameter #1 $array is passed by reference so it does not accept static @readonly property Bug12676\C::$readonlyArr.',
1998+
35,
1999+
],
2000+
];
2001+
2002+
if (PHP_VERSION_ID < 80000) {
2003+
$errors = [
2004+
[
2005+
'Parameter #1 $array_arg is passed by reference so it does not accept @readonly property Bug12676\A::$a.',
2006+
15,
2007+
],
2008+
[
2009+
'Parameter #1 $array_arg is passed by reference so it does not accept @readonly property Bug12676\B::$readonlyArr.',
2010+
25,
2011+
],
2012+
[
2013+
'Parameter #1 $array_arg is passed by reference so it does not accept static @readonly property Bug12676\C::$readonlyArr.',
2014+
35,
2015+
],
2016+
];
2017+
}
2018+
2019+
$this->analyse([__DIR__ . '/data/bug-12676.php'], $errors);
2020+
}
2021+
19852022
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Bug12676;
4+
5+
/**
6+
* @immutable
7+
*/
8+
class A {
9+
10+
/** @var array<string, int> */
11+
public array $a;
12+
13+
public function __construct() {
14+
$this->a = ['b' => 2, 'a' => 1];
15+
ksort($this->a);
16+
}
17+
}
18+
19+
class B {
20+
/** @readonly */
21+
public array $readonlyArr;
22+
23+
public function __construct() {
24+
$this->readonlyArr = ['b' => 2, 'a' => 1];
25+
ksort($this->readonlyArr);
26+
}
27+
}
28+
29+
class C {
30+
/** @readonly */
31+
static public array $readonlyArr;
32+
33+
public function __construct() {
34+
self::$readonlyArr = ['b' => 2, 'a' => 1];
35+
ksort(self::$readonlyArr);
36+
}
37+
}

0 commit comments

Comments
 (0)