File: Simplification\Reducers\CSharpCastReducer.Rewriter.cs
Web Access
Project: src\src\Workspaces\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Workspaces.csproj (Microsoft.CodeAnalysis.CSharp.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.
 
#nullable disable
 
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Simplification;
 
namespace Microsoft.CodeAnalysis.CSharp.Simplification;
 
internal partial class CSharpCastReducer
{
    private class Rewriter : AbstractReductionRewriter
    {
        public Rewriter(ObjectPool<IReductionRewriter> pool)
            : base(pool)
        {
        }
 
        public override SyntaxNode VisitCastExpression(CastExpressionSyntax node)
        {
            return SimplifyNode(
                node,
                newNode: base.VisitCastExpression(node),
                simplifier: s_simplifyCast);
        }
 
        public override SyntaxNode VisitBinaryExpression(BinaryExpressionSyntax node)
        {
            var result = base.VisitBinaryExpression(node);
            var reducedNode = result as BinaryExpressionSyntax;
            if (reducedNode != node && reducedNode != null)
            {
                if ((node.Left.IsKind(SyntaxKind.CastExpression) && !reducedNode.Left.IsKind(SyntaxKind.CastExpression)) ||
                    (node.Right.IsKind(SyntaxKind.CastExpression) && !reducedNode.Right.IsKind(SyntaxKind.CastExpression)))
                {
                    // Cast simplification inside a binary expression, check if we need to parenthesize the binary expression to avoid breaking parent syntax.
                    // For example, cast removal in below case leads to syntax errors in error free code, unless parenting binary expression is parenthesized:
                    //   Original:                  Goo(x < (int)i, x > y)
                    //   Incorrect cast removal:    Goo(x < i, x > y)
                    //   Correct cast removal:      Goo((x < i), x > y)
 
                    // We'll do the following to detect such cases:
                    // 1) Get the topmostExpressionAncestor of node.
                    // 2) Get the reducedAncestor after replacing node with reducedNode within it.
                    // 3) Reparse the reducedAncestor to get reparsedAncestor.
                    // 4) Check for syntax equivalence of reducedAncestor and reparsedAncestor. If not syntactically equivalent,
                    //    then cast removal breaks the syntax and needs explicit parentheses around the binary expression.
 
                    var topmostExpressionAncestor = node
                        .AncestorsAndSelf()
                        .OfType<ExpressionSyntax>()
                        .LastOrDefault();
 
                    if (topmostExpressionAncestor != null && topmostExpressionAncestor != node)
                    {
                        var reducedAncestor = topmostExpressionAncestor.ReplaceNode(node, reducedNode);
                        var reparsedAncestor = SyntaxFactory.ParseExpression(reducedAncestor.ToFullString());
                        if (reparsedAncestor != null && !reparsedAncestor.IsEquivalentTo(reducedAncestor))
                        {
                            return SyntaxFactory.ParenthesizedExpression(reducedNode)
                                .WithAdditionalAnnotations(Simplifier.Annotation);
                        }
                    }
                }
            }
 
            return result;
        }
    }
}