public function testDeclareVar()
 {
     $scope = new Scope($this->getMock('PHPParser_Node'));
     $this->assertNull($scope->getVar('x'));
     $scope->declareVar('x', $this->registry->getNativeType('integer'));
     $this->assertNotNull($var = $scope->getVar('x'));
     $this->assertSame($this->registry->getNativeType('integer'), $var->getType());
     $this->assertTrue($var->isTypeInferred());
 }
 public function getSlot($name)
 {
     if (isset($this->symbols[$name])) {
         return $this->symbols[$name];
     }
     return $this->functionScope->getVar($name);
 }
 private function removeFromUseIfLocal($name, UseLattice $uses)
 {
     $var = $this->scope->getVar($name);
     if (null === $var) {
         return;
     }
     unset($uses[$var]);
 }
 /**
  * Declares a variable.
  *
  * @param string $name
  * @param array $types
  * @param boolean $typesInferred
  */
 private function declareVar($name, PhpType $type = null, $typeInferred = true, \PHPParser_Node $nameNode = null)
 {
     if ($this->scope->isDeclared($name)) {
         return $this->scope->getVar($name);
     }
     $var = $this->scope->declareVar($name, $type, $typeInferred);
     if ($nameNode) {
         $var->setNameNode($nameNode);
     }
     return $var;
 }
 private function checkParameterAssignment(\PHPParser_Node_Param $node)
 {
     $builder = new UnionTypeBuilder($this->typeRegistry);
     if (null !== $node->default) {
         $this->attachLiteralTypes($node->default);
         $type = $node->default->getAttribute('type');
         if ($type) {
             $builder->addAlternate($type);
         }
     }
     if ('array' === $node->type) {
         $type = $this->typeRegistry->getNativeType('array');
         // If the annotated type also contains the array type, we use the annotated
         // type as it is potentially more specific, e.g. "array<string>" or "array|null".
         $annotatedType = $this->commentParser->getTypeFromParamAnnotation($node->getAttribute('parent'), $node->name);
         if (null !== ($containedArrayType = $this->getContainedArrayType($annotatedType))) {
             $type = $containedArrayType;
         }
         $builder->addAlternate($type);
     } else {
         if ($node->type instanceof \PHPParser_Node_Name) {
             $builder->addAlternate($this->typeRegistry->getClassOrCreate(implode("\\", $node->type->parts)));
         }
     }
     $type = $builder->build();
     if ($type->isNoType()) {
         $type = $this->commentParser->getTypeFromParamAnnotation($node->getAttribute('parent'), $node->name);
     } else {
         if ($type->isNullType()) {
             $commentType = $this->commentParser->getTypeFromParamAnnotation($node->getAttribute('parent'), $node->name);
             if ($commentType) {
                 $type = $this->typeRegistry->createNullableType($commentType);
             } else {
                 $type = null;
             }
         } else {
             if ($type->isBooleanType()) {
                 $commentType = $this->commentParser->getTypeFromParamAnnotation($node->getAttribute('parent'), $node->name);
                 if ($commentType) {
                     $type = $this->typeRegistry->createUnionType(array($type, $commentType));
                 }
             }
         }
     }
     if (null === $type) {
         $type = $this->typeRegistry->getNativeType('unknown');
     }
     $node->setAttribute('type', $type);
     // This could be the case if the same name is used twice as parameter.
     if (false === $this->scope->isDeclared($node->name)) {
         $var = $this->scope->declareVar($node->name, $type, null === $type);
         $var->setNameNode($node);
         $var->setReference($node->byRef);
     } else {
         $var = $this->scope->getVar($node->name);
         if ($varType = $var->getType()) {
             $var->setType($this->typeRegistry->createUnionType(array($varType, $type)));
         } else {
             if (null !== $type) {
                 $var->setType($type);
                 $var->setTypeInferred(true);
             }
         }
         $var->setReference($node->byRef);
     }
 }
 private function updateScopeForTypeChange(LinkedFlowScope $scope, \PHPParser_Node $left, PhpType $leftType = null, PhpType $resultType = null, array $castTypes = array())
 {
     assert('null !== $resultType');
     switch (true) {
         case $left instanceof \PHPParser_Node_Expr_Variable && is_string($left->name):
             $var = $this->syntacticScope->getVar($left->name);
             // If an explicit type has been annotated for the variable, we
             // use this instead of whatever the type inference engine has found.
             if (isset($castTypes[$left->name])) {
                 $resultType = $castTypes[$left->name];
             }
             $this->redeclareSimpleVar($scope, $left, $resultType);
             $left->setAttribute('type', $resultType);
             if (null !== $var && $var->isTypeInferred()) {
                 $oldType = $var->getType();
                 $newType = null === $oldType ? $resultType : $oldType->getLeastSupertype($resultType);
                 $var->setType($newType);
             }
             break;
         case $left instanceof \PHPParser_Node_Expr_ArrayDimFetch:
             if ($left->var instanceof \PHPParser_Node_Expr_Variable && is_string($left->var->name)) {
                 $varType = $this->getType($left->var);
                 // If the variable type is not known yet, then it has not yet
                 // been initialized. In such a case, it must be an array.
                 if (null === $varType || $varType->isUnknownType()) {
                     $this->redeclareSimpleVar($scope, $left->var, $this->getRefinedArrayType($this->typeRegistry->getNativeType('array'), $resultType, $left));
                 } else {
                     if ($varType->isArrayType()) {
                         $this->redeclareSimpleVar($scope, $left->var, $this->getRefinedArrayType($varType, $resultType, $left));
                     }
                 }
             } else {
                 if (($left->var instanceof \PHPParser_Node_Expr_PropertyFetch || $left->var instanceof \PHPParser_Node_Expr_StaticPropertyFetch) && null !== ($qualifiedName = self::getQualifiedName($left->var))) {
                     $bottomType = $this->getType($left->var);
                     if (null === $bottomType || $bottomType->isUnknownType()) {
                         $refinedArrayType = $this->getRefinedArrayType($this->typeRegistry->getNativeType('array'), $resultType, $left);
                         $scope->inferQualifiedSlot($left->var, $qualifiedName, $bottomType, $refinedArrayType);
                         $left->setAttribute('type', $refinedArrayType);
                         $this->ensurePropertyDefined($left->var, $refinedArrayType);
                     } else {
                         if ($bottomType->isArrayType()) {
                             $refinedArrayType = $this->getRefinedArrayType($bottomType, $resultType, $left);
                             $scope->inferQualifiedSlot($left->var, $qualifiedName, $bottomType, $refinedArrayType);
                             $left->setAttribute('type', $refinedArrayType);
                             $this->ensurePropertyDefined($left->var, $refinedArrayType);
                         }
                     }
                 }
             }
             break;
         case $left instanceof \PHPParser_Node_Expr_PropertyFetch:
         case $left instanceof \PHPParser_Node_Expr_StaticPropertyFetch:
             if (null === ($qualifiedName = self::getQualifiedName($left))) {
                 break;
             }
             $bottomType = $leftType ?: $this->typeRegistry->getNativeType('unknown');
             $scope->inferQualifiedSlot($left, $qualifiedName, $bottomType, $resultType);
             $left->setAttribute('type', $resultType);
             $this->ensurePropertyDefined($left, $resultType);
             break;
     }
 }