| File: src\roslyn\src\Analyzers\CSharp\Analyzers\RemoveUnnecessaryParentheses\CSharpRemoveUnnecessaryExpressionParenthesesDiagnosticAnalyzer.cs | Web Access |
| Project: Microsoft.CodeAnalysis.CSharp.CodeStyle.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle) |
// 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.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Precedence; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Precedence; using Microsoft.CodeAnalysis.RemoveUnnecessaryParentheses; namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryParentheses; [DiagnosticAnalyzer(LanguageNames.CSharp)] internal sealed class CSharpRemoveUnnecessaryExpressionParenthesesDiagnosticAnalyzer : AbstractRemoveUnnecessaryParenthesesDiagnosticAnalyzer<SyntaxKind, ParenthesizedExpressionSyntax> { protected override SyntaxKind GetSyntaxKind() => SyntaxKind.ParenthesizedExpression; protected override ISyntaxFacts GetSyntaxFacts() => CSharpSyntaxFacts.Instance; protected override bool CanRemoveParentheses( ParenthesizedExpressionSyntax parenthesizedExpression, SemanticModel semanticModel, CancellationToken cancellationToken, out PrecedenceKind precedence, out bool clarifiesPrecedence, out bool innerExpressionHasPrimaryPrecedence) { return CanRemoveParenthesesHelper( parenthesizedExpression, semanticModel, cancellationToken, out precedence, out clarifiesPrecedence, out innerExpressionHasPrimaryPrecedence); } public static bool CanRemoveParenthesesHelper( ParenthesizedExpressionSyntax parenthesizedExpression, SemanticModel semanticModel, CancellationToken cancellationToken, out PrecedenceKind parentPrecedenceKind, out bool clarifiesPrecedence, out bool innerExpressionHasPrimaryPrecedence) { var result = parenthesizedExpression.CanRemoveParentheses(semanticModel, cancellationToken); if (!result) { parentPrecedenceKind = default; clarifiesPrecedence = false; innerExpressionHasPrimaryPrecedence = false; return false; } var inner = parenthesizedExpression.Expression; var innerPrecedence = inner.GetOperatorPrecedence(); var innerIsSimple = innerPrecedence is OperatorPrecedence.Primary or OperatorPrecedence.None; innerExpressionHasPrimaryPrecedence = innerIsSimple; ExpressionSyntax parentExpression; switch (parenthesizedExpression.Parent) { case ConditionalExpressionSyntax: // If our parent is a conditional, then only remove parens if the inner // expression is a primary. i.e. it's ok to remove any of the following: // // (a()) ? (b.length) : (c[0]) // // But we shouldn't remove parens for anything more complex like: // // ++a ? b + c : d << e // parentPrecedenceKind = PrecedenceKind.Other; clarifiesPrecedence = false; return innerIsSimple; case BinaryExpressionSyntax binaryExpression: parentExpression = binaryExpression; break; case IsPatternExpressionSyntax isPatternExpression: // on the left side of an 'x is pat' expression parentExpression = isPatternExpression; break; case ConstantPatternSyntax { Parent: IsPatternExpressionSyntax isPatternExpression }: // on the right side of an 'x is const_pattern' expression parentExpression = isPatternExpression; break; default: parentPrecedenceKind = PrecedenceKind.Other; clarifiesPrecedence = false; return true; } // We're parented by something binary-like. parentPrecedenceKind = CSharpExpressionPrecedenceService.Instance.GetPrecedenceKind(parentExpression); // Precedence is clarified any time we have expression with different precedence // (and the inner expression is not a primary expression). in other words, this // is helps clarify precedence: // // a + (b * c) // // However, this does not: // // a + (b.Length) clarifiesPrecedence = !innerIsSimple && parentExpression.GetOperatorPrecedence() != innerPrecedence; return true; } }