Skip to content

Commit b2c2f3b

Browse files
committed
Merge pull request #1893 from pdelvo/fix-1879
Implement a custom fix all provider for SA1133
2 parents 5004e23 + 1392c9e commit b2c2f3b

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
@@ -306,6 +306,121 @@ internal class BarAttribute : Attribute
306306
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
307307
}
308308

309+
/// <summary>
310+
/// Regression test for issue 1879 (SA1133CodeFixProvider does only half the work), https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1879
311+
/// </summary>
312+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
313+
[Fact]
314+
public async Task TestFixAllAsync()
315+
{
316+
var testCode = @"
317+
namespace Stylecop_rc1_bug_repro
318+
{
319+
class Foo
320+
{
321+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
322+
public string Foo1{ get; set; }
323+
324+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
325+
public string Foo2{ get; set; }
326+
327+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
328+
public string Foo3 { get; set; }
329+
330+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
331+
public string Foo4{ get; set; }
332+
333+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
334+
public string Foo5{ get; set; }
335+
336+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
337+
public string Foo6{ get; set; }
338+
339+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
340+
public string Foo7{ get; set; }
341+
342+
[CanBeNull, UsedImplicitly(ImplicitUseKindFlags.Assign)]
343+
public string Foo8{ get; set; }
344+
345+
}
346+
}
347+
348+
public class CanBeNullAttribute : System.Attribute { }
349+
public class UsedImplicitly : System.Attribute
350+
{
351+
public UsedImplicitly (ImplicitUseKindFlags flags) { }
352+
}
353+
354+
public enum ImplicitUseKindFlags { Assign }
355+
";
356+
357+
var fixedTestCode = @"
358+
namespace Stylecop_rc1_bug_repro
359+
{
360+
class Foo
361+
{
362+
[CanBeNull]
363+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
364+
public string Foo1{ get; set; }
365+
366+
[CanBeNull]
367+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
368+
public string Foo2{ get; set; }
369+
370+
[CanBeNull]
371+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
372+
public string Foo3 { get; set; }
373+
374+
[CanBeNull]
375+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
376+
public string Foo4{ get; set; }
377+
378+
[CanBeNull]
379+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
380+
public string Foo5{ get; set; }
381+
382+
[CanBeNull]
383+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
384+
public string Foo6{ get; set; }
385+
386+
[CanBeNull]
387+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
388+
public string Foo7{ get; set; }
389+
390+
[CanBeNull]
391+
[UsedImplicitly(ImplicitUseKindFlags.Assign)]
392+
public string Foo8{ get; set; }
393+
394+
}
395+
}
396+
397+
public class CanBeNullAttribute : System.Attribute { }
398+
public class UsedImplicitly : System.Attribute
399+
{
400+
public UsedImplicitly (ImplicitUseKindFlags flags) { }
401+
}
402+
403+
public enum ImplicitUseKindFlags { Assign }
404+
";
405+
406+
DiagnosticResult[] expected =
407+
{
408+
this.CSharpDiagnostic().WithLocation(6, 21),
409+
this.CSharpDiagnostic().WithLocation(9, 21),
410+
this.CSharpDiagnostic().WithLocation(12, 21),
411+
this.CSharpDiagnostic().WithLocation(15, 21),
412+
this.CSharpDiagnostic().WithLocation(18, 21),
413+
this.CSharpDiagnostic().WithLocation(21, 21),
414+
this.CSharpDiagnostic().WithLocation(24, 21),
415+
this.CSharpDiagnostic().WithLocation(27, 21)
416+
};
417+
418+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
419+
await this.VerifyCSharpDiagnosticAsync(fixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
420+
await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false);
421+
await this.VerifyCSharpFixAllFixAsync(testCode, fixedTestCode, maxNumberOfIterations: 1).ConfigureAwait(false);
422+
}
423+
309424
/// <inheritdoc/>
310425
protected override CodeFixProvider GetCSharpCodeFixProvider()
311426
{

0 commit comments

Comments
 (0)