/**
* @param CodeBase $code_base
* The global code base
*
* @param FunctionInterface $method
* The method we're analyzing arguments for
*
* @param Node $node
* The node holding the method call we're looking at
*
* @param Context $context
* The context in which we see the call
*
* @return null
*
* @see \Phan\Deprecated\Pass2::arglist_type_check
* Formerly `function arglist_type_check`
*/
private static function analyzeParameterList(CodeBase $code_base, FunctionInterface $method, Node $node, Context $context)
{
foreach ($node->children ?? [] as $i => $argument) {
// Get the parameter associated with this argument
$parameter = $method->getParameterList()[$i] ?? null;
// This issue should be caught elsewhere
if (!$parameter) {
continue;
}
// If this is a pass-by-reference parameter, make sure
// we're passing an allowable argument
if ($parameter->isPassByReference()) {
if (!$argument instanceof \ast\Node || $argument->kind != \ast\AST_VAR && $argument->kind != \ast\AST_DIM && $argument->kind != \ast\AST_PROP && $argument->kind != \ast\AST_STATIC_PROP) {
Issue::maybeEmit($code_base, $context, Issue::TypeNonVarPassByRef, $node->lineno ?? 0, $i + 1, (string) $method->getFQSEN());
} else {
$variable_name = (new ContextNode($code_base, $context, $argument))->getVariableName();
if (Type::isSelfTypeString($variable_name) && !$context->isInClassScope() && $argument->kind == \ast\AST_STATIC_PROP && $argument->kind == \ast\AST_PROP) {
Issue::maybeEmit($code_base, $context, Issue::ContextNotObject, $node->lineno ?? 0, "{$variable_name}");
}
}
}
// Get the type of the argument. We'll check it against
// the parameter in a moment
$argument_type = UnionType::fromNode($context, $code_base, $argument);
// Expand it to include all parent types up the chain
$argument_type_expanded = $argument_type->asExpandedTypes($code_base);
// Check the method to see if it has the correct
// parameter types. If not, keep hunting through
// alternates of the method until we find one that
// takes the correct types
$alternate_parameter = null;
$alternate_found = false;
foreach ($method->alternateGenerator($code_base) as $alternate_id => $alternate_method) {
if (empty($alternate_method->getParameterList()[$i])) {
continue;
}
// Get the parameter associated with this argument
$alternate_parameter = $alternate_method->getParameterList()[$i] ?? null;
// Expand the types to find all parents and traits
$alternate_parameter_type_expanded = $alternate_parameter->getUnionType()->asExpandedTypes($code_base);
// See if the argument can be cast to the
// parameter
if ($argument_type_expanded->canCastToUnionType($alternate_parameter_type_expanded)) {
$alternate_found = true;
break;
}
}
if (!$alternate_found) {
$parameter_name = $alternate_parameter ? $alternate_parameter->getName() : 'unknown';
$parameter_type = $alternate_parameter ? $alternate_parameter->getUnionType() : 'unknown';
if ($method->isInternal()) {
Issue::maybeEmit($code_base, $context, Issue::TypeMismatchArgumentInternal, $node->lineno ?? 0, $i + 1, $parameter_name, $argument_type_expanded, (string) $method->getFQSEN(), (string) $parameter_type);
} else {
Issue::maybeEmit($code_base, $context, Issue::TypeMismatchArgument, $node->lineno ?? 0, $i + 1, $parameter_name, $argument_type_expanded, (string) $method->getFQSEN(), (string) $parameter_type, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart());
}
}
}
}