Skip to content

Commit

Permalink
Add Immutable attribute and the possibility to exclude some annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
carlos-granados committed Feb 28, 2024
1 parent c35515d commit 19cdb95
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 5 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,29 @@ return RectorConfig::configure()
);
```

If you want to replace most annotations but exclude a few, you can use the `excludeAnnotations` config parameter like this:

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

return RectorConfig::configure()
->withConfiguredRule(
AnnotationsToAttributesRector::class,
[
'excludeAnnotations' => ['throws', 'deprecated'],
]
);
```

That would convert all annotations except `@throws` and `@deprecated`

These are the available attributes and their corresponding PHPDoc annotations:

| Attribute | PHPDoc Annotations |
|-------------------------------------------------------------------------------------------------------------------|--------------------|
| [Deprecated](https://github.com/php-static-analysis/attributes/blob/main/doc/Deprecated.md) | `@deprecated` |
| [Immmutable](https://github.com/php-static-analysis/attributes/blob/main/doc/Immmutable.md) | `@immmutable` |
| [Impure](https://github.com/php-static-analysis/attributes/blob/main/doc/Impure.md) | `@impure` |
| [Internal](https://github.com/php-static-analysis/attributes/blob/main/doc/Internal.md) | `@internal` |
| [IsReadOnly](https://github.com/php-static-analysis/attributes/blob/main/doc/IsReadOnly.md) | `@readonly` |
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"require": {
"php": ">=8.0",
"cweagans/composer-patches": "^1.7",
"php-static-analysis/attributes": "^0.1.16 || dev-main",
"php-static-analysis/attributes": "^0.1.17 || dev-main",
"rector/rector": "^0.19 || ^1.0"
},
"require-dev": {
Expand Down
3 changes: 3 additions & 0 deletions config/sets/php-static-analysis-annotations-to-attributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
declare(strict_types=1);

use PhpStaticAnalysis\Attributes\Deprecated;
use PhpStaticAnalysis\Attributes\Immutable;
use PhpStaticAnalysis\Attributes\Impure;
use PhpStaticAnalysis\Attributes\Internal;
use PhpStaticAnalysis\Attributes\Method;
Expand Down Expand Up @@ -36,6 +37,7 @@
[
new AnnotationToAttribute('deprecated', Deprecated::class),
new AnnotationToAttribute('extends', TemplateExtends::class),
new AnnotationToAttribute('immutable', Immutable::class),
new AnnotationToAttribute('impure', Impure::class),
new AnnotationToAttribute('implements', TemplateImplements::class),
new AnnotationToAttribute('internal', Internal::class),
Expand Down Expand Up @@ -65,6 +67,7 @@
'addParamAttributeOnParameters' => false,
'useTypeAttributeForReturnAnnotation' => false,
'usePropertyAttributeForVarAnnotation' => false,
'excludeAnnotations' => [],
]
);
};
16 changes: 12 additions & 4 deletions src/AnnotationsToAttributesRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,19 +124,22 @@ public function addArrays(array $array1, array $array2): array
/**
* @psalm-suppress MoreSpecificImplementedParamType
*/
#[Param(configuration: '(AnnotationToAttribute|bool)[]')]
#[Param(configuration: '(AnnotationToAttribute|bool|string[])[]')]
public function configure(array $configuration): void
{
$excludedAnnotations = [];
foreach ($configuration as $key => $value) {
if ($value instanceof AnnotationToAttribute) {
$tag = str_replace('_', '-', $value->getTag());
$this->annotationsToAttributes[$tag] = $value;
} elseif ($key == 'addParamAttributeOnParameters') {
} elseif (is_bool($value) && $key == 'addParamAttributeOnParameters') {
$this->addParamAttributeOnParameters = $value;
} elseif ($key == 'useTypeAttributeForReturnAnnotation') {
} elseif (is_bool($value) && $key == 'useTypeAttributeForReturnAnnotation') {
$this->useTypeAttributeForReturnAnnotation = $value;
} elseif ($key == 'usePropertyAttributeForVarAnnotation') {
} elseif (is_bool($value) && $key == 'usePropertyAttributeForVarAnnotation') {
$this->usePropertyAttributeForVarAnnotation = $value;
} elseif (is_array($value) && $key == 'excludeAnnotations') {
$excludedAnnotations = $value;
}
}
if ($this->useTypeAttributeForReturnAnnotation) {
Expand All @@ -151,6 +154,11 @@ public function configure(array $configuration): void
new AnnotationToAttribute('var', Property::class);
}
}
foreach ($excludedAnnotations as $excludedAnnotation) {
if (isset($this->annotationsToAttributes[$excludedAnnotation])) {
unset($this->annotationsToAttributes[$excludedAnnotation]);
}
}
}

#[Returns('array<class-string<Node>>')]
Expand Down
29 changes: 29 additions & 0 deletions tests/ExcludeAnnotationsTest.php
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 ExcludeAnnotationsTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

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

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured-rule-with-exclude-annotations.php';
}
}
55 changes: 55 additions & 0 deletions tests/Fixture/ImmutableAttributeTest.php.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace test\PhpStaticAnalysis\RectorRule\Fixture;

use PhpStaticAnalysis\Attributes\Param;

/**
* @codeCoverageIgnore
* @immutable all properties are read only
*/
class ImmutableAttributeTest
{
}

/**
* @psalm-immutable
*/
class ImmutableAttributePsalmTest
{
}

/**
* @phpstan-immutable
*/
class ImmutableAttributePHPStanTest
{
}

?>
-----
<?php

namespace test\PhpStaticAnalysis\RectorRule\Fixture;

use PhpStaticAnalysis\Attributes\Param;

/**
* @codeCoverageIgnore
*/
#[\PhpStaticAnalysis\Attributes\Immutable] // all properties are read only
class ImmutableAttributeTest
{
}

#[\PhpStaticAnalysis\Attributes\Immutable]
class ImmutableAttributePsalmTest
{
}

#[\PhpStaticAnalysis\Attributes\Immutable]
class ImmutableAttributePHPStanTest
{
}

?>
51 changes: 51 additions & 0 deletions tests/SpecialFixture/ExcludeAnnotationsTest.php.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace test\PhpStaticAnalysis\RectorRule\Fixture;

use Exception;

/**
* @deprecated
* @template T
*/
class ExcludeAnnotationsTest
{
/**
* @var string
*/
public $name;

/**
* @throws Exception
*/
public function throwsException()
{
}
}

?>
-----
<?php

namespace test\PhpStaticAnalysis\RectorRule\Fixture;

use Exception;

/**
* @deprecated
*/
#[\PhpStaticAnalysis\Attributes\Template('T')]
class ExcludeAnnotationsTest
{
#[\PhpStaticAnalysis\Attributes\Type('string')]
public $name;

/**
* @throws Exception
*/
public function throwsException()
{
}
}

?>
19 changes: 19 additions & 0 deletions tests/config/configured-rule-with-exclude-annotations.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

use PhpStaticAnalysis\RectorRule\AnnotationsToAttributesRector;
use PhpStaticAnalysis\RectorRule\Set\PhpStaticAnalysisSetList;
use Rector\Config\RectorConfig;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([
PhpStaticAnalysisSetList::ANNOTATIONS_TO_ATTRIBUTES
]);
$rectorConfig->ruleWithConfiguration(
AnnotationsToAttributesRector::class,
[
'excludeAnnotations' => ['throws', 'deprecated'],
]
);
};

0 comments on commit 19cdb95

Please sign in to comment.