Skip to content

Commit 76faf86

Browse files
committed
PSR12/ClassInstantiation: bug fix for attributes with anonymous classes
The `class` keyword was correctly tokenized as `T_ANON_CLASS`, even when there is an attribute between the `new` and the `class` keyword, however, the `PSR12.Classes.ClassInstantiation` sniff did not take that possibility into account, leading to false positives. This commit: * Adds code to skip over attributes. Note: I've elected to skip over attributes, but as these attributes AFAICS can only be added in combination with an anonymous class and anonymous classes are ignored by this sniff, it would also be a valid option to bow out of the sniff when an attribute is encountered. As this would be inconsistent with how the `T_ANON_CLASS` token was handled so far, I've not gone with that option. * Adds some additional extra guard code to prevent future false positives for when the class name could not be determined. Includes unit test. Fixes 3456
1 parent f268ca4 commit 76faf86

File tree

3 files changed

+26
-1
lines changed

3 files changed

+26
-1
lines changed

src/Standards/PSR12/Sniffs/Classes/ClassInstantiationSniff.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ public function process(File $phpcsFile, $stackPtr)
6363
continue;
6464
}
6565

66+
// Skip over potential attributes for anonymous classes.
67+
if ($tokens[$i]['code'] === T_ATTRIBUTE
68+
&& isset($tokens[$i]['attribute_closer']) === true
69+
) {
70+
$i = $tokens[$i]['attribute_closer'];
71+
continue;
72+
}
73+
6674
if ($tokens[$i]['code'] === T_OPEN_SQUARE_BRACKET
6775
|| $tokens[$i]['code'] === T_OPEN_CURLY_BRACKET
6876
) {
@@ -72,7 +80,7 @@ public function process(File $phpcsFile, $stackPtr)
7280

7381
$classNameEnd = $i;
7482
break;
75-
}
83+
}//end for
7684

7785
if ($classNameEnd === null) {
7886
return;
@@ -88,6 +96,11 @@ public function process(File $phpcsFile, $stackPtr)
8896
return;
8997
}
9098

99+
if ($classNameEnd === $stackPtr) {
100+
// Failed to find the class name.
101+
return;
102+
}
103+
91104
$error = 'Parentheses must be used when instantiating a new class';
92105
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'MissingParentheses');
93106
if ($fix === true) {

src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,9 @@ $a = new ${$varHoldingClassName};
3636
$class = new $obj?->classname();
3737
$class = new $obj?->classname;
3838
$class = new ${$obj?->classname};
39+
40+
// Issue 3456.
41+
// Anon classes should be skipped, even when there is an attribute between the new and the class keywords.
42+
$anonWithAttribute = new #[SomeAttribute('summary')] class {
43+
public const SOME_STUFF = 'foo';
44+
};

src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc.fixed

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,9 @@ $a = new ${$varHoldingClassName}();
3636
$class = new $obj?->classname();
3737
$class = new $obj?->classname();
3838
$class = new ${$obj?->classname}();
39+
40+
// Issue 3456.
41+
// Anon classes should be skipped, even when there is an attribute between the new and the class keywords.
42+
$anonWithAttribute = new #[SomeAttribute('summary')] class {
43+
public const SOME_STUFF = 'foo';
44+
};

0 commit comments

Comments
 (0)