diff --git a/src/EditorFeatures/CSharpTest/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs b/src/EditorFeatures/CSharpTest/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs index a64de404bb211e11079936216ae472bf99623895..3118c8c78941327458f0a22dbce05b14f04896bc 100644 --- a/src/EditorFeatures/CSharpTest/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs +++ b/src/EditorFeatures/CSharpTest/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs @@ -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; + } }"); } } diff --git a/src/EditorFeatures/CSharpTest/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs b/src/EditorFeatures/CSharpTest/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs index 04d8064ba586d1bc6daee918d9e73ccc296b5ac5..d434972507e5a7f1b7a0d566eb20df75a80a125b 100644 --- a/src/EditorFeatures/CSharpTest/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs +++ b/src/EditorFeatures/CSharpTest/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs @@ -703,6 +703,124 @@ IEnumerable 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 M(int a) + { + [||]if (a == 0) + { + yield return false; + } + else + { + yield return true; + } + } +}", +@" +using System.Collections.Generic; + +class C +{ + IEnumerable M(int a) + { + yield return a != 0; + } }"); } } diff --git a/src/Features/CSharp/Portable/CodeRefactorings/InvertIf/InvertIfCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/InvertIf/InvertIfCodeRefactoringProvider.cs index 9f4acf77c255d02d70f84b4059521bb3e506f1c3..a4b136f89732361ef49a19501c6485c0e57e2318 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/InvertIf/InvertIfCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/InvertIf/InvertIfCodeRefactoringProvider.cs @@ -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); diff --git a/src/Features/CSharp/Portable/UsePatternMatching/CSharpIsAndCastCheckWithoutNameDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UsePatternMatching/CSharpIsAndCastCheckWithoutNameDiagnosticAnalyzer.cs index 0407903bb7065217f8096355be4d2c4a8e052b3b..1549a165003d2fbaba0fe9cd3b25d979f5a7a87a 100644 --- a/src/Features/CSharp/Portable/UsePatternMatching/CSharpIsAndCastCheckWithoutNameDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UsePatternMatching/CSharpIsAndCastCheckWithoutNameDiagnosticAnalyzer.cs @@ -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 diff --git a/src/Features/Core/Portable/CodeRefactorings/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs b/src/Features/Core/Portable/CodeRefactorings/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs index c7798473e4cccae663302c0dfde59512c7f3be1e..3ce781dd5db4373d34e67053fdbde9474252adbe 100644 --- a/src/Features/Core/Portable/CodeRefactorings/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/CodeRefactorings/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs @@ -15,23 +15,6 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings.InvertIf { internal abstract partial class AbstractInvertIfCodeRefactoringProvider : CodeRefactoringProvider { - private const string LongLength = "LongLength"; - - private static readonly Dictionary s_negatedBinaryMap = - new Dictionary - { - {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; - } - - /// - /// 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. - /// - 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> createChangedDocument) : diff --git a/src/Features/Core/Portable/ConvertAnonymousTypeToClass/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertAnonymousTypeToClass/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs index 62469b9ebb685acf530c71e622c6cf42a86debaf..698e53a8e990727ae0fd8632b00e9f99a22dcda6 100644 --- a/src/Features/Core/Portable/ConvertAnonymousTypeToClass/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertAnonymousTypeToClass/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs @@ -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); diff --git a/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs index db9c032b0f7c0453896d3710d38c0df4b6700a60..68c7eed771e95453b6c8c51d3d6cf28cc4690d40 100644 --- a/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs @@ -743,7 +743,7 @@ private SyntaxNodeOrToken ConvertArgumentOrToken(ISyntaxFactsService syntaxFacts var equalsMethod = await equalsAndGetHashCodeService.GenerateEqualsMethodAsync( document, namedTypeWithoutMembers, ImmutableArray.CastUp(fields), - localNameOpt: ICodeDefinitionFactoryExtensions.OtherName, cancellationToken).ConfigureAwait(false); + localNameOpt: SyntaxGeneratorExtensions.OtherName, cancellationToken).ConfigureAwait(false); var getHashCodeMethod = await equalsAndGetHashCodeService.GenerateGetHashCodeMethodAsync( document, namedTypeWithoutMembers, ImmutableArray.CastUp(fields), cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs b/src/Features/Core/Portable/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs index eeb41b76cd7c7d4dbd93e63d2c89ad19401eb997..92c27f915381b9a6678a99d4ed743b031f76ef41 100644 --- a/src/Features/Core/Portable/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs @@ -81,10 +81,27 @@ internal abstract class AbstractUseConditionalExpressionCodeFixProvider< bool isRef, CancellationToken cancellationToken) { var generator = SyntaxGenerator.GetGenerator(document); - - var syntaxFacts = document.GetLanguageService(); + 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; diff --git a/src/Features/VisualBasic/Portable/CodeRefactorings/InvertIf/InvertIfCodeRefactoringProvider.vb b/src/Features/VisualBasic/Portable/CodeRefactorings/InvertIf/InvertIfCodeRefactoringProvider.vb index d27985f6aaaa5a0d939b9702cb3119f8328f11c1..99329b587efac5988ce40bbf68b76c23f567f57f 100644 --- a/src/Features/VisualBasic/Portable/CodeRefactorings/InvertIf/InvertIfCodeRefactoringProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeRefactorings/InvertIf/InvertIfCodeRefactoringProvider.vb @@ -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)) _ diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index f3a400a40ea1ea5e1c72e6a2d1dd908c7c2e0998..3885944b7d4ac45f2e8383328433fb7fe687efac 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -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); } diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index ce58c1d5c4d8b7fb3e78e33de9382ac5ed60dee4..348308b79b40e37674ff06c8dce5f17be20fb056 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -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); diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ICodeDefinitionFactoryExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions.cs similarity index 99% rename from src/Workspaces/Core/Portable/Shared/Extensions/ICodeDefinitionFactoryExtensions.cs rename to src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions.cs index e41f10117564bfe1d88d26e1a79a2a22e7c3e4b7..f60eabdedacd02ebb26ffbb88ec12bb0b6e08839 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ICodeDefinitionFactoryExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions.cs @@ -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, diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ICodeDefinitionFactoryExtensions_CreateEqualsMethod.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions_CreateEqualsMethod.cs similarity index 99% rename from src/Workspaces/Core/Portable/Shared/Extensions/ICodeDefinitionFactoryExtensions_CreateEqualsMethod.cs rename to src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions_CreateEqualsMethod.cs index 19b61db5e1ff474f1e28db29ca94f776c10eb1e5..797790c27ae57cee4203901ad0047e07ac8aac32 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ICodeDefinitionFactoryExtensions_CreateEqualsMethod.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions_CreateEqualsMethod.cs @@ -1,7 +1,6 @@ // 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"; diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ICodeDefinitionFactoryExtensions_CreateGetHashCodeMethod.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions_CreateGetHashCodeMethod.cs similarity index 98% rename from src/Workspaces/Core/Portable/Shared/Extensions/ICodeDefinitionFactoryExtensions_CreateGetHashCodeMethod.cs rename to src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions_CreateGetHashCodeMethod.cs index 46cdc04bf680914cd4bb3205c7e8b589adfa8213..4e5c0df9ad30b175830ccfb1874991fef5bfeb63 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ICodeDefinitionFactoryExtensions_CreateGetHashCodeMethod.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions_CreateGetHashCodeMethod.cs @@ -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); diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions_Negate.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions_Negate.cs new file mode 100644 index 0000000000000000000000000000000000000000..9da3674f8c2fd165f3f5679fd65ce531864756a6 --- /dev/null +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SyntaxGeneratorExtensions_Negate.cs @@ -0,0 +1,287 @@ +// 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 s_negatedBinaryMap = + new Dictionary + { + {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; + } + + /// + /// 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. + /// + 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); + } + } +} diff --git a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb index 197e980aa095f00d08f334a12b4dacd87421bac7..c6dcd37f2942278dfbe80f2ee66f17f3bb9f481f 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb @@ -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