Skip to content

Commit

Permalink
Support $foo::BAR where $foo is declared as class-string<Foo> (#…
Browse files Browse the repository at this point in the history
…186)

Update ClassConstantUsages rule to not fail when accessing constant on class-string that can be resolved to appropriate class

Fixes #44 (partly, not the general `class-string` case)
  • Loading branch information
spaze authored Apr 25, 2023
2 parents ad3a6d0 + 0c080b6 commit 2baf1ae
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/RuleErrors/DisallowedConstantRuleErrors.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function get(string $constant, Scope $scope, ?string $displayName, array
$errorBuilder = RuleErrorBuilder::message(sprintf(
'Using %s%s is forbidden, %s',
$disallowedConstant->getConstant(),
$displayName && $displayName !== $disallowedConstant->getConstant() ? ' (as ' . $displayName . ')' : '',
$displayName && ltrim($displayName, '\\') !== $disallowedConstant->getConstant() ? ' (as ' . $displayName . ')' : '',
$disallowedConstant->getMessage()
));
if ($disallowedConstant->getErrorIdentifier()) {
Expand Down
11 changes: 6 additions & 5 deletions src/Usages/ClassConstantUsages.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ public function processNode(Node $node, Scope $scope): array
throw new ShouldNotHappenException(sprintf('$node->name should be %s but is %s', Identifier::class, get_class($node->name)));
}
$constant = (string)$node->name;
$usedOnType = $this->typeResolver->getType($node->class, $scope);
$type = $this->typeResolver->getType($node->class, $scope);
$usedOnType = $type->getObjectTypeOrClassStringObjectType();

if (strtolower($constant) === 'class') {
return [];
Expand All @@ -99,16 +100,16 @@ function (ConstantStringType $constantString): string {
$usedOnType->getConstantStrings()
);
} else {
if ($usedOnType->hasConstant($constant)->no()) {
if ($usedOnType->hasConstant($constant)->yes()) {
$classNames = [$usedOnType->getConstant($constant)->getDeclaringClass()->getDisplayName()];
} else {
return [
RuleErrorBuilder::message(sprintf(
'Cannot access constant %s on %s',
$constant,
$usedOnType->describe(VerbosityLevel::getRecommendedLevelByType($usedOnType))
$type->describe(VerbosityLevel::getRecommendedLevelByType($type))
))->build(),
];
} else {
$classNames = [$usedOnType->getConstant($constant)->getDeclaringClass()->getDisplayName()];
}
}
return $this->disallowedConstantRuleErrors->get($this->getFullyQualified($classNames, $constant), $scope, $displayName, $this->disallowedConstants);
Expand Down
4 changes: 2 additions & 2 deletions tests/Usages/ClassConstantInvalidUsagesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ public function testRule(): void
14,
],
[
'Cannot access constant UTC on class-string<DateTimeZone>',
18,
'Cannot access constant FTC on class-string<DateTimeZone>',
24,
],
]);
}
Expand Down
6 changes: 6 additions & 0 deletions tests/src/invalid/constantUsages.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
$monster = DateTime::class;
$monster::COOKIE;

// a valid type, for a change
/** @var class-string<DateTimeZone> $tz */
$tz = DateTimeZone::class;
$tz::UTC;

// this constant doesn't exist
/** @var class-string<DateTimeZone> $tz */
$tz = DateTimeZone::class;
$tz::FTC;

0 comments on commit 2baf1ae

Please sign in to comment.