Skip to content

Commit ed49747

Browse files
Decorate with reason
1 parent d58874e commit ed49747

6 files changed

+72
-46
lines changed

src/Rules/Comparison/ConstantConditionRuleHelper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public function shouldSkip(Scope $scope, Expr $expr): bool
6464
|| $expr instanceof MethodCall
6565
|| $expr instanceof Expr\StaticCall
6666
) {
67-
$isAlways = $this->impossibleCheckTypeHelper->findSpecifiedType($scope, $expr);
67+
$isAlways = $this->impossibleCheckTypeHelper->findSpecifiedType($scope, $expr)[0];
6868
if ($isAlways !== null) {
6969
return true;
7070
}

src/Rules/Comparison/ImpossibleCheckTypeFunctionCallRule.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,21 @@ public function processNode(Node $node, Scope $scope): array
3737
}
3838

3939
$functionName = (string) $node->name;
40-
$isAlways = $this->impossibleCheckTypeHelper->findSpecifiedType($scope, $node);
40+
[$isAlways, $reasons] = $this->impossibleCheckTypeHelper->findSpecifiedType($scope, $node);
4141
if ($isAlways === null) {
4242
return [];
4343
}
4444

45-
$addTip = function (RuleErrorBuilder $ruleErrorBuilder) use ($scope, $node): RuleErrorBuilder {
45+
$addTip = function (RuleErrorBuilder $ruleErrorBuilder) use ($scope, $node, $reasons): RuleErrorBuilder {
46+
if (count($reasons) > 0) {
47+
return $ruleErrorBuilder->acceptsReasonsTip($reasons);
48+
}
49+
4650
if (!$this->treatPhpDocTypesAsCertain) {
4751
return $ruleErrorBuilder;
4852
}
4953

50-
$isAlways = $this->impossibleCheckTypeHelper->doNotTreatPhpDocTypesAsCertain()->findSpecifiedType($scope, $node);
54+
$isAlways = $this->impossibleCheckTypeHelper->doNotTreatPhpDocTypesAsCertain()->findSpecifiedType($scope, $node)[0];
5155
if ($isAlways !== null) {
5256
return $ruleErrorBuilder;
5357
}

src/Rules/Comparison/ImpossibleCheckTypeHelper.php

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,17 @@ public function __construct(
5353
{
5454
}
5555

56+
/**
57+
* @return array{bool|null, list<string>}
58+
*/
5659
public function findSpecifiedType(
5760
Scope $scope,
5861
Expr $node,
59-
): ?bool
62+
): array
6063
{
6164
if ($node instanceof FuncCall) {
6265
if ($node->isFirstClassCallable()) {
63-
return null;
66+
return [null, []];
6467
}
6568
$argsCount = count($node->getArgs());
6669
if ($node->name instanceof Node\Name) {
@@ -69,34 +72,34 @@ public function findSpecifiedType(
6972
$arg = $node->getArgs()[0]->value;
7073
$assertValue = ($this->treatPhpDocTypesAsCertain ? $scope->getType($arg) : $scope->getNativeType($arg))->toBoolean();
7174
if (!$assertValue instanceof ConstantBooleanType) {
72-
return null;
75+
return [null, []];
7376
}
7477

75-
return $assertValue->getValue();
78+
return [$assertValue->getValue(), []];
7679
}
7780
if (in_array($functionName, [
7881
'class_exists',
7982
'interface_exists',
8083
'trait_exists',
8184
'enum_exists',
8285
], true)) {
83-
return null;
86+
return [null, []];
8487
}
8588
if (in_array($functionName, ['count', 'sizeof'], true)) {
86-
return null;
89+
return [null, []];
8790
} elseif ($functionName === 'defined') {
88-
return null;
91+
return [null, []];
8992
} elseif ($functionName === 'array_search') {
90-
return null;
93+
return [null, []];
9194
} elseif ($functionName === 'in_array' && $argsCount >= 2) {
9295
$haystackArg = $node->getArgs()[1]->value;
9396
$haystackType = ($this->treatPhpDocTypesAsCertain ? $scope->getType($haystackArg) : $scope->getNativeType($haystackArg));
9497
if ($haystackType instanceof MixedType) {
95-
return null;
98+
return [null, []];
9699
}
97100

98101
if (!$haystackType->isArray()->yes()) {
99-
return null;
102+
return [null, []];
100103
}
101104

102105
$needleArg = $node->getArgs()[0]->value;
@@ -113,36 +116,36 @@ public function findSpecifiedType(
113116
|| $haystackType->getIterableValueType()->isEnum()->yes();
114117

115118
if (!$isStrictComparison) {
116-
return null;
119+
return [null, []];
117120
}
118121

119122
$valueType = $haystackType->getIterableValueType();
120123
$constantNeedleTypesCount = count($needleType->getFiniteTypes());
121124
$constantHaystackTypesCount = count($valueType->getFiniteTypes());
122-
$isNeedleSupertype = $needleType->isSuperTypeOf($valueType);
125+
$isNeedleSupertype = $needleType->isSuperTypeOfWithReason($valueType);
123126
if ($haystackType->isConstantArray()->no()) {
124127
if ($haystackType->isIterableAtLeastOnce()->yes()) {
125128
// In this case the generic implementation via typeSpecifier fails, because the argument types cannot be narrowed down.
126129
if ($constantNeedleTypesCount === 1 && $constantHaystackTypesCount === 1) {
127-
if ($isNeedleSupertype->yes()) {
128-
return true;
130+
if ($isNeedleSupertype->result->yes()) {
131+
return [true, $isNeedleSupertype->reasons];
129132
}
130-
if ($isNeedleSupertype->no()) {
131-
return false;
133+
if ($isNeedleSupertype->result->no()) {
134+
return [false, $isNeedleSupertype->reasons];
132135
}
133136
}
134137

135-
return null;
138+
return [null, []];
136139
}
137140
}
138141

139142
if (!$haystackType instanceof ConstantArrayType || count($haystackType->getValueTypes()) > 0) {
140143
$haystackArrayTypes = $haystackType->getArrays();
141144
if (count($haystackArrayTypes) === 1 && $haystackArrayTypes[0]->getIterableValueType() instanceof NeverType) {
142-
return null;
145+
return [null, []];
143146
}
144147

145-
if ($isNeedleSupertype->maybe() || $isNeedleSupertype->yes()) {
148+
if ($isNeedleSupertype->result->maybe() || $isNeedleSupertype->result->yes()) {
146149
foreach ($haystackArrayTypes as $haystackArrayType) {
147150
if ($haystackArrayType instanceof ConstantArrayType) {
148151
foreach ($haystackArrayType->getValueTypes() as $i => $haystackArrayValueType) {
@@ -164,18 +167,18 @@ public function findSpecifiedType(
164167
}
165168
}
166169

167-
return null;
170+
return [null, []];
168171
}
169172
}
170173

171-
if ($isNeedleSupertype->yes()) {
174+
if ($isNeedleSupertype->result->yes()) {
172175
$hasConstantNeedleTypes = $constantNeedleTypesCount > 0;
173176
$hasConstantHaystackTypes = $constantHaystackTypesCount > 0;
174177
if (
175178
(!$hasConstantNeedleTypes && !$hasConstantHaystackTypes)
176179
|| $hasConstantNeedleTypes !== $hasConstantHaystackTypes
177180
) {
178-
return null;
181+
return [null, []];
179182
}
180183
}
181184
}
@@ -186,7 +189,7 @@ public function findSpecifiedType(
186189
if ($objectType instanceof ConstantStringType
187190
&& !$this->reflectionProvider->hasClass($objectType->getValue())
188191
) {
189-
return false;
192+
return [false, []];
190193
}
191194

192195
$methodArg = $node->getArgs()[1]->value;
@@ -199,11 +202,11 @@ public function findSpecifiedType(
199202

200203
if ($objectType->getObjectClassNames() !== []) {
201204
if ($objectType->hasMethod($methodType->getValue())->yes()) {
202-
return true;
205+
return [true, []];
203206
}
204207

205208
if ($objectType->hasMethod($methodType->getValue())->no()) {
206-
return false;
209+
return [false, []];
207210
}
208211
}
209212

@@ -219,15 +222,15 @@ public function findSpecifiedType(
219222

220223
if ($genericType instanceof TypeWithClassName) {
221224
if ($genericType->hasMethod($methodType->getValue())->yes()) {
222-
return true;
225+
return [true, []];
223226
}
224227

225228
$classReflection = $genericType->getClassReflection();
226229
if (
227230
$classReflection !== null
228231
&& $classReflection->isFinal()
229232
&& $genericType->hasMethod($methodType->getValue())->no()) {
230-
return false;
233+
return [false, []];
231234
}
232235
}
233236
}
@@ -240,7 +243,7 @@ public function findSpecifiedType(
240243

241244
// don't validate types on overwrite
242245
if ($specifiedTypes->shouldOverwrite()) {
243-
return null;
246+
return [null, []];
244247
}
245248

246249
$sureTypes = $specifiedTypes->getSureTypes();
@@ -249,15 +252,15 @@ public function findSpecifiedType(
249252
$rootExpr = $specifiedTypes->getRootExpr();
250253
if ($rootExpr !== null) {
251254
if (self::isSpecified($typeSpecifierScope, $node, $rootExpr)) {
252-
return null;
255+
return [null, []];
253256
}
254257

255258
$rootExprType = ($this->treatPhpDocTypesAsCertain ? $scope->getType($rootExpr) : $scope->getNativeType($rootExpr));
256259
if ($rootExprType instanceof ConstantBooleanType) {
257-
return $rootExprType->getValue();
260+
return [$rootExprType->getValue(), []];
258261
}
259262

260-
return null;
263+
return [null, []];
261264
}
262265

263266
$results = [];
@@ -277,7 +280,12 @@ public function findSpecifiedType(
277280
/** @var Type $resultType */
278281
$resultType = $sureType[1];
279282

280-
$results[] = $resultType->isSuperTypeOf($argumentType);
283+
$isSuperType = $resultType->isSuperTypeOfWithReason($argumentType);
284+
if ($isSuperType->result->no()) {
285+
return [false, $isSuperType->reasons];
286+
}
287+
288+
$results[] = $isSuperType->result;
281289
}
282290

283291
foreach ($sureNotTypes as $sureNotType) {
@@ -295,15 +303,20 @@ public function findSpecifiedType(
295303
/** @var Type $resultType */
296304
$resultType = $sureNotType[1];
297305

298-
$results[] = $resultType->isSuperTypeOf($argumentType)->negate();
306+
$isSuperType = $resultType->isSuperTypeOfWithReason($argumentType);
307+
if ($isSuperType->result->yes()) {
308+
return [false, $isSuperType->reasons];
309+
}
310+
311+
$results[] = $isSuperType->result->negate();
299312
}
300313

301314
if (count($results) === 0) {
302-
return null;
315+
return [null, []];
303316
}
304317

305318
$result = TrinaryLogic::createYes()->and(...$results);
306-
return $result->maybe() ? null : $result->yes();
319+
return $result->maybe() ? [null, []] : [$result->yes(), []];
307320
}
308321

309322
private static function isSpecified(Scope $scope, Expr $node, Expr $expr): bool

src/Rules/Comparison/ImpossibleCheckTypeMethodCallRule.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,21 @@ public function processNode(Node $node, Scope $scope): array
3939
return [];
4040
}
4141

42-
$isAlways = $this->impossibleCheckTypeHelper->findSpecifiedType($scope, $node);
42+
[$isAlways, $reasons] = $this->impossibleCheckTypeHelper->findSpecifiedType($scope, $node);
4343
if ($isAlways === null) {
4444
return [];
4545
}
4646

47-
$addTip = function (RuleErrorBuilder $ruleErrorBuilder) use ($scope, $node): RuleErrorBuilder {
47+
$addTip = function (RuleErrorBuilder $ruleErrorBuilder) use ($scope, $node, $reasons): RuleErrorBuilder {
48+
if (count($reasons)) {
49+
return $ruleErrorBuilder->acceptsReasonsTip($reasons);
50+
}
51+
4852
if (!$this->treatPhpDocTypesAsCertain) {
4953
return $ruleErrorBuilder;
5054
}
5155

52-
$isAlways = $this->impossibleCheckTypeHelper->doNotTreatPhpDocTypesAsCertain()->findSpecifiedType($scope, $node);
56+
$isAlways = $this->impossibleCheckTypeHelper->doNotTreatPhpDocTypesAsCertain()->findSpecifiedType($scope, $node)[0];
5357
if ($isAlways !== null) {
5458
return $ruleErrorBuilder;
5559
}

src/Rules/Comparison/ImpossibleCheckTypeStaticMethodCallRule.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,22 @@ public function processNode(Node $node, Scope $scope): array
3939
return [];
4040
}
4141

42-
$isAlways = $this->impossibleCheckTypeHelper->findSpecifiedType($scope, $node);
42+
[$isAlways, $reasons] = $this->impossibleCheckTypeHelper->findSpecifiedType($scope, $node);
4343
if ($isAlways === null) {
4444
return [];
4545
}
4646

47-
$addTip = function (RuleErrorBuilder $ruleErrorBuilder) use ($scope, $node): RuleErrorBuilder {
47+
$addTip = function (RuleErrorBuilder $ruleErrorBuilder) use ($scope, $node, $reasons): RuleErrorBuilder {
48+
if (count($reasons) > 0) {
49+
var_dump($reasons);die;
50+
return $ruleErrorBuilder->acceptsReasonsTip($reasons);
51+
}
52+
4853
if (!$this->treatPhpDocTypesAsCertain) {
4954
return $ruleErrorBuilder;
5055
}
5156

52-
$isAlways = $this->impossibleCheckTypeHelper->doNotTreatPhpDocTypesAsCertain()->findSpecifiedType($scope, $node);
57+
$isAlways = $this->impossibleCheckTypeHelper->doNotTreatPhpDocTypesAsCertain()->findSpecifiedType($scope, $node)[0];
5358
if ($isAlways !== null) {
5459
return $ruleErrorBuilder;
5560
}

src/Type/Php/TypeSpecifyingFunctionsDynamicReturnTypeExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public function getTypeFromFunctionCall(
5757
$isAlways = $this->getHelper()->findSpecifiedType(
5858
$scope,
5959
$functionCall,
60-
);
60+
)[0];
6161
if ($isAlways === null) {
6262
return null;
6363
}

0 commit comments

Comments
 (0)