Skip to content

Commit

Permalink
Fix #2739 - specify wildcards in constants
Browse files Browse the repository at this point in the history
  • Loading branch information
muglug committed Feb 7, 2020
1 parent cecc5ed commit dcc855d
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 14 deletions.
55 changes: 43 additions & 12 deletions src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -1311,23 +1311,54 @@ private static function fleshOutAtomicType(
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 = [];

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;

return array_values($class_constant->getAtomicTypes())[0];
$matching_constant_types = \array_merge(
\array_values($class_constant->getAtomicTypes()),
$matching_constant_types
);
}
}
}

if ($matching_constant_types) {
return $matching_constant_types;
}
}

return $return_type;
Expand Down
2 changes: 1 addition & 1 deletion src/Psalm/Internal/Type/ParseTree.php
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ public static function createFromTokens(array $type_tokens)
$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(
Expand Down
2 changes: 1 addition & 1 deletion src/Psalm/Internal/Visitor/SimpleNameResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ private function resolveType($node)
* 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
*/
Expand Down
53 changes: 53 additions & 0 deletions tests/ConstantTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,42 @@ class A {
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);',
],
];
}

Expand Down Expand Up @@ -790,6 +826,23 @@ public static function bar(string $j) : void {}
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'
],
];
}
}

0 comments on commit dcc855d

Please sign in to comment.