Phan\Language\UnionType::canCastToUnionType PHP Method

canCastToUnionType() public method

See also: Phan\Deprecated\Pass2::type_check Formerly 'function type_check'
public canCastToUnionType ( UnionType $target ) : boolean
$target UnionType A type to check to see if this can cast to it
return boolean True if this type is allowed to cast to the given type i.e. int->float is allowed while float->int is not.
    public function canCastToUnionType(UnionType $target) : bool
    {
        // Fast-track most common cases first
        // If either type is unknown, we can't call it
        // a success
        if ($this->isEmpty() || $target->isEmpty()) {
            return true;
        }
        // T === T
        if ($this->isEqualTo($target)) {
            return true;
        }
        if (Config::get()->null_casts_as_any_type) {
            // null <-> null
            if ($this->isType(NullType::instance()) || $target->isType(NullType::instance())) {
                return true;
            }
        }
        // mixed <-> mixed
        if ($target->hasType(MixedType::instance()) || $this->hasType(MixedType::instance())) {
            return true;
        }
        // int -> float
        if ($this->isType(IntType::instance()) && $target->isType(FloatType::instance())) {
            return true;
        }
        // Check conversion on the cross product of all
        // type combinations and see if any can cast to
        // any.
        foreach ($this->getTypeSet() as $source_type) {
            if (empty($source_type)) {
                continue;
            }
            foreach ($target->getTypeSet() as $target_type) {
                if (empty($target_type)) {
                    continue;
                }
                if ($source_type->canCastToType($target_type)) {
                    return true;
                }
            }
        }
        // Only if no source types can be cast to any target
        // types do we say that we cannot perform the cast
        return false;
    }

Usage Example

Example #1
0
 /**
  * Visit a node with kind `\ast\AST_PROP_DECL`
  *
  * @param Node $node
  * A node to parse
  *
  * @return Context
  * A new or an unchanged context resulting from
  * parsing the node
  */
 public function visitPropDecl(Node $node) : Context
 {
     // Bomb out if we're not in a class context
     $clazz = $this->getContextClass();
     // Get a comment on the property declaration
     $comment = Comment::fromStringInContext($node->children[0]->docComment ?? '', $this->context);
     foreach ($node->children ?? [] as $i => $child_node) {
         // Ignore children which are not property elements
         if (!$child_node || $child_node->kind != \ast\AST_PROP_ELEM) {
             continue;
         }
         // If something goes wrong will getting the type of
         // a property, we'll store it as a future union
         // type and try to figure it out later
         $future_union_type = null;
         try {
             // Get the type of the default
             $union_type = UnionType::fromNode($this->context, $this->code_base, $child_node->children['default'], false);
         } catch (IssueException $exception) {
             $future_union_type = new FutureUnionType($this->code_base, $this->context, $child_node->children['default']);
             $union_type = new UnionType();
         }
         // Don't set 'null' as the type if thats the default
         // given that its the default default.
         if ($union_type->isType(NullType::instance())) {
             $union_type = new UnionType();
         }
         $property_name = $child_node->children['name'];
         assert(is_string($property_name), 'Property name must be a string. ' . 'Got ' . print_r($property_name, true) . ' at ' . $this->context);
         $property = new Property(clone $this->context->withLineNumberStart($child_node->lineno ?? 0), is_string($child_node->children['name']) ? $child_node->children['name'] : '_error_', $union_type, $node->flags ?? 0);
         $property->setFQSEN(FullyQualifiedPropertyName::make($clazz->getFQSEN(), $property->getName()));
         // Add the property to the class
         $clazz->addProperty($this->code_base, $property);
         $property->setSuppressIssueList($comment->getSuppressIssueList());
         // Look for any @var declarations
         if ($variable = $comment->getVariableList()[$i] ?? null) {
             if ((string) $union_type != 'null' && !$union_type->canCastToUnionType($variable->getUnionType())) {
                 $this->emitIssue(Issue::TypeMismatchProperty, $child_node->lineno ?? 0, (string) $union_type, (string) $property->getFQSEN(), (string) $variable->getUnionType());
             }
             // Set the declared type to the doc-comment type and add
             // |null if the default value is null
             $property->getUnionType()->addUnionType($variable->getUnionType());
         }
         // Wait until after we've added the (at)var type
         // before setting the future so that calling
         // $property->getUnionType() doesn't force the
         // future to be reified.
         if (!empty($future_union_type)) {
             $property->setFutureUnionType($future_union_type);
         }
     }
     return $this->context;
 }