Skip to content

Commit

Permalink
Add Assert, AssertIfTrue and AssertIfFalse attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
carlos-granados committed Sep 12, 2024
1 parent 10632c6 commit f123356
Show file tree
Hide file tree
Showing 12 changed files with 1,413 additions and 5 deletions.
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ These are the available attributes and their corresponding PHPDoc annotations:

| Attribute | PHPDoc Annotations |
|-------------------------------------------------------------------------------------------------------------------|--------------------|
| [Assert](https://github.com/php-static-analysis/attributes/blob/main/doc/Assert.md) | `@assert` |
| [AssertIfFalse](https://github.com/php-static-analysis/attributes/blob/main/doc/AssertIfFalse.md) | `@assert-if-false` |
| [AssertIfTrue](https://github.com/php-static-analysis/attributes/blob/main/doc/AssertIfTrue.md) | `@assert-if-true` |
| [DefineType](https://github.com/php-static-analysis/attributes/blob/main/doc/DefineType.md) | `@type` |
| [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` |
Expand Down Expand Up @@ -160,7 +163,7 @@ These are the available attributes and their corresponding PHPDoc annotations:

### Location of Param and ParamOut attributes

By default `Param` and `ParamOut `attributes are added on the method/function where the `@param` or `@param-out` annotation was located. It is possible to instead add them on the corresponding parameter in the function. To activate this option, add this code to your configuration:
By default `Param` and `ParamOut` attributes are added on the method/function where the `@param` or `@param-out` annotation was located. It is possible to instead add them on the corresponding parameter in the function. To activate this option, add this code to your configuration:

```php
use PhpStaticAnalysis\RectorRule\AnnotationsToAttributesRector;
Expand All @@ -177,6 +180,25 @@ return RectorConfig::configure()
);
```

### Location of Assert, AssertIfFalse and AssertIfTrue attributes

By default `Assert`, `AssertIfFalse` and `AssertIfTrue` attributes are added on the method/function where the `@assert`, `@assert-if-false` or `@assert-if-true` annotation was located. It is possible to instead add them on the corresponding parameter in the function. 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,
[
'addAssertAttributeOnParameters' => true,
]
);
```

### 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:
Expand Down Expand Up @@ -215,6 +237,25 @@ return RectorConfig::configure()
);
```

### Attribute to use for the definition of types for classes

By default `DefineType` attributes are added to define a type for a class. 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,
[
'useTypeAttributeForTypeClassAnnotation' => true,
]
);
```

## Sponsor this project

If you would like to support the development of this project, please consider [sponsoring me](https://github.com/sponsors/carlos-granados)
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
"prefer-stable": true,
"require": {
"php": ">=8.0",
"php-static-analysis/attributes": "^0.3.0 || dev-main",
"php-static-analysis/node-visitor": "^0.3.0 || dev-main",
"php-static-analysis/attributes": "^0.3.1 || dev-main",
"php-static-analysis/node-visitor": "^0.3.1 || dev-main",
"rector/rector": "^0.19 || ^1.0"
},
"require-dev": {
Expand Down
7 changes: 7 additions & 0 deletions config/sets/php-static-analysis-annotations-to-attributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

declare(strict_types=1);

use PhpStaticAnalysis\Attributes\Assert;
use PhpStaticAnalysis\Attributes\AssertIfFalse;
use PhpStaticAnalysis\Attributes\AssertIfTrue;
use PhpStaticAnalysis\Attributes\DefineType;
use PhpStaticAnalysis\Attributes\Deprecated;
use PhpStaticAnalysis\Attributes\Immutable;
Expand Down Expand Up @@ -37,6 +40,9 @@
->withConfiguredRule(
AnnotationsToAttributesRector::class,
[
new AnnotationToAttribute('assert', Assert::class),
new AnnotationToAttribute('assert_if_false', AssertIfFalse::class),
new AnnotationToAttribute('assert_if_true', AssertIfTrue::class),
new AnnotationToAttribute('deprecated', Deprecated::class),
new AnnotationToAttribute('extends', TemplateExtends::class),
new AnnotationToAttribute('immutable', Immutable::class),
Expand Down Expand Up @@ -69,6 +75,7 @@
new AnnotationToAttribute('use', TemplateUse::class),
new AnnotationToAttribute('var', Type::class),
'addParamAttributeOnParameters' => false,
'addAssertAttributeOnParameters' => false,
'useTypeAttributeForReturnAnnotation' => false,
'usePropertyAttributeForVarAnnotation' => false,
'excludeAnnotations' => [],
Expand Down
46 changes: 44 additions & 2 deletions src/AnnotationsToAttributesRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\Node\Stmt\TraitUse;
use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagMethodValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagPropertyValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\DeprecatedTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ExtendsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
Expand All @@ -42,6 +45,9 @@
use PHPStan\PhpDocParser\Ast\PhpDoc\UsesTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PhpStaticAnalysis\Attributes\Assert;
use PhpStaticAnalysis\Attributes\AssertIfFalse;
use PhpStaticAnalysis\Attributes\AssertIfTrue;
use PhpStaticAnalysis\Attributes\Param;
use PhpStaticAnalysis\Attributes\ParamOut;
use PhpStaticAnalysis\Attributes\Property;
Expand Down Expand Up @@ -69,6 +75,8 @@ final class AnnotationsToAttributesRector extends AbstractRector implements Conf

private bool $addParamAttributeOnParameters = false;

private bool $addAssertAttributeOnParameters = false;

private bool $useTypeAttributeForReturnAnnotation = false;

private bool $usePropertyAttributeForVarAnnotation = false;
Expand Down Expand Up @@ -148,6 +156,8 @@ public function configure(array $configuration): void
$this->annotationsToAttributes[$tag] = $value;
} elseif (is_bool($value) && $key == 'addParamAttributeOnParameters') {
$this->addParamAttributeOnParameters = $value;
} elseif (is_bool($value) && $key == 'addAssertAttributeOnParameters') {
$this->addAssertAttributeOnParameters = $value;
} elseif (is_bool($value) && $key == 'useTypeAttributeForReturnAnnotation') {
$this->useTypeAttributeForReturnAnnotation = $value;
} elseif (is_bool($value) && $key == 'usePropertyAttributeForVarAnnotation') {
Expand Down Expand Up @@ -210,12 +220,17 @@ public function refactor(Node $node): ?Node
$this->attributeGroupNamedArgumentManipulator->decorate($attributeGroups);
}

if ($this->addParamAttributeOnParameters &&
if (($this->addParamAttributeOnParameters || $this->addAssertAttributeOnParameters) &&
($node instanceof ClassMethod || $node instanceof Function_)) {
foreach ($attributeGroups as $attrKey => $attributeGroup) {
foreach ($attributeGroup->attrs as $key => $attribute) {
$attributeName = (string)$attribute->name;
if ($attributeName === Param::class || $attributeName == ParamOut::class) {
if (
(($attributeName === Param::class || $attributeName === ParamOut::class)
&& $this->addParamAttributeOnParameters) ||
(($attributeName === Assert::class || $attributeName === AssertIfFalse::class || $attributeName === AssertIfTrue::class)
&& $this->addAssertAttributeOnParameters)
) {
$args = $attribute->args;
if (isset($args[0])) {
$arg = $args[0];
Expand Down Expand Up @@ -373,6 +388,33 @@ private function processAnnotations(PhpDocInfo $phpDocInfo): array
)
];
break;
case $tagValueNode instanceof AssertTagValueNode:
case $tagValueNode instanceof AssertTagPropertyValueNode:
case $tagValueNode instanceof AssertTagMethodValueNode:
$type = (string)($tagValueNode->type);
if ($tagValueNode->isNegated) {
$type = '!' . $type;
}
if ($tagValueNode->isEquality) {
$type = '=' . $type;
}
if ($tagValueNode instanceof AssertTagValueNode) {
$args = [
new Arg(
value: new String_($type),
name: new Identifier(substr($tagValueNode->parameter, 1))
)
];
} else {
if ($tagValueNode instanceof AssertTagPropertyValueNode) {
$type .= ' ' . $tagValueNode->parameter . '->' . $tagValueNode->property;
} else {
$type .= ' ' . $tagValueNode->parameter . '->' . $tagValueNode->method . '()';
}
$args = [new Arg(new String_($type))];
}
$attributeComment = $tagValueNode->description;
break;
default:
continue 2;
}
Expand Down
31 changes: 31 additions & 0 deletions tests/AnnotationsToAttributesWithAssertOnParamRectorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace test\PhpStaticAnalysis\RectorRule;

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

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

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

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured-rule-with-assert-on-param.php';
}
}
Loading

0 comments on commit f123356

Please sign in to comment.