提交 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) ...@@ -955,6 +955,64 @@ void M(ref int i, ref int j)
ref int x = ref i; ref int x = ref i;
x = ref true ? ref i : ref j; 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() ...@@ -703,6 +703,124 @@ IEnumerable<int> M()
yield return 1; 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 ...@@ -71,7 +71,7 @@ protected override SyntaxNode GetIfStatement(TextSpan textSpan, SyntaxToken toke
? SyntaxFactory.Block(newIfNodeStatement) ? SyntaxFactory.Block(newIfNodeStatement)
: 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) .WithStatement(newIfStatment)
.WithElse(newElseStatement); .WithElse(newElseStatement);
......
...@@ -134,7 +134,7 @@ private void SyntaxNodeAction(SyntaxNodeAnalysisContext context) ...@@ -134,7 +134,7 @@ private void SyntaxNodeAction(SyntaxNodeAnalysisContext context)
.ToSet(); .ToSet();
var localName = NameGenerator.EnsureUniqueness( var localName = NameGenerator.EnsureUniqueness(
ICodeDefinitionFactoryExtensions.GetLocalName(typeSymbol), SyntaxGeneratorExtensions.GetLocalName(typeSymbol),
reservedNames).EscapeIdentifier(); reservedNames).EscapeIdentifier();
// Now, go and actually try to make the change. This will allow us to see all the // 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 ...@@ -15,23 +15,6 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings.InvertIf
{ {
internal abstract partial class AbstractInvertIfCodeRefactoringProvider : CodeRefactoringProvider 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 GetIfStatement(TextSpan textSpan, SyntaxToken token, CancellationToken cancellationToken);
protected abstract SyntaxNode GetRootWithInvertIfStatement(Document document, SemanticModel model, SyntaxNode ifStatement, CancellationToken cancellationToken); protected abstract SyntaxNode GetRootWithInvertIfStatement(Document document, SemanticModel model, SyntaxNode ifStatement, CancellationToken cancellationToken);
protected abstract ISyntaxFactsService GetSyntaxFactsService(); protected abstract ISyntaxFactsService GetSyntaxFactsService();
...@@ -81,264 +64,6 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte ...@@ -81,264 +64,6 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
document, model, ifStatement, cancellationToken)); 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 private class MyCodeAction : CodeAction.DocumentChangeAction
{ {
public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument) : public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument) :
......
...@@ -309,7 +309,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte ...@@ -309,7 +309,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
var equalsMethod = await equalsAndGetHashCodeService.GenerateEqualsMethodAsync( var equalsMethod = await equalsAndGetHashCodeService.GenerateEqualsMethodAsync(
document, namedTypeWithoutMembers, readonlyProperties, document, namedTypeWithoutMembers, readonlyProperties,
localNameOpt: ICodeDefinitionFactoryExtensions.OtherName, cancellationToken).ConfigureAwait(false); localNameOpt: SyntaxGeneratorExtensions.OtherName, cancellationToken).ConfigureAwait(false);
var getHashCodeMethod = await equalsAndGetHashCodeService.GenerateGetHashCodeMethodAsync( var getHashCodeMethod = await equalsAndGetHashCodeService.GenerateGetHashCodeMethodAsync(
document, namedTypeWithoutMembers, document, namedTypeWithoutMembers,
readonlyProperties, cancellationToken).ConfigureAwait(false); readonlyProperties, cancellationToken).ConfigureAwait(false);
......
...@@ -743,7 +743,7 @@ private SyntaxNodeOrToken ConvertArgumentOrToken(ISyntaxFactsService syntaxFacts ...@@ -743,7 +743,7 @@ private SyntaxNodeOrToken ConvertArgumentOrToken(ISyntaxFactsService syntaxFacts
var equalsMethod = await equalsAndGetHashCodeService.GenerateEqualsMethodAsync( var equalsMethod = await equalsAndGetHashCodeService.GenerateEqualsMethodAsync(
document, namedTypeWithoutMembers, ImmutableArray<ISymbol>.CastUp(fields), document, namedTypeWithoutMembers, ImmutableArray<ISymbol>.CastUp(fields),
localNameOpt: ICodeDefinitionFactoryExtensions.OtherName, cancellationToken).ConfigureAwait(false); localNameOpt: SyntaxGeneratorExtensions.OtherName, cancellationToken).ConfigureAwait(false);
var getHashCodeMethod = await equalsAndGetHashCodeService.GenerateGetHashCodeMethodAsync( var getHashCodeMethod = await equalsAndGetHashCodeService.GenerateGetHashCodeMethodAsync(
document, namedTypeWithoutMembers, document, namedTypeWithoutMembers,
ImmutableArray<ISymbol>.CastUp(fields), cancellationToken).ConfigureAwait(false); ImmutableArray<ISymbol>.CastUp(fields), cancellationToken).ConfigureAwait(false);
......
...@@ -81,10 +81,27 @@ internal abstract class AbstractUseConditionalExpressionCodeFixProvider< ...@@ -81,10 +81,27 @@ internal abstract class AbstractUseConditionalExpressionCodeFixProvider<
bool isRef, CancellationToken cancellationToken) bool isRef, CancellationToken cancellationToken)
{ {
var generator = SyntaxGenerator.GetGenerator(document); var generator = SyntaxGenerator.GetGenerator(document);
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var condition = ifOperation.Condition.Syntax; 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( var conditionalExpression = (TConditionalExpressionSyntax)generator.ConditionalExpression(
condition.WithoutTrivia(), condition.WithoutTrivia(),
MakeRef(generator, isRef, CastValueIfNecessary(generator, trueValue)), MakeRef(generator, isRef, CastValueIfNecessary(generator, trueValue)),
...@@ -103,6 +120,17 @@ internal abstract class AbstractUseConditionalExpressionCodeFixProvider< ...@@ -103,6 +120,17 @@ internal abstract class AbstractUseConditionalExpressionCodeFixProvider<
return MakeRef(generator, isRef, conditionalExpression); 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) private TExpressionSyntax MakeRef(SyntaxGenerator generator, bool isRef, TExpressionSyntax syntaxNode)
=> isRef ? (TExpressionSyntax)generator.RefExpression(syntaxNode) : syntaxNode; => isRef ? (TExpressionSyntax)generator.RefExpression(syntaxNode) : syntaxNode;
......
...@@ -61,7 +61,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf ...@@ -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 Protected Overrides Function GetRootWithInvertIfStatement(document As Document, model As SemanticModel, ifStatement As SyntaxNode, cancellationToken As CancellationToken) As SyntaxNode
Dim generator = SyntaxGenerator.GetGenerator(document) Dim generator = SyntaxGenerator.GetGenerator(document)
Dim syntaxFacts = GetSyntaxFactsService()
Dim result = UpdateSemanticModel(model, model.SyntaxTree.GetRoot().ReplaceNode(ifStatement, ifStatement.WithAdditionalAnnotations(s_ifNodeAnnotation)), cancellationToken) Dim result = UpdateSemanticModel(model, model.SyntaxTree.GetRoot().ReplaceNode(ifStatement, ifStatement.WithAdditionalAnnotations(s_ifNodeAnnotation)), cancellationToken)
...@@ -81,9 +80,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf ...@@ -81,9 +80,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
End If End If
If (TypeOf ifNode Is SingleLineIfStatementSyntax) Then 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 Else
model = InvertMultiLineIfBlock(DirectCast(ifNode, MultiLineIfBlockSyntax), document, generator, syntaxFacts, result.Model, cancellationToken) model = InvertMultiLineIfBlock(DirectCast(ifNode, MultiLineIfBlockSyntax), document, generator, result.Model, cancellationToken)
End If End If
' Complexify the inverted if node. ' Complexify the inverted if node.
...@@ -105,11 +104,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf ...@@ -105,11 +104,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
document As Document, document As Document,
originalIfNode As SingleLineIfStatementSyntax, originalIfNode As SingleLineIfStatementSyntax,
generator As SyntaxGenerator, generator As SyntaxGenerator,
syntaxFacts As ISyntaxFactsService,
model As SemanticModel, model As SemanticModel,
cancellationToken As CancellationToken) As SemanticModel cancellationToken As CancellationToken) As SemanticModel
Dim root = model.SyntaxTree.GetRoot() 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) Dim result = UpdateSemanticModel(model, root.ReplaceNode(originalIfNode, invertedIfNode), cancellationToken)
' Complexify the next statement if there is one. ' Complexify the next statement if there is one.
...@@ -137,7 +135,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf ...@@ -137,7 +135,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
ifNode As SingleLineIfStatementSyntax, ifNode As SingleLineIfStatementSyntax,
document As Document, document As Document,
generator As SyntaxGenerator, generator As SyntaxGenerator,
syntaxFacts As ISyntaxFactsService,
semanticModel As SemanticModel, semanticModel As SemanticModel,
cancellationToken As CancellationToken) As SingleLineIfStatementSyntax cancellationToken As CancellationToken) As SingleLineIfStatementSyntax
...@@ -166,7 +163,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf ...@@ -166,7 +163,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
Dim trailing = singleLineIf.GetTrailingTrivia() Dim trailing = singleLineIf.GetTrailingTrivia()
If trailing.Any(SyntaxKind.EndOfLineTrivia) Then If trailing.Any(SyntaxKind.EndOfLineTrivia) Then
Dim eol = trailing.Last(Function(t) t.Kind = SyntaxKind.EndOfLineTrivia) 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 End If
Dim withElsePart = singleLineIf.WithTrailingTrivia(trailing).WithElseClause( Dim withElsePart = singleLineIf.WithTrailingTrivia(trailing).WithElseClause(
...@@ -177,7 +174,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf ...@@ -177,7 +174,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
End If End If
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) _ .WithStatements(newIfStatements) _
.WithElseClause(elseClause.WithStatements(ifNode.Statements).WithTrailingTrivia(elseClause.GetTrailingTrivia())) .WithElseClause(elseClause.WithStatements(ifNode.Statements).WithTrailingTrivia(elseClause.GetTrailingTrivia()))
End Function End Function
...@@ -209,8 +206,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf ...@@ -209,8 +206,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
End Function End Function
#End If #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 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, syntaxFacts, model, cancellationToken) Dim invertedIfNode = GetInvertedIfNode(originalIfNode, document, generator, model, cancellationToken)
Dim result = UpdateSemanticModel(model, model.SyntaxTree.GetRoot().ReplaceNode(originalIfNode, invertedIfNode), cancellationToken) Dim result = UpdateSemanticModel(model, model.SyntaxTree.GetRoot().ReplaceNode(originalIfNode, invertedIfNode), cancellationToken)
Return result.Model Return result.Model
...@@ -220,7 +217,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf ...@@ -220,7 +217,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
ifNode As MultiLineIfBlockSyntax, ifNode As MultiLineIfBlockSyntax,
document As Document, document As Document,
generator As SyntaxGenerator, generator As SyntaxGenerator,
syntaxFacts As ISyntaxFactsService,
semanticModel As SemanticModel, semanticModel As SemanticModel,
cancellationToken As CancellationToken) As MultiLineIfBlockSyntax cancellationToken As CancellationToken) As MultiLineIfBlockSyntax
...@@ -235,7 +231,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf ...@@ -235,7 +231,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InvertIf
Dim endifLeadingTrivia = ifNode.EndIfStatement.GetLeadingTrivia() Dim endifLeadingTrivia = ifNode.EndIfStatement.GetLeadingTrivia()
Return ifNode _ 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) _ .WithStatements(elseBlock.Statements) _
.WithElseBlock(elseBlock.WithStatements(ifPart.Statements).WithLeadingTrivia(endifLeadingTrivia)) _ .WithElseBlock(elseBlock.WithStatements(ifPart.Statements).WithLeadingTrivia(endifLeadingTrivia)) _
.WithEndIfStatement(ifNode.EndIfStatement.WithTrailingTrivia(endifTrailingTrivia).WithLeadingTrivia(elseBlockLeadingTrivia)) _ .WithEndIfStatement(ifNode.EndIfStatement.WithTrailingTrivia(endifTrailingTrivia).WithLeadingTrivia(elseBlockLeadingTrivia)) _
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -24,6 +25,8 @@ internal class CSharpSyntaxGenerator : SyntaxGenerator ...@@ -24,6 +25,8 @@ internal class CSharpSyntaxGenerator : SyntaxGenerator
internal override bool RequiresExplicitImplementationForInterfaceMembers => false; internal override bool RequiresExplicitImplementationForInterfaceMembers => false;
internal override ISyntaxFactsService SyntaxFacts => CSharpSyntaxFactsService.Instance;
internal override SyntaxTrivia EndOfLine(string text) internal override SyntaxTrivia EndOfLine(string text)
=> SyntaxFactory.EndOfLine(text); => SyntaxFactory.EndOfLine(text);
...@@ -2646,7 +2649,7 @@ public override SyntaxNode Visit(SyntaxNode node) ...@@ -2646,7 +2649,7 @@ public override SyntaxNode Visit(SyntaxNode node)
public override SyntaxToken VisitToken(SyntaxToken token) public override SyntaxToken VisitToken(SyntaxToken token)
{ {
var rewrittenToken = base.VisitToken(token); var rewrittenToken = base.VisitToken(token);
if (!rewrittenToken.IsMissing || !SyntaxFacts.IsPunctuationOrKeyword(token.Kind())) if (!rewrittenToken.IsMissing || !CSharp.SyntaxFacts.IsPunctuationOrKeyword(token.Kind()))
{ {
return rewrittenToken; return rewrittenToken;
} }
...@@ -3563,7 +3566,7 @@ public override SyntaxNode AwaitExpression(SyntaxNode expression) ...@@ -3563,7 +3566,7 @@ public override SyntaxNode AwaitExpression(SyntaxNode expression)
public override SyntaxNode NameOfExpression(SyntaxNode expression) public override SyntaxNode NameOfExpression(SyntaxNode expression)
{ {
return this.InvocationExpression( return this.InvocationExpression(
this.IdentifierName(SyntaxFacts.GetText(SyntaxKind.NameOfKeyword)), this.IdentifierName(CSharp.SyntaxFacts.GetText(SyntaxKind.NameOfKeyword)),
expression); expression);
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Simplification;
...@@ -33,6 +34,7 @@ public abstract class SyntaxGenerator : ILanguageService ...@@ -33,6 +34,7 @@ public abstract class SyntaxGenerator : ILanguageService
internal abstract SyntaxTrivia CarriageReturnLineFeed { get; } internal abstract SyntaxTrivia CarriageReturnLineFeed { get; }
internal abstract SyntaxTrivia ElasticCarriageReturnLineFeed { get; } internal abstract SyntaxTrivia ElasticCarriageReturnLineFeed { get; }
internal abstract bool RequiresExplicitImplementationForInterfaceMembers { get; } internal abstract bool RequiresExplicitImplementationForInterfaceMembers { get; }
internal abstract ISyntaxFactsService SyntaxFacts { get; }
internal abstract SyntaxTrivia EndOfLine(string text); internal abstract SyntaxTrivia EndOfLine(string text);
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
namespace Microsoft.CodeAnalysis.Shared.Extensions namespace Microsoft.CodeAnalysis.Shared.Extensions
{ {
internal static partial class ICodeDefinitionFactoryExtensions internal static partial class SyntaxGeneratorExtensions
{ {
public static SyntaxNode CreateThrowNotImplementedStatement( public static SyntaxNode CreateThrowNotImplementedStatement(
this SyntaxGenerator codeDefinitionFactory, 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. // 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;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
...@@ -14,7 +13,7 @@ ...@@ -14,7 +13,7 @@
namespace Microsoft.CodeAnalysis.Shared.Extensions namespace Microsoft.CodeAnalysis.Shared.Extensions
{ {
internal static partial class ICodeDefinitionFactoryExtensions internal static partial class SyntaxGeneratorExtensions
{ {
private const string EqualsName = "Equals"; private const string EqualsName = "Equals";
private const string DefaultName = "Default"; private const string DefaultName = "Default";
......
...@@ -5,12 +5,11 @@ ...@@ -5,12 +5,11 @@
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities; using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Shared.Extensions namespace Microsoft.CodeAnalysis.Shared.Extensions
{ {
internal static partial class ICodeDefinitionFactoryExtensions internal static partial class SyntaxGeneratorExtensions
{ {
private const string GetHashCodeName = nameof(object.GetHashCode); 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 ...@@ -5,6 +5,7 @@ Imports System.Composition
Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editing Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.LanguageServices
Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Simplification Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
...@@ -21,6 +22,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration ...@@ -21,6 +22,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
Friend Overrides ReadOnly Property RequiresExplicitImplementationForInterfaceMembers As Boolean = True 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 Friend Overrides Function EndOfLine(text As String) As SyntaxTrivia
Return SyntaxFactory.EndOfLine(text) Return SyntaxFactory.EndOfLine(text)
End Function End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册