Skip to content

Commit 996bc69

Browse files
committed
ApiInstanceofRule - report "maybe" on @api-covered classes
1 parent ff4d02d commit 996bc69

File tree

3 files changed

+59
-1
lines changed

3 files changed

+59
-1
lines changed

src/Rules/Api/ApiInstanceofRule.php

+25-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\ClassReflection;
78
use PHPStan\Reflection\ReflectionProvider;
89
use PHPStan\Rules\Rule;
10+
use PHPStan\Rules\RuleError;
911
use PHPStan\Rules\RuleErrorBuilder;
12+
use PHPStan\Type\Constant\ConstantBooleanType;
1013
use function count;
1114
use function sprintf;
1215

@@ -61,11 +64,32 @@ public function processNode(Node $node, Scope $scope): array
6164
foreach ($docBlock->getPhpDocNodes() as $phpDocNode) {
6265
$apiTags = $phpDocNode->getTagsByName('@api');
6366
if (count($apiTags) > 0) {
64-
return [];
67+
return $this->processCoveredClass($node, $scope, $classReflection);
6568
}
6669
}
6770

6871
return [$ruleError];
6972
}
7073

74+
/**
75+
* @return RuleError[]
76+
*/
77+
private function processCoveredClass(Node\Expr\Instanceof_ $node, Scope $scope, ClassReflection $classReflection): array
78+
{
79+
$instanceofType = $scope->getType($node);
80+
if ($instanceofType instanceof ConstantBooleanType) {
81+
return [];
82+
}
83+
84+
return [
85+
RuleErrorBuilder::message(sprintf(
86+
'Although %s is covered by backward compatibility promise, this instanceof assumption might break because it\'s not guaranteed to always stay the same.',
87+
$classReflection->getDisplayName(),
88+
))->tip(sprintf(
89+
"In case of questions how to solve this correctly, open a discussion:\n %s\n\n See also:\n https://phpstan.org/developing-extensions/backward-compatibility-promise",
90+
'https://github.com/phpstan/phpstan/discussions',
91+
))->build(),
92+
];
93+
}
94+
7195
}

tests/PHPStan/Rules/Api/ApiInstanceofRuleTest.php

+14
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,27 @@ public function testRuleOutOfPhpStan(): void
2828
"If you think it should be covered by backward compatibility promise, open a discussion:\n %s\n\n See also:\n https://phpstan.org/developing-extensions/backward-compatibility-promise",
2929
'https://github.com/phpstan/phpstan/discussions',
3030
);
31+
$instanceofTip = sprintf(
32+
"In case of questions how to solve this correctly, open a discussion:\n %s\n\n See also:\n https://phpstan.org/developing-extensions/backward-compatibility-promise",
33+
'https://github.com/phpstan/phpstan/discussions',
34+
);
3135

3236
$this->analyse([__DIR__ . '/data/instanceof-out-of-phpstan.php'], [
37+
[
38+
'Although PHPStan\Reflection\ClassReflection is covered by backward compatibility promise, this instanceof assumption might break because it\'s not guaranteed to always stay the same.',
39+
13,
40+
$instanceofTip,
41+
],
3342
[
3443
'Asking about instanceof PHPStan\Reflection\BetterReflection\SourceLocator\AutoloadSourceLocator is not covered by backward compatibility promise. The class might change in a minor PHPStan version.',
3544
17,
3645
$tip,
3746
],
47+
[
48+
'Although PHPStan\Reflection\ClassReflection is covered by backward compatibility promise, this instanceof assumption might break because it\'s not guaranteed to always stay the same.',
49+
37,
50+
$instanceofTip,
51+
],
3852
]);
3953
}
4054

tests/PHPStan/Rules/Api/data/instanceof-out-of-phpstan.php

+20
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,23 @@ public function doFoo(object $o): void
2020
}
2121

2222
}
23+
24+
class Bar
25+
{
26+
27+
public function doBar(ClassReflection $classReflection, object $o): void
28+
{
29+
if ($classReflection instanceof ClassReflection) { // yes - do not report
30+
31+
}
32+
33+
if ($classReflection instanceof ClassReflection) { // no - do not report
34+
35+
}
36+
37+
if ($o instanceof ClassReflection) { // maybe - report
38+
39+
}
40+
}
41+
42+
}

0 commit comments

Comments
 (0)