Skip to content

Refuse an expression body that writes no expression, before writing [patch] - #178

Merged
matt-edmondson merged 4 commits into
mainfrom
claude/issue-175-empty-expression-body
Nov 4, 2026
Merged

matt-edmondson merged 4 commits into
mainfrom
claude/issue-175-empty-expression-body

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #175

What changed

WriteExpressionBody wrote the => arrow and a bare ; when the callback produced nothing. That gave int P => ; and get => ; (CS1525). Validate only caught a null factory.

  • TemplateRendering.RenderExpression renders the expression into lines. If every line is blank, it throws InvalidOperationException. WriteExpressionBody now takes those pre-rendered lines and no longer has a "wrote nothing" branch.
  • PropertyTemplate.WriteTo renders its ExpressionBodyFactory and any expression accessors before base.WriteTo, so a rejection leaves the CodeBlocker unchanged. That includes the doc comment.
  • AccessorTemplate.WriteTo (public) also renders the expression first. The new internal overload accepts the pre-rendered expression from PropertyTemplate.
  • The ExpressionBodyFactory, AccessorTemplate.WriteTo, Validate and PropertyTemplate.WriteTo docs now cover the empty-expression case.

Rendering the expression first doesn't change the output, because RenderFragment depends only on the indent string and the newline. The current indent doesn't matter.

Tests

  • AnEmptyExpressionBodyStillTerminatesTheDeclaration asserted the broken public int X => ; output. It is replaced by AnEmptyExpressionBodyIsRejectedBeforeAnythingIsWritten, which also checks that the doc comment isn't left behind.
  • New tests cover a body of only blank and whitespace lines, the issue's Getter = AccessorTemplate.Expression(_ => { }) repro, and a standalone AccessorTemplate.WriteTo. Each one asserts that the call throws and the buffer is unchanged.
  • With the fix reverted, all four tests fail. With it, all 252 tests pass. A local Sonar build reports only the five existing S2699 findings.

Related

#176 covers a different case: a callback that throws. This PR doesn't try to fix that one.


Generated by Claude Code

…patch]

An ExpressionBodyFactory, or an expression accessor's BodyFactory, that
wrote nothing or only blank lines rendered `int P => ;` / `get => ;`,
which never compiles. The expression is now rendered before the
declaration, and an empty one throws InvalidOperationException with the
CodeBlocker left unchanged, as a null factory already does.

Fixes #175

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW4VgeC1VNAtTYhxCrm
# Conflicts:
#	CodeBlocker/Templates/PropertyTemplate.cs

matt-edmondson commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor Author

I merged main into this branch to clear the conflict in PropertyTemplate.cs. WriteTo now calls main's Validate() first. It then renders the expression body, or an expression accessor's body, before it writes the declaration, as this PR intended.

One gap remains. Main's container pre-check (ClassTemplate.Validate()) cannot detect an empty expression body, because that is only known after the callback runs, and running the callback needs a writer. A class whose later property has an empty expression body can therefore still be left partly written. Closing the gap would mean running each callback twice, so I left it as it is.

Update: the earlier red CI came from the PragmaScope line-terminator failures on main, and #162 has fixed them there. Main has been merged into this branch, and at c9574d7 every check passes.


Generated by Claude Code

Main's empty-expression fallback is dropped: this branch refuses an empty
expression before anything is written, so the branch can't be reached.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW5jpiuVuBRXSgitSY1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW5jpiuVuBRXSgitSY1
@sonarqubecloud

sonarqubecloud Bot commented Nov 4, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 44840db into main Nov 4, 2026
15 checks passed
@matt-edmondson
matt-edmondson deleted the claude/issue-175-empty-expression-body branch November 4, 2026 04:10
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.

An expression-body callback that writes nothing renders int P => ; / get => ; (CS1525), the output Validate says it prevents

2 participants