Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
romalytvynenko committed Jun 28, 2024
1 parent 83007b3 commit d8a88bc
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 39 deletions.
10 changes: 3 additions & 7 deletions src/Infer/Definition/ClassDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,9 @@ public function getMethodDefinition(string $name, Scope $scope = new GlobalScope
new FileNameResolver(new NameContext(new Throwing())),
);

if (ReferenceTypeResolver::hasResolvableReferences($returnType = $this->methods[$name]->type->getReturnType())) {
$this->methods[$name]->type->setReturnType(
(new ReferenceTypeResolver($scope->index))
->resolve($methodScope, $returnType)
->mergeAttributes($returnType->attributes())
);
}
(new ReferenceTypeResolver($scope->index))
->resolveFunctionReturnReferences($scope, $this->methods[$name]->type);

foreach ($this->methods[$name]->type->exceptions as $i => $exceptionType) {
if (ReferenceTypeResolver::hasResolvableReferences($exceptionType)) {
$this->methods[$name]->type->exceptions[$i] = (new ReferenceTypeResolver($scope->index))
Expand Down
31 changes: 31 additions & 0 deletions src/Infer/Services/ReferenceTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Dedoc\Scramble\Infer\Scope\Index;
use Dedoc\Scramble\Infer\Scope\Scope;
use Dedoc\Scramble\Support\Type\CallableStringType;
use Dedoc\Scramble\Support\Type\FunctionLikeType;
use Dedoc\Scramble\Support\Type\FunctionType;
use Dedoc\Scramble\Support\Type\Generic;
use Dedoc\Scramble\Support\Type\ObjectType;
Expand Down Expand Up @@ -49,6 +50,36 @@ public static function getInstance(): static
return app(static::class);
}

public function resolveFunctionReturnReferences(Scope $scope, FunctionType $functionType): void
{
if (static::hasResolvableReferences($returnType = $functionType->getReturnType())) {
$resolvedReference = $this->resolve($scope, $returnType);
$functionType->setReturnType($resolvedReference);
}

if ($annotatedReturnType = $functionType->getAttribute('returnTypeAnnotation')) {
// $functionType->setReturnType(
// $this->addAnnotatedReturnType($functionType->getReturnType(), $annotatedReturnType)
// );
}
}

private function addAnnotatedReturnType(Type $inferredReturnType, Type $annotatedReturnType): Type
{
$types = $inferredReturnType instanceof Union
? $inferredReturnType->types
: [$inferredReturnType];

$annotatedTypeCanAcceptAnyInferredType = collect($types)
->some(fn (Type $t) => $annotatedReturnType->accepts($t));

if (! $annotatedTypeCanAcceptAnyInferredType) {
$types = [$annotatedReturnType];
}

return Union::wrap($types)->mergeAttributes($inferredReturnType->attributes());
}

public static function hasResolvableReferences(Type $type): bool
{
return (bool) (new TypeWalker)->first(
Expand Down
21 changes: 1 addition & 20 deletions src/Support/RouteInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
use Dedoc\Scramble\Support\Type\Type;
use Dedoc\Scramble\Support\Type\TypeTraverser;
use Dedoc\Scramble\Support\Type\TypeWalker;
use Dedoc\Scramble\Support\Type\Union;
use Dedoc\Scramble\Support\Type\UnknownType;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Resources\Json\JsonResource;
Expand Down Expand Up @@ -209,26 +208,8 @@ public function getCodeReturnType()
return null;
}

$inferredType = (new ObjectType($this->reflectionMethod()->getDeclaringClass()->getName()))
return (new ObjectType($this->reflectionMethod()->getDeclaringClass()->getName()))
->getMethodReturnType($this->methodName());

$inferredTypes = $inferredType instanceof Union
? $inferredType->types
: [$inferredType];

/*
* Despite us respecting manually annotated type, there may be an inferred type that contains more information
* than annotated type. In fact, any inferred type will contain more information than annotated type. Hence,
* we want to make sure that we omit annotated type.
*/
if (
($annotationType = $this->getMethodType()->getAttribute('returnTypeAnnotation'))
&& ! $this->inferredTypesContainMoreConcreteAnnotatedType($inferredTypes, $annotationType)
) {
$inferredTypes = [$annotationType, ...$inferredTypes];
}

return Union::wrap($inferredTypes);
}

private function inferredTypesContainMoreConcreteAnnotatedType(array $inferredTypes, Type $annotationType): bool
Expand Down
5 changes: 5 additions & 0 deletions src/Support/Type/AbstractType.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public function isInstanceOf(string $className)
return false;
}

public function accepts(Type $otherType): bool
{
return is_a($this::class, $otherType::class, true);
}

public function getPropertyType(string $propertyName, Scope $scope): Type
{
$className = $this::class;
Expand Down
13 changes: 13 additions & 0 deletions src/Support/Type/ArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ public function nodes(): array
return ['value', 'key'];
}

public function accepts(Type $otherType): bool
{
if (parent::accepts($otherType)) {
return true;
}

if ($otherType instanceof KeyedArrayType) {
return true;
}

return false;
}

public function isSame(Type $type)
{
return false;
Expand Down
9 changes: 9 additions & 0 deletions src/Support/Type/ObjectType.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ public function getMethodReturnType(string $methodName, array $arguments = [], S
: $returnType;
}

public function accepts(Type $otherType): bool
{
if (! $otherType instanceof ObjectType) {
return false;
}

return is_a($this->name, $otherType->name, true);
}

public function toString(): string
{
return $this->name;
Expand Down
2 changes: 2 additions & 0 deletions src/Support/Type/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public function getAttribute(string $key);

public function isInstanceOf(string $className);

public function accepts(Type $otherType): bool;

public function nodes(): array;

public function getPropertyType(string $propertyName, Scope $scope): Type;
Expand Down
6 changes: 6 additions & 0 deletions src/Support/Type/TypeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ public static function createTypeFromTypeNode(Node $typeNode)
return new FloatType();
}

if ($typeNode->name === 'array') {
return new ArrayType(
value: new MixedType(),
);
}

return new ObjectType($typeNode->toString());
}

Expand Down
20 changes: 20 additions & 0 deletions tests/Infer/AnnotatedReturnTypesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

it('generates function type with generic correctly', function (string $returnAnnotation, string $returnExpression, string $inferredReturnTypeString) {
$type = analyzeFile(<<<"EOD"
<?php
function foo (): {$returnAnnotation} {
return {$returnExpression};
}
EOD)->getFunctionDefinition('foo');

expect($type->type->returnType->toString())->toBe($inferredReturnTypeString);
})->with([
['Foo_AnnotatedReturnTypesTest', 'new Foo_AnnotatedReturnTypesTest(42)', 'Foo_AnnotatedReturnTypesTest<int(42)>'],
['int', 'new Foo_AnnotatedReturnTypesTest(42)', 'int'],
['Foo_AnnotatedReturnTypesTest', '42', 'Foo_AnnotatedReturnTypesTest'],
]);

class Foo_AnnotatedReturnTypesTest {
public function __construct(private int $wow){}
}
14 changes: 2 additions & 12 deletions tests/Pest.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,24 +73,14 @@ function analyzeClass(string $className, array $extensions = []): AnalysisResult

function resolveReferences(Index $index, ReferenceTypeResolver $referenceResolver)
{
$resolveReferencesInFunctionReturn = function ($scope, $functionType) use ($referenceResolver) {
if (! ReferenceTypeResolver::hasResolvableReferences($returnType = $functionType->getReturnType())) {
return;
}

$resolvedReference = $referenceResolver->resolve($scope, $returnType);

$functionType->setReturnType($resolvedReference);
};

foreach ($index->functionsDefinitions as $functionDefinition) {
$fnScope = new Scope(
$index,
new NodeTypesResolver,
new ScopeContext(functionDefinition: $functionDefinition),
new FileNameResolver(new NameContext(new Throwing())),
);
$resolveReferencesInFunctionReturn($fnScope, $functionDefinition->type);
$referenceResolver->resolveFunctionReturnReferences($fnScope, $functionDefinition->type);
}

foreach ($index->classesDefinitions as $classDefinition) {
Expand All @@ -101,7 +91,7 @@ function resolveReferences(Index $index, ReferenceTypeResolver $referenceResolve
new ScopeContext($classDefinition, $methodDefinition),
new FileNameResolver(new NameContext(new Throwing())),
);
$resolveReferencesInFunctionReturn($methodScope, $methodDefinition->type);
$referenceResolver->resolveFunctionReturnReferences($methodScope, $methodDefinition->type);
}
}
}
Expand Down

0 comments on commit d8a88bc

Please sign in to comment.