Skip to content

Commit 0e15788

Browse files
committed
Port #1957 to stabilization
Fix crash when running SA1623 code fix (cherry picked from commit 45c2c7d)
1 parent 1c701f5 commit 0e15788

3 files changed

Lines changed: 54 additions & 11 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/PropertySummaryDocumentationCodeFixProvider.cs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,15 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
4040
{
4141
foreach (Diagnostic diagnostic in context.Diagnostics)
4242
{
43-
context.RegisterCodeFix(
44-
CodeAction.Create(
45-
DocumentationResources.PropertySummaryStartTextCodeFix,
46-
cancellationToken => GetTransformedDocumentAsync(context.Document, diagnostic, cancellationToken),
47-
nameof(PropertySummaryDocumentationCodeFixProvider)),
48-
diagnostic);
43+
if (!diagnostic.Properties.ContainsKey(PropertySummaryDocumentationAnalyzer.NoCodeFixKey))
44+
{
45+
context.RegisterCodeFix(
46+
CodeAction.Create(
47+
DocumentationResources.PropertySummaryStartTextCodeFix,
48+
cancellationToken => GetTransformedDocumentAsync(context.Document, diagnostic, cancellationToken),
49+
nameof(PropertySummaryDocumentationCodeFixProvider)),
50+
diagnostic);
51+
}
4952
}
5053

5154
return SpecializedTasks.CompletedTask;
@@ -59,14 +62,18 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
5962
var documentation = node.GetDocumentationCommentTriviaSyntax();
6063

6164
var summaryElement = (XmlElementSyntax)documentation.Content.GetFirstXmlElement(XmlCommentHelper.SummaryXmlTag);
62-
var textElement = (XmlTextSyntax)summaryElement.Content.First();
65+
var textElement = (XmlTextSyntax)summaryElement.Content.FirstOrDefault();
66+
if (textElement == null)
67+
{
68+
return document;
69+
}
70+
6371
var textToken = textElement.TextTokens.First(token => token.IsKind(SyntaxKind.XmlTextLiteralToken));
6472
var text = textToken.ValueText;
65-
var newTextBuilder = StringBuilderPool.Allocate();
6673

6774
// preserve leading whitespace
6875
int index = 0;
69-
while (char.IsWhiteSpace(text, index))
76+
while (text.Length > index && char.IsWhiteSpace(text, index))
7077
{
7178
index++;
7279
}
@@ -85,14 +92,18 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
8592
modifiedText = text.Substring(index);
8693
}
8794

88-
modifiedText = char.ToLowerInvariant(modifiedText[0]) + modifiedText.Substring(1);
95+
if (modifiedText.Length > 0)
96+
{
97+
modifiedText = char.ToLowerInvariant(modifiedText[0]) + modifiedText.Substring(1);
98+
}
8999

90100
// create the new text string
91101
var textToAdd = diagnostic.Properties[PropertySummaryDocumentationAnalyzer.ExpectedTextKey];
92102
var newText = $"{preservedWhitespace}{textToAdd} {modifiedText}";
93103

94104
// replace the token
95-
var newTextTokens = textElement.TextTokens.Replace(textToken, SyntaxFactory.XmlTextLiteral(textToken.LeadingTrivia, newText, newText, textToken.TrailingTrivia));
105+
var newXmlTextLiteral = SyntaxFactory.XmlTextLiteral(textToken.LeadingTrivia, newText, newText, textToken.TrailingTrivia);
106+
var newTextTokens = textElement.TextTokens.Replace(textToken, newXmlTextLiteral);
96107
var newTextElement = textElement.WithTextTokens(newTextTokens);
97108

98109
var newSyntaxRoot = syntaxRoot.ReplaceNode(textElement, newTextElement);

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1623UnitTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,30 @@ private class PrivateTestClass
152152
await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false);
153153
}
154154

155+
/// <summary>
156+
/// Verifies that empty summary tag does not throw an exception.
157+
/// Regression test for #1943
158+
/// </summary>
159+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
160+
[Fact]
161+
public async Task EmptySummaryTagShouldNotThrowAnExceptionAsync()
162+
{
163+
var testCode = @"public class ClassName
164+
{
165+
/// <summary></summary>
166+
public int Property
167+
{
168+
get;
169+
}
170+
}";
171+
172+
var expected = this.CSharpDiagnostic(PropertySummaryDocumentationAnalyzer.SA1623Descriptor).WithLocation(4, 16).WithArguments("Gets");
173+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
174+
175+
var offeredFixes = await this.GetOfferedCSharpFixesAsync(testCode).ConfigureAwait(false);
176+
Assert.Empty(offeredFixes);
177+
}
178+
155179
/// <inheritdoc/>
156180
protected override CodeFixProvider GetCSharpCodeFixProvider()
157181
{

StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/PropertySummaryDocumentationAnalyzer.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ internal class PropertySummaryDocumentationAnalyzer : PropertyDocumentationBase
1919
{
2020
public const string ExpectedTextKey = "ExpectedText";
2121
public const string TextToRemoveKey = "TextToRemove";
22+
public const string NoCodeFixKey = "NoCodeFix";
2223

2324
private const string SA1623DiagnosticId = "SA1623";
2425
private const string SA1624DiagnosticId = "SA1624";
@@ -108,6 +109,13 @@ private static void AnalyzeSummaryElement(SyntaxNodeAnalysisContext context, Xml
108109
return;
109110
}
110111

112+
// Add a no code fix tag when the summary element is empty.
113+
// This will only impact SA1623, because SA1624 cannot trigger with an empty summary.
114+
if (summaryElement.Content.Count == 0)
115+
{
116+
diagnosticProperties.Add(NoCodeFixKey, string.Empty);
117+
}
118+
111119
var textElement = summaryElement.Content.FirstOrDefault() as XmlTextSyntax;
112120
var text = textElement == null ? string.Empty : XmlCommentHelper.GetText(textElement, true).TrimStart();
113121

0 commit comments

Comments
 (0)