diff --git a/CodeBlocker.Test/TemplateTests.cs b/CodeBlocker.Test/TemplateTests.cs index cbe4b4a..fe47d2e 100644 --- a/CodeBlocker.Test/TemplateTests.cs +++ b/CodeBlocker.Test/TemplateTests.cs @@ -648,6 +648,71 @@ public void ABodyThatWritesOnlyBlankLinesRendersAsAnEmptyBlock(int newLines) Assert.AreEqual("public void M() { }\n", Render(method)); } + [TestMethod] + public void ABodyThatWritesOnlyALineCommentIsBraced() => + // "void M() // nothing to reset" has no body at all (CS1002). + Assert.AreEqual( + "void M()\n{\n\t// nothing to reset\n}\n", + Render(new MethodTemplate + { + Type = "void", + Name = "M", + BodyFactory = codeBlocker => codeBlocker.WriteLine("// nothing to reset"), + })); + + [TestMethod] + public void ABodyThatWritesOnlyLineCommentsIsBraced() => + Assert.AreEqual( + "void M()\n{\n\t// a\n\t// b\n}\n", + Render(new MethodTemplate + { + Type = "void", + Name = "M", + BodyFactory = codeBlocker => + { + codeBlocker.WriteLine("// a"); + codeBlocker.WriteLine("// b"); + }, + })); + + [TestMethod] + public void AConstructorBodyThatWritesOnlyALineCommentIsBraced() => + Assert.AreEqual( + "C()\n{\n\t// no state\n}\n", + Render(new ConstructorTemplate + { + Name = "C", + BodyFactory = codeBlocker => codeBlocker.WriteLine("// no state"), + })); + + [TestMethod] + public void ABodyThatWritesOnlyAnEmptyPragmaScopeIsBraced() => + Assert.AreEqual( + "void M()\n{\n\t#pragma warning disable CS0168\n\t#pragma warning restore CS0168\n}\n", + Render(new MethodTemplate + { + Type = "void", + Name = "M", + BodyFactory = codeBlocker => + { + using PragmaScope pragma = new(codeBlocker, "CS0168"); + }, + })); + + [TestMethod] + [DataRow("=> x;")] + [DataRow("{ }")] + [DataRow("=> x; // trailing")] + public void AOneLineBodyWithCodeStaysOnTheDeclarationLine(string body) => + Assert.AreEqual( + $"int M() {body}\n", + Render(new MethodTemplate + { + Type = "int", + Name = "M", + BodyFactory = codeBlocker => codeBlocker.WriteLine(body), + })); + [TestMethod] public void AnExpressionBodyBetweenBlankLinesStaysOnTheDeclarationLine() => Assert.AreEqual( diff --git a/CodeBlocker/Templates/TemplateRendering.cs b/CodeBlocker/Templates/TemplateRendering.cs index a2625bd..0405eb0 100644 --- a/CodeBlocker/Templates/TemplateRendering.cs +++ b/CodeBlocker/Templates/TemplateRendering.cs @@ -294,6 +294,20 @@ internal static void WriteBody(CodeBlocker codeBlocker, Action? bod return; } + // A comment or a directive is not a body either: written after the declaration it leaves the + // member with none, so a body made only of them is wrapped in braces instead. A generator + // writing "// no fields to reset" in place of an empty loop would otherwise emit CS1002. + if (!HasCode(lines)) + { + codeBlocker.WriteLine(); + using (new Scope(codeBlocker)) + { + SpliceFragment(codeBlocker, body); + } + + return; + } + if (contentLines.Length == 1) { // An expression body stays on the declaration line. @@ -372,6 +386,27 @@ internal static void WriteExpressionBody(CodeBlocker codeBlocker, Action + /// Reports whether any line of a fragment holds code: something other than a line comment or a + /// preprocessor directive. + /// + /// The lines of a fragment, as returns them. + /// if at least one line has code on it. + private static bool HasCode(string[] lines) + { + int[] codeEnds = FindCodeEnds(lines); + for (int i = 0; i < lines.Length; i++) + { + string code = lines[i][..codeEnds[i]].Trim(); + if (code.Length != 0 && code[0] != '#') + { + return true; + } + } + + return false; + } + /// /// Finds where the code on each line ends: at the start of the line comment the line ends in, /// or at the end of the line when it has none.