提交 110163ef 编写于 作者: C CyrusNajmabadi

Better respect lang version when generating expression bodies.

上级 d010af63
...@@ -427,6 +427,38 @@ void M() ...@@ -427,6 +427,38 @@ void M()
}", options: UseExpressionBody, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)); }", options: UseExpressionBody, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5));
} }
[WorkItem(20352, "https://github.com/dotnet/roslyn/issues/20352")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)]
public async Task TestDoNotOfferToConvertToBlockIfExpressionBodyPreferredIfCSharp6()
{
await TestMissingAsync(
@"
using System;
class C
{
void M() [|=>|] 0;
}", new TestParameters(options: UseExpressionBody, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp6)));
}
[WorkItem(20352, "https://github.com/dotnet/roslyn/issues/20352")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)]
public async Task TestOfferToConvertToExpressionIfCSharp6()
{
await TestAsync(
@"
using System;
class C
{
void M() { [|return|] 0; }
}",
@"
using System;
class C
{
void M() => 0;
}", options: UseExpressionBody, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp6));
}
[WorkItem(20362, "https://github.com/dotnet/roslyn/issues/20362")] [WorkItem(20362, "https://github.com/dotnet/roslyn/issues/20362")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)]
public async Task TestOfferToConvertToBlockEvenIfExpressionBodyPreferredIfPriorToCSharp6_FixAll() public async Task TestOfferToConvertToBlockEvenIfExpressionBodyPreferredIfPriorToCSharp6_FixAll()
......
...@@ -95,7 +95,7 @@ public void RemoveSetMethod(SyntaxEditor editor, SyntaxNode setMethodDeclaration ...@@ -95,7 +95,7 @@ public void RemoveSetMethod(SyntaxEditor editor, SyntaxNode setMethodDeclaration
} }
else if (getAccessor.Body != null && else if (getAccessor.Body != null &&
getAccessor.Body.TryConvertToExpressionBody( getAccessor.Body.TryConvertToExpressionBody(
parseOptions, expressionBodyPreference, propertyDeclaration.Kind(), parseOptions, expressionBodyPreference,
out var arrowExpression, out var semicolonToken)) out var arrowExpression, out var semicolonToken))
{ {
return propertyDeclaration.WithExpressionBody(arrowExpression) return propertyDeclaration.WithExpressionBody(arrowExpression)
...@@ -183,7 +183,7 @@ private SyntaxToken GetPropertyName(SyntaxToken identifier, string propertyName, ...@@ -183,7 +183,7 @@ private SyntaxToken GetPropertyName(SyntaxToken identifier, string propertyName,
if (accessorDeclaration?.Body != null && expressionBodyPreference != ExpressionBodyPreference.Never) if (accessorDeclaration?.Body != null && expressionBodyPreference != ExpressionBodyPreference.Never)
{ {
if (accessorDeclaration.Body.TryConvertToExpressionBody( if (accessorDeclaration.Body.TryConvertToExpressionBody(
parseOptions, expressionBodyPreference, accessorDeclaration.Kind(), parseOptions, expressionBodyPreference,
out var arrowExpression, out var semicolonToken)) out var arrowExpression, out var semicolonToken))
{ {
return accessorDeclaration.WithBody(null) return accessorDeclaration.WithBody(null)
......
...@@ -227,7 +227,8 @@ private static SyntaxTrivia ConvertDocumentationComment(SyntaxTrivia trivia, CSh ...@@ -227,7 +227,8 @@ private static SyntaxTrivia ConvertDocumentationComment(SyntaxTrivia trivia, CSh
if (methodDeclaration?.Body != null && expressionBodyPreference != ExpressionBodyPreference.Never) if (methodDeclaration?.Body != null && expressionBodyPreference != ExpressionBodyPreference.Never)
{ {
if (methodDeclaration.Body.TryConvertToExpressionBody( if (methodDeclaration.Body.TryConvertToExpressionBody(
parseOptions, expressionBodyPreference, out var arrowExpression, out var semicolonToken)) methodDeclaration.Kind(), parseOptions, expressionBodyPreference,
out var arrowExpression, out var semicolonToken))
{ {
return methodDeclaration.WithBody(null) return methodDeclaration.WithBody(null)
.WithExpressionBody(arrowExpression) .WithExpressionBody(arrowExpression)
......
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
using System; using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Options;
...@@ -58,5 +60,16 @@ protected override IndexerDeclarationSyntax WithBody(IndexerDeclarationSyntax de ...@@ -58,5 +60,16 @@ protected override IndexerDeclarationSyntax WithBody(IndexerDeclarationSyntax de
} }
protected override bool CreateReturnStatementForExpression(IndexerDeclarationSyntax declaration) => true; protected override bool CreateReturnStatementForExpression(IndexerDeclarationSyntax declaration) => true;
protected override bool TryConvertToExpressionBody(
IndexerDeclarationSyntax declaration, ParseOptions options,
ExpressionBodyPreference conversionPreference,
out ArrowExpressionClauseSyntax arrowExpression,
out SyntaxToken semicolonToken)
{
return this.TryConvertToExpressionBodyForBaseProperty(
declaration, options, conversionPreference,
out arrowExpression, out semicolonToken);
}
} }
} }
...@@ -67,21 +67,9 @@ protected override PropertyDeclarationSyntax WithBody(PropertyDeclarationSyntax ...@@ -67,21 +67,9 @@ protected override PropertyDeclarationSyntax WithBody(PropertyDeclarationSyntax
out ArrowExpressionClauseSyntax arrowExpression, out ArrowExpressionClauseSyntax arrowExpression,
out SyntaxToken semicolonToken) out SyntaxToken semicolonToken)
{ {
if (base.TryConvertToExpressionBody(declaration, options, conversionPreference, out arrowExpression, out semicolonToken)) return this.TryConvertToExpressionBodyForBaseProperty(
{ declaration, options, conversionPreference,
return true; out arrowExpression, out semicolonToken);
}
var getAccessor = GetSingleGetAccessor(declaration.AccessorList);
if (getAccessor?.ExpressionBody != null &&
BlockSyntaxExtensions.MatchesPreference(getAccessor.ExpressionBody.Expression, conversionPreference))
{
arrowExpression = SyntaxFactory.ArrowExpressionClause(getAccessor.ExpressionBody.Expression);
semicolonToken = getAccessor.SemicolonToken;
return true;
}
return false;
} }
protected override Location GetDiagnosticLocation(PropertyDeclarationSyntax declaration) protected override Location GetDiagnosticLocation(PropertyDeclarationSyntax declaration)
......
...@@ -111,13 +111,48 @@ protected virtual Location GetDiagnosticLocation(TDeclaration declaration) ...@@ -111,13 +111,48 @@ protected virtual Location GetDiagnosticLocation(TDeclaration declaration)
ParseOptions options, ExpressionBodyPreference conversionPreference, ParseOptions options, ExpressionBodyPreference conversionPreference,
out ArrowExpressionClauseSyntax expressionWhenOnSingleLine, out ArrowExpressionClauseSyntax expressionWhenOnSingleLine,
out SyntaxToken semicolonWhenOnSingleLine) out SyntaxToken semicolonWhenOnSingleLine)
{
return TryConvertToExpressionBodyWorker(
declaration, options, conversionPreference,
out expressionWhenOnSingleLine, out semicolonWhenOnSingleLine);
}
private bool TryConvertToExpressionBodyWorker(
SyntaxNode declaration, ParseOptions options, ExpressionBodyPreference conversionPreference,
out ArrowExpressionClauseSyntax expressionWhenOnSingleLine, out SyntaxToken semicolonWhenOnSingleLine)
{ {
var body = this.GetBody(declaration); var body = this.GetBody(declaration);
return body.TryConvertToExpressionBody(options, conversionPreference, return body.TryConvertToExpressionBody(
declaration.Kind(), options, conversionPreference,
out expressionWhenOnSingleLine, out semicolonWhenOnSingleLine); out expressionWhenOnSingleLine, out semicolonWhenOnSingleLine);
} }
protected bool TryConvertToExpressionBodyForBaseProperty(
BasePropertyDeclarationSyntax declaration, ParseOptions options,
ExpressionBodyPreference conversionPreference,
out ArrowExpressionClauseSyntax arrowExpression,
out SyntaxToken semicolonToken)
{
if (this.TryConvertToExpressionBodyWorker(
declaration, options, conversionPreference,
out arrowExpression, out semicolonToken))
{
return true;
}
var getAccessor = GetSingleGetAccessor(declaration.AccessorList);
if (getAccessor?.ExpressionBody != null &&
BlockSyntaxExtensions.MatchesPreference(getAccessor.ExpressionBody.Expression, conversionPreference))
{
arrowExpression = SyntaxFactory.ArrowExpressionClause(getAccessor.ExpressionBody.Expression);
semicolonToken = getAccessor.SemicolonToken;
return true;
}
return false;
}
public (bool canOffer, bool fixesError) CanOfferUseBlockBody( public (bool canOffer, bool fixesError) CanOfferUseBlockBody(
OptionSet optionSet, TDeclaration declaration, bool forAnalyzer) OptionSet optionSet, TDeclaration declaration, bool forAnalyzer)
{ {
...@@ -171,8 +206,9 @@ protected virtual Location GetDiagnosticLocation(TDeclaration declaration) ...@@ -171,8 +206,9 @@ protected virtual Location GetDiagnosticLocation(TDeclaration declaration)
{ {
if (useExpressionBody) if (useExpressionBody)
{ {
TryConvertToExpressionBody(declaration, declaration.SyntaxTree.Options, TryConvertToExpressionBody(
ExpressionBodyPreference.WhenPossible, out var expressionBody, out var semicolonToken); declaration, declaration.SyntaxTree.Options, ExpressionBodyPreference.WhenPossible,
out var expressionBody, out var semicolonToken);
var trailingTrivia = semicolonToken.TrailingTrivia var trailingTrivia = semicolonToken.TrailingTrivia
.Where(t => t.Kind() != SyntaxKind.EndOfLineTrivia) .Where(t => t.Kind() != SyntaxKind.EndOfLineTrivia)
......
...@@ -74,7 +74,8 @@ private static MemberDeclarationSyntax LastConstructorOrField(SyntaxList<MemberD ...@@ -74,7 +74,8 @@ private static MemberDeclarationSyntax LastConstructorOrField(SyntaxList<MemberD
{ {
var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedConstructors).Value; var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedConstructors).Value;
if (declaration.Body.TryConvertToExpressionBody( if (declaration.Body.TryConvertToExpressionBody(
options, expressionBodyPreference, out var expressionBody, out var semicolonToken)) declaration.Kind(), options, expressionBodyPreference,
out var expressionBody, out var semicolonToken))
{ {
return declaration.WithBody(null) return declaration.WithBody(null)
.WithExpressionBody(expressionBody) .WithExpressionBody(expressionBody)
......
...@@ -86,7 +86,8 @@ internal static class ConversionGenerator ...@@ -86,7 +86,8 @@ internal static class ConversionGenerator
var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedOperators).Value; var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedOperators).Value;
if (declaration.Body.TryConvertToExpressionBody( if (declaration.Body.TryConvertToExpressionBody(
options, expressionBodyPreference, out var expressionBody, out var semicolonToken)) declaration.Kind(), options, expressionBodyPreference,
out var expressionBody, out var semicolonToken))
{ {
return declaration.WithBody(null) return declaration.WithBody(null)
.WithExpressionBody(expressionBody) .WithExpressionBody(expressionBody)
......
...@@ -119,7 +119,8 @@ internal static class MethodGenerator ...@@ -119,7 +119,8 @@ internal static class MethodGenerator
{ {
var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedMethods).Value; var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedMethods).Value;
if (methodDeclaration.Body.TryConvertToExpressionBody( if (methodDeclaration.Body.TryConvertToExpressionBody(
options, expressionBodyPreference, out var expressionBody, out var semicolonToken)) methodDeclaration.Kind(), options, expressionBodyPreference,
out var expressionBody, out var semicolonToken))
{ {
return methodDeclaration.WithBody(null) return methodDeclaration.WithBody(null)
.WithExpressionBody(expressionBody) .WithExpressionBody(expressionBody)
......
...@@ -58,7 +58,8 @@ internal static class OperatorGenerator ...@@ -58,7 +58,8 @@ internal static class OperatorGenerator
{ {
var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedOperators).Value; var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedOperators).Value;
if (declaration.Body.TryConvertToExpressionBody( if (declaration.Body.TryConvertToExpressionBody(
options, expressionBodyPreference, out var expressionBody, out var semicolonToken)) declaration.Kind(), options, expressionBodyPreference,
out var expressionBody, out var semicolonToken))
{ {
return declaration.WithBody(null) return declaration.WithBody(null)
.WithExpressionBody(expressionBody) .WithExpressionBody(expressionBody)
......
...@@ -146,16 +146,19 @@ private static TypeSyntax GeneratePropertyType(IPropertySymbol property) ...@@ -146,16 +146,19 @@ private static TypeSyntax GeneratePropertyType(IPropertySymbol property)
} }
private static bool TryGetExpressionBody( private static bool TryGetExpressionBody(
AccessorListSyntax accessorList, ParseOptions options, ExpressionBodyPreference preference, BasePropertyDeclarationSyntax baseProperty, ParseOptions options, ExpressionBodyPreference preference,
out ArrowExpressionClauseSyntax arrowExpression, out SyntaxToken semicolonToken) out ArrowExpressionClauseSyntax arrowExpression, out SyntaxToken semicolonToken)
{ {
var accessorList = baseProperty.AccessorList;
if (preference != ExpressionBodyPreference.Never && if (preference != ExpressionBodyPreference.Never &&
accessorList.Accessors.Count == 1) accessorList.Accessors.Count == 1)
{ {
var accessor = accessorList.Accessors[0]; var accessor = accessorList.Accessors[0];
if (accessor.IsKind(SyntaxKind.GetAccessorDeclaration)) if (accessor.IsKind(SyntaxKind.GetAccessorDeclaration))
{ {
return TryGetExpressionBody(accessor, options, preference, out arrowExpression, out semicolonToken); return TryGetExpressionBody(
baseProperty.Kind(), accessor, options, preference,
out arrowExpression, out semicolonToken);
} }
} }
...@@ -172,8 +175,9 @@ private static TypeSyntax GeneratePropertyType(IPropertySymbol property) ...@@ -172,8 +175,9 @@ private static TypeSyntax GeneratePropertyType(IPropertySymbol property)
var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties).Value; var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties).Value;
if (declaration.Initializer == null) if (declaration.Initializer == null)
{ {
if (TryGetExpressionBody(declaration.AccessorList, options, if (TryGetExpressionBody(
expressionBodyPreference, out var expressionBody, out var semicolonToken)) declaration, options, expressionBodyPreference,
out var expressionBody, out var semicolonToken))
{ {
declaration = declaration.WithAccessorList(null) declaration = declaration.WithAccessorList(null)
.WithExpressionBody(expressionBody) .WithExpressionBody(expressionBody)
...@@ -191,8 +195,9 @@ private static TypeSyntax GeneratePropertyType(IPropertySymbol property) ...@@ -191,8 +195,9 @@ private static TypeSyntax GeneratePropertyType(IPropertySymbol property)
if (declaration.ExpressionBody == null) if (declaration.ExpressionBody == null)
{ {
var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedIndexers).Value; var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedIndexers).Value;
if (TryGetExpressionBody(declaration.AccessorList, if (TryGetExpressionBody(
options, expressionBodyPreference, out var expressionBody, out var semicolonToken)) declaration, options, expressionBodyPreference,
out var expressionBody, out var semicolonToken))
{ {
declaration = declaration.WithAccessorList(null) declaration = declaration.WithAccessorList(null)
.WithExpressionBody(expressionBody) .WithExpressionBody(expressionBody)
...@@ -210,7 +215,8 @@ private static TypeSyntax GeneratePropertyType(IPropertySymbol property) ...@@ -210,7 +215,8 @@ private static TypeSyntax GeneratePropertyType(IPropertySymbol property)
{ {
var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors).Value; var expressionBodyPreference = workspace.Options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors).Value;
if (declaration.Body.TryConvertToExpressionBody( if (declaration.Body.TryConvertToExpressionBody(
options, expressionBodyPreference, out var expressionBody, out var semicolonToken)) declaration.Kind(), options, expressionBodyPreference,
out var expressionBody, out var semicolonToken))
{ {
declaration = declaration.WithBody(null) declaration = declaration.WithBody(null)
.WithExpressionBody(expressionBody) .WithExpressionBody(expressionBody)
...@@ -222,7 +228,7 @@ private static TypeSyntax GeneratePropertyType(IPropertySymbol property) ...@@ -222,7 +228,7 @@ private static TypeSyntax GeneratePropertyType(IPropertySymbol property)
} }
private static bool TryGetExpressionBody( private static bool TryGetExpressionBody(
AccessorDeclarationSyntax accessor, ParseOptions options, ExpressionBodyPreference preference, SyntaxKind declaratoinKind, AccessorDeclarationSyntax accessor, ParseOptions options, ExpressionBodyPreference preference,
out ArrowExpressionClauseSyntax arrowExpression, out SyntaxToken semicolonToken) out ArrowExpressionClauseSyntax arrowExpression, out SyntaxToken semicolonToken)
{ {
// If the accessor has an expression body already, then use that as the expression body // If the accessor has an expression body already, then use that as the expression body
...@@ -235,7 +241,7 @@ private static TypeSyntax GeneratePropertyType(IPropertySymbol property) ...@@ -235,7 +241,7 @@ private static TypeSyntax GeneratePropertyType(IPropertySymbol property)
} }
return accessor.Body.TryConvertToExpressionBody( return accessor.Body.TryConvertToExpressionBody(
options, preference, out arrowExpression, out semicolonToken); declaratoinKind, options, preference, out arrowExpression, out semicolonToken);
} }
private static AccessorListSyntax GenerateAccessorList( private static AccessorListSyntax GenerateAccessorList(
......
...@@ -13,29 +13,36 @@ namespace Microsoft.CodeAnalysis.CSharp.Extensions ...@@ -13,29 +13,36 @@ namespace Microsoft.CodeAnalysis.CSharp.Extensions
internal static class BlockSyntaxExtensions internal static class BlockSyntaxExtensions
{ {
public static bool TryConvertToExpressionBody( public static bool TryConvertToExpressionBody(
this BlockSyntax block, ParseOptions options, this BlockSyntax block, SyntaxKind declarationKind,
ExpressionBodyPreference preference, ParseOptions options, ExpressionBodyPreference preference,
out ArrowExpressionClauseSyntax arrowExpression, out ArrowExpressionClauseSyntax arrowExpression,
out SyntaxToken semicolonToken) out SyntaxToken semicolonToken)
{ {
if (preference != ExpressionBodyPreference.Never && if (preference != ExpressionBodyPreference.Never)
(options as CSharpParseOptions)?.LanguageVersion >= LanguageVersion.CSharp7)
{ {
if (block != null && block.Statements.Count == 1) var csharpOptions = (CSharpParseOptions)options;
{ var acceptableVersion =
var firstStatement = block.Statements[0]; csharpOptions.LanguageVersion >= LanguageVersion.CSharp7 ||
(csharpOptions.LanguageVersion >= LanguageVersion.CSharp6 && !RequiresCSharp7(declarationKind));
if (TryGetExpression(firstStatement, out var expression, out semicolonToken) && if (acceptableVersion)
MatchesPreference(expression, preference)) {
if (block != null && block.Statements.Count == 1)
{ {
arrowExpression = SyntaxFactory.ArrowExpressionClause(expression); var firstStatement = block.Statements[0];
if (TryGetExpression(firstStatement, out var expression, out semicolonToken) &&
MatchesPreference(expression, preference))
{
arrowExpression = SyntaxFactory.ArrowExpressionClause(expression);
// The close brace of the block may have important trivia on it (like // The close brace of the block may have important trivia on it (like
// comments or directives). Preserve them on the semicolon when we // comments or directives). Preserve them on the semicolon when we
// convert to an expression body. // convert to an expression body.
semicolonToken = semicolonToken.WithAppendedTrailingTrivia( semicolonToken = semicolonToken.WithAppendedTrailingTrivia(
block.CloseBraceToken.LeadingTrivia.Where(t => !t.IsWhitespaceOrEndOfLine())); block.CloseBraceToken.LeadingTrivia.Where(t => !t.IsWhitespaceOrEndOfLine()));
return true; return true;
}
} }
} }
} }
...@@ -45,6 +52,22 @@ internal static class BlockSyntaxExtensions ...@@ -45,6 +52,22 @@ internal static class BlockSyntaxExtensions
return false; return false;
} }
private static bool RequiresCSharp7(SyntaxKind declarationKind)
{
switch (declarationKind)
{
case SyntaxKind.ConstructorDeclaration:
case SyntaxKind.DestructorDeclaration:
case SyntaxKind.AddAccessorDeclaration:
case SyntaxKind.RemoveAccessorDeclaration:
case SyntaxKind.GetAccessorDeclaration:
case SyntaxKind.SetAccessorDeclaration:
return true;
}
return false;
}
public static bool MatchesPreference( public static bool MatchesPreference(
ExpressionSyntax expression, ExpressionBodyPreference preference) ExpressionSyntax expression, ExpressionBodyPreference preference)
{ {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册