Skip to content

Commit

Permalink
feat: added concated strings type
Browse files Browse the repository at this point in the history
  • Loading branch information
romalytvynenko committed Sep 24, 2023
1 parent a967b2b commit b498847
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 4 deletions.
29 changes: 29 additions & 0 deletions src/Infer/Handler/ConcatHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Dedoc\Scramble\Infer\Handler;

use Dedoc\Scramble\Infer\Scope\Scope;
use Dedoc\Scramble\Support\Type\ConcatenatedStringType;
use Dedoc\Scramble\Support\Type\SideEffects\SelfTemplateDefinition;
use Dedoc\Scramble\Support\Type\TemplateType;
use Dedoc\Scramble\Support\Type\TypeHelper;
use PhpParser\Node;

class ConcatHandler
{
public function shouldHandle($node)
{
return $node instanceof Node\Expr\BinaryOp\Concat;
}

public function leave(Node\Expr\BinaryOp\Concat $node, Scope $scope)
{
$scope->setType(
$node,
new ConcatenatedStringType(TypeHelper::flattenStringConcatTypes([
$scope->getType($node->left),
$scope->getType($node->right),
])),
);
}
}
2 changes: 2 additions & 0 deletions src/Infer/TypeInferer.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Dedoc\Scramble\Infer\Handler\ArrayItemHandler;
use Dedoc\Scramble\Infer\Handler\AssignHandler;
use Dedoc\Scramble\Infer\Handler\ClassHandler;
use Dedoc\Scramble\Infer\Handler\ConcatHandler;
use Dedoc\Scramble\Infer\Handler\CreatesScope;
use Dedoc\Scramble\Infer\Handler\ExceptionInferringExtensions;
use Dedoc\Scramble\Infer\Handler\ExpressionTypeInferringExtensions;
Expand Down Expand Up @@ -41,6 +42,7 @@ public function __construct(
$this->handlers = [
new FunctionLikeHandler(),
new AssignHandler(),
new ConcatHandler(),
new ClassHandler(),
new PropertyHandler(),
new ArrayHandler(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@

namespace Dedoc\Scramble\Support\OperationExtensions\RulesExtractor;

use Dedoc\Scramble\Infer;
use Dedoc\Scramble\Infer\Scope\GlobalScope;
use Dedoc\Scramble\Infer\Scope\Scope;
use Dedoc\Scramble\Support\RouteInfo;
use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use PhpParser\Node;
use PhpParser\NodeFinder;
use PhpParser\PrettyPrinter\Standard;
Expand Down Expand Up @@ -85,6 +81,9 @@ public function extract(RouteInfo $routeInfo)

if ($validationRules) {
$type = $routeInfo->getMethodScopeTypeResolver()->getType($validationRules);
// dump([
// $routeInfo->className().'@'.$routeInfo->methodName() => $type->toString(),
// ]);
}

if ($validationRules) {
Expand Down
28 changes: 28 additions & 0 deletions src/Support/Type/ConcatenatedStringType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Dedoc\Scramble\Support\Type;

class ConcatenatedStringType extends StringType
{
/**
* @param Type[] $parts Types of parts being concatenated.
*/
public function __construct(public array $parts)
{
}

public function nodes(): array
{
return ['parts'];
}

public function isSame(Type $type)
{
return $this === $type;
}

public function toString(): string
{
return parent::toString().'('.implode(', ', array_map(fn ($p) => $p->toString(), $this->parts)).')';
}
}
11 changes: 11 additions & 0 deletions src/Support/Type/TypeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,15 @@ public static function createTypeFromValue(mixed $value)

return null; // @todo: object
}

/**
* @param Type[] $parts
*/
public static function flattenStringConcatTypes(array $parts): array
{
return collect($parts)
->flatMap(fn ($t) => $t instanceof ConcatenatedStringType ? $t->parts : [$t])
->values()
->all();
}
}
11 changes: 11 additions & 0 deletions tests/Infer/Scope/ScopeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,14 @@ function getStatementTypeForScopeTest(string $statement, array $extensions = [])
['$foo->bar', 'unknown'],
['$foo->bar->{"baz"}', 'unknown'],
]);

it('infers concat string type', function ($code, $expectedTypeString) {
expect(getStatementTypeForScopeTest($code)->toString())->toBe($expectedTypeString);
})->with([
['"a"."b"."c"', 'string(string(a), string(b), string(c))'],
]);
it('infers concat string type with unknowns', function ($code, $expectedTypeString) {
expect(getStatementTypeForScopeTest($code)->toString())->toBe($expectedTypeString);
})->with([
['"a"."b".auth()->user()->id', 'string(string(a), string(b), unknown)'],
]);

0 comments on commit b498847

Please sign in to comment.