private static void AnalyzeBitwiseAndExpression(SyntaxNodeAnalysisContext context) { var bitwiseAnd = (BinaryExpressionSyntax)context.Node; ExpressionSyntax expression = bitwiseAnd.WalkUpParentheses(); if (!expression.IsParentKind(SyntaxKind.EqualsExpression, SyntaxKind.NotEqualsExpression)) { return; } var equalsOrNotEquals = (BinaryExpressionSyntax)expression.Parent; ExpressionSyntax otherExpression = (ReferenceEquals(equalsOrNotEquals.Left, expression)) ? equalsOrNotEquals.Right : equalsOrNotEquals.Left; otherExpression = otherExpression.WalkDownParentheses(); ExpressionSyntax right = bitwiseAnd.Right.WalkDownParentheses(); SemanticModel semanticModel = context.SemanticModel; CancellationToken cancellationToken = context.CancellationToken; if (otherExpression.IsNumericLiteralExpression("0")) { if (SyntaxUtility.IsCompositeEnumValue(right, semanticModel, cancellationToken)) { return; } } else if (!CSharpFactory.AreEquivalent(right, otherExpression)) { return; } IMethodSymbol methodSymbol = semanticModel.GetMethodSymbol(bitwiseAnd, cancellationToken); if (methodSymbol?.MethodKind != MethodKind.BuiltinOperator || methodSymbol.Name != WellKnownMemberNames.BitwiseAndOperatorName || methodSymbol.ContainingType?.TypeKind != TypeKind.Enum) { return; } ExpressionSyntax left = bitwiseAnd.Left.WalkDownParentheses(); if (!IsSuitableAsExpressionOfHasFlag(left)) { return; } if (!IsSuitableAsArgumentOfHasFlag(right)) { return; } DiagnosticHelpers.ReportDiagnostic( context, DiagnosticRules.ReportOnly.ConvertBitwiseOperationToHasFlagCall, equalsOrNotEquals); bool IsSuitableAsExpressionOfHasFlag(ExpressionSyntax expression) { if (expression.IsKind( SyntaxKind.IdentifierName, SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.InvocationExpression, SyntaxKind.ElementAccessExpression)) { return(semanticModel.GetTypeSymbol(expression, cancellationToken)?.TypeKind == TypeKind.Enum); } return(false); } bool IsSuitableAsArgumentOfHasFlag(ExpressionSyntax expression) { expression = expression.WalkDownParentheses(); if (expression.IsKind( SyntaxKind.BitwiseAndExpression, SyntaxKind.BitwiseOrExpression, SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.IdentifierName)) { return(semanticModel.GetTypeSymbol(expression, cancellationToken)?.TypeKind == TypeKind.Enum); } return(false); } }
public static Task <Document> RefactorAsync( Document document, InvocationExpressionSyntax invocation, SemanticModel semanticModel, CancellationToken cancellationToken = default) { ExpressionSyntax expression = invocation.ArgumentList.Arguments[0].Expression; bool isComposite = SyntaxUtility.IsCompositeEnumValue(expression, semanticModel, cancellationToken); ParenthesizedExpressionSyntax parenthesizedExpression = ParenthesizedExpression( BitwiseAndExpression( ((MemberAccessExpressionSyntax)invocation.Expression).Expression.Parenthesize(), expression.Parenthesize()) .Parenthesize()); SyntaxKind binaryExpressionKind = (isComposite) ? SyntaxKind.EqualsExpression : SyntaxKind.NotEqualsExpression; SyntaxNode nodeToReplace = invocation; SyntaxNode parent = invocation.Parent; if (!parent.SpanContainsDirectives()) { switch (parent.Kind()) { case SyntaxKind.LogicalNotExpression: { binaryExpressionKind = (isComposite) ? SyntaxKind.NotEqualsExpression : SyntaxKind.EqualsExpression; nodeToReplace = parent; break; } case SyntaxKind.EqualsExpression: { switch (((BinaryExpressionSyntax)parent).Right?.Kind()) { case SyntaxKind.TrueLiteralExpression: { binaryExpressionKind = (isComposite) ? SyntaxKind.EqualsExpression : SyntaxKind.NotEqualsExpression; nodeToReplace = parent; break; } case SyntaxKind.FalseLiteralExpression: { binaryExpressionKind = (isComposite) ? SyntaxKind.NotEqualsExpression : SyntaxKind.EqualsExpression; nodeToReplace = parent; break; } } break; } } } ExpressionSyntax right; if (isComposite) { right = expression.Parenthesize(); } else { right = NumericLiteralExpression(0); } ParenthesizedExpressionSyntax newNode = BinaryExpression(binaryExpressionKind, parenthesizedExpression, right).WithTriviaFrom(nodeToReplace) .Parenthesize() .WithFormatterAnnotation(); return(document.ReplaceNodeAsync(nodeToReplace, newNode, cancellationToken)); }