Repository: spaze/phpstan-disallowed-calls (php)

Issue:
Support $foo::BAR where foo is declared as a string or a class-string
These should be supported
https://github.com/spaze/phpstan-disallowed-calls/blob/0c425fe1a84965e838770166bdfb4f2179785dbf/tests/src/invalid/constantUsages.php#L8-L14

But currently they aren't:
https://github.com/spaze/phpstan-disallowed-calls/blob/0c425fe1a84965e838770166bdfb4f2179785dbf/tests/ClassConstantInvalidUsagesTest.php#L29-L36

This is a follow-up to #42, which was fixed by #43 to just not crash.

Pull request:
Support `$foo::BAR` where `$foo` is declared as `class-string<Foo>`
This PR will:
- 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)

Patch:
diff --git a/src/RuleErrors/DisallowedConstantRuleErrors.php b/src/RuleErrors/DisallowedConstantRuleErrors.php
index 3d62669..0c67e03 100644
--- a/src/RuleErrors/DisallowedConstantRuleErrors.php
+++ b/src/RuleErrors/DisallowedConstantRuleErrors.php
@@ -39,7 +39,7 @@ class DisallowedConstantRuleErrors
 				$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()) {
diff --git a/src/Usages/ClassConstantUsages.php b/src/Usages/ClassConstantUsages.php
index 812e449..e06e213 100644
--- a/src/Usages/ClassConstantUsages.php
+++ b/src/Usages/ClassConstantUsages.php
@@ -84,7 +84,8 @@ class ClassConstantUsages implements Rule
 			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 [];
@@ -99,16 +100,16 @@ class ClassConstantUsages implements Rule
 				$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);


Test patch:
diff --git a/tests/Usages/ClassConstantInvalidUsagesTest.php b/tests/Usages/ClassConstantInvalidUsagesTest.php
index 67f9c42..a6879af 100644
--- a/tests/Usages/ClassConstantInvalidUsagesTest.php
+++ b/tests/Usages/ClassConstantInvalidUsagesTest.php
@@ -50,8 +50,8 @@ class ClassConstantInvalidUsagesTest extends RuleTestCase
 				14,
 			],
 			[
-				'Cannot access constant UTC on class-string<DateTimeZone>',
-				18,
+				'Cannot access constant FTC on class-string<DateTimeZone>',
+				24,
 			],
 		]);
 	}
diff --git a/tests/src/invalid/constantUsages.php b/tests/src/invalid/constantUsages.php
index b73b615..0205db9 100644
--- a/tests/src/invalid/constantUsages.php
+++ b/tests/src/invalid/constantUsages.php
@@ -13,6 +13,12 @@ $monster::COOKIE;
 $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;