Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix analyzer [RCS1250](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1250) ([PR](https://github.com/dotnet/roslynator/pull/1652) by @aihnatiuk)
- Fix analyzer [RCS1260](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1260) ([PR](https://github.com/dotnet/roslynator/pull/1668))
- Fix analyzer [RCS1105](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1105) ([PR](https://github.com/dotnet/roslynator/pull/1669))
- Fix analyzer [RCS1260](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1260) ([PR](https://github.com/dotnet/roslynator/pull/1672))

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,25 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
root,
context.Span,
out SyntaxNode node,
predicate: f => f.IsKind(
SyntaxKind.ArrayInitializerExpression,
SyntaxKind.ObjectInitializerExpression,
SyntaxKind.CollectionInitializerExpression,
SyntaxKind.EnumDeclaration,
predicate: f =>
{
switch (f.Kind())
{
case SyntaxKind.ArrayInitializerExpression:
case SyntaxKind.ObjectInitializerExpression:
case SyntaxKind.CollectionInitializerExpression:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.AnonymousObjectCreationExpression:
case SyntaxKind.SwitchExpression:
case SyntaxKind.PropertyPatternClause:
#if ROSLYN_4_7
SyntaxKind.CollectionExpression,
case SyntaxKind.CollectionExpression:
#endif
SyntaxKind.AnonymousObjectCreationExpression)))
return true;
default:
return false;
}
}))
{
return;
}
Expand Down Expand Up @@ -72,8 +82,58 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
context.RegisterCodeFix(codeAction, diagnostic);
}
}
else if (node is SwitchExpressionSyntax switchExpression)
{
SeparatedSyntaxList<SwitchExpressionArmSyntax> arms = switchExpression.Arms;

int count = arms.Count;

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

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

context.RegisterCodeFix(codeAction, diagnostic);
}
}
else if (node is PropertyPatternClauseSyntax propertyPatternClause)
{
SeparatedSyntaxList<SubpatternSyntax> subpatterns = propertyPatternClause.Subpatterns;

int count = subpatterns.Count;

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

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

context.RegisterCodeFix(codeAction, diagnostic);
}
}
#if ROSLYN_4_7
if (node is CollectionExpressionSyntax collectionExpression)
else if (node is CollectionExpressionSyntax collectionExpression)
{
SeparatedSyntaxList<CollectionElementSyntax> elements = collectionExpression.Elements;

Expand Down
88 changes: 88 additions & 0 deletions src/Analyzers/CSharp/Analysis/AddOrRemoveTrailingCommaAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public override void Initialize(AnalysisContext context)

context.RegisterSyntaxNodeAction(f => AnalyzeEnumDeclaration(f), SyntaxKind.EnumDeclaration);
context.RegisterSyntaxNodeAction(f => AnalyzeAnonymousObjectCreationExpression(f), SyntaxKind.AnonymousObjectCreationExpression);
context.RegisterSyntaxNodeAction(f => AnalyzeSwitchExpression(f), SyntaxKind.SwitchExpression);
context.RegisterSyntaxNodeAction(f => AnalyzePropertyPatternClause(f), SyntaxKind.PropertyPatternClause);
#if ROSLYN_4_7
context.RegisterSyntaxNodeAction(f => AnalyzeCollectionExpression(f), SyntaxKind.CollectionExpression);
#endif
Expand Down Expand Up @@ -172,6 +174,92 @@ private static void AnalyzeAnonymousObjectCreationExpression(SyntaxNodeAnalysisC
}
}

private static void AnalyzeSwitchExpression(SyntaxNodeAnalysisContext context)
{
TrailingCommaStyle style = context.GetTrailingCommaStyle();

if (style == TrailingCommaStyle.None)
return;

var objectCreation = (SwitchExpressionSyntax)context.Node;

SeparatedSyntaxList<SwitchExpressionArmSyntax> arms = objectCreation.Arms;

if (!arms.Any())
return;

int count = arms.Count;
int separatorCount = arms.SeparatorCount;

if (count == separatorCount)
{
if (style == TrailingCommaStyle.Omit)
{
ReportRemove(context, arms.GetSeparator(count - 1));
}
else if (style == TrailingCommaStyle.OmitWhenSingleLine
&& arms.IsSingleLine(cancellationToken: context.CancellationToken))
{
ReportRemove(context, arms.GetSeparator(count - 1));
}
}
else if (separatorCount == count - 1)
{
if (style == TrailingCommaStyle.Include)
{
ReportAdd(context, arms.Last());
}
else if (style == TrailingCommaStyle.OmitWhenSingleLine
&& !arms.IsSingleLine(cancellationToken: context.CancellationToken))
{
ReportAdd(context, arms.Last());
}
}
}

private static void AnalyzePropertyPatternClause(SyntaxNodeAnalysisContext context)
{
TrailingCommaStyle style = context.GetTrailingCommaStyle();

if (style == TrailingCommaStyle.None)
return;

var objectCreation = (PropertyPatternClauseSyntax)context.Node;

SeparatedSyntaxList<SubpatternSyntax> subpatterns = objectCreation.Subpatterns;

if (!subpatterns.Any())
return;

int count = subpatterns.Count;
int separatorCount = subpatterns.SeparatorCount;

if (count == separatorCount)
{
if (style == TrailingCommaStyle.Omit)
{
ReportRemove(context, subpatterns.GetSeparator(count - 1));
}
else if (style == TrailingCommaStyle.OmitWhenSingleLine
&& subpatterns.IsSingleLine(cancellationToken: context.CancellationToken))
{
ReportRemove(context, subpatterns.GetSeparator(count - 1));
}
}
else if (separatorCount == count - 1)
{
if (style == TrailingCommaStyle.Include)
{
ReportAdd(context, subpatterns.Last());
}
else if (style == TrailingCommaStyle.OmitWhenSingleLine
&& !subpatterns.IsSingleLine(cancellationToken: context.CancellationToken))
{
ReportAdd(context, subpatterns.Last());
}
}
}

#if ROSLYN_4_7
private static void AnalyzeCollectionExpression(SyntaxNodeAnalysisContext context)
{
Expand Down
146 changes: 146 additions & 0 deletions src/Tests/Analyzers.Tests/RCS1260AddOrRemoveTrailingCommaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,28 @@ void M()
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_OmitWhenSingleLine));
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.AddOrRemoveTrailingComma)]
public async Task Test_CollectionExpression_Include()
{
await VerifyDiagnosticAndFixAsync("""
class C
{
void M()
{
int[] x = [1, 2, 3[||]];
}
}
""", """
class C
{
void M()
{
int[] x = [1, 2, 3,];
}
}
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_Include));
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.AddOrRemoveTrailingComma)]
public async Task Test_CollectionExpression_Omit()
{
Expand Down Expand Up @@ -373,4 +395,128 @@ void M()
}
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_OmitWhenSingleLine));
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.AddOrRemoveTrailingComma)]
public async Task Test_SwitchExpression_Include()
{
await VerifyDiagnosticAndFixAsync("""
class C
{
void M(int p)
{
var x = p switch
{
1 => "foo",
_ => "bar"[||]
};
}
}
""", """
class C
{
void M(int p)
{
var x = p switch
{
1 => "foo",
_ => "bar",
};
}
}
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_Include));
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.AddOrRemoveTrailingComma)]
public async Task Test_SwitchExpression_Omit()
{
await VerifyDiagnosticAndFixAsync("""
class C
{
void M(int p)
{
var x = p switch
{
1 => "foo",
_ => "bar"[|,|]
};
}
}
""", """
class C
{
void M(int p)
{
var x = p switch
{
1 => "foo",
_ => "bar"
};
}
}
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_Omit));
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.AddOrRemoveTrailingComma)]
public async Task Test_PatternMatching_Include()
{
await VerifyDiagnosticAndFixAsync("""
class C
{
public int P1 { get; set; }
public int P2 { get; set; }

void M(C p)
{
if (p is { P1: 1, P2: 2[||] })
{
}
}
}
""", """
class C
{
public int P1 { get; set; }
public int P2 { get; set; }

void M(C p)
{
if (p is { P1: 1, P2: 2, })
{
}
}
}
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_Include));
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.AddOrRemoveTrailingComma)]
public async Task Test_PatternMatching_Omit()
{
await VerifyDiagnosticAndFixAsync("""
class C
{
public int P1 { get; set; }
public int P2 { get; set; }

void M(C p)
{
if (p is { P1: 1, P2: 2[|,|] })
{
}
}
}
""", """
class C
{
public int P1 { get; set; }
public int P2 { get; set; }

void M(C p)
{
if (p is { P1: 1, P2: 2 })
{
}
}
}
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_Omit));
}
}
Loading