Skip to content

Commit

Permalink
Allow using the Type attribute to specify the return type of a functi…
Browse files Browse the repository at this point in the history
…on or method
  • Loading branch information
carlos-granados committed Feb 14, 2024
1 parent 08d95c0 commit f05ab43
Show file tree
Hide file tree
Showing 12 changed files with 316 additions and 5 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,22 @@ return RectorConfig::configure()
);
```

### Attribute to use for the return type of methods and functions

By default `Returns` attributes are added to define the return type of methods/functions. It is possible to use the `Type` attribute instead. To activate this option, add this code to your configuration:

```php
use PhpStaticAnalysis\RectorRule\AnnotationsToAttributesRector;
use Rector\Config\RectorConfig;
...

return RectorConfig::configure()
...
->withConfiguredRule(
AnnotationsToAttributesRector::class,
[
'useTypeAttributeForReturnAnnotation' => true,
]
);
```

2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"prefer-stable": true,
"require": {
"php": ">=8.0",
"php-static-analysis/attributes": "^0.1.1 || dev-main",
"php-static-analysis/attributes": "^0.1.2 || dev-main",
"rector/rector": "^0.19 || ^1.0"
},
"require-dev": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
new AnnotationToAttribute('template', Template::class),
new AnnotationToAttribute('var', Type::class),
'addParamAttributeOnParameters' => false,
'useTypeAttributeForReturnAnnotation' => false,
]
);
};
24 changes: 20 additions & 4 deletions src/AnnotationsToAttributesRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ final class AnnotationsToAttributesRector extends AbstractRector implements Conf

private bool $addParamAttributeOnParameters = false;

private bool $useTypeAttributeForReturnAnnotation = false;

public function __construct(
private PhpDocTagRemover $phpDocTagRemover,
private AttributeGroupNamedArgumentManipulator $attributeGroupNamedArgumentManipulator,
Expand Down Expand Up @@ -110,9 +112,17 @@ public function configure(array $configuration): void
{
foreach ($configuration as $key => $value) {
if ($value instanceof AnnotationToAttribute) {
$this->annotationsToAttributes[] = $value;
$this->annotationsToAttributes[$value->getTag()] = $value;
} elseif ($key == 'addParamAttributeOnParameters') {
$this->addParamAttributeOnParameters = $value;
} elseif ($key == 'useTypeAttributeForReturnAnnotation') {
$this->useTypeAttributeForReturnAnnotation = $value;
}
}
if ($this->useTypeAttributeForReturnAnnotation) {
if (isset($this->annotationsToAttributes['return'])) {
$this->annotationsToAttributes['return'] =
new AnnotationToAttribute('return', Type::class);
}
}
}
Expand Down Expand Up @@ -258,9 +268,15 @@ public function provideMinPhpVersion(): int
private function matchAnnotationToAttribute(
string $tagName
): AnnotationToAttribute|null {
foreach ($this->annotationsToAttributes as $annotationToAttribute) {
if ($tagName == '@' . $annotationToAttribute->getTag()) {
return $annotationToAttribute;
if (str_starts_with($tagName, '@')) {
$tagName = substr($tagName, 1);
if (str_starts_with($tagName, 'psalm-')) {
$tagName = substr($tagName, 6);
} elseif (str_starts_with($tagName, 'phpstan-')) {
$tagName = substr($tagName, 8);
}
if (isset($this->annotationsToAttributes[$tagName])) {
return $this->annotationsToAttributes[$tagName];
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace test\PhpStaticAnalysis\RectorRule;

use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class AnnotationsToAttributesWithTypeAttributeForReturnRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): Iterator
{
yield [__DIR__ . '/SpecialFixture/TypeAttributeTestForReturnAnnotation.php.inc'];
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured-rule-with-type-attribute-for-return.php';
}
}
16 changes: 16 additions & 0 deletions tests/Fixture/IsReadOnlyAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ class IsReadOnlyAttributeTest
*/
#[Type('string')]
public $otherName;

/**
* @psalm-readonly
*/
public $psalmName;

/**
* @phpstan-readonly
*/
public $phpstanName;
}

?>
Expand All @@ -46,6 +56,12 @@ class IsReadOnlyAttributeTest
#[Type('string')]
#[\PhpStaticAnalysis\Attributes\IsReadOnly]
public $otherName;

#[\PhpStaticAnalysis\Attributes\IsReadOnly]
public $psalmName;

#[\PhpStaticAnalysis\Attributes\IsReadOnly]
public $phpstanName;
}

?>
28 changes: 28 additions & 0 deletions tests/Fixture/ParamAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ class ParamAttributeTest
{
return $name1 . $name2;
}

/**
* @psalm-param string $name
*/
public function getPsalmName($name)
{
return $name;
}

/**
* @phpstan-param string $name
*/
public function getPHPStanName($name)
{
return $name;
}
}

/**
Expand Down Expand Up @@ -106,6 +122,18 @@ class ParamAttributeTest
{
return $name1 . $name2;
}

#[\PhpStaticAnalysis\Attributes\Param(name: 'string')]
public function getPsalmName($name)
{
return $name;
}

#[\PhpStaticAnalysis\Attributes\Param(name: 'string')]
public function getPHPStanName($name)
{
return $name;
}
}

#[\PhpStaticAnalysis\Attributes\Param(name: 'string')]
Expand Down
28 changes: 28 additions & 0 deletions tests/Fixture/ReturnsAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ class ReturnsAttributeTest
{
return "Hello " . $name;
}

/**
* @psalm-return string
*/
public function getPsalmName()
{
return 'John';
}

/**
* @phpstan-return string
*/
public function getPHPStanName()
{
return 'John';
}
}

/**
Expand Down Expand Up @@ -72,6 +88,18 @@ class ReturnsAttributeTest
{
return "Hello " . $name;
}

#[\PhpStaticAnalysis\Attributes\Returns('string')]
public function getPsalmName()
{
return 'John';
}

#[\PhpStaticAnalysis\Attributes\Returns('string')]
public function getPHPStanName()
{
return 'John';
}
}

#[\PhpStaticAnalysis\Attributes\Returns('string')]
Expand Down
28 changes: 28 additions & 0 deletions tests/Fixture/TemplateAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,22 @@ class TemplateAttributeTest
{
return "Hello " . $name;
}

/**
* @psalm-template T
*/
public function getPsalmName()
{
return 'John';
}

/**
* @phpstan-template T
*/
public function getPHPStanName()
{
return 'John';
}
}

/**
Expand Down Expand Up @@ -90,6 +106,18 @@ class TemplateAttributeTest
{
return "Hello " . $name;
}

#[\PhpStaticAnalysis\Attributes\Template('T')]
public function getPsalmName()
{
return 'John';
}

#[\PhpStaticAnalysis\Attributes\Template('T')]
public function getPHPStanName()
{
return 'John';
}
}

#[\PhpStaticAnalysis\Attributes\Template('T')]
Expand Down
16 changes: 16 additions & 0 deletions tests/Fixture/TypeAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ class TypeAttributeTest
*/
#[IsReadOnly]
public $otherName;

/**
* @psalm-var string
*/
public $psalmName;

/**
* @phpstan-var string
*/
public $phpstanName;
}

?>
Expand All @@ -46,6 +56,12 @@ class TypeAttributeTest
#[IsReadOnly]
#[\PhpStaticAnalysis\Attributes\Type('string')]
public $otherName;

#[\PhpStaticAnalysis\Attributes\Type('string')]
public $psalmName;

#[\PhpStaticAnalysis\Attributes\Type('string')]
public $phpstanName;
}

?>
Loading

0 comments on commit f05ab43

Please sign in to comment.