Skip to content

Commit 4ffc43a

Browse files
committed
Fixed attributes parsing
1 parent fcdcf4a commit 4ffc43a

File tree

6 files changed

+73
-60
lines changed

6 files changed

+73
-60
lines changed

SlevomatCodingStandard/Helpers/AttributeHelper.php

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
namespace SlevomatCodingStandard\Helpers;
44

5-
use InvalidArgumentException;
65
use PHP_CodeSniffer\Files\File;
76
use function array_map;
87
use function in_array;
9-
use function sprintf;
108
use const T_ANON_CLASS;
119
use const T_ATTRIBUTE;
10+
use const T_ATTRIBUTE_END;
1211
use const T_CLASS;
12+
use const T_CLOSE_CURLY_BRACKET;
1313
use const T_CLOSURE;
1414
use const T_COMMA;
1515
use const T_CONST;
@@ -18,7 +18,9 @@
1818
use const T_FN;
1919
use const T_FUNCTION;
2020
use const T_INTERFACE;
21+
use const T_OPEN_CURLY_BRACKET;
2122
use const T_OPEN_PARENTHESIS;
23+
use const T_SEMICOLON;
2224
use const T_TRAIT;
2325
use const T_VARIABLE;
2426

@@ -44,21 +46,9 @@ class AttributeHelper
4446

4547
public static function hasAttribute(File $phpcsFile, int $pointer, string $attributeName): bool
4648
{
47-
$docCommentOpenPointer = DocCommentHelper::findDocCommentOpenPointer($phpcsFile, $pointer);
48-
if ($docCommentOpenPointer === null) {
49-
return false;
50-
}
51-
52-
$tokens = $phpcsFile->getTokens();
53-
$nextPointer = TokenHelper::findNextEffective($phpcsFile, $docCommentOpenPointer + 1);
54-
55-
if ($nextPointer === null || $tokens[$nextPointer]['code'] !== T_ATTRIBUTE) {
56-
return false;
57-
}
58-
5949
$attributeNames = array_map(
6050
static fn (Attribute $name): string => $name->getFullyQualifiedName(),
61-
self::getAttributes($phpcsFile, $nextPointer),
51+
self::getAttributes($phpcsFile, $pointer),
6252
);
6353

6454
return in_array($attributeName, $attributeNames, true);
@@ -67,14 +57,36 @@ public static function hasAttribute(File $phpcsFile, int $pointer, string $attri
6757
/**
6858
* @return list<Attribute>
6959
*/
70-
public static function getAttributes(File $phpcsFile, int $attributeOpenerPointer): array
60+
public static function getAttributes(File $phpcsFile, int $pointer): array
7161
{
7262
$tokens = $phpcsFile->getTokens();
7363

74-
if ($tokens[$attributeOpenerPointer]['code'] !== T_ATTRIBUTE) {
75-
throw new InvalidArgumentException(
76-
sprintf('Token %d must be attribute, %s given.', $attributeOpenerPointer, $tokens[$attributeOpenerPointer]['type']),
77-
);
64+
if ($tokens[$pointer]['code'] !== T_ATTRIBUTE) {
65+
$attributeOpenerPointer = null;
66+
67+
do {
68+
$attributeEndPointerCandidate = TokenHelper::findPrevious(
69+
$phpcsFile,
70+
[T_ATTRIBUTE_END, T_SEMICOLON, T_CLOSE_CURLY_BRACKET, T_OPEN_CURLY_BRACKET],
71+
$attributeOpenerPointer ?? $pointer - 1,
72+
);
73+
74+
if (
75+
$attributeEndPointerCandidate === null
76+
|| $tokens[$attributeEndPointerCandidate]['code'] !== T_ATTRIBUTE_END
77+
) {
78+
break;
79+
}
80+
81+
$attributeOpenerPointer = $tokens[$attributeEndPointerCandidate]['attribute_opener'];
82+
} while (true);
83+
84+
if ($attributeOpenerPointer === null) {
85+
return [];
86+
}
87+
88+
} else {
89+
$attributeOpenerPointer = $pointer;
7890
}
7991

8092
$attributeCloserPointer = $tokens[$attributeOpenerPointer]['attribute_closer'];

SlevomatCodingStandard/Helpers/DocCommentHelper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ public static function isInline(File $phpcsFile, int $docCommentOpenPointer): bo
229229
$nextPointer !== null
230230
&& in_array(
231231
$tokens[$nextPointer]['code'],
232-
[T_PUBLIC, T_PROTECTED, T_PRIVATE, T_READONLY, T_FINAL, T_STATIC, T_ABSTRACT, T_CONST, T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM],
232+
[T_PUBLIC, T_PROTECTED, T_PRIVATE, T_READONLY, T_FINAL, T_STATIC, T_ABSTRACT, T_CONST, T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM, T_ATTRIBUTE],
233233
true,
234234
)
235235
) {

SlevomatCodingStandard/Sniffs/Classes/ClassStructureSniff.php

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use SlevomatCodingStandard\Helpers\DocCommentHelper;
1212
use SlevomatCodingStandard\Helpers\FixerHelper;
1313
use SlevomatCodingStandard\Helpers\FunctionHelper;
14-
use SlevomatCodingStandard\Helpers\NamespaceHelper;
1514
use SlevomatCodingStandard\Helpers\PropertyHelper;
1615
use SlevomatCodingStandard\Helpers\SniffSettingsHelper;
1716
use SlevomatCodingStandard\Helpers\StringHelper;
@@ -36,7 +35,6 @@
3635
use function substr;
3736
use const PREG_SPLIT_NO_EMPTY;
3837
use const T_ABSTRACT;
39-
use const T_ATTRIBUTE_END;
4038
use const T_CLOSE_CURLY_BRACKET;
4139
use const T_CONST;
4240
use const T_ENUM_CASE;
@@ -499,35 +497,10 @@ private function hasRequiredAttributes(File $phpcsFile, int $pointer, array $req
499497
*/
500498
private function getAttributeClassNamesForToken(File $phpcsFile, int $pointer): array
501499
{
502-
$tokens = $phpcsFile->getTokens();
503-
$attributePointer = null;
504500
$attributes = [];
505501

506-
while (true) {
507-
$attributeEndPointerCandidate = TokenHelper::findPrevious(
508-
$phpcsFile,
509-
[T_ATTRIBUTE_END, T_SEMICOLON, T_CLOSE_CURLY_BRACKET, T_OPEN_CURLY_BRACKET],
510-
$attributePointer ?? $pointer - 1,
511-
);
512-
513-
if (
514-
$attributeEndPointerCandidate === null
515-
|| $tokens[$attributeEndPointerCandidate]['code'] !== T_ATTRIBUTE_END
516-
) {
517-
break;
518-
}
519-
520-
$attributePointer = $tokens[$attributeEndPointerCandidate]['attribute_opener'];
521-
522-
foreach (AttributeHelper::getAttributes($phpcsFile, $attributePointer) as $attribute) {
523-
$attributeClass = NamespaceHelper::resolveClassName(
524-
$phpcsFile,
525-
$attribute->getName(),
526-
$attribute->getStartPointer(),
527-
);
528-
$attributeClass = ltrim($attributeClass, '\\');
529-
$attributes[strtolower($attributeClass)] = $attributeClass;
530-
}
502+
foreach (AttributeHelper::getAttributes($phpcsFile, $pointer) as $attribute) {
503+
$attributes[strtolower(ltrim($attribute->getFullyQualifiedName(), '\\'))] = $attribute->getFullyQualifiedName();
531504
}
532505

533506
return $attributes;

SlevomatCodingStandard/Sniffs/TypeHints/DisallowMixedTypeHintSniff.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
88
use SlevomatCodingStandard\Helpers\AnnotationHelper;
99
use SlevomatCodingStandard\Helpers\AttributeHelper;
10+
use SlevomatCodingStandard\Helpers\DocCommentHelper;
1011
use SlevomatCodingStandard\Helpers\SuppressHelper;
1112
use function sprintf;
1213
use function strtolower;
@@ -43,7 +44,12 @@ public function process(File $phpcsFile, $docCommentOpenPointer): void
4344
return;
4445
}
4546

46-
if (AttributeHelper::hasAttribute($phpcsFile, $docCommentOpenPointer, '\Override')) {
47+
$docCommentOwnerPointer = DocCommentHelper::findDocCommentOwnerPointer($phpcsFile, $docCommentOpenPointer);
48+
49+
if (
50+
$docCommentOwnerPointer !== null
51+
&& AttributeHelper::hasAttribute($phpcsFile, $docCommentOwnerPointer, '\Override')
52+
) {
4753
return;
4854
}
4955

tests/Helpers/AttributeHelperTest.php

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,29 @@
22

33
namespace SlevomatCodingStandard\Helpers;
44

5-
use InvalidArgumentException;
65
use function count;
76
use const T_ATTRIBUTE;
7+
use const T_FUNCTION;
88

99
class AttributeHelperTest extends TestCase
1010
{
1111

12+
public function testHasAttribute(): void
13+
{
14+
$phpcsFile = $this->getCodeSnifferFile(__DIR__ . '/data/attributes.php');
15+
16+
$functionsPointers = TokenHelper::findNextAll($phpcsFile, T_FUNCTION, 0);
17+
18+
self::assertCount(2, $functionsPointers);
19+
20+
foreach ($functionsPointers as $functionPointer) {
21+
self::assertTrue(
22+
AttributeHelper::hasAttribute($phpcsFile, $functionPointer, '\Attribute1'),
23+
FunctionHelper::getName($phpcsFile, $functionPointer),
24+
);
25+
}
26+
}
27+
1228
public function testGetAttributesInsideAttributeTags(): void
1329
{
1430
$phpcsFile = $this->getCodeSnifferFile(__DIR__ . '/data/classWithJoinedAttributes.php');
@@ -52,12 +68,4 @@ public function testIsValidAttributeAttributeAppliedIncorrectly(): void
5268
self::assertFalse(AttributeHelper::isValidAttribute($phpcsFile, $firstAttributePointer));
5369
}
5470

55-
public function testGetAttributesInsideAttributeTagsException(): void
56-
{
57-
$phpcsFile = $this->getCodeSnifferFile(__DIR__ . '/data/classWithJoinedAttributes.php');
58-
self::expectException(InvalidArgumentException::class);
59-
self::expectExceptionMessage('Token 0 must be attribute, T_OPEN_TAG given');
60-
AttributeHelper::getAttributes($phpcsFile, 0);
61-
}
62-
6371
}

tests/Helpers/data/attributes.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php // lint < 8.0
2+
3+
#[Attribute1]
4+
function foo()
5+
{
6+
}
7+
8+
/**
9+
* Doc comment
10+
*/
11+
#[Attribute1]
12+
function boo()
13+
{
14+
}

0 commit comments

Comments
 (0)