Skip to content

Commit d6a733d

Browse files
committed
Merge pull request #1862 from nbarbettini/fix-1121
Implement SA1413 (UseTrailingCommasInMultiLineInitializers)
2 parents e313f8c + 8769c6e commit d6a733d

11 files changed

Lines changed: 781 additions & 0 deletions

File tree

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.MaintainabilityRules
5+
{
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Collections.Immutable;
9+
using System.Composition;
10+
using System.Linq;
11+
using System.Threading;
12+
using System.Threading.Tasks;
13+
using Helpers;
14+
using Microsoft.CodeAnalysis;
15+
using Microsoft.CodeAnalysis.CodeActions;
16+
using Microsoft.CodeAnalysis.CodeFixes;
17+
using Microsoft.CodeAnalysis.CSharp;
18+
using Microsoft.CodeAnalysis.CSharp.Syntax;
19+
20+
/// <summary>
21+
/// Implements a code fix for <see cref="SA1413UseTrailingCommasInMultiLineInitializers"/>.
22+
/// </summary>
23+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(SA1413CodeFixProvider))]
24+
[Shared]
25+
internal class SA1413CodeFixProvider : CodeFixProvider
26+
{
27+
/// <inheritdoc/>
28+
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
29+
ImmutableArray.Create(SA1413UseTrailingCommasInMultiLineInitializers.DiagnosticId);
30+
31+
/// <inheritdoc/>
32+
public override FixAllProvider GetFixAllProvider()
33+
{
34+
return CustomFixAllProviders.BatchFixer;
35+
}
36+
37+
/// <inheritdoc/>
38+
public override Task RegisterCodeFixesAsync(CodeFixContext context)
39+
{
40+
foreach (var diagnostic in context.Diagnostics)
41+
{
42+
context.RegisterCodeFix(
43+
CodeAction.Create(
44+
MaintainabilityResources.SA1413CodeFix,
45+
cancellationToken => GetTransformedDocumentAsync(context.Document, diagnostic, cancellationToken),
46+
nameof(SA1413CodeFixProvider)),
47+
diagnostic);
48+
}
49+
50+
return SpecializedTasks.CompletedTask;
51+
}
52+
53+
private static async Task<Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
54+
{
55+
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
56+
var parent = syntaxRoot.FindNode(diagnostic.Location.SourceSpan).Parent;
57+
58+
SyntaxNode newParent = null;
59+
switch (parent.Kind())
60+
{
61+
case SyntaxKind.ObjectInitializerExpression:
62+
case SyntaxKind.ArrayInitializerExpression:
63+
case SyntaxKind.CollectionInitializerExpression:
64+
newParent = RewriteInitializer((InitializerExpressionSyntax)parent);
65+
break;
66+
67+
case SyntaxKind.AnonymousObjectCreationExpression:
68+
newParent = RewriteAnonymousObjectInitializer((AnonymousObjectCreationExpressionSyntax)parent);
69+
break;
70+
71+
default:
72+
throw new InvalidOperationException("Unknown initializer type: " + parent.Kind());
73+
}
74+
75+
var newSyntaxRoot = syntaxRoot.ReplaceNode(parent, newParent);
76+
77+
var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());
78+
return newDocument;
79+
}
80+
81+
private static SyntaxNode RewriteInitializer(InitializerExpressionSyntax initializer)
82+
{
83+
var existingItems = new List<ExpressionSyntax>(initializer.Expressions);
84+
var last = existingItems.Last();
85+
existingItems.Remove(last);
86+
existingItems.Add(last.WithoutTrailingTrivia());
87+
88+
var existingSeparators = initializer.Expressions.GetSeparators();
89+
var newSeparators = new List<SyntaxToken>(existingSeparators);
90+
newSeparators.Add(SyntaxFactory.Token(SyntaxKind.CommaToken).WithTrailingTrivia(last.GetTrailingTrivia()));
91+
92+
var newInitializerExpressions = SyntaxFactory.SeparatedList(
93+
existingItems,
94+
newSeparators);
95+
96+
var fixedInitializer = initializer.WithExpressions(newInitializerExpressions);
97+
return fixedInitializer;
98+
}
99+
100+
private static SyntaxNode RewriteAnonymousObjectInitializer(AnonymousObjectCreationExpressionSyntax initializer)
101+
{
102+
var existingItems = new List<AnonymousObjectMemberDeclaratorSyntax>(initializer.Initializers);
103+
var last = existingItems.Last();
104+
existingItems.Remove(last);
105+
existingItems.Add(last.WithoutTrailingTrivia());
106+
107+
var existingSeparators = initializer.Initializers.GetSeparators();
108+
var newSeparators = new List<SyntaxToken>(existingSeparators);
109+
newSeparators.Add(SyntaxFactory.Token(SyntaxKind.CommaToken).WithTrailingTrivia(last.GetTrailingTrivia()));
110+
111+
var newInitializerExpressions = SyntaxFactory.SeparatedList(
112+
existingItems,
113+
newSeparators);
114+
115+
var fixedInitializer = initializer.WithInitializers(newInitializerExpressions);
116+
return fixedInitializer;
117+
}
118+
}
119+
}

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/StyleCop.Analyzers.CodeFixes.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
<Compile Include="MaintainabilityRules\SA1410SA1411CodeFixProvider.cs" />
9696
<Compile Include="MaintainabilityRules\SA1412CodeFixProvider.cs" />
9797
<Compile Include="MaintainabilityRules\SA1412FixAllProvider.cs" />
98+
<Compile Include="MaintainabilityRules\SA1413CodeFixProvider.cs" />
9899
<Compile Include="NamingRules\RenameToLowerCaseCodeFixProvider.cs" />
99100
<Compile Include="NamingRules\RenameToUpperCaseCodeFixProvider.cs" />
100101
<Compile Include="NamingRules\SA1302CodeFixProvider.cs" />

0 commit comments

Comments
 (0)