Skip to content

Commit 3ed21df

Browse files
committed
Add support for self-referencing constants
1 parent de3720d commit 3ed21df

File tree

3 files changed

+124
-19
lines changed

3 files changed

+124
-19
lines changed

src/Rules/Traits/ConflictingTraitConstantsRule.php

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -224,26 +224,58 @@ private function processSingleConstant(ClassReflection $classReflection, Reflect
224224
}
225225

226226
$classConstantValueType = $this->initializerExprTypeResolver->getType($valueExpr, InitializerExprContext::fromClassReflection($classReflection));
227-
$traitConstantValueType = $this->initializerExprTypeResolver->getType(
228-
$traitConstant->getValueExpression(),
229-
InitializerExprContext::fromClass(
230-
$traitDeclaringClass->getName(),
231-
$traitDeclaringClass->getFileName() !== false ? $traitDeclaringClass->getFileName() : null,
232-
),
227+
$traitValueExpr = $traitConstant->getValueExpression();
228+
$isTraitSelfReferencing = (
229+
$traitValueExpr instanceof Node\Expr\ClassConstFetch
230+
&& $traitValueExpr->class->name === 'self'
231+
&& $traitValueExpr->name->name === $traitConstant->getName()
233232
);
234-
if (!$classConstantValueType->equals($traitConstantValueType)) {
235-
$errors[] = RuleErrorBuilder::message(sprintf(
236-
'Constant %s::%s with value %s overriding constant %s::%s with different value %s should have the same value.',
237-
$classReflection->getDisplayName(),
238-
$traitConstant->getName(),
239-
$classConstantValueType->describe(VerbosityLevel::value()),
240-
$traitConstant->getDeclaringClass()->getName(),
241-
$traitConstant->getName(),
242-
$traitConstantValueType->describe(VerbosityLevel::value()),
243-
))
244-
->nonIgnorable()
245-
->identifier('classConstant.value')
246-
->build();
233+
if ($isTraitSelfReferencing) {
234+
$isValueSelfReference = (
235+
$valueExpr instanceof Node\Expr\ClassConstFetch
236+
&& $valueExpr->class->name === 'self'
237+
&& $valueExpr->name->name === $traitConstant->getName()
238+
);
239+
if (
240+
!$isValueSelfReference
241+
&& isset($classConstantValueType)
242+
&& !$classConstantValueType instanceof $constantNativeTypeType
243+
&& $classConstantValueType->isSuperTypeOf($constantNativeTypeType)->no()
244+
) {
245+
$errors[] = RuleErrorBuilder::message(sprintf(
246+
'Constant %s::%s with value %s cannot override native type of constant %s::%s.',
247+
$classReflection->getDisplayName(),
248+
$traitConstant->getName(),
249+
$classConstantValueType->describe(VerbosityLevel::value()),
250+
$traitConstant->getDeclaringClass()->getName(),
251+
$traitConstant->getName(),
252+
))
253+
->nonIgnorable()
254+
->identifier('classConstant.value')
255+
->build();
256+
}
257+
} else {
258+
$traitConstantValueType = $this->initializerExprTypeResolver->getType(
259+
$traitValueExpr,
260+
InitializerExprContext::fromClass(
261+
$traitDeclaringClass->getName(),
262+
$traitDeclaringClass->getFileName() !== false ? $traitDeclaringClass->getFileName() : null,
263+
),
264+
);
265+
if (!$classConstantValueType->equals($traitConstantValueType)) {
266+
$errors[] = RuleErrorBuilder::message(sprintf(
267+
'Constant %s::%s with value %s overriding constant %s::%s with different value %s should have the same value.',
268+
$classReflection->getDisplayName(),
269+
$traitConstant->getName(),
270+
$classConstantValueType->describe(VerbosityLevel::value()),
271+
$traitConstant->getDeclaringClass()->getName(),
272+
$traitConstant->getName(),
273+
$traitConstantValueType->describe(VerbosityLevel::value()),
274+
))
275+
->nonIgnorable()
276+
->identifier('classConstant.value')
277+
->build();
278+
}
247279
}
248280

249281
return $errors;

tests/PHPStan/Rules/Traits/ConflictingTraitConstantsRuleTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ public function testNativeTypes(): void
7878
'Constant ConflictingTraitConstantsTypes\Lorem::FOO_CONST overriding constant ConflictingTraitConstantsTypes\Foo::FOO_CONST (int|string) should also have native type int|string.',
7979
39,
8080
],
81+
[
82+
'Constant ConflictingTraitConstantsTypes\SelfRefWrongType::SR_CONST with value array{1} cannot override native type of constant ConflictingTraitConstantsTypes\SelfRef::SR_CONST.',
83+
64,
84+
],
85+
[
86+
'Constant ConflictingTraitConstantsTypes\SelfRefExtWrong::SR_CONST (string) overriding constant ConflictingTraitConstantsTypes\SelfRef::SR_CONST (int) should have the same native type int.',
87+
82,
88+
],
89+
[
90+
'Constant ConflictingTraitConstantsTypes\SelfRefExtOverrideExt::SR_CONST with value mixed overriding constant ConflictingTraitConstantsTypes\SelfRefExtOverride::SR_CONST with different value 1 should have the same value.',
91+
100,
92+
],
8193
]);
8294
}
8395

tests/PHPStan/Rules/Traits/data/conflicting-trait-constants-types.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,64 @@ class Lorem
3939
public const FOO_CONST = 1;
4040

4141
}
42+
43+
trait SelfRef
44+
{
45+
46+
public const int SR_CONST = self::SR_CONST;
47+
48+
}
49+
50+
class SelfRefOverride
51+
{
52+
53+
use SelfRef;
54+
55+
public const int SR_CONST = 1;
56+
57+
}
58+
59+
class SelfRefWrongType
60+
{
61+
62+
use SelfRef;
63+
64+
public const int SR_CONST = [1];
65+
66+
}
67+
68+
class SelfRefExt
69+
{
70+
71+
use SelfRef;
72+
73+
public const int SR_CONST = self::SR_CONST;
74+
75+
}
76+
77+
class SelfRefExtWrong
78+
{
79+
80+
use SelfRef;
81+
82+
public const string SR_CONST = self::SR_CONST;
83+
84+
}
85+
86+
class SelfRefExtOverride
87+
{
88+
89+
use SelfRefExt;
90+
91+
public const int SR_CONST = 1;
92+
93+
}
94+
95+
class SelfRefExtOverrideExt
96+
{
97+
98+
use SelfRefExtOverride;
99+
100+
public const int SR_CONST = self::SR_CONST;
101+
102+
}

0 commit comments

Comments
 (0)