public static fromNode ( |
||
$context | The context in which the node appears | |
$code_base | ||
$node | ast\Node\Decl | An AST node representing a method |
$fqsen | ||
return | A Method representing the AST node in the given context |
/** * Visit a node with kind `\ast\AST_METHOD` * * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitMethod(Decl $node) : Context { // Bomb out if we're not in a class context $class = $this->getContextClass(); $method_name = (string) $node->name; $method_fqsen = FullyQualifiedMethodName::fromStringInContext($method_name, $this->context); // Hunt for an available alternate ID if necessary $alternate_id = 0; while ($this->code_base->hasMethodWithFQSEN($method_fqsen)) { $method_fqsen = $method_fqsen->withAlternateId(++$alternate_id); } $method = Method::fromNode(clone $this->context, $this->code_base, $node, $method_fqsen); $class->addMethod($this->code_base, $method, new None()); if ('__construct' === $method_name) { $class->setIsParentConstructorCalled(false); if ($class->isGeneric()) { // Get the set of template type identifiers defined on // the class $template_type_identifiers = array_keys($class->getTemplateTypeMap()); // Get the set of template type identifiers defined // across all parameter types $parameter_template_type_identifiers = []; foreach ($method->getParameterList() as $parameter) { foreach ($parameter->getUnionType()->getTypeSet() as $type) { if ($type instanceof TemplateType) { $parameter_template_type_identifiers[] = $type->getName(); } } } $missing_template_type_identifiers = array_diff($template_type_identifiers, $parameter_template_type_identifiers); if ($missing_template_type_identifiers) { $this->emitIssue(Issue::GenericConstructorTypes, $node->lineno ?? 0, implode(',', $missing_template_type_identifiers), (string) $class->getFQSEN()); } } } elseif ('__invoke' === $method_name) { $class->getUnionType()->addType(CallableType::instance()); } elseif ('__toString' === $method_name && !$this->context->getIsStrictTypes()) { $class->getUnionType()->addType(StringType::instance()); } // Create a new context with a new scope return $this->context->withScope($method->getInternalScope()); }
/** * Visit a node with kind `\ast\AST_METHOD` * * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitMethod(Decl $node) : Context { // Bomb out if we're not in a class context $clazz = $this->getContextClass(); $method_name = (string) $node->name; $method_fqsen = FullyQualifiedMethodName::fromStringInContext($method_name, $this->context); // Hunt for an available alternate ID if necessary $alternate_id = 0; while ($this->code_base->hasMethodWithFQSEN($method_fqsen)) { $method_fqsen = $method_fqsen->withAlternateId(++$alternate_id); } // Create a new context with a new scope $context = $this->context->withScope(new Scope()); $method = Method::fromNode($context, $this->code_base, $node); // Override the FQSEN with the found alternate ID $method->setFQSEN($method_fqsen); $clazz->addMethod($this->code_base, $method); if ('__construct' === $method_name) { $clazz->setIsParentConstructorCalled(false); } elseif ('__invoke' === $method_name) { $clazz->getUnionType()->addType(CallableType::instance()); } elseif ('__toString' === $method_name && !$this->context->getIsStrictTypes()) { $clazz->getUnionType()->addType(StringType::instance()); } // Send the context into the method and reset the scope $context = $this->context->withMethodFQSEN($method->getFQSEN()); return $context; }
/** * Visit a node with kind `\ast\AST_CLOSURE` * * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitClosure(Decl $node) : Context { $closure_fqsen = FullyQualifiedFunctionName::fromClosureInContext($this->context->withLineNumberStart($node->lineno ?? 0)); // If we have a 'this' variable in our current scope, // pass it down into the closure $context = $this->context->withScope(new Scope()); if ($this->context->getScope()->hasVariableWithName('this')) { $context->addScopeVariable($this->context->getScope()->getVariableWithName('this')); } $method = Method::fromNode($context, $this->code_base, $node); // Override the FQSEN with the found alternate ID $method->setFQSEN($closure_fqsen); // Make the closure reachable by FQSEN from anywhere $this->code_base->addMethod($method); if (!empty($node->children['uses']) && $node->children['uses']->kind == \ast\AST_CLOSURE_USES) { $uses = $node->children['uses']; foreach ($uses->children as $use) { if ($use->kind != \ast\AST_CLOSURE_VAR) { Issue::emit(Issue::VariableUseClause, $this->context->getFile(), $node->lineno ?? 0); continue; } $variable_name = (new ContextNode($this->code_base, $this->context, $use->children['name']))->getVariableName(); if (empty($variable_name)) { continue; } $variable = null; // Check to see if the variable exists in this scope if (!$this->context->getScope()->hasVariableWithName($variable_name)) { // If this is not pass-by-reference variable we // have a problem if (!($use->flags & \ast\flags\PARAM_REF)) { Issue::emit(Issue::UndeclaredVariable, $this->context->getFile(), $node->lineno ?? 0, $variable_name); continue; } else { // If the variable doesn't exist, but its // a pass-by-reference variable, we can // just create it $variable = Variable::fromNodeInContext($use, $this->context, $this->code_base, false); } } else { $variable = $this->context->getScope()->getVariableWithName($variable_name); // If this isn't a pass-by-reference variable, we // clone the variable so state within this scope // doesn't update the outer scope if (!($use->flags & \ast\flags\PARAM_REF)) { $variable = clone $variable; } } // Pass the variable into a new scope $context = $context->withScopeVariable($variable); } } // Add all parameters to the scope if (!empty($node->children['params']) && $node->children['params']->kind == \ast\AST_PARAM_LIST) { $params = $node->children['params']; foreach ($params->children as $param) { // Read the parameter $parameter = Parameter::fromNode($this->context, $this->code_base, $param); // Add it to the scope $context = $context->withScopeVariable($parameter); } } return $context->withClosureFQSEN($closure_fqsen); }