File: src\Workspaces\SharedUtilitiesAndExtensions\Workspace\Core\Extensions\SyntaxGeneratorExtensions_Negate.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Shared.Extensions;
 
internal static partial class SyntaxGeneratorExtensions
{
    private static readonly ImmutableDictionary<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 },
        }.ToImmutableDictionary();
 
    public static SyntaxNode Negate(
        this SyntaxGenerator generator,
        SyntaxGeneratorInternal generatorInternal,
        SyntaxNode expression,
        SemanticModel semanticModel,
        CancellationToken cancellationToken)
    {
        return Negate(generator, generatorInternal, expression, semanticModel, negateBinary: true, cancellationToken);
    }
 
    public static SyntaxNode Negate(
        this SyntaxGenerator generator,
        SyntaxGeneratorInternal generatorInternal,
        SyntaxNode expressionOrPattern,
        SemanticModel semanticModel,
        bool negateBinary,
        CancellationToken cancellationToken)
    {
        return Negate(generator, generatorInternal, expressionOrPattern, semanticModel, negateBinary, patternValueType: null, cancellationToken);
    }
 
    public static SyntaxNode Negate(
        this SyntaxGenerator generator,
        SyntaxGeneratorInternal generatorInternal,
        SyntaxNode expressionOrPattern,
        SemanticModel semanticModel,
        bool negateBinary,
        SpecialType? patternValueType,
        CancellationToken cancellationToken)
    {
        var options = semanticModel.SyntaxTree.Options;
        var syntaxFacts = generatorInternal.SyntaxFacts;
 
        if (syntaxFacts.IsParenthesizedExpression(expressionOrPattern))
        {
            return generatorInternal.AddParentheses(
                generator.Negate(
                    generatorInternal,
                    syntaxFacts.GetExpressionOfParenthesizedExpression(expressionOrPattern),
                    semanticModel,
                    negateBinary,
                    cancellationToken))
                .WithTriviaFrom(expressionOrPattern);
        }
 
        if (negateBinary && syntaxFacts.IsBinaryExpression(expressionOrPattern))
            return GetNegationOfBinaryExpression(expressionOrPattern, generator, generatorInternal, semanticModel, cancellationToken);
 
        if (syntaxFacts.IsLiteralExpression(expressionOrPattern))
            return GetNegationOfLiteralExpression(expressionOrPattern, generator, semanticModel);
 
        if (syntaxFacts.IsLogicalNotExpression(expressionOrPattern))
            return GetNegationOfLogicalNotExpression(expressionOrPattern, syntaxFacts);
 
        if (negateBinary && syntaxFacts.IsIsPatternExpression(expressionOrPattern))
            return GetNegationOfIsPatternExpression(expressionOrPattern, generator, generatorInternal, semanticModel, cancellationToken);
 
        if (syntaxFacts.IsParenthesizedPattern(expressionOrPattern))
        {
            // Push the negation inside the parenthesized pattern.
            return generatorInternal.AddParentheses(
                generator.Negate(
                    generatorInternal,
                    syntaxFacts.GetPatternOfParenthesizedPattern(expressionOrPattern),
                    semanticModel,
                    negateBinary,
                    cancellationToken))
                .WithTriviaFrom(expressionOrPattern);
        }
 
        if (negateBinary && syntaxFacts.IsBinaryPattern(expressionOrPattern))
            return GetNegationOfBinaryPattern(expressionOrPattern, generator, generatorInternal, semanticModel, cancellationToken);
 
        if (syntaxFacts.IsConstantPattern(expressionOrPattern))
            return GetNegationOfConstantPattern(expressionOrPattern, generator, generatorInternal, patternValueType);
 
        if (syntaxFacts.IsUnaryPattern(expressionOrPattern))
            return GetNegationOfUnaryPattern(expressionOrPattern, generator, generatorInternal, syntaxFacts);
 
        if (syntaxFacts.IsIsTypeExpression(expressionOrPattern))
        {
            syntaxFacts.GetPartsOfAnyIsTypeExpression(expressionOrPattern, out var expression, out var type);
            if (syntaxFacts.SupportsNotPattern(options))
                return generatorInternal.IsPatternExpression(expression, generatorInternal.NotPattern(type));
 
            if (syntaxFacts.SupportsIsNotTypeExpression(options))
                return generatorInternal.IsNotTypeExpression(expression, type);
        }
 
        if (syntaxFacts.IsIsNotTypeExpression(expressionOrPattern))
        {
            syntaxFacts.GetPartsOfAnyIsTypeExpression(expressionOrPattern, out var expression, out var type);
            return generator.IsTypeExpression(expression, type);
        }
 
        if (syntaxFacts.IsRelationalPattern(expressionOrPattern))
        {
            return GetNegationOfRelationalPattern(expressionOrPattern, generatorInternal, patternValueType);
        }
 
        return syntaxFacts.IsAnyPattern(expressionOrPattern)
            ? generatorInternal.NotPattern(expressionOrPattern)
            : generator.LogicalNotExpression(expressionOrPattern);
    }
 
    private static SyntaxNode GetNegationOfBinaryExpression(
        SyntaxNode expressionNode,
        SyntaxGenerator generator,
        SyntaxGeneratorInternal generatorInternal,
        SemanticModel semanticModel,
        CancellationToken cancellationToken)
    {
        var syntaxFacts = generatorInternal.SyntaxFacts;
        syntaxFacts.GetPartsOfBinaryExpression(expressionNode, out var leftOperand, out var operatorToken, out var rightOperand);
 
        var operation = semanticModel.GetOperation(expressionNode, cancellationToken);
        if (operation is IBinaryOperation binaryOperation)
        {
            if (!s_negatedBinaryMap.TryGetValue(binaryOperation.OperatorKind, out var negatedKind))
                return generator.LogicalNotExpression(expressionNode);
 
            // Lifted relational operators return false if either operand is null.
            // Inverting the operator fails to invert the behavior when an operand is null.
            if (binaryOperation.IsLifted
                && binaryOperation.OperatorKind is BinaryOperatorKind.LessThan or
                                                   BinaryOperatorKind.LessThanOrEqual or
                                                   BinaryOperatorKind.GreaterThan or
                                                   BinaryOperatorKind.GreaterThanOrEqual)
            {
                return generator.LogicalNotExpression(expressionNode);
            }
 
            if (binaryOperation.OperatorKind is BinaryOperatorKind.Or or
                                                BinaryOperatorKind.And or
                                                BinaryOperatorKind.ConditionalAnd or
                                                BinaryOperatorKind.ConditionalOr)
            {
                leftOperand = generator.Negate(generatorInternal, leftOperand, semanticModel, cancellationToken);
                rightOperand = generator.Negate(generatorInternal, rightOperand, semanticModel, cancellationToken);
            }
 
            var newBinaryExpressionSyntax = negatedKind is BinaryOperatorKind.Equals or BinaryOperatorKind.NotEquals
                ? generatorInternal.NegateEquality(generator, expressionNode, leftOperand, negatedKind, rightOperand)
                : NegateRelational(generator, binaryOperation, leftOperand, negatedKind, rightOperand);
            newBinaryExpressionSyntax = newBinaryExpressionSyntax.WithTriviaFrom(expressionNode);
 
            var newToken = syntaxFacts.GetOperatorTokenOfBinaryExpression(newBinaryExpressionSyntax);
            return newBinaryExpressionSyntax.ReplaceToken(
                newToken,
                newToken.WithTriviaFrom(operatorToken));
        }
        else if (operation is IIsTypeOperation { TypeOperand.SpecialType: SpecialType.System_Object } && generatorInternal.SupportsPatterns(semanticModel.SyntaxTree.Options))
        {
            // `is object`  ->   `is null`
            return generatorInternal.IsPatternExpression(leftOperand, operatorToken, generatorInternal.ConstantPattern(generator.NullLiteralExpression().WithTriviaFrom(rightOperand)));
        }
        else if (syntaxFacts.IsIsTypeExpression(expressionNode) && syntaxFacts.SupportsNotPattern(semanticModel.SyntaxTree.Options))
        {
            // `is y`   ->    `is not y`
            SyntaxNode innerPattern;
            if (operation is IIsTypeOperation isTypeOperation)
            {
                if (syntaxFacts.IsAnyName(rightOperand))
                {
                    // For named types, we need to convert to a constant expression (where the named type becomes a member
                    // access expression).  For other types (predefined, arrays, etc) we can keep this as a type pattern.
                    // For example: `x is int` can just become `x is not int` where that's a type pattern.  But `x is Y`
                    // will need to become `x is not Y` where that's a constant pattern instead.
                    var typeExpression = generatorInternal.Type(isTypeOperation.TypeOperand, typeContext: false);
                    innerPattern = syntaxFacts.IsAnyType(typeExpression) && !syntaxFacts.IsAnyName(typeExpression)
                        ? generatorInternal.TypePattern(typeExpression)
                        : generatorInternal.ConstantPattern(typeExpression);
                }
                else
                {
                    // original form was already not a name (like a predefined type, or array type, etc.).  Can just
                    // use as is as a type pattern.
                    innerPattern = generatorInternal.TypePattern(rightOperand);
                }
            }
            else
            {
                innerPattern = generatorInternal.ConstantPattern(rightOperand);
            }
 
            return generatorInternal.IsPatternExpression(
                leftOperand,
                operatorToken,
                generatorInternal.NotPattern(innerPattern.WithTriviaFrom(rightOperand)));
        }
        else
        {
            // Apply the logical not operator if it is not a binary operation and also doesn't support patterns.
            return generator.LogicalNotExpression(expressionNode);
        }
    }
 
    private static SyntaxNode GetNegationOfBinaryPattern(
        SyntaxNode pattern,
        SyntaxGenerator generator,
        SyntaxGeneratorInternal generatorInternal,
        SemanticModel semanticModel,
        CancellationToken cancellationToken)
    {
        // Apply De Morgan's laws here.
        //
        //  not (a and b)   ->   not a or not b
        //  not (a or b)    ->   not a and not b
 
        var syntaxFacts = generatorInternal.SyntaxFacts;
        syntaxFacts.GetPartsOfBinaryPattern(pattern, out var left, out var operatorToken, out var right);
 
        var newLeft = generator.Negate(generatorInternal, left, semanticModel, cancellationToken);
        var newRight = generator.Negate(generatorInternal, right, semanticModel, cancellationToken);
 
        var newPattern =
            syntaxFacts.IsAndPattern(pattern) ? generatorInternal.OrPattern(newLeft, newRight) :
            syntaxFacts.IsOrPattern(pattern) ? generatorInternal.AndPattern(newLeft, newRight) :
            throw ExceptionUtilities.UnexpectedValue(pattern.RawKind);
 
        newPattern = newPattern.WithTriviaFrom(pattern);
 
        syntaxFacts.GetPartsOfBinaryPattern(newPattern, out _, out var newToken, out _);
        var newTokenWithTrivia = newToken.WithTriviaFrom(operatorToken);
        return newPattern.ReplaceToken(newToken, newTokenWithTrivia);
    }
 
    private static SyntaxNode GetNegationOfIsPatternExpression(SyntaxNode isExpression, SyntaxGenerator generator, SyntaxGeneratorInternal generatorInternal, SemanticModel semanticModel, CancellationToken cancellationToken)
    {
        // Don't recurse into patterns if the language doesn't support negated patterns.
        // Just wrap with a normal '!' expression.
        var syntaxFacts = generatorInternal.SyntaxFacts;
        syntaxFacts.GetPartsOfIsPatternExpression(isExpression, out var left, out var isToken, out var pattern);
 
        SyntaxNode? negatedPattern = null;
        if (syntaxFacts.SupportsNotPattern(semanticModel.SyntaxTree.Options))
        {
            // We do support 'not' patterns.  So attempt to push a 'not' pattern into the current is-pattern RHS.
            // We include the type of the value when negating the pattern, since it allows for nicer negations of
            // `is true/false` for Boolean values and relational patterns for numeric values.
            var operation = semanticModel.GetOperation(isExpression, cancellationToken);
            var valueType = (operation as IIsPatternOperation)?.Value.Type?.SpecialType;
            negatedPattern = generator.Negate(generatorInternal, pattern, semanticModel, negateBinary: true, valueType, cancellationToken);
        }
        else if (syntaxFacts.IsNotPattern(pattern))
        {
            // we don't support 'not' patterns, but we have a 'not' pattern in code.  Do a simple unwrapping of it.
            negatedPattern = GetNegationOfNotPattern(pattern, generator, generatorInternal, syntaxFacts);
        }
 
        // Negating the pattern may have formed something illegal.  If so, just do a normal `!` negation.
        if (negatedPattern != null && IsLegalPattern(syntaxFacts, negatedPattern, designatorsLegal: true))
        {
            if (syntaxFacts.IsTypePattern(negatedPattern))
            {
                // We started with `x is not t`.  Unwrap the type pattern for 't' and create a simple `is` binary expr `x is t`.
                var type = syntaxFacts.GetTypeOfTypePattern(negatedPattern);
                return generator.IsTypeExpression(left, type);
            }
            else
            {
                // Keep this as a normal `is-pattern`, just with the pattern portion negated.
                return generatorInternal.IsPatternExpression(left, isToken, negatedPattern);
            }
        }
 
        return generator.LogicalNotExpression(isExpression);
    }
 
    private static SyntaxNode GetNegationOfRelationalPattern(
        SyntaxNode expressionNode,
        SyntaxGeneratorInternal generatorInternal,
        SpecialType? patternValueType)
    {
        if (patternValueType is SpecialType specialType && specialType.IsNumericType())
        {
            // If we know the value is numeric, we can negate the relational operator.
            // This is not valid for non-numeric value since they never match a relational pattern.
            // Similarly, it's not valid for nullable values, since null never matches a relational pattern.
            // As an example, `!(new object() is < 1)` is equivalent to `new object() is not < 1` but not `new object() is >= 1`.
            var syntaxFacts = generatorInternal.SyntaxFacts;
            syntaxFacts.GetPartsOfRelationalPattern(expressionNode, out var operatorToken, out var expression);
            syntaxFacts.TryGetPredefinedOperator(operatorToken, out var predefinedOperator);
            return predefinedOperator switch
            {
                PredefinedOperator.LessThan => generatorInternal.GreaterThanEqualsRelationalPattern(expression),
                PredefinedOperator.LessThanOrEqual => generatorInternal.GreaterThanRelationalPattern(expression),
                PredefinedOperator.GreaterThan => generatorInternal.LessThanEqualsRelationalPattern(expression),
                PredefinedOperator.GreaterThanOrEqual => generatorInternal.LessThanRelationalPattern(expression),
                _ => generatorInternal.NotPattern(expressionNode)
            };
        }
 
        return generatorInternal.NotPattern(expressionNode);
    }
 
    private static bool IsLegalPattern(ISyntaxFacts syntaxFacts, SyntaxNode pattern, bool designatorsLegal)
    {
        // It is illegal to create a pattern that has a designator under a not-pattern or or-pattern
        if (syntaxFacts.IsBinaryPattern(pattern))
        {
            syntaxFacts.GetPartsOfBinaryPattern(pattern, out var left, out _, out var right);
            designatorsLegal = designatorsLegal && !syntaxFacts.IsOrPattern(pattern);
            return IsLegalPattern(syntaxFacts, left, designatorsLegal) &&
                   IsLegalPattern(syntaxFacts, right, designatorsLegal);
        }
 
        if (syntaxFacts.IsNotPattern(pattern))
        {
            // it's fine to have `not string s` (or `not (string s)`) as long as we're currently in a location where
            // designators are legal themselves.
            syntaxFacts.GetPartsOfUnaryPattern(pattern, out _, out var subPattern);
            if (syntaxFacts.IsParenthesizedPattern(subPattern))
                subPattern = syntaxFacts.GetPatternOfParenthesizedPattern(subPattern);
 
            if (syntaxFacts.IsDeclarationPattern(subPattern))
                return designatorsLegal;
 
            return IsLegalPattern(syntaxFacts, subPattern, designatorsLegal: false);
        }
 
        if (syntaxFacts.IsParenthesizedPattern(pattern))
        {
            syntaxFacts.GetPartsOfParenthesizedPattern(pattern, out _, out var subPattern, out _);
            return IsLegalPattern(syntaxFacts, subPattern, designatorsLegal);
        }
 
        if (syntaxFacts.IsDeclarationPattern(pattern))
        {
            syntaxFacts.GetPartsOfDeclarationPattern(pattern, out _, out var designator);
            return designator == null || designatorsLegal;
        }
 
        if (syntaxFacts.IsRecursivePattern(pattern))
        {
            syntaxFacts.GetPartsOfRecursivePattern(pattern, out _, out _, out _, out var designator);
            return designator == null || designatorsLegal;
        }
 
        if (syntaxFacts.IsVarPattern(pattern))
            return designatorsLegal;
 
        return true;
    }
 
    private static SyntaxNode NegateRelational(
        SyntaxGenerator generator,
        IBinaryOperation binaryOperation,
        SyntaxNode leftOperand,
        BinaryOperatorKind operationKind,
        SyntaxNode rightOperand)
    {
        return operationKind switch
        {
            BinaryOperatorKind.LessThanOrEqual => IsSpecialCaseBinaryExpression(binaryOperation, operationKind)
                ? generator.ValueEqualsExpression(leftOperand, rightOperand)
                : generator.LessThanOrEqualExpression(leftOperand, rightOperand),
            BinaryOperatorKind.GreaterThanOrEqual => IsSpecialCaseBinaryExpression(binaryOperation, operationKind)
                ? generator.ValueEqualsExpression(leftOperand, rightOperand)
                : generator.GreaterThanOrEqualExpression(leftOperand, rightOperand),
            BinaryOperatorKind.LessThan => generator.LessThanExpression(leftOperand, rightOperand),
            BinaryOperatorKind.GreaterThan => generator.GreaterThanExpression(leftOperand, rightOperand),
            BinaryOperatorKind.Or => generator.BitwiseOrExpression(leftOperand, rightOperand),
            BinaryOperatorKind.And => generator.BitwiseAndExpression(leftOperand, rightOperand),
            BinaryOperatorKind.ConditionalOr => generator.LogicalOrExpression(leftOperand, rightOperand),
            BinaryOperatorKind.ConditionalAnd => generator.LogicalAndExpression(leftOperand, rightOperand),
            _ => throw ExceptionUtilities.UnexpectedValue(operationKind),
        };
    }
 
    /// <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)
    {
        if (binaryOperation == null)
            return false;
 
        var rightOperand = RemoveImplicitConversion(binaryOperation.RightOperand);
        var leftOperand = RemoveImplicitConversion(binaryOperation.LeftOperand);
 
        return operationKind switch
        {
            BinaryOperatorKind.LessThanOrEqual when rightOperand.IsNumericLiteral()
                => CanSimplifyToLengthEqualsZeroExpression(leftOperand, (ILiteralOperation)rightOperand),
            BinaryOperatorKind.GreaterThanOrEqual when leftOperand.IsNumericLiteral()
                => CanSimplifyToLengthEqualsZeroExpression(rightOperand, (ILiteralOperation)leftOperand),
            _ => false,
        };
    }
 
    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)
    {
        var numericValue = numericLiteralExpression.ConstantValue;
        if (numericValue.HasValue && numericValue.Value is 0)
        {
            if (variableExpression is IPropertyReferenceOperation propertyOperation)
            {
                var property = propertyOperation.Property;
                if (property.Name is nameof(Array.Length) or nameof(Array.LongLength))
                {
                    var containingType = property.ContainingType;
                    if (containingType?.SpecialType == SpecialType.System_Array ||
                        containingType?.SpecialType == SpecialType.System_String)
                    {
                        return true;
                    }
                }
            }
 
            var type = variableExpression.Type;
            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 GetNegationOfConstantPattern(
        SyntaxNode pattern,
        SyntaxGenerator generator,
        SyntaxGeneratorInternal generatorInternal,
        SpecialType? patternValueType)
    {
        var syntaxFacts = generatorInternal.SyntaxFacts;
 
        // If we have `is true/false` and a Boolean value, just swap that to be `is false/true`.
        // If the value isn't a Boolean, swapping to `is false/true` is incorrect since non-Booleans match neither.
        // As an example, `!(new object() is true)` is equivalent to `new object() is not true` but not `new object() is false`.
        if (patternValueType == SpecialType.System_Boolean)
        {
            var expression = syntaxFacts.GetExpressionOfConstantPattern(pattern);
            if (syntaxFacts.IsTrueLiteralExpression(expression))
                return generatorInternal.ConstantPattern(generator.FalseLiteralExpression());
 
            if (syntaxFacts.IsFalseLiteralExpression(expression))
                return generatorInternal.ConstantPattern(generator.TrueLiteralExpression());
        }
 
        // Otherwise, just negate the entire pattern, we don't have anything else special we can do here.
        return generatorInternal.NotPattern(pattern);
    }
 
    private static SyntaxNode GetNegationOfLogicalNotExpression(
        SyntaxNode expression,
        ISyntaxFacts syntaxFacts)
    {
        var operatorToken = syntaxFacts.GetOperatorTokenOfPrefixUnaryExpression(expression);
        var operand = syntaxFacts.GetOperandOfPrefixUnaryExpression(expression);
 
        return operand.WithPrependedLeadingTrivia(operatorToken.LeadingTrivia)
                      .WithAdditionalAnnotations(Simplifier.Annotation);
    }
 
    private static SyntaxNode GetNegationOfUnaryPattern(
        SyntaxNode pattern,
        SyntaxGenerator generator,
        SyntaxGeneratorInternal generatorInternal,
        ISyntaxFacts syntaxFacts)
    {
        // not not p    ->   p
        if (syntaxFacts.IsNotPattern(pattern))
        {
            return GetNegationOfNotPattern(pattern, generator, generatorInternal, syntaxFacts);
        }
 
        // If there are other interesting unary patterns in the future, we can support specialized logic for
        // negating them here.
        return generatorInternal.NotPattern(pattern);
    }
 
    private static SyntaxNode GetNegationOfNotPattern(
        SyntaxNode pattern,
        SyntaxGenerator generator,
        SyntaxGeneratorInternal generatorInternal,
        ISyntaxFacts syntaxFacts)
    {
        Contract.ThrowIfFalse(syntaxFacts.IsNotPattern(pattern));
 
        syntaxFacts.GetPartsOfUnaryPattern(pattern, out var opToken, out var subPattern);
 
        // If we started with `not object`, instead of converting to `object`, directly convert to `not null`
        if (syntaxFacts.SupportsNotPattern(pattern.SyntaxTree.Options) &&
            syntaxFacts.IsTypePattern(subPattern))
        {
            var type = syntaxFacts.GetTypeOfTypePattern(subPattern);
            if (syntaxFacts.IsPredefinedType(type, PredefinedType.Object))
            {
                return generatorInternal.UnaryPattern(opToken,
                    generatorInternal.ConstantPattern(
                        generator.NullLiteralExpression().WithTriviaFrom(type)));
            }
        }
 
        return subPattern.WithPrependedLeadingTrivia(opToken.LeadingTrivia)
                         .WithAdditionalAnnotations(Simplifier.Annotation);
    }
}