Skip to content

Convert Doctrine annotations from phpdoc-parser nodes, remove StaticDoctrineAnnotationParser - #8881

Merged
TomasVotruba merged 2 commits into
rectorphp:mainfrom
ondrejmirtes:doctrine-native-nodes
Oct 29, 2026
Merged

TomasVotruba merged 2 commits into
rectorphp:mainfrom
ondrejmirtes:doctrine-native-nodes

Conversation

@ondrejmirtes

@ondrejmirtes ondrejmirtes commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

And now a dessert 😉

phpdoc-parser already parses Doctrine annotations (since 1.22.0) to DoctrineTagValueNode. Before, DoctrineAnnotationDecorator printed these nodes back to a string and parsed the string again with StaticDoctrineAnnotationParser, ArrayParser and PlainValueParser. Now the decorator converts the phpdoc-parser nodes directly to DoctrineAnnotationTagValueNode, CurlyListNode, ArrayItemNode and StringNode. The public API does not change.

Removed: StaticDoctrineAnnotationParser, ArrayParser, PlainValueParser, NextTokenType, and the @\FQN text and description splitting in DoctrineAnnotationDecorator.

Changes in behaviour. Rector now uses the phpdoc-parser and Doctrine semantics:

  • A multi-line string keeps the * line prefixes in the docblock node, as in doctrine/annotations. When Rector converts it to an attribute, it removes the prefixes.
  • A {} list in a {} list is a CurlyListNode. Before, it was a PHP array.
  • An integer key stays an integer: {1="Foo"}. ArrayItemNode now prints integer keys. Before, a changed annotation lost them.
  • A nested annotation that is a positional argument has the key null. Before, the key was 0.
  • Single-quoted strings and quoted argument keys are not Doctrine syntax: @Foo('bar'), @Foo("key"="value"). phpdoc-parser gives InvalidTagValueNode for them, and Rector does not change them. Fixtures are updated.

Needs rectorphp/rector-doctrine#543 to be merged first.

@ondrejmirtes
ondrejmirtes marked this pull request as draft October 28, 2026 19:17
@ondrejmirtes
ondrejmirtes force-pushed the doctrine-native-nodes branch 3 times, most recently from 1293dde to 54899d0 Compare October 28, 2026 20:10
@ondrejmirtes
ondrejmirtes marked this pull request as ready for review October 28, 2026 21:58
@ondrejmirtes
ondrejmirtes force-pushed the doctrine-native-nodes branch from 54899d0 to 1815799 Compare October 29, 2026 05:56
@ondrejmirtes

Copy link
Copy Markdown
Contributor Author

This is ready.

@TomasVotruba

Copy link
Copy Markdown
Member

Thanks the updates 👍 Great to have these glitches resolve out of the box. I think we can leave the invalid docblock in Doctrine tags behind.

Let's merge 🚀

@TomasVotruba
TomasVotruba merged commit 9a6fdb0 into rectorphp:main Oct 29, 2026
51 checks passed
@bbrala

bbrala commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

MIght regres drupal rector a bit this, not 100% sure yet. We used part of this code to do annotation to attribute conversion for Drupal

@TomasVotruba

Copy link
Copy Markdown
Member

We still do annotation to attributes conversion as before.
Can you share broken example? https://getrector.com/demo uses latest dev-main Rector

@bbrala

bbrala commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Yeah will investigate and post back if there work in rector.

@bbrala

bbrala commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Well, seems this is not really an issue, but i did find a discrepancy between doctrines parser and rector.

https://getrector.com/demo/0ffc42df-7754-41b2-969e-b8e30dde6c1e

- doctrine/annotations (upstream, 2.0.x): the lexer skips whitespace and * between tokens; 
   in DocLexer::getNonCatchablePatterns() they are ['\s+', '\*+', '(.)']. The parser's MethodCall() 
   then checks whether the next token is (, so a newline plus * before the ( doesn't matter.
- Drupal's copy (Drupal\Component\Annotation\Doctrine\DocParser) uses the same lexer 
   patterns. I ran it on both forms from the demo and got the same values for each:
      next line: [{"id":"next_line","label":"Next line"}]
      same line: [{"id":"same_line","label":"Same line"}]

The cause is that PHPStan's phpdoc-parser, which Rector uses, only treats the tag as a Doctrine annotation when the ( comes straight after the tag name. Otherwise it gives the tag a plain text value. Rector's rule then turns that plain tag into an attribute with no arguments and drops the docblock text, so data is lost.

But it seems this might not be a rector issue but a phpstan issue ;x

samsonasik pushed a commit that referenced this pull request Oct 31, 2026
…8900)

On Windows, Git checks out the test files with CRLF, so the heredoc input
contains \r\n. Format-preserving printing correctly keeps those line endings,
so the output is normalized before comparing with LF-only expectations.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1908f-48b6-7423-b4cd-641c531e141f
@samsonasik

samsonasik commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

@bbrala how often the open parentheses on new line happen in projects?

@bbrala

bbrala commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

I only found one oublic drupal modules with this pattern. So I guess not that much. I dont know custom code of course. But its not wide spread

@samsonasik

Copy link
Copy Markdown
Member

@ondrejmirtes can parentheses in new line be supported, that would be great if it works out of the box

@bbrala

bbrala commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

It would need to be more greedy and do newlines. Might have performance implicatio s. Im not sure of the real life impact, but it would be more correct since that is how the doctrine parser works.

@samsonasik

Copy link
Copy Markdown
Member

as this changing code, correctness should more important than speed so it would be great if this not cause regression on allowed annotation syntax so would be great to have support out of the box.

bbrala added a commit to palantirnet/drupal-rector that referenced this pull request Nov 4, 2026
…rrayParser (#443)

rectorphp/rector-src#8881 removed StaticDoctrineAnnotationParser\ArrayParser,
so the rule failed to construct on Rector dev-main. The GenericTagValueNode
branch only produced a correct attribute for an argument-less tag; convert
that to an attribute without arguments and leave other unparsed tags alone.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants