File: IncrementalParsing\BinaryExpression.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Syntax\Microsoft.CodeAnalysis.CSharp.Syntax.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Syntax.UnitTests)
// 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;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.IncrementalParsing
{
    public class BinaryExpressionChanges
    {
        // This test is an exception to the others as * will be assumed to be a pointer type in an
        // expression syntax.  To make this parse correctly, the multiplication must be in a location
        // that is definitely an expression (i.e. an assignment expression)
        [Fact]
        public void PlusToMultiply()
        {
            string text = @"class C{
                       void M() {
                            int x = y + 2;
                            } 
                    }";
            var oldTree = SyntaxFactory.ParseSyntaxTree(text);
            var newTree = oldTree.WithReplaceFirst("+", "*");
            var type = newTree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax;
            var method = type.Members[0] as MethodDeclarationSyntax;
            var block = method.Body;
            var statement = block.Statements[0] as LocalDeclarationStatementSyntax;
            var expression = statement.Declaration.Variables[0].Initializer.Value as BinaryExpressionSyntax;
            Assert.Equal(SyntaxKind.MultiplyExpression, expression.Kind());
        }
 
        [Fact]
        public void PlusToMinus()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.SubtractExpression);
        }
 
        [Fact]
        public void PlusToDivide()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.DivideExpression);
        }
 
        [Fact]
        public void PlusToModulo()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.ModuloExpression);
        }
 
        [Fact]
        public void PlusToLeftShift()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.LeftShiftExpression);
        }
 
        [Fact]
        public void PlusToRightShift()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.RightShiftExpression);
        }
 
        [Fact]
        public void PlusToUnsignedRightShift()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.UnsignedRightShiftExpression);
        }
 
        [Fact]
        public void PlusToLogicalOr()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.LogicalOrExpression);
        }
 
        [Fact]
        public void PlusToLogicalAnd()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.LogicalAndExpression);
        }
 
        [Fact]
        public void PlusToBitwiseAnd()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.BitwiseAndExpression);
        }
 
        [Fact]
        public void PlusToBitwiseOr()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.BitwiseOrExpression);
        }
 
        [Fact]
        public void PlusToExclusiveOr()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.ExclusiveOrExpression);
        }
 
        [Fact]
        public void PlusToEquals()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.EqualsExpression);
        }
 
        [Fact]
        public void PlusToNotEquals()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.NotEqualsExpression);
        }
 
        [Fact]
        public void PlusToLessThan()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.LessThanExpression);
        }
 
        [Fact]
        public void PlusToLessThanEqualTo()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.LessThanOrEqualExpression);
        }
 
        [Fact]
        public void PlusToGreaterThan()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.GreaterThanExpression);
        }
 
        [Fact]
        public void PlusToGreaterThanEqualTo()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.GreaterThanOrEqualExpression);
        }
 
        [Fact]
        public void PlusToAs()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.AsExpression);
        }
 
        [Fact]
        public void PlusToIs()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.IsExpression);
        }
 
        [Fact]
        public void PlusToCoalesce()
        {
            MakeBinOpChange(SyntaxKind.AddExpression, SyntaxKind.CoalesceExpression);
        }
 
        [Fact]
        public void DotToArrow()
        {
            MakeMemberAccessChange(SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.PointerMemberAccessExpression);
        }
 
        [Fact]
        public void ArrowToDot()
        {
            MakeMemberAccessChange(SyntaxKind.PointerMemberAccessExpression, SyntaxKind.SimpleMemberAccessExpression);
        }
 
        #region Helpers
 
        private static void MakeMemberAccessChange(SyntaxKind oldStyle, SyntaxKind newStyle)
        {
            MakeChange(oldStyle, newStyle);
            MakeChange(oldStyle, newStyle, options: TestOptions.Script);
            MakeChange(oldStyle, newStyle, topLevel: true, options: TestOptions.Script);
        }
 
        private static void MakeBinOpChange(SyntaxKind oldStyle, SyntaxKind newStyle)
        {
            MakeChange(oldStyle, newStyle);
            MakeChange(oldStyle, newStyle, options: TestOptions.Script);
            MakeChange(oldStyle, newStyle, topLevel: true, options: TestOptions.Script);
        }
 
        private static void MakeChange(SyntaxKind oldSyntaxKind, SyntaxKind newSyntaxKind, bool topLevel = false, CSharpParseOptions options = null)
        {
            string oldName = GetExpressionString(oldSyntaxKind);
            string newName = GetExpressionString(newSyntaxKind);
 
            string topLevelStatement = "x " + oldName + " y";
            // Be warned when changing the fields here
            var code = @"class C { void m() {
                 " + topLevelStatement + @";
                }}";
            var oldTree = SyntaxFactory.ParseSyntaxTree(topLevel ? topLevelStatement : code, options: options);
 
            // Make the change to the node
            var newTree = oldTree.WithReplaceFirst(oldName, newName);
            var treeNode = topLevel ? GetGlobalExpressionNode(newTree) : GetExpressionNode(newTree);
            Assert.Equal(treeNode.Kind(), newSyntaxKind);
        }
 
        private static ExpressionSyntax GetExpressionNode(SyntaxTree newTree)
        {
            TypeDeclarationSyntax classType = newTree.GetCompilationUnitRoot().Members[0] as TypeDeclarationSyntax;
            MethodDeclarationSyntax method = classType.Members[0] as MethodDeclarationSyntax;
            var block = method.Body;
            var statement = block.Statements[0] as ExpressionStatementSyntax;
            return statement.Expression;
        }
 
        private static ExpressionSyntax GetGlobalExpressionNode(SyntaxTree newTree)
        {
            var statementType = newTree.GetCompilationUnitRoot().Members[0] as GlobalStatementSyntax;
            Assert.True(statementType.AttributeLists.Count == 0);
            Assert.True(statementType.Modifiers.Count == 0);
            var statement = statementType.Statement as ExpressionStatementSyntax;
            return statement.Expression;
        }
 
        private static string GetExpressionString(SyntaxKind oldStyle)
        {
            switch (oldStyle)
            {
                case SyntaxKind.AddExpression:
                    return "+";
                case SyntaxKind.SubtractExpression:
                    return "-";
                case SyntaxKind.MultiplyExpression:
                    return " * ";
                case SyntaxKind.DivideExpression:
                    return "/";
                case SyntaxKind.ModuloExpression:
                    return "%";
                case SyntaxKind.LeftShiftExpression:
                    return "<<";
                case SyntaxKind.RightShiftExpression:
                    return ">>";
                case SyntaxKind.UnsignedRightShiftExpression:
                    return ">>>";
                case SyntaxKind.LogicalOrExpression:
                    return "||";
                case SyntaxKind.LogicalAndExpression:
                    return "&&";
                case SyntaxKind.BitwiseOrExpression:
                    return "|";
                case SyntaxKind.BitwiseAndExpression:
                    return "&";
                case SyntaxKind.ExclusiveOrExpression:
                    return "^";
                case SyntaxKind.EqualsExpression:
                    return "==";
                case SyntaxKind.NotEqualsExpression:
                    return "!=";
                case SyntaxKind.LessThanExpression:
                    return "<";
                case SyntaxKind.LessThanOrEqualExpression:
                    return "<=";
                case SyntaxKind.GreaterThanExpression:
                    return ">";
                case SyntaxKind.GreaterThanOrEqualExpression:
                    return ">=";
                case SyntaxKind.AsExpression:
                    return " as ";
                case SyntaxKind.IsExpression:
                    return " is ";
                case SyntaxKind.CoalesceExpression:
                    return "??";
                case SyntaxKind.SimpleMemberAccessExpression:
                    return ".";
                case SyntaxKind.PointerMemberAccessExpression:
                    return "->";
                default:
                    throw new Exception("unexpected Type given");
            }
        }
        #endregion
    }
}