[FR] Attributes: sniff to enforce attribute blocks to be on their own line
Setting the scene
PHP 8.0 introduced support for attributes via the #[...] syntax. At this moment, neither PHPCS itself, nor PHPCSExtra contain any sniffs to handle the formatting of attributes.
The PER Coding Standard from FIG, since PER 2.0, outlines a set of rules for attribute formatting to comply with, so using those rules as a starting point would allow for creating an initial set of sniffs to address attribute formatting.
Proposed new sniff: Universal.Attributes.OneBlockPerLine
To address these rules from PER:
Attributes on classes, methods, functions, constants and properties MUST be placed on their own line...
Notes for the implementation
This needs a good think on the sniff name as this rule does not apply to parameter attributes.
This also needs a good think about the error codes as there are multiple possible situations this sniff may need to address and it may be useful to differentiate between these via the error code(s).
Think:
- Multiple attribute blocks on one line.
- Attribute in the middle of a line with other code (arrow functions, closures).
- Trailing comment.
- Trailing tooling annotation (should not be auto-fixed!)
We may also need further clarification from the FIG PER team about how closures and arrow function attributes should be handled in the context of this rule.
Note: it may be possible to write this sniff for PHPCS itself as I'm not sure this sniff needs the utilities from PHPCSUtils. Mind - if the sniff goes into PHPCS itself, it will only become available in PHPCS 4.x, while if it goes into PHPCSExtra, it would be available for PHPCS 3.x as well as 4.x, while Extra hasn't dropped support for PHPCS 3.x.
Describe the solution you'd like
A new sniff as outlined above.
The sniff should be able to flag and auto-fix the following:
// OK.
#[AttributeOne]
#[AttributeTwo]
#[AttributeThree]
function foo() {}
// Errors.
#[AttributeOne] #[AttributeTwo] #[AttributeThree]
function foo() {}
$closure = #[MyAttribute] function() {...};
$arrow = #[MyAttribute] static fn() => ...;
/** Short docblock, auto-fixing this should be okay. */ #[AttributeOne]
function foo() {}
#[AttributeOne] // some arbitrary trailing comment -- this should not be auto-fixed as we don't know where to place the comment
function foo() {}
#[AttributeOne] // phpcs:ignore Something.Something -- this should not be auto-fixed as it would change the meaning of the annotation.
function foo() {}
#[AttributeOne] // @phpstan .... -- this should not be auto-fixed as it would change the meaning of the annotation.
function foo() {}
Also see the examples outlined in the PER documents (rules + migration guide).
Additional context (optional)
This ticket is part of a series of tickets related to PHP attributes and is the result of a detailed analysis of the rules as outlined in PER 2.0, as well as a critical look at what's still missing rule-wise.
- [ ] I intend to create a pull request to implement this feature.