Skip to content

Commit 01d608d

Browse files
VincentLangletphpstan-bot
authored andcommitted
Fix class-string constants described as 'string' at typeOnly verbosity
- ConstantStringType::describe() at typeOnly level now returns 'class-string<ClassName>' instead of 'string' when the constant is a known class-string - This fixes misleading error messages like "returns string" when the actual type is a class-string (e.g. from SomeClass::class expressions) - Updated test expectations in 5 test files to reflect improved error messages - Added regression test for phpstan/phpstan#14440
1 parent c24d365 commit 01d608d

File tree

6 files changed

+59
-16
lines changed

6 files changed

+59
-16
lines changed

src/Type/Constant/ConstantStringType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public function getObjectTypeOrClassStringObjectType(): Type
113113
public function describe(VerbosityLevel $level): string
114114
{
115115
return $level->handle(
116-
static fn (): string => 'string',
116+
fn (): string => $this->isClassString ? 'class-string<' . $this->value . '>' : 'string',
117117
function (): string {
118118
$value = $this->value;
119119

tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,7 +1161,7 @@ public function testBug4413(): void
11611161
require_once __DIR__ . '/data/bug-4413.php';
11621162
$this->analyse([__DIR__ . '/data/bug-4413.php'], [
11631163
[
1164-
'Parameter #1 $date of function Bug4413\takesDate expects class-string<DateTime>, string given.',
1164+
'Parameter #1 $date of function Bug4413\takesDate expects class-string<DateTime>, class-string<stdClass> given.',
11651165
18,
11661166
],
11671167
]);
@@ -1200,11 +1200,11 @@ public function testBug4371(): void
12001200
{
12011201
$errors = [
12021202
[
1203-
'Parameter #1 $object_or_class of function is_a expects object, string given.',
1203+
'Parameter #1 $object_or_class of function is_a expects object, class-string<Bug4371\Bar> given.',
12041204
14,
12051205
],
12061206
[
1207-
'Parameter #1 $object_or_class of function is_a expects object, string given.',
1207+
'Parameter #1 $object_or_class of function is_a expects object, class-string<Bug4371\Bar> given.',
12081208
22,
12091209
],
12101210
];
@@ -1213,11 +1213,11 @@ public function testBug4371(): void
12131213
// php 7.x had different parameter names
12141214
$errors = [
12151215
[
1216-
'Parameter #1 $object_or_string of function is_a expects object, string given.',
1216+
'Parameter #1 $object_or_string of function is_a expects object, class-string<Bug4371\Bar> given.',
12171217
14,
12181218
],
12191219
[
1220-
'Parameter #1 $object_or_string of function is_a expects object, string given.',
1220+
'Parameter #1 $object_or_string of function is_a expects object, class-string<Bug4371\Bar> given.',
12211221
22,
12221222
],
12231223
];
@@ -1230,15 +1230,15 @@ public function testIsSubclassAllowString(): void
12301230
{
12311231
$errors = [
12321232
[
1233-
'Parameter #1 $object_or_class of function is_subclass_of expects object, string given.',
1233+
'Parameter #1 $object_or_class of function is_subclass_of expects object, class-string<IsSubclassAllowString\A> given.',
12341234
11,
12351235
],
12361236
[
1237-
'Parameter #1 $object_or_class of function is_subclass_of expects object, string given.',
1237+
'Parameter #1 $object_or_class of function is_subclass_of expects object, class-string<IsSubclassAllowString\B> given.',
12381238
14,
12391239
],
12401240
[
1241-
'Parameter #1 $object_or_class of function is_subclass_of expects object, string given.',
1241+
'Parameter #1 $object_or_class of function is_subclass_of expects object, class-string<IsSubclassAllowString\B> given.',
12421242
17,
12431243
],
12441244
];
@@ -1247,15 +1247,15 @@ public function testIsSubclassAllowString(): void
12471247
// php 7.x had different parameter names
12481248
$errors = [
12491249
[
1250-
'Parameter #1 $object_or_string of function is_subclass_of expects object, string given.',
1250+
'Parameter #1 $object_or_string of function is_subclass_of expects object, class-string<IsSubclassAllowString\A> given.',
12511251
11,
12521252
],
12531253
[
1254-
'Parameter #1 $object_or_string of function is_subclass_of expects object, string given.',
1254+
'Parameter #1 $object_or_string of function is_subclass_of expects object, class-string<IsSubclassAllowString\B> given.',
12551255
14,
12561256
],
12571257
[
1258-
'Parameter #1 $object_or_string of function is_subclass_of expects object, string given.',
1258+
'Parameter #1 $object_or_string of function is_subclass_of expects object, class-string<IsSubclassAllowString\B> given.',
12591259
17,
12601260
],
12611261
];

tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ public function testCallMethods(): void
507507
1461,
508508
],
509509
[
510-
'Parameter #1 $s of method Test\ClassStringWithUpperBounds::doFoo() expects class-string<Exception>, string given.',
510+
'Parameter #1 $s of method Test\ClassStringWithUpperBounds::doFoo() expects class-string<Exception>, class-string<Throwable> given.',
511511
1490,
512512
],
513513
[
@@ -834,7 +834,7 @@ public function testCallMethodsOnThisOnly(): void
834834
1379,
835835
],
836836
[
837-
'Parameter #1 $s of method Test\ClassStringWithUpperBounds::doFoo() expects class-string<Exception>, string given.',
837+
'Parameter #1 $s of method Test\ClassStringWithUpperBounds::doFoo() expects class-string<Exception>, class-string<Throwable> given.',
838838
1490,
839839
],
840840
[

tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ public function testTemplateTypeInOneBranchOfConditional(): void
579579
[
580580
'Parameter #1 $params of static method TemplateTypeInOneBranchOfConditional\DriverManager::getConnection() expects array{wrapperClass?: class-string<TemplateTypeInOneBranchOfConditional\Connection>}, array{wrapperClass: \'stdClass\'} given.',
581581
27,
582-
"Offset 'wrapperClass' (class-string<TemplateTypeInOneBranchOfConditional\Connection>) does not accept type string.",
582+
"Offset 'wrapperClass' (class-string<TemplateTypeInOneBranchOfConditional\Connection>) does not accept type class-string<stdClass>.",
583583
],
584584
[
585585
'Unable to resolve the template type T in call to static method TemplateTypeInOneBranchOfConditional\DriverManager::getConnection()',

tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@ public function testBug8071(): void
817817
// there should be no errors
818818
'Method Bug8071\Inheritance::inherit() should return array<TKey of (int|string), TValues of bool|float|int|string|null> but returns array<string>.',
819819
17,
820-
'Type string is not always the same as TValues. It breaks the contract for some argument types, typically subtypes.',
820+
"Type class-string<Bug8071\Inheritance> is not always the same as TValues. It breaks the contract for some argument types, typically subtypes.\n• Type string is not always the same as TValues. It breaks the contract for some argument types, typically subtypes.",
821821
],
822822
]);
823823
}
@@ -1331,4 +1331,18 @@ public function testBug11430(): void
13311331
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-11430.php'], []);
13321332
}
13331333

1334+
public function testBug14440(): void
1335+
{
1336+
$this->analyse([__DIR__ . '/data/bug-14440.php'], [
1337+
[
1338+
'Method Bug14440\ChildOne::getCounterpartClass() should return class-string<Bug14440\ChildOne> but returns class-string<Bug14440\ChildTwo>.',
1339+
18,
1340+
],
1341+
[
1342+
'Method Bug14440\ChildTwo::getCounterpartClass() should return class-string<Bug14440\ChildTwo> but returns class-string<Bug14440\ChildOne>.',
1343+
27,
1344+
],
1345+
]);
1346+
}
1347+
13341348
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug14440;
4+
5+
interface I {}
6+
7+
abstract class A
8+
{
9+
/** @return class-string<static&I> */
10+
abstract public static function getCounterpartClass(): string;
11+
}
12+
13+
final class ChildOne extends A implements I
14+
{
15+
#[\Override]
16+
public static function getCounterpartClass(): string
17+
{
18+
return ChildTwo::class;
19+
}
20+
}
21+
22+
final class ChildTwo extends A implements I
23+
{
24+
#[\Override]
25+
public static function getCounterpartClass(): string
26+
{
27+
return ChildOne::class;
28+
}
29+
}

0 commit comments

Comments
 (0)