protected function verifyReturn($function, State $state) { if (!$function->stmts) { // interface return []; } $errors = []; if ($function->returnType) { $type = Type::fromDecl($function->returnType->value); } else { $type = Type::extractTypeFromComment("return", $function->getAttribute('doccomment')); if (Type::mixed()->equals($type)) { // only verify actual types return $errors; } } $returns = $this->findReturnBlocks($function->stmts); foreach ($returns as $return) { if (!$return || !$return->expr) { // Default return, no if ($type->allowsNull()) { continue; } if (!$return) { $errors[] = ["Default return found for non-null type {$type}", $function]; } else { $errors[] = ["Explicit null return found for non-null type {$type}", $return]; } } elseif (!$return->expr->type) { var_dump($return->expr); $errors[] = ["Could not resolve type for return", $return]; } else { if (!$state->resolver->resolves($return->expr->type, $type)) { $errors[] = ["Type mismatch on return value, found {$return->expr->type} expecting {$type}", $return]; } } } return $errors; }
/** * @return array */ protected function verifyInternalCall($func, $call, $state, $name) { $errors = []; foreach ($func['params'] as $idx => $param) { if (!isset($call->args[$idx])) { if (substr($param['name'], -1) !== '=') { $errors[] = ["Missing required argument {$idx} for call {$name}()", $call]; } continue; } if ($param['type'] && isset($call->args[$idx]->type)) { $type = Type::fromDecl($param['type']); if (is_string($call->args[$idx]->type)) { $call->args[$idx]->type = Type::fromDecl($call->args[$idx]->type); } if (!$state->resolver->resolves($call->args[$idx]->type, $type)) { $errors[] = ["Type mismatch on {$name}() argument {$idx}, found {$call->args[$idx]->type} expecting {$type}", $call]; } } } return $errors; }
protected function processTypeAssertion(Assertion\TypeAssertion $assertion, Operand $source, SplObjectStorage $resolved) { if ($assertion->value instanceof Operand) { if ($assertion->value instanceof Operand\Literal) { return Type::fromDecl($assertion->value->value); } if (isset($resolved[$assertion->value])) { return $resolved[$assertion->value]; } return false; } $subTypes = []; foreach ($assertion->value as $sub) { $subTypes[] = $subType = $this->processTypeAssertion($sub, $source, $resolved); if (!$subType) { // Not fully resolved yet return false; } } $type = $assertion->mode === Assertion::MODE_UNION ? Type::TYPE_UNION : Type::TYPE_INTERSECTION; return new Type($type, $subTypes); }