提交 046597cb 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #19285 from CyrusNajmabadi/directiveHandling

Better handling of directive-trivia in UseExpressionBody/ReplacePropWithMethod.
...@@ -1416,6 +1416,120 @@ public async Task TestDocumentationComment3() ...@@ -1416,6 +1416,120 @@ public async Task TestDocumentationComment3()
}", ignoreTrivia: false); }", ignoreTrivia: false);
} }
[WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)]
public async Task TestWithDirectives1()
{
await TestInRegularAndScriptAsync(
@"class C
{
int [||]Prop
{
get
{
#if true
return 0;
#else
return 1;
#endif
}
}
}",
@"class C
{
private int GetProp()
{
#if true
return 0;
#else
return 1;
#endif
}
}", ignoreTrivia: false);
}
[WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)]
public async Task TestWithDirectives2()
{
await TestInRegularAndScriptAsync(
@"class C
{
int [||]Prop
{
get
{
#if true
return 0;
#else
return 1;
#endif
}
}
}",
@"class C
{
private int GetProp() =>
#if true
0;
#else
return 1;
#endif
}", ignoreTrivia: false,
options: PreferExpressionBodiedMethods);
}
[WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)]
public async Task TestWithDirectives3()
{
await TestInRegularAndScriptAsync(
@"class C
{
int [||]Prop =>
#if true
0;
#else
1;
#endif
}",
@"class C
{
private int GetProp() =>
#if true
0;
#else
1;
#endif
}", ignoreTrivia: false);
}
[WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)]
public async Task TestWithDirectives4()
{
await TestInRegularAndScriptAsync(
@"class C
{
int [||]Prop =>
#if true
0;
#else
1;
#endif
}",
@"class C
{
private int GetProp() =>
#if true
0;
#else
1;
#endif
}", ignoreTrivia: false,
options: PreferExpressionBodiedMethods);
}
private IDictionary<OptionKey, object> PreferExpressionBodiedMethods => private IDictionary<OptionKey, object> PreferExpressionBodiedMethods =>
OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement)); OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement));
} }
......
...@@ -244,5 +244,125 @@ public async Task TestUseExpressionBodyKeepTrailingTrivia() ...@@ -244,5 +244,125 @@ public async Task TestUseExpressionBodyKeepTrailingTrivia()
public string OtherThing => ""Pickles""; public string OtherThing => ""Pickles"";
}", ignoreTrivia: false, options: UseExpressionBody); }", ignoreTrivia: false, options: UseExpressionBody);
} }
[WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)]
public async Task TestDirectivesInBlockBody1()
{
await TestInRegularAndScript1Async(
@"class C
{
int Foo
{
get
{
#if true
[|return|] Bar();
#else
return Baz();
#endif
}
}
}",
@"class C
{
int Foo =>
#if true
Bar();
#else
return Baz();
#endif
}", ignoreTrivia: false,
parameters: new TestParameters(options: UseExpressionBody));
}
[WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)]
public async Task TestDirectivesInBlockBody2()
{
await TestInRegularAndScript1Async(
@"class C
{
int Foo
{
get
{
#if false
return Bar();
#else
[|return|] Baz();
#endif
}
}
}",
@"class C
{
int Foo =>
#if false
return Bar();
#else
Baz();
#endif
}", ignoreTrivia: false,
parameters: new TestParameters(options: UseExpressionBody));
}
[WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)]
public async Task TestMissingWithDirectivesInExpressionBody1()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
int Foo [|=>|]
#if true
Bar();
#else
Baz();
#endif
}", parameters: new TestParameters(options: UseBlockBody));
}
[WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)]
public async Task TestMissingWithDirectivesInExpressionBody2()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
int Foo [|=>|]
#if false
Bar();
#else
Baz();
#endif
}", parameters: new TestParameters(options: UseBlockBody));
}
[WorkItem(19193, "https://github.com/dotnet/roslyn/issues/19193")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)]
public async Task TestMoveTriviaFromExpressionToReturnStatement()
{
await TestInRegularAndScriptAsync(
@"class C
{
int Foo(int i) [|=>|]
//comment
i * i;
}",
@"class C
{
int Foo(int i)
{
//comment
return i * i;
}
}", ignoreTrivia: false,
options: UseBlockBody);
}
} }
} }
\ No newline at end of file
...@@ -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, parseOptions, expressionBodyPreference,
out var arrowExpression, out var semicolonToken)) out var arrowExpression, out var semicolonToken))
{ {
return propertyDeclaration.WithExpressionBody(arrowExpression) return propertyDeclaration.WithExpressionBody(arrowExpression)
...@@ -106,10 +106,12 @@ public void RemoveSetMethod(SyntaxEditor editor, SyntaxNode setMethodDeclaration ...@@ -106,10 +106,12 @@ public void RemoveSetMethod(SyntaxEditor editor, SyntaxNode setMethodDeclaration
} }
else else
{ {
if (propertyDeclaration.ExpressionBody != null) if (propertyDeclaration.ExpressionBody != null &&
propertyDeclaration.ExpressionBody.TryConvertToBlock(
propertyDeclaration.SemicolonToken,
createReturnStatementForExpression: true,
block: out var block))
{ {
var block = propertyDeclaration.ExpressionBody.ConvertToBlock(
propertyDeclaration.SemicolonToken, createReturnStatementForExpression: true);
var accessor = var accessor =
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithBody(block); .WithBody(block);
...@@ -181,7 +183,7 @@ private SyntaxToken GetPropertyName(SyntaxToken identifier, string propertyName, ...@@ -181,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, parseOptions, expressionBodyPreference,
out var arrowExpression, out var semicolonToken)) out var arrowExpression, out var semicolonToken))
{ {
return accessorDeclaration.WithBody(null) return accessorDeclaration.WithBody(null)
...@@ -192,13 +194,16 @@ private SyntaxToken GetPropertyName(SyntaxToken identifier, string propertyName, ...@@ -192,13 +194,16 @@ private SyntaxToken GetPropertyName(SyntaxToken identifier, string propertyName,
} }
else if (accessorDeclaration?.ExpressionBody != null && expressionBodyPreference == ExpressionBodyPreference.Never) else if (accessorDeclaration?.ExpressionBody != null && expressionBodyPreference == ExpressionBodyPreference.Never)
{ {
var block = accessorDeclaration.ExpressionBody.ConvertToBlock( if (accessorDeclaration.ExpressionBody.TryConvertToBlock(
accessorDeclaration.SemicolonToken, accessorDeclaration.SemicolonToken,
createReturnStatementForExpression: accessorDeclaration.Kind() == SyntaxKind.GetAccessorDeclaration); createReturnStatementForExpression: accessorDeclaration.Kind() == SyntaxKind.GetAccessorDeclaration,
return accessorDeclaration.WithExpressionBody(null) block: out var block))
.WithSemicolonToken(default(SyntaxToken)) {
.WithBody(block) return accessorDeclaration.WithExpressionBody(null)
.WithAdditionalAnnotations(Formatter.Annotation); .WithSemicolonToken(default(SyntaxToken))
.WithBody(block)
.WithAdditionalAnnotations(Formatter.Annotation);
}
} }
return accessorDeclaration; return accessorDeclaration;
......
...@@ -246,12 +246,14 @@ private static SyntaxTrivia ConvertDocumentationComment(SyntaxTrivia trivia) ...@@ -246,12 +246,14 @@ private static SyntaxTrivia ConvertDocumentationComment(SyntaxTrivia trivia)
} }
else if (methodDeclaration?.ExpressionBody != null && expressionBodyPreference == ExpressionBodyPreference.Never) else if (methodDeclaration?.ExpressionBody != null && expressionBodyPreference == ExpressionBodyPreference.Never)
{ {
var block = methodDeclaration?.ExpressionBody.ConvertToBlock( if (methodDeclaration.ExpressionBody.TryConvertToBlock(
methodDeclaration.SemicolonToken, createReturnStatementForExpression); methodDeclaration.SemicolonToken, createReturnStatementForExpression, out var block))
return methodDeclaration.WithExpressionBody(null) {
.WithSemicolonToken(default(SyntaxToken)) return methodDeclaration.WithExpressionBody(null)
.WithBody(block) .WithSemicolonToken(default(SyntaxToken))
.WithAdditionalAnnotations(Formatter.Annotation); .WithBody(block)
.WithAdditionalAnnotations(Formatter.Annotation);
}
} }
return methodDeclaration; return methodDeclaration;
......
...@@ -128,8 +128,8 @@ protected virtual Location GetDiagnosticLocation(TDeclaration declaration) ...@@ -128,8 +128,8 @@ protected virtual Location GetDiagnosticLocation(TDeclaration declaration)
// If the user does not like block bodies then we offer block bodies from the refactoring provider. // If the user does not like block bodies then we offer block bodies from the refactoring provider.
if (userPrefersBlockBodies == forAnalyzer) if (userPrefersBlockBodies == forAnalyzer)
{ {
// If we have an expression body, we can always convert it to a block body. return this.GetExpressionBody(declaration)?.TryConvertToBlock(
return this.GetExpressionBody(declaration) != null; SyntaxFactory.Token(SyntaxKind.SemicolonToken), false, out var block) == true;
} }
return false; return false;
...@@ -180,11 +180,16 @@ public TDeclaration Update(TDeclaration declaration, OptionSet options, bool use ...@@ -180,11 +180,16 @@ public TDeclaration Update(TDeclaration declaration, OptionSet options, bool use
{ {
var expressionBody = GetExpressionBody(declaration); var expressionBody = GetExpressionBody(declaration);
var semicolonToken = GetSemicolonToken(declaration); var semicolonToken = GetSemicolonToken(declaration);
var block = expressionBody.ConvertToBlock(
GetSemicolonToken(declaration),
CreateReturnStatementForExpression(declaration));
return WithBody(declaration, block); if (expressionBody.TryConvertToBlock(
GetSemicolonToken(declaration),
CreateReturnStatementForExpression(declaration),
out var block))
{
return WithBody(declaration, block);
}
return declaration;
} }
protected TDeclaration WithAccessorList( protected TDeclaration WithAccessorList(
...@@ -196,7 +201,8 @@ public TDeclaration Update(TDeclaration declaration, OptionSet options, bool use ...@@ -196,7 +201,8 @@ public TDeclaration Update(TDeclaration declaration, OptionSet options, bool use
var expressionBodyPreference = options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors).Value; var expressionBodyPreference = options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors).Value;
AccessorDeclarationSyntax accessor; AccessorDeclarationSyntax accessor;
if (expressionBodyPreference != ExpressionBodyPreference.Never) if (expressionBodyPreference != ExpressionBodyPreference.Never ||
!expressionBody.TryConvertToBlock(GetSemicolonToken(declaration), CreateReturnStatementForExpression(declaration), out var block))
{ {
accessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) accessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithExpressionBody(expressionBody) .WithExpressionBody(expressionBody)
...@@ -204,9 +210,6 @@ public TDeclaration Update(TDeclaration declaration, OptionSet options, bool use ...@@ -204,9 +210,6 @@ public TDeclaration Update(TDeclaration declaration, OptionSet options, bool use
} }
else else
{ {
var block = expressionBody.ConvertToBlock(
GetSemicolonToken(declaration),
CreateReturnStatementForExpression(declaration));
accessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, block); accessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, block);
} }
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
...@@ -7,14 +8,29 @@ namespace Microsoft.CodeAnalysis.CSharp.Extensions ...@@ -7,14 +8,29 @@ namespace Microsoft.CodeAnalysis.CSharp.Extensions
{ {
internal static class ArrowExpressionClauseSyntaxExtensions internal static class ArrowExpressionClauseSyntaxExtensions
{ {
public static BlockSyntax ConvertToBlock( public static bool TryConvertToBlock(
this ArrowExpressionClauseSyntax arrowExpression, this ArrowExpressionClauseSyntax arrowExpression,
SyntaxToken semicolonToken, SyntaxToken semicolonToken,
bool createReturnStatementForExpression) bool createReturnStatementForExpression,
out BlockSyntax block)
{ {
// It's tricky to convert an arrow expression with directives over to a block.
// We'd need to find and remove the directives *after* the arrow expression and
// move them accordingly. So, for now, we just disallow this.
if (arrowExpression.Expression.GetLeadingTrivia().Any(t => t.IsDirective))
{
block = null;
return false;
}
var statement = ConvertToStatement(arrowExpression.Expression, semicolonToken, createReturnStatementForExpression); var statement = ConvertToStatement(arrowExpression.Expression, semicolonToken, createReturnStatementForExpression);
statement = statement.WithPrependedLeadingTrivia(arrowExpression.ArrowToken.TrailingTrivia); if (arrowExpression.ArrowToken.TrailingTrivia.Any(t => t.IsSingleOrMultiLineComment()))
return SyntaxFactory.Block(statement); {
statement = statement.WithPrependedLeadingTrivia(arrowExpression.ArrowToken.TrailingTrivia);
}
block = SyntaxFactory.Block(statement);
return true;
} }
private static StatementSyntax ConvertToStatement( private static StatementSyntax ConvertToStatement(
...@@ -29,8 +45,18 @@ internal static class ArrowExpressionClauseSyntaxExtensions ...@@ -29,8 +45,18 @@ internal static class ArrowExpressionClauseSyntaxExtensions
} }
else if (createReturnStatementForExpression) else if (createReturnStatementForExpression)
{ {
return SyntaxFactory.ReturnStatement(expression) if (expression.GetLeadingTrivia().Any(t => t.IsSingleOrMultiLineComment()))
.WithSemicolonToken(semicolonToken); {
return SyntaxFactory.ReturnStatement(expression.WithLeadingTrivia(SyntaxFactory.ElasticSpace))
.WithSemicolonToken(semicolonToken)
.WithLeadingTrivia(expression.GetLeadingTrivia())
.WithPrependedLeadingTrivia(SyntaxFactory.ElasticMarker);
}
else
{
return SyntaxFactory.ReturnStatement(expression)
.WithSemicolonToken(semicolonToken);
}
} }
else else
{ {
......
...@@ -70,9 +70,9 @@ internal static class BlockSyntaxExtensions ...@@ -70,9 +70,9 @@ internal static class BlockSyntaxExtensions
{ {
if (returnStatement.Expression != null) if (returnStatement.Expression != null)
{ {
// If there are any comments on the return keyword, move them to // If there are any comments or directives on the return keyword, move them to
// the expression. // the expression.
expression = firstStatement.GetLeadingTrivia().Any(t => t.IsSingleOrMultiLineComment()) expression = firstStatement.GetLeadingTrivia().Any(t => t.IsDirective || t.IsSingleOrMultiLineComment())
? returnStatement.Expression.WithLeadingTrivia(returnStatement.GetLeadingTrivia()) ? returnStatement.Expression.WithLeadingTrivia(returnStatement.GetLeadingTrivia())
: returnStatement.Expression; : returnStatement.Expression;
semicolonToken = returnStatement.SemicolonToken; semicolonToken = returnStatement.SemicolonToken;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册