Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions CodeBlocker.Test/TemplateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,48 @@ public void AnExpressionBodyOfOnlyBlankLinesIsRejectedBeforeAnythingIsWritten()
Assert.AreEqual("int a;\n", codeBlocker.ToString());
}

[TestMethod]
public void AnExpressionBodyWithADefaultValueIsRejectedBeforeAnythingIsWritten()
{
// It used to render "int P => x;" and drop the "= 5" without a word.
PropertyTemplate property = new()
{
Type = "int",
Name = "P",
DefaultValue = "5",
ExpressionBodyFactory = codeBlocker => codeBlocker.Write("x"),
};

using CodeBlocker codeBlocker = CodeBlocker.Create(CodeBlocker.DefaultIndentString, NewLines.Lf);
codeBlocker.WriteLine("int a;");

InvalidOperationException exception =
Assert.ThrowsExactly<InvalidOperationException>(() => property.WriteTo(codeBlocker));
Assert.Contains("'P'", exception.Message);
Assert.AreEqual("int a;\n", codeBlocker.ToString());
}

[TestMethod]
public void AClassWithAnExpressionBodiedPropertyThatHasADefaultValueWritesNothing()
{
ClassTemplate type = new() { Name = "C" };
type.Members.Add(new FieldTemplate { Name = "a", Type = "int" });
type.Members.Add(new PropertyTemplate
{
Type = "int",
Name = "P",
DefaultValue = "5",
ExpressionBodyFactory = codeBlocker => codeBlocker.Write("x"),
});

using CodeBlocker codeBlocker = CodeBlocker.Create(CodeBlocker.DefaultIndentString, NewLines.Lf);

InvalidOperationException exception =
Assert.ThrowsExactly<InvalidOperationException>(() => codeBlocker.AddClass(type));
Assert.Contains("'P'", exception.Message);
Assert.AreEqual(string.Empty, codeBlocker.ToString());
}

private static string RenderExpressionBody(Action<CodeBlocker> expressionFactory) =>
Render(new PropertyTemplate { Type = "int", Name = "X", Keywords = { "public" }, ExpressionBodyFactory = expressionFactory });

Expand Down
13 changes: 12 additions & 1 deletion CodeBlocker/Templates/PropertyTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ public class PropertyTemplate : MemberTemplate
/// <exception cref="ArgumentNullException"><paramref name="codeBlocker"/> is <see langword="null"/>.</exception>
/// <exception cref="InvalidOperationException">
/// <see cref="Getter"/>, <see cref="Setter"/> and <see cref="ExpressionBodyFactory"/> are all
/// <see langword="null"/>; or, with no expression body, an accessor's kind is unknown, or is
/// <see langword="null"/>; or <see cref="ExpressionBodyFactory"/> is set together with a
/// <see cref="TemplateBase.DefaultValue"/>; or, with no expression body, an accessor's kind is unknown, or is
/// <see cref="AccessorKind.Expression"/> with no <see cref="AccessorTemplate.BodyFactory"/>; or
/// the expression body, or an expression accessor's body, writes no expression. Nothing is
/// written in any of these cases.
Expand Down Expand Up @@ -98,6 +99,16 @@ internal override void Validate()
"Setter, or ExpressionBodyFactory.");
}

if (ExpressionBodyFactory is not null && HasDefaultValue)
{
// C# has no initialiser on an expression-bodied property, so writing either one would
// silently drop the other.
throw new InvalidOperationException(
$"Property '{Name}' has both an expression body and a default value. An " +
"expression-bodied property cannot have an initialiser; clear ExpressionBodyFactory " +
"or DefaultValue.");
}

if (ExpressionBodyFactory is null)
{
// Likewise checked up front: the accessor list is only reached after "Type Name {".
Expand Down
Loading