private static bool RemovalChangesAssociation( ParenthesizedExpressionSyntax node, ExpressionSyntax parentExpression, SemanticModel semanticModel) { var expression = node.Expression; var precedence = expression.GetOperatorPrecedence(); var parentPrecedence = parentExpression.GetOperatorPrecedence(); if (precedence == OperatorPrecedence.None || parentPrecedence == OperatorPrecedence.None) { // Be conservative if the expression or its parent has no precedence. return(true); } if (precedence > parentPrecedence) { // Association never changes if the expression's precedence is higher than its parent. return(false); } else if (precedence < parentPrecedence) { // Association always changes if the expression's precedence is lower that its parent. return(true); } else if (precedence == parentPrecedence) { // If the expression's precedence is the same as its parent, and both are binary expressions, // check for associativity and commutability. if (!(expression is BinaryExpressionSyntax || expression is AssignmentExpressionSyntax)) { // If the expression is not a binary expression, association never changes. return(false); } if (parentExpression is BinaryExpressionSyntax parentBinaryExpression) { // If both the expression and its parent are binary expressions and their kinds // are the same, and the parenthesized expression is on hte right and the // operation is associative, it can sometimes be safe to remove these parens. // // i.e. if you have "a && (b && c)" it can be converted to "a && b && c" // as that new interpretation "(a && b) && c" operates the exact same way at // runtime. // // Specifically: // 1) the operands are still executed in the same order: a, b, then c. // So even if they have side effects, it will not matter. // 2) the same shortcircuiting happens. // 3) for logical operators the result will always be the same (there are // additional conditions that are checked for non-logical operators). if (IsAssociative(parentBinaryExpression.Kind()) && node.Expression.Kind() == parentBinaryExpression.Kind() && parentBinaryExpression.Right == node) { return(!node.IsSafeToChangeAssociativity( node.Expression, parentBinaryExpression.Left, parentBinaryExpression.Right, semanticModel)); } // Null-coalescing is right associative; removing parens from the LHS changes the association. if (parentExpression.IsKind(SyntaxKind.CoalesceExpression)) { return(parentBinaryExpression.Left == node); } // All other binary operators are left associative; removing parens from the RHS changes the association. return(parentBinaryExpression.Right == node); } if (parentExpression is AssignmentExpressionSyntax parentAssignmentExpression) { // Assignment expressions are right associative; removing parens from the LHS changes the association. return(parentAssignmentExpression.Left == node); } // If the parent is not a binary expression, association never changes. return(false); } throw ExceptionUtilities.Unreachable; }