private function caseGettypeFunctionCall(\PHPParser_Node $node, PhpType $type, $value, $resultEqualsValue, LinkedFlowScope $blindScope)
 {
     $restrictedType = $this->getRestrictedByGettypeResult($type, $value, $resultEqualsValue);
     // cannot narrow the scope
     if (null === $restrictedType) {
         return $blindScope;
     }
     $informed = $blindScope->createChildFlowScope();
     $this->declareNameInScope($informed, $node, $restrictedType);
     return $informed;
 }
 /**
  * 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;
     }
 }
 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 newScope()
 {
     $globalScope = new Scope($this->getMock('PHPParser_Node'));
     $this->functionScope = new Scope($this->getMock('PHPParser_Node'), $globalScope);
     return LinkedFlowScope::createLatticeElement($this->functionScope);
 }
 /**
  * @return LinkedFlowScope
  */
 public function createChildFlowScope()
 {
     $this->frozen = true;
     if ($this->depth > self::MAX_DEPTH) {
         if (null === $this->flattened) {
             $this->flattened = FlatFlowScopeCache::createFromLinkedScope($this);
         }
         return new LinkedFlowScope($this->flattened);
     }
     return LinkedFlowScope::createFromLinkedFlowScope($this);
 }
 /**
  * Helper Constructor.
  *
  * This is called when we join two flow scope chains. For example, when we combine the outcomes of the ON_TRUE
  * and the ON_FALSE branch of an IF clause.
  *
  * @param \Scrutinizer\PhpAnalyzer\DataFlow\TypeInference\LinkedFlowScope $joinedScopeA
  * @param \Scrutinizer\PhpAnalyzer\DataFlow\TypeInference\LinkedFlowScope $joinedScopeB
  *
  * @return FlatFlowScopeCache
  */
 public static function createFromLinkedScopes(LinkedFlowScope $joinedScopeA, LinkedFlowScope $joinedScopeB)
 {
     $cache = new self();
     // Always prefer the "real" scope to the faked-out bottom scope.
     $cache->functionScope = $joinedScopeA->flowsFromBottom() ? $joinedScopeB->getFunctionScope() : $joinedScopeA->getFunctionScope();
     $slotsA = $cache->symbols = $joinedScopeA->allFlowSlots();
     $slotsB = $joinedScopeB->allFlowSlots();
     // There are 5 different join cases:
     // 1) The type is declared in joinedScopeA, not in joinedScopeB,
     //    and not in functionScope. Just use the one in A.
     // 2) The type is declared in joinedScopeB, not in joinedScopeA,
     //    and not in functionScope. Just use the one in B.
     // 3) The type is declared in functionScope and joinedScopeA, but
     //    not in joinedScopeB. Join the two types.
     // 4) The type is declared in functionScope and joinedScopeB, but
     //    not in joinedScopeA. Join the two types.
     // 5) The type is declared in joinedScopeA and joinedScopeB. Join
     //    the two types.
     $symbolNames = array_unique(array_merge(array_keys($slotsA), array_keys($slotsB)));
     foreach ($symbolNames as $name) {
         $slotA = isset($slotsA[$name]) ? $slotsA[$name] : null;
         $slotB = isset($slotsB[$name]) ? $slotsB[$name] : null;
         $joinedType = null;
         if (null === $slotB || $slotB->getType() === null) {
             $fnSlot = $joinedScopeB->getFunctionScope()->getVar($name);
             $fnSlotType = null === $fnSlot ? null : $fnSlot->getType();
             if (null === $fnSlotType) {
                 // Case #1 -- The symbol was already inserted from A slots.
             } else {
                 // Case #3
                 $joinedType = $slotA->getType()->getLeastSuperType($fnSlotType);
             }
         } else {
             if (null === $slotA || $slotA->getType() === null) {
                 $fnSlot = $joinedScopeA->getFunctionScope()->getVar($name);
                 $fnSlotType = null === $fnSlot ? null : $fnSlot->getType();
                 if (null === $fnSlotType) {
                     // Case #2
                     $cache->symbols[$name] = $slotB;
                 } else {
                     // Case #4
                     $joinedType = $slotB->getType()->getLeastSuperType($fnSlotType);
                 }
             } else {
                 // Case #5
                 $joinedType = $slotA->getType()->getLeastSuperType($slotB->getType());
             }
         }
         if (null !== $joinedType) {
             $cache->symbols[$name] = new SimpleSlot($name, $joinedType, true);
         }
     }
     return $cache;
 }
 private function assertScopesDiffer(LinkedFlowScope $a, LinkedFlowScope $b)
 {
     $this->assertFalse($a->equals($b));
     $this->assertFalse($b->equals($a));
     $this->assertTrue($a->equals($a));
     $this->assertTrue($b->equals($b));
 }