private static bool RemovalMayIntroduceCastAmbiguity(ParenthesizedExpressionSyntax node)
        {
            // Be careful not to break the special case around (x)(-y)
            // as defined in section 7.7.6 of the C# language specification.

            if (node.IsParentKind(SyntaxKind.CastExpression))
            {
                var castExpression = (CastExpressionSyntax)node.Parent;
                if (castExpression.Type is PredefinedTypeSyntax)
                {
                    return(false);
                }

                var expression = node.Expression;

                if (expression.IsKind(SyntaxKind.UnaryMinusExpression))
                {
                    return(true);
                }

                if (expression.IsKind(SyntaxKind.NumericLiteralExpression))
                {
                    var numericLiteral = (LiteralExpressionSyntax)expression;
                    if (numericLiteral.Token.ValueText.StartsWith("-", StringComparison.Ordinal))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Пример #2
0
        public static bool CanRemoveParentheses(
            this ParenthesizedExpressionSyntax node, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (node.OpenParenToken.IsMissing || node.CloseParenToken.IsMissing)
            {
                // int x = (3;
                return(false);
            }

            var expression = node.Expression;

            // The 'direct' expression that contains this parenthesized node.  Note: in the case
            // of code like: ```x is (y)``` there is an intermediary 'no-syntax' 'ConstantPattern'
            // node between the 'is-pattern' node and the parenthesized expression.  So we manually
            // jump past that as, for all intents and purposes, we want to consider the 'is' expression
            // as the parent expression of the (y) expression.
            var parentExpression = node.IsParentKind(SyntaxKind.ConstantPattern)
                ? node.Parent.Parent as ExpressionSyntax
                : node.Parent as ExpressionSyntax;

            // Have to be careful if we would remove parens and cause a + and a + to become a ++.
            // (same with - as well).
            var tokenBeforeParen = node.GetFirstToken().GetPreviousToken();
            var tokenAfterParen  = node.Expression.GetFirstToken();
            var previousChar     = tokenBeforeParen.Text.LastOrDefault();
            var nextChar         = tokenAfterParen.Text.FirstOrDefault();

            if ((previousChar == '+' && nextChar == '+') ||
                (previousChar == '-' && nextChar == '-'))
            {
                return(false);
            }

            // Simplest cases:
            //   ((x)) -> (x)
            if (expression.IsKind(SyntaxKind.ParenthesizedExpression) ||
                parentExpression.IsKind(SyntaxKind.ParenthesizedExpression))
            {
                return(true);
            }

            if (expression is StackAllocArrayCreationExpressionSyntax or ImplicitStackAllocArrayCreationExpressionSyntax)
            {
                // var span = (stackalloc byte[8]);
                // https://github.com/dotnet/roslyn/issues/44629
                // The code semantics changes if the parenthesis removed.
                // With parenthesis:    variable span is of type `Span<byte>`.
                // Without parenthesis: variable span is of type `byte*` which can only be used in unsafe context.
                if (node.Parent is EqualsValueClauseSyntax {
                    Parent : VariableDeclaratorSyntax {
                        Parent : VariableDeclarationSyntax varDecl
                    }
                })
        private static bool RemovalMayIntroduceCastAmbiguity(ParenthesizedExpressionSyntax node)
        {
            // Be careful not to break the special case around (x)(-y)
            // as defined in section 7.7.6 of the C# language specification.
            //
            // cases we can't remove the parens for are:
            //
            //      (x)(+y)
            //      (x)(-y)
            //      (x)(&y) // unsafe code
            //      (x)(*y) // unsafe code
            //
            // Note: we can remove the parens if the (x) part is unambiguously a type.
            // i.e. if it something like:
            //
            //      (int)(...)
            //      (x[])(...)
            //      (X*)(...)
            //      (X?)(...)
            //      (global::X)(...)

            if (node.IsParentKind(SyntaxKind.CastExpression))
            {
                var castExpression = (CastExpressionSyntax)node.Parent;
                if (castExpression.Type.IsKind(
                        SyntaxKind.PredefinedType,
                        SyntaxKind.ArrayType,
                        SyntaxKind.PointerType,
                        SyntaxKind.NullableType))
                {
                    return(false);
                }

                if (castExpression.Type is NameSyntax name && StartsWithAlias(name))
                {
                    return(false);
                }

                var expression = node.Expression;

                if (expression.IsKind(
                        SyntaxKind.UnaryMinusExpression,
                        SyntaxKind.UnaryPlusExpression,
                        SyntaxKind.PointerIndirectionExpression,
                        SyntaxKind.AddressOfExpression))
                {
                    return(true);
                }
            }

            return(false);
        }
Пример #4
0
        private static bool RemovalMayIntroduceInterpolationAmbiguity(ParenthesizedExpressionSyntax node)
        {
            if (node.IsParentKind(SyntaxKind.Interpolation))
            {
                // Can't remove parentheses in this case:
                //   $"{(true ? == 0 : 1):x"
                var interpolation = (InterpolationSyntax)node.Parent;
                if (node.Expression.IsKind(SyntaxKind.ConditionalExpression) &&
                    interpolation.AlignmentClause == null &&
                    interpolation.FormatClause != null &&
                    !interpolation.FormatClause.ColonToken.IsMissing)
                {
                    return(true);
                }
            }

            return(false);
        }
        public static bool CanRemoveParentheses(this ParenthesizedExpressionSyntax node, SemanticModel semanticModel)
        {
            if (node.OpenParenToken.IsMissing || node.CloseParenToken.IsMissing)
            {
                // int x = (3;
                return(false);
            }

            var expression = node.Expression;

            // The 'direct' expression that contains this parenthesized node.  Note: in the case
            // of code like: ```x is (y)``` there is an intermediary 'no-syntax' 'ConstantPattern'
            // node between the 'is-pattern' node and the parenthesized expression.  So we manually
            // jump past that as, for all intents and purposes, we want to consider the 'is' expression
            // as the parent expression of the (y) expression.
            var parentExpression = node.IsParentKind(SyntaxKind.ConstantPattern)
                ? node.Parent.Parent as ExpressionSyntax
                : node.Parent as ExpressionSyntax;

            // Have to be careful if we would remove parens and cause a + and a + to become a ++.
            // (same with - as well).
            var tokenBeforeParen = node.GetFirstToken().GetPreviousToken();
            var tokenAfterParen  = node.Expression.GetFirstToken();
            var previousChar     = tokenBeforeParen.Text.LastOrDefault();
            var nextChar         = tokenAfterParen.Text.FirstOrDefault();

            if ((previousChar == '+' && nextChar == '+') ||
                (previousChar == '-' && nextChar == '-'))
            {
                return(false);
            }

            // Simplest cases:
            //   ((x)) -> (x)
            if (expression.IsKind(SyntaxKind.ParenthesizedExpression) ||
                parentExpression.IsKind(SyntaxKind.ParenthesizedExpression))
            {
                return(true);
            }

            // (x); -> x;
            if (node.IsParentKind(SyntaxKind.ExpressionStatement))
            {
                return(true);
            }

            // => (x)   ->   => x
            if (node.IsParentKind(SyntaxKind.ArrowExpressionClause))
            {
                return(true);
            }

            // checked((x)) -> checked(x)
            if (node.IsParentKind(SyntaxKind.CheckedExpression) ||
                node.IsParentKind(SyntaxKind.UncheckedExpression))
            {
                return(true);
            }
            // ((x, y)) -> (x, y)
            if (expression.IsKind(SyntaxKind.TupleExpression))
            {
                return(true);
            }

            // int Prop => (x); -> int Prop => x;
            if (node.Parent is ArrowExpressionClauseSyntax arrowExpressionClause && arrowExpressionClause.Expression == node)
            {
                return(true);
            }

            // Don't change (x?.Count).GetValueOrDefault() to x?.Count.GetValueOrDefault()
            if (expression.IsKind(SyntaxKind.ConditionalAccessExpression) && parentExpression is MemberAccessExpressionSyntax)
            {
                return(false);
            }

            // Easy statement-level cases:
            //   var y = (x);           -> var y = x;
            //   var (y, z) = (x);      -> var (y, z) = x;
            //   if ((x))               -> if (x)
            //   return (x);            -> return x;
            //   yield return (x);      -> yield return x;
            //   throw (x);             -> throw x;
            //   switch ((x))           -> switch (x)
            //   while ((x))            -> while (x)
            //   do { } while ((x))     -> do { } while (x)
            //   for(;(x);)             -> for(;x;)
            //   foreach (var y in (x)) -> foreach (var y in x)
            //   lock ((x))             -> lock (x)
            //   using ((x))            -> using (x)
            //   catch when ((x))       -> catch when (x)
            if ((node.IsParentKind(SyntaxKind.EqualsValueClause) && ((EqualsValueClauseSyntax)node.Parent).Value == node) ||
                (node.IsParentKind(SyntaxKind.IfStatement) && ((IfStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.ReturnStatement) && ((ReturnStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.YieldReturnStatement) && ((YieldStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.ThrowStatement) && ((ThrowStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.SwitchStatement) && ((SwitchStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.WhileStatement) && ((WhileStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.DoStatement) && ((DoStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.ForStatement) && ((ForStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.ForEachStatement, SyntaxKind.ForEachVariableStatement) && ((CommonForEachStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.LockStatement) && ((LockStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.UsingStatement) && ((UsingStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.CatchFilterClause) && ((CatchFilterClauseSyntax)node.Parent).FilterExpression == node))
            {
                return(true);
            }

            // Handle expression-level ambiguities
            if (RemovalMayIntroduceCastAmbiguity(node) ||
                RemovalMayIntroduceCommaListAmbiguity(node) ||
                RemovalMayIntroduceInterpolationAmbiguity(node))
            {
                return(false);
            }

            // Cases:
            //   (C)(this) -> (C)this
            if (node.IsParentKind(SyntaxKind.CastExpression) && expression.IsKind(SyntaxKind.ThisExpression))
            {
                return(true);
            }

            // Cases:
            //   y((x)) -> y(x)
            if (node.IsParentKind(SyntaxKind.Argument) && ((ArgumentSyntax)node.Parent).Expression == node)
            {
                return(true);
            }

            // Cases:
            //   $"{(x)}" -> $"{x}"
            if (node.IsParentKind(SyntaxKind.Interpolation))
            {
                return(true);
            }

            // Cases:
            //   ($"{x}") -> $"{x}"
            if (expression.IsKind(SyntaxKind.InterpolatedStringExpression))
            {
                return(true);
            }

            // Cases:
            //   {(x)} -> {x}
            if (node.Parent is InitializerExpressionSyntax)
            {
                // Assignment expressions are not allowed in initializers
                if (expression.IsAnyAssignExpression())
                {
                    return(false);
                }

                return(true);
            }

            // Cases:
            //   new {(x)} -> {x}
            //   new { a = (x)} -> { a = x }
            //   new { a = (x = c)} -> { a = x = c }
            if (node.Parent is AnonymousObjectMemberDeclaratorSyntax anonymousDeclarator)
            {
                // Assignment expressions are not allowed unless member is named
                if (anonymousDeclarator.NameEquals == null && expression.IsAnyAssignExpression())
                {
                    return(false);
                }

                return(true);
            }

            // Cases:
            // where (x + 1 > 14) -> where x + 1 > 14
            if (node.Parent is QueryClauseSyntax)
            {
                return(true);
            }

            // Cases:
            //   (x)   -> x
            //   (x.y) -> x.y
            if (IsSimpleOrDottedName(expression))
            {
                return(true);
            }

            // Cases:
            //   ('')    -> ''
            //   ("")    -> ""
            //   (false) -> false
            //   (true)  -> true
            //   (null)  -> null
            //   (1)     -> 1
            if (expression.IsAnyLiteralExpression())
            {
                return(true);
            }

            // x ?? (throw ...) -> x ?? throw ...
            if (expression.IsKind(SyntaxKind.ThrowExpression) &&
                node.IsParentKind(SyntaxKind.CoalesceExpression) &&
                ((BinaryExpressionSyntax)node.Parent).Right == node)
            {
                return(true);
            }

            // case (x): -> case x:
            if (node.IsParentKind(SyntaxKind.CaseSwitchLabel))
            {
                return(true);
            }

            // case (x) when y: -> case x when y:
            if (node.IsParentKind(SyntaxKind.ConstantPattern) &&
                node.Parent.IsParentKind(SyntaxKind.CasePatternSwitchLabel))
            {
                return(true);
            }

            // case x when (y): -> case x when y:
            if (node.IsParentKind(SyntaxKind.WhenClause))
            {
                return(true);
            }

            // #if (x)   ->   #if x
            if (node.Parent is DirectiveTriviaSyntax)
            {
                return(true);
            }

            // If we have: (X)(++x) or (X)(--x), we don't want to remove the parens. doing so can
            // make the ++/-- now associate with the previous part of the cast expression.
            if (parentExpression.IsKind(SyntaxKind.CastExpression))
            {
                if (expression.IsKind(SyntaxKind.PreIncrementExpression) ||
                    expression.IsKind(SyntaxKind.PreDecrementExpression))
                {
                    return(false);
                }
            }

            // (condition ? ref a : ref b ) = SomeValue, parenthesis can't be removed for when conditional expression appears at left
            // This syntax is only allowed since C# 7.2
            if (expression.IsKind(SyntaxKind.ConditionalExpression) &&
                node.IsLeftSideOfAnyAssignExpression())
            {
                return(false);
            }

            // Operator precedence cases:
            // - If the parent is not an expression, do not remove parentheses
            // - Otherwise, parentheses may be removed if doing so does not change operator associations.
            return(parentExpression != null && !RemovalChangesAssociation(node, parentExpression, semanticModel));
        }
Пример #6
0
        public static bool CanRemoveParentheses(this ParenthesizedExpressionSyntax node, SemanticModel semanticModel)
        {
            var expression       = node.Expression;
            var parentExpression = node.Parent as ExpressionSyntax;

            // Simplest cases:
            //   ((x)) -> (x)
            if (expression.IsKind(SyntaxKind.ParenthesizedExpression) ||
                parentExpression.IsKind(SyntaxKind.ParenthesizedExpression))
            {
                return(true);
            }

            // (x); -> x;
            if (node.IsParentKind(SyntaxKind.ExpressionStatement))
            {
                return(true);
            }

            // Don't change (x?.Count).GetValueOrDefault() to x?.Count.GetValueOrDefault()
            if (expression.IsKind(SyntaxKind.ConditionalAccessExpression) && parentExpression is MemberAccessExpressionSyntax)
            {
                return(false);
            }

            // Easy statement-level cases:
            //   var y = (x);           -> var y = x;
            //   var (y, z) = (x);      -> var (y, z) = x;
            //   if ((x))               -> if (x)
            //   return (x);            -> return x;
            //   yield return (x);      -> yield return x;
            //   throw (x);             -> throw x;
            //   switch ((x))           -> switch (x)
            //   while ((x))            -> while (x)
            //   do { } while ((x))     -> do { } while (x)
            //   for(;(x);)             -> for(;x;)
            //   foreach (var y in (x)) -> foreach (var y in x)
            //   lock ((x))             -> lock (x)
            //   using ((x))            -> using (x)
            //   catch when ((x))       -> catch when (x)
            if ((node.IsParentKind(SyntaxKind.EqualsValueClause) && ((EqualsValueClauseSyntax)node.Parent).Value == node) ||
                (node.IsParentKind(SyntaxKind.IfStatement) && ((IfStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.ReturnStatement) && ((ReturnStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.YieldReturnStatement) && ((YieldStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.ThrowStatement) && ((ThrowStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.SwitchStatement) && ((SwitchStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.WhileStatement) && ((WhileStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.DoStatement) && ((DoStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.ForStatement) && ((ForStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.ForEachStatement, SyntaxKind.ForEachVariableStatement) && ((CommonForEachStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.LockStatement) && ((LockStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.UsingStatement) && ((UsingStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.CatchFilterClause) && ((CatchFilterClauseSyntax)node.Parent).FilterExpression == node))
            {
                return(true);
            }

            // Handle expression-level ambiguities
            if (RemovalMayIntroduceCastAmbiguity(node) ||
                RemovalMayIntroduceCommaListAmbiguity(node) ||
                RemovalMayIntroduceInterpolationAmbiguity(node))
            {
                return(false);
            }

            // Cases:
            //   (C)(this) -> (C)this
            if (node.IsParentKind(SyntaxKind.CastExpression) && expression.IsKind(SyntaxKind.ThisExpression))
            {
                return(true);
            }

            // Cases:
            //   y((x)) -> y(x)
            if (node.IsParentKind(SyntaxKind.Argument) && ((ArgumentSyntax)node.Parent).Expression == node)
            {
                return(true);
            }

            // Cases:
            //   $"{(x)}" -> $"{x}"
            if (node.IsParentKind(SyntaxKind.Interpolation))
            {
                return(true);
            }

            // Cases:
            //   ($"{x}") -> $"{x}"
            if (expression.IsKind(SyntaxKind.InterpolatedStringExpression))
            {
                return(true);
            }

            // Cases:
            //   {(x)} -> {x}
            if (node.Parent is InitializerExpressionSyntax)
            {
                // Assignment expressions are not allowed in initializers
                if (expression.IsAnyAssignExpression())
                {
                    return(false);
                }

                return(true);
            }

            // Cases:
            // where (x + 1 > 14) -> where x + 1 > 14
            if (node.Parent is QueryClauseSyntax)
            {
                return(true);
            }

            // Cases:
            //   (x)   -> x
            //   (x.y) -> x.y
            if (IsSimpleOrDottedName(expression))
            {
                return(true);
            }

            // Cases:
            //   ('')    -> ''
            //   ("")    -> ""
            //   (false) -> false
            //   (true)  -> true
            //   (null)  -> null
            //   (1)     -> 1
            if (expression.IsAnyLiteralExpression())
            {
                return(true);
            }

            // x ?? (throw ...) -> x ?? throw ...
            if (expression.IsKind(SyntaxKind.ThrowExpression) &&
                node.IsParentKind(SyntaxKind.CoalesceExpression) &&
                ((BinaryExpressionSyntax)node.Parent).Right == node)
            {
                return(true);
            }

            // Operator precedence cases:
            // - If the parent is not an expression, do not remove parentheses
            // - Otherwise, parentheses may be removed if doing so does not change operator associations.
            return(parentExpression != null
                ? !RemovalChangesAssociation(node, expression, parentExpression, semanticModel)
                : false);
        }
Пример #7
0
        public static bool CanRemoveParentheses(this ParenthesizedExpressionSyntax node)
        {
            var expression       = node.Expression;
            var parentExpression = node.Parent as ExpressionSyntax;

            // Simplest cases:
            //   ((x)) -> (x)
            if (expression.IsKind(SyntaxKind.ParenthesizedExpression) ||
                parentExpression.IsKind(SyntaxKind.ParenthesizedExpression))
            {
                return(true);
            }

            // Easy statement-level cases:
            //   var y = (x);           -> var y = x;
            //   if ((x))               -> if (x)
            //   return (x);            -> return x;
            //   yield return (x);      -> yield return x;
            //   throw (x);             -> throw x;
            //   switch ((x))           -> switch (x)
            //   while ((x))            -> while (x)
            //   do { } while ((x))     -> do { } while (x)
            //   for(;(x);)             -> for(;x;)
            //   foreach (var y in (x)) -> foreach (var y in x)
            //   lock ((x))             -> lock (x)
            //   using ((x))            -> using (x)
            if ((node.IsParentKind(SyntaxKind.EqualsValueClause) && ((EqualsValueClauseSyntax)node.Parent).Value == node) ||
                (node.IsParentKind(SyntaxKind.IfStatement) && ((IfStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.ReturnStatement) && ((ReturnStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.YieldReturnStatement) && ((YieldStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.ThrowStatement) && ((ThrowStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.SwitchStatement) && ((SwitchStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.WhileStatement) && ((WhileStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.DoStatement) && ((DoStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.ForStatement) && ((ForStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.ForEachStatement) && ((ForEachStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.LockStatement) && ((LockStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.UsingStatement) && ((UsingStatementSyntax)node.Parent).Expression == node))
            {
                return(true);
            }

            // Handle expression-level ambiguities
            if (RemovalMayIntroduceCastAmbiguity(node) ||
                RemovalMayIntroduceCommaListAmbiguity(node))
            {
                return(false);
            }

            // Cases:
            //   y((x)) -> y(x)
            if (node.IsParentKind(SyntaxKind.Argument) && ((ArgumentSyntax)node.Parent).Expression == node)
            {
                return(true);
            }

            // Cases:
            //   {(x)} -> {x}
            if (node.Parent is InitializerExpressionSyntax)
            {
                // Assignment expressions are not allowed in initializers
                if (expression.IsAnyAssignExpression())
                {
                    return(false);
                }

                return(true);
            }

            // Cases:
            // where (x + 1 > 14) -> where x + 1 > 14
            if (node.Parent is QueryClauseSyntax)
            {
                return(true);
            }

            // Cases:
            //   (x)   -> x
            //   (x.y) -> x.y
            if (IsSimpleOrDottedName(expression))
            {
                return(true);
            }

            // Cases:
            //   ('')    -> ''
            //   ("")    -> ""
            //   (false) -> false
            //   (true)  -> true
            //   (null)  -> null
            //   (1)     -> 1
            if (expression.IsAnyLiteralExpression())
            {
                return(true);
            }

            // Operator precedence cases:
            // - If the parent is not an expression, do not remove parentheses
            // - Otherwise, parentheses may be removed if doing so does not change operator associations.
            return(parentExpression == null ? false : !RemovalChangesAssociation(node, expression, parentExpression));
        }
        private static bool RemovalMayIntroduceCastAmbiguity(ParenthesizedExpressionSyntax node)
        {
            // Be careful not to break the special case around (x)(-y)
            // as defined in section 7.7.6 of the C# language specification.

            if (node.IsParentKind(SyntaxKind.CastExpression))
            {
                var castExpression = (CastExpressionSyntax)node.Parent;
                if (castExpression.Type is PredefinedTypeSyntax)
                {
                    return false;
                }

                var expression = node.Expression;

                if (expression.IsKind(SyntaxKind.UnaryMinusExpression))
                {
                    return true;
                }

                if (expression.IsKind(SyntaxKind.NumericLiteralExpression))
                {
                    var numericLiteral = (LiteralExpressionSyntax)expression;
                    if (numericLiteral.Token.ValueText.StartsWith("-"))
                    {
                        return true;
                    }
                }
            }

            return false;
        }
        public static bool CanRemoveParentheses(this ParenthesizedExpressionSyntax node)
        {
            var expression       = node.Expression;
            var parentExpression = node.Parent as ExpressionSyntax;

            // Simplest cases:
            //   ((x)) -> (x)
            if (expression.IsKind(SyntaxKind.ParenthesizedExpression))
            {
                return(true);
            }

            // Easy statement-level cases:
            //   var y = (x);           -> var y = x;
            //   if ((x))               -> if (x)
            //   return (x);            -> return x;
            //   yield return (x);      -> yield return x;
            //   throw (x);             -> throw x;
            //   switch ((x))           -> switch (x)
            //   while ((x))            -> while (x)
            //   do { } while ((x))     -> do { } while (x)
            //   for(;(x);)             -> for(;x;)
            //   foreach (var y in (x)) -> foreach (var y in x)
            //   lock ((x))             -> lock (x)
            //   using ((x))            -> using (x)
            if ((node.IsParentKind(SyntaxKind.EqualsValueClause) && ((EqualsValueClauseSyntax)node.Parent).Value == node) ||
                (node.IsParentKind(SyntaxKind.IfStatement) && ((IfStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.ReturnStatement) && ((ReturnStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.YieldReturnStatement) && ((YieldStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.ThrowStatement) && ((ThrowStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.SwitchStatement) && ((SwitchStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.WhileStatement) && ((WhileStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.DoStatement) && ((DoStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.ForStatement) && ((ForStatementSyntax)node.Parent).Condition == node) ||
                (node.IsParentKind(SyntaxKind.ForEachStatement) && ((ForEachStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.LockStatement) && ((LockStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.UsingStatement) && ((UsingStatementSyntax)node.Parent).Expression == node))
            {
                return(true);
            }

            // Handle expression-level ambiguities
            if (RemovalMayIntroduceCastAmbiguity(node) ||
                RemovalMayIntroduceCommaListAmbiguity(node))
            {
                return(false);
            }

            // Cases:
            //   y((x)) -> y(x)
            if (node.IsParentKind(SyntaxKind.Argument) && ((ArgumentSyntax)node.Parent).Expression == node)
            {
                return(true);
            }

            // Cases:
            //   {(x)} -> {x}
            if (node.Parent is InitializerExpressionSyntax)
            {
                // Assignment expressions are not allowed in initializers
                if (expression.IsAnyAssignExpression())
                {
                    return(false);
                }

                return(true);
            }

            // Cases:
            // where (x + 1 > 14) -> where x + 1 > 14
            if (node.Parent is QueryClauseSyntax)
            {
                return(true);
            }

            // Cases:
            //   (x)   -> x
            //   (x.y) -> x.y
            if (IsSimpleOrDottedName(expression))
            {
                return(true);
            }

            // Cases:
            //   ('')    -> ''
            //   ("")    -> ""
            //   (false) -> false
            //   (true)  -> true
            //   (null)  -> null
            //   (1)     -> 1
            if (expression.IsAnyLiteralExpression())
            {
                return(true);
            }

            // Operator precedence cases:
            var precedence = expression.GetOperatorPrecedence();

            if (parentExpression != null)
            {
                var parentPrecedence = parentExpression.GetOperatorPrecedence();

                // Only remove if the expression's precedence is higher than its parent.
                if (parentPrecedence != OperatorPrecedence.None &&
                    precedence > parentPrecedence)
                {
                    return(true);
                }

                // If the expression's precedence is the same as its parent, and both are binary expressions,
                // check for associativity and commutability.
                if (precedence != OperatorPrecedence.None && precedence == parentPrecedence)
                {
                    var binaryExpression       = expression as BinaryExpressionSyntax;
                    var parentBinaryExpression = parentExpression as BinaryExpressionSyntax;
                    if (binaryExpression == null || parentBinaryExpression == null)
                    {
                        return(true);
                    }

                    // Handle associate cases. Note that all binary expressions except assignment
                    // and null-coalescing are left associative.
                    if (parentBinaryExpression.Left == node)
                    {
                        if (!parentBinaryExpression.IsAnyAssignExpression() &&
                            !parentBinaryExpression.IsKind(SyntaxKind.CoalesceExpression))
                        {
                            return(true);
                        }
                    }
                    else if (parentBinaryExpression.Right == node)
                    {
                        if (parentBinaryExpression.IsAnyAssignExpression() ||
                            parentBinaryExpression.IsKind(SyntaxKind.CoalesceExpression))
                        {
                            return(true);
                        }
                    }

                    // If both the expression and it's parent are binary expressions and their kinds
                    // are the same, check to see if they are commutative (e.g. + or *).
                    if (parentBinaryExpression.MatchesKind(SyntaxKind.AddExpression, SyntaxKind.MultiplyExpression) &&
                        expression.CSharpKind() == parentExpression.CSharpKind())
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
        public static bool CanRemoveParentheses(
            this ParenthesizedExpressionSyntax node, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (node.OpenParenToken.IsMissing || node.CloseParenToken.IsMissing)
            {
                // int x = (3;
                return(false);
            }

            var expression = node.Expression;

            // The 'direct' expression that contains this parenthesized node.  Note: in the case
            // of code like: ```x is (y)``` there is an intermediary 'no-syntax' 'ConstantPattern'
            // node between the 'is-pattern' node and the parenthesized expression.  So we manually
            // jump past that as, for all intents and purposes, we want to consider the 'is' expression
            // as the parent expression of the (y) expression.
            var parentExpression = node.IsParentKind(SyntaxKind.ConstantPattern)
                ? node.Parent.Parent as ExpressionSyntax
                : node.Parent as ExpressionSyntax;

            // Have to be careful if we would remove parens and cause a + and a + to become a ++.
            // (same with - as well).
            var tokenBeforeParen = node.GetFirstToken().GetPreviousToken();
            var tokenAfterParen  = node.Expression.GetFirstToken();
            var previousChar     = tokenBeforeParen.Text.LastOrDefault();
            var nextChar         = tokenAfterParen.Text.FirstOrDefault();

            if ((previousChar == '+' && nextChar == '+') ||
                (previousChar == '-' && nextChar == '-'))
            {
                return(false);
            }

            // Simplest cases:
            //   ((x)) -> (x)
            if (expression.IsKind(SyntaxKind.ParenthesizedExpression) ||
                parentExpression.IsKind(SyntaxKind.ParenthesizedExpression))
            {
                return(true);
            }

            if (expression is StackAllocArrayCreationExpressionSyntax ||
                expression is ImplicitStackAllocArrayCreationExpressionSyntax)
            {
                // var span = (stackalloc byte[8]);
                // https://github.com/dotnet/roslyn/issues/44629
                // The code semantics changes if the parenthesis removed.
                // With parenthesis:    variable span is of type `Span<byte>`.
                // Without parenthesis: variable span is of type `byte*` which can only be used in unsafe context.
                return(false);
            }

            // (throw ...) -> throw ...
            if (expression.IsKind(SyntaxKind.ThrowExpression))
            {
                return(true);
            }

            // (x); -> x;
            if (node.IsParentKind(SyntaxKind.ExpressionStatement))
            {
                return(true);
            }

            // => (x)   ->   => x
            if (node.IsParentKind(SyntaxKind.ArrowExpressionClause))
            {
                return(true);
            }

            // checked((x)) -> checked(x)
            if (node.IsParentKind(SyntaxKind.CheckedExpression) ||
                node.IsParentKind(SyntaxKind.UncheckedExpression))
            {
                return(true);
            }
            // ((x, y)) -> (x, y)
            if (expression.IsKind(SyntaxKind.TupleExpression))
            {
                return(true);
            }

            // int Prop => (x); -> int Prop => x;
            if (node.Parent is ArrowExpressionClauseSyntax arrowExpressionClause && arrowExpressionClause.Expression == node)
            {
                return(true);
            }

            // Easy statement-level cases:
            //   var y = (x);           -> var y = x;
            //   var (y, z) = (x);      -> var (y, z) = x;
            //   if ((x))               -> if (x)
            //   return (x);            -> return x;
            //   yield return (x);      -> yield return x;
            //   throw (x);             -> throw x;
            //   switch ((x))           -> switch (x)
            //   while ((x))            -> while (x)
            //   do { } while ((x))     -> do { } while (x)
            //   for(;(x);)             -> for(;x;)
            //   foreach (var y in (x)) -> foreach (var y in x)
            //   lock ((x))             -> lock (x)
            //   using ((x))            -> using (x)
            //   catch when ((x))       -> catch when (x)
            if ((node.IsParentKind(SyntaxKind.EqualsValueClause, out EqualsValueClauseSyntax equalsValue) && equalsValue.Value == node) ||
                (node.IsParentKind(SyntaxKind.IfStatement, out IfStatementSyntax ifStatement) && ifStatement.Condition == node) ||
                (node.IsParentKind(SyntaxKind.ReturnStatement, out ReturnStatementSyntax returnStatement) && returnStatement.Expression == node) ||
                (node.IsParentKind(SyntaxKind.YieldReturnStatement, out YieldStatementSyntax yieldStatement) && yieldStatement.Expression == node) ||
                (node.IsParentKind(SyntaxKind.ThrowStatement, out ThrowStatementSyntax throwStatement) && throwStatement.Expression == node) ||
                (node.IsParentKind(SyntaxKind.SwitchStatement, out SwitchStatementSyntax switchStatement) && switchStatement.Expression == node) ||
                (node.IsParentKind(SyntaxKind.WhileStatement, out WhileStatementSyntax whileStatement) && whileStatement.Condition == node) ||
                (node.IsParentKind(SyntaxKind.DoStatement, out DoStatementSyntax doStatement) && doStatement.Condition == node) ||
                (node.IsParentKind(SyntaxKind.ForStatement, out ForStatementSyntax forStatement) && forStatement.Condition == node) ||
                (node.IsParentKind(SyntaxKind.ForEachStatement, SyntaxKind.ForEachVariableStatement) && ((CommonForEachStatementSyntax)node.Parent).Expression == node) ||
                (node.IsParentKind(SyntaxKind.LockStatement, out LockStatementSyntax lockStatement) && lockStatement.Expression == node) ||
                (node.IsParentKind(SyntaxKind.UsingStatement, out UsingStatementSyntax usingStatement) && usingStatement.Expression == node) ||
                (node.IsParentKind(SyntaxKind.CatchFilterClause, out CatchFilterClauseSyntax catchFilter) && catchFilter.FilterExpression == node))
            {
                return(true);
            }

            // Handle expression-level ambiguities
            if (RemovalMayIntroduceCastAmbiguity(node) ||
                RemovalMayIntroduceCommaListAmbiguity(node) ||
                RemovalMayIntroduceInterpolationAmbiguity(node) ||
                RemovalWouldChangeConstantReferenceToTypeReference(node, expression, semanticModel, cancellationToken))
            {
                return(false);
            }

            // Cases:
            //   (C)(this) -> (C)this
            if (node.IsParentKind(SyntaxKind.CastExpression) && expression.IsKind(SyntaxKind.ThisExpression))
            {
                return(true);
            }

            // Cases:
            //   y((x)) -> y(x)
            if (node.IsParentKind(SyntaxKind.Argument, out ArgumentSyntax argument) && argument.Expression == node)
            {
                return(true);
            }

            // Cases:
            //   $"{(x)}" -> $"{x}"
            if (node.IsParentKind(SyntaxKind.Interpolation))
            {
                return(true);
            }

            // Cases:
            //   ($"{x}") -> $"{x}"
            if (expression.IsKind(SyntaxKind.InterpolatedStringExpression))
            {
                return(true);
            }

            // Cases:
            //   {(x)} -> {x}
            if (node.Parent is InitializerExpressionSyntax)
            {
                // Assignment expressions are not allowed in initializers
                if (expression.IsAnyAssignExpression())
                {
                    return(false);
                }

                return(true);
            }

            // Cases:
            //   new {(x)} -> {x}
            //   new { a = (x)} -> { a = x }
            //   new { a = (x = c)} -> { a = x = c }
            if (node.Parent is AnonymousObjectMemberDeclaratorSyntax anonymousDeclarator)
            {
                // Assignment expressions are not allowed unless member is named
                if (anonymousDeclarator.NameEquals == null && expression.IsAnyAssignExpression())
                {
                    return(false);
                }

                return(true);
            }

            // Cases:
            // where (x + 1 > 14) -> where x + 1 > 14
            if (node.Parent is QueryClauseSyntax)
            {
                return(true);
            }

            // Cases:
            //   (x)   -> x
            //   (x.y) -> x.y
            if (IsSimpleOrDottedName(expression))
            {
                return(true);
            }

            // Cases:
            //   ('')      -> ''
            //   ("")      -> ""
            //   (false)   -> false
            //   (true)    -> true
            //   (null)    -> null
            //   (default) -> default;
            //   (1)       -> 1
            if (expression.IsAnyLiteralExpression())
            {
                return(true);
            }

            // (this)   -> this
            if (expression.IsKind(SyntaxKind.ThisExpression))
            {
                return(true);
            }

            // x ?? (throw ...) -> x ?? throw ...
            if (expression.IsKind(SyntaxKind.ThrowExpression) &&
                node.IsParentKind(SyntaxKind.CoalesceExpression, out BinaryExpressionSyntax binary) &&
                binary.Right == node)
            {
                return(true);
            }

            // case (x): -> case x:
            if (node.IsParentKind(SyntaxKind.CaseSwitchLabel))
            {
                return(true);
            }

            // case (x) when y: -> case x when y:
            if (node.IsParentKind(SyntaxKind.ConstantPattern) &&
                node.Parent.IsParentKind(SyntaxKind.CasePatternSwitchLabel))
            {
                return(true);
            }

            // case x when (y): -> case x when y:
            if (node.IsParentKind(SyntaxKind.WhenClause))
            {
                return(true);
            }

            // #if (x)   ->   #if x
            if (node.Parent is DirectiveTriviaSyntax)
            {
                return(true);
            }

            // Switch expression arm
            // x => (y)
            if (node.Parent is SwitchExpressionArmSyntax arm && arm.Expression == node)
            {
                return(true);
            }

            // If we have: (X)(++x) or (X)(--x), we don't want to remove the parens. doing so can
            // make the ++/-- now associate with the previous part of the cast expression.
            if (parentExpression.IsKind(SyntaxKind.CastExpression))
            {
                if (expression.IsKind(SyntaxKind.PreIncrementExpression) ||
                    expression.IsKind(SyntaxKind.PreDecrementExpression))
                {
                    return(false);
                }
            }

            // (condition ? ref a : ref b ) = SomeValue, parenthesis can't be removed for when conditional expression appears at left
            // This syntax is only allowed since C# 7.2
            if (expression.IsKind(SyntaxKind.ConditionalExpression) &&
                node.IsLeftSideOfAnyAssignExpression())
            {
                return(false);
            }

            // Don't change (x?.Count)... to x?.Count...
            //
            // It very much changes the semantics to have code that always executed (outside the
            // parenthesized expression) now only conditionally run depending on if 'x' is null or
            // not.
            if (expression.IsKind(SyntaxKind.ConditionalAccessExpression))
            {
                return(false);
            }

            // Operator precedence cases:
            // - If the parent is not an expression, do not remove parentheses
            // - Otherwise, parentheses may be removed if doing so does not change operator associations.
            return(parentExpression != null && !RemovalChangesAssociation(node, parentExpression, semanticModel));
        }