提交 39d9655d 编写于 作者: C Cyrus Najmabadi

Simplify conditional expressions that are of the form 'expr ? true : false'

上级 1cd08cc2
......@@ -955,6 +955,64 @@ void M(ref int i, ref int j)
ref int x = ref i;
x = ref true ? ref i : ref j;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseConditionalExpression)]
public async Task TestTrueFalse1()
{
await TestInRegularAndScriptAsync(
@"
class C
{
void M(bool i, int j)
{
[||]if (j == 0)
{
i = true;
}
else
{
i = false;
}
}
}",
@"
class C
{
void M(bool i, int j)
{
i = j == 0;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseConditionalExpression)]
public async Task TestTrueFalse2()
{
await TestInRegularAndScriptAsync(
@"
class C
{
void M(bool i, int j)
{
[||]if (j == 0)
{
i = false;
}
else
{
i = true;
}
}
}",
@"
class C
{
void M(bool i, int j)
{
i = j != 0;
}
}");
}
}
......
......@@ -703,6 +703,124 @@ IEnumerable<int> M()
yield return 1;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseConditionalExpression)]
public async Task TestReturnTrueFalse1()
{
await TestInRegularAndScriptAsync(
@"
class C
{
bool M(int a)
{
[||]if (a == 0)
{
return true;
}
else
{
return false;
}
}
}",
@"
class C
{
bool M(int a)
{
return a == 0;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseConditionalExpression)]
public async Task TestReturnTrueFalse2()
{
await TestInRegularAndScriptAsync(
@"
class C
{
bool M(int a)
{
[||]if (a == 0)
{
return false;
}
else
{
return true;
}
}
}",
@"
class C
{
bool M(int a)
{
return a != 0;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseConditionalExpression)]
public async Task TestReturnTrueFalse3()
{
await TestInRegularAndScriptAsync(
@"
class C
{
bool M(int a)
{
[||]if (a == 0)
{
return false;
}
return true;
}
}",
@"
class C
{
bool M(int a)
{
return a != 0;
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseConditionalExpression)]
public async Task TestReturnTrueFalse4()
{
await TestInRegularAndScriptAsync(
@"
using System.Collections.Generic;
class C
{
IEnumerable<bool> M(int a)
{
[||]if (a == 0)
{
yield return false;
}
else
{
yield return true;
}
}
}",
@"
using System.Collections.Generic;
class C
{
IEnumerable<bool> M(int a)
{
yield return a != 0;
}
}");
}
}
......
......@@ -71,7 +71,7 @@ protected override SyntaxNode GetIfStatement(TextSpan textSpan, SyntaxToken toke
? SyntaxFactory.Block(newIfNodeStatement)
: newIfNodeStatement;
oldIfStatement = oldIfStatement.WithCondition((ExpressionSyntax)(Negate(oldIfStatement.Condition, generator, syntaxFacts, model, cancellationToken)))
oldIfStatement = oldIfStatement.WithCondition((ExpressionSyntax)generator.Negate(oldIfStatement.Condition, model, cancellationToken))
.WithStatement(newIfStatment)
.WithElse(newElseStatement);
......
......@@ -134,7 +134,7 @@ private void SyntaxNodeAction(SyntaxNodeAnalysisContext context)
.ToSet();
var localName = NameGenerator.EnsureUniqueness(
ICodeDefinitionFactoryExtensions.GetLocalName(typeSymbol),
SyntaxGeneratorExtensions.GetLocalName(typeSymbol),
reservedNames).EscapeIdentifier();
// Now, go and actually try to make the change. This will allow us to see all the
......
......@@ -15,23 +15,6 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings.InvertIf
{
internal abstract partial class AbstractInvertIfCodeRefactoringProvider : CodeRefactoringProvider
{
private const string LongLength = "LongLength";
private static readonly Dictionary<BinaryOperatorKind, BinaryOperatorKind> s_negatedBinaryMap =
new Dictionary<BinaryOperatorKind, BinaryOperatorKind>
{
{BinaryOperatorKind.Equals, BinaryOperatorKind.NotEquals},
{BinaryOperatorKind.NotEquals, BinaryOperatorKind.Equals},
{BinaryOperatorKind.LessThan, BinaryOperatorKind.GreaterThanOrEqual},
{BinaryOperatorKind.GreaterThan, BinaryOperatorKind.LessThanOrEqual},
{BinaryOperatorKind.LessThanOrEqual, BinaryOperatorKind.GreaterThan},
{BinaryOperatorKind.GreaterThanOrEqual, BinaryOperatorKind.LessThan},
{BinaryOperatorKind.Or, BinaryOperatorKind.And},
{BinaryOperatorKind.And, BinaryOperatorKind.Or},
{BinaryOperatorKind.ConditionalOr, BinaryOperatorKind.ConditionalAnd},
{BinaryOperatorKind.ConditionalAnd, BinaryOperatorKind.ConditionalOr},
};
protected abstract SyntaxNode GetIfStatement(TextSpan textSpan, SyntaxToken token, CancellationToken cancellationToken);
protected abstract SyntaxNode GetRootWithInvertIfStatement(Document document, SemanticModel model, SyntaxNode ifStatement, CancellationToken cancellationToken);
protected abstract ISyntaxFactsService GetSyntaxFactsService();
......@@ -81,264 +64,6 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
document, model, ifStatement, cancellationToken));
}
internal SyntaxNode Negate(
SyntaxNode expression,
SyntaxGenerator generator,
ISyntaxFactsService syntaxFacts,
SemanticModel semanticModel,
CancellationToken cancellationToken)
{
if (syntaxFacts.IsParenthesizedExpression(expression))
{
return syntaxFacts.Parenthesize(
Negate(
syntaxFacts.GetExpressionOfParenthesizedExpression(expression),
generator,
syntaxFacts,
semanticModel,
cancellationToken))
.WithTriviaFrom(expression);
}
if (syntaxFacts.IsBinaryExpression(expression))
{
return GetNegationOfBinaryExpression(expression, generator, syntaxFacts, semanticModel, cancellationToken);
}
else if (syntaxFacts.IsLiteralExpression(expression))
{
return GetNegationOfLiteralExpression(expression, generator, semanticModel);
}
else if (syntaxFacts.IsLogicalNotExpression(expression))
{
return GetNegationOfLogicalNotExpression(expression, syntaxFacts);
}
return generator.LogicalNotExpression(expression);
}
private SyntaxNode GetNegationOfBinaryExpression(
SyntaxNode expressionNode,
SyntaxGenerator generator,
ISyntaxFactsService syntaxFacts,
SemanticModel semanticModel,
CancellationToken cancellationToken)
{
syntaxFacts.GetPartsOfBinaryExpression(expressionNode, out var leftOperand, out var operatorToken, out var rightOperand);
var operation = semanticModel.GetOperation(expressionNode, cancellationToken);
if (operation.Kind == OperationKind.IsPattern)
{
return generator.LogicalNotExpression(expressionNode);
}
var binaryOperation = (IBinaryOperation)operation;
if (!s_negatedBinaryMap.TryGetValue(binaryOperation.OperatorKind, out var negatedKind))
{
return generator.LogicalNotExpression(expressionNode);
}
else
{
var negateOperands = false;
switch (binaryOperation.OperatorKind)
{
case BinaryOperatorKind.Or:
case BinaryOperatorKind.And:
case BinaryOperatorKind.ConditionalAnd:
case BinaryOperatorKind.ConditionalOr:
negateOperands = true;
break;
}
//Workaround for https://github.com/dotnet/roslyn/issues/23956
//Issue to remove this when above is merged
if (binaryOperation.OperatorKind == BinaryOperatorKind.Or && syntaxFacts.IsConditionalOr(expressionNode))
{
negatedKind = BinaryOperatorKind.ConditionalAnd;
}
else if (binaryOperation.OperatorKind == BinaryOperatorKind.And && syntaxFacts.IsConditionalAnd(expressionNode))
{
negatedKind = BinaryOperatorKind.ConditionalOr;
}
var newLeftOperand = leftOperand;
var newRightOperand = rightOperand;
if (negateOperands)
{
newLeftOperand = Negate(leftOperand, generator, syntaxFacts, semanticModel, cancellationToken);
newRightOperand = Negate(rightOperand, generator, syntaxFacts, semanticModel, cancellationToken);
}
var newBinaryExpressionSyntax = NewBinaryOperation(binaryOperation, newLeftOperand, negatedKind, newRightOperand, generator, syntaxFacts, cancellationToken)
.WithTriviaFrom(expressionNode);
var newToken = syntaxFacts.GetOperatorTokenOfBinaryExpression(newBinaryExpressionSyntax);
var newTokenWithTrivia = newToken.WithTriviaFrom(operatorToken);
return newBinaryExpressionSyntax.ReplaceToken(newToken, newTokenWithTrivia);
}
}
private SyntaxNode NewBinaryOperation(
IBinaryOperation binaryOperation,
SyntaxNode leftOperand,
BinaryOperatorKind operationKind,
SyntaxNode rightOperand,
SyntaxGenerator generator,
ISyntaxFactsService syntaxFacts,
CancellationToken cancellationToken)
{
switch (operationKind)
{
case BinaryOperatorKind.Equals:
return binaryOperation.LeftOperand.Type?.IsValueType == true && binaryOperation.RightOperand.Type?.IsValueType == true
? generator.ValueEqualsExpression(leftOperand, rightOperand)
: generator.ReferenceEqualsExpression(leftOperand, rightOperand);
case BinaryOperatorKind.NotEquals:
return binaryOperation.LeftOperand.Type?.IsValueType == true && binaryOperation.RightOperand.Type?.IsValueType == true
? generator.ValueNotEqualsExpression(leftOperand, rightOperand)
: generator.ReferenceNotEqualsExpression(leftOperand, rightOperand);
case BinaryOperatorKind.LessThanOrEqual:
return IsSpecialCaseBinaryExpression(binaryOperation, operationKind, cancellationToken)
? generator.ValueEqualsExpression(leftOperand, rightOperand)
: generator.LessThanOrEqualExpression(leftOperand, rightOperand);
case BinaryOperatorKind.GreaterThanOrEqual:
return IsSpecialCaseBinaryExpression(binaryOperation, operationKind, cancellationToken)
? generator.ValueEqualsExpression(leftOperand, rightOperand)
: generator.GreaterThanOrEqualExpression(leftOperand, rightOperand);
case BinaryOperatorKind.LessThan:
return generator.LessThanExpression(leftOperand, rightOperand);
case BinaryOperatorKind.GreaterThan:
return generator.GreaterThanExpression(leftOperand, rightOperand);
case BinaryOperatorKind.Or:
return generator.BitwiseOrExpression(leftOperand, rightOperand);
case BinaryOperatorKind.And:
return generator.BitwiseAndExpression(leftOperand, rightOperand);
case BinaryOperatorKind.ConditionalOr:
return generator.LogicalOrExpression(leftOperand, rightOperand);
case BinaryOperatorKind.ConditionalAnd:
return generator.LogicalAndExpression(leftOperand, rightOperand);
}
return null;
}
/// <summary>
/// Returns true if the binaryExpression consists of an expression that can never be negative,
/// such as length or unsigned numeric types, being compared to zero with greater than,
/// less than, or equals relational operator.
/// </summary>
public bool IsSpecialCaseBinaryExpression(
IBinaryOperation binaryOperation,
BinaryOperatorKind operationKind,
CancellationToken cancellationToken)
{
if (binaryOperation == null)
{
return false;
}
var rightOperand = RemoveImplicitConversion(binaryOperation.RightOperand);
var leftOperand = RemoveImplicitConversion(binaryOperation.LeftOperand);
switch (operationKind)
{
case BinaryOperatorKind.LessThanOrEqual when IsNumericLiteral(rightOperand):
return CanSimplifyToLengthEqualsZeroExpression(
leftOperand,
(ILiteralOperation)rightOperand,
cancellationToken);
case BinaryOperatorKind.GreaterThanOrEqual when IsNumericLiteral(leftOperand):
return CanSimplifyToLengthEqualsZeroExpression(
rightOperand,
(ILiteralOperation)leftOperand,
cancellationToken);
}
return false;
}
private bool IsNumericLiteral(IOperation operation)
=> operation.Kind == OperationKind.Literal && operation.Type.IsNumericType();
private IOperation RemoveImplicitConversion(IOperation operation)
{
return operation is IConversionOperation conversion && conversion.IsImplicit
? RemoveImplicitConversion(conversion.Operand)
: operation;
}
private bool CanSimplifyToLengthEqualsZeroExpression(
IOperation variableExpression,
ILiteralOperation numericLiteralExpression,
CancellationToken cancellationToken)
{
var numericValue = numericLiteralExpression.ConstantValue;
if (numericValue.HasValue && numericValue.Value is 0)
{
if (variableExpression is IPropertyReferenceOperation propertyOperation)
{
var property = propertyOperation.Property;
if ((property.Name == nameof(Array.Length) || property.Name == LongLength))
{
var containingType = property.ContainingType;
if (containingType?.SpecialType == SpecialType.System_Array ||
containingType.SpecialType == SpecialType.System_String)
{
return true;
}
}
}
var type = variableExpression.Type;
if (type != null)
{
switch (type.SpecialType)
{
case SpecialType.System_Byte:
case SpecialType.System_UInt16:
case SpecialType.System_UInt32:
case SpecialType.System_UInt64:
return true;
}
}
}
return false;
}
private SyntaxNode GetNegationOfLiteralExpression(
SyntaxNode expression,
SyntaxGenerator generator,
SemanticModel semanticModel)
{
var operation = semanticModel.GetOperation(expression);
SyntaxNode newLiteralExpression;
if (operation?.Kind == OperationKind.Literal && operation.ConstantValue.HasValue && operation.ConstantValue.Value is true)
{
newLiteralExpression = generator.FalseLiteralExpression();
}
else if (operation?.Kind == OperationKind.Literal && operation.ConstantValue.HasValue && operation.ConstantValue.Value is false)
{
newLiteralExpression = generator.TrueLiteralExpression();
}
else
{
newLiteralExpression = generator.LogicalNotExpression(expression.WithoutTrivia());
}
return newLiteralExpression.WithTriviaFrom(expression);
}
private SyntaxNode GetNegationOfLogicalNotExpression(
SyntaxNode expression,
ISyntaxFactsService syntaxFacts)
{
var operatorToken = syntaxFacts.GetOperatorTokenOfPrefixUnaryExpression(expression);
var operand = syntaxFacts.GetOperandOfPrefixUnaryExpression(expression);
return operand.WithPrependedLeadingTrivia(operatorToken.LeadingTrivia);
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument) :
......
......@@ -309,7 +309,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
var equalsMethod = await equalsAndGetHashCodeService.GenerateEqualsMethodAsync(
document, namedTypeWithoutMembers, readonlyProperties,
localNameOpt: ICodeDefinitionFactoryExtensions.OtherName, cancellationToken).ConfigureAwait(false);
localNameOpt: SyntaxGeneratorExtensions.OtherName, cancellationToken).ConfigureAwait(false);
var getHashCodeMethod = await equalsAndGetHashCodeService.GenerateGetHashCodeMethodAsync(
document, namedTypeWithoutMembers,
readonlyProperties, cancellationToken).ConfigureAwait(false);
......
......@@ -743,7 +743,7 @@ private SyntaxNodeOrToken ConvertArgumentOrToken(ISyntaxFactsService syntaxFacts
var equalsMethod = await equalsAndGetHashCodeService.GenerateEqualsMethodAsync(
document, namedTypeWithoutMembers, ImmutableArray<ISymbol>.CastUp(fields),
localNameOpt: ICodeDefinitionFactoryExtensions.OtherName, cancellationToken).ConfigureAwait(false);
localNameOpt: SyntaxGeneratorExtensions.OtherName, cancellationToken).ConfigureAwait(false);
var getHashCodeMethod = await equalsAndGetHashCodeService.GenerateGetHashCodeMethodAsync(
document, namedTypeWithoutMembers,
ImmutableArray<ISymbol>.CastUp(fields), cancellationToken).ConfigureAwait(false);
......
......@@ -81,10 +81,27 @@ internal abstract class AbstractUseConditionalExpressionCodeFixProvider<
bool isRef, CancellationToken cancellationToken)
{
var generator = SyntaxGenerator.GetGenerator(document);
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var condition = ifOperation.Condition.Syntax;
if (!isRef)
{
// If we are going to generate "expr ? true : false" then just generate "expr"
// instead.
if (IsBooleanLiteral(trueValue, true) && IsBooleanLiteral(falseValue, false))
{
return (TExpressionSyntax)condition.WithoutTrivia();
}
// If we are going to generate "expr ? false : true" then just generate "!expr"
// instead.
if (IsBooleanLiteral(trueValue, false) && IsBooleanLiteral(falseValue, true))
{
return (TExpressionSyntax)generator.Negate(
condition, semanticModel, cancellationToken).WithoutTrivia();
}
}
var conditionalExpression = (TConditionalExpressionSyntax)generator.ConditionalExpression(
condition.WithoutTrivia(),
MakeRef(generator, isRef, CastValueIfNecessary(generator, trueValue)),
......@@ -103,6 +120,17 @@ internal abstract class AbstractUseConditionalExpressionCodeFixProvider<
return MakeRef(generator, isRef, conditionalExpression);
}
private static bool IsBooleanLiteral(IOperation trueValue, bool val)
{
if (trueValue is ILiteralOperation)
{
var constant = trueValue.ConstantValue;
return constant.HasValue && constant.Value is bool b && b == val;
}
return false;
}
private TExpressionSyntax MakeRef(SyntaxGenerator generator, bool isRef, TExpressionSyntax syntaxNode)
=> isRef ? (TExpressionSyntax)generator.RefExpression(syntaxNode) : syntaxNode;
......
......@@ -61,7 +61,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
Protected Overrides Function GetRootWithInvertIfStatement(document As Document, model As SemanticModel, ifStatement As SyntaxNode, cancellationToken As CancellationToken) As SyntaxNode
Dim generator = SyntaxGenerator.GetGenerator(document)
Dim syntaxFacts = GetSyntaxFactsService()
Dim result = UpdateSemanticModel(model, model.SyntaxTree.GetRoot().ReplaceNode(ifStatement, ifStatement.WithAdditionalAnnotations(s_ifNodeAnnotation)), cancellationToken)
......@@ -81,9 +80,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
End If
If (TypeOf ifNode Is SingleLineIfStatementSyntax) Then
model = InvertSingleLineIfStatement(document, DirectCast(ifNode, SingleLineIfStatementSyntax), generator, syntaxFacts, result.Model, cancellationToken)
model = InvertSingleLineIfStatement(document, DirectCast(ifNode, SingleLineIfStatementSyntax), generator, result.Model, cancellationToken)
Else
model = InvertMultiLineIfBlock(DirectCast(ifNode, MultiLineIfBlockSyntax), document, generator, syntaxFacts, result.Model, cancellationToken)
model = InvertMultiLineIfBlock(DirectCast(ifNode, MultiLineIfBlockSyntax), document, generator, result.Model, cancellationToken)
End If
' Complexify the inverted if node.
......@@ -105,11 +104,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
document As Document,
originalIfNode As SingleLineIfStatementSyntax,
generator As SyntaxGenerator,
syntaxFacts As ISyntaxFactsService,
model As SemanticModel,
cancellationToken As CancellationToken) As SemanticModel
Dim root = model.SyntaxTree.GetRoot()
Dim invertedIfNode = GetInvertedIfNode(originalIfNode, document, generator, syntaxFacts, model, cancellationToken)
Dim invertedIfNode = GetInvertedIfNode(originalIfNode, document, generator, model, cancellationToken)
Dim result = UpdateSemanticModel(model, root.ReplaceNode(originalIfNode, invertedIfNode), cancellationToken)
' Complexify the next statement if there is one.
......@@ -137,7 +135,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
ifNode As SingleLineIfStatementSyntax,
document As Document,
generator As SyntaxGenerator,
syntaxFacts As ISyntaxFactsService,
semanticModel As SemanticModel,
cancellationToken As CancellationToken) As SingleLineIfStatementSyntax
......@@ -166,7 +163,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
Dim trailing = singleLineIf.GetTrailingTrivia()
If trailing.Any(SyntaxKind.EndOfLineTrivia) Then
Dim eol = trailing.Last(Function(t) t.Kind = SyntaxKind.EndOfLineTrivia)
trailing = trailing.Select(Function(t) If(t = eol, SyntaxFactory.ColonTrivia(syntaxFacts.GetText(SyntaxKind.ColonTrivia)), t)).ToSyntaxTriviaList()
trailing = trailing.Select(Function(t) If(t = eol, SyntaxFactory.ColonTrivia(SyntaxFacts.GetText(SyntaxKind.ColonTrivia)), t)).ToSyntaxTriviaList()
End If
Dim withElsePart = singleLineIf.WithTrailingTrivia(trailing).WithElseClause(
......@@ -177,7 +174,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
End If
End If
Return ifNode.WithCondition(DirectCast(Negate(ifNode.Condition, generator, syntaxFacts, semanticModel, cancellationToken), ExpressionSyntax)) _
Return ifNode.WithCondition(DirectCast(generator.Negate(ifNode.Condition, semanticModel, cancellationToken), ExpressionSyntax)) _
.WithStatements(newIfStatements) _
.WithElseClause(elseClause.WithStatements(ifNode.Statements).WithTrailingTrivia(elseClause.GetTrailingTrivia()))
End Function
......@@ -209,8 +206,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
End Function
#End If
Private Function InvertMultiLineIfBlock(originalIfNode As MultiLineIfBlockSyntax, document As Document, generator As SyntaxGenerator, syntaxFacts As ISyntaxFactsService, model As SemanticModel, cancellationToken As CancellationToken) As SemanticModel
Dim invertedIfNode = GetInvertedIfNode(originalIfNode, document, generator, syntaxFacts, model, cancellationToken)
Private Function InvertMultiLineIfBlock(originalIfNode As MultiLineIfBlockSyntax, document As Document, generator As SyntaxGenerator, model As SemanticModel, cancellationToken As CancellationToken) As SemanticModel
Dim invertedIfNode = GetInvertedIfNode(originalIfNode, document, generator, model, cancellationToken)
Dim result = UpdateSemanticModel(model, model.SyntaxTree.GetRoot().ReplaceNode(originalIfNode, invertedIfNode), cancellationToken)
Return result.Model
......@@ -220,7 +217,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
ifNode As MultiLineIfBlockSyntax,
document As Document,
generator As SyntaxGenerator,
syntaxFacts As ISyntaxFactsService,
semanticModel As SemanticModel,
cancellationToken As CancellationToken) As MultiLineIfBlockSyntax
......@@ -235,7 +231,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
Dim endifLeadingTrivia = ifNode.EndIfStatement.GetLeadingTrivia()
Return ifNode _
.WithIfStatement(ifStatement.WithCondition(DirectCast(Negate(ifStatement.Condition, generator, syntaxFacts, semanticModel, cancellationToken), ExpressionSyntax))) _
.WithIfStatement(ifStatement.WithCondition(DirectCast(generator.Negate(ifStatement.Condition, semanticModel, cancellationToken), ExpressionSyntax))) _
.WithStatements(elseBlock.Statements) _
.WithElseBlock(elseBlock.WithStatements(ifPart.Statements).WithLeadingTrivia(endifLeadingTrivia)) _
.WithEndIfStatement(ifNode.EndIfStatement.WithTrailingTrivia(endifTrailingTrivia).WithLeadingTrivia(elseBlockLeadingTrivia)) _
......
......@@ -10,6 +10,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
......@@ -24,6 +25,8 @@ internal class CSharpSyntaxGenerator : SyntaxGenerator
internal override bool RequiresExplicitImplementationForInterfaceMembers => false;
internal override ISyntaxFactsService SyntaxFacts => CSharpSyntaxFactsService.Instance;
internal override SyntaxTrivia EndOfLine(string text)
=> SyntaxFactory.EndOfLine(text);
......@@ -2646,7 +2649,7 @@ public override SyntaxNode Visit(SyntaxNode node)
public override SyntaxToken VisitToken(SyntaxToken token)
{
var rewrittenToken = base.VisitToken(token);
if (!rewrittenToken.IsMissing || !SyntaxFacts.IsPunctuationOrKeyword(token.Kind()))
if (!rewrittenToken.IsMissing || !CSharp.SyntaxFacts.IsPunctuationOrKeyword(token.Kind()))
{
return rewrittenToken;
}
......@@ -3563,7 +3566,7 @@ public override SyntaxNode AwaitExpression(SyntaxNode expression)
public override SyntaxNode NameOfExpression(SyntaxNode expression)
{
return this.InvocationExpression(
this.IdentifierName(SyntaxFacts.GetText(SyntaxKind.NameOfKeyword)),
this.IdentifierName(CSharp.SyntaxFacts.GetText(SyntaxKind.NameOfKeyword)),
expression);
}
......
......@@ -7,6 +7,7 @@
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
......@@ -33,6 +34,7 @@ public abstract class SyntaxGenerator : ILanguageService
internal abstract SyntaxTrivia CarriageReturnLineFeed { get; }
internal abstract SyntaxTrivia ElasticCarriageReturnLineFeed { get; }
internal abstract bool RequiresExplicitImplementationForInterfaceMembers { get; }
internal abstract ISyntaxFactsService SyntaxFacts { get; }
internal abstract SyntaxTrivia EndOfLine(string text);
......
......@@ -14,7 +14,7 @@
namespace Microsoft.CodeAnalysis.Shared.Extensions
{
internal static partial class ICodeDefinitionFactoryExtensions
internal static partial class SyntaxGeneratorExtensions
{
public static SyntaxNode CreateThrowNotImplementedStatement(
this SyntaxGenerator codeDefinitionFactory,
......
// 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.Collections.Immutable;
using System.Linq;
using System.Threading;
......@@ -14,7 +13,7 @@
namespace Microsoft.CodeAnalysis.Shared.Extensions
{
internal static partial class ICodeDefinitionFactoryExtensions
internal static partial class SyntaxGeneratorExtensions
{
private const string EqualsName = "Equals";
private const string DefaultName = "Default";
......
......@@ -5,12 +5,11 @@
using System.Threading;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Shared.Extensions
{
internal static partial class ICodeDefinitionFactoryExtensions
internal static partial class SyntaxGeneratorExtensions
{
private const string GetHashCodeName = nameof(object.GetHashCode);
......
// 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.Threading;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Operations;
namespace Microsoft.CodeAnalysis.Shared.Extensions
{
internal static partial class SyntaxGeneratorExtensions
{
private const string LongLength = "LongLength";
private static readonly Dictionary<BinaryOperatorKind, BinaryOperatorKind> s_negatedBinaryMap =
new Dictionary<BinaryOperatorKind, BinaryOperatorKind>
{
{BinaryOperatorKind.Equals, BinaryOperatorKind.NotEquals},
{BinaryOperatorKind.NotEquals, BinaryOperatorKind.Equals},
{BinaryOperatorKind.LessThan, BinaryOperatorKind.GreaterThanOrEqual},
{BinaryOperatorKind.GreaterThan, BinaryOperatorKind.LessThanOrEqual},
{BinaryOperatorKind.LessThanOrEqual, BinaryOperatorKind.GreaterThan},
{BinaryOperatorKind.GreaterThanOrEqual, BinaryOperatorKind.LessThan},
{BinaryOperatorKind.Or, BinaryOperatorKind.And},
{BinaryOperatorKind.And, BinaryOperatorKind.Or},
{BinaryOperatorKind.ConditionalOr, BinaryOperatorKind.ConditionalAnd},
{BinaryOperatorKind.ConditionalAnd, BinaryOperatorKind.ConditionalOr},
};
public static SyntaxNode Negate(
this SyntaxGenerator generator,
SyntaxNode expression,
SemanticModel semanticModel,
CancellationToken cancellationToken)
{
var syntaxFacts = generator.SyntaxFacts;
if (syntaxFacts.IsParenthesizedExpression(expression))
{
return syntaxFacts.Parenthesize(
generator.Negate(
syntaxFacts.GetExpressionOfParenthesizedExpression(expression),
semanticModel,
cancellationToken))
.WithTriviaFrom(expression);
}
if (syntaxFacts.IsBinaryExpression(expression))
{
return GetNegationOfBinaryExpression(expression, generator, semanticModel, cancellationToken);
}
else if (syntaxFacts.IsLiteralExpression(expression))
{
return GetNegationOfLiteralExpression(expression, generator, semanticModel);
}
else if (syntaxFacts.IsLogicalNotExpression(expression))
{
return GetNegationOfLogicalNotExpression(expression, syntaxFacts);
}
return generator.LogicalNotExpression(expression);
}
private static SyntaxNode GetNegationOfBinaryExpression(
SyntaxNode expressionNode,
SyntaxGenerator generator,
SemanticModel semanticModel,
CancellationToken cancellationToken)
{
var syntaxFacts = generator.SyntaxFacts;
syntaxFacts.GetPartsOfBinaryExpression(expressionNode, out var leftOperand, out var operatorToken, out var rightOperand);
var operation = semanticModel.GetOperation(expressionNode, cancellationToken);
if (operation.Kind == OperationKind.IsPattern)
{
return generator.LogicalNotExpression(expressionNode);
}
var binaryOperation = (IBinaryOperation)operation;
if (!s_negatedBinaryMap.TryGetValue(binaryOperation.OperatorKind, out var negatedKind))
{
return generator.LogicalNotExpression(expressionNode);
}
else
{
var negateOperands = false;
switch (binaryOperation.OperatorKind)
{
case BinaryOperatorKind.Or:
case BinaryOperatorKind.And:
case BinaryOperatorKind.ConditionalAnd:
case BinaryOperatorKind.ConditionalOr:
negateOperands = true;
break;
}
//Workaround for https://github.com/dotnet/roslyn/issues/23956
//Issue to remove this when above is merged
if (binaryOperation.OperatorKind == BinaryOperatorKind.Or && syntaxFacts.IsConditionalOr(expressionNode))
{
negatedKind = BinaryOperatorKind.ConditionalAnd;
}
else if (binaryOperation.OperatorKind == BinaryOperatorKind.And && syntaxFacts.IsConditionalAnd(expressionNode))
{
negatedKind = BinaryOperatorKind.ConditionalOr;
}
var newLeftOperand = leftOperand;
var newRightOperand = rightOperand;
if (negateOperands)
{
newLeftOperand = generator.Negate(leftOperand, semanticModel, cancellationToken);
newRightOperand = generator.Negate(rightOperand, semanticModel, cancellationToken);
}
var newBinaryExpressionSyntax = NewBinaryOperation(binaryOperation, newLeftOperand, negatedKind, newRightOperand, generator, cancellationToken)
.WithTriviaFrom(expressionNode);
var newToken = syntaxFacts.GetOperatorTokenOfBinaryExpression(newBinaryExpressionSyntax);
var newTokenWithTrivia = newToken.WithTriviaFrom(operatorToken);
return newBinaryExpressionSyntax.ReplaceToken(newToken, newTokenWithTrivia);
}
}
private static SyntaxNode NewBinaryOperation(
IBinaryOperation binaryOperation,
SyntaxNode leftOperand,
BinaryOperatorKind operationKind,
SyntaxNode rightOperand,
SyntaxGenerator generator,
CancellationToken cancellationToken)
{
switch (operationKind)
{
case BinaryOperatorKind.Equals:
return binaryOperation.LeftOperand.Type?.IsValueType == true && binaryOperation.RightOperand.Type?.IsValueType == true
? generator.ValueEqualsExpression(leftOperand, rightOperand)
: generator.ReferenceEqualsExpression(leftOperand, rightOperand);
case BinaryOperatorKind.NotEquals:
return binaryOperation.LeftOperand.Type?.IsValueType == true && binaryOperation.RightOperand.Type?.IsValueType == true
? generator.ValueNotEqualsExpression(leftOperand, rightOperand)
: generator.ReferenceNotEqualsExpression(leftOperand, rightOperand);
case BinaryOperatorKind.LessThanOrEqual:
return IsSpecialCaseBinaryExpression(binaryOperation, operationKind, cancellationToken)
? generator.ValueEqualsExpression(leftOperand, rightOperand)
: generator.LessThanOrEqualExpression(leftOperand, rightOperand);
case BinaryOperatorKind.GreaterThanOrEqual:
return IsSpecialCaseBinaryExpression(binaryOperation, operationKind, cancellationToken)
? generator.ValueEqualsExpression(leftOperand, rightOperand)
: generator.GreaterThanOrEqualExpression(leftOperand, rightOperand);
case BinaryOperatorKind.LessThan:
return generator.LessThanExpression(leftOperand, rightOperand);
case BinaryOperatorKind.GreaterThan:
return generator.GreaterThanExpression(leftOperand, rightOperand);
case BinaryOperatorKind.Or:
return generator.BitwiseOrExpression(leftOperand, rightOperand);
case BinaryOperatorKind.And:
return generator.BitwiseAndExpression(leftOperand, rightOperand);
case BinaryOperatorKind.ConditionalOr:
return generator.LogicalOrExpression(leftOperand, rightOperand);
case BinaryOperatorKind.ConditionalAnd:
return generator.LogicalAndExpression(leftOperand, rightOperand);
}
return null;
}
/// <summary>
/// Returns true if the binaryExpression consists of an expression that can never be negative,
/// such as length or unsigned numeric types, being compared to zero with greater than,
/// less than, or equals relational operator.
/// </summary>
public static bool IsSpecialCaseBinaryExpression(
IBinaryOperation binaryOperation,
BinaryOperatorKind operationKind,
CancellationToken cancellationToken)
{
if (binaryOperation == null)
{
return false;
}
var rightOperand = RemoveImplicitConversion(binaryOperation.RightOperand);
var leftOperand = RemoveImplicitConversion(binaryOperation.LeftOperand);
switch (operationKind)
{
case BinaryOperatorKind.LessThanOrEqual when IsNumericLiteral(rightOperand):
return CanSimplifyToLengthEqualsZeroExpression(
leftOperand,
(ILiteralOperation)rightOperand,
cancellationToken);
case BinaryOperatorKind.GreaterThanOrEqual when IsNumericLiteral(leftOperand):
return CanSimplifyToLengthEqualsZeroExpression(
rightOperand,
(ILiteralOperation)leftOperand,
cancellationToken);
}
return false;
}
private static bool IsNumericLiteral(IOperation operation)
=> operation.Kind == OperationKind.Literal && operation.Type.IsNumericType();
private static IOperation RemoveImplicitConversion(IOperation operation)
{
return operation is IConversionOperation conversion && conversion.IsImplicit
? RemoveImplicitConversion(conversion.Operand)
: operation;
}
private static bool CanSimplifyToLengthEqualsZeroExpression(
IOperation variableExpression,
ILiteralOperation numericLiteralExpression,
CancellationToken cancellationToken)
{
var numericValue = numericLiteralExpression.ConstantValue;
if (numericValue.HasValue && numericValue.Value is 0)
{
if (variableExpression is IPropertyReferenceOperation propertyOperation)
{
var property = propertyOperation.Property;
if ((property.Name == nameof(Array.Length) || property.Name == LongLength))
{
var containingType = property.ContainingType;
if (containingType?.SpecialType == SpecialType.System_Array ||
containingType.SpecialType == SpecialType.System_String)
{
return true;
}
}
}
var type = variableExpression.Type;
if (type != null)
{
switch (type.SpecialType)
{
case SpecialType.System_Byte:
case SpecialType.System_UInt16:
case SpecialType.System_UInt32:
case SpecialType.System_UInt64:
return true;
}
}
}
return false;
}
private static SyntaxNode GetNegationOfLiteralExpression(
SyntaxNode expression,
SyntaxGenerator generator,
SemanticModel semanticModel)
{
var operation = semanticModel.GetOperation(expression);
SyntaxNode newLiteralExpression;
if (operation?.Kind == OperationKind.Literal && operation.ConstantValue.HasValue && operation.ConstantValue.Value is true)
{
newLiteralExpression = generator.FalseLiteralExpression();
}
else if (operation?.Kind == OperationKind.Literal && operation.ConstantValue.HasValue && operation.ConstantValue.Value is false)
{
newLiteralExpression = generator.TrueLiteralExpression();
}
else
{
newLiteralExpression = generator.LogicalNotExpression(expression.WithoutTrivia());
}
return newLiteralExpression.WithTriviaFrom(expression);
}
private static SyntaxNode GetNegationOfLogicalNotExpression(
SyntaxNode expression,
ISyntaxFactsService syntaxFacts)
{
var operatorToken = syntaxFacts.GetOperatorTokenOfPrefixUnaryExpression(expression);
var operand = syntaxFacts.GetOperandOfPrefixUnaryExpression(expression);
return operand.WithPrependedLeadingTrivia(operatorToken.LeadingTrivia);
}
}
}
......@@ -5,6 +5,7 @@ Imports System.Composition
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.LanguageServices
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
......@@ -21,6 +22,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
Friend Overrides ReadOnly Property RequiresExplicitImplementationForInterfaceMembers As Boolean = True
Friend Overrides ReadOnly Property SyntaxFacts As ISyntaxFactsService = VisualBasicSyntaxFactsService.Instance
Friend Overrides Function EndOfLine(text As String) As SyntaxTrivia
Return SyntaxFactory.EndOfLine(text)
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册