You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Unknown TypeKind or AccessorKind writes half a declaration before throwing, and an Expression accessor with no BodyFactory emits get => ; #142
#128 (fixed by #140) made two paths validate before writing: a property with no accessors, and an unknown OperatorKind. Three related paths are still unfixed. Two of them, which #128 itself named, write output first and throw afterwards. The third emits code that does not compile and raises no error.
Unknown ClassTemplate.Kind.ClassTemplate.WriteTo (CodeBlocker/Templates/ClassTemplate.cs:67-69) calls base.WriteTo first, which writes the doc comment, attributes and modifiers. Only after that does it call TypeKindKeywords.For(Kind), which throws ArgumentOutOfRangeException. For a nested class, the parent's using Scope then closes onto the unfinished line.
Unknown AccessorTemplate.Kind.PropertyTemplate.WriteTo writes Type Name, a newline and {, then indents. Only then does AccessorTemplate.WriteTo reach default: throw new InvalidOperationException("Unknown accessor kind ...") (AccessorTemplate.cs:107).
Expression accessor with a null body.new AccessorTemplate { Kind = AccessorKind.Expression } leaves BodyFactory == null, and object-initializer syntax allows it. AccessorTemplate.cs:92-95 passes that null to TemplateRendering.WriteExpressionBody, which writes get => ;. The output does not compile and nothing reports it.
varouter=newClassTemplate{Name="Outer"};outer.NestedClasses.Add(newClassTemplate{Name="Inner",Kind=(TypeKind)99,Keywords={"public"}});cb.AddClass(outer);// throws ArgumentOutOfRangeException; buffer: "class Outer\n{\n\tpublic }\n"varc=newClassTemplate{Name="C"};c.Members.Add(newFieldTemplate{Type="int",Name="a"});c.Members.Add(newPropertyTemplate{Type="int",Name="P",Keywords={"public"},Getter=newAccessorTemplate{Kind=(AccessorKind)99}});cb.AddClass(c);// throws; buffer ends "\tpublic int P\n\t{\n\t}\n"vard=newClassTemplate{Name="C"};d.Members.Add(newPropertyTemplate{Type="int",Name="P",Getter=newAccessorTemplate{Kind=AccessorKind.Expression}});cb.AddClass(d);// no exception; emits "\t\tget => ;"
Why it matters
A source generator might catch the exception and keep using the same CodeBlocker. It then emits corrupt source, the exact failure #128 set out to prevent. Cases 1 and 2 need an out-of-range enum cast to trigger. Case 3 needs only ordinary object-initializer code, and the generator ships C# that fails to compile with no diagnostic.
Suggested fix / acceptance criteria
ClassTemplate.WriteTo resolves the type keyword (or validates Kind) before calling base.WriteTo.
PropertyTemplate.WriteTo validates Getter and Setter before writing anything:
each Kind must be a defined AccessorKind
an Expression (and Block) accessor must have a non-null BodyFactory
On failure it throws InvalidOperationException and writes nothing.
Priority: Medium. Cases 1 and 2 need an out-of-range enum cast (Low on their own). Case 3 (Kind = Expression with no BodyFactory) is reachable from ordinary object-initializer code and silently emits non-compiling C# (get => ;), which raises the overall priority.
Activity
matt-edmondson commentedon Oct 18, 2026
Triage
Kind = Expressionwith noBodyFactory) is reachable from ordinary object-initializer code and silently emits non-compiling C# (get => ;), which raises the overall priority.ClassTemplate.WriteTo(resolve the keyword beforebase.WriteTo),PropertyTemplate.WriteTo(validateGetter/Setterkind and non-nullBodyFactorybefore writing). Tests in the style of the PropertyTemplate with no accessors writes half its declaration before throwing, leavingpublic int Foo}in the output #128 tests.public int Foo}in the output #128 (fixed by Reject an accessorless property or unknown operator kind before writing anything [patch] #140), which named cases 1 and 2.Generated by Claude Code