Ejemplo n.º 1
0
 public function leaveNode(Node $node)
 {
     if (!$node instanceof Expr) {
         return;
     }
     $type = null;
     if ($node instanceof Expr\Assign || $node instanceof Expr\AssignRef) {
         $type = $node->expr->getAttribute('type');
     } elseif ($node instanceof Expr\Ternary) {
         $exprType1 = $node->if === null ? $node->cond->getAttribute('type') : $node->if->getAttribute('type');
         $exprType2 = $node->else->getAttribute('type');
         $type = Type::alternatives([$exprType1, $exprType2]);
     } elseif ($node instanceof Expr\BinaryOp\Coalesce) {
         $exprType1 = $node->left->getAttribute('type');
         $exprType2 = $node->right->getAttribute('type');
         $type = Type::alternatives([$exprType1, $exprType2]);
     } elseif ($node instanceof Expr\ErrorSuppress) {
         $type = $node->expr->getAttribute('type');
     } elseif ($node instanceof Expr\Eval_) {
         $type = Type::mixed_();
     } elseif ($node instanceof Expr\Exit_) {
         $type = Type::null_();
     }
     if ($type !== null || !$node->hasAttribute('type')) {
         if ($type === null) {
             $type = Type::mixed_();
         }
         $node->setAttribute('type', $type);
     }
 }
Ejemplo n.º 2
0
 /**
  * @param string $name
  * @param string $text
  */
 protected function __construct($name, $text)
 {
     parent::__construct($name, $text);
     $text = trim($text);
     $parts = preg_split('~\\s+~', trim($text), 2);
     $this->type = Type::fromString($parts[0]);
     if (count($parts) >= 2) {
         $this->description = $parts[1] ?: null;
     }
 }
Ejemplo n.º 3
0
 public function test_StaticPropertyFetch_self()
 {
     $prop = (new Property())->setName('x')->setStatic(true)->setType(Type::int_());
     $refl = $this->getMockBuilder(Reflection::class)->disableOriginalConstructor()->getMock();
     $refl->expects($this->once())->method('findProperty')->with($this->equalTo('\\C'), $this->equalTo('$x'))->willReturn($prop);
     $expr = new Expr\StaticPropertyFetch(new Name('self'), 'x');
     $class = new Stmt\Class_('C', ['stmts' => [new Stmt\ClassMethod('f', ['stmts' => [$expr]])]], ['namespacedName' => new Name\FullyQualified('C')]);
     $this->infer([$class], $refl);
     $this->assertTrue($expr->getAttribute('type')->equals(Type::int_()));
     $this->assertSame([$prop], $expr->getAttribute('reflections'));
 }
Ejemplo n.º 4
0
 public function test_Ternary()
 {
     $var1 = new Expr\Variable('a', ['type' => Type::array_()]);
     $var2 = new Expr\Variable('b', ['type' => Type::int_()]);
     $var3 = new Expr\Variable('c', ['type' => Type::float_()]);
     $expr = new Expr\Ternary($var1, $var2, $var3);
     $this->infer([$expr]);
     $this->assertTrue($expr->getAttribute('type')->equals(Type::alternatives([Type::int_(), Type::float_()])));
     $expr = new Expr\Ternary($var1, null, $var3);
     $this->infer([$expr]);
     $this->assertTrue($expr->getAttribute('type')->equals(Type::alternatives([Type::array_(), Type::float_()])));
 }
Ejemplo n.º 5
0
 public function test_getType()
 {
     $expr = new Expr\Variable('qaz', ['type' => Type::int_()]);
     $container = new Container();
     $parser = $this->getMockBuilder(Parser::class)->disableOriginalConstructor()->getMock();
     $parser->method('getNodes')->willReturn([]);
     $parser->method('getNodesAtOffset')->with($this->equalTo(7))->willReturn([$expr]);
     $resolver = $this->getMockBuilder(NameResolver::class)->disableOriginalConstructor()->getMock();
     $container->set('parser', $parser);
     $container->set('name_resolver', $resolver);
     $this->assertTrue(Type::int_()->equals((new TypeInferrer($container))->getType(7)));
 }
Ejemplo n.º 6
0
 protected function resolveDocTagType(Type $type)
 {
     return $type->walk(function (Type $type) {
         if ($type instanceof ObjectType) {
             $nameStr = $type->getClass();
             if ($nameStr !== null && $nameStr !== '') {
                 if ($nameStr[0] === '\\') {
                     $name = new FullyQualified(substr($nameStr, 1));
                 } else {
                     $name = new Name($nameStr);
                 }
                 $name = $this->resolveClassName($name)->getAttribute('resolved');
                 $nameStr = $name->toString();
                 if ($name instanceof FullyQualified) {
                     $nameStr = '\\' . $nameStr;
                 }
                 return Type::object_($nameStr);
             }
         }
         return $type;
     });
 }
Ejemplo n.º 7
0
 public function getGoToLocations($offset, $nodes)
 {
     $this->run();
     $name = null;
     $node = null;
     if (count($nodes) >= 2 && $nodes[0] instanceof Name) {
         $name = Type::nameToString($nodes[0]);
         $node = $nodes[1];
     }
     $locations = [];
     if ($node !== null && ($node instanceof Expr\Instanceof_ || $node instanceof Expr\New_ || $node instanceof Stmt\Catch_ || $node instanceof Stmt\Function_ || $node instanceof Stmt\ClassMethod || $node instanceof Expr\Closure || $node instanceof Param || $node instanceof Stmt\Class_ || $node instanceof Stmt\Interface_ || $node instanceof Stmt\TraitUse || $node instanceof Stmt\TraitUseAdaptation)) {
         foreach ($this->reflection->findClass($name) as $class) {
             if ($class->getLocation() !== null) {
                 $locations[] = $class->getLocation();
             }
         }
     }
     return $locations;
 }
Ejemplo n.º 8
0
 public function test_StaticCall()
 {
     $class = (new Class_())->setName('\\C');
     $method = (new Method())->setName('qaz')->setClass($class)->setStatic(true)->setDocReturnType(Type::object_('\\X\\Y'));
     $prop = (new Property())->setName('$wsx')->setClass($class)->setStatic(true)->setType(Type::string_());
     $const = (new ClassConst())->setName('EDC')->setClass($class);
     $cls = new Name\FullyQualified('C');
     $id = new Identifier('q');
     $expr = new Expr\StaticCall($cls, $id, []);
     $completions = $this->complete([$id, $expr], [$method], [$prop], [$const]);
     $this->assertCount(3, $completions);
     $this->assertSame('qaz', $completions[0]->getInsertion());
     $this->assertSame('qaz()', $completions[0]->getDisplay());
     $this->assertSame('static_method', $completions[0]->getKind());
     $this->assertSame('Y', $completions[0]->getType());
     $this->assertSame('EDC', $completions[1]->getInsertion());
     $this->assertSame('EDC', $completions[1]->getDisplay());
     $this->assertSame('class_const', $completions[1]->getKind());
     $this->assertSame('$wsx', $completions[2]->getInsertion());
     $this->assertSame('$wsx', $completions[2]->getDisplay());
     $this->assertSame('static_property', $completions[2]->getKind());
     $this->assertSame('string', $completions[2]->getType());
 }
Ejemplo n.º 9
0
 protected function makeMethod($name, $visibility = ClassLike::M_PUBLIC)
 {
     return (new Method())->setName($name)->setReturnType(Type::mixed_())->setDocReturnType(Type::mixed_())->setAccessibility($visibility);
 }
Ejemplo n.º 10
0
 public function leaveNode(Node $node)
 {
     if ($node instanceof Stmt\Function_ || $node instanceof Stmt\ClassMethod || $node instanceof Expr\Closure) {
         array_pop($this->functionScopeStack);
     } elseif ($node instanceof Stmt\ClassLike) {
         array_pop($this->classStack);
         array_pop($this->functionScopeStack);
     }
     if (!$node instanceof Expr) {
         return;
     }
     $type = null;
     $reflections = null;
     if ($node instanceof Expr\Variable) {
         if (is_string($node->name)) {
             if ($node->name === 'this') {
                 $type = $this->getCurrentClass();
             } elseif (array_key_exists('$' . $node->name, $this->getCurrentFunctionScope())) {
                 $type = $this->getCurrentFunctionScope()['$' . $node->name];
             }
         }
     } elseif ($node instanceof Expr\FuncCall) {
         $reflections = [];
         if ($node->name instanceof Name) {
             $reflections = $this->reflection->findFunction(Type::nameToString($node->name));
         } else {
             $reflections = $this->findMethods($node->name->getAttribute('type'), '__invoke');
         }
         $type = $this->functionsReturnType($reflections);
         // TODO: ConstFetch
     } elseif ($node instanceof Expr\MethodCall) {
         $reflections = [];
         if (is_string($node->name) || $node->name instanceof Identifier) {
             $reflections = $this->findMethods($node->var->getAttribute('type'), (string) $node->name);
         }
         $type = $this->functionsReturnType($reflections);
     } elseif ($node instanceof Expr\StaticCall) {
         $reflections = [];
         if ($node->class instanceof Name && (is_string($node->name) || $node->name instanceof Identifier)) {
             $reflections = $this->findMethods(Type::object_(Type::nameToString($node->class)), (string) $node->name, true);
         }
         $type = $this->functionsReturnType($reflections);
     } elseif ($node instanceof Expr\PropertyFetch) {
         $reflections = [];
         if (is_string($node->name) || $node->name instanceof Identifier) {
             $reflections = $this->findProperties($node->var->getAttribute('type'), '$' . (string) $node->name);
         }
         $type = $this->variablesType($reflections);
     } elseif ($node instanceof Expr\StaticPropertyFetch) {
         $reflections = [];
         if ($node->class instanceof Name && is_string($node->name)) {
             $reflections = $this->findProperties(Type::object_(Type::nameToString($node->class)), '$' . $node->name, true);
         }
         $type = $this->variablesType($reflections);
     } elseif ($node instanceof Expr\ClassConstFetch) {
         // TODO ::class
         $reflections = [];
         if ($node->class instanceof Name) {
             $reflections = $this->findClassConsts(Type::object_(Type::nameToString($node->class)), (string) $node->name);
         }
         $type = $this->constsType($reflections);
     } elseif ($node instanceof Expr\ArrayDimFetch) {
         $arrayType = $node->var->getAttribute('type');
         $altTypes = [$arrayType];
         if ($arrayType instanceof AlternativesType) {
             $altTypes = $arrayType->getAlternatives();
         }
         $types = [];
         foreach ($altTypes as $altType) {
             if ($altType instanceof ArrayType) {
                 $types[] = $altType->getValueType();
             } elseif ($altType instanceof ObjectType) {
                 $types[] = $this->functionsReturnType($this->findMethods($altType, 'offsetGet'));
                 // TODO: check for ArrayAccess
             }
         }
         $type = $types !== [] ? Type::alternatives($types) : Type::mixed_();
     } elseif ($node instanceof Expr\Array_) {
         $type = Type::array_();
     } elseif ($node instanceof Expr\New_) {
         if ($node->class instanceof Name) {
             $type = Type::object_(Type::nameToString($node->class));
         } else {
             $type = Type::object_();
         }
     } elseif ($node instanceof Expr\Clone_) {
         $type = $node->expr->getAttribute('type');
     } elseif ($node instanceof Expr\Closure) {
         $type = Type::object_('\\Closure');
     } elseif ($node instanceof Expr\Cast\Array_) {
         $exprType = $node->expr->getAttribute('type');
         $altTypes = [$exprType];
         if ($exprType instanceof AlternativesType) {
             $altTypes = $exprType->getAlternatives();
         }
         $types = [];
         foreach ($altTypes as $altType) {
             if ($altType instanceof ArrayType) {
                 $types[] = $altType;
             } else {
                 $types[] = Type::array_();
                 // TODO primitives: (array)int --> int[]
             }
         }
         $type = $types !== [] ? Type::alternatives($types) : Type::mixed_();
     } elseif ($node instanceof Expr\Cast\Object_) {
         $exprType = $node->expr->getAttribute('type');
         $altTypes = [$exprType];
         if ($exprType instanceof AlternativesType) {
             $altTypes = $exprType->getAlternatives();
         }
         $types = [];
         foreach ($altTypes as $altType) {
             if ($altType instanceof ObjectType) {
                 $types[] = $altType;
             } else {
                 $types[] = Type::object_('\\stdClass');
             }
         }
         $type = $types !== [] ? Type::alternatives($types) : Type::mixed_();
     } elseif ($node instanceof Expr\Include_) {
         $type = Type::mixed_();
         // TODO: Yield_
         // TODO: YieldFrom
     }
     if ($type !== null) {
         $node->setAttribute('type', $type);
     }
     if ($reflections !== null) {
         $node->setAttribute('reflections', $reflections);
     }
 }
Ejemplo n.º 11
0
 /**
  * @param Interface_      $interface
  * @param Stmt\Interface_ $node
  */
 protected function processInterface(Interface_ $interface, Stmt\Interface_ $node)
 {
     $this->processClassLike($interface, $node);
     foreach ($node->extends as $extends) {
         $interface->addExtends(Type::nameToString($extends));
     }
 }
Ejemplo n.º 12
0
 public function enterNode(Node $node)
 {
     array_push($this->nodePathFromTop, $node);
     $classes = [];
     if ($node instanceof Expr\Instanceof_ || $node instanceof Expr\New_ || $node instanceof Expr\ClassConstFetch || $node instanceof Expr\StaticPropertyFetch || $node instanceof Expr\StaticCall) {
         $classes[] = $node->class;
     } elseif ($node instanceof Stmt\Catch_ || $node instanceof Param) {
         $classes[] = $node->type;
     } elseif ($node instanceof Stmt\Function_ || $node instanceof Stmt\ClassMethod || $node instanceof Expr\Closure) {
         $classes[] = $node->returnType;
     } elseif ($node instanceof Stmt\Class_) {
         $classes[] = $node->extends;
         $classes = array_merge($classes, $node->implements);
     } elseif ($node instanceof Stmt\Interface_) {
         $classes = array_merge($classes, $node->extends);
     } elseif ($node instanceof Stmt\TraitUse) {
         $classes = array_merge($classes, $node->traits);
     } elseif ($node instanceof Stmt\TraitUseAdaptation) {
         $classes[] = $node->trait;
     }
     foreach ($classes as $class) {
         if (is_object($class) && $class instanceof Name) {
             $name = Type::nameToString($class);
             if (!in_array(strtolower($name), ['self', 'parent', 'static']) && empty($this->reflection->findClass($name))) {
                 $range = Range::fromNode($class, $this->file->getPath());
                 $fixes = [];
                 if ($this->namespaceReflection !== null && $class->isUnqualified()) {
                     foreach ($this->namespaceReflection->findFullyQualifiedClasses($class->toString()) as $fqname) {
                         //$fixes[] = new Fix([new FixChunk($range, $fqname)], $fqname);
                         $fixes[] = $this->getFix($fqname);
                     }
                 }
                 $this->diagnostics[] = new Diagnostic([$range], 'Undefined class', $fixes);
             }
         }
     }
 }
Ejemplo n.º 13
0
 protected function handleFunction(Function_ $function, array $data)
 {
     $this->handleElement($function, $data);
     $function->setReturnType(Type::mixed_());
     $function->setDocReturnType($this->getType($data['return_type']));
     $function->setReturnByRef($data['return_by_ref']);
     foreach ($data['params'] as $paramData) {
         $param = new Param();
         $param->setName($paramData['name']);
         $param->setTypeHint(Type::mixed_());
         $param->setDocType($this->getType($paramData['type']));
         $param->setOptional($paramData['optional']);
         $param->setByRef($paramData['by_ref']);
         $param->setVariadic($paramData['variadic']);
         $function->addParam($param);
     }
 }
Ejemplo n.º 14
0
 public function complete($offset)
 {
     $this->run();
     $nodes = $this->parser->getNodesAtOffset($offset, true);
     $node = null;
     if (count($nodes) > 0) {
         $node = $nodes[0];
         if ($node instanceof Identifier || $node instanceof ErrorNode\Nothing) {
             $node = count($nodes) > 1 ? $nodes[1] : null;
         }
     }
     $completions = [];
     $ctxClass = null;
     foreach ($nodes as $ctxNode) {
         if ($ctxNode instanceof Stmt\ClassLike) {
             $ctxClass = $ctxNode->hasAttribute('namespacedName') ? Type::nameToString($ctxNode->getAttribute('namespacedName')) : $node->name;
             break;
         }
     }
     if ($node instanceof Expr\MethodCall || $node instanceof Expr\PropertyFetch) {
         $methods = $this->findMethods($node->var->getAttribute('type'));
         $properties = $this->findProperties($node->var->getAttribute('type'));
         $methods = $this->reflection->filterAvailableMembers($methods, $ctxClass);
         $properties = $this->reflection->filterAvailableMembers($properties, $ctxClass);
         $completions = array_merge($this->formatMethods($methods), $this->formatProperties($properties));
     } elseif ($node instanceof Expr\StaticCall || $node instanceof Expr\StaticPropertyFetch || $node instanceof Expr\ClassConstFetch) {
         // TODO: Support static call on an object: $object::staticMethod() etc.
         // TODO: Filter out non-static members outside the inheritance
         //       chain - while static calls to them are allowed in PHP, they
         //       are pretty useless.
         $staticOnly = true;
         foreach ($nodes as $ctxNode) {
             if ($ctxNode instanceof Stmt\ClassMethod) {
                 $staticOnly = $ctxNode->isStatic();
                 break;
             }
         }
         $methods = $this->findMethods(Type::object_(Type::nameToString($node->class)), $staticOnly);
         $properties = $this->findProperties(Type::object_(Type::nameToString($node->class)), $staticOnly);
         $consts = $this->findClassConsts(Type::object_(Type::nameToString($node->class)));
         $methods = $this->reflection->filterAvailableMembers($methods, $ctxClass);
         $properties = $this->reflection->filterAvailableMembers($properties, $ctxClass);
         $completions = array_merge($this->formatMethods($methods), $this->formatClassConsts($consts), $this->formatProperties($properties, true));
     }
     return $completions;
 }
Ejemplo n.º 15
0
 /**
  * @param Method[] $methods
  * @param Method   $method
  */
 protected function mergeMethod(array &$methods, Method $method)
 {
     // TODO: Do a better job here?
     $method->setDocReturnType(Type::alternatives([$method->getDocReturnType(), $method->getReturnType()]));
     $params = [];
     foreach ($method->getParams() as $param) {
         $param->setDocType(Type::alternatives([$param->getDocType(), $param->getTypeHint()]));
         $params[$param->getName()] = $param;
     }
     if (isset($methods[strtolower($method->getName())])) {
         $baseMethod = $methods[strtolower($method->getName())];
         $method->setDocReturnType(Type::alternatives([$method->getDocReturnType(), $baseMethod->getDocReturnType()]));
         foreach ($baseMethod->getParams() as $baseParam) {
             if (array_key_exists($baseParam->getName(), $params)) {
                 $param = $params[$baseParam->getName()];
                 $param->setDocType(Type::alternatives([$param->getDocType(), $baseParam->getDocType()]));
             }
         }
     }
     $methods[strtolower($method->getName())] = $method;
 }