Skip to content

Commit

Permalink
Allow class constants to refer to other constants
Browse files Browse the repository at this point in the history
This change introduces the TMixedDeferredConstant type, which marks a
type as being a constant that needs to be resolved at a later stage.

Fixes: vimeo#1551
  • Loading branch information
lhchavez committed Aug 26, 2019
1 parent b4213a9 commit 44f20c7
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 8 deletions.
Expand Up @@ -18,6 +18,7 @@
use Psalm\IssueBuffer;
use Psalm\Type;
use function implode;
use function is_null;
use function strtolower;
use function explode;

Expand Down Expand Up @@ -352,7 +353,18 @@ public static function analyzeClassConst(
if (isset($class_constants[$stmt->name->name])
&& ($first_part_lc !== 'static' || $class_const_storage->final)
) {
$stmt->inferredType = clone $class_constants[$stmt->name->name];
$inferred_type = clone $class_constants[$stmt->name->name];
if ($inferred_type->isMixedDeferredConstant()) {
$inferred_type = $statements_analyzer->getConstType(
$stmt->name->name,
$stmt->name instanceof PhpParser\Node\Name\FullyQualified,
$context
);
}
if (is_null($inferred_type)) {
$inferred_type = Type::getMixed();
}
$stmt->inferredType = $inferred_type;
$context->vars_in_scope[$const_id] = $stmt->inferredType;
} else {
$stmt->inferredType = Type::getMixed();
Expand Down
17 changes: 10 additions & 7 deletions src/Psalm/Internal/Codebase/Populator.php
Expand Up @@ -134,7 +134,8 @@ public function populateCodebase(\Psalm\Codebase $codebase)
$class_storage->name
);

$class_storage->public_class_constants[$const_name] = $const_type ?: Type::getMixed();
$class_storage->public_class_constants[$const_name] =
$const_type ?: Type::getMixedDeferredConstant();
}

foreach ($class_storage->protected_class_constant_nodes as $const_name => $node) {
Expand All @@ -147,7 +148,8 @@ public function populateCodebase(\Psalm\Codebase $codebase)
$class_storage->name
);

$class_storage->protected_class_constants[$const_name] = $const_type ?: Type::getMixed();
$class_storage->protected_class_constants[$const_name] =
$const_type ?: Type::getMixedDeferredConstant();
}

foreach ($class_storage->private_class_constant_nodes as $const_name => $node) {
Expand All @@ -160,7 +162,8 @@ public function populateCodebase(\Psalm\Codebase $codebase)
$class_storage->name
);

$class_storage->private_class_constants[$const_name] = $const_type ?: Type::getMixed();
$class_storage->private_class_constants[$const_name] =
$const_type ?: Type::getMixedDeferredConstant();
}
}
}
Expand Down Expand Up @@ -545,11 +548,11 @@ private function populateDataFromParentClass(
);

foreach ($parent_storage->public_class_constant_nodes as $name => $_) {
$storage->public_class_constants[$name] = Type::getMixed();
$storage->public_class_constants[$name] = Type::getMixedDeferredConstant();
}

foreach ($parent_storage->protected_class_constant_nodes as $name => $_) {
$storage->protected_class_constants[$name] = Type::getMixed();
$storage->protected_class_constants[$name] = Type::getMixedDeferredConstant();
}

$storage->pseudo_property_get_types += $parent_storage->pseudo_property_get_types;
Expand Down Expand Up @@ -598,7 +601,7 @@ private function populateInterfaceDataFromParentInterfaces(
);

foreach ($parent_interface_storage->public_class_constant_nodes as $name => $_) {
$storage->public_class_constants[$name] = Type::getMixed();
$storage->public_class_constants[$name] = Type::getMixedDeferredConstant();
}

if ($parent_interface_storage->template_types) {
Expand Down Expand Up @@ -685,7 +688,7 @@ private function populateDataFromImplementedInterfaces(
);

foreach ($implemented_interface_storage->public_class_constant_nodes as $name => $_) {
$storage->public_class_constants[$name] = Type::getMixed();
$storage->public_class_constants[$name] = Type::getMixedDeferredConstant();
}

$storage->invalid_dependencies = array_merge(
Expand Down
11 changes: 11 additions & 0 deletions src/Psalm/Type.php
Expand Up @@ -41,6 +41,7 @@
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Atomic\TLiteralString;
use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TMixedDeferredConstant;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TNumeric;
Expand Down Expand Up @@ -1251,6 +1252,16 @@ public static function getMixed($from_loop_isset = false)
return new Union([$type]);
}

/**
* @return Type\Union
*/
public static function getMixedDeferredConstant()
{
$type = new TMixedDeferredConstant();

return new Union([$type]);
}

/**
* @return Type\Union
*/
Expand Down
13 changes: 13 additions & 0 deletions src/Psalm/Type/Atomic/TMixedDeferredConstant.php
@@ -0,0 +1,13 @@
<?php
namespace Psalm\Type\Atomic;

class TMixedDeferredConstant extends TMixed
{
/**
* @return string
*/
public function getId()
{
return 'mixed-deferred-constant';
}
}
9 changes: 9 additions & 0 deletions src/Psalm/Type/Union.php
Expand Up @@ -847,6 +847,15 @@ public function isVanillaMixed()
&& get_class($this->types['mixed']) === Type\Atomic\TMixed::class;
}

/**
* @return bool
*/
public function isMixedDeferredConstant()
{
return isset($this->types['mixed'])
&& get_class($this->types['mixed']) === Type\Atomic\TMixedDeferredConstant::class;
}

/**
* @return bool
*/
Expand Down
8 changes: 8 additions & 0 deletions tests/ConstantTest.php
Expand Up @@ -400,6 +400,14 @@ public static function bar() : bool {
}
}'
],
'constantDeferredConstants' => [
'<?php
const A = 1;
class B {
public const C = A;
}
echo B::C;'
],
];
}

Expand Down

0 comments on commit 44f20c7

Please sign in to comment.