Skip to content

Commit 1df7113

Browse files
committed
rework
1 parent 14d45f4 commit 1df7113

File tree

9 files changed

+164
-23
lines changed

9 files changed

+164
-23
lines changed

src/Php/PhpVersions.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,9 @@ public function supportsNamedArgumentAfterUnpackedArgument(): TrinaryLogic
4343
return IntegerRangeType::fromInterval(80100, null)->isSuperTypeOf($this->phpVersions)->result;
4444
}
4545

46+
public function supportsTrueAndFalseStandaloneType(): TrinaryLogic
47+
{
48+
return IntegerRangeType::fromInterval(80200, null)->isSuperTypeOf($this->phpVersions)->result;
49+
}
50+
4651
}

src/Rules/TooWideTypehints/TooWideFunctionReturnTypehintRule.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,16 @@ public function processNode(Node $node, Scope $scope): array
3131
{
3232
$function = $node->getFunctionReflection();
3333

34-
$functionReturnType = $function->getReturnType();
3534
return $this->check->checkFunction(
3635
$node,
37-
$functionReturnType,
36+
$function->getReturnType(),
37+
$function->getPhpDocReturnType(),
3838
sprintf(
3939
'Function %s()',
4040
$function->getName(),
4141
),
4242
false,
43+
$scope,
4344
);
4445
}
4546

src/Rules/TooWideTypehints/TooWideMethodReturnTypehintRule.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,17 @@ public function processNode(Node $node, Scope $scope): array
4949
}
5050
}
5151

52-
$methodReturnType = $method->getReturnType();
5352
return $this->check->checkFunction(
5453
$node,
55-
$methodReturnType,
54+
$method->getReturnType(),
55+
$method->getPhpDocReturnType(),
5656
sprintf(
5757
'Method %s::%s()',
5858
$method->getDeclaringClass()->getDisplayName(),
5959
$method->getName(),
6060
),
6161
!$isFirstDeclaration && !$method->isPrivate(),
62+
$scope,
6263
);
6364
}
6465

src/Rules/TooWideTypehints/TooWidePropertyTypeRule.php

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@
44

55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
7-
use PHPStan\DependencyInjection\AutowiredParameter;
87
use PHPStan\DependencyInjection\RegisteredRule;
98
use PHPStan\Node\ClassPropertiesNode;
109
use PHPStan\Reflection\PropertyReflection;
1110
use PHPStan\Rules\Properties\PropertyReflectionFinder;
1211
use PHPStan\Rules\Properties\ReadWritePropertiesExtensionProvider;
1312
use PHPStan\Rules\Rule;
1413
use PHPStan\Type\TypeCombinator;
15-
use PHPStan\Type\UnionType;
1614
use function count;
1715
use function sprintf;
1816

@@ -27,8 +25,6 @@ public function __construct(
2725
private ReadWritePropertiesExtensionProvider $extensionProvider,
2826
private PropertyReflectionFinder $propertyReflectionFinder,
2927
private TooWideTypeCheck $check,
30-
#[AutowiredParameter(ref: '%featureToggles.reportTooWideBool%')]
31-
private bool $reportTooWideBool,
3228
)
3329
{
3430
}
@@ -60,11 +56,13 @@ public function processNode(Node $node, Scope $scope): array
6056

6157
$propertyReflection = $classReflection->getNativeProperty($propertyName);
6258
$propertyType = $propertyReflection->getWritableType();
63-
if (!$propertyType instanceof UnionType) {
64-
if (!$propertyType->isBoolean()->yes() || !$this->reportTooWideBool) {
65-
continue;
66-
}
59+
$phpdocType = $propertyReflection->getPhpDocType();
60+
61+
$propertyType = $this->check->findTypeToCheck($propertyType, $phpdocType, $scope);
62+
if ($propertyType === null) {
63+
continue;
6764
}
65+
6866
foreach ($this->extensionProvider->getExtensions() as $extension) {
6967
if ($extension->isAlwaysRead($propertyReflection, $propertyName)) {
7068
continue 2;

src/Rules/TooWideTypehints/TooWideTypeCheck.php

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Rules\TooWideTypehints;
44

5+
use PHPStan\Analyser\Scope;
56
use PHPStan\DependencyInjection\AutowiredParameter;
67
use PHPStan\DependencyInjection\AutowiredService;
78
use PHPStan\Node\ClassPropertyNode;
@@ -11,6 +12,7 @@
1112
use PHPStan\Rules\RuleErrorBuilder;
1213
use PHPStan\Type\Type;
1314
use PHPStan\Type\TypeCombinator;
15+
use PHPStan\Type\TypehintHelper;
1416
use PHPStan\Type\TypeUtils;
1517
use PHPStan\Type\UnionType;
1618
use PHPStan\Type\VerbosityLevel;
@@ -71,18 +73,18 @@ public function checkProperty(
7173
*/
7274
public function checkFunction(
7375
MethodReturnStatementsNode|FunctionReturnStatementsNode $node,
74-
Type $functionReturnType,
76+
Type $nativeFunctionReturnType,
77+
Type $phpdocFunctionReturnType,
7578
string $functionDescription,
7679
bool $checkDescendantClass,
80+
Scope $scope,
7781
): array
7882
{
79-
$functionReturnType = TypeUtils::resolveLateResolvableTypes($functionReturnType);
80-
81-
if (!$functionReturnType instanceof UnionType) {
82-
if (!$functionReturnType->isBoolean()->yes() || !$this->reportTooWideBool) {
83-
return [];
84-
}
83+
$functionReturnType = $this->findTypeToCheck($nativeFunctionReturnType, $phpdocFunctionReturnType, $scope);
84+
if ($functionReturnType === null) {
85+
return [];
8586
}
87+
8688
$statementResult = $node->getStatementResult();
8789
if ($statementResult->hasYield()) {
8890
return [];
@@ -178,4 +180,46 @@ public function checkAnonymousFunction(
178180
return $messages;
179181
}
180182

183+
/**
184+
* Returns null when type should not be checked, e.g. because it would be too annoying.
185+
*/
186+
public function findTypeToCheck(
187+
Type $nativeType,
188+
Type $phpdocType,
189+
Scope $scope,
190+
): ?Type
191+
{
192+
$combinedType = TypeUtils::resolveLateResolvableTypes(TypehintHelper::decideType($nativeType, $phpdocType));
193+
if ($combinedType instanceof UnionType) {
194+
return $combinedType;
195+
}
196+
197+
if (!$this->reportTooWideBool) {
198+
return null;
199+
}
200+
201+
if (
202+
$phpdocType->isBoolean()->yes()
203+
) {
204+
if (
205+
!$phpdocType->isTrue()->yes()
206+
&& !$phpdocType->isFalse()->yes()
207+
) {
208+
return $combinedType;
209+
}
210+
} elseif (
211+
$scope->getPhpVersion()->supportsTrueAndFalseStandaloneType()->yes()
212+
&& $nativeType->isBoolean()->yes()
213+
) {
214+
if (
215+
!$nativeType->isTrue()->yes()
216+
&& !$nativeType->isFalse()->yes()
217+
) {
218+
return $combinedType;
219+
}
220+
}
221+
222+
return null;
223+
}
224+
181225
}

tests/PHPStan/Rules/TooWideTypehints/TooWideFunctionReturnTypehintRuleTest.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use PHPStan\Rules\Rule;
66
use PHPStan\Testing\RuleTestCase;
7+
use PHPUnit\Framework\Attributes\RequiresPhp;
78

89
/**
910
* @extends RuleTestCase<TooWideFunctionReturnTypehintRule>
@@ -68,7 +69,8 @@ public function testBug10312a(): void
6869
$this->analyse([__DIR__ . '/data/bug-10312a.php'], []);
6970
}
7071

71-
public function testBug13384c(): void
72+
#[RequiresPhp('>= 8.2')]
73+
public function testBug13384cPhp82(): void
7274
{
7375
$this->reportTooWideBool = true;
7476
$this->analyse([__DIR__ . '/data/bug-13384c.php'], [
@@ -80,6 +82,30 @@ public function testBug13384c(): void
8082
'Function Bug13384c\doFoo2() never returns false so it can be removed from the return type.',
8183
9,
8284
],
85+
[
86+
'Function Bug13384c\doFooPhpdoc() never returns false so it can be removed from the return type.',
87+
93,
88+
],
89+
[
90+
'Function Bug13384c\doFooPhpdoc2() never returns true so it can be removed from the return type.',
91+
100,
92+
],
93+
]);
94+
}
95+
96+
#[RequiresPhp('< 8.2')]
97+
public function testBug13384cPrePhp82(): void
98+
{
99+
$this->reportTooWideBool = true;
100+
$this->analyse([__DIR__ . '/data/bug-13384c.php'], [
101+
[
102+
'Function Bug13384c\doFooPhpdoc() never returns false so it can be removed from the return type.',
103+
93,
104+
],
105+
[
106+
'Function Bug13384c\doFooPhpdoc2() never returns true so it can be removed from the return type.',
107+
100,
108+
],
83109
]);
84110
}
85111

tests/PHPStan/Rules/TooWideTypehints/TooWideMethodReturnTypehintRuleTest.php

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ public function testBug10312d(): void
220220
$this->analyse([__DIR__ . '/data/bug-10312d.php'], []);
221221
}
222222

223+
#[RequiresPhp('>= 8.2')]
223224
public function testBug13384c(): void
224225
{
225226
$this->reportTooWideBool = true;
@@ -232,13 +233,37 @@ public function testBug13384c(): void
232233
'Method Bug13384c\Bug13384c::doBar2() never returns false so it can be removed from the return type.',
233234
37,
234235
],
236+
[
237+
'Method Bug13384c\Bug13384c::doBarPhpdoc() never returns false so it can be removed from the return type.',
238+
55,
239+
],
235240
[
236241
'Method Bug13384c\Bug13384Static::doBar() never returns true so it can be removed from the return type.',
237-
50,
242+
62,
238243
],
239244
[
240245
'Method Bug13384c\Bug13384Static::doBar2() never returns false so it can be removed from the return type.',
241-
54,
246+
66,
247+
],
248+
[
249+
'Method Bug13384c\Bug13384Static::doBarPhpdoc() never returns false so it can be removed from the return type.',
250+
84,
251+
],
252+
]);
253+
}
254+
255+
#[RequiresPhp('< 8.2')]
256+
public function testBug13384cPrePhp82(): void
257+
{
258+
$this->reportTooWideBool = true;
259+
$this->analyse([__DIR__ . '/data/bug-13384c.php'], [
260+
[
261+
'Method Bug13384c\Bug13384c::doBarPhpdoc() never returns false so it can be removed from the return type.',
262+
55,
263+
],
264+
[
265+
'Method Bug13384c\Bug13384Static::doBarPhpdoc() never returns false so it can be removed from the return type.',
266+
84,
242267
],
243268
]);
244269
}

tests/PHPStan/Rules/TooWideTypehints/TooWidePropertyTypeRuleTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ protected function getRule(): Rule
2222
new DirectReadWritePropertiesExtensionProvider([]),
2323
new PropertyReflectionFinder(),
2424
new TooWideTypeCheck($this->reportTooWideBool),
25-
$this->reportTooWideBool,
2625
);
2726
}
2827

tests/PHPStan/Rules/TooWideTypehints/data/bug-13384c.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ private function doBar3(): bool {
4444
}
4545
return false;
4646
}
47+
48+
private function doBarMixed() {
49+
return true;
50+
}
51+
52+
/**
53+
* @return bool
54+
*/
55+
private function doBarPhpdoc() {
56+
return true;
57+
}
58+
4759
}
4860

4961
class Bug13384Static {
@@ -61,4 +73,34 @@ private static function doBar3(): bool {
6173
}
6274
return false;
6375
}
76+
77+
private static function doBarMixed() {
78+
return true;
79+
}
80+
81+
/**
82+
* @return bool
83+
*/
84+
private static function doBarPhpdoc() {
85+
return true;
86+
}
87+
88+
}
89+
90+
/**
91+
* @return bool
92+
*/
93+
function doFooPhpdoc() {
94+
return true;
95+
}
96+
97+
/**
98+
* @return bool
99+
*/
100+
function doFooPhpdoc2() {
101+
return false;
102+
}
103+
104+
function doFooMixed() {
105+
return true;
64106
}

0 commit comments

Comments
 (0)