[BetterPhpDocParser] Don't wrap first-position @method param union in extra parens#8003
Merged
TomasVotruba merged 1 commit intoMay 22, 2026
Conversation
@method param union in extra parens
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ran into this using rector's
withImportNameson a project that generates@methoddocblocks for Laravel facades via laravel/facade-documenter.Setup
facade-documenter writes
@methodlines with fully-qualified class names. A facade that imports its proxy ends up like:Then
vendor/bin/rector processruns. WithwithImportNamesenabled, rector shortens the FQCN to its imported alias, which forces it to reprint the entire@methodline.What it produces
Extra parens injected around the first parameter's union.
What it should produce
FQCN shortened, union left alone.
Cause
UnionTypeNodePhpDocNodeVisitor::isWrappedInCurlyBrackets()returns true if there's either a(immediately before the union or a)immediately after. For a union that's the first parameter of an@method, the(belongs to the method's parameter list, not a type wrap. But the check fires anyway, marksisWrappedInBrackets = true, and the reprinter wraps the union.The close-paren half of that check was also broken. It looked at
getEnd(), which is the position of the union's last token (e.g.array), not one past it. The comment// there is no + 1, as end is right at the next tokenassumedgetEnd()returns one past the union, butendIndexOfLastRelevantToken()returns the index of the last token. So the close check never actually matched anything, and the OR was effectively just running the open-paren check.Fix
Require both flanking parens, and correct the close offset to
getEnd() + 1:A union is wrapped only when both parens really do flank it.
Test
Added a fixture under
rules-tests/CodingStyle/Rector/Namespace_/ImportFullyQualifiedNamesRector/Fixture/reproducing the case via name-importing on a class with an@methodwhose first parameter is a union. Fails onmain, passes here. Full--testsuite=main(5201 tests) stays green.