diff --git a/src/ArgumentParsing.Generators/ArgumentParserGenerator.Extract.cs b/src/ArgumentParsing.Generators/ArgumentParserGenerator.Extract.cs index 87538b6..70fd481 100644 --- a/src/ArgumentParsing.Generators/ArgumentParserGenerator.Extract.cs +++ b/src/ArgumentParsing.Generators/ArgumentParserGenerator.Extract.cs @@ -2,7 +2,6 @@ using System.Diagnostics; using ArgumentParsing.Generators.Extensions; using ArgumentParsing.Generators.Models; -using ArgumentParsing.Generators.Utils; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -12,44 +11,38 @@ public partial class ArgumentParserGenerator { private static readonly SymbolDisplayFormat s_qualifiedNameFormat = SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted); - private static (ArgumentParserInfo? ArgumentParserInfo, OptionsHelpInfo? OptionsHelpInfo, ImmutableEquatableArray Diagnostics) ExtractMainInfo(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) + private static (ArgumentParserInfo? ArgumentParserInfo, OptionsHelpInfo? OptionsHelpInfo) ExtractMainInfo(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) { var argumentParserMethodSyntax = (MethodDeclarationSyntax)context.TargetNode; var argumentParserMethodSymbol = (IMethodSymbol)context.TargetSymbol; - var diagnosticsBuilder = ImmutableArray.CreateBuilder(); - SimpleParameterInfo? parameterInfo = null; INamedTypeSymbol? validOptionsType = null; - var hasErrors = false; - if (argumentParserMethodSymbol.Parameters is not [var singleParameter]) { - hasErrors = true; + return default; } - else - { - var singleParameterSyntax = argumentParserMethodSyntax.ParameterList.Parameters[0]; - if (singleParameter.IsParams || - singleParameter.RefKind != RefKind.None || - singleParameter.ScopedKind != ScopedKind.None || - argumentParserMethodSymbol.IsExtensionMethod) - { - hasErrors = true; - } + var singleParameterSyntax = argumentParserMethodSyntax.ParameterList.Parameters[0]; - var singleParameterType = singleParameter.Type; + if (singleParameter.IsParams || + singleParameter.RefKind != RefKind.None || + singleParameter.ScopedKind != ScopedKind.None || + argumentParserMethodSymbol.IsExtensionMethod) + { + return default; + } - if (!singleParameterType.IsEnumerableCollectionOfStrings()) - { - hasErrors = true; - } + var singleParameterType = singleParameter.Type; - parameterInfo = new(singleParameterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), singleParameter.Name); + if (!singleParameterType.IsEnumerableCollectionOfStrings()) + { + return default; } + parameterInfo = new(singleParameterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), singleParameter.Name); + var returnTypeSyntax = argumentParserMethodSyntax.ReturnType; var returnType = argumentParserMethodSymbol.ReturnType; @@ -59,38 +52,25 @@ private static (ArgumentParserInfo? ArgumentParserInfo, OptionsHelpInfo? Options if (returnType is not INamedTypeSymbol { TypeArguments: [var optionsType] } namedReturnType || !namedReturnType.ConstructedFrom.Equals(parseResultOfTType, SymbolEqualityComparer.Default)) { - hasErrors = true; - } - else - { - if (optionsType is not INamedTypeSymbol { SpecialType: SpecialType.None, TypeKind: TypeKind.Class or TypeKind.Struct } namedOptionsType || !namedOptionsType.Constructors.Any(c => c.Parameters.Length == 0)) - { - hasErrors = true; - } - else - { - validOptionsType = namedOptionsType; - } + return default; } - if (validOptionsType is null) + if (optionsType is not INamedTypeSymbol { SpecialType: SpecialType.None, TypeKind: TypeKind.Class or TypeKind.Struct } namedOptionsType || !namedOptionsType.Constructors.Any(c => c.Parameters.Length == 0)) { - return (null, null, diagnosticsBuilder.ToImmutable()); + return default; } + validOptionsType = namedOptionsType; + cancellationToken.ThrowIfCancellationRequested(); - var (optionsInfo, optionsHelpInfo, optionsDiagnostics) = ExtractInfoFromOptionsType(validOptionsType, compilation, cancellationToken); - hasErrors |= optionsInfo is null; - diagnosticsBuilder.AddRange(optionsDiagnostics); + var (optionsInfo, optionsHelpInfo) = ExtractInfoFromOptionsType(validOptionsType, compilation, cancellationToken); - if (hasErrors) + if (optionsHelpInfo is null) { - return (null, null, diagnosticsBuilder.ToImmutable()); + return default; } - Debug.Assert(parameterInfo is not null); - var methodInfo = new ArgumentParserMethodInfo( argumentParserMethodSyntax.Modifiers.ToString(), argumentParserMethodSymbol.ReturnType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), @@ -102,18 +82,14 @@ private static (ArgumentParserInfo? ArgumentParserInfo, OptionsHelpInfo? Options methodInfo, optionsInfo!); - return (argumentParserInfo, optionsHelpInfo, diagnosticsBuilder.ToImmutable()); + return (argumentParserInfo, optionsHelpInfo); } - private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, ImmutableArray Diagnostics) ExtractInfoFromOptionsType(INamedTypeSymbol optionsType, Compilation compilation, CancellationToken cancellationToken) + private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo) ExtractInfoFromOptionsType(INamedTypeSymbol optionsType, Compilation compilation, CancellationToken cancellationToken) { var optionsBuilder = ImmutableArray.CreateBuilder(); var optionsHelpBuilder = ImmutableArray.CreateBuilder(); - var diagnosticsBuilder = ImmutableArray.CreateBuilder(); - - var hasErrors = false; - var seenShortNames = new HashSet(); var seenLongNames = new HashSet(); @@ -131,7 +107,7 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu if (member is IFieldSymbol { IsRequired: true }) { - hasErrors = true; + return default; } if (member is not IPropertySymbol property) @@ -246,7 +222,7 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu { if (property.IsRequired) { - hasErrors = true; + return default; } continue; @@ -254,7 +230,7 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu if (property is not { DeclaredAccessibility: >= Accessibility.Internal, SetMethod.DeclaredAccessibility: >= Accessibility.Internal }) { - hasErrors = true; + return default; } var propertyName = property.Name; @@ -267,13 +243,9 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu { var snv = shortName.Value; - if (!char.IsLetter(snv)) - { - hasErrors = true; - } - else if (!seenShortNames.Add(snv)) + if (!char.IsLetter(snv) || !seenShortNames.Add(snv)) { - hasErrors = true; + return default; } } @@ -284,18 +256,14 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu if (!shortName.HasValue && longName is null) { - hasErrors = true; + return default; } if (longName is not null) { - if (!char.IsLetter(longName[0]) || !longName.Replace("-", string.Empty).All(char.IsLetterOrDigit)) + if (!char.IsLetter(longName[0]) || !longName.Replace("-", string.Empty).All(char.IsLetterOrDigit) || !seenLongNames.Add(longName)) { - hasErrors = true; - } - else if (!seenLongNames.Add(longName)) - { - hasErrors = true; + return default; } } @@ -303,13 +271,12 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu if (parseStrategy == ParseStrategy.None) { - hasErrors = true; - continue; + return default; } if (isRequired && parseStrategy == ParseStrategy.Flag && nullableUnderlyingType is null) { - hasErrors = true; + return default; } optionsBuilder.Add(new( @@ -332,26 +299,22 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu { var hasParameter = parameterMap.ContainsKey(parameterIndex); - if (parameterIndex < 0) - { - hasErrors = true; - } - else if (hasParameter) + if (parameterIndex < 0 || hasParameter) { - hasErrors = true; + return default; } parameterName ??= propertyName.ToKebabCase(); if (!char.IsLetter(parameterName[0]) || !parameterName.Replace("-", string.Empty).All(char.IsLetterOrDigit)) { - hasErrors = true; + return default; } var (parseStrategy, nullableUnderlyingType) = GetParseStrategyForParameter(propertyType); if (parseStrategy == ParseStrategy.None) { - hasErrors = true; + return default; } if (!hasParameter) @@ -375,7 +338,7 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu if (declaredRemainingParameters) { - hasErrors = true; + return default; } declaredRemainingParameters = true; @@ -383,7 +346,7 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu var (parseStrategy, _, sequenceType, sequenceUnderlyingType) = GetParseStrategyForOption(propertyType, compilation); if (parseStrategy == ParseStrategy.None || sequenceType == SequenceType.None) { - hasErrors = true; + return default; } remainingParametersInfo = new( @@ -406,7 +369,7 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu if (index > (lastSeenIndex + 1)) { - hasErrors = true; + return default; } var parameterInfo = pair.Value; @@ -423,7 +386,7 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu { if (!canNextParameterBeRequired) { - hasErrors = true; + return default; } } else @@ -432,11 +395,6 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu } } - if (hasErrors) - { - return (null, null, diagnosticsBuilder.ToImmutable()); - } - var qualifiedName = optionsType.ToDisplayString(s_qualifiedNameFormat); var optionsInfo = new OptionsInfo( qualifiedName, @@ -451,7 +409,7 @@ private static (OptionsInfo? OptionsInfo, OptionsHelpInfo? OptionsHelpInfo, Immu parametersHelpBuilder.ToImmutable(), remainingParametersHelpInfo); - return (optionsInfo, optionsHelpInfo, diagnosticsBuilder.ToImmutable()); + return (optionsInfo, optionsHelpInfo); static (ParseStrategy, string? NullableUnderlyingType, SequenceType, string? SequenceUnderlyingType) GetParseStrategyForOption(ITypeSymbol type, Compilation compilation) { diff --git a/src/ArgumentParsing.Generators/ArgumentParserGenerator.cs b/src/ArgumentParsing.Generators/ArgumentParserGenerator.cs index 6bad635..4b38de6 100644 --- a/src/ArgumentParsing.Generators/ArgumentParserGenerator.cs +++ b/src/ArgumentParsing.Generators/ArgumentParserGenerator.cs @@ -1,4 +1,3 @@ -using ArgumentParsing.Generators.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -13,13 +12,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .ForAttributeWithMetadataName( "ArgumentParsing.GeneratedArgumentParserAttribute", static (node, _) => node is MethodDeclarationSyntax, - ExtractMainInfo); - - var diagnostics = extractedInfosProvider - .Where(info => !info.Diagnostics.IsDefaultOrEmpty) - .Select((info, _) => info.Diagnostics); - - context.ReportDiagnostics(diagnostics); + ExtractMainInfo) + .Where(info => info != default); var infoFromCompilation = context.CompilationProvider .Select(ExtractInfoFromCompilation); @@ -28,7 +22,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Select((info, _) => info.EnvironmentInfo); var argumentParserInfos = extractedInfosProvider - .Where(info => info.ArgumentParserInfo is not null) .Select((info, _) => info.ArgumentParserInfo!) .Combine(environmentInfo); @@ -38,7 +31,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Select((info, _) => info.AssemblyVersionInfo); var optionsHelpInfos = extractedInfosProvider - .Where(info => info.OptionsHelpInfo is not null) .Select((info, _) => info.OptionsHelpInfo!) .Combine(assemblyVersionInfo); diff --git a/src/ArgumentParsing.Generators/Extensions/DiagnosticsExtension.cs b/src/ArgumentParsing.Generators/Extensions/DiagnosticsExtension.cs deleted file mode 100644 index 993c797..0000000 --- a/src/ArgumentParsing.Generators/Extensions/DiagnosticsExtension.cs +++ /dev/null @@ -1,19 +0,0 @@ -using ArgumentParsing.Generators.Models; -using ArgumentParsing.Generators.Utils; -using Microsoft.CodeAnalysis; - -namespace ArgumentParsing.Generators.Extensions; - -internal static class DiagnosticsExtension -{ - public static void ReportDiagnostics(this IncrementalGeneratorInitializationContext context, IncrementalValuesProvider> diagnostics) - { - context.RegisterSourceOutput(diagnostics, static (context, diagnostics) => - { - foreach (var diagnostic in diagnostics) - { - context.ReportDiagnostic(diagnostic.ToDiagnostic()); - } - }); - } -} diff --git a/src/ArgumentParsing.Generators/Models/DiagnosticInfo.cs b/src/ArgumentParsing.Generators/Models/DiagnosticInfo.cs deleted file mode 100644 index a95a8ea..0000000 --- a/src/ArgumentParsing.Generators/Models/DiagnosticInfo.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Immutable; -using ArgumentParsing.Generators.Utils; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; - -namespace ArgumentParsing.Generators.Models; - -internal sealed record DiagnosticInfo( - DiagnosticDescriptor Descriptor, - SyntaxTree? SyntaxTree, - TextSpan TextSpan, - ImmutableEquatableArray Arguments) -{ - public Diagnostic ToDiagnostic() - { - return SyntaxTree is not null - ? Diagnostic.Create(Descriptor, Location.Create(SyntaxTree, TextSpan), Arguments.ToArray()) - : Diagnostic.Create(Descriptor, null, Arguments.ToArray()); - } - - public static DiagnosticInfo Create(DiagnosticDescriptor descriptor, Location location, params object[] args) - { - return new(descriptor, location.SourceTree, location.SourceSpan, args.Select(static arg => arg.ToString()).ToImmutableArray()); - } - - public static DiagnosticInfo Create(DiagnosticDescriptor descriptor, ISymbol symbol, params object[] args) - => Create(descriptor, symbol.Locations.First(), args); - - public static DiagnosticInfo Create(DiagnosticDescriptor descriptor, SyntaxToken token, params object[] args) - { - return new(descriptor, token.SyntaxTree, token.Span, args.Select(static arg => arg.ToString()).ToImmutableArray()); - } - - public static DiagnosticInfo Create(DiagnosticDescriptor descriptor, SyntaxNode node, params object[] args) - { - return new(descriptor, node.SyntaxTree, node.Span, args.Select(static arg => arg.ToString()).ToImmutableArray()); - } -} diff --git a/tests/ArgumentParsing.Tests.Unit/ArgumentParserGeneratorTests.cs b/tests/ArgumentParsing.Tests.Unit/ArgumentParserGeneratorTests.cs index be1fb6b..35847b0 100644 --- a/tests/ArgumentParsing.Tests.Unit/ArgumentParserGeneratorTests.cs +++ b/tests/ArgumentParsing.Tests.Unit/ArgumentParserGeneratorTests.cs @@ -2338,13 +2338,7 @@ class MyOptions await VerifyGeneratorAsync(source); } - private static async Task VerifyGeneratorAsync(string source, DiagnosticResult[] diagnostics) - => await VerifyGeneratorAsync(source, diagnostics, []); - private static async Task VerifyGeneratorAsync(string source, params (string Hint, string Content)[] generatedDocuments) - => await VerifyGeneratorAsync(source, [], generatedDocuments); - - private static async Task VerifyGeneratorAsync(string source, DiagnosticResult[] diagnostics, (string Hint, string Content)[] generatedDocuments) { var test = new CSharpSourceGeneratorTest() { @@ -2369,8 +2363,6 @@ private static async Task VerifyGeneratorAsync(string source, DiagnosticResult[] ReferenceAssemblies = ReferenceAssemblies.Net.Net80 }; - test.TestState.ExpectedDiagnostics.AddRange(diagnostics); - foreach (var (hint, content) in generatedDocuments) { test.TestState.GeneratedSources.Add((typeof(ArgumentParserGenerator), hint, content.Replace("", '"' + typeof(ArgumentParserGenerator).Assembly.GetName().Version.ToString() + '"')));