1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Fix #2739 - specify wildcards in constants

This commit is contained in:
Brown 2020-02-07 10:54:57 -05:00
parent cecc5ed798
commit dcc855de95
4 changed files with 98 additions and 14 deletions

View File

@ -1311,22 +1311,53 @@ class ExpressionAnalyzer
return new Type\Atomic\TLiteralClassString($return_type->fq_classlike_name);
}
try {
$class_constant = $codebase->classlikes->getConstantForClass(
$return_type->fq_classlike_name,
$return_type->const_name,
\ReflectionProperty::IS_PRIVATE
);
} catch (\Psalm\Exception\CircularReferenceException $e) {
$class_constant = null;
if (strpos($return_type->const_name, '*') !== false) {
$class_storage = $codebase->classlike_storage_provider->get($return_type->fq_classlike_name);
$matching_constants = \array_keys($class_storage->class_constant_locations);
$const_name_part = \substr($return_type->const_name, 0, -1);
if ($const_name_part) {
$matching_constants = \array_filter(
$matching_constants,
function ($constant_name) use ($const_name_part) {
return $constant_name !== $const_name_part
&& \strpos($constant_name, $const_name_part) === 0;
}
);
}
} else {
$matching_constants = [$return_type->const_name];
}
if ($class_constant) {
if ($class_constant->isSingle()) {
$class_constant = clone $class_constant;
$matching_constant_types = [];
return array_values($class_constant->getAtomicTypes())[0];
foreach ($matching_constants as $matching_constant) {
try {
$class_constant = $codebase->classlikes->getConstantForClass(
$return_type->fq_classlike_name,
$matching_constant,
\ReflectionProperty::IS_PRIVATE
);
} catch (\Psalm\Exception\CircularReferenceException $e) {
$class_constant = null;
}
if ($class_constant) {
if ($class_constant->isSingle()) {
$class_constant = clone $class_constant;
$matching_constant_types = \array_merge(
\array_values($class_constant->getAtomicTypes()),
$matching_constant_types
);
}
}
}
if ($matching_constant_types) {
return $matching_constant_types;
}
}

View File

@ -547,7 +547,7 @@ class ParseTree
$nexter_token = $i + 2 < $c ? $type_tokens[$i + 2] : null;
if (!$nexter_token
|| (!preg_match('/^[a-zA-Z_][a-zA-Z_0-9]*$/', $nexter_token[0])
|| (!preg_match('/^([a-zA-Z_][a-zA-Z_0-9]*\*?|\*)$/', $nexter_token[0])
&& strtolower($nexter_token[0]) !== 'class')
) {
throw new TypeParseTreeException(

View File

@ -197,7 +197,7 @@ class SimpleNameResolver extends NodeVisitorAbstract
* Resolve name, according to name resolver options.
*
* @param Name $name Function or constant name to resolve
* @param int $type One of Stmt\Use_::TYPE_*
* @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
*
* @return Name Resolved name, or original name with attribute
*/

View File

@ -622,6 +622,42 @@ class ConstantTest extends TestCase
public $foo = "a";
}',
],
'wildcardEnum' => [
'<?php
class A {
const C_1 = 1;
const C_2 = 2;
const C_3 = 3;
/**
* @param self::C_* $i
*/
public static function foo(int $i) : void {}
}
A::foo(1);
A::foo(2);
A::foo(3);',
],
'wildcardEnumAnyConstant' => [
'<?php
class A {
const C_1 = 1;
const C_2 = 2;
const C_3 = 3;
const D_4 = 4;
/**
* @param self::* $i
*/
public static function foo(int $i) : void {}
}
A::foo(1);
A::foo(2);
A::foo(3);
A::foo(A::D_4);',
],
];
}
@ -790,6 +826,23 @@ class ConstantTest extends TestCase
A::bar("d");',
'error_message' => 'InvalidArgument',
],
'wildcardEnumBadValue' => [
'<?php
class A {
const C_1 = 1;
const C_2 = 2;
const C_3 = 3;
const D_4 = 4;
/**
* @param self::C_* $i
*/
public static function foo(int $i) : void {}
}
A::foo(A::D_4);',
'error_message' => 'InvalidArgument'
],
];
}
}