diff --git a/analyzers/its/expected/Ember-MM/Ember.Plugins-{9496C697-5AFD-4813-AEDC-AF33FACEADF0}-S2094.json b/analyzers/its/expected/Ember-MM/Ember.Plugins-{9496C697-5AFD-4813-AEDC-AF33FACEADF0}-S2094.json index 0ac2683eee3..45dbbc1ebc5 100644 --- a/analyzers/its/expected/Ember-MM/Ember.Plugins-{9496C697-5AFD-4813-AEDC-AF33FACEADF0}-S2094.json +++ b/analyzers/its/expected/Ember-MM/Ember.Plugins-{9496C697-5AFD-4813-AEDC-AF33FACEADF0}-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\Ember-MM\Ember.Plugins\PluginActionContext.cs", "region": { diff --git a/analyzers/its/expected/Net5/Net5--net5.0-S2094.json b/analyzers/its/expected/Net5/Net5--net5.0-S2094.json index 808fb732c6d..6410000afd1 100644 --- a/analyzers/its/expected/Net5/Net5--net5.0-S2094.json +++ b/analyzers/its/expected/Net5/Net5--net5.0-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\Net5\Net5\ModuleInitializers.cs", "region": { @@ -15,7 +15,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\Net5\Net5\ParameterValidation.cs", "region": { @@ -28,7 +28,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\Net5\Net5\S2330.cs", "region": { @@ -41,7 +41,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\Net5\Net5\S3240.cs", "region": { @@ -54,7 +54,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\Net5\Net5\S3247.cs", "region": { @@ -67,7 +67,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\Net5\Net5\S3453.cs", "region": { @@ -80,7 +80,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\Net5\Net5\StaticLambdas.cs", "region": { diff --git a/analyzers/its/expected/Net7/Net7--net7.0-S2094.json b/analyzers/its/expected/Net7/Net7--net7.0-S2094.json index 289b4af880f..9315f036e86 100644 --- a/analyzers/its/expected/Net7/Net7--net7.0-S2094.json +++ b/analyzers/its/expected/Net7/Net7--net7.0-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\Net7\Net7\features\WarningWave7.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka--netstandard2.0-S2094.json b/analyzers/its/expected/akka.net/Akka--netstandard2.0-S2094.json index 6370b12ad44..b8fd5dbc010 100644 --- a/analyzers/its/expected/akka.net/Akka--netstandard2.0-S2094.json +++ b/analyzers/its/expected/akka.net/Akka--netstandard2.0-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\Actor\ActorSelection.cs", "region": { @@ -15,7 +15,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\Actor\FSM.cs", "region": { @@ -28,7 +28,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\Event\LoggerInitialized.cs", "region": { @@ -41,7 +41,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\Event\Logging.cs", "region": { @@ -54,7 +54,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\IO\Dns.cs", "region": { @@ -67,7 +67,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\IO\Inet.cs", "region": { @@ -80,7 +80,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\IO\Tcp.cs", "region": { @@ -93,7 +93,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\IO\Tcp.cs", "region": { @@ -106,7 +106,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\IO\Tcp.cs", "region": { @@ -119,7 +119,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\IO\Udp.cs", "region": { @@ -132,7 +132,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\IO\Udp.cs", "region": { @@ -145,7 +145,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\IO\UdpConnected.cs", "region": { @@ -158,7 +158,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\Pattern\BackoffOptions.cs", "region": { @@ -171,7 +171,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\Routing\Listeners.cs", "region": { @@ -184,7 +184,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka\Routing\RouterMsg.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka.Benchmarks--netcoreapp3.1-S2094.json b/analyzers/its/expected/akka.net/Akka.Benchmarks--netcoreapp3.1-S2094.json index 025ab6cbded..77c88c4e851 100644 --- a/analyzers/its/expected/akka.net/Akka.Benchmarks--netcoreapp3.1-S2094.json +++ b/analyzers/its/expected/akka.net/Akka.Benchmarks--netcoreapp3.1-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\benchmark\Akka.Benchmarks\IO\TcpOperationsBenchmarks.cs", "region": { @@ -15,7 +15,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\benchmark\Akka.Benchmarks\IO\TcpOperationsBenchmarks.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka.Cluster--netstandard2.0-S2094.json b/analyzers/its/expected/akka.net/Akka.Cluster--netstandard2.0-S2094.json index 7e3af656072..aac4b747412 100644 --- a/analyzers/its/expected/akka.net/Akka.Cluster--netstandard2.0-S2094.json +++ b/analyzers/its/expected/akka.net/Akka.Cluster--netstandard2.0-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Cluster\ClusterDaemon.cs", "region": { @@ -15,7 +15,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Cluster\ClusterHeartbeat.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka.DI.TestKit--netstandard2.0-S2094.json b/analyzers/its/expected/akka.net/Akka.DI.TestKit--netstandard2.0-S2094.json index 8bbc0bb2e33..f0fd59ab4d8 100644 --- a/analyzers/its/expected/akka.net/Akka.DI.TestKit--netstandard2.0-S2094.json +++ b/analyzers/its/expected/akka.net/Akka.DI.TestKit--netstandard2.0-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\contrib\dependencyinjection\Akka.DI.TestKit\DiResolverSpec.cs", "region": { @@ -15,7 +15,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\contrib\dependencyinjection\Akka.DI.TestKit\DiResolverSpec.cs", "region": { @@ -28,7 +28,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\contrib\dependencyinjection\Akka.DI.TestKit\DiResolverSpec.cs", "region": { @@ -41,7 +41,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\contrib\dependencyinjection\Akka.DI.TestKit\DiResolverSpec.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner--net471-S2094.json b/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner--net471-S2094.json index 3c7716e2695..cc98d74023d 100644 --- a/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner--net471-S2094.json +++ b/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner--net471-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner\Program.cs", "region": { @@ -15,7 +15,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner\Program.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner--net5.0-S2094.json b/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner--net5.0-S2094.json index 3c7716e2695..cc98d74023d 100644 --- a/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner--net5.0-S2094.json +++ b/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner--net5.0-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner\Program.cs", "region": { @@ -15,7 +15,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner\Program.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner--netcoreapp3.1-S2094.json b/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner--netcoreapp3.1-S2094.json index 3c7716e2695..cc98d74023d 100644 --- a/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner--netcoreapp3.1-S2094.json +++ b/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner--netcoreapp3.1-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner\Program.cs", "region": { @@ -15,7 +15,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner\Program.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner.Shared--netstandard2.0-S2094.json b/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner.Shared--netstandard2.0-S2094.json index 42a6a955478..8ebac24a9d7 100644 --- a/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner.Shared--netstandard2.0-S2094.json +++ b/analyzers/its/expected/akka.net/Akka.MultiNodeTestRunner.Shared--netstandard2.0-S2094.json @@ -2,20 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", -"location": { -"uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\CompilerErrorCollection.cs", -"region": { -"startLine": 14, -"startColumn": 18, -"endLine": 14, -"endColumn": 41 -} -} -}, -{ -"id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Reporting\TestRunCoordinator.cs", "region": { @@ -28,7 +15,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Sinks\Messages.cs", "region": { @@ -41,7 +28,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Sinks\MessageSinkActor.cs", "region": { @@ -54,7 +41,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Sinks\SinkCoordinator.cs", "region": { @@ -67,7 +54,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Sinks\SinkCoordinator.cs", "region": { @@ -80,7 +67,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Sinks\SinkCoordinator.cs", "region": { @@ -93,7 +80,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Sinks\TimelineLogCollectorActor.cs", "region": { @@ -106,7 +93,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.MultiNodeTestRunner.Shared\Sinks\TimelineLogCollectorActor.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka.Remote--netstandard2.0-S2094.json b/analyzers/its/expected/akka.net/Akka.Remote--netstandard2.0-S2094.json index 5ecaa3af7c5..627fcfd752e 100644 --- a/analyzers/its/expected/akka.net/Akka.Remote--netstandard2.0-S2094.json +++ b/analyzers/its/expected/akka.net/Akka.Remote--netstandard2.0-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Remote\Endpoint.cs", "region": { @@ -15,7 +15,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Remote\Endpoint.cs", "region": { @@ -28,7 +28,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Remote\EndpointManager.cs", "region": { @@ -41,7 +41,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Remote\EndpointManager.cs", "region": { @@ -54,7 +54,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Remote\Transport\AkkaPduCodec.cs", "region": { @@ -67,7 +67,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Remote\Transport\AkkaProtocolTransport.cs", "region": { @@ -80,7 +80,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Remote\Transport\AkkaProtocolTransport.cs", "region": { @@ -93,7 +93,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Remote\Transport\AkkaProtocolTransport.cs", "region": { @@ -106,7 +106,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Remote\Transport\AkkaProtocolTransport.cs", "region": { @@ -119,7 +119,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Remote\Transport\TestTransport.cs", "region": { @@ -132,7 +132,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Remote\Transport\ThrottleTransportAdapter.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka.Remote.Tests.Performance--netcoreapp3.1-S2094.json b/analyzers/its/expected/akka.net/Akka.Remote.Tests.Performance--netcoreapp3.1-S2094.json index 76790ed6bfc..f4479e9850a 100644 --- a/analyzers/its/expected/akka.net/Akka.Remote.Tests.Performance--netcoreapp3.1-S2094.json +++ b/analyzers/its/expected/akka.net/Akka.Remote.Tests.Performance--netcoreapp3.1-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Remote.Tests.Performance\Transports\RemoteMessagingThroughputSpecBase.cs", "region": { diff --git a/analyzers/its/expected/akka.net/Akka.Streams--netstandard2.0-S2094.json b/analyzers/its/expected/akka.net/Akka.Streams--netstandard2.0-S2094.json index cea40b0ba52..1aed34bc8d5 100644 --- a/analyzers/its/expected/akka.net/Akka.Streams--netstandard2.0-S2094.json +++ b/analyzers/its/expected/akka.net/Akka.Streams--netstandard2.0-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Streams\Attributes.cs", "region": { @@ -15,7 +15,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Streams\Attributes.cs", "region": { @@ -28,7 +28,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Streams\Attributes.cs", "region": { @@ -41,7 +41,7 @@ }, { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\core\Akka.Streams\Stage\Context.cs", "region": { diff --git a/analyzers/its/expected/akka.net/RemotePingPong--net471-S2094.json b/analyzers/its/expected/akka.net/RemotePingPong--net471-S2094.json index 19fa046d379..c626699ce26 100644 --- a/analyzers/its/expected/akka.net/RemotePingPong--net471-S2094.json +++ b/analyzers/its/expected/akka.net/RemotePingPong--net471-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\benchmark\RemotePingPong\Program.cs", "region": { diff --git a/analyzers/its/expected/akka.net/RemotePingPong--net5.0-S2094.json b/analyzers/its/expected/akka.net/RemotePingPong--net5.0-S2094.json index 19fa046d379..c626699ce26 100644 --- a/analyzers/its/expected/akka.net/RemotePingPong--net5.0-S2094.json +++ b/analyzers/its/expected/akka.net/RemotePingPong--net5.0-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\benchmark\RemotePingPong\Program.cs", "region": { diff --git a/analyzers/its/expected/akka.net/RemotePingPong--netcoreapp3.1-S2094.json b/analyzers/its/expected/akka.net/RemotePingPong--netcoreapp3.1-S2094.json index 19fa046d379..c626699ce26 100644 --- a/analyzers/its/expected/akka.net/RemotePingPong--netcoreapp3.1-S2094.json +++ b/analyzers/its/expected/akka.net/RemotePingPong--netcoreapp3.1-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\benchmark\RemotePingPong\Program.cs", "region": { diff --git a/analyzers/its/expected/akka.net/TcpEchoService.Server--netcoreapp3.1-S2094.json b/analyzers/its/expected/akka.net/TcpEchoService.Server--netcoreapp3.1-S2094.json index 0c7c2328513..6b95eb1b717 100644 --- a/analyzers/its/expected/akka.net/TcpEchoService.Server--netcoreapp3.1-S2094.json +++ b/analyzers/its/expected/akka.net/TcpEchoService.Server--netcoreapp3.1-S2094.json @@ -2,7 +2,7 @@ "issues": [ { "id": "S2094", -"message": "Remove this empty class, or add members to it.", +"message": "Remove this empty class, write its code or make it an "interface".", "location": { "uri": "sources\akka.net\src\examples\TcpEchoService.Server\Actors.cs", "region": { diff --git a/analyzers/rspec/cs/S2094_c#.html b/analyzers/rspec/cs/S2094_c#.html index 369f2453587..dcee0efaa5c 100644 --- a/analyzers/rspec/cs/S2094_c#.html +++ b/analyzers/rspec/cs/S2094_c#.html @@ -14,9 +14,10 @@

Compliant Solution

}

Exceptions

-

Partial classes are ignored entirely, as they are often used with Source Generators. Subclasses of System.Exception are be ignored, as even an -empty Exception class can provide useful information by its type name alone. Subclasses of certain framework types - like the PageModel class used in -ASP.NET Core Razor Pages - are also be ignored.

+

Partial classes are ignored entirely, as they are often used with Source Generators. Subclasses of System.Exception are ignored, as even an empty +Exception class can provide useful information by its type name alone. Subclasses of System.Attribute are ignored, as well as classes which are +annotated with attributes. Subclasses of generic classes are ignored, as even when empty they can be used for type specialization. Subclasses of +certain framework types - like the PageModel class used in ASP.NET Core Razor Pages - are also ignored.

 using Microsoft.AspNetCore.Mvc.RazorPages;
 
diff --git a/analyzers/rspec/vbnet/S2094_vb.net.html b/analyzers/rspec/vbnet/S2094_vb.net.html
index dd073431b49..7f83fc3a456 100644
--- a/analyzers/rspec/vbnet/S2094_vb.net.html
+++ b/analyzers/rspec/vbnet/S2094_vb.net.html
@@ -14,9 +14,10 @@ 

Compliant Solution

End Interface

Exceptions

-

Partial classes are ignored entirely, as they are often used with Source Generators. Subclasses of System.Exception will be ignored, as even an -empty Exception class can provide useful information by its type name alone. Subclasses of certain framework types - like the PageModel class used in -ASP.NET Core Razor Pages - will also be ignored.

+

Partial classes are ignored entirely, as they are often used with Source Generators. Subclasses of System.Exception are ignored, as even an empty +Exception class can provide useful information by its type name alone. Subclasses of System.Attribute are ignored, as well as classes which are +annotated with attributes. Subclasses of generic classes are ignored, as even when empty they can be used for type specialization. Subclasses of +certain framework types - like the PageModel class used in ASP.NET Core Razor Pages - are also ignored.

 Imports Microsoft.AspNetCore.Mvc.RazorPages
 
diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/ClassShouldNotBeEmpty.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/ClassShouldNotBeEmpty.cs
index 1dae0adc1d4..70fe3c4b6d6 100644
--- a/analyzers/src/SonarAnalyzer.CSharp/Rules/ClassShouldNotBeEmpty.cs
+++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/ClassShouldNotBeEmpty.cs
@@ -21,7 +21,7 @@
 namespace SonarAnalyzer.Rules.CSharp;
 
 [DiagnosticAnalyzer(LanguageNames.CSharp)]
-public sealed class ClassShouldNotBeEmpty : ClassShouldNotBeEmptyBase
+public sealed class ClassShouldNotBeEmpty : ClassShouldNotBeEmptyBase
 {
     protected override ILanguageFacade Language => CSharpFacade.Instance;
 
@@ -30,9 +30,26 @@ public sealed class ClassShouldNotBeEmpty : ClassShouldNotBeEmptyBase x.IsKind(SyntaxKind.PartialKeyword))
         && (node is ClassDeclarationSyntax || IsParameterlessRecord(node));
 
-    protected override bool IsClassWithDeclaredBaseClass(SyntaxNode node) => node is ClassDeclarationSyntax { BaseList: not null };
+    protected override BaseTypeDeclarationSyntax GetIfHasDeclaredBaseClass(SyntaxNode node) =>
+        node is ClassDeclarationSyntax { BaseList: not null } declaration
+            ? declaration
+            : null;
 
-    protected override string DeclarationTypeKeyword(SyntaxNode node) => ((TypeDeclarationSyntax)node).Keyword.ValueText;
+    protected override bool HasGenericBaseClassOrInterface(BaseTypeDeclarationSyntax declaration) =>
+        declaration.BaseList.Types.Any(x => x.Type is GenericNameSyntax);
+
+    protected override bool HasAnyAttribute(SyntaxNode node) =>
+        node is TypeDeclarationSyntax { AttributeLists.Count: > 0  };
+
+    protected override string DeclarationTypeKeyword(SyntaxNode node) =>
+        ((TypeDeclarationSyntax)node).Keyword.ValueText;
+
+    protected override bool HasConditionalCompilationDirectives(SyntaxNode node) =>
+        node.DescendantNodes(descendIntoTrivia: true).Any(x => x.IsAnyKind(
+            SyntaxKind.IfDirectiveTrivia,
+            SyntaxKind.ElifDirectiveTrivia,
+            SyntaxKind.ElseDirectiveTrivia,
+            SyntaxKind.EndIfDirectiveTrivia));
 
     private bool IsParameterlessRecord(SyntaxNode node) =>
         RecordDeclarationSyntaxWrapper.IsInstance(node)
diff --git a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownType.cs b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownType.cs
index b0b254a4c2a..7a34af784c7 100644
--- a/analyzers/src/SonarAnalyzer.Common/Helpers/KnownType.cs
+++ b/analyzers/src/SonarAnalyzer.Common/Helpers/KnownType.cs
@@ -60,6 +60,7 @@ public sealed partial class KnownType
         public static readonly KnownType Microsoft_AspNetCore_Mvc_ControllerAttribute = new("Microsoft.AspNetCore.Mvc.ControllerAttribute");
         public static readonly KnownType Microsoft_AspNetCore_Mvc_DisableRequestSizeLimitAttribute = new("Microsoft.AspNetCore.Mvc.DisableRequestSizeLimitAttribute");
         public static readonly KnownType Microsoft_AspNetCore_Mvc_FromServicesAttribute = new("Microsoft.AspNetCore.Mvc.FromServicesAttribute");
+        public static readonly KnownType Microsoft_AspNetCore_Mvc_IActionResult = new("Microsoft.AspNetCore.Mvc.IActionResult");
         public static readonly KnownType Microsoft_AspNetCore_Mvc_IgnoreAntiforgeryTokenAttribute = new("Microsoft.AspNetCore.Mvc.IgnoreAntiforgeryTokenAttribute");
         public static readonly KnownType Microsoft_AspNetCore_Mvc_NonActionAttribute = new("Microsoft.AspNetCore.Mvc.NonActionAttribute");
         public static readonly KnownType Microsoft_AspNetCore_Mvc_NonControllerAttribute = new("Microsoft.AspNetCore.Mvc.NonControllerAttribute");
diff --git a/analyzers/src/SonarAnalyzer.Common/Rules/ClassShouldNotBeEmptyBase.cs b/analyzers/src/SonarAnalyzer.Common/Rules/ClassShouldNotBeEmptyBase.cs
index 95de276a89d..aba8dd35454 100644
--- a/analyzers/src/SonarAnalyzer.Common/Rules/ClassShouldNotBeEmptyBase.cs
+++ b/analyzers/src/SonarAnalyzer.Common/Rules/ClassShouldNotBeEmptyBase.cs
@@ -20,20 +20,28 @@
 
 namespace SonarAnalyzer.Rules;
 
-public abstract class ClassShouldNotBeEmptyBase : SonarDiagnosticAnalyzer
+public abstract class ClassShouldNotBeEmptyBase : SonarDiagnosticAnalyzer
     where TSyntaxKind : struct
+    where TDeclarationSyntax: SyntaxNode
 {
     private const string DiagnosticId = "S2094";
 
     private static readonly ImmutableArray BaseClassesToIgnore = ImmutableArray.Create(
         KnownType.Microsoft_AspNetCore_Mvc_RazorPages_PageModel,
+        KnownType.System_Attribute,
         KnownType.System_Exception);
 
+    private static readonly ImmutableArray InterfacesToIgnore = ImmutableArray.Create(
+        KnownType.Microsoft_AspNetCore_Mvc_IActionResult);
+
     protected abstract bool IsEmptyAndNotPartial(SyntaxNode node);
-    protected abstract bool IsClassWithDeclaredBaseClass(SyntaxNode node);
+    protected abstract TDeclarationSyntax GetIfHasDeclaredBaseClass(SyntaxNode node);
+    protected abstract bool HasGenericBaseClassOrInterface(TDeclarationSyntax declaration);
+    protected abstract bool HasAnyAttribute(SyntaxNode node);
     protected abstract string DeclarationTypeKeyword(SyntaxNode node);
+    protected abstract bool HasConditionalCompilationDirectives(SyntaxNode node);
 
-    protected override string MessageFormat => "Remove this empty {0}, or add members to it.";
+    protected override string MessageFormat => "Remove this empty {0}, write its code or make it an \"interface\".";
 
     protected ClassShouldNotBeEmptyBase() : base(DiagnosticId) { }
 
@@ -44,15 +52,22 @@ public abstract class ClassShouldNotBeEmptyBase : SonarDiagnosticAn
             {
                 if (Language.Syntax.NodeIdentifier(c.Node) is { IsMissing: false } identifier
                     && IsEmptyAndNotPartial(c.Node)
-                    && !ShouldIgnoreBecauseOfBaseClass(c.Node, c.SemanticModel))
+                    && !HasAnyAttribute(c.Node)
+                    && !HasConditionalCompilationDirectives(c.Node)
+                    && !ShouldIgnoreBecauseOfBaseClassOrInterface(c.Node, c.SemanticModel))
                 {
                     c.ReportIssue(Diagnostic.Create(Rule, identifier.GetLocation(), DeclarationTypeKeyword(c.Node)));
                 }
             },
             Language.SyntaxKind.ClassAndRecordClassDeclarations);
 
-    private bool ShouldIgnoreBecauseOfBaseClass(SyntaxNode node, SemanticModel model) =>
-        IsClassWithDeclaredBaseClass(node)
-        && model.GetDeclaredSymbol(node) is INamedTypeSymbol classSymbol
-        && (classSymbol.BaseType is { IsAbstract: true } || classSymbol.DerivesFromAny(BaseClassesToIgnore));
+    private bool ShouldIgnoreBecauseOfBaseClassOrInterface(SyntaxNode node, SemanticModel model) =>
+        GetIfHasDeclaredBaseClass(node) is { } declaration
+        && (HasGenericBaseClassOrInterface(declaration) || ShouldIgnoreType(declaration, model));
+
+    private static bool ShouldIgnoreType(TDeclarationSyntax node, SemanticModel model) =>
+        model.GetDeclaredSymbol(node) is INamedTypeSymbol classSymbol
+            && (classSymbol.BaseType is { IsAbstract: true }
+            || classSymbol.DerivesFromAny(BaseClassesToIgnore)
+            || classSymbol.ImplementsAny(InterfacesToIgnore));
 }
diff --git a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ClassShouldNotBeEmpty.cs b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ClassShouldNotBeEmpty.cs
index 931ae271056..1bff2c9f1bb 100644
--- a/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ClassShouldNotBeEmpty.cs
+++ b/analyzers/src/SonarAnalyzer.VisualBasic/Rules/ClassShouldNotBeEmpty.cs
@@ -21,7 +21,7 @@
 namespace SonarAnalyzer.Rules.VisualBasic;
 
 [DiagnosticAnalyzer(LanguageNames.VisualBasic)]
-public sealed class ClassShouldNotBeEmpty : ClassShouldNotBeEmptyBase
+public sealed class ClassShouldNotBeEmpty : ClassShouldNotBeEmptyBase
 {
     protected override ILanguageFacade Language => VisualBasicFacade.Instance;
 
@@ -29,7 +29,24 @@ public sealed class ClassShouldNotBeEmpty : ClassShouldNotBeEmptyBase x.IsKind(SyntaxKind.PartialKeyword));
 
-    protected override bool IsClassWithDeclaredBaseClass(SyntaxNode node) => node is ClassBlockSyntax { Inherits.Count: > 0 };
+    protected override TypeBlockSyntax GetIfHasDeclaredBaseClass(SyntaxNode node) =>
+        node is ClassBlockSyntax { Inherits.Count: > 0 } classBlock
+            ? classBlock
+            : null;
 
-    protected override string DeclarationTypeKeyword(SyntaxNode node) => ((TypeBlockSyntax)node).BlockStatement.DeclarationKeyword.ValueText.ToLower();
+    protected override bool HasGenericBaseClassOrInterface(TypeBlockSyntax declaration) =>
+        declaration.Inherits.Any(x => x.Types.Any(t => t is GenericNameSyntax));
+
+    protected override bool HasAnyAttribute(SyntaxNode node) =>
+        node is ClassBlockSyntax { ClassStatement.AttributeLists.Count: > 0 };
+
+    protected override bool HasConditionalCompilationDirectives(SyntaxNode node) =>
+        node.DescendantNodes(descendIntoTrivia: true).Any(x => x.IsAnyKind(
+            SyntaxKind.IfDirectiveTrivia,
+            SyntaxKind.ElseIfDirectiveTrivia,
+            SyntaxKind.ElseDirectiveTrivia,
+            SyntaxKind.EndIfDirectiveTrivia));
+
+    protected override string DeclarationTypeKeyword(SyntaxNode node) =>
+        ((TypeBlockSyntax)node).BlockStatement.DeclarationKeyword.ValueText.ToLower();
 }
diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ClassShouldNotBeEmptyTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ClassShouldNotBeEmptyTest.cs
index e5c2643502a..fb7699daef8 100644
--- a/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ClassShouldNotBeEmptyTest.cs
+++ b/analyzers/tests/SonarAnalyzer.UnitTest/Rules/ClassShouldNotBeEmptyTest.cs
@@ -45,7 +45,9 @@ public class ClassShouldNotBeEmptyTest
 
     private static readonly MetadataReference[] AdditionalReferences = new[]
     {
-        AspNetCoreMetadataReference.MicrosoftAspNetCoreRazorPages
+        AspNetCoreMetadataReference.MicrosoftAspNetCoreMvcAbstractions,
+        AspNetCoreMetadataReference.MicrosoftAspNetCoreMvcCore,
+        AspNetCoreMetadataReference.MicrosoftAspNetCoreRazorPages,
     };
 
     [TestMethod]
diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.CSharp10.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.CSharp10.cs
index 1f2342b1612..02fd205d0ae 100644
--- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.CSharp10.cs
+++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.CSharp10.cs
@@ -1,5 +1,5 @@
 
-record class EmptyRecordClass1();        // Noncompliant {{Remove this empty record, or add members to it.}}
+record class EmptyRecordClass1();        // Noncompliant {{Remove this empty record, write its code or make it an "interface".}}
 //           ^^^^^^^^^^^^^^^^^
 record class EmptyRecordClass2() { };    // Noncompliant
 
diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.CSharp9.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.CSharp9.cs
index dbdd5228c73..7383f176f20 100644
--- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.CSharp9.cs
+++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.CSharp9.cs
@@ -1,6 +1,6 @@
 using System;
 
-record EmptyRecord1();                                      // Noncompliant {{Remove this empty record, or add members to it.}}
+record EmptyRecord1();                                      // Noncompliant {{Remove this empty record, write its code or make it an "interface".}}
 //     ^^^^^^^^^^^^
 record EmptyRecord2() { };                                  // Noncompliant
 
diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.Inheritance.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.Inheritance.cs
index c1753d38de1..3896e880e1a 100644
--- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.Inheritance.cs
+++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.Inheritance.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
 using System;
 
 class BaseClass
@@ -18,6 +19,9 @@ abstract class AbstractBaseWithoutAbstractMethods
 class NoImplementation: AbstractBaseWithAbstractMethods { }          // Error - abstract methods should be implemented
 class DefaultImplementation: AbstractBaseWithoutAbstractMethods { }  // Compliant - the class will use the default implementation of DefaultMethod
 
-class EmptyPageModel: PageModel { }                                  // Compliant - an empty PageModel can be fully functional, the C# code can be in the cshtml file
 class CustomException: Exception { }                                 // Compliant - empty exception classes are allowed, the name of the class already provides information
+class CustomAttribute: Attribute { }                                 // Compliant - empty attribute classes are allowed, the name of the class already provides information
+
+class EmptyPageModel: PageModel { }                                  // Compliant - an empty PageModel can be fully functional, the C# code can be in the cshtml file
+class CustomActionResult: ActionResult { }                           // Compliant - an empty action result can still provide information by its name
 
diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.Inheritance.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.Inheritance.vb
index 60cf17b6482..c28782f9c07 100644
--- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.Inheritance.vb
+++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.Inheritance.vb
@@ -1,4 +1,5 @@
-Imports Microsoft.AspNetCore.Mvc.RazorPages
+Imports Microsoft.AspNetCore.Mvc
+Imports Microsoft.AspNetCore.Mvc.RazorPages
 Imports System
 
 Public Class BaseClass
@@ -9,7 +10,7 @@ Public Class BaseClass
     End Property
 End Class
 
-Public Class SubClass       ' Noncompliant - not derived from any special base class
+Public Class SubClass               ' Noncompliant - not derived from any special base class
     Inherits BaseClass
 End Class
 
@@ -22,18 +23,26 @@ MustInherit Class AbstractBaseWithoutAbstractMethods
     End Sub
 End Class
 
-Class NoImplementation       ' Error - abstract methods should be implemented
+Class NoImplementation              ' Error - abstract methods should be implemented
     Inherits AbstractBaseWithAbstractMethods
 End Class
 
-Class DefaultImplementation  ' Compliant - the class will use the default implementation of DefaultMethod
+Class DefaultImplementation         ' Compliant - the class will use the default implementation of DefaultMethod
     Inherits AbstractBaseWithoutAbstractMethods
 End Class
 
-Public Class EmptyPageModel ' Compliant - an empty PageModel can be fully functional, the VB code can be in the vbhtml file
-    Inherits PageModel
+Public Class CustomException        ' Compliant - empty exception classes are allowed, the name of the class already provides information
+    Inherits Exception
 End Class
 
-Public Class CustomException ' Compliant - empty exception classes are allowed, the name of the class already provides information
+Public Class CustomAttribute        ' Compliant - empty attribute classes are allowed, the name of the class already provides information
     Inherits Exception
 End Class
+
+Public Class EmptyPageModel         ' Compliant - an empty PageModel can be fully functional, the VB code can be in the vbhtml file
+    Inherits PageModel
+End Class
+
+Public Class CustomActionResult     ' Compliant - an empty action result can still provide information by its name
+    Inherits ActionResult
+End Class
diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.cs
index 493dbc28ec3..c842c307146 100644
--- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.cs
+++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.cs
@@ -1,6 +1,8 @@
 using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
 
-class Empty { }                              // Noncompliant {{Remove this empty class, or add members to it.}}
+class Empty { }                              // Noncompliant {{Remove this empty class, write its code or make it an "interface".}}
 //    ^^^^^
 
 public class PublicEmpty { }                 // Noncompliant
@@ -73,6 +75,19 @@ class GenericNotEmptyWithConstraints
     void Method(T arg) { }
 }
 
+class IntegerList: List { }             // Compliant - creates a more specific type without adding new members to it (similar to using the typedef keyword in C/C++)
+class StringLookup: Dictionary { }
+interface IIntegerSet: ISet { }
+
+[ComVisible(true)]
+class ClassWithAttribute { }                 // Compliant - types with attributes are ignored
+
+[ComVisible(true), Obsolete]
+class ClassWithMultipleAttributes { }
+
+[]                                           // Error
+class AttributeError { }
+
 static class StaticEmpty { }                 // Noncompliant
 
 abstract class AbstractEmpty { }             // Noncompliant
@@ -90,5 +105,15 @@ struct EmptyStruct { }                       // Compliant - this rule only deals
 
 enum EmptyEnum { }                           // Compliant - this rule only deals with classes
 
+class Conditional                            // Compliant - it's not empty when the given symbol is defined
+{
+#if NOTDEFINED
+    public override string ToString()
+    {
+        return "Debug Text";
+    }
+#endif
+}
+
 class { }                                    // Error
 
diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.vb
index d6221105cf5..6a7845a4431 100644
--- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.vb
+++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/ClassShouldNotBeEmpty.vb
@@ -1,6 +1,8 @@
 Imports System
+Imports System.Collections.Generic
+Imports System.Runtime.InteropServices
 
-Class Empty                                         ' Noncompliant {{Remove this empty class, or add members to it.}}
+Class Empty                                         ' Noncompliant {{Remove this empty class, write its code or make it an "interface".}}
     ' ^^^^^
 End Class
 
@@ -97,6 +99,30 @@ Class GenericNotEmptyWithConstraints(Of T As Class)
     End Sub
 End Class
 
+Class IntegerList                                   ' Compliant - creates a more specific type without adding new members to it (similar to using the typedef keyword in C/C++)
+    Inherits List(Of Integer)
+End Class
+
+Class StringLookup(Of T)
+    Inherits Dictionary(Of String, T)
+End Class
+
+Interface IIntegerSet(Of T)
+    Inherits ISet(Of Integer)
+End Interface
+
+
+Class ClassWithAttribute                            ' Compliant - types with attributes are ignored
+End Class
+
+
+Class ClassWithMultipleAttributes
+End Class
+
+<>                                                  ' Error
+Class AttributeError                                ' Noncompliant
+End Class
+
 MustInherit Class AbstractEmpty                     ' Noncompliant
 End Class
 
@@ -117,5 +143,13 @@ End Interface
 Structure EmptyStruct                               ' Compliant - this rule only deals with classes
 End Structure
 
+Class Conditional                                   ' Compliant - it's not empty when the given symbol is defined
+#If NOTDEFINED Then
+    Public Overrides Function ToString() As String
+        Return "Debug Text"
+    End Function
+#End If
+End Class
+
 Class                                               ' Error
-End Class                                    
+End Class