File: src\roslyn\src\Analyzers\CSharp\Analyzers\UsePatternMatching\UsePatternMatchingHelpers.cs
Web Access
Project: Microsoft.CodeAnalysis.CSharp.CodeStyle.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle)
// 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.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Microsoft.CodeAnalysis.CSharp.UsePatternMatching;

internal static class UsePatternMatchingHelpers
{
    public static bool TryGetPartsOfAsAndMemberAccessCheck(
        BinaryExpressionSyntax asExpression,
        [NotNullWhen(true)] out ConditionalAccessExpressionSyntax? conditionalAccessExpression,
        out BinaryExpressionSyntax? binaryExpression,
        out IsPatternExpressionSyntax? isPatternExpression,
        out LanguageVersion requiredLanguageVersion)
    {
        conditionalAccessExpression = null;
        binaryExpression = null;
        isPatternExpression = null;
        requiredLanguageVersion = LanguageVersion.CSharp8;

        if (asExpression.Kind() == SyntaxKind.AsExpression)
        {
            // has to be `(expr as T)`
            if (asExpression.Parent is not ParenthesizedExpressionSyntax
                {
                    // Has to be `(expr as T)?...`
                    Parent: ConditionalAccessExpressionSyntax parentConditionalAccess
                })
            {
                return false;
            }

            conditionalAccessExpression = parentConditionalAccess;

            // After the `?` has to be `.X.Y.Z`

            var whenNotNull = parentConditionalAccess.WhenNotNull;
            while (whenNotNull is MemberAccessExpressionSyntax memberAccess)
            {
                // Extended property patterns are only in 10 and up.
                requiredLanguageVersion = LanguageVersion.CSharp10;
                whenNotNull = memberAccess.Expression;
            }

            // Ensure we have `.X`
            if (whenNotNull is not MemberBindingExpressionSyntax)
                return false;

            if (conditionalAccessExpression.Parent is BinaryExpressionSyntax(SyntaxKind.EqualsExpression) parentBinaryExpression1 &&
                parentBinaryExpression1.Left == conditionalAccessExpression)
            {
                // `(expr as T)?... == other_expr
                //
                // Can convert if other_expr is a constant (checked by caller).
                binaryExpression = parentBinaryExpression1;
                return true;
            }
            else if (conditionalAccessExpression.Parent is
                    BinaryExpressionSyntax(
                        SyntaxKind.NotEqualsExpression or
                        SyntaxKind.GreaterThanExpression or
                        SyntaxKind.GreaterThanOrEqualExpression or
                        SyntaxKind.LessThanExpression or
                        SyntaxKind.LessThanOrEqualExpression) parentBinaryExpression2 &&
                parentBinaryExpression2.Left == conditionalAccessExpression)
            {
                // `(expr as T)?... != other_expr
                //
                // Can convert if other_expr is a constant (checked by caller).
                binaryExpression = parentBinaryExpression2;

                // relational patterns need c# 9 or above.
                requiredLanguageVersion = (LanguageVersion)Math.Max((int)requiredLanguageVersion, (int)LanguageVersion.CSharp9);
                return true;
            }
            else if (conditionalAccessExpression.Parent is IsPatternExpressionSyntax parentIsPatternExpression)
            {
                // `(expr as T)?... is pattern`
                //
                //  We can convert this to a pattern in most cases except for:
                //
                // `(expr as T)?... is not X y`.  As it is not legal to transform into `expr is T { Member: not X y }`.
                // Specifically, `not variable` patterns are not legal except at the top level of a pattern.
                if (parentIsPatternExpression.Pattern is UnaryPatternSyntax
                    {
                        Pattern: DeclarationPatternSyntax or VarPatternSyntax or RecursivePatternSyntax { Designation: not null }
                    })
                {
                    return false;
                }

                isPatternExpression = parentIsPatternExpression;
                return true;
            }
        }

        return false;
    }
}