Skip to content

Commit

Permalink
Fix S3875 FP: Do not raise an issue when implementing IEqualityOperat…
Browse files Browse the repository at this point in the history
…ors interface (#6316)
  • Loading branch information
gregory-paidis-sonarsource committed Nov 14, 2022
1 parent ff36ffb commit e84fc45
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,65 +29,41 @@
namespace SonarAnalyzer.Rules.CSharp
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class DotNotOverloadOperatorEqual : SonarDiagnosticAnalyzer
public sealed class DoNotOverloadOperatorEqual : SonarDiagnosticAnalyzer
{
internal const string DiagnosticId = "S3875";
private const string DiagnosticId = "S3875";
private const string MessageFormat = "Remove this overload of 'operator =='.";

private static readonly ImmutableArray<KnownType> InterfacesRelyingOnOperatorEqualOverload =
ImmutableArray.Create(
KnownType.System_IComparable,
KnownType.System_IComparable_T,
KnownType.System_IEquatable_T
);
KnownType.System_IEquatable_T,
KnownType.System_Numerics_IEqualityOperators_TSelf_TOther_TResult);

private static readonly DiagnosticDescriptor rule =
private static readonly DiagnosticDescriptor Rule =
DescriptorFactory.Create(DiagnosticId, MessageFormat);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(rule);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);

protected override void Initialize(SonarAnalysisContext context)
{
protected override void Initialize(SonarAnalysisContext context) =>
context.RegisterSyntaxNodeActionInNonGenerated(CheckForIssue, SyntaxKind.OperatorDeclaration);
}

private void CheckForIssue(SyntaxNodeAnalysisContext analysisContext)
private static void CheckForIssue(SyntaxNodeAnalysisContext analysisContext)
{
var declaration = (OperatorDeclarationSyntax)analysisContext.Node;

if (!declaration.OperatorToken.IsKind(SyntaxKind.EqualsEqualsToken))
{
return;
}

if (declaration.Parent is StructDeclarationSyntax)
{
return;
}

if (!(declaration.Parent is ClassDeclarationSyntax classDeclaration))
if (declaration.OperatorToken.IsKind(SyntaxKind.EqualsEqualsToken)
&& declaration.Parent is ClassDeclarationSyntax classDeclaration
&& !classDeclaration.ChildNodes()
.OfType<OperatorDeclarationSyntax>()
.Any(op => op.OperatorToken.IsKind(SyntaxKind.PlusToken)
|| op.OperatorToken.IsKind(SyntaxKind.MinusToken))
&& analysisContext.SemanticModel.GetDeclaredSymbol(classDeclaration) is { } namedTypeSymbol
&& !namedTypeSymbol.ImplementsAny(InterfacesRelyingOnOperatorEqualOverload))
{
return;
analysisContext.ReportIssue(Diagnostic.Create(Rule, declaration.OperatorToken.GetLocation()));
}

var hasAdditionOrSubstractionOverload =
classDeclaration.ChildNodes()
.OfType<OperatorDeclarationSyntax>()
.Any(op => op.OperatorToken.IsKind(SyntaxKind.PlusToken) ||
op.OperatorToken.IsKind(SyntaxKind.MinusToken));
if (hasAdditionOrSubstractionOverload)
{
return;
}

var namedTypeSymbol = analysisContext.SemanticModel.GetDeclaredSymbol(classDeclaration);
if (namedTypeSymbol == null ||
namedTypeSymbol.ImplementsAny(InterfacesRelyingOnOperatorEqualOverload))
{
return;
}

analysisContext.ReportIssue(Diagnostic.Create(rule, declaration.OperatorToken.GetLocation()));
}
}
}
1 change: 1 addition & 0 deletions analyzers/src/SonarAnalyzer.Common/Helpers/KnownType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ internal sealed partial class KnownType
internal static readonly KnownType System_NotSupportedException = new("System.NotSupportedException");
internal static readonly KnownType System_Nullable_T = new("System.Nullable", "T");
internal static readonly KnownType System_NullReferenceException = new("System.NullReferenceException");
internal static readonly KnownType System_Numerics_IEqualityOperators_TSelf_TOther_TResult = new("System.Numerics.IEqualityOperators", "TSelf", "TOther", "TResult");
internal static readonly KnownType System_Object = new("System.Object");
internal static readonly KnownType System_ObsoleteAttribute = new("System.ObsoleteAttribute");
internal static readonly KnownType System_OutOfMemoryException = new("System.OutOfMemoryException");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,25 @@
namespace SonarAnalyzer.UnitTest.Rules
{
[TestClass]
public class DotNotOverloadOperatorEqualTest
public class DoNotOverloadOperatorEqualTest
{
private readonly VerifierBuilder builder = new VerifierBuilder<DotNotOverloadOperatorEqual>();
private readonly VerifierBuilder builder = new VerifierBuilder<DoNotOverloadOperatorEqual>();

[TestMethod]
public void DotNotOverloadOperatorEqual() =>
builder.AddPaths("DotNotOverloadOperatorEqual.cs").Verify();
public void DoNotOverloadOperatorEqual() =>
builder.AddPaths("DoNotOverloadOperatorEqual.cs").Verify();

#if NET

[TestMethod]
public void DotNotOverloadOperatorEqual_CSharp9() =>
builder.AddPaths("DotNotOverloadOperatorEqual.CSharp9.cs")
public void DoNotOverloadOperatorEqual_CSharp9() =>
builder.AddPaths("DoNotOverloadOperatorEqual.CSharp9.cs")
.WithOptions(ParseOptionsHelper.FromCSharp9)
.Verify();

[TestMethod]
public void DotNotOverloadOperatorEqual_CSharp11() =>
builder.AddPaths("DotNotOverloadOperatorEqual.CSharp11.cs")
public void DoNotOverloadOperatorEqual_CSharp11() =>
builder.AddPaths("DoNotOverloadOperatorEqual.CSharp11.cs")
.WithOptions(ParseOptionsHelper.FromCSharp11)
.Verify();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace Tests.Diagnostics
{
class MyClass : IEqualityOperators<MyClass, MyClass, MyClass>
{
public static MyClass operator ==(MyClass? left, MyClass? right) => new MyClass(); // Noncompliant FP (implementing IEqualityOperators interface require == operator overload)
public static MyClass operator ==(MyClass? left, MyClass? right) => new MyClass(); // Compliant

public static MyClass operator !=(MyClass? left, MyClass? right) => new MyClass();
}
Expand Down

0 comments on commit e84fc45

Please sign in to comment.