Skip to content

C#: Add precedence-aware parenthesization utility#7068

Merged
knutwannheden merged 12 commits intomainfrom
csharp-parenthesization-utility
Mar 20, 2026
Merged

C#: Add precedence-aware parenthesization utility#7068
knutwannheden merged 12 commits intomainfrom
csharp-parenthesization-utility

Conversation

@knutwannheden
Copy link
Contributor

Summary

  • Add CSharpPrecedences (internal): C# operator precedence table derived from the language spec, with GetPrecedence, NeedsParentheses, Parenthesize, and IsAssociative methods
  • Add CSharpParenthesizeVisitor: visitor that adds parentheses where needed based on operator precedence and context, with static MaybeParenthesize entry point for template integration
  • Add CSharpUnwrapParentheses: visitor that safely removes parentheses from a specific node when semantics are preserved
  • Wire MaybeParenthesize into CSharpTemplate.Apply so template substitutions auto-parenthesize

This eliminates the need for recipe authors to manually decide when parentheses are needed. Currently 8+ recipes in recipes-csharp re-implement their own NeedsParentheses checks with inconsistent logic.

Test plan

  • 47 unit tests for precedence table (CSharpPrecedencesTests)
  • 9 tests for parenthesization visitor (CSharpParenthesizeVisitorTests)
  • 6 tests for unwrap logic (CSharpUnwrapParenthesesTests)
  • All 1518 existing C# tests pass
  • Gradle :rewrite-csharp:csharpBuild succeeds
  • Validate by simplifying a recipe in recipes-csharp (e.g., InvertIf, SimplifyBooleanComparison) to use the new utility

Precedence-aware parenthesization/unwrapping utility for the C# SDK,
modeled after Java's ParenthesizeVisitor and UnwrapParentheses.
- Add IsPattern, RangeExpression, SwitchExpression, WithExpression, Lambda
  to GetPrecedence dispatch and visitor methods
- Handle pattern combinator (and/or/not) precedence as separate sub-domain
- Add right-associativity handling for ?? in NeedsParentheses
- Expand IsUnwrappable structural parent list (ForLoop, FixedStatement, etc.)
- Include C#-specific types in MaybeParenthesize fast-exit check
- Document IsAssociative return values
- Fix Parenthesize helper to use expr.WithPrefix() not J.SetPrefix()
Five tasks: CSharpPrecedences, CSharpParenthesizeVisitor,
CSharpUnwrapParentheses, template integration, final verification.
The C# parser models ?? as Ternary with a NullCoalescing marker,
not as CsBinary.NullCoalescing. Update GetPrecedence, IsAssociative,
IsRightAssociative, and tests to handle both representations.
…Apply

Auto-parenthesizes expression substitutions after template application,
matching Java's JavaTemplate integration pattern.
- Unary inside different unary no longer gets unnecessary parens:
  all prefix unary operators are at the same precedence level (13),
  so -(~x), ~(!x), etc. are always unambiguous
- Ternary inside ternary now gets parens: C# parses a ? b : c ? d : e
  right-to-left, so the left ternary as a condition needs wrapping
- TypeCast no longer over-parenthesized: uses precedence-based logic
  instead of unconditional wrapping (prec 13 binds tighter than binary)
- Add VisitLambda override for recursive visitor (prec -2 like assignment)
- Add Lambda to IsPattern parent check in NeedsParenthesesInContext
- IsRightAssociative check now verifies both child and parent
- MaybeWrapAssignment now checks IsPattern parent
- NeedsParentheses parent parameter typed as Expression (was object)
Demonstrates that CSharpTemplate.Apply auto-parenthesizes when a
substitution produces a lower-precedence expression in a higher-precedence
context: replacing + with - inside a * context preserves the parens.
@knutwannheden knutwannheden merged commit 6547966 into main Mar 20, 2026
1 check passed
@knutwannheden knutwannheden deleted the csharp-parenthesization-utility branch March 20, 2026 07:53
@github-project-automation github-project-automation bot moved this from In Progress to Done in OpenRewrite Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant