Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
6 changes: 6 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Add analyzer "Put expression body on its own line" [RCS0062](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS0062) ([PR](https://github.com/dotnet/roslynator/pull/1593) by @cbersch)
- Affects analyzer [RCS1016](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1016)
- Affects refacoring [RR0169](https://josefpihrt.github.io/docs/roslynator/refactorings/RR0169)

## [4.12.11] - 2025-01-28

### Added
Expand Down
17 changes: 17 additions & 0 deletions src/Analyzers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1629,6 +1629,23 @@ public class C
</Sample>
</Samples>
</Analyzer>
<Analyzer>
<Id>RCS0062</Id>
<Identifier>PutExpressionBodyOnItsOwnLine</Identifier>
<Title>Put expression body on its own line</Title>
<DefaultSeverity>Info</DefaultSeverity>
<IsEnabledByDefault>false</IsEnabledByDefault>
<Samples>
<Sample>
<Before><![CDATA[object Foo() => null;]]></Before>
<After><![CDATA[object Foo()
=> null;]]></After>
</Sample>
</Samples>
<ConfigOptions>
<Option Key="arrow_token_new_line" IsRequired="false" />
</ConfigOptions>
</Analyzer>
<Analyzer>
<Id>RCS1001</Id>
<Identifier>AddBracesWhenExpressionSpansOverMultipleLines</Identifier>
Expand Down
24 changes: 24 additions & 0 deletions src/Common/CSharp/Analysis/ConvertExpressionBodyAnalysis.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.CodeAnalysis.CSharp;

namespace Roslynator.CSharp.Analysis;

internal static class ConvertExpressionBodyAnalysis
{
public static bool AllowPutExpressionBodyOnItsOwnLine(SyntaxKind syntaxKind)
{
// allow putting expression on new line for all method-like declarations, except for accessors.
switch (syntaxKind)
{
case SyntaxKind.MethodDeclaration:
case SyntaxKind.ConstructorDeclaration:
case SyntaxKind.DestructorDeclaration:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.IndexerDeclaration:
case SyntaxKind.OperatorDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
return true;
default:
return false;
}
}
}
1 change: 1 addition & 0 deletions src/Common/DiagnosticIdentifiers.Generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public static partial class DiagnosticIdentifiers
public const string PlaceNewLineAfterOrBeforeNullConditionalOperator = "RCS0059";
public const string BlankLineAfterFileScopedNamespaceDeclaration = "RCS0060";
public const string BlankLineBetweenSwitchSections = "RCS0061";
public const string PutExpressionBodyOnItsOwnLine = "RCS0062";
public const string AddBracesWhenExpressionSpansOverMultipleLines = "RCS1001";
public const string RemoveBraces = "RCS1002";
public const string AddBracesToIfElseWhenExpressionSpansOverMultipleLines = "RCS1003";
Expand Down
12 changes: 12 additions & 0 deletions src/Common/DiagnosticRules.Generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,18 @@ public static partial class DiagnosticRules
helpLinkUri: DiagnosticIdentifiers.BlankLineBetweenSwitchSections,
customTags: []);

/// <summary>RCS0062</summary>
public static readonly DiagnosticDescriptor PutExpressionBodyOnItsOwnLine = DiagnosticDescriptorFactory.Create(
id: DiagnosticIdentifiers.PutExpressionBodyOnItsOwnLine,
title: "Put expression body on its own line",
messageFormat: "Put expression body on its own line",
category: DiagnosticCategories.Roslynator,
defaultSeverity: DiagnosticSeverity.Info,
isEnabledByDefault: false,
description: null,
helpLinkUri: DiagnosticIdentifiers.PutExpressionBodyOnItsOwnLine,
customTags: []);

/// <summary>RCS1001</summary>
public static readonly DiagnosticDescriptor AddBracesWhenExpressionSpansOverMultipleLines = DiagnosticDescriptorFactory.Create(
id: DiagnosticIdentifiers.AddBracesWhenExpressionSpansOverMultipleLines,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public override ImmutableArray<string> FixableDiagnosticIds
DiagnosticIdentifiers.PlaceNewLineAfterOrBeforeArrowToken,
DiagnosticIdentifiers.PlaceNewLineAfterOrBeforeEqualsToken,
DiagnosticIdentifiers.PutAttributeListOnItsOwnLine,
DiagnosticIdentifiers.AddOrRemoveNewLineBeforeWhileInDoStatement);
DiagnosticIdentifiers.AddOrRemoveNewLineBeforeWhileInDoStatement,
DiagnosticIdentifiers.PutExpressionBodyOnItsOwnLine);
}
}

Expand Down Expand Up @@ -60,6 +61,11 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
await CodeActionFactory.RegisterCodeActionForNewLineAsync(context).ConfigureAwait(false);
break;
}
case DiagnosticIdentifiers.PutExpressionBodyOnItsOwnLine:
{
await CodeActionFactory.RegisterCodeActionForNewLineAsync(context, increaseIndentation: true).ConfigureAwait(false);
break;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) .NET Foundation 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslynator.CSharp;
using Roslynator.CSharp.Analysis;
using Roslynator.CSharp.CodeStyle;

namespace Roslynator.Formatting.CSharp;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class PutExpressionBodyOnItsOwnLineAnalyzer : BaseDiagnosticAnalyzer
{
private static ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics;

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
if (_supportedDiagnostics.IsDefault)
Immutable.InterlockedInitialize(ref _supportedDiagnostics, DiagnosticRules.PutExpressionBodyOnItsOwnLine);

return _supportedDiagnostics;
}
}

public override void Initialize(AnalysisContext context)
{
base.Initialize(context);

context.RegisterSyntaxNodeAction(f => AnalyzeArrowExpressionClause(f), SyntaxKind.ArrowExpressionClause);
}

private static void AnalyzeArrowExpressionClause(SyntaxNodeAnalysisContext context)
{
var arrowExpressionClause = (ArrowExpressionClauseSyntax)context.Node;

if (ConvertExpressionBodyAnalysis.AllowPutExpressionBodyOnItsOwnLine(arrowExpressionClause.Parent.Kind()))
{
AnalyzeArrowExpressionClause(arrowExpressionClause.ArrowToken, context);
}
}

private static void AnalyzeArrowExpressionClause(SyntaxToken arrowToken, SyntaxNodeAnalysisContext context)
{
NewLinePosition newLinePosition = context.GetArrowTokenNewLinePosition();

SyntaxToken first;
SyntaxToken second;
if (newLinePosition == NewLinePosition.After)
{
first = arrowToken;
second = arrowToken.GetNextToken();
}
else
{
first = arrowToken.GetPreviousToken();
second = arrowToken;
}

TriviaBlock block = TriviaBlock.FromBetween(first, second);

if (block.Kind == TriviaBlockKind.NoNewLine)
{
DiagnosticHelpers.ReportDiagnostic(
context,
DiagnosticRules.PutExpressionBodyOnItsOwnLine,
block.GetLocation());
}
}
}
Loading
Loading