diff --git a/src/Psalm/Internal/BCHelper.php b/src/Psalm/Internal/BCHelper.php index 9cdd9df1673..e08e34bacbd 100644 --- a/src/Psalm/Internal/BCHelper.php +++ b/src/Psalm/Internal/BCHelper.php @@ -12,6 +12,28 @@ */ final class BCHelper { + private const CLASS_MAP = [ + Node\Expr\ArrayItem::class => Node\ArrayItem::class, + Node\Expr\ClosureUse::class => Node\ClosureUse::class, + Node\Scalar\DNumber::class => Node\Scalar\Float_::class, + Node\Scalar\Encapsed::class => Node\Scalar\InterpolatedString::class, + Node\Scalar\EncapsedStringPart::class => Node\InterpolatedStringPart::class, + Node\Scalar\LNumber::class => Node\Scalar\Int_::class, + Node\Stmt\DeclareDeclare::class => Node\DeclareItem::class, + Node\Stmt\PropertyProperty::class => Node\PropertyItem::class, + Node\Stmt\StaticVar::class => Node\StaticVar::class, + Node\Stmt\UseUse::class => Node\UseItem::class, + ]; + + public static function getPHPParserClassName(string $className): string + { + if (isset(self::CLASS_MAP[$className]) && class_exists(self::CLASS_MAP[$className])) { + return self::CLASS_MAP[$className]; + } + + return $className; + } + public static function usePHPParserV4(): bool { return class_exists('\PhpParser\Node\Stmt\Throw'); diff --git a/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php b/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php index 353bdb3f408..147f63a857a 100644 --- a/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php +++ b/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php @@ -4,6 +4,7 @@ use InvalidArgumentException; use LogicException; +use Psalm\Internal\BCHelper; use Psalm\Storage\ClassLikeStorage; use function array_merge; @@ -39,7 +40,7 @@ public function __construct(?ClassLikeStorageCacheProvider $cache = null) */ public function get(string $fq_classlike_name): ClassLikeStorage { - $fq_classlike_name_lc = strtolower($fq_classlike_name); + $fq_classlike_name_lc = $this->formatClassName($fq_classlike_name); /** @psalm-suppress ImpureStaticProperty Used only for caching */ if (!isset(self::$storage[$fq_classlike_name_lc])) { throw new InvalidArgumentException('Could not get class storage for ' . $fq_classlike_name_lc); @@ -49,12 +50,17 @@ public function get(string $fq_classlike_name): ClassLikeStorage return self::$storage[$fq_classlike_name_lc]; } + private function formatClassName(string $class): string + { + return strtolower(BCHelper::getPHPParserClassName($class)); + } + /** * @psalm-mutation-free */ public function has(string $fq_classlike_name): bool { - $fq_classlike_name_lc = strtolower($fq_classlike_name); + $fq_classlike_name_lc = $this->formatClassName($fq_classlike_name); /** @psalm-suppress ImpureStaticProperty Used only for caching */ return isset(self::$storage[$fq_classlike_name_lc]); @@ -62,7 +68,7 @@ public function has(string $fq_classlike_name): bool public function exhume(string $fq_classlike_name, string $file_path, string $file_contents): ClassLikeStorage { - $fq_classlike_name_lc = strtolower($fq_classlike_name); + $fq_classlike_name_lc = $this->formatClassName($fq_classlike_name); if (isset(self::$storage[$fq_classlike_name_lc])) { return self::$storage[$fq_classlike_name_lc]; @@ -112,7 +118,7 @@ public function makeNew(string $fq_classlike_name_lc): void public function create(string $fq_classlike_name): ClassLikeStorage { - $fq_classlike_name_lc = strtolower($fq_classlike_name); + $fq_classlike_name_lc = $this->formatClassName($fq_classlike_name); $storage = new ClassLikeStorage($fq_classlike_name); self::$storage[$fq_classlike_name_lc] = $storage; @@ -123,7 +129,7 @@ public function create(string $fq_classlike_name): ClassLikeStorage public function remove(string $fq_classlike_name): void { - $fq_classlike_name_lc = strtolower($fq_classlike_name); + $fq_classlike_name_lc = $this->formatClassName($fq_classlike_name); unset(self::$storage[$fq_classlike_name_lc]); } diff --git a/src/Psalm/Internal/Provider/StatementsProvider.php b/src/Psalm/Internal/Provider/StatementsProvider.php index e923d405327..eca9f658245 100644 --- a/src/Psalm/Internal/Provider/StatementsProvider.php +++ b/src/Psalm/Internal/Provider/StatementsProvider.php @@ -394,7 +394,17 @@ public static function parseStatements( if (!self::$lexer) { $major_version = Codebase::transformPhpVersionId($analysis_php_version_id, 10_000); $minor_version = Codebase::transformPhpVersionId($analysis_php_version_id % 10_000, 100); - self::$lexer = new Emulative(PhpVersion::fromComponents($major_version, $minor_version)); + + if (class_exists(PhpVersion::class)) { + self::$lexer = new Emulative(PhpVersion::fromComponents($major_version, $minor_version)); + } else { + self::$lexer = new Emulative([ + 'usedAttributes' => [ + 'comments', 'startLine', 'startFilePos', 'endFilePos', + ], + 'phpVersion' => $major_version . '.' . $minor_version, + ]); + } } if (!self::$parser) {