Skip to content
Merged
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
33 changes: 33 additions & 0 deletions CodeBlocker.Test/DocCommentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,39 @@ public void ATemplateWritesItsDocumentationAboveItsAttributes()
Render(method.WriteTo));
}

[TestMethod]
[DataRow("\n", DisplayName = "LF")]
[DataRow("\r\n", DisplayName = "CRLF")]
[DataRow("\r", DisplayName = "CR")]
[DataRow("\u0085", DisplayName = "NEL")]
[DataRow("\u2028", DisplayName = "LINE SEPARATOR")]
[DataRow("\u2029", DisplayName = "PARAGRAPH SEPARATOR")]
public void ALineBreakInAnAttributeValueIsRejectedBeforeWriting(string terminator)
{
string value = $"A{terminator}B";
DocComment[] comments =
[
new() { Summary = "s", SeeAlso = { value } },
new() { Summary = "s", InheritDoc = true, InheritDocCref = value },
new() { Summary = "s", Params = { new DocTag { Name = value, Text = "t" } } },
new() { Summary = "s", TypeParams = { new DocTag { Name = value, Text = "t" } } },
new() { Summary = "s", Exceptions = { new DocTag { Name = value, Text = "t" } } },
];

foreach (DocComment documentation in comments)
{
using CodeBlocker codeBlocker = CodeBlocker.Create(CodeBlocker.DefaultIndentString, NewLines.Lf);
Assert.ThrowsExactly<ArgumentException>(() => documentation.WriteTo(codeBlocker));
Assert.AreEqual(string.Empty, codeBlocker.ToString(), "Nothing should be written before the check.");
}
}

[TestMethod]
public void AnUnusedInheritDocCrefIsNotChecked() =>
Assert.AreEqual(
"/// <summary>s</summary>\n",
Render(new DocComment { Summary = "s", InheritDocCref = "A\nB" }));

[TestMethod]
public void NullCodeBlockerIsRejected() =>
Assert.ThrowsExactly<ArgumentNullException>(() => new DocComment().WriteTo(null!));
Expand Down
42 changes: 42 additions & 0 deletions CodeBlocker/Templates/DocComment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,19 @@ public class DocComment
/// </summary>
/// <param name="codeBlocker">The <see cref="CodeBlocker"/> to write to.</param>
/// <exception cref="ArgumentNullException"><paramref name="codeBlocker"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">
/// <see cref="InheritDocCref"/>, a <see cref="SeeAlso"/> entry, or the <see cref="DocTag.Name"/> of
/// a <see cref="TypeParams"/>, <see cref="Params"/> or <see cref="Exceptions"/> entry contains a
/// line break. Nothing is written.
/// </exception>
public void WriteTo(CodeBlocker codeBlocker)
{
Ensure.NotNull(codeBlocker);

// Checked before the first line is written: a cref or name cannot be split across /// lines
// the way element text is, and left as it is the rest of it would be compiled as code.
EnsureAttributesFitOnOneLine();

if (InheritDoc)
{
codeBlocker.WriteLine(string.IsNullOrEmpty(InheritDocCref)
Expand Down Expand Up @@ -181,6 +190,39 @@ void Check(Collection<DocTag> tags, HashSet<string> declared, string tagName, st
}
}

private void EnsureAttributesFitOnOneLine()
{
if (InheritDoc)
{
EnsureSingleLine(InheritDocCref, "inheritdoc cref", nameof(InheritDocCref));
}

EnsureNamesSingleLine(TypeParams, "typeparam name", nameof(TypeParams));
EnsureNamesSingleLine(Params, "param name", nameof(Params));
EnsureNamesSingleLine(Exceptions, "exception cref", nameof(Exceptions));

foreach (string cref in SeeAlso)
{
EnsureSingleLine(cref, "seealso cref", nameof(SeeAlso));
}

static void EnsureNamesSingleLine(Collection<DocTag> tags, string what, string paramName)
{
foreach (DocTag tag in tags)
{
EnsureSingleLine(tag.Name, what, paramName);
}
}

static void EnsureSingleLine(string? value, string what, string paramName)
{
if (value is not null && LineTerminators.Contains(value))
{
throw new ArgumentException($"A doc comment {what} must fit on one /// line.", paramName);
}
}
}

private void WriteElement(CodeBlocker codeBlocker, string tagName, string? attributes, string? text)
{
if (string.IsNullOrEmpty(text))
Expand Down
Loading