public function visitMethodCall(Node $node) : Context
{
$method_name = $node->children['method'];
if (!is_string($method_name)) {
return $this->context;
}
try {
$method = (new ContextNode($this->code_base, $this->context, $node))->getMethod($method_name, false);
} catch (IssueException $exception) {
Issue::maybeEmitInstance($this->code_base, $this->context, $exception->getIssueInstance());
return $this->context;
} catch (NodeException $exception) {
// If we can't figure out the class for this method
// call, cry YOLO and mark every method with that
// name with a reference.
if (Config::get()->dead_code_detection && Config::get()->dead_code_detection_prefer_false_negative) {
foreach ($this->code_base->getMethodSetByName($method_name) as $method) {
$method->addReference($this->context);
}
}
// Swallow it
return $this->context;
}
// Make sure the magic method is accessible
if ($method->isPrivate() && !$method->getDefiningClass($this->code_base)->isTrait() && (!$this->context->isInClassScope() || $this->context->getClassFQSEN() != $method->getDefiningClassFQSEN())) {
$this->emitIssue(Issue::AccessMethodPrivate, $node->lineno ?? 0, (string) $method->getFQSEN(), $method->getFileRef()->getFile(), (string) $method->getFileRef()->getLineNumberStart());
} else {
if ($method->isProtected() && !$method->getDefiningClass($this->code_base)->isTrait() && (!$this->context->isInClassScope() || !$this->context->getClassFQSEN()->asType()->canCastToType($method->getClassFQSEN()->asType()) && !$this->context->getClassFQSEN()->asType()->isSubclassOf($this->code_base, $method->getDefiningClassFQSEN()->asType()) && $this->context->getClassFQSEN() != $method->getDefiningClassFQSEN())) {
$this->emitIssue(Issue::AccessMethodProtected, $node->lineno ?? 0, (string) $method->getFQSEN(), $method->getFileRef()->getFile(), (string) $method->getFileRef()->getLineNumberStart());
}
}
// Check the call for paraemter and argument types
$this->analyzeCallToMethod($this->code_base, $method, $node);
return $this->context;
}