Skip to content

Commit 1392c9e

Browse files
committed
Implement a custom fix all provider for SA1133
1 parent 73afee8 commit 1392c9e

2 files changed

Lines changed: 163 additions & 6 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1133CodeFixProvider.cs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace StyleCop.Analyzers.ReadabilityRules
66
using System.Collections.Generic;
77
using System.Collections.Immutable;
88
using System.Composition;
9+
using System.Linq;
910
using System.Threading;
1011
using System.Threading.Tasks;
1112
using Helpers;
@@ -33,7 +34,7 @@ internal class SA1133CodeFixProvider : CodeFixProvider
3334
/// <inheritdoc/>
3435
public override FixAllProvider GetFixAllProvider()
3536
{
36-
return CustomFixAllProviders.BatchFixer;
37+
return FixAll.Instance;
3738
}
3839

3940
/// <inheritdoc/>
@@ -58,12 +59,22 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
5859
var nodeInSourceSpan = syntaxRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
5960
AttributeListSyntax attributeList = nodeInSourceSpan.FirstAncestorOrSelf<AttributeListSyntax>();
6061

61-
var newAttributeLists = new List<AttributeListSyntax>();
62-
6362
var indentationOptions = IndentationOptions.FromDocument(document);
6463
var indentationSteps = IndentationHelper.GetIndentationSteps(indentationOptions, attributeList);
6564
var indentationTrivia = IndentationHelper.GenerateWhitespaceTrivia(indentationOptions, indentationSteps);
6665

66+
List<AttributeListSyntax> newAttributeLists = GetNewAttributeList(attributeList, indentationTrivia);
67+
68+
var newSyntaxRoot = syntaxRoot.ReplaceNode(attributeList, newAttributeLists);
69+
var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());
70+
71+
return newDocument;
72+
}
73+
74+
private static List<AttributeListSyntax> GetNewAttributeList(AttributeListSyntax attributeList, SyntaxTrivia indentationTrivia)
75+
{
76+
var newAttributeLists = new List<AttributeListSyntax>();
77+
6778
for (var i = 0; i < attributeList.Attributes.Count; i++)
6879
{
6980
var newAttributes = SyntaxFactory.SingletonSeparatedList(attributeList.Attributes[i]);
@@ -80,10 +91,41 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
8091
newAttributeLists.Add(newAttributeList);
8192
}
8293

83-
var newSyntaxRoot = syntaxRoot.ReplaceNode(attributeList, newAttributeLists);
84-
var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());
94+
return newAttributeLists;
95+
}
8596

86-
return newDocument;
97+
private class FixAll : DocumentBasedFixAllProvider
98+
{
99+
public static FixAllProvider Instance { get; } =
100+
new FixAll();
101+
102+
protected override string CodeActionTitle =>
103+
ReadabilityResources.SA1133CodeFix;
104+
105+
protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fixAllContext, Document document)
106+
{
107+
var diagnostics = await fixAllContext.GetDocumentDiagnosticsAsync(document).ConfigureAwait(false);
108+
if (diagnostics.IsEmpty)
109+
{
110+
return null;
111+
}
112+
113+
var indentationOptions = IndentationOptions.FromDocument(document);
114+
var syntaxRoot = await document.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
115+
116+
var nodes = diagnostics.Select(diagnostic => syntaxRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true).FirstAncestorOrSelf<AttributeListSyntax>());
117+
118+
var newRoot = syntaxRoot.TrackNodes(nodes);
119+
120+
foreach (var attributeList in nodes)
121+
{
122+
var indentationSteps = IndentationHelper.GetIndentationSteps(indentationOptions, attributeList);
123+
var indentationTrivia = IndentationHelper.GenerateWhitespaceTrivia(indentationOptions, indentationSteps);
124+
newRoot = newRoot.ReplaceNode(newRoot.GetCurrentNode(attributeList), GetNewAttributeList(attributeList, indentationTrivia));
125+
}
126+
127+
return newRoot;
128+
}
87129
}
88130
}
89131
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1133UnitTests.cs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,121 @@ internal class BarAttribute : Attribute
276276
await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false);
277277
}
278278

279+
/// <summary>
280+
/// Regression test for issue 1879 (SA1133CodeFixProvider does only half the work), https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1879
281+
/// </summary>
282+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
283+
[Fact]
284+
public async Task TestFixAllAsync()
285+
{
286+
var testCode = @"
287+
namespace Stylecop_rc1_bug_repro
288+
{
289+
class Foo
290+
{
291+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
292+
public string Foo1{ get; set; }
293+
294+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
295+
public string Foo2{ get; set; }
296+
297+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
298+
public string Foo3 { get; set; }
299+
300+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
301+
public string Foo4{ get; set; }
302+
303+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
304+
public string Foo5{ get; set; }
305+
306+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
307+
public string Foo6{ get; set; }
308+
309+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
310+
public string Foo7{ get; set; }
311+
312+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
313+
public string Foo8{ get; set; }
314+
315+
}
316+
}
317+
318+
public class CanBeNullAttribute : System.Attribute { }
319+
public class UsedImplicitly : System.Attribute
320+
{
321+
public UsedImplicitly (ImplicitUseKindFlags flags) { }
322+
}
323+
324+
public enum ImplicitUseKindFlags { Assign }
325+
";
326+
327+
var fixedTestCode = @"
328+
namespace Stylecop_rc1_bug_repro
329+
{
330+
class Foo
331+
{
332+
[CanBeNull]
333+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
334+
public string Foo1{ get; set; }
335+
336+
[CanBeNull]
337+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
338+
public string Foo2{ get; set; }
339+
340+
[CanBeNull]
341+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
342+
public string Foo3 { get; set; }
343+
344+
[CanBeNull]
345+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
346+
public string Foo4{ get; set; }
347+
348+
[CanBeNull]
349+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
350+
public string Foo5{ get; set; }
351+
352+
[CanBeNull]
353+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
354+
public string Foo6{ get; set; }
355+
356+
[CanBeNull]
357+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
358+
public string Foo7{ get; set; }
359+
360+
[CanBeNull]
361+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
362+
public string Foo8{ get; set; }
363+
364+
}
365+
}
366+
367+
public class CanBeNullAttribute : System.Attribute { }
368+
public class UsedImplicitly : System.Attribute
369+
{
370+
public UsedImplicitly (ImplicitUseKindFlags flags) { }
371+
}
372+
373+
public enum ImplicitUseKindFlags { Assign }
374+
";
375+
376+
DiagnosticResult[] expected =
377+
{
378+
this.CSharpDiagnostic().WithLocation(6, 21),
379+
this.CSharpDiagnostic().WithLocation(9, 21),
380+
this.CSharpDiagnostic().WithLocation(12, 21),
381+
this.CSharpDiagnostic().WithLocation(15, 21),
382+
this.CSharpDiagnostic().WithLocation(18, 21),
383+
this.CSharpDiagnostic().WithLocation(21, 21),
384+
this.CSharpDiagnostic().WithLocation(24, 21),
385+
this.CSharpDiagnostic().WithLocation(27, 21)
386+
};
387+
388+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
389+
await this.VerifyCSharpDiagnosticAsync(fixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
390+
await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false);
391+
await this.VerifyCSharpFixAllFixAsync(testCode, fixedTestCode, maxNumberOfIterations: 1).ConfigureAwait(false);
392+
}
393+
279394
/// <inheritdoc/>
280395
protected override CodeFixProvider GetCSharpCodeFixProvider()
281396
{

0 commit comments

Comments
 (0)