Skip to content

Commit 4196540

Browse files
committed
Fix callable-string must be non-empty-string
1 parent b7137d1 commit 4196540

File tree

5 files changed

+41
-1
lines changed

5 files changed

+41
-1
lines changed

src/PhpDoc/TypeNodeResolver.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameSco
238238
return new ClassStringType();
239239

240240
case 'callable-string':
241-
return new IntersectionType([new StringType(), new CallableType()]);
241+
return new IntersectionType([new StringType(), new AccessoryNonEmptyStringType(), new CallableType()]);
242242

243243
case 'array-key':
244244
return new BenevolentUnionType([new IntegerType(), new StringType()]);

src/Type/IntersectionType.php

+7
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ private function describeItself(VerbosityLevel $level, bool $skipAccessoryTypes)
336336

337337
$nonEmptyStr = false;
338338
$nonFalsyStr = false;
339+
$callable = false;
339340
foreach ($this->getSortedTypes() as $i => $type) {
340341
if ($type instanceof AccessoryNonEmptyStringType
341342
|| $type instanceof AccessoryLiteralStringType
@@ -367,6 +368,11 @@ private function describeItself(VerbosityLevel $level, bool $skipAccessoryTypes)
367368
}
368369
}
369370

371+
// prevent redundant 'callable-string&non-empty-string'
372+
if ($callable && $nonEmptyStr) {
373+
continue;
374+
}
375+
370376
$typesToDescribe[$i] = $type;
371377
$skipTypeNames[] = 'string';
372378
continue;
@@ -381,6 +387,7 @@ private function describeItself(VerbosityLevel $level, bool $skipAccessoryTypes)
381387
$typesToDescribe[$i] = $type;
382388
$skipTypeNames[] = 'object';
383389
$skipTypeNames[] = 'string';
390+
$callable = true;
384391
continue;
385392
}
386393

tests/PHPStan/Analyser/AnalyserIntegrationTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -1481,6 +1481,12 @@ public function testBug11913(): void
14811481
$this->assertNoErrors($errors);
14821482
}
14831483

1484+
public function testBug12979(): void
1485+
{
1486+
$errors = $this->runAnalyse(__DIR__ . '/data/bug-12979.php');
1487+
$this->assertNoErrors($errors);
1488+
}
1489+
14841490
/**
14851491
* @param string[]|null $allAnalysedFiles
14861492
* @return Error[]
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug12979;
4+
5+
abstract class X
6+
{
7+
/**
8+
* @return callable-string
9+
*/
10+
abstract public function callableString(): string;
11+
12+
/**
13+
* @param non-empty-string $nonEmptyString
14+
*/
15+
abstract public function acceptNonEmptyString(string $nonEmptyString): void;
16+
17+
public function test(): void
18+
{
19+
$this->acceptNonEmptyString($this->callableString());
20+
}
21+
}

tests/PHPStan/Type/IntersectionTypeTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPStan\Testing\PHPStanTestCase;
99
use PHPStan\TrinaryLogic;
1010
use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
11+
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
1112
use PHPStan\Type\Accessory\AccessoryUppercaseStringType;
1213
use PHPStan\Type\Accessory\HasOffsetType;
1314
use PHPStan\Type\Accessory\HasPropertyType;
@@ -481,6 +482,11 @@ public function dataDescribe(): iterable
481482
VerbosityLevel::precise(),
482483
'uppercase-string',
483484
];
485+
yield [
486+
new IntersectionType([new StringType(), new AccessoryNonEmptyStringType(), new CallableType()]),
487+
VerbosityLevel::value(),
488+
'callable-string',
489+
];
484490
}
485491

486492
/**

0 commit comments

Comments
 (0)