Different Code parts depending on other packages show different errors
Using Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface
before, getTypes was used to return a List of in this file imported LegacyType
with Symfony 7.1, a new method getType was added to return new Component https://symfony.com/doc/current/components/type_info.html
if (method_exists($this->extractor, 'getType') && class_exists(Type::class)) {
$type = $this->extractor->getType($class, $property);
if (null === $type) {
return null;
}
if ($type->isIdentifiedBy(CronExpression::class)) {
return new TypeGuess(CronExpressionType::class, [], Guess::VERY_HIGH_CONFIDENCE);
}
} else {
$types = $this->extractor->getTypes($class, $property);
if (null === $types) {
return null;
}
foreach ($types as $lType) {
if (LegacyType::BUILTIN_TYPE_OBJECT === $lType->getBuiltinType() &&
CronExpression::class === $lType->getClassName()) {
return new TypeGuess(CronExpressionType::class, [], Guess::VERY_HIGH_CONFIDENCE);
}
}
}
right now, the psalm tests for Symfony 5.4 and 6.4 complain about the first part:
Error: src/Form/TypeGuesser/CronExpressionTypeGuesser.php:41:13: MixedAssignment: Unable to determine the type that $type is being assigned to (see https://psalm.dev/032)
Error: src/Form/TypeGuesser/CronExpressionTypeGuesser.php:45:24: MixedMethodCall: Cannot determine the type of $type when calling method isIdentifiedBy (see https://psalm.dev/015)
while the newer for Symfony 7.3 complain about the second part:
Error: src/Form/TypeGuesser/CronExpressionTypeGuesser.php:45:22: DeprecatedClass: Class Symfony\Component\PropertyInfo\Type is marked as deprecated (see https://psalm.dev/098)
Error: src/Form/TypeGuesser/CronExpressionTypeGuesser.php:45:40: DeprecatedMethod: The method Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface::getTypes has been marked as deprecated (see https://psalm.dev/001)
Error: src/Form/TypeGuesser/CronExpressionTypeGuesser.php:50:21: DeprecatedClass: Class Symfony\Component\PropertyInfo\Type is deprecated (see https://psalm.dev/098)
How can i tell psalm which part it should use? (i tried adding class_exists(Type::class) but it seems to be ignored)
Hey @Hanmac, can you reproduce the issue on https://psalm.dev? These will be used as phpunit tests when implementing the feature or fixing this bug.
Smyfony below 7.3 (mixed asignment/undefined docblock class) https://psalm.dev/r/dbe7e02a86
Symfony more than 7.3 (DeprecatedClass/DeprecatedMethod) https://psalm.dev/r/3915bdd186
I found these snippets:
https://psalm.dev/r/dbe7e02a86
<?php
class CronExpression
{
};
class LegacyType
{
const string BUILTIN_TYPE_OBJECT = "object";
public function getBuiltinType(): string
{
return self::BUILTIN_TYPE_OBJECT;
}
public function getClassName(): string
{
return CronExpression::class;
}
}
/*
class Type
{
public function isIdentifiedBy(string $class): bool
{
return true;
}
}
//*/
/**
* Type Extractor Interface.
*
* @author Kévin Dunglas <[email protected]>
*
* @method Type|null getType(string $class, string $property, array $context = [])
*/
interface PropertyTypeExtractorInterface
{
#public function getType(string $class, string $property, array $context = []): Type|null;
/**
* Gets types of a property.
*
* @return LegacyType[]|null
*/
public function getTypes(string $class, string $property, array $context = []): ?array;
}
class PropertyInfoExtractor implements PropertyTypeExtractorInterface
{
#public function getType(string $class, string $property, array $context = []): Type|null { return null; }
public function getTypes(string $class, string $property, array $context = []): ?array
{
return[];
}
}
final class CronExpressionTypeGuesser
{
private PropertyTypeExtractorInterface $extractor;
public function __construct(?PropertyTypeExtractorInterface $extractor = null)
{
$this->extractor = $extractor ?? $this->createExtractor();
}
public function guessType(string $class, string $property): ?bool
{
if (!class_exists($class)) {
return null;
}
if (class_exists(Type::class) && method_exists($this->extractor, 'getType')) {
$type = $this->extractor->getType($class, $property);
if (null === $type) {
return null;
}
if ($type->isIdentifiedBy(CronExpression::class)) {
return true;
}
} else {
$types = $this->extractor->getTypes($class, $property);
if (null === $types) {
return null;
}
foreach ($types as $lType) {
if (LegacyType::BUILTIN_TYPE_OBJECT === $lType->getBuiltinType() &&
CronExpression::class === $lType->getClassName()) {
return true;
}
}
}
return null;
}
private function createExtractor(): PropertyTypeExtractorInterface
{
return new PropertyInfoExtractor();
}
}
Psalm output (using commit cdceda0):
ERROR: UndefinedDocblockClass - 77:17 - Docblock-defined class, interface or enum named Type does not exist
https://psalm.dev/r/3915bdd186
<?php
class CronExpression
{
};
/**
* @deprecated
*/
class LegacyType
{
const string BUILTIN_TYPE_OBJECT = "object";
public function getBuiltinType(): string
{
return self::BUILTIN_TYPE_OBJECT;
}
public function getClassName(): string
{
return CronExpression::class;
}
}
//*
class Type
{
public function isIdentifiedBy(string $class): bool
{
return true;
}
}
//*/
/**
* Type Extractor Interface.
*
* @author Kévin Dunglas <[email protected]>
*
* @method Type|null getType(string $class, string $property, array $context = [])
*/
interface PropertyTypeExtractorInterface
{
#public function getType(string $class, string $property, array $context = []): Type|null;
/**
* Gets types of a property.
*
* @deprecated since Symfony 7.3, use "getType" instead
*
* @return LegacyType[]|null
*/
public function getTypes(string $class, string $property, array $context = []): ?array;
}
class PropertyInfoExtractor implements PropertyTypeExtractorInterface
{
public function getType(string $class, string $property, array $context = []): Type|null { return null; }
public function getTypes(string $class, string $property, array $context = []): ?array
{
return[];
}
}
final class CronExpressionTypeGuesser
{
private PropertyTypeExtractorInterface $extractor;
public function __construct(?PropertyTypeExtractorInterface $extractor = null)
{
$this->extractor = $extractor ?? $this->createExtractor();
}
public function guessType(string $class, string $property): ?bool
{
if (!class_exists($class)) {
return null;
}
if (class_exists(Type::class) && method_exists($this->extractor, 'getType')) {
$type = $this->extractor->getType($class, $property);
if (null === $type) {
return null;
}
if ($type->isIdentifiedBy(CronExpression::class)) {
return true;
}
} else {
$types = $this->extractor->getTypes($class, $property);
if (null === $types) {
return null;
}
foreach ($types as $lType) {
if (LegacyType::BUILTIN_TYPE_OBJECT === $lType->getBuiltinType() &&
CronExpression::class === $lType->getClassName()) {
return true;
}
}
}
return null;
}
private function createExtractor(): PropertyTypeExtractorInterface
{
return new PropertyInfoExtractor();
}
}
Psalm output (using commit cdceda0):
ERROR: DeprecatedClass - 49:16 - Class LegacyType is marked as deprecated
INFO: DeprecatedMethod - 86:40 - The method PropertyTypeExtractorInterface::getTypes has been marked as deprecated
ERROR: DeprecatedClass - 91:21 - Class LegacyType is deprecated