Skip to content

Commit f123356

Browse files
Add Assert, AssertIfTrue and AssertIfFalse attributes
1 parent 10632c6 commit f123356

12 files changed

+1413
-5
lines changed

README.md

+42-1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ These are the available attributes and their corresponding PHPDoc annotations:
130130

131131
| Attribute | PHPDoc Annotations |
132132
|-------------------------------------------------------------------------------------------------------------------|--------------------|
133+
| [Assert](https://github.com/php-static-analysis/attributes/blob/main/doc/Assert.md) | `@assert` |
134+
| [AssertIfFalse](https://github.com/php-static-analysis/attributes/blob/main/doc/AssertIfFalse.md) | `@assert-if-false` |
135+
| [AssertIfTrue](https://github.com/php-static-analysis/attributes/blob/main/doc/AssertIfTrue.md) | `@assert-if-true` |
133136
| [DefineType](https://github.com/php-static-analysis/attributes/blob/main/doc/DefineType.md) | `@type` |
134137
| [Deprecated](https://github.com/php-static-analysis/attributes/blob/main/doc/Deprecated.md) | `@deprecated` |
135138
| [Immmutable](https://github.com/php-static-analysis/attributes/blob/main/doc/Immmutable.md) | `@immmutable` |
@@ -160,7 +163,7 @@ These are the available attributes and their corresponding PHPDoc annotations:
160163

161164
### Location of Param and ParamOut attributes
162165

163-
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:
166+
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:
164167

165168
```php
166169
use PhpStaticAnalysis\RectorRule\AnnotationsToAttributesRector;
@@ -177,6 +180,25 @@ return RectorConfig::configure()
177180
);
178181
```
179182

183+
### Location of Assert, AssertIfFalse and AssertIfTrue attributes
184+
185+
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:
186+
187+
```php
188+
use PhpStaticAnalysis\RectorRule\AnnotationsToAttributesRector;
189+
use Rector\Config\RectorConfig;
190+
...
191+
192+
return RectorConfig::configure()
193+
...
194+
->withConfiguredRule(
195+
AnnotationsToAttributesRector::class,
196+
[
197+
'addAssertAttributeOnParameters' => true,
198+
]
199+
);
200+
```
201+
180202
### Attribute to use for the return type of methods and functions
181203

182204
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:
@@ -215,6 +237,25 @@ return RectorConfig::configure()
215237
);
216238
```
217239

240+
### Attribute to use for the definition of types for classes
241+
242+
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:
243+
244+
```php
245+
use PhpStaticAnalysis\RectorRule\AnnotationsToAttributesRector;
246+
use Rector\Config\RectorConfig;
247+
...
248+
249+
return RectorConfig::configure()
250+
...
251+
->withConfiguredRule(
252+
AnnotationsToAttributesRector::class,
253+
[
254+
'useTypeAttributeForTypeClassAnnotation' => true,
255+
]
256+
);
257+
```
258+
218259
## Sponsor this project
219260

220261
If you would like to support the development of this project, please consider [sponsoring me](https://github.com/sponsors/carlos-granados)

composer.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
"prefer-stable": true,
2828
"require": {
2929
"php": ">=8.0",
30-
"php-static-analysis/attributes": "^0.3.0 || dev-main",
31-
"php-static-analysis/node-visitor": "^0.3.0 || dev-main",
30+
"php-static-analysis/attributes": "^0.3.1 || dev-main",
31+
"php-static-analysis/node-visitor": "^0.3.1 || dev-main",
3232
"rector/rector": "^0.19 || ^1.0"
3333
},
3434
"require-dev": {

config/sets/php-static-analysis-annotations-to-attributes.php

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
declare(strict_types=1);
44

5+
use PhpStaticAnalysis\Attributes\Assert;
6+
use PhpStaticAnalysis\Attributes\AssertIfFalse;
7+
use PhpStaticAnalysis\Attributes\AssertIfTrue;
58
use PhpStaticAnalysis\Attributes\DefineType;
69
use PhpStaticAnalysis\Attributes\Deprecated;
710
use PhpStaticAnalysis\Attributes\Immutable;
@@ -37,6 +40,9 @@
3740
->withConfiguredRule(
3841
AnnotationsToAttributesRector::class,
3942
[
43+
new AnnotationToAttribute('assert', Assert::class),
44+
new AnnotationToAttribute('assert_if_false', AssertIfFalse::class),
45+
new AnnotationToAttribute('assert_if_true', AssertIfTrue::class),
4046
new AnnotationToAttribute('deprecated', Deprecated::class),
4147
new AnnotationToAttribute('extends', TemplateExtends::class),
4248
new AnnotationToAttribute('immutable', Immutable::class),
@@ -69,6 +75,7 @@
6975
new AnnotationToAttribute('use', TemplateUse::class),
7076
new AnnotationToAttribute('var', Type::class),
7177
'addParamAttributeOnParameters' => false,
78+
'addAssertAttributeOnParameters' => false,
7279
'useTypeAttributeForReturnAnnotation' => false,
7380
'usePropertyAttributeForVarAnnotation' => false,
7481
'excludeAnnotations' => [],

src/AnnotationsToAttributesRector.php

+44-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
use PhpParser\Node\Stmt\Interface_;
2222
use PhpParser\Node\Stmt\Trait_;
2323
use PhpParser\Node\Stmt\TraitUse;
24+
use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagMethodValueNode;
25+
use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagPropertyValueNode;
26+
use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagValueNode;
2427
use PHPStan\PhpDocParser\Ast\PhpDoc\DeprecatedTagValueNode;
2528
use PHPStan\PhpDocParser\Ast\PhpDoc\ExtendsTagValueNode;
2629
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
@@ -42,6 +45,9 @@
4245
use PHPStan\PhpDocParser\Ast\PhpDoc\UsesTagValueNode;
4346
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
4447
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
48+
use PhpStaticAnalysis\Attributes\Assert;
49+
use PhpStaticAnalysis\Attributes\AssertIfFalse;
50+
use PhpStaticAnalysis\Attributes\AssertIfTrue;
4551
use PhpStaticAnalysis\Attributes\Param;
4652
use PhpStaticAnalysis\Attributes\ParamOut;
4753
use PhpStaticAnalysis\Attributes\Property;
@@ -69,6 +75,8 @@ final class AnnotationsToAttributesRector extends AbstractRector implements Conf
6975

7076
private bool $addParamAttributeOnParameters = false;
7177

78+
private bool $addAssertAttributeOnParameters = false;
79+
7280
private bool $useTypeAttributeForReturnAnnotation = false;
7381

7482
private bool $usePropertyAttributeForVarAnnotation = false;
@@ -148,6 +156,8 @@ public function configure(array $configuration): void
148156
$this->annotationsToAttributes[$tag] = $value;
149157
} elseif (is_bool($value) && $key == 'addParamAttributeOnParameters') {
150158
$this->addParamAttributeOnParameters = $value;
159+
} elseif (is_bool($value) && $key == 'addAssertAttributeOnParameters') {
160+
$this->addAssertAttributeOnParameters = $value;
151161
} elseif (is_bool($value) && $key == 'useTypeAttributeForReturnAnnotation') {
152162
$this->useTypeAttributeForReturnAnnotation = $value;
153163
} elseif (is_bool($value) && $key == 'usePropertyAttributeForVarAnnotation') {
@@ -210,12 +220,17 @@ public function refactor(Node $node): ?Node
210220
$this->attributeGroupNamedArgumentManipulator->decorate($attributeGroups);
211221
}
212222

213-
if ($this->addParamAttributeOnParameters &&
223+
if (($this->addParamAttributeOnParameters || $this->addAssertAttributeOnParameters) &&
214224
($node instanceof ClassMethod || $node instanceof Function_)) {
215225
foreach ($attributeGroups as $attrKey => $attributeGroup) {
216226
foreach ($attributeGroup->attrs as $key => $attribute) {
217227
$attributeName = (string)$attribute->name;
218-
if ($attributeName === Param::class || $attributeName == ParamOut::class) {
228+
if (
229+
(($attributeName === Param::class || $attributeName === ParamOut::class)
230+
&& $this->addParamAttributeOnParameters) ||
231+
(($attributeName === Assert::class || $attributeName === AssertIfFalse::class || $attributeName === AssertIfTrue::class)
232+
&& $this->addAssertAttributeOnParameters)
233+
) {
219234
$args = $attribute->args;
220235
if (isset($args[0])) {
221236
$arg = $args[0];
@@ -373,6 +388,33 @@ private function processAnnotations(PhpDocInfo $phpDocInfo): array
373388
)
374389
];
375390
break;
391+
case $tagValueNode instanceof AssertTagValueNode:
392+
case $tagValueNode instanceof AssertTagPropertyValueNode:
393+
case $tagValueNode instanceof AssertTagMethodValueNode:
394+
$type = (string)($tagValueNode->type);
395+
if ($tagValueNode->isNegated) {
396+
$type = '!' . $type;
397+
}
398+
if ($tagValueNode->isEquality) {
399+
$type = '=' . $type;
400+
}
401+
if ($tagValueNode instanceof AssertTagValueNode) {
402+
$args = [
403+
new Arg(
404+
value: new String_($type),
405+
name: new Identifier(substr($tagValueNode->parameter, 1))
406+
)
407+
];
408+
} else {
409+
if ($tagValueNode instanceof AssertTagPropertyValueNode) {
410+
$type .= ' ' . $tagValueNode->parameter . '->' . $tagValueNode->property;
411+
} else {
412+
$type .= ' ' . $tagValueNode->parameter . '->' . $tagValueNode->method . '()';
413+
}
414+
$args = [new Arg(new String_($type))];
415+
}
416+
$attributeComment = $tagValueNode->description;
417+
break;
376418
default:
377419
continue 2;
378420
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace test\PhpStaticAnalysis\RectorRule;
6+
7+
use Iterator;
8+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
9+
10+
final class AnnotationsToAttributesWithAssertOnParamRectorTest extends AbstractRectorTestCase
11+
{
12+
/**
13+
* @dataProvider provideData()
14+
*/
15+
public function test(string $filePath): void
16+
{
17+
$this->doTestFile($filePath);
18+
}
19+
20+
public static function provideData(): Iterator
21+
{
22+
yield [__DIR__ . '/SpecialFixture/AssertAttributeTestWithAssertOnParam.php.inc'];
23+
yield [__DIR__ . '/SpecialFixture/AssertIfFalseAttributeTestWithAssertOnParam.php.inc'];
24+
yield [__DIR__ . '/SpecialFixture/AssertIfTrueAttributeTestWithAssertOnParam.php.inc'];
25+
}
26+
27+
public function provideConfigFilePath(): string
28+
{
29+
return __DIR__ . '/config/configured-rule-with-assert-on-param.php';
30+
}
31+
}

0 commit comments

Comments
 (0)