From cc9a36c14496f48b50308889b6215a0fcc742aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Wed, 24 Sep 2025 21:04:41 -0400 Subject: [PATCH 1/5] Added two new diagnostic rules: - MA0174: Enforces explicit use of the `class` keyword in record declarations. - MA0175: Enforces implicit record declarations without the `class` keyword. --- src/Meziantou.Analyzer/RuleIdentifiers.cs | 2 + ...lassDeclarationShouldBeExplicitAnalyzer.cs | 46 ++++++ ...lassDeclarationShouldBeImplicitAnalyzer.cs | 43 +++++ ...eclarationShouldBeExplicitAnalyzerTests.cs | 152 ++++++++++++++++++ ...eclarationShouldBeImplicitAnalyzerTests.cs | 140 ++++++++++++++++ 5 files changed, 383 insertions(+) create mode 100644 src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeExplicitAnalyzer.cs create mode 100644 src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeImplicitAnalyzer.cs create mode 100644 tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs create mode 100644 tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs diff --git a/src/Meziantou.Analyzer/RuleIdentifiers.cs b/src/Meziantou.Analyzer/RuleIdentifiers.cs index 56b55de8..91e44d7f 100755 --- a/src/Meziantou.Analyzer/RuleIdentifiers.cs +++ b/src/Meziantou.Analyzer/RuleIdentifiers.cs @@ -176,6 +176,8 @@ internal static class RuleIdentifiers public const string UsePatternMatchingInsteadOfHasvalue = "MA0171"; public const string BothSideOfTheConditionAreIdentical = "MA0172"; public const string UseLazyInitializerEnsureInitialize = "MA0173"; + public const string RecordClassDeclarationShouldBeExplicit = "MA0174"; + public const string RecordClassDeclarationShouldBeImplicit = "MA0175"; public static string GetHelpUri(string identifier) { diff --git a/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeExplicitAnalyzer.cs b/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeExplicitAnalyzer.cs new file mode 100644 index 00000000..67463994 --- /dev/null +++ b/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeExplicitAnalyzer.cs @@ -0,0 +1,46 @@ +using System.Collections.Immutable; +using Meziantou.Analyzer.Internals; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Meziantou.Analyzer.Rules; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public sealed class RecordClassDeclarationShouldBeExplicitAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor Rule = new( + RuleIdentifiers.RecordClassDeclarationShouldBeExplicit, + title: "Record should use explicit 'class' keyword", + messageFormat: "Record should be declared with explicit 'class' keyword", + RuleCategories.Style, + DiagnosticSeverity.Info, + isEnabledByDefault: false, + description: "", + helpLinkUri: RuleIdentifiers.GetHelpUri(RuleIdentifiers.RecordClassDeclarationShouldBeExplicit)); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.RegisterSyntaxNodeAction(AnalyzeRecordDeclaration, SyntaxKind.RecordDeclaration); + } + + private static void AnalyzeRecordDeclaration(SyntaxNodeAnalysisContext context) + { + var recordDeclaration = (RecordDeclarationSyntax)context.Node; + + // Check if this is a record without the explicit 'class' keyword + // RecordDeclarationSyntax.ClassOrStructKeyword will be null/missing for implicit record classes + // and will contain 'class' or 'struct' for explicit ones + if (recordDeclaration.ClassOrStructKeyword.IsKind(SyntaxKind.None)) + { + // This is an implicit record class (no 'class' or 'struct' keyword) + // Report diagnostic on the record keyword + context.ReportDiagnostic(Rule, recordDeclaration.Keyword); + } + } +} \ No newline at end of file diff --git a/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeImplicitAnalyzer.cs b/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeImplicitAnalyzer.cs new file mode 100644 index 00000000..85cc5387 --- /dev/null +++ b/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeImplicitAnalyzer.cs @@ -0,0 +1,43 @@ +using System.Collections.Immutable; +using Meziantou.Analyzer.Internals; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Meziantou.Analyzer.Rules; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public sealed class RecordClassDeclarationShouldBeImplicitAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor Rule = new( + RuleIdentifiers.RecordClassDeclarationShouldBeImplicit, + title: "Record should not use explicit 'class' keyword", + messageFormat: "Record should not be declared with explicit 'class' keyword", + RuleCategories.Style, + DiagnosticSeverity.Info, + isEnabledByDefault: false, + description: "", + helpLinkUri: RuleIdentifiers.GetHelpUri(RuleIdentifiers.RecordClassDeclarationShouldBeImplicit)); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.RegisterSyntaxNodeAction(AnalyzeRecordDeclaration, SyntaxKind.RecordDeclaration); + } + + private static void AnalyzeRecordDeclaration(SyntaxNodeAnalysisContext context) + { + var recordDeclaration = (RecordDeclarationSyntax)context.Node; + + // Check if this is a record with the explicit 'class' keyword + if (recordDeclaration.ClassOrStructKeyword.IsKind(SyntaxKind.ClassKeyword)) + { + // This is an explicit record class - report diagnostic on the 'class' keyword + context.ReportDiagnostic(Rule, recordDeclaration.ClassOrStructKeyword); + } + } +} \ No newline at end of file diff --git a/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs new file mode 100644 index 00000000..782a8e6e --- /dev/null +++ b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs @@ -0,0 +1,152 @@ +using Meziantou.Analyzer.Rules; +using Meziantou.Analyzer.Test.Helpers; +using TestHelper; + +namespace Meziantou.Analyzer.Test.Rules; + +public sealed class RecordClassDeclarationShouldBeExplicitAnalyzerTests +{ + private static ProjectBuilder CreateProjectBuilder() + { + return new ProjectBuilder() + .WithAnalyzer() + .WithTargetFramework(TargetFramework.NetLatest); + } + + [Fact] + public async Task ImplicitRecordClass_ShouldReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public [|record|] Target { public required int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ImplicitRecordClass_WithModifiers_ShouldReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public sealed [|record|] Target { public required int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ExplicitRecordClass_ShouldNotReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public record class Target { public required int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ExplicitRecordStruct_ShouldNotReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public record struct Target { public required int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task RegularClass_ShouldNotReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public class Target { public required int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task RegularStruct_ShouldNotReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public struct Target { public int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ImplicitRecordClass_WithParameters_ShouldReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public [|record|] Target(int Id); + """) + .ValidateAsync(); + } + + [Fact] + public async Task ExplicitRecordClass_WithParameters_ShouldNotReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public record class Target(int Id); + """) + .ValidateAsync(); + } + + [Fact] + public async Task ImplicitRecordClass_Generic_ShouldReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public [|record|] Target { public required T Value { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ImplicitRecordClass_InNamespace_ShouldReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + namespace MyNamespace + { + public [|record|] Target { public required int Id { get; init; } } + } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ImplicitRecordClass_WithInheritance_ShouldReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public abstract [|record|] BaseRecord; + public [|record|] Target : BaseRecord { public required int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task RxplicitRecordClass_WithInheritance_ShouldNotReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public abstract record class BaseRecord; + public record class Target : BaseRecord { public required int Id { get; init; } } + """) + .ValidateAsync(); + } +} \ No newline at end of file diff --git a/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs new file mode 100644 index 00000000..48af6dbd --- /dev/null +++ b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs @@ -0,0 +1,140 @@ +using Meziantou.Analyzer.Rules; +using Meziantou.Analyzer.Test.Helpers; +using TestHelper; + +namespace Meziantou.Analyzer.Test.Rules; + +public sealed class RecordClassDeclarationShouldBeImplicitAnalyzerTests +{ + private static ProjectBuilder CreateProjectBuilder() + { + return new ProjectBuilder() + .WithAnalyzer() + .WithTargetFramework(TargetFramework.NetLatest); + } + + [Fact] + public async Task ExplicitRecordClass_ShouldReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public record [|class|] Target { public required int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ExplicitRecordClass_WithModifiers_ShouldReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public sealed record [|class|] Target { public required int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ImplicitRecordClass_ShouldNotReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public record Target { public required int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ExplicitRecordStruct_ShouldNotReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public record struct Target { public required int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task RegularClass_ShouldNotReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public class Target { public required int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task RegularStruct_ShouldNotReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public struct Target { public int Id { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ExplicitRecordClass_WithParameters_ShouldReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public record [|class|] Target(int Id); + """) + .ValidateAsync(); + } + + [Fact] + public async Task ImplicitRecordClass_WithParameters_ShouldNotReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public record Target(int Id); + """) + .ValidateAsync(); + } + + [Fact] + public async Task ExplicitRecordClass_Generic_ShouldReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public record [|class|] Target { public required T Value { get; init; } } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ExplicitRecordClass_InNamespace_ShouldReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + namespace MyNamespace + { + public record [|class|] Target { public required int Id { get; init; } } + } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ExplicitRecordClass_WithInheritance_ShouldReportDiagnostic() + { + + await CreateProjectBuilder() + .WithSourceCode(""" + public abstract record BaseRecord; + public record [|class|] Target : BaseRecord { public required int Id { get; init; } } + """) + .ValidateAsync(); + } +} \ No newline at end of file From fd3d148b2ffb5951851e257e483146c158417379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Wed, 24 Sep 2025 21:08:30 -0400 Subject: [PATCH 2/5] Add documentation --- README.md | 2 ++ docs/README.md | 14 ++++++++++++++ docs/Rules/MA0174.md | 12 ++++++++++++ docs/Rules/MA0175.md | 12 ++++++++++++ .../configuration/default.editorconfig | 6 ++++++ .../configuration/none.editorconfig | 6 ++++++ 6 files changed, 52 insertions(+) create mode 100644 docs/Rules/MA0174.md create mode 100644 docs/Rules/MA0175.md diff --git a/README.md b/README.md index 3b151917..2a1f9961 100755 --- a/README.md +++ b/README.md @@ -189,6 +189,8 @@ If you are already using other analyzers, you can check [which rules are duplica |[MA0171](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0171.md)|Usage|Use pattern matching instead of inequality operators for discrete value|ℹ️|❌|✔️| |[MA0172](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0172.md)|Usage|Both sides of the logical operation are identical|⚠️|❌|❌| |[MA0173](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0173.md)|Design|Use LazyInitializer.EnsureInitialize|ℹ️|✔️|❌| +|[MA0174](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0174.md)|Style|Record should use explicit 'class' keyword|ℹ️|❌|❌| +|[MA0175](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0175.md)|Style|Record should not use explicit 'class' keyword|ℹ️|❌|❌| diff --git a/docs/README.md b/docs/README.md index df614d90..9732a745 100755 --- a/docs/README.md +++ b/docs/README.md @@ -173,6 +173,8 @@ |[MA0171](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0171.md)|Usage|Use pattern matching instead of inequality operators for discrete value|ℹ️|❌|✔️| |[MA0172](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0172.md)|Usage|Both sides of the logical operation are identical|⚠️|❌|❌| |[MA0173](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0173.md)|Design|Use LazyInitializer.EnsureInitialize|ℹ️|✔️|❌| +|[MA0174](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0174.md)|Style|Record should use explicit 'class' keyword|ℹ️|❌|❌| +|[MA0175](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0175.md)|Style|Record should not use explicit 'class' keyword|ℹ️|❌|❌| |Id|Suppressed rule|Justification| |--|---------------|-------------| @@ -700,6 +702,12 @@ dotnet_diagnostic.MA0172.severity = none # MA0173: Use LazyInitializer.EnsureInitialize dotnet_diagnostic.MA0173.severity = suggestion + +# MA0174: Record should use explicit 'class' keyword +dotnet_diagnostic.MA0174.severity = none + +# MA0175: Record should not use explicit 'class' keyword +dotnet_diagnostic.MA0175.severity = none ``` # .editorconfig - all rules disabled @@ -1220,4 +1228,10 @@ dotnet_diagnostic.MA0172.severity = none # MA0173: Use LazyInitializer.EnsureInitialize dotnet_diagnostic.MA0173.severity = none + +# MA0174: Record should use explicit 'class' keyword +dotnet_diagnostic.MA0174.severity = none + +# MA0175: Record should not use explicit 'class' keyword +dotnet_diagnostic.MA0175.severity = none ``` diff --git a/docs/Rules/MA0174.md b/docs/Rules/MA0174.md new file mode 100644 index 00000000..b705d736 --- /dev/null +++ b/docs/Rules/MA0174.md @@ -0,0 +1,12 @@ +# MA0174 - Record should use explicit 'class' keyword + +This rule suggests adding the explicit `class` keyword to record declarations that don't specify it. + +```csharp +public sealed record Customer; // non-compliant +public sealed record class Customer; // compliant +``` + +## Related rules + +- [MA0175](MA0175.md) - Record should not use explicit 'class' keyword (opposite rule) diff --git a/docs/Rules/MA0175.md b/docs/Rules/MA0175.md new file mode 100644 index 00000000..a32f60c5 --- /dev/null +++ b/docs/Rules/MA0175.md @@ -0,0 +1,12 @@ +# MA0175 - Record should not use explicit 'class' keyword + +This rule suggests adding the explicit `class` keyword to record declarations that don't specify it. + +```csharp +public sealed record class Customer; // non-compliant +public sealed record Customer; // compliant +``` + +## Related rules + +- [MA0174](MA0174.md) - Record should use explicit 'class' keyword (opposite rule) diff --git a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig index 5cad7507..02b9eeee 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig @@ -517,3 +517,9 @@ dotnet_diagnostic.MA0172.severity = none # MA0173: Use LazyInitializer.EnsureInitialize dotnet_diagnostic.MA0173.severity = suggestion + +# MA0174: Record should use explicit 'class' keyword +dotnet_diagnostic.MA0174.severity = none + +# MA0175: Record should not use explicit 'class' keyword +dotnet_diagnostic.MA0175.severity = none diff --git a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig index 1770f021..b1856ed3 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig @@ -517,3 +517,9 @@ dotnet_diagnostic.MA0172.severity = none # MA0173: Use LazyInitializer.EnsureInitialize dotnet_diagnostic.MA0173.severity = none + +# MA0174: Record should use explicit 'class' keyword +dotnet_diagnostic.MA0174.severity = none + +# MA0175: Record should not use explicit 'class' keyword +dotnet_diagnostic.MA0175.severity = none From 292518b8ee975dc38b599fae4f8a6bde8eee2dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Wed, 24 Sep 2025 21:18:01 -0400 Subject: [PATCH 3/5] fix --- .../Rules/RecordClassDeclarationShouldBeExplicitAnalyzer.cs | 4 +++- .../Rules/RecordClassDeclarationShouldBeImplicitAnalyzer.cs | 4 +++- .../RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs | 6 ++++-- .../RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs | 6 ++++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeExplicitAnalyzer.cs b/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeExplicitAnalyzer.cs index 67463994..cc529bbb 100644 --- a/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeExplicitAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeExplicitAnalyzer.cs @@ -1,3 +1,4 @@ +#if CSHARP10_OR_GREATER using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; @@ -43,4 +44,5 @@ private static void AnalyzeRecordDeclaration(SyntaxNodeAnalysisContext context) context.ReportDiagnostic(Rule, recordDeclaration.Keyword); } } -} \ No newline at end of file +} +#endif diff --git a/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeImplicitAnalyzer.cs b/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeImplicitAnalyzer.cs index 85cc5387..0422fc1b 100644 --- a/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeImplicitAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeImplicitAnalyzer.cs @@ -1,3 +1,4 @@ +#if CSHARP10_OR_GREATER using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; @@ -40,4 +41,5 @@ private static void AnalyzeRecordDeclaration(SyntaxNodeAnalysisContext context) context.ReportDiagnostic(Rule, recordDeclaration.ClassOrStructKeyword); } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs index 782a8e6e..cc8cdf64 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs @@ -1,3 +1,4 @@ +#if CSHARP10_OR_GREATER using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; @@ -57,7 +58,7 @@ public record struct Target { public required int Id { get; init; } } .ValidateAsync(); } - [Fact] +[Fact] public async Task RegularClass_ShouldNotReportDiagnostic() { @@ -149,4 +150,5 @@ public record class Target : BaseRecord { public required int Id { get; init; } """) .ValidateAsync(); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs index 48af6dbd..511fdc45 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs @@ -1,3 +1,4 @@ +#if CSHARP10_OR_GREATER using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; @@ -57,7 +58,7 @@ public record struct Target { public required int Id { get; init; } } .ValidateAsync(); } - [Fact] +[Fact] public async Task RegularClass_ShouldNotReportDiagnostic() { @@ -137,4 +138,5 @@ public record [|class|] Target : BaseRecord { public required int Id { get; init """) .ValidateAsync(); } -} \ No newline at end of file +} +#endif \ No newline at end of file From 9815a28d499834fae01bb348b0c4002baca00e82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Wed, 24 Sep 2025 21:31:24 -0400 Subject: [PATCH 4/5] wip --- ...eclarationShouldBeExplicitAnalyzerTests.cs | 16 ++++++------- ...eclarationShouldBeImplicitAnalyzerTests.cs | 24 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs index cc8cdf64..c6aa6c46 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs @@ -86,7 +86,7 @@ public async Task ImplicitRecordClass_WithParameters_ShouldReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public [|record|] Target(int Id); + public [|record|] Target(int Id) { } """) .ValidateAsync(); } @@ -97,7 +97,7 @@ public async Task ExplicitRecordClass_WithParameters_ShouldNotReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public record class Target(int Id); + public record class Target(int Id) { } """) .ValidateAsync(); } @@ -108,7 +108,7 @@ public async Task ImplicitRecordClass_Generic_ShouldReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public [|record|] Target { public required T Value { get; init; } } + public [|record|] Target { } """) .ValidateAsync(); } @@ -121,7 +121,7 @@ await CreateProjectBuilder() .WithSourceCode(""" namespace MyNamespace { - public [|record|] Target { public required int Id { get; init; } } + public [|record|] Target { } } """) .ValidateAsync(); @@ -133,8 +133,8 @@ public async Task ImplicitRecordClass_WithInheritance_ShouldReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public abstract [|record|] BaseRecord; - public [|record|] Target : BaseRecord { public required int Id { get; init; } } + public abstract [|record|] BaseRecord { } + public [|record|] Target : BaseRecord { } """) .ValidateAsync(); } @@ -145,8 +145,8 @@ public async Task RxplicitRecordClass_WithInheritance_ShouldNotReportDiagnostic( await CreateProjectBuilder() .WithSourceCode(""" - public abstract record class BaseRecord; - public record class Target : BaseRecord { public required int Id { get; init; } } + public abstract record class BaseRecord { } + public record class Target : BaseRecord { } """) .ValidateAsync(); } diff --git a/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs index 511fdc45..c5a4b44e 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeImplicitAnalyzerTests.cs @@ -20,7 +20,7 @@ public async Task ExplicitRecordClass_ShouldReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public record [|class|] Target { public required int Id { get; init; } } + public record [|class|] Target { } """) .ValidateAsync(); } @@ -31,7 +31,7 @@ public async Task ExplicitRecordClass_WithModifiers_ShouldReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public sealed record [|class|] Target { public required int Id { get; init; } } + public sealed record [|class|] Target { } """) .ValidateAsync(); } @@ -42,7 +42,7 @@ public async Task ImplicitRecordClass_ShouldNotReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public record Target { public required int Id { get; init; } } + public record Target { } """) .ValidateAsync(); } @@ -53,7 +53,7 @@ public async Task ExplicitRecordStruct_ShouldNotReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public record struct Target { public required int Id { get; init; } } + public record struct Target { } """) .ValidateAsync(); } @@ -64,7 +64,7 @@ public async Task RegularClass_ShouldNotReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public class Target { public required int Id { get; init; } } + public class Target { } """) .ValidateAsync(); } @@ -75,7 +75,7 @@ public async Task RegularStruct_ShouldNotReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public struct Target { public int Id { get; init; } } + public struct Target { } """) .ValidateAsync(); } @@ -86,7 +86,7 @@ public async Task ExplicitRecordClass_WithParameters_ShouldReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public record [|class|] Target(int Id); + public record [|class|] Target(int Id) { } """) .ValidateAsync(); } @@ -97,7 +97,7 @@ public async Task ImplicitRecordClass_WithParameters_ShouldNotReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public record Target(int Id); + public record Target(int Id) { } """) .ValidateAsync(); } @@ -108,7 +108,7 @@ public async Task ExplicitRecordClass_Generic_ShouldReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public record [|class|] Target { public required T Value { get; init; } } + public record [|class|] Target { } """) .ValidateAsync(); } @@ -121,7 +121,7 @@ await CreateProjectBuilder() .WithSourceCode(""" namespace MyNamespace { - public record [|class|] Target { public required int Id { get; init; } } + public record [|class|] Target { } } """) .ValidateAsync(); @@ -133,8 +133,8 @@ public async Task ExplicitRecordClass_WithInheritance_ShouldReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public abstract record BaseRecord; - public record [|class|] Target : BaseRecord { public required int Id { get; init; } } + public abstract record BaseRecord { } + public record [|class|] Target : BaseRecord { } """) .ValidateAsync(); } From f9c26540695c500c3d9241706f6956fd91ee765f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Wed, 24 Sep 2025 21:39:47 -0400 Subject: [PATCH 5/5] wip --- ...lassDeclarationShouldBeExplicitAnalyzerTests.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs index c6aa6c46..f2f980d1 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/RecordClassDeclarationShouldBeExplicitAnalyzerTests.cs @@ -20,7 +20,7 @@ public async Task ImplicitRecordClass_ShouldReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public [|record|] Target { public required int Id { get; init; } } + public [|record|] Target { } """) .ValidateAsync(); } @@ -31,7 +31,7 @@ public async Task ImplicitRecordClass_WithModifiers_ShouldReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public sealed [|record|] Target { public required int Id { get; init; } } + public sealed [|record|] Target { } """) .ValidateAsync(); } @@ -42,7 +42,7 @@ public async Task ExplicitRecordClass_ShouldNotReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public record class Target { public required int Id { get; init; } } + public record class Target { } """) .ValidateAsync(); } @@ -53,7 +53,7 @@ public async Task ExplicitRecordStruct_ShouldNotReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public record struct Target { public required int Id { get; init; } } + public record struct Target { } """) .ValidateAsync(); } @@ -64,7 +64,7 @@ public async Task RegularClass_ShouldNotReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public class Target { public required int Id { get; init; } } + public class Target { } """) .ValidateAsync(); } @@ -75,7 +75,7 @@ public async Task RegularStruct_ShouldNotReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public struct Target { public int Id { get; init; } } + public struct Target { } """) .ValidateAsync(); } @@ -133,7 +133,7 @@ public async Task ImplicitRecordClass_WithInheritance_ShouldReportDiagnostic() await CreateProjectBuilder() .WithSourceCode(""" - public abstract [|record|] BaseRecord { } + public abstract [|record|] BaseRecord { } public [|record|] Target : BaseRecord { } """) .ValidateAsync();