Skip to content

Compiler: Rationalize nodes, tokens, visitors, walkers, and rewriters, oh my! #11853

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

DustinCampbell
Copy link
Member

@DustinCampbell DustinCampbell commented May 9, 2025

Razor's syntax model doesn't provide as much separation between syntax nodes and tokens as Roslyn's. This is due in part to the fact that Roslyn's model is designed to support two languages, not just one. When Razor's syntax model was adapted from Roslyn's, some mistakes were made when modifying it fit a single language. Currently, the base hierarchy of Razor's syntax model like so:

---
  config:
    class:
      hideEmptyMembersBox: true
---
classDiagram
    SyntaxNode <|-- SyntaxList
    SyntaxNode <|-- RazorSyntaxNode
    RazorSyntaxNode <|-- SyntaxToken
    RazorSyntaxNode <|-- RazorBlockSyntax
    RazorSyntaxNode <|-- `...Add Other RazorSyntaxNodes`
Loading

A glaring mistake in the class hierarchy above is that SyntaxToken inherits from RazorSyntaxNode, which is reserved for Razor non-terminal syntax nodes. This PR changes SyntaxToken to inherit from SyntaxNode and moves several methods intended for non-terminals to `RazorSyntaxNode. This creates a much more sensible hierarchy:

---
  config:
    class:
      hideEmptyMembersBox: true
---
classDiagram
    SyntaxNode <|-- SyntaxList
    SyntaxNode <|-- SyntaxToken
    SyntaxNode <|-- RazorSyntaxNode
    RazorSyntaxNode <|-- RazorBlockSyntax
    RazorSyntaxNode <|-- `...And Other RazorSyntaxNodes`
Loading

This PR adds much cleaner separation between SyntaxNode, SyntaxList and SyntaxToken, and updates the SyntaxVisitor, SyntaxWalker, and SyntaxRewriter APIs to better match Roslyn's. This removes a lot of baggage from SyntaxToken, making it easier to convert to a struct in the near future.


CI Build: https://dev.azure.com/dnceng/internal/_build/results?buildId=2706052&view=results
Test Insertion: https://dev.azure.com/devdiv/DevDiv/_git/VS/pullrequest/635607
Toolset Test Run: https://dev.azure.com/dnceng/internal/_build/results?buildId=2706053&view=results

SyntaxToken should inherit from SyntaxNode rather than RazorSyntaxNode.
- Add nullability annotations
- Make Visit method target RazorSyntaxNode
- Handle SyntaxTokens separately
The SyntaxWalker is the only visitor that takes a TextSpan to limit what nodes and tokens are visited. However, that feature was only used by the SemanticTokensVisitor in the tooling layer. This change moves the range checking feature out of SyntaxWalker and into the visitor that needs it.
Update existing SyntaxWalker and SyntaxRewriter descendants for nullability and other API changes.
There are two syntax serializers with slightly different output: one used to implement SyntaxNode.SerializedValue and FindToken unit tests, and one used by legacy syntax tests.

This change unifies both syntax serializers by inheriting from the same base class: SyntaxSerializer. The implementation has been greatly simplified to a single SyntaxWalker. In the end there are two simple serializers:

1. SyntaxNode.Serializer - This is protected and nested inside of SyntaxNode. It's used to implement the SerializedValue property for RazorSyntaxNode and SyntaxToken.
2. TestSyntaxSerializer - This is used by all tests and customizes the output of the serializer by overriding a couple of methods.
The SyntaxReplacer needs to override VisitToken to enable ReplaceToken. To do this, I've brought over more of the Roslyn implementation. Also, I've pushed the implementation of methods that call into SyntaxReplacer into RazorSyntaxNode (where they're meaningful) and made them abstract in SyntaxNode. The implementations in SyntaxList and SyntaxToken throw.
The DiagnostSyntaxWalker needs to override and handle VisitToken.
@DustinCampbell DustinCampbell requested review from a team as code owners May 9, 2025 23:16
Copy link
Member

@davidwengier davidwengier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Tooling

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants