Skip to content

Commit bd398f3

Browse files
authored
Improve analyzer RCS1260 (#1672)
1 parent 2986b7f commit bd398f3

File tree

4 files changed

+303
-8
lines changed

4 files changed

+303
-8
lines changed

ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818
- Fix analyzer [RCS1250](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1250) ([PR](https://github.com/dotnet/roslynator/pull/1652) by @aihnatiuk)
1919
- Fix analyzer [RCS1260](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1260) ([PR](https://github.com/dotnet/roslynator/pull/1668))
2020
- Fix analyzer [RCS1105](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1105) ([PR](https://github.com/dotnet/roslynator/pull/1669))
21+
- Fix analyzer [RCS1260](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1260) ([PR](https://github.com/dotnet/roslynator/pull/1672))
2122

2223
### Changed
2324

src/Analyzers.CodeFixes/CSharp/CodeFixes/AddOrRemoveTrailingCommaCodeFixProvider.cs

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,25 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
3131
root,
3232
context.Span,
3333
out SyntaxNode node,
34-
predicate: f => f.IsKind(
35-
SyntaxKind.ArrayInitializerExpression,
36-
SyntaxKind.ObjectInitializerExpression,
37-
SyntaxKind.CollectionInitializerExpression,
38-
SyntaxKind.EnumDeclaration,
34+
predicate: f =>
35+
{
36+
switch (f.Kind())
37+
{
38+
case SyntaxKind.ArrayInitializerExpression:
39+
case SyntaxKind.ObjectInitializerExpression:
40+
case SyntaxKind.CollectionInitializerExpression:
41+
case SyntaxKind.EnumDeclaration:
42+
case SyntaxKind.AnonymousObjectCreationExpression:
43+
case SyntaxKind.SwitchExpression:
44+
case SyntaxKind.PropertyPatternClause:
3945
#if ROSLYN_4_7
40-
SyntaxKind.CollectionExpression,
46+
case SyntaxKind.CollectionExpression:
4147
#endif
42-
SyntaxKind.AnonymousObjectCreationExpression)))
48+
return true;
49+
default:
50+
return false;
51+
}
52+
}))
4353
{
4454
return;
4555
}
@@ -72,8 +82,58 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
7282
context.RegisterCodeFix(codeAction, diagnostic);
7383
}
7484
}
85+
else if (node is SwitchExpressionSyntax switchExpression)
86+
{
87+
SeparatedSyntaxList<SwitchExpressionArmSyntax> arms = switchExpression.Arms;
88+
89+
int count = arms.Count;
90+
91+
if (count == arms.SeparatorCount)
92+
{
93+
CodeAction codeAction = CodeAction.Create(
94+
"Remove comma",
95+
ct => RemoveTrailingComma(document, arms.GetSeparator(count - 1), ct),
96+
GetEquivalenceKey(diagnostic));
97+
98+
context.RegisterCodeFix(codeAction, diagnostic);
99+
}
100+
else
101+
{
102+
CodeAction codeAction = CodeAction.Create(
103+
"Add comma",
104+
ct => AddTrailingComma(document, arms.Last(), ct),
105+
GetEquivalenceKey(diagnostic));
106+
107+
context.RegisterCodeFix(codeAction, diagnostic);
108+
}
109+
}
110+
else if (node is PropertyPatternClauseSyntax propertyPatternClause)
111+
{
112+
SeparatedSyntaxList<SubpatternSyntax> subpatterns = propertyPatternClause.Subpatterns;
113+
114+
int count = subpatterns.Count;
115+
116+
if (count == subpatterns.SeparatorCount)
117+
{
118+
CodeAction codeAction = CodeAction.Create(
119+
"Remove comma",
120+
ct => RemoveTrailingComma(document, subpatterns.GetSeparator(count - 1), ct),
121+
GetEquivalenceKey(diagnostic));
122+
123+
context.RegisterCodeFix(codeAction, diagnostic);
124+
}
125+
else
126+
{
127+
CodeAction codeAction = CodeAction.Create(
128+
"Add comma",
129+
ct => AddTrailingComma(document, subpatterns.Last(), ct),
130+
GetEquivalenceKey(diagnostic));
131+
132+
context.RegisterCodeFix(codeAction, diagnostic);
133+
}
134+
}
75135
#if ROSLYN_4_7
76-
if (node is CollectionExpressionSyntax collectionExpression)
136+
else if (node is CollectionExpressionSyntax collectionExpression)
77137
{
78138
SeparatedSyntaxList<CollectionElementSyntax> elements = collectionExpression.Elements;
79139

src/Analyzers/CSharp/Analysis/AddOrRemoveTrailingCommaAnalyzer.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public override void Initialize(AnalysisContext context)
3232

3333
context.RegisterSyntaxNodeAction(f => AnalyzeEnumDeclaration(f), SyntaxKind.EnumDeclaration);
3434
context.RegisterSyntaxNodeAction(f => AnalyzeAnonymousObjectCreationExpression(f), SyntaxKind.AnonymousObjectCreationExpression);
35+
context.RegisterSyntaxNodeAction(f => AnalyzeSwitchExpression(f), SyntaxKind.SwitchExpression);
36+
context.RegisterSyntaxNodeAction(f => AnalyzePropertyPatternClause(f), SyntaxKind.PropertyPatternClause);
3537
#if ROSLYN_4_7
3638
context.RegisterSyntaxNodeAction(f => AnalyzeCollectionExpression(f), SyntaxKind.CollectionExpression);
3739
#endif
@@ -172,6 +174,92 @@ private static void AnalyzeAnonymousObjectCreationExpression(SyntaxNodeAnalysisC
172174
}
173175
}
174176

177+
private static void AnalyzeSwitchExpression(SyntaxNodeAnalysisContext context)
178+
{
179+
TrailingCommaStyle style = context.GetTrailingCommaStyle();
180+
181+
if (style == TrailingCommaStyle.None)
182+
return;
183+
184+
var objectCreation = (SwitchExpressionSyntax)context.Node;
185+
186+
SeparatedSyntaxList<SwitchExpressionArmSyntax> arms = objectCreation.Arms;
187+
188+
if (!arms.Any())
189+
return;
190+
191+
int count = arms.Count;
192+
int separatorCount = arms.SeparatorCount;
193+
194+
if (count == separatorCount)
195+
{
196+
if (style == TrailingCommaStyle.Omit)
197+
{
198+
ReportRemove(context, arms.GetSeparator(count - 1));
199+
}
200+
else if (style == TrailingCommaStyle.OmitWhenSingleLine
201+
&& arms.IsSingleLine(cancellationToken: context.CancellationToken))
202+
{
203+
ReportRemove(context, arms.GetSeparator(count - 1));
204+
}
205+
}
206+
else if (separatorCount == count - 1)
207+
{
208+
if (style == TrailingCommaStyle.Include)
209+
{
210+
ReportAdd(context, arms.Last());
211+
}
212+
else if (style == TrailingCommaStyle.OmitWhenSingleLine
213+
&& !arms.IsSingleLine(cancellationToken: context.CancellationToken))
214+
{
215+
ReportAdd(context, arms.Last());
216+
}
217+
}
218+
}
219+
220+
private static void AnalyzePropertyPatternClause(SyntaxNodeAnalysisContext context)
221+
{
222+
TrailingCommaStyle style = context.GetTrailingCommaStyle();
223+
224+
if (style == TrailingCommaStyle.None)
225+
return;
226+
227+
var objectCreation = (PropertyPatternClauseSyntax)context.Node;
228+
229+
SeparatedSyntaxList<SubpatternSyntax> subpatterns = objectCreation.Subpatterns;
230+
231+
if (!subpatterns.Any())
232+
return;
233+
234+
int count = subpatterns.Count;
235+
int separatorCount = subpatterns.SeparatorCount;
236+
237+
if (count == separatorCount)
238+
{
239+
if (style == TrailingCommaStyle.Omit)
240+
{
241+
ReportRemove(context, subpatterns.GetSeparator(count - 1));
242+
}
243+
else if (style == TrailingCommaStyle.OmitWhenSingleLine
244+
&& subpatterns.IsSingleLine(cancellationToken: context.CancellationToken))
245+
{
246+
ReportRemove(context, subpatterns.GetSeparator(count - 1));
247+
}
248+
}
249+
else if (separatorCount == count - 1)
250+
{
251+
if (style == TrailingCommaStyle.Include)
252+
{
253+
ReportAdd(context, subpatterns.Last());
254+
}
255+
else if (style == TrailingCommaStyle.OmitWhenSingleLine
256+
&& !subpatterns.IsSingleLine(cancellationToken: context.CancellationToken))
257+
{
258+
ReportAdd(context, subpatterns.Last());
259+
}
260+
}
261+
}
262+
175263
#if ROSLYN_4_7
176264
private static void AnalyzeCollectionExpression(SyntaxNodeAnalysisContext context)
177265
{

src/Tests/Analyzers.Tests/RCS1260AddOrRemoveTrailingCommaTests.cs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,28 @@ void M()
330330
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_OmitWhenSingleLine));
331331
}
332332

333+
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.AddOrRemoveTrailingComma)]
334+
public async Task Test_CollectionExpression_Include()
335+
{
336+
await VerifyDiagnosticAndFixAsync("""
337+
class C
338+
{
339+
void M()
340+
{
341+
int[] x = [1, 2, 3[||]];
342+
}
343+
}
344+
""", """
345+
class C
346+
{
347+
void M()
348+
{
349+
int[] x = [1, 2, 3,];
350+
}
351+
}
352+
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_Include));
353+
}
354+
333355
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.AddOrRemoveTrailingComma)]
334356
public async Task Test_CollectionExpression_Omit()
335357
{
@@ -373,4 +395,128 @@ void M()
373395
}
374396
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_OmitWhenSingleLine));
375397
}
398+
399+
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.AddOrRemoveTrailingComma)]
400+
public async Task Test_SwitchExpression_Include()
401+
{
402+
await VerifyDiagnosticAndFixAsync("""
403+
class C
404+
{
405+
void M(int p)
406+
{
407+
var x = p switch
408+
{
409+
1 => "foo",
410+
_ => "bar"[||]
411+
};
412+
}
413+
}
414+
""", """
415+
class C
416+
{
417+
void M(int p)
418+
{
419+
var x = p switch
420+
{
421+
1 => "foo",
422+
_ => "bar",
423+
};
424+
}
425+
}
426+
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_Include));
427+
}
428+
429+
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.AddOrRemoveTrailingComma)]
430+
public async Task Test_SwitchExpression_Omit()
431+
{
432+
await VerifyDiagnosticAndFixAsync("""
433+
class C
434+
{
435+
void M(int p)
436+
{
437+
var x = p switch
438+
{
439+
1 => "foo",
440+
_ => "bar"[|,|]
441+
};
442+
}
443+
}
444+
""", """
445+
class C
446+
{
447+
void M(int p)
448+
{
449+
var x = p switch
450+
{
451+
1 => "foo",
452+
_ => "bar"
453+
};
454+
}
455+
}
456+
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_Omit));
457+
}
458+
459+
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.AddOrRemoveTrailingComma)]
460+
public async Task Test_PatternMatching_Include()
461+
{
462+
await VerifyDiagnosticAndFixAsync("""
463+
class C
464+
{
465+
public int P1 { get; set; }
466+
public int P2 { get; set; }
467+
468+
void M(C p)
469+
{
470+
if (p is { P1: 1, P2: 2[||] })
471+
{
472+
}
473+
}
474+
}
475+
""", """
476+
class C
477+
{
478+
public int P1 { get; set; }
479+
public int P2 { get; set; }
480+
481+
void M(C p)
482+
{
483+
if (p is { P1: 1, P2: 2, })
484+
{
485+
}
486+
}
487+
}
488+
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_Include));
489+
}
490+
491+
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.AddOrRemoveTrailingComma)]
492+
public async Task Test_PatternMatching_Omit()
493+
{
494+
await VerifyDiagnosticAndFixAsync("""
495+
class C
496+
{
497+
public int P1 { get; set; }
498+
public int P2 { get; set; }
499+
500+
void M(C p)
501+
{
502+
if (p is { P1: 1, P2: 2[|,|] })
503+
{
504+
}
505+
}
506+
}
507+
""", """
508+
class C
509+
{
510+
public int P1 { get; set; }
511+
public int P2 { get; set; }
512+
513+
void M(C p)
514+
{
515+
if (p is { P1: 1, P2: 2 })
516+
{
517+
}
518+
}
519+
}
520+
""", options: Options.AddConfigOption(ConfigOptionKeys.TrailingCommaStyle, ConfigOptionValues.TrailingCommaStyle_Omit));
521+
}
376522
}

0 commit comments

Comments
 (0)