Exemplo n.º 1
0
 /**
  * @param Context $context
  * The context in which the node appears
  *
  * @param CodeBase $code_base
  *
  * @param Node $node
  * An AST node representing a method
  *
  * @return Method
  * A Method representing the AST node in the
  * given context
  *
  *
  * @see \Phan\Deprecated\Pass1::node_func
  * Formerly 'function node_func'
  */
 public static function fromNode(Context $context, CodeBase $code_base, Node $node) : Method
 {
     // Parse the comment above the method to get
     // extra meta information about the method.
     $comment = Comment::fromStringInContext($node->docComment ?? '', $context);
     // @var Parameter[]
     // The list of parameters specified on the
     // method
     $parameter_list = Parameter::listFromNode($context, $code_base, $node->children['params']);
     // Add each parameter to the scope of the function
     foreach ($parameter_list as $parameter) {
         $context = $context->withScopeVariable($parameter);
     }
     // Create the skeleton method object from what
     // we know so far
     $method = new Method($context, $node->name, new UnionType(), $node->flags ?? 0);
     // If the method is Analyzable, set the node so that
     // we can come back to it whenever we like and
     // rescan it
     $method->setNode($node);
     // Set the parameter list on the method
     $method->setParameterList($parameter_list);
     $method->setNumberOfRequiredParameters(array_reduce($parameter_list, function (int $carry, Parameter $parameter) : int {
         return $carry + ($parameter->isRequired() ? 1 : 0);
     }, 0));
     $method->setNumberOfOptionalParameters(array_reduce($parameter_list, function (int $carry, Parameter $parameter) : int {
         return $carry + ($parameter->isOptional() ? 1 : 0);
     }, 0));
     // Check to see if the comment specifies that the
     // method is deprecated
     $method->setIsDeprecated($comment->isDeprecated());
     // Take a look at method return types
     if ($node->children['returnType'] !== null) {
         $union_type = UnionType::fromSimpleNode($context, $node->children['returnType']);
         $method->getUnionType()->addUnionType($union_type);
     } else {
         if ($comment->hasReturnUnionType()) {
             // See if we have a return type specified in the comment
             $union_type = $comment->getReturnType();
             if ($union_type->hasSelfType()) {
                 // We can't actually figure out 'static' at this
                 // point, but fill it in regardless. It will be partially
                 // correct
                 if ($context->hasClassFQSEN()) {
                     // n.b.: We're leaving the reference to self, static
                     //       or $this in the type because I'm guessing
                     //       it doesn't really matter. Apologies if it
                     //       ends up being an issue.
                     $union_type->addUnionType($context->getClassFQSEN()->asUnionType());
                 }
             }
             $method->getUnionType()->addUnionType($union_type);
         }
     }
     // Add params to local scope for user functions
     if ($context->getFile() != 'internal') {
         $parameter_offset = 0;
         foreach ($method->getParameterList() as $i => $parameter) {
             if ($parameter->getUnionType()->isEmpty()) {
                 // If there is no type specified in PHP, check
                 // for a docComment with @param declarations. We
                 // assume order in the docComment matches the
                 // parameter order in the code
                 if ($comment->hasParameterWithNameOrOffset($parameter->getName(), $parameter_offset)) {
                     $comment_type = $comment->getParameterWithNameOrOffset($parameter->getName(), $parameter_offset)->getUnionType();
                     $parameter->getUnionType()->addUnionType($comment_type);
                 }
             }
             // If there's a default value on the parameter, check to
             // see if the type of the default is cool with the
             // specified type.
             if ($parameter->hasDefaultValue()) {
                 $default_type = $parameter->getDefaultValueType();
                 if (!$default_type->canCastToUnionType($parameter->getUnionType())) {
                     Log::err(Log::ETYPE, "Default value for {$parameter->getUnionType()} \${$parameter->getName()} can't be {$default_type}", $context->getFile(), $node->lineno);
                 }
                 // If we have no other type info about a parameter,
                 // just because it has a default value of null
                 // doesn't mean that is its type. Any type can default
                 // to null
                 if ((string) $default_type === 'null' && !$parameter->getUnionType()->isEmpty()) {
                     $parameter->getUnionType()->addType(NullType::instance());
                 }
             }
             ++$parameter_offset;
         }
     }
     return $method;
 }
Exemplo n.º 2
0
 /**
  * @return Parameter
  * A parameter built from a node
  *
  * @see \Phan\Deprecated\Pass1::node_param
  * Formerly `function node_param`
  */
 public static function fromNode(Context $context, CodeBase $code_base, Node $node) : Parameter
 {
     assert($node instanceof Node, "node was not an \\ast\\Node");
     // Get the type of the parameter
     $type = UnionType::fromSimpleNode($context, $node->children['type']);
     $comment = Comment::fromStringInContext($node->docComment ?? '', $context);
     // Create the skeleton parameter from what we know so far
     $parameter = new Parameter($context, (string) $node->children['name'], $type, $node->flags ?? 0);
     // If there is a default value, store it and its type
     if (($default_node = $node->children['default']) !== null) {
         // We can't figure out default values during the
         // parsing phase, unfortunately
         if (!$default_node instanceof Node || $default_node->kind == \ast\AST_CONST || $default_node->kind == \ast\AST_UNARY_OP || $default_node->kind == \ast\AST_ARRAY) {
             // Set the default value
             $parameter->setDefaultValue($node->children['default'], UnionType::fromNode($context, $code_base, $node->children['default']));
         } else {
             // Nodes here may be of type \ast\AST_CLASS_CONST
             // which we can't figure out during the first
             // parsing pass
             $parameter->setDefaultValue(null, NullType::instance()->asUnionType());
         }
     }
     return $parameter;
 }