/**
  * Declares a refined type in {@code scope} for the name represented by
  * {@code node}. It must be possible to refine the type of the given node in
  * the given scope, as determined by {@link #getTypeIfRefinable}.
  */
 protected function declareNameInScope(LinkedFlowScope $scope, \PHPParser_Node $node, PhpType $type)
 {
     switch (true) {
         case $node instanceof \PHPParser_Node_Expr_Variable:
             if (is_string($node->name)) {
                 $scope->inferSlotType($node->name, $type);
             }
             break;
         case $node instanceof \PHPParser_Node_Expr_StaticPropertyFetch:
         case $node instanceof \PHPParser_Node_Expr_PropertyFetch:
             if (null !== ($qualifiedName = TypeInference::getQualifiedName($node))) {
                 $origType = $node->getAttribute('type') ?: $this->typeRegistry->getNativeType('unknown');
                 $scope->inferQualifiedSlot($node, $qualifiedName, $origType, $type);
             }
             break;
             // Something like: if (is_array($functions = getFunctionNames())) { }
         // Something like: if (is_array($functions = getFunctionNames())) { }
         case $node instanceof \PHPParser_Node_Expr_Assign:
         case $node instanceof \PHPParser_Node_Expr_AssignRef:
             $this->declareNameInScope($scope, $node->var, $type);
             break;
         case $node instanceof \PHPParser_Node_Expr_ArrayDimFetch:
             $dim = \Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::getValue($node->dim);
             if ($dim->isDefined() && null !== ($slotType = $node->var->getAttribute('type')) && $slotType->isArrayType()) {
                 $newSlotType = $slotType->inferItemType($dim->get(), $type);
                 $this->declareNameInScope($scope, $node->var, $newSlotType);
             }
             break;
     }
 }
 /**
  * Refines an array type with the information about the used key, and assigned element type.
  *
  * This also defines the type for the item key if available and non-dynamic.
  *
  * @param PhpType $varType
  * @param PhpType $resultType
  * @param \PHPParser_Node_Expr_ArrayDimFetch $node
  *
  * @return ArrayType
  */
 private function getRefinedArrayType(PhpType $varType, PhpType $resultType, \PHPParser_Node_Expr_ArrayDimFetch $node)
 {
     $usedKeyType = $this->getKeyTypeForDimFetch($node);
     $keyType = $varType->getKeyType();
     if ($keyType === $this->typeRegistry->getNativeType('generic_array_key')) {
         $refinedKeyType = $usedKeyType;
     } else {
         $refinedKeyType = $keyType->getLeastSupertype($usedKeyType);
     }
     $elementType = $varType->getElementType();
     if ($elementType === $this->typeRegistry->getNativeType('generic_array_value')) {
         $refinedElementType = $resultType;
     } else {
         $refinedElementType = $elementType->getLeastSupertype($resultType);
     }
     $refinedType = $this->typeRegistry->getArrayType($refinedElementType, $refinedKeyType);
     // Infer the type of the key if a concrete value is available.
     NodeUtil::getValue($node->dim)->map(function ($keyName) use(&$refinedType, $resultType) {
         $refinedType = $refinedType->inferItemType($keyName, $resultType);
     });
     return $refinedType;
 }