未验证 提交 e123a420 编写于 作者: C Cheryl Borley 提交者: GitHub

Merge pull request #25232 from chborl/removeinvertif

Remove disabled invert if feature
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.InvertIf;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings.InvertIf
{
public class InvertIfTests : AbstractCSharpCodeActionTest
{
private async Task TestFixOneAsync(
string initial,
string expected)
{
await TestInRegularAndScriptAsync(CreateTreeText(initial), CreateTreeText(expected));
}
protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters)
=> new InvertIfCodeRefactoringProvider();
private string CreateTreeText(string initial)
{
return
@"class A
{
void Goo()
{
" + initial + @"
}
}";
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestIdentifier()
{
await TestFixOneAsync(
@"[||]if (a) { a(); } else { b(); }",
@" if (!a) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestNotIdentifier()
{
await TestFixOneAsync(
@"[||]if (!a) { a(); } else { b(); }",
@" if (a) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestEqualsEquals()
{
await TestFixOneAsync(
@"[||]if (a == b) { a(); } else { b(); }",
@" if (a != b) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestNotEquals()
{
await TestFixOneAsync(
@"[||]if (a != b) { a(); } else { b(); }",
@" if (a == b) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestGreaterThan()
{
await TestFixOneAsync(
@"[||]if (a > b) { a(); } else { b(); }",
@" if (a <= b) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestGreaterThanEquals()
{
await TestFixOneAsync(
@"[||]if (a >= b) { a(); } else { b(); }",
@" if (a < b) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestLessThan()
{
await TestFixOneAsync(
@"[||]if (a < b) { a(); } else { b(); }",
@" if (a >= b) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestLessThanEquals()
{
await TestFixOneAsync(
@"[||]if (a <= b) { a(); } else { b(); }",
@" if (a > b) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestParens()
{
await TestFixOneAsync(
@"[||]if ((a)) { a(); } else { b(); }",
@" if (!a) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestIs()
{
await TestFixOneAsync(
@"[||]if (a is Goo) { a(); } else { b(); }",
@" if (!(a is Goo)) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestCall()
{
await TestFixOneAsync(
@"[||]if (a.Goo()) { a(); } else { b(); }",
@" if (!a.Goo()) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestOr()
{
await TestFixOneAsync(
@"[||]if (a || b) { a(); } else { b(); }",
@" if (!a && !b) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestOr2()
{
await TestFixOneAsync(
@"[||]if (!a || !b) { a(); } else { b(); }",
@" if (a && b) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestAnd()
{
await TestFixOneAsync(
@"[||]if (a && b) { a(); } else { b(); }",
@" if (!a || !b) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestAnd2()
{
await TestFixOneAsync(
@"[||]if (!a && !b) { a(); } else { b(); }",
@" if (a || b) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestParenthesizeAndForPrecedence()
{
await TestFixOneAsync(
@"[||]if (a && b || c) { a(); } else { b(); }",
@" if ((!a || !b) && !c) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestPlus()
{
await TestFixOneAsync(
@"[||]if (a + b) { a(); } else { b(); }",
@" if (!(a + b)) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestTrue()
{
await TestFixOneAsync(
@"[||]if (true) { a(); } else { b(); }",
@" if (false) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestFalse()
{
await TestFixOneAsync(
@"[||]if (false) { a(); } else { b(); }",
@" if (true) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestTrueAndFalse()
{
await TestFixOneAsync(
@"[||]if (true && false) { a(); } else { b(); }",
@" if (false || true) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestCurlies1()
{
await TestFixOneAsync(
@"[||]if (a) a(); else b();",
@" if (!a) b();
else a();");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestCurlies2()
{
await TestFixOneAsync(
@"[||]if (a) { a(); } else b();",
@" if (!a) b();
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestCurlies3()
{
await TestFixOneAsync(
@"[||]if (a) a(); else { b(); }",
@" if (!a) { b(); }
else a();");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestIfElseIf()
{
await TestFixOneAsync(
@"[||]if (a) { a(); } else if (b) { b(); }",
@" if (!a)
{
if (b) { b(); }
}
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestIfElseIf2()
{
await TestFixOneAsync(
@"[||]if (a) { a(); } else if (b) { b(); } else { c(); }",
@" if (!a)
{
if (b) { b(); } else { c(); }
}
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestNested()
{
await TestFixOneAsync(
@"[||]if (((a == b) && (c != d)) || ((e < f) && (!g))) { a(); } else { b(); }",
@" if ((a != b || c == d) && (e >= f || g)) { b(); }
else { a(); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestKeepTriviaWithinExpression()
{
await TestFixOneAsync(
@"[||]if (a ||
b &&
c < // comment
d)
{
a();
}
else
{
b();
}",
@" if (!a &&
(!b ||
c >= // comment
d))
{
b();
}
else
{
a();
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestMissingOnNonEmptySpan()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void F()
{
[|if (a)
{
a();
}
else
{
b();
}|]
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestOverlapsHiddenPosition1()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void F()
{
#line hidden
[||]if (a)
{
a();
}
else
{
b();
}
#line default
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestOverlapsHiddenPosition2()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void F()
{
[||]if (a)
{
#line hidden
a();
#line default
}
else
{
b();
}
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestOverlapsHiddenPosition3()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void F()
{
[||]if (a)
{
a();
}
else
{
#line hidden
b();
#line default
}
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestOverlapsHiddenPosition4()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void F()
{
[||]if (a)
{
#line hidden
a();
}
else
{
b();
#line default
}
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestOverlapsHiddenPosition5()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void F()
{
[||]if (a)
{
a();
#line hidden
}
else
{
#line default
b();
}
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestOverlapsHiddenPosition6()
{
await TestInRegularAndScriptAsync(
@"
#line hidden
class C
{
void F()
{
#line default
[||]if (a)
{
a();
}
else
{
b();
}
}
}",
@"
#line hidden
class C
{
void F()
{
#line default
if (!a)
{
b();
}
else
{
a();
}
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestOverlapsHiddenPosition7()
{
await TestInRegularAndScriptAsync(
@"
#line hidden
class C
{
void F()
{
#line default
[||]if (a)
{
a();
}
else
{
b();
}
#line hidden
}
}
#line default",
@"
#line hidden
class C
{
void F()
{
#line default
if (!a)
{
b();
}
else
{
a();
}
#line hidden
}
}
#line default");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestSimplifyToLengthEqualsZero()
{
await TestFixOneAsync(
@"string x; [||]if (x.Length > 0) { GreaterThanZero(); } else { EqualsZero(); } } } ",
@"string x; if (x.Length == 0) { EqualsZero(); } else { GreaterThanZero(); }
} } ");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestSimplifyToLengthEqualsZero2()
{
await TestFixOneAsync(
@"string[] x; [||]if (x.Length > 0) { GreaterThanZero(); } else { EqualsZero(); } } } ",
@"string[] x; if (x.Length == 0) { EqualsZero(); } else { GreaterThanZero(); }
} } ");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestSimplifyToLengthEqualsZero3()
{
await TestFixOneAsync(
@"string x; [||]if (x.Length > 0x0) { a(); } else { b(); } } } ",
@"string x; if (x.Length == 0x0) { b(); } else { a(); }
} } ");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestSimplifyToLengthEqualsZero4()
{
await TestFixOneAsync(
@"string x; [||]if (0 < x.Length) { a(); } else { b(); } } } ",
@"string x; if (0 == x.Length) { b(); } else { a(); }
} } ");
}
[WorkItem(545986, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestSimplifyToLengthEqualsZero5()
{
await TestFixOneAsync(
@"byte x = 1; [||]if (0 < x) { a(); } else { b(); } } } ",
@"byte x = 1; if (0 == x) { b(); } else { a(); }
} } ");
}
[WorkItem(545986, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestSimplifyToLengthEqualsZero6()
{
await TestFixOneAsync(
@"ushort x = 1; [||]if (0 < x) { a(); } else { b(); } } } ",
@"ushort x = 1; if (0 == x) { b(); } else { a(); }
} } ");
}
[WorkItem(545986, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestSimplifyToLengthEqualsZero7()
{
await TestFixOneAsync(
@"uint x = 1; [||]if (0 < x) { a(); } else { b(); } } } ",
@"uint x = 1; if (0 == x) { b(); } else { a(); }
} } ");
}
[WorkItem(545986, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestSimplifyToLengthEqualsZero8()
{
await TestFixOneAsync(
@"ulong x = 1; [||]if (0 < x) { a(); } else { b(); } } } ",
@"ulong x = 1; if (0 == x) { b(); } else { a(); }
} } ");
}
[WorkItem(545986, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestSimplifyToLengthEqualsZero9()
{
await TestFixOneAsync(
@"ulong x = 1; [||]if (0 == x) { a(); } else { b(); } } } ",
@"ulong x = 1; if (0 < x) { b(); } else { a(); }
} } ");
}
[WorkItem(545986, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestSimplifyToLengthEqualsZero10()
{
await TestFixOneAsync(
@"ulong x = 1; [||]if (x == 0) { a(); } else { b(); } } } ",
@"ulong x = 1; if (x > 0) { b(); } else { a(); }
} } ");
}
[WorkItem(530505, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530505")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestSimplifyToLengthEqualsZero11()
{
await TestFixOneAsync(
@"string[] x; [||]if (x.LongLength > 0) { GreaterThanZero(); } else { EqualsZero(); } } } ",
@"string[] x; if (x.LongLength == 0) { EqualsZero(); } else { GreaterThanZero(); }
} } ");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestDoesNotSimplifyToLengthEqualsZero()
{
await TestFixOneAsync(
@"string x; [||]if (x.Length >= 0) { a(); } else { b(); } } } ",
@"string x; if (x.Length < 0) { b(); } else { a(); }
} } ");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)]
public async Task TestDoesNotSimplifyToLengthEqualsZero2()
{
await TestFixOneAsync(
@"string x; [||]if (x.Length > 0.0f) { GreaterThanZero(); } else { EqualsZero(); } } } ",
@"string x; if (x.Length <= 0.0f) { EqualsZero(); } else { GreaterThanZero(); }
} } ");
}
}
}
......@@ -77,7 +77,6 @@ public static class Features
public const string CodeActionsInsertMissingCast = "CodeActions.InsertMissingCast";
public const string CodeActionsInsertMissingTokens = "CodeActions.InsertMissingTokens";
public const string CodeActionsIntroduceVariable = "CodeActions.IntroduceVariable";
public const string CodeActionsInvertIf = "CodeActions.InvertIf";
public const string CodeActionsInvokeDelegateWithConditionalAccess = "CodeActions.InvokeDelegateWithConditionalAccess";
public const string CodeActionsLambdaSimplifier = "CodeActions.LambdaSimplifier";
public const string CodeActionsMakeMethodAsynchronous = "CodeActions.MakeMethodAsynchronous";
......
......@@ -548,16 +548,7 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Invert if statement.
/// </summary>
internal static string Invert_if_statement {
get {
return ResourceManager.GetString("Invert_if_statement", resourceCulture);
}
}
/// <summary>
/// <summary>
/// Looks up a localized string similar to is pattern.
/// </summary>
internal static string is_pattern {
......
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.InvertIf
{
// [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.InvertIf)]
internal partial class InvertIfCodeRefactoringProvider : CodeRefactoringProvider
{
private static readonly Dictionary<SyntaxKind, Tuple<SyntaxKind, SyntaxKind>> s_binaryMap =
new Dictionary<SyntaxKind, Tuple<SyntaxKind, SyntaxKind>>(SyntaxFacts.EqualityComparer)
{
{ SyntaxKind.EqualsExpression, Tuple.Create(SyntaxKind.NotEqualsExpression, SyntaxKind.ExclamationEqualsToken) },
{ SyntaxKind.NotEqualsExpression, Tuple.Create(SyntaxKind.EqualsExpression, SyntaxKind.EqualsEqualsToken) },
{ SyntaxKind.LessThanExpression, Tuple.Create(SyntaxKind.GreaterThanOrEqualExpression, SyntaxKind.GreaterThanEqualsToken) },
{ SyntaxKind.LessThanOrEqualExpression, Tuple.Create(SyntaxKind.GreaterThanExpression, SyntaxKind.GreaterThanToken) },
{ SyntaxKind.GreaterThanExpression, Tuple.Create(SyntaxKind.LessThanOrEqualExpression, SyntaxKind.LessThanEqualsToken) },
{ SyntaxKind.GreaterThanOrEqualExpression, Tuple.Create(SyntaxKind.LessThanExpression, SyntaxKind.LessThanToken) },
};
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var document = context.Document;
var textSpan = context.Span;
var cancellationToken = context.CancellationToken;
if (!textSpan.IsEmpty)
{
return;
}
if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles)
{
return;
}
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var ifStatement = root.FindToken(textSpan.Start).GetAncestor<IfStatementSyntax>();
if (ifStatement == null || ifStatement.Else == null)
{
return;
}
if (!ifStatement.IfKeyword.Span.IntersectsWith(textSpan.Start))
{
return;
}
if (ifStatement.OverlapsHiddenPosition(cancellationToken))
{
return;
}
context.RegisterRefactoring(
new MyCodeAction(
CSharpFeaturesResources.Invert_if_statement,
c => InvertIfAsync(document, ifStatement, c)));
}
private async Task<Document> InvertIfAsync(Document document, IfStatementSyntax ifStatement, CancellationToken cancellationToken)
{
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var ifNode = ifStatement;
// In the case that the else clause is actually an else if clause, place the if
// statement to be moved in a new block in order to make sure that the else
// statement matches the right if statement after the edit.
var newIfNodeStatement = ifNode.Else.Statement.Kind() == SyntaxKind.IfStatement ?
SyntaxFactory.Block(ifNode.Else.Statement) :
ifNode.Else.Statement;
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var invertedIf = ifNode.WithCondition(Negate(ifNode.Condition, semanticModel, cancellationToken))
.WithStatement(newIfNodeStatement)
.WithElse(ifNode.Else.WithStatement(ifNode.Statement))
.WithAdditionalAnnotations(Formatter.Annotation);
var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);
var result = root.ReplaceNode(ifNode, invertedIf);
return document.WithSyntaxRoot(result);
}
private bool IsComparisonOfZeroAndSomethingNeverLessThanZero(BinaryExpressionSyntax binaryExpression, SemanticModel semanticModel, CancellationToken cancellationToken)
{
var canSimplify = false;
if (binaryExpression.Kind() == SyntaxKind.GreaterThanExpression &&
binaryExpression.Right.Kind() == SyntaxKind.NumericLiteralExpression)
{
canSimplify = CanSimplifyToLengthEqualsZeroExpression(
binaryExpression.Left,
(LiteralExpressionSyntax)binaryExpression.Right,
semanticModel,
cancellationToken);
}
else if (binaryExpression.Kind() == SyntaxKind.LessThanExpression &&
binaryExpression.Left.Kind() == SyntaxKind.NumericLiteralExpression)
{
canSimplify = CanSimplifyToLengthEqualsZeroExpression(
binaryExpression.Right,
(LiteralExpressionSyntax)binaryExpression.Left,
semanticModel,
cancellationToken);
}
else if (binaryExpression.Kind() == SyntaxKind.EqualsExpression &&
binaryExpression.Right.Kind() == SyntaxKind.NumericLiteralExpression)
{
canSimplify = CanSimplifyToLengthEqualsZeroExpression(
binaryExpression.Left,
(LiteralExpressionSyntax)binaryExpression.Right,
semanticModel,
cancellationToken);
}
else if (binaryExpression.Kind() == SyntaxKind.EqualsExpression &&
binaryExpression.Left.Kind() == SyntaxKind.NumericLiteralExpression)
{
canSimplify = CanSimplifyToLengthEqualsZeroExpression(
binaryExpression.Right,
(LiteralExpressionSyntax)binaryExpression.Left,
semanticModel,
cancellationToken);
}
return canSimplify;
}
private bool CanSimplifyToLengthEqualsZeroExpression(
ExpressionSyntax variableExpression,
LiteralExpressionSyntax numericLiteralExpression,
SemanticModel semanticModel,
CancellationToken cancellationToken)
{
var numericValue = semanticModel.GetConstantValue(numericLiteralExpression, cancellationToken);
if (numericValue.HasValue && numericValue.Value is int && (int)numericValue.Value == 0)
{
var symbol = semanticModel.GetSymbolInfo(variableExpression, cancellationToken).Symbol;
if (symbol != null && (symbol.Name == "Length" || symbol.Name == "LongLength"))
{
var containingType = symbol.ContainingType;
if (containingType != null &&
(containingType.SpecialType == SpecialType.System_Array ||
containingType.SpecialType == SpecialType.System_String))
{
return true;
}
}
var typeInfo = semanticModel.GetTypeInfo(variableExpression, cancellationToken);
if (typeInfo.Type != null)
{
switch (typeInfo.Type.SpecialType)
{
case SpecialType.System_Byte:
case SpecialType.System_UInt16:
case SpecialType.System_UInt32:
case SpecialType.System_UInt64:
return true;
}
}
}
return false;
}
private bool TryNegateBinaryComparisonExpression(
ExpressionSyntax expression,
SemanticModel semanticModel,
CancellationToken cancellationToken,
out ExpressionSyntax result)
{
if (s_binaryMap.TryGetValue(expression.Kind(), out var tuple))
{
var binaryExpression = (BinaryExpressionSyntax)expression;
var expressionType = tuple.Item1;
var operatorType = tuple.Item2;
// Special case negating Length > 0 to Length == 0 and 0 < Length to 0 == Length
// for arrays and strings. We can do this because we know that Length cannot be
// less than 0. Additionally, if we find Length == 0 or 0 == Length, we'll invert
// it to Length > 0 or 0 < Length, respectively.
if (IsComparisonOfZeroAndSomethingNeverLessThanZero(binaryExpression, semanticModel, cancellationToken))
{
operatorType = binaryExpression.OperatorToken.Kind() == SyntaxKind.EqualsEqualsToken
? binaryExpression.Right is LiteralExpressionSyntax ? SyntaxKind.GreaterThanToken : SyntaxKind.LessThanToken
: SyntaxKind.EqualsEqualsToken;
expressionType = binaryExpression.Kind() == SyntaxKind.EqualsExpression
? binaryExpression.Right is LiteralExpressionSyntax ? SyntaxKind.GreaterThanExpression : SyntaxKind.LessThanExpression
: SyntaxKind.EqualsExpression;
}
result = SyntaxFactory.BinaryExpression(
expressionType,
binaryExpression.Left,
SyntaxFactory.Token(
binaryExpression.OperatorToken.LeadingTrivia,
operatorType,
binaryExpression.OperatorToken.TrailingTrivia),
binaryExpression.Right);
return true;
}
result = null;
return false;
}
private ExpressionSyntax Negate(ExpressionSyntax expression, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (TryNegateBinaryComparisonExpression(expression, semanticModel, cancellationToken, out var result))
{
return result;
}
switch (expression.Kind())
{
case SyntaxKind.ParenthesizedExpression:
{
var parenthesizedExpression = (ParenthesizedExpressionSyntax)expression;
return parenthesizedExpression
.WithExpression(Negate(parenthesizedExpression.Expression, semanticModel, cancellationToken))
.WithAdditionalAnnotations(Simplifier.Annotation);
}
case SyntaxKind.LogicalNotExpression:
{
var logicalNotExpression = (PrefixUnaryExpressionSyntax)expression;
var notToken = logicalNotExpression.OperatorToken;
var nextToken = logicalNotExpression.Operand.GetFirstToken(
includeZeroWidth: true, includeSkipped: true, includeDirectives: true, includeDocumentationComments: true);
var existingTrivia = SyntaxFactory.TriviaList(
notToken.LeadingTrivia.Concat(
notToken.TrailingTrivia.Concat(
nextToken.LeadingTrivia)));
var updatedNextToken = nextToken.WithLeadingTrivia(existingTrivia);
return logicalNotExpression.Operand.ReplaceToken(
nextToken,
updatedNextToken);
}
case SyntaxKind.LogicalOrExpression:
{
var binaryExpression = (BinaryExpressionSyntax)expression;
result = SyntaxFactory.BinaryExpression(
SyntaxKind.LogicalAndExpression,
Negate(binaryExpression.Left, semanticModel, cancellationToken),
SyntaxFactory.Token(
binaryExpression.OperatorToken.LeadingTrivia,
SyntaxKind.AmpersandAmpersandToken,
binaryExpression.OperatorToken.TrailingTrivia),
Negate(binaryExpression.Right, semanticModel, cancellationToken));
return result
.Parenthesize()
.WithLeadingTrivia(binaryExpression.GetLeadingTrivia())
.WithTrailingTrivia(binaryExpression.GetTrailingTrivia());
}
case SyntaxKind.LogicalAndExpression:
{
var binaryExpression = (BinaryExpressionSyntax)expression;
result = SyntaxFactory.BinaryExpression(
SyntaxKind.LogicalOrExpression,
Negate(binaryExpression.Left, semanticModel, cancellationToken),
SyntaxFactory.Token(
binaryExpression.OperatorToken.LeadingTrivia,
SyntaxKind.BarBarToken,
binaryExpression.OperatorToken.TrailingTrivia),
Negate(binaryExpression.Right, semanticModel, cancellationToken));
return result
.Parenthesize()
.WithLeadingTrivia(binaryExpression.GetLeadingTrivia())
.WithTrailingTrivia(binaryExpression.GetTrailingTrivia());
}
case SyntaxKind.TrueLiteralExpression:
{
var literalExpression = (LiteralExpressionSyntax)expression;
return SyntaxFactory.LiteralExpression(
SyntaxKind.FalseLiteralExpression,
SyntaxFactory.Token(
literalExpression.Token.LeadingTrivia,
SyntaxKind.FalseKeyword,
literalExpression.Token.TrailingTrivia));
}
case SyntaxKind.FalseLiteralExpression:
{
var literalExpression = (LiteralExpressionSyntax)expression;
return SyntaxFactory.LiteralExpression(
SyntaxKind.TrueLiteralExpression,
SyntaxFactory.Token(
literalExpression.Token.LeadingTrivia,
SyntaxKind.TrueKeyword,
literalExpression.Token.TrailingTrivia));
}
}
// Anything else we can just negate by adding a ! in front of the parenthesized expression.
// Unnecessary parentheses will get removed by the simplification service.
return SyntaxFactory.PrefixUnaryExpression(
SyntaxKind.LogicalNotExpression,
expression.Parenthesize());
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument) :
base(title, createChangedDocument)
{
}
}
}
}
......@@ -15,7 +15,6 @@ internal static class PredefinedCodeRefactoringProviderNames
public const string GenerateOverrides = "Generate Overrides Code Action Provider";
public const string InlineTemporary = "Inline Temporary Code Action Provider";
public const string IntroduceVariable = "Introduce Variable Code Action Provider";
public const string InvertIf = "Invert If Code Action Provider";
public const string MoveDeclarationNearReference = "Move Declaration Near Reference Code Action Provider";
public const string SimplifyLambda = "Simplify Lambda Code Action Provider";
public const string ConvertToInterpolatedString = "Convert To Interpolated String Code Action Provider";
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册