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());
}