Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add analyzer 'Add/remove trailing comma' #931

Merged
merged 35 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
57f5f7c
Add analyzer Add/remove trailing comma
josefpihrt Jul 13, 2022
254abc6
Merge branch 'main' into feature/trailing-comma
josefpihrt Aug 22, 2022
33de661
Merge branch 'main' into feature/trailing-comma
josefpihrt Oct 31, 2022
72bc5b5
Merge branch 'main' into feature/trailing-comma
josefpihrt Nov 17, 2022
e844186
Add AnonymousObjectCreationExpression
josefpihrt Nov 17, 2022
2c3d189
Merge main
josefpihrt Nov 19, 2022
fa52fc2
update
josefpihrt Nov 19, 2022
0a209a4
update
josefpihrt Nov 19, 2022
5af2773
Merge branch 'main' into feature/trailing-comma
josefpihrt Nov 19, 2022
b680a3a
update
josefpihrt Nov 19, 2022
41e9e98
Merge branch 'main' into feature/trailing-comma
josefpihrt Nov 19, 2022
a6b0be3
Merge branch 'main' into feature/trailing-comma
josefpihrt Nov 27, 2022
6bd0781
Merge branch 'main' into feature/trailing-comma
josefpihrt Jul 16, 2023
92e4217
fixes
josefpihrt Jul 16, 2023
e05de70
Merge branch 'main' into feature/trailing-comma
josefpihrt Aug 17, 2023
ba544dd
update
josefpihrt Aug 17, 2023
bbe1ce2
update
josefpihrt Aug 17, 2023
c7da767
Merge branch 'main' into feature/trailing-comma
josefpihrt Aug 18, 2023
336aabd
update
josefpihrt Aug 18, 2023
71c8701
update
josefpihrt Aug 18, 2023
4cbbd3a
update
josefpihrt Aug 18, 2023
9700cec
x
josefpihrt Aug 18, 2023
740c2d7
merge main
josefpihrt Aug 19, 2023
f994f44
Merge branch 'main' into feature/trailing-comma
josefpihrt Aug 19, 2023
cd68646
merge main
josefpihrt Aug 20, 2023
7c94028
merge main
josefpihrt Aug 23, 2023
030cd8b
Merge branch 'main' into feature/trailing-comma
josefpihrt Aug 23, 2023
fceb835
update
josefpihrt Aug 23, 2023
670e683
Merge branch 'main' into feature/trailing-comma
josefpihrt Aug 23, 2023
c9793ca
Merge branch 'main' into feature/trailing-comma
josefpihrt Aug 25, 2023
2ebab0b
Merge branch 'main' into feature/trailing-comma
josefpihrt Aug 25, 2023
7029aa4
update
josefpihrt Aug 25, 2023
d4f38b7
update
josefpihrt Aug 25, 2023
e2ca102
update
josefpihrt Aug 25, 2023
7fdc645
merge main
josefpihrt Aug 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ roslynator_doc_comment_summary_style = multi_line
roslynator_enum_flag_value_style = shift_operator
roslynator_blank_line_after_file_scoped_namespace_declaration = true
roslynator_null_check_style = pattern_matching
roslynator_trailing_comma_style = omit_when_single_line

dotnet_diagnostic.RCS0001.severity = suggestion
dotnet_diagnostic.RCS0003.severity = suggestion
Expand Down Expand Up @@ -146,6 +147,7 @@ dotnet_diagnostic.RCS1252.severity = suggestion
dotnet_diagnostic.RCS1253.severity = suggestion
dotnet_diagnostic.RCS1254.severity = suggestion
dotnet_diagnostic.RCS1255.severity = none
dotnet_diagnostic.RCS1260.severity = suggestion

dotnet_diagnostic.IDE0007.severity = none
dotnet_diagnostic.IDE0007WithoutSuggestion.severity = none
Expand Down
3 changes: 3 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Remove empty region directive ([RCS1091](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1091))
- Remove empty destructor ([RCS1106](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1106))
- [CLI] Add glob pattern matching (`--include` or/and `--exclude`) ([#1178](https://github.com/josefpihrt/roslynator/pull/1178), [#1183](https://github.com/josefpihrt/roslynator/pull/1183)).
- Add analyzer "Include/omit trailing comma" ([RCS1256](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1256.md)) ([#931](https://github.com/JosefPihrt/Roslynator/pull/931)).
- Required option: `roslynator_trailing_comma_style = include|omit|omit_when_single_line`
- Not enabled by default

### Changed

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Immutable;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslynator.CodeFixes;

namespace Roslynator.CSharp.CodeFixes;

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AddOrRemoveTrailingCommaCodeFixProvider))]
[Shared]
public sealed class AddOrRemoveTrailingCommaCodeFixProvider : BaseCodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(DiagnosticIdentifiers.AddOrRemoveTrailingComma); }
}

public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

if (!TryFindFirstAncestorOrSelf(
root,
context.Span,
out SyntaxNode node,
predicate: f => f.IsKind(
SyntaxKind.ArrayInitializerExpression,
SyntaxKind.ObjectInitializerExpression,
SyntaxKind.CollectionInitializerExpression,
SyntaxKind.EnumDeclaration,
SyntaxKind.AnonymousObjectCreationExpression)))
{
return;
}

Diagnostic diagnostic = context.Diagnostics[0];
Document document = context.Document;

if (node is InitializerExpressionSyntax initializer)
{
SeparatedSyntaxList<ExpressionSyntax> expressions = initializer.Expressions;

int count = expressions.Count;

if (count == expressions.SeparatorCount)
{
CodeAction codeAction = CodeAction.Create(
"Remove comma",
ct => RemoveTrailingComma(document, expressions.GetSeparator(count - 1), ct),
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
}
else
{
CodeAction codeAction = CodeAction.Create(
"Add comma",
ct => AddTrailingComma(document, expressions.Last(), ct),
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
}
}
else if (node is AnonymousObjectCreationExpressionSyntax objectCreation)
{
SeparatedSyntaxList<AnonymousObjectMemberDeclaratorSyntax> initializers = objectCreation.Initializers;

int count = initializers.Count;

if (count == initializers.SeparatorCount)
{
CodeAction codeAction = CodeAction.Create(
"Remove comma",
ct => RemoveTrailingComma(document, initializers.GetSeparator(count - 1), ct),
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
}
else
{
CodeAction codeAction = CodeAction.Create(
"Add comma",
ct => AddTrailingComma(document, initializers.Last(), ct),
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
}
}
else if (node is EnumDeclarationSyntax enumDeclaration)
{
SeparatedSyntaxList<EnumMemberDeclarationSyntax> members = enumDeclaration.Members;

int count = members.Count;

if (count == members.SeparatorCount)
{
CodeAction codeAction = CodeAction.Create(
"Remove comma",
ct => RemoveTrailingComma(document, members.GetSeparator(count - 1), ct),
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
}
else
{
CodeAction codeAction = CodeAction.Create(
"Add comma",
ct => AddTrailingComma(document, members.Last(), ct),
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
}
}
}

private static Task<Document> RemoveTrailingComma(
Document document,
SyntaxToken comma,
CancellationToken cancellationToken)
{
return document.WithTextChangeAsync(new TextChange(comma.Span, ""), cancellationToken);
}

private static Task<Document> AddTrailingComma(
Document document,
SyntaxNode lastNode,
CancellationToken cancellationToken)
{
return document.WithTextChangeAsync(new TextChange(new TextSpan(lastNode.Span.End, 0), ","), cancellationToken);
}
}
40 changes: 40 additions & 0 deletions src/Analyzers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2336,10 +2336,13 @@ if (f)
<Analyzer>
<Id>RCS1035</Id>
<Identifier>RemoveRedundantCommaInInitializer</Identifier>
<Status>Obsolete</Status>
<ObsoleteMessage>Use RCS1260 instead</ObsoleteMessage>
<Title>Remove redundant comma in initializer.</Title>
<DefaultSeverity>Hidden</DefaultSeverity>
<IsEnabledByDefault>false</IsEnabledByDefault>
<SupportsFadeOut>true</SupportsFadeOut>
<Tags>HideFromConfiguration</Tags>
<Samples>
<Sample>
<Before><![CDATA[public void Foo()
Expand Down Expand Up @@ -7421,6 +7424,43 @@ void M()
* empty region directive
</Summary>
</Analyzer>
<Analyzer Identifier="AddOrRemoveTrailingComma">
<Id>RCS1260</Id>
<Title>Add/remove trailing comma.</Title>
<MessageFormat>{0} trailing comma.</MessageFormat>
<Category>General</Category>
<DefaultSeverity>Info</DefaultSeverity>
<IsEnabledByDefault>false</IsEnabledByDefault>
<ConfigOptions>
<Option Key="trailing_comma_style" IsRequired="true" />
</ConfigOptions>
<Samples>
<Sample>
<ConfigOptions>
<Option Key="trailing_comma_style" Value="include" />
</ConfigOptions>
<Before><![CDATA[public enum Foo
{
A,
B,
C
}]]></Before>
<After><![CDATA[public enum Foo
{
A,
B,
C,
}]]></After>
</Sample>
<Sample>
<ConfigOptions>
<Option Key="trailing_comma_style" Value="omit_when_single_line" />
</ConfigOptions>
<Before><![CDATA[public enum Foo { A, B, C, }]]></Before>
<After><![CDATA[public enum Foo { A, B, C }]]></After>
</Sample>
</Samples>
</Analyzer>
<Analyzer>
<Id>RCS9001</Id>
<Identifier>UsePatternMatching</Identifier>
Expand Down