public function __construct(\PHPParser_Node $rootNode, Scope $parent = null, PhpType $thisType = null)
 {
     $this->rootNode = $rootNode;
     $this->parent = $parent;
     $this->thisType = $thisType;
     $this->depth = null === $parent ? 0 : $parent->getDepth() + 1;
 }
 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 testGetSlotFromFunctionScope()
 {
     $functionScope = new Scope($this->getMock('PHPParser_Node'));
     $functionScope->declareVar('x', $this->registry->getNativeType('integer'));
     $flowScope = LinkedFlowScope::createLatticeElement($functionScope);
     $this->assertNotNull($var = $flowScope->getSlot('x'));
     $this->assertSame($this->registry->getNativeType('integer'), $var->getType());
     $flowScope = $flowScope->createChildFlowScope();
     $this->assertNotNull($var = $flowScope->getSlot('x'));
     $this->assertSame($this->registry->getNativeType('integer'), $var->getType());
 }
 public function getSlot($name)
 {
     if (isset($this->symbols[$name])) {
         return $this->symbols[$name];
     }
     return $this->functionScope->getVar($name);
 }
 private function createVar(LinkedFlowScope $scope, $name, $type)
 {
     $type = $this->registry->resolveType($type);
     $this->functionScope->declareVar($name)->setNameNode($n = new \PHPParser_Node_Expr_Variable($name));
     $scope->inferSlotType($name, $type);
     $n->setAttribute('type', $type);
     return $n;
 }
 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 getThisType(\PHPParser_Node $node, Scope $parentScope = null)
 {
     $parent = $node;
     while (null !== $parent) {
         // As of PHP 5.4, closures inherit the this type of their parent scope. Since there is currently no way to
         // configure the PHP version that we build against, we will simply always infer the ThisType. Other passes,
         // can then perform checks whether ``$this`` may be accessed, allowing us to produce better error messages.
         if ($parent instanceof \PHPParser_Node_Expr_Closure) {
             if (null === $parentScope) {
                 return null;
             }
             return $parentScope->getTypeOfThis();
         }
         if (NodeUtil::isMethodContainer($parent)) {
             $name = implode("\\", $parent->namespacedName->parts);
             return $this->typeRegistry->getClassOrCreate($name);
         }
         $parent = $parent->getAttribute('parent');
     }
     return null;
 }
 private function getPropertyType(PhpType $objType = null, $propName, \PHPParser_Node $n, LinkedFlowScope $scope)
 {
     // Check whether the scope contains inferred type information about the property,
     // or fallback to the wider property type if not available. Scopes could
     // contain information about properties if we have reverse interpreted
     // the property previously.
     $qualifiedName = self::getQualifiedName($n);
     $var = $scope->getSlot($qualifiedName);
     if (null !== $var && null !== ($varType = $var->getType())) {
         if ($varType->equals($this->typeRegistry->getNativeType('unknown')) && $var !== $this->syntacticScope->getSlot($qualifiedName)) {
             // If the type of this qualified name has been checked in this scope,
             // then use CHECKED_UNKNOWN_TYPE instead to indicate that.
             // TODO: The checked unknown type has not really proved useful in
             //       practice. Consider removing it entirely.
             return $this->typeRegistry->getNativeType('unknown_checked');
         }
         return $varType;
     }
     $propertyType = null;
     if (null !== $objType && null !== ($objType = $objType->toMaybeObjectType()) && $objType instanceof Clazz && $objType->hasProperty($propName)) {
         $propertyType = $objType->getProperty($propName)->getPhpType();
     }
     return $propertyType ?: $this->typeRegistry->getNativeType('unknown');
 }
 private function attachLiteralTypes(\PHPParser_Node $node, \PHPParser_Node $parent = null)
 {
     switch (true) {
         case $node instanceof \PHPParser_Node_Name_FullyQualified:
             $node->setAttribute('type', $this->typeRegistry->getClassOrCreate(implode("\\", $node->parts)));
             break;
         case $node instanceof \PHPParser_Node_Name:
             if ($parent instanceof \PHPParser_Node_Expr_New || $parent instanceof \PHPParser_Node_Expr_StaticPropertyFetch || $parent instanceof \PHPParser_Node_Expr_StaticCall || $parent instanceof \PHPParser_Node_Expr_Instanceof || $parent instanceof \PHPParser_Node_Stmt_Catch) {
                 $name = implode("\\", $node->parts);
                 $lowerName = strtolower($name);
                 if ('static' === $name) {
                     $node->setAttribute('type', $this->typeRegistry->getThisType($this->scope->getTypeOfThis()));
                 } else {
                     if ('self' === $name) {
                         $node->setAttribute('type', $this->scope->getTypeOfThis());
                     } else {
                         if ('parent' === $name) {
                             $thisType = $this->scope->getTypeOfThis()->toMaybeObjectType();
                             if (null === $thisType || !$thisType->isClass() || null === $thisType->getSuperClassType()) {
                                 $node->setAttribute('type', $this->typeRegistry->getNativeType('unknown'));
                             } else {
                                 $node->setAttribute('type', $thisType->getSuperClassType());
                             }
                         } else {
                             $node->setAttribute('type', $this->typeRegistry->getClassOrCreate($name));
                         }
                     }
                 }
             }
             break;
         case $node instanceof \PHPParser_Node_Expr_Array:
         case $node instanceof \PHPParser_Node_Expr_Cast_Array:
             // We only do attach the generic array type on the first build.
             // For subsequent builds, other passes likely have already made
             // the array type more specific.
             if (null === $node->getAttribute('type')) {
                 $node->setAttribute('type', $this->typeRegistry->getNativeType('array'));
             }
             break;
         case $node instanceof \PHPParser_Node_Expr_UnaryMinus:
         case $node instanceof \PHPParser_Node_Expr_UnaryPlus:
         case $node instanceof \PHPParser_Node_Scalar_LNumber:
         case $node instanceof \PHPParser_Node_Scalar_LineConst:
             $node->setAttribute('type', $this->typeRegistry->getNativeType('integer'));
             break;
         case $node instanceof \PHPParser_Node_Scalar_DNumber:
             $node->setAttribute('type', $this->typeRegistry->getNativeType('double'));
             break;
         case $node instanceof \PHPParser_Node_Scalar_String:
         case $node instanceof \PHPParser_Node_Scalar_FileConst:
         case $node instanceof \PHPParser_Node_Scalar_DirConst:
             $node->setAttribute('type', $this->typeRegistry->getNativeType('string'));
             break;
         case $node instanceof \PHPParser_Node_Expr_ClassConstFetch:
             if ($node->class instanceof \PHPParser_Node_Name && in_array($node->class->parts[0], array('self', 'static')) && null !== ($thisType = $this->scope->getTypeOfThis()) && null !== ($objType = $thisType->toMaybeObjectType()) && ($objType->isClass() || $objType->isInterface()) && $objType->hasConstant($node->name)) {
                 $node->setAttribute('type', $objType->getConstant($node->name)->getPhpType());
             }
             break;
         case $node instanceof \PHPParser_Node_Expr_ConstFetch:
             $nameParts = $node->name->parts;
             if (1 === count($nameParts)) {
                 $name = strtolower($nameParts[0]);
                 if ('true' === $name) {
                     $node->setAttribute('type', $this->typeRegistry->getNativeType('boolean'));
                 } else {
                     if ('false' === $name) {
                         $node->setAttribute('type', $this->typeRegistry->getNativeType('false'));
                     } else {
                         if ('null' === $name) {
                             $node->setAttribute('type', $this->typeRegistry->getNativeType('null'));
                         }
                     }
                 }
             }
             break;
     }
 }
 /**
  * Completes the given scope with type information inferred from the local flow where necessary.
  */
 public function completeScope(Scope $scope)
 {
     foreach ($scope->getVars() as $name => $var) {
         if ($var->isTypeInferred()) {
             $type = $var->getType();
             if (null === $type) {
                 $flowType = $this->getSlot($name)->getType();
                 $var->setType($flowType);
             }
         }
     }
 }