File: src\roslyn\src\Analyzers\CSharp\Analyzers\AddRequiredParentheses\CSharpAddRequiredExpressionParenthesesDiagnosticAnalyzer.cs
Web Access
Project: src\roslyn\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.Features)
// 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.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.AddRequiredParentheses;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Precedence;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Microsoft.CodeAnalysis.CSharp.AddRequiredParentheses;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpAddRequiredExpressionParenthesesDiagnosticAnalyzer() :
    AbstractAddRequiredParenthesesDiagnosticAnalyzer<
        ExpressionSyntax, ExpressionSyntax, SyntaxKind>(CSharpExpressionPrecedenceService.Instance)
{
    private static readonly ImmutableArray<SyntaxKind> s_kinds =
    [
        SyntaxKind.AddExpression,
        SyntaxKind.SubtractExpression,
        SyntaxKind.MultiplyExpression,
        SyntaxKind.DivideExpression,
        SyntaxKind.ModuloExpression,
        SyntaxKind.LeftShiftExpression,
        SyntaxKind.RightShiftExpression,
        SyntaxKind.LogicalOrExpression,
        SyntaxKind.LogicalAndExpression,
        SyntaxKind.BitwiseOrExpression,
        SyntaxKind.BitwiseAndExpression,
        SyntaxKind.ExclusiveOrExpression,
        SyntaxKind.EqualsExpression,
        SyntaxKind.NotEqualsExpression,
        SyntaxKind.LessThanExpression,
        SyntaxKind.LessThanOrEqualExpression,
        SyntaxKind.GreaterThanExpression,
        SyntaxKind.GreaterThanOrEqualExpression,
        SyntaxKind.IsExpression,
        SyntaxKind.AsExpression,
        SyntaxKind.CoalesceExpression,
        SyntaxKind.IsPatternExpression,
    ];

    protected override ImmutableArray<SyntaxKind> GetSyntaxNodeKinds()
        => s_kinds;

    protected override int GetPrecedence(ExpressionSyntax binaryLike)
        => (int)binaryLike.GetOperatorPrecedence();

    protected override bool IsBinaryLike(ExpressionSyntax node)
        => node is BinaryExpressionSyntax ||
           // Support `x is const` pattern as binary-like for precedence purposes.
           node is IsPatternExpressionSyntax { Pattern: ConstantPatternSyntax } ||
           // Support `x is not const` pattern as binary-like for precedence purposes.
           node is IsPatternExpressionSyntax { Pattern: UnaryPatternSyntax { Pattern: ConstantPatternSyntax } };

    protected override (ExpressionSyntax, SyntaxToken, ExpressionSyntax) GetPartsOfBinaryLike(ExpressionSyntax binaryLike)
    {
        Debug.Assert(IsBinaryLike(binaryLike));
        return binaryLike switch
        {
            BinaryExpressionSyntax binaryExpression => (binaryExpression.Left, binaryExpression.OperatorToken, binaryExpression.Right),
            IsPatternExpressionSyntax { Pattern: ConstantPatternSyntax constantPattern } isPatternExpression => (isPatternExpression.Expression, isPatternExpression.IsKeyword, constantPattern.Expression),
            IsPatternExpressionSyntax { Pattern: UnaryPatternSyntax { Pattern: ConstantPatternSyntax constantPattern } } isPatternExpression => (isPatternExpression.Expression, isPatternExpression.IsKeyword, constantPattern.Expression),
            _ => throw ExceptionUtilities.UnexpectedValue(binaryLike),
        };
    }

    protected override ExpressionSyntax? TryGetAppropriateParent(ExpressionSyntax binaryLike)
        => binaryLike.Parent switch
        {
            ExpressionSyntax expression => expression,
            ConstantPatternSyntax { Parent: ExpressionSyntax expression } => expression,
            ConstantPatternSyntax { Parent: UnaryPatternSyntax { Parent: ExpressionSyntax expression } } => expression,
            _ => null,
        };

    protected override bool IsAsExpression(ExpressionSyntax node)
        => node.Kind() == SyntaxKind.AsExpression;
}