From 16f63e65675db2015a4445b01f00d30874570ecb Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sun, 17 Mar 2024 18:25:52 +0300 Subject: [PATCH] Report error when property is annotated with multiple parser-related attributes --- .../AnalyzerReleases.Unshipped.md | 1 + .../ArgumentParserGenerator.Extract.cs | 8 ++++- .../Analyzers/OptionsTypeAnalyzer.cs | 13 ++++++-- .../Diagnostics/DiagnosticDescriptors.cs | 8 +++++ .../ArgumentParserGeneratorTests.cs | 31 +++++++++++++++++++ .../OptionsTypeAnalyzerTests.cs | 31 +++++++++++++++++++ 6 files changed, 89 insertions(+), 3 deletions(-) diff --git a/src/ArgumentParsing.Generators/AnalyzerReleases.Unshipped.md b/src/ArgumentParsing.Generators/AnalyzerReleases.Unshipped.md index 47e523f..ebfc4fa 100644 --- a/src/ArgumentParsing.Generators/AnalyzerReleases.Unshipped.md +++ b/src/ArgumentParsing.Generators/AnalyzerReleases.Unshipped.md @@ -4,3 +4,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- ARGP0034 | ArgumentParsing | Error | ARGP0035 | ArgumentParsing | Warning | +ARGP0036 | ArgumentParsing | Error | diff --git a/src/ArgumentParsing.Generators/ArgumentParserGenerator.Extract.cs b/src/ArgumentParsing.Generators/ArgumentParserGenerator.Extract.cs index a222caa..de19652 100644 --- a/src/ArgumentParsing.Generators/ArgumentParserGenerator.Extract.cs +++ b/src/ArgumentParsing.Generators/ArgumentParserGenerator.Extract.cs @@ -219,7 +219,9 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo) Extr } } - if (!isOption && !isParameter && !isRemainingParameters) + var countOfParserRelatedAttributes = (isOption ? 1 : 0) + (isParameter ? 1 : 0) + (isRemainingParameters ? 1 : 0); + + if (countOfParserRelatedAttributes == 0) { if (property.IsRequired) { @@ -228,6 +230,10 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo) Extr continue; } + else if (countOfParserRelatedAttributes > 1) + { + return default; + } if (property is not { DeclaredAccessibility: >= Accessibility.Internal, SetMethod.DeclaredAccessibility: >= Accessibility.Internal }) { diff --git a/src/ArgumentParsing.Generators/Diagnostics/Analyzers/OptionsTypeAnalyzer.cs b/src/ArgumentParsing.Generators/Diagnostics/Analyzers/OptionsTypeAnalyzer.cs index 125eb7e..39d32b8 100644 --- a/src/ArgumentParsing.Generators/Diagnostics/Analyzers/OptionsTypeAnalyzer.cs +++ b/src/ArgumentParsing.Generators/Diagnostics/Analyzers/OptionsTypeAnalyzer.cs @@ -38,7 +38,8 @@ public sealed class OptionsTypeAnalyzer : DiagnosticAnalyzer DiagnosticDescriptors.DuplicateRemainingParameters, DiagnosticDescriptors.InvalidRemainingParametersPropertyType, DiagnosticDescriptors.TooLowAccessibilityOfOptionsType, - DiagnosticDescriptors.NoOptionNames); + DiagnosticDescriptors.NoOptionNames, + DiagnosticDescriptors.PropertyCannotHaveMultipleParserRoles); public override void Initialize(AnalysisContext context) { @@ -204,7 +205,9 @@ private static void AnalyzeOptionsType(SymbolAnalysisContext context, KnownTypes var propertyType = property.Type; var propertyLocation = property.Locations.First(); - if (!isOption && !isParameter && !isRemainingParameters) + var countOfParserRelatedAttributes = (isOption ? 1 : 0) + (isParameter ? 1 : 0) + (isRemainingParameters ? 1 : 0); + + if (countOfParserRelatedAttributes == 0) { if (property.IsRequired && property.SetMethod is not null && @@ -218,6 +221,12 @@ property.SetMethod is not null && continue; } + else if (countOfParserRelatedAttributes > 1) + { + context.ReportDiagnostic( + Diagnostic.Create( + DiagnosticDescriptors.PropertyCannotHaveMultipleParserRoles, propertyLocation)); + } if (property.DeclaredAccessibility < Accessibility.Internal) { diff --git a/src/ArgumentParsing.Generators/Diagnostics/DiagnosticDescriptors.cs b/src/ArgumentParsing.Generators/Diagnostics/DiagnosticDescriptors.cs index 8ce4afe..5329cc9 100644 --- a/src/ArgumentParsing.Generators/Diagnostics/DiagnosticDescriptors.cs +++ b/src/ArgumentParsing.Generators/Diagnostics/DiagnosticDescriptors.cs @@ -260,4 +260,12 @@ public static class DiagnosticDescriptors category: ArgumentParsingCategoryName, defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor PropertyCannotHaveMultipleParserRoles = new( + id: "ARGP0036", + title: "Property cannot have multiple parser roles", + messageFormat: "Property cannot have multiple parser roles, meaning it cannot be annotated with multiple parser-related attributes", + category: ArgumentParsingCategoryName, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); } diff --git a/tests/ArgumentParsing.Tests.Unit/ArgumentParserGeneratorTests.cs b/tests/ArgumentParsing.Tests.Unit/ArgumentParserGeneratorTests.cs index a0485f0..9279e01 100644 --- a/tests/ArgumentParsing.Tests.Unit/ArgumentParserGeneratorTests.cs +++ b/tests/ArgumentParsing.Tests.Unit/ArgumentParserGeneratorTests.cs @@ -2413,6 +2413,37 @@ class MyOptions await VerifyGeneratorAsync(source); } + [Fact] + public async Task OptionsType_MultipleParserRelatedAttributes() + { + var source = """ + partial class C + { + [GeneratedArgumentParser] + private static partial ParseResult {|CS8795:ParseArguments|}(string[] args); + } + + [OptionsType] + class MyOptions + { + [Option] + [Parameter(0)] + public string Prop1 { get; set; } + + [Parameter(1)] + [RemainingParameters] + public string Prop2 { get; set; } + + [Option] + [Parameter(2)] + [RemainingParameters] + public string Prop3 { get; set; } + } + """; + + await VerifyGeneratorAsync(source); + } + private static async Task VerifyGeneratorAsync(string source, params (string Hint, string Content)[] generatedDocuments) { var test = new CSharpSourceGeneratorTest() diff --git a/tests/ArgumentParsing.Tests.Unit/OptionsTypeAnalyzerTests.cs b/tests/ArgumentParsing.Tests.Unit/OptionsTypeAnalyzerTests.cs index fd13470..1718496 100644 --- a/tests/ArgumentParsing.Tests.Unit/OptionsTypeAnalyzerTests.cs +++ b/tests/ArgumentParsing.Tests.Unit/OptionsTypeAnalyzerTests.cs @@ -1348,4 +1348,35 @@ class MyOptions await VerifyAnalyzerAsync(source); } + + [Fact] + public async Task MultipleParserRelatedAttributes() + { + var source = """ + partial class C + { + [GeneratedArgumentParser] + private static partial ParseResult {|CS8795:ParseArguments|}(string[] args); + } + + [OptionsType] + class MyOptions + { + [Option] + [Parameter(0)] + public string {|ARGP0036:Prop1|} { get; set; } + + [Parameter(1)] + [RemainingParameters] + public string {|ARGP0036:Prop2|} { get; set; } + + [Option] + [Parameter(2)] + [RemainingParameters] + public string {|ARGP0036:Prop3|} { get; set; } + } + """; + + await VerifyAnalyzerAsync(source); + } }