diff --git a/analyzers/its/expected/CSharpLatest/S2094-CSharpLatest-net8.0.json b/analyzers/its/expected/CSharpLatest/S2094-CSharpLatest-net8.0.json index 62b9978441b..32f361f4377 100644 --- a/analyzers/its/expected/CSharpLatest/S2094-CSharpLatest-net8.0.json +++ b/analyzers/its/expected/CSharpLatest/S2094-CSharpLatest-net8.0.json @@ -24,12 +24,6 @@ "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/CSharpLatest/CSharpLatest/CSharp9Features/Records.cs#L109", "Location": "Line 109 Position 15-16" }, - { - "Id": "S2094", - "Message": "Remove this empty record, write its code or make it an \u0022interface\u0022.", - "Uri": "https://github.com/SonarSource/sonar-dotnet/blob/master/analyzers/its/sources/CSharpLatest/CSharpLatest/CSharp9Features/Records.cs#L130", - "Location": "Line 130 Position 15-16" - }, { "Id": "S2094", "Message": "Remove this empty record, write its code or make it an \u0022interface\u0022.", diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ClassShouldNotBeEmpty.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ClassShouldNotBeEmpty.cs index 7d2a732c671..60504f0074f 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ClassShouldNotBeEmpty.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ClassShouldNotBeEmpty.cs @@ -31,7 +31,7 @@ protected override bool IsEmptyAndNotPartial(SyntaxNode node) => && (node is ClassDeclarationSyntax || IsParameterlessRecord(node)); protected override BaseTypeDeclarationSyntax GetIfHasDeclaredBaseClassOrInterface(SyntaxNode node) => - node is ClassDeclarationSyntax { BaseList: not null } declaration + node is TypeDeclarationSyntax { BaseList: not null } declaration ? declaration : null; diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ClassShouldNotBeEmptyBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ClassShouldNotBeEmptyBase.cs index 249f49ce9f1..b5396cd597a 100644 --- a/analyzers/src/SonarAnalyzer.Common/Rules/ClassShouldNotBeEmptyBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/Rules/ClassShouldNotBeEmptyBase.cs @@ -66,5 +66,5 @@ private static bool ShouldIgnoreType(TDeclarationSyntax node, SemanticModel mode model.GetDeclaredSymbol(node) is INamedTypeSymbol classSymbol && (classSymbol.BaseType is { IsAbstract: true } || classSymbol.DerivesFromAny(BaseClassesToIgnore) - || classSymbol.Interfaces.Any()); + || classSymbol.Interfaces.Any(x => !x.Is(KnownType.System_IEquatable_T))); // every record type implicitly implements System.IEquatable } diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/ClassShouldNotBeEmpty.CSharp9.cs b/analyzers/tests/SonarAnalyzer.Test/TestCases/ClassShouldNotBeEmpty.CSharp9.cs index 6fa76cadc8c..5aa7ab9b798 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/ClassShouldNotBeEmpty.CSharp9.cs +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/ClassShouldNotBeEmpty.CSharp9.cs @@ -61,3 +61,11 @@ namespace Ignore partial record EmptyPartialRecord(); // Compliant - partial classes are ignored, so partial record classes are ignored as well } +// https://github.com/SonarSource/sonar-dotnet/issues/7709 +namespace Repro_7709 +{ + interface IMarker { } + record ImplementsMarker : IMarker { } + record ImplementsEmptyRecordAndMarker : Noncompliant.EmptyRecord, IMarker { } +} +