Description
What happens
The constructor initialiser is gated solely on the argument list being non-empty:
// CodeBlocker/Templates/ConstructorTemplate.cs:50-61
private void WriteInitialiserTo(CodeBlocker codeBlocker)
{
if (BaseParameters.Count == 0)
{
return;
}
// The initialiser goes on its own line, indented below the declaration it belongs to.
codeBlocker.WriteLine();
using IndentScope indent = new(codeBlocker);
codeBlocker.Write($": {(ChainsToThis ? "this" : "base")}({string.Join(", ", BaseParameters)})");
}ChainsToThis is unreachable when BaseParameters is empty, so : this() — a zero-argument delegation to another constructor of the same type — cannot be expressed at all.
Failure scenario
new ConstructorTemplate
{
Name = "Widget",
Keywords = { "public" },
Parameters = { new ParameterTemplate { Type = "int", Name = "count" } },
ChainsToThis = true, // intent: ": this()"
BodyFactory = cb => cb.WriteLine("Count = count;"),
}renders public Widget(int count) with no initialiser — that is, an implicit : base(). The parameterless Widget() constructor never runs, so whatever field initialisation or validation it performs is skipped.
Why it matters
The output compiles cleanly and produces different runtime behaviour in the generated code, with nothing in the output or the API to signal it.
ChainsToThis's own XML doc (:22-26) says nothing about the dependency on BaseParameters, and BaseParameters' doc (:16-20) describes only that an empty collection omits the : base(...) clause — which is correct for base and wrong for this.
Missing coverage
CodeBlocker.Test/TemplateTests.cs:410-411 exercises ChainsToThis = true only with BaseParameters = { "0" }. The empty case is untested.
Suggested fix
Either:
- emit the initialiser whenever
ChainsToThisis set as well as whenBaseParametersis non-empty, producing: this()for an empty list; or - replace the inference with a tri-state
InitialiserKind(None/Base/This), so "no initialiser" is stated rather than inferred from an empty collection.
The second is the cleaner shape — BaseParameters.Count == 0 is currently carrying two unrelated meanings. Either way, add a test for the zero-argument this() form.
Activity
matt-edmondson commentedon Sep 11, 2026
Triage
Priority: Medium Effort: Small
What needs to be done:
ChainsToThisis unreachable wheneverBaseParametersis empty, so: this()(zero-argument same-type delegation) can never be emitted — the generated constructor silently gets an implicit: base()instead, skipping whatever the parameterless constructor does.Suggested next steps / acceptance criteria:
ChainsToThisis set, independent ofBaseParameters.Count, producing: this()for the empty-argument case — or replace the inference with an explicit tri-stateInitialiserKind(None/Base/This) as the issue suggests, which is the cleaner long-term shape.ChainsToThis = truewith an emptyBaseParameters, alongside the existing non-empty case inTemplateTests.cs:410-411.ChainsToThis's XML doc to state the current (post-fix) relationship toBaseParametersexplicitly.Blockers / dependencies: None identified. Self-contained, single-file change.
Generated by Claude Code