|
// 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.Collections.Immutable;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Operations;
namespace Microsoft.CodeAnalysis.RemoveRedundantEquality;
internal abstract class AbstractRemoveRedundantEqualityDiagnosticAnalyzer(ISyntaxFacts syntaxFacts)
: AbstractBuiltInCodeStyleDiagnosticAnalyzer(
IDEDiagnosticIds.RemoveRedundantEqualityDiagnosticId,
EnforceOnBuildValues.RemoveRedundantEquality,
option: null,
new LocalizableResourceString(nameof(AnalyzersResources.Remove_redundant_equality), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)))
{
public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
protected override void InitializeWorker(AnalysisContext context)
{
context.RegisterOperationAction(AnalyzeBinaryOperator, OperationKind.BinaryOperator);
context.RegisterOperationAction(AnalyzeIsPatternOperator, OperationKind.IsPattern);
}
private void AnalyzeIsPatternOperator(OperationAnalysisContext context)
{
if (ShouldSkipAnalysis(context, notification: null))
return;
var syntax = context.Operation.Syntax;
if (!syntaxFacts.IsIsPatternExpression(syntax))
return;
var operation = (IIsPatternOperation)context.Operation;
if (operation.Pattern is not IConstantPatternOperation { Value.ConstantValue.Value: true or false } constantPattern)
return;
syntaxFacts.GetPartsOfIsPatternExpression(syntax, out _, out var isToken, out _);
AnalyzeOperator(
context,
leftOperand: operation.Value,
rightOperand: constantPattern.Value,
isOperatorEquals: true,
isToken);
}
private void AnalyzeBinaryOperator(OperationAnalysisContext context)
{
if (ShouldSkipAnalysis(context, notification: null))
return;
var syntax = context.Operation.Syntax;
if (!syntaxFacts.IsBinaryExpression(syntax))
return;
// We shouldn't report diagnostic on overloaded operator as the behavior can change.
var operation = (IBinaryOperation)context.Operation;
if (operation.OperatorMethod is not null)
return;
if (operation.OperatorKind is not (BinaryOperatorKind.Equals or BinaryOperatorKind.NotEquals))
return;
var isOperatorEquals = operation.OperatorKind == BinaryOperatorKind.Equals;
syntaxFacts.GetPartsOfBinaryExpression(syntax, out _, out var operatorToken, out _);
AnalyzeOperator(
context,
operation.LeftOperand,
operation.RightOperand,
isOperatorEquals,
operatorToken);
}
private void AnalyzeOperator(
OperationAnalysisContext context,
IOperation leftOperand,
IOperation rightOperand,
bool isOperatorEquals,
SyntaxToken operatorToken)
{
var leftType = leftOperand.Type;
var rightType = rightOperand.Type;
if (leftType?.SpecialType != SpecialType.System_Boolean ||
rightType?.SpecialType != SpecialType.System_Boolean)
{
return;
}
var properties = ImmutableDictionary.CreateBuilder<string, string?>();
if (TryGetLiteralValue(rightOperand) is bool rightBool)
{
properties.Add(RedundantEqualityConstants.RedundantSide, RedundantEqualityConstants.Right);
if (rightBool != isOperatorEquals)
properties.Add(RedundantEqualityConstants.Negate, RedundantEqualityConstants.Negate);
}
else if (TryGetLiteralValue(leftOperand) is bool leftBool)
{
properties.Add(RedundantEqualityConstants.RedundantSide, RedundantEqualityConstants.Left);
if (leftBool != isOperatorEquals)
properties.Add(RedundantEqualityConstants.Negate, RedundantEqualityConstants.Negate);
}
else
{
return;
}
context.ReportDiagnostic(Diagnostic.Create(Descriptor,
operatorToken.GetLocation(),
additionalLocations: [operatorToken.GetLocation()],
properties: properties.ToImmutable()));
return;
static bool? TryGetLiteralValue(IOperation? operand)
{
// Make sure we only simplify literals to avoid changing
// something like the following example:
// const bool Activated = true; ... if (state == Activated)
if (operand is
{
Kind: OperationKind.Literal,
ConstantValue: { HasValue: true, Value: bool constValue }
})
{
return constValue;
}
return null;
}
}
}
|