Skip to content

Commit 89144ec

Browse files
committed
Add analyzer RCS0062, put expression body on its own line (#1575)
1 parent 96dfb34 commit 89144ec

File tree

7 files changed

+400
-3
lines changed

7 files changed

+400
-3
lines changed

src/Analyzers.xml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1629,6 +1629,23 @@ public class C
16291629
</Sample>
16301630
</Samples>
16311631
</Analyzer>
1632+
<Analyzer>
1633+
<Id>RCS0062</Id>
1634+
<Identifier>PutExpressionBodyOnItsOwnLine</Identifier>
1635+
<Title>Put expression body on its own line</Title>
1636+
<DefaultSeverity>Info</DefaultSeverity>
1637+
<IsEnabledByDefault>false</IsEnabledByDefault>
1638+
<Samples>
1639+
<Sample>
1640+
<Before><![CDATA[object Foo() => null;]]></Before>
1641+
<After><![CDATA[object Foo()
1642+
=> null;]]></After>
1643+
</Sample>
1644+
</Samples>
1645+
<ConfigOptions>
1646+
<Option Key="arrow_token_new_line" IsRequired="false" />
1647+
</ConfigOptions>
1648+
</Analyzer>
16321649
<Analyzer>
16331650
<Id>RCS1001</Id>
16341651
<Identifier>AddBracesWhenExpressionSpansOverMultipleLines</Identifier>
@@ -7973,4 +7990,4 @@ class FooCodeFixProvider : CodeFixProvider
79737990
</Sample>
79747991
</Samples>
79757992
</Analyzer>
7976-
</Analyzers>
7993+
</Analyzers>

src/Formatting.Analyzers.CodeFixes/CSharp/SyntaxTokenCodeFixProvider.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ public override ImmutableArray<string> FixableDiagnosticIds
2525
DiagnosticIdentifiers.PlaceNewLineAfterOrBeforeArrowToken,
2626
DiagnosticIdentifiers.PlaceNewLineAfterOrBeforeEqualsToken,
2727
DiagnosticIdentifiers.PutAttributeListOnItsOwnLine,
28-
DiagnosticIdentifiers.AddOrRemoveNewLineBeforeWhileInDoStatement);
28+
DiagnosticIdentifiers.AddOrRemoveNewLineBeforeWhileInDoStatement,
29+
DiagnosticIdentifiers.PutExpressionBodyOnItsOwnLine);
2930
}
3031
}
3132

@@ -61,6 +62,11 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
6162
await CodeActionFactory.RegisterCodeActionForNewLineAsync(context).ConfigureAwait(false);
6263
break;
6364
}
65+
case DiagnosticIdentifiers.PutExpressionBodyOnItsOwnLine:
66+
{
67+
await CodeActionFactory.RegisterCodeActionForNewLineAsync(context, increaseIndentation: true).ConfigureAwait(false);
68+
break;
69+
}
6470
}
6571
}
6672
}

src/Formatting.Analyzers/CSharp/DiagnosticIdentifiers.Generated.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,6 @@ public static partial class DiagnosticIdentifiers
6161
public const string PlaceNewLineAfterOrBeforeNullConditionalOperator = "RCS0059";
6262
public const string BlankLineAfterFileScopedNamespaceDeclaration = "RCS0060";
6363
public const string BlankLineBetweenSwitchSections = "RCS0061";
64+
public const string PutExpressionBodyOnItsOwnLine = "RCS0062";
6465
}
6566
}

src/Formatting.Analyzers/CSharp/DiagnosticRules.Generated.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,5 +645,17 @@ public static partial class DiagnosticRules
645645
helpLinkUri: DiagnosticIdentifiers.BlankLineBetweenSwitchSections,
646646
customTags: Array.Empty<string>());
647647

648+
/// <summary>RCS0062</summary>
649+
public static readonly DiagnosticDescriptor PutExpressionBodyOnItsOwnLine = DiagnosticDescriptorFactory.Create(
650+
id: DiagnosticIdentifiers.PutExpressionBodyOnItsOwnLine,
651+
title: "Put expression body on its own line",
652+
messageFormat: "Put expression body on its own line",
653+
category: DiagnosticCategories.Roslynator,
654+
defaultSeverity: DiagnosticSeverity.Info,
655+
isEnabledByDefault: false,
656+
description: null,
657+
helpLinkUri: DiagnosticIdentifiers.PutExpressionBodyOnItsOwnLine,
658+
customTags: Array.Empty<string>());
659+
648660
}
649661
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using System.Collections.Immutable;
4+
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.CSharp;
6+
using Microsoft.CodeAnalysis.CSharp.Syntax;
7+
using Microsoft.CodeAnalysis.Diagnostics;
8+
using Roslynator.CSharp;
9+
using Roslynator.CSharp.CodeStyle;
10+
11+
namespace Roslynator.Formatting.CSharp;
12+
13+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
14+
public sealed class PutExpressionBodyOnItsOwnLineAnalyzer : BaseDiagnosticAnalyzer
15+
{
16+
private static ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics;
17+
18+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
19+
{
20+
get
21+
{
22+
if (_supportedDiagnostics.IsDefault)
23+
Immutable.InterlockedInitialize(ref _supportedDiagnostics, DiagnosticRules.PutExpressionBodyOnItsOwnLine);
24+
25+
return _supportedDiagnostics;
26+
}
27+
}
28+
29+
public override void Initialize(AnalysisContext context)
30+
{
31+
base.Initialize(context);
32+
33+
context.RegisterSyntaxNodeAction(f => AnalyzeArrowExpressionClause(f), SyntaxKind.ArrowExpressionClause);
34+
}
35+
36+
private static void AnalyzeArrowExpressionClause(SyntaxNodeAnalysisContext context)
37+
{
38+
var arrowExpressionClause = (ArrowExpressionClauseSyntax)context.Node;
39+
40+
switch (arrowExpressionClause.Parent.Kind())
41+
{
42+
case SyntaxKind.MethodDeclaration:
43+
case SyntaxKind.ConstructorDeclaration:
44+
case SyntaxKind.DestructorDeclaration:
45+
case SyntaxKind.PropertyDeclaration:
46+
case SyntaxKind.IndexerDeclaration:
47+
case SyntaxKind.OperatorDeclaration:
48+
case SyntaxKind.ConversionOperatorDeclaration:
49+
AnalyzeArrowExpressionClause(arrowExpressionClause.ArrowToken, context);
50+
break;
51+
}
52+
}
53+
54+
private static void AnalyzeArrowExpressionClause(SyntaxToken arrowToken, SyntaxNodeAnalysisContext context)
55+
{
56+
NewLinePosition newLinePosition = context.GetArrowTokenNewLinePosition();
57+
58+
SyntaxToken first;
59+
SyntaxToken second;
60+
if (newLinePosition == NewLinePosition.After)
61+
{
62+
first = arrowToken;
63+
second = arrowToken.GetNextToken();
64+
}
65+
else
66+
{
67+
first = arrowToken.GetPreviousToken();
68+
second = arrowToken;
69+
}
70+
71+
TriviaBlock block = TriviaBlock.FromBetween(first, second);
72+
73+
if (block.Kind == TriviaBlockKind.NoNewLine)
74+
{
75+
DiagnosticHelpers.ReportDiagnostic(
76+
context,
77+
DiagnosticRules.PutExpressionBodyOnItsOwnLine,
78+
block.GetLocation());
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)