Skip to content

Commit

Permalink
Support all attribute targets
Browse files Browse the repository at this point in the history
This adds support for properties, class constants, params, enums

Close #224
  • Loading branch information
spaze committed Dec 9, 2023
1 parent 67ea78d commit d8023ee
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 8 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ jobs:
run: composer lint
- php-version: "8.0"
run: composer lint
- php-version: "8.1"
run: composer lint
include:
- php-version: "7.2"
run: composer lint-7.x
Expand All @@ -38,6 +40,8 @@ jobs:
run: composer lint-7.x
- php-version: "8.0"
run: composer lint-8.0
- php-version: "8.1"
run: composer lint-8.1

steps:
- uses: actions/checkout@v4
Expand Down
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@
},
"scripts": {
"lint": "vendor/bin/parallel-lint --colors src/ tests/",
"lint-7.x": "vendor/bin/parallel-lint --colors src/ tests/ --exclude tests/src/TypesEverywhere.php --exclude tests/src/disallowed/functionCallsNamedParams.php --exclude tests/src/disallowed-allow/functionCallsNamedParams.php --exclude tests/src/disallowed/attributeUsages.php --exclude tests/src/disallowed-allow/attributeUsages.php",
"lint-8.0": "vendor/bin/parallel-lint --colors src/ tests/ --exclude tests/src/TypesEverywhere.php",
"lint-7.x": "vendor/bin/parallel-lint --colors src/ tests/ --exclude tests/src/TypesEverywhere.php --exclude tests/src/AttributesEverywhere.php --exclude tests/src/disallowed/functionCallsNamedParams.php --exclude tests/src/disallowed-allow/functionCallsNamedParams.php --exclude tests/src/disallowed/attributeUsages.php --exclude tests/src/disallowed-allow/attributeUsages.php",
"lint-8.0": "vendor/bin/parallel-lint --colors src/ tests/ --exclude tests/src/TypesEverywhere.php --exclude tests/src/AttributesEverywhere.php",
"lint-8.1": "vendor/bin/parallel-lint --colors src/ tests/ --exclude tests/src/AttributesEverywhere.php",
"lint-neon": "vendor/bin/neon-lint .",
"phpcs": "vendor/bin/phpcs src/ tests/",
"cs-fix": "vendor/bin/phpcbf src/ tests/",
Expand Down
12 changes: 12 additions & 0 deletions src/Usages/AttributeUsages.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
use PhpParser\Node\Attribute;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\EnumCase;
use PhpParser\Node\Stmt\Property;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use Spaze\PHPStan\Rules\Disallowed\DisallowedAttribute;
Expand Down Expand Up @@ -72,6 +76,14 @@ public function processNode(Node $node, Scope $scope): array
$this->addAttrs(array_values($node->attrGroups));
} elseif ($node instanceof FunctionLike) {
$this->addAttrs(array_values($node->getAttrGroups()));
} elseif ($node instanceof Property) {
$this->addAttrs(array_values($node->attrGroups));
} elseif ($node instanceof ClassConst) {
$this->addAttrs(array_values($node->attrGroups));
} elseif ($node instanceof Param) {
$this->addAttrs(array_values($node->attrGroups));
} elseif ($node instanceof EnumCase) {
$this->addAttrs(array_values($node->attrGroups));
} else {
return [];
}
Expand Down
34 changes: 33 additions & 1 deletion tests/Usages/AttributeUsagesAllowParamsMultipleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,25 @@ public function testRule(): void
// on this line:
8,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
12,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
15,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
18,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
30,
40,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
42,
],
]);
$this->analyse([__DIR__ . '/../src/disallowed-allow/ClassWithAttributesAllow.php'], [
Expand All @@ -85,10 +101,26 @@ public function testRule(): void
'Attribute Attributes\AttributeEntity is forbidden.',
12,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
15,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
18,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
22,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
28,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
42,
],
]);
}

Expand Down
89 changes: 88 additions & 1 deletion tests/Usages/AttributeUsagesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,99 @@ public function testRule(): void
// on this line:
8,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
12,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
15,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
18,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
30,
40,
],
[
'Attribute Attributes\AttributeEntity is forbidden.',
42,
],
]);
$this->analyse([__DIR__ . '/../src/disallowed-allow/ClassWithAttributesAllow.php'], []);

$this->analyse([__DIR__ . '/../src/AttributesEverywhere.php'], [
[
'Attribute Attributes\AttributeClass is forbidden.',
6,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
10,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
13,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
19,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
23,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
26,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
30,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
32,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
48,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
52,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
54,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
61,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
63,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
69,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
70,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
76,
],
[
'Attribute Attributes\AttributeClass is forbidden.',
77,
],
]);
}

}
79 changes: 79 additions & 0 deletions tests/src/AttributesEverywhere.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
declare(strict_types = 1);

namespace Attributes;

#[AttributeClass]
enum EnumWithAttributes
{

#[AttributeClass]
public const ENUM_CONST = true;

#[AttributeClass]
case Foo;

}


#[AttributeClass]
trait TraitWithAttributes
{

#[AttributeClass]
private const TRAIT_CONST = true;

#[AttributeClass]
private $bar;


#[AttributeClass]
public function traitMethod(
#[AttributeClass]
bool $param
): void {
}

}

// https://phpstan.org/blog/how-phpstan-analyses-traits
class ClassWithTraitWithAttributes
{

use TraitWithAttributes;

}


#[AttributeClass]
interface InterfaceWithAttributes
{

#[AttributeClass]
public function interfaceMethod(
#[AttributeClass]
bool $param
): void;

}


#[AttributeClass]
function functionWithAttributes(
#[AttributeClass]
int $param
): void {
}


$anonymousFunction = #[AttributeClass] function (
#[AttributeClass]
int $param
): void {
};


$arrowFunction = #[AttributeClass] fn(
#[AttributeClass]
int $param
) => 1;
16 changes: 14 additions & 2 deletions tests/src/disallowed-allow/ClassWithAttributesAllow.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@
class ClassWithAttributesAllow
{

#[AttributeEntity] // allowed by path in all tests
private const MAYO = true;

#[AttributeEntity] // allowed by path in all tests
public $cheddar = 'plz';

#[AttributeEntity] // disallowed
public static $pepper = 'ofc';


#[\Attributes\AttributeEntity(repositoryClass: \Attributes\UserRepository::class, readOnly: false)] // allowed by path in AttributeUsagesTest, disallowed in AttributeUsagesAllowParamsMultipleTest because $repositoryClass has other value
public function hasAvocado(): bool
{
Expand All @@ -28,8 +38,10 @@ public function hasKetchup(): bool


#[AttributeClass()] // allowed by path in all tests
public function hasPineapple(): bool
{
public function hasPineapple(
#[AttributeEntity] // allowed by path in all tests
bool $really
): bool {
}

}
16 changes: 14 additions & 2 deletions tests/src/disallowed/ClassWithAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@
class ClassWithAttributes
{

#[AttributeEntity] // disallowed
private const MAYO = true;

#[AttributeEntity] // disallowed
public $cheddar = 'plz';

#[AttributeEntity] // disallowed
public static $pepper = 'ofc';


#[AttributeEntity(repositoryClass: UserRepository::class, readOnly: false)] // disallowed, $repositoryClass present with any value
public function hasAvocado(): bool
{
Expand All @@ -28,8 +38,10 @@ public function hasKetchup(): bool


#[AttributeClass()] // disallowed
public function hasPineapple(): bool
{
public function hasPineapple(
#[AttributeEntity] // disallowed
bool $really
): bool {
}

}

0 comments on commit d8023ee

Please sign in to comment.