From fadc4cbc44f73ad66978afd2ec33d385729f3d7c Mon Sep 17 00:00:00 2001 From: Ruben Schmidmeister <4602612+bash@users.noreply.github.com> Date: Tue, 1 Nov 2022 12:36:11 +0100 Subject: [PATCH 1/6] Add net7.0 target framework to Funcky Co-authored-by: Mathias Fischler --- Funcky/Funcky.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Funcky/Funcky.csproj b/Funcky/Funcky.csproj index 3a33b07e..3aba41f7 100644 --- a/Funcky/Funcky.csproj +++ b/Funcky/Funcky.csproj @@ -1,6 +1,6 @@ - net6.0;net5.0;netcoreapp3.1;netstandard2.0;netstandard2.1 + net7.0;net6.0;net5.0;netcoreapp3.1;netstandard2.0;netstandard2.1 preview enable Funcky From 17998e82dd52999dbaaba6a6ca793aca5b63d10c Mon Sep 17 00:00:00 2001 From: Ruben Schmidmeister <4602612+bash@users.noreply.github.com> Date: Tue, 1 Nov 2022 12:37:18 +0100 Subject: [PATCH 2/6] Copy [StringSyntax] attribute to generated methods Co-authored-by: Mathias Fischler --- ...ibuteFromOriginalDefinition.00.verified.cs | 12 ++++++ ...ibuteFromOriginalDefinition.01.verified.cs | 15 +++++++ .../OrNoneGeneratorSnapshotTests.cs | 40 +++++++++++++++++++ .../OrNoneFromTryPatternGenerator.cs | 22 +++++++++- 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.CopiesStringSyntaxAttributeFromOriginalDefinition.00.verified.cs create mode 100644 Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.CopiesStringSyntaxAttributeFromOriginalDefinition.01.verified.cs diff --git a/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.CopiesStringSyntaxAttributeFromOriginalDefinition.00.verified.cs b/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.CopiesStringSyntaxAttributeFromOriginalDefinition.00.verified.cs new file mode 100644 index 00000000..57e7b2e4 --- /dev/null +++ b/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.CopiesStringSyntaxAttributeFromOriginalDefinition.00.verified.cs @@ -0,0 +1,12 @@ +//HintName: .g.cs +// +#nullable enable + +namespace Funcky.Extensions +{ + public static partial class ParseExtensions + { + [global::System.Diagnostics.Contracts.Pure] + public static Funcky.Monads.Option ParseTargetOrNone(this string candidate, [global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("foo")] string format) => global::Funcky.Extensions.Target.TryParse(candidate, format, out var result) ? result : default(Funcky.Monads.Option); + } +} \ No newline at end of file diff --git a/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.CopiesStringSyntaxAttributeFromOriginalDefinition.01.verified.cs b/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.CopiesStringSyntaxAttributeFromOriginalDefinition.01.verified.cs new file mode 100644 index 00000000..524f7293 --- /dev/null +++ b/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.CopiesStringSyntaxAttributeFromOriginalDefinition.01.verified.cs @@ -0,0 +1,15 @@ +//HintName: OrNoneFromTryPatternAttribute.g.cs +namespace Funcky.Internal +{ + [global::System.Diagnostics.Conditional("COMPILE_TIME_ONLY")] + [global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = true)] + internal class OrNoneFromTryPatternAttribute : global::System.Attribute + { + public OrNoneFromTryPatternAttribute(global::System.Type type, string method) + => (Type, Method) = (type, method); + + public global::System.Type Type { get; } + + public string Method { get; } + } +} \ No newline at end of file diff --git a/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.cs b/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.cs index 5900f1f2..0524b113 100644 --- a/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.cs +++ b/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.cs @@ -1,3 +1,5 @@ +using Xunit.Abstractions; + namespace Funcky.SourceGenerator.Test; [UsesVerify] // 👈 Adds hooks for Verify into XUnit @@ -14,6 +16,13 @@ public struct Option where TItem : notnull } """; + private readonly ITestOutputHelper _testOutputHelper; + + public OrNoneGeneratorSnapshotTests(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + } + [Fact] public Task GenerateSingleMethodWithTheSingleArgumentCandidate() { @@ -183,4 +192,35 @@ public static bool TryParseExact(string candidate, out Target result) return TestHelper.Verify(source + Environment.NewLine + OptionSource); } + + [Fact] + public Task CopiesStringSyntaxAttributeFromOriginalDefinition() + { + const string source = """ + #nullable enable + + using System; + using System.Diagnostics.CodeAnalysis; + using Funcky.Internal; + + namespace Funcky.Extensions + { + [OrNoneFromTryPattern(typeof(Target), nameof(Target.TryParse))] + public static partial class ParseExtensions + { + } + + public sealed class Target + { + public static bool TryParse(string candidate, [StringSyntaxAttribute("foo")] string format, out Target result) + { + result = default!; + return false; + } + } + } + """; + + return TestHelper.Verify(source + Environment.NewLine + OptionSource); + } } diff --git a/Funcky.SourceGenerator/OrNoneFromTryPatternGenerator.cs b/Funcky.SourceGenerator/OrNoneFromTryPatternGenerator.cs index c8545624..f9630863 100644 --- a/Funcky.SourceGenerator/OrNoneFromTryPatternGenerator.cs +++ b/Funcky.SourceGenerator/OrNoneFromTryPatternGenerator.cs @@ -11,6 +11,7 @@ namespace Funcky.SourceGenerator; public sealed class OrNoneFromTryPatternGenerator : IIncrementalGenerator { private const string AttributeFullName = "Funcky.Internal.OrNoneFromTryPatternAttribute"; + private const string StringSyntaxAttributeFullName = "System.Diagnostics.CodeAnalysis.StringSyntaxAttribute"; private static readonly IEnumerable GeneratedFileHeadersSource = ImmutableList.Create("// ", "#nullable enable", string.Empty); public void Initialize(IncrementalGeneratorInitializationContext context) @@ -127,7 +128,26 @@ private static ParameterSyntax GenerateParameter(IParameterSymbol parameter, int => Parameter(Identifier(GetParameterName(parameter, index))) .WithModifiers(index == 0 ? TokenList(Token(SyntaxKind.ThisKeyword)) : TokenList()) .WithType(GenerateTypeSyntax(parameter.Type)) - .WithDefault(GetParameterDefaultValue(parameter)); + .WithDefault(GetParameterDefaultValue(parameter)) + .WithAttributeLists(GenerateParameterAttributes(parameter)); + + private static SyntaxList GenerateParameterAttributes(IParameterSymbol parameter) + => parameter.GetAttributes().Where(ShouldCopyParameterAttribute).Select(GenerateParameterAttribute).ToImmutableArray() switch + { + { Length: 0 } => List(), + { Length: >0 } attributes => SingletonList(AttributeList(SeparatedList(attributes))), + }; + + private static AttributeSyntax GenerateParameterAttribute(AttributeData originalAttribute) + => Attribute( + ParseName(GenerateTypeSyntax(originalAttribute.AttributeClass!).ToString()), + AttributeArgumentList(SeparatedList(originalAttribute.ConstructorArguments.Select(GenerateAttributeArgument)))); + + private static AttributeArgumentSyntax GenerateAttributeArgument(TypedConstant argumentValue) + => AttributeArgument(ParseExpression(argumentValue.ToCSharpString())); + + private static bool ShouldCopyParameterAttribute(AttributeData originalAttribute) + => originalAttribute.AttributeClass?.ToDisplayString() == StringSyntaxAttributeFullName; private static TypeSyntax GenerateTypeSyntax(ITypeSymbol type) { From e914edeb9bca8c2904595fc0e6a76f94f3aa5d23 Mon Sep 17 00:00:00 2001 From: Ruben Schmidmeister <4602612+bash@users.noreply.github.com> Date: Tue, 1 Nov 2022 12:45:59 +0100 Subject: [PATCH 3/6] Split `GenerateParameterAttributes` into two functions Co-authored-by: Mathias Fischler --- .../OrNoneFromTryPatternGenerator.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Funcky.SourceGenerator/OrNoneFromTryPatternGenerator.cs b/Funcky.SourceGenerator/OrNoneFromTryPatternGenerator.cs index f9630863..f48f3427 100644 --- a/Funcky.SourceGenerator/OrNoneFromTryPatternGenerator.cs +++ b/Funcky.SourceGenerator/OrNoneFromTryPatternGenerator.cs @@ -129,14 +129,15 @@ private static ParameterSyntax GenerateParameter(IParameterSymbol parameter, int .WithModifiers(index == 0 ? TokenList(Token(SyntaxKind.ThisKeyword)) : TokenList()) .WithType(GenerateTypeSyntax(parameter.Type)) .WithDefault(GetParameterDefaultValue(parameter)) - .WithAttributeLists(GenerateParameterAttributes(parameter)); - - private static SyntaxList GenerateParameterAttributes(IParameterSymbol parameter) - => parameter.GetAttributes().Where(ShouldCopyParameterAttribute).Select(GenerateParameterAttribute).ToImmutableArray() switch - { - { Length: 0 } => List(), - { Length: >0 } attributes => SingletonList(AttributeList(SeparatedList(attributes))), - }; + .WithAttributeLists(GenerateParameterAttributeLists(parameter)); + + private static SyntaxList GenerateParameterAttributeLists(IParameterSymbol parameter) + => GenerateParameterAttributes(parameter).ToImmutableArray() is { Length: >0 } attributes + ? SingletonList(AttributeList(SeparatedList(attributes))) + : List(); + + private static IEnumerable GenerateParameterAttributes(IParameterSymbol parameter) + => parameter.GetAttributes().Where(ShouldCopyParameterAttribute).Select(GenerateParameterAttribute); private static AttributeSyntax GenerateParameterAttribute(AttributeData originalAttribute) => Attribute( From 45baea61a1fb265c79c5ccc5e05c782760e36d5f Mon Sep 17 00:00:00 2001 From: Ruben Schmidmeister <4602612+bash@users.noreply.github.com> Date: Tue, 1 Nov 2022 14:33:53 +0100 Subject: [PATCH 4/6] Remove unused constructors --- .../OrNoneGeneratorSnapshotTests.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.cs b/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.cs index 0524b113..364e2e6e 100644 --- a/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.cs +++ b/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.cs @@ -1,5 +1,3 @@ -using Xunit.Abstractions; - namespace Funcky.SourceGenerator.Test; [UsesVerify] // 👈 Adds hooks for Verify into XUnit @@ -16,13 +14,6 @@ public struct Option where TItem : notnull } """; - private readonly ITestOutputHelper _testOutputHelper; - - public OrNoneGeneratorSnapshotTests(ITestOutputHelper testOutputHelper) - { - _testOutputHelper = testOutputHelper; - } - [Fact] public Task GenerateSingleMethodWithTheSingleArgumentCandidate() { From 831c77c3866376b654bc2a1e8c3cfed21a1b1d20 Mon Sep 17 00:00:00 2001 From: Ruben Schmidmeister <4602612+bash@users.noreply.github.com> Date: Wed, 2 Nov 2022 08:59:21 +0100 Subject: [PATCH 5/6] Analyze public API for .NET 7 target framework --- Funcky/Funcky.csproj | 2 +- Funcky/PublicAPI.Unshipped.txt | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Funcky/Funcky.csproj b/Funcky/Funcky.csproj index 3aba41f7..519c2fd8 100644 --- a/Funcky/Funcky.csproj +++ b/Funcky/Funcky.csproj @@ -19,7 +19,7 @@ $(DefineConstants);CONTRACTS_FULL - + diff --git a/Funcky/PublicAPI.Unshipped.txt b/Funcky/PublicAPI.Unshipped.txt index 2dbb3e98..55580012 100644 --- a/Funcky/PublicAPI.Unshipped.txt +++ b/Funcky/PublicAPI.Unshipped.txt @@ -4,6 +4,40 @@ Funcky.RequireClass.RequireClass() -> void Funcky.RequireStruct Funcky.RequireStruct.RequireStruct() -> void static Funcky.Extensions.EnumerableExtensions.ElementAtOrNone(this System.Collections.Generic.IEnumerable! source, System.Index index) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseBigIntegerOrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseBigIntegerOrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseByteOrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseByteOrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseDateOnlyOrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseDateOnlyOrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseDateTimeOffsetOrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseDateTimeOffsetOrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseDateTimeOrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseDateTimeOrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseDecimalOrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseDecimalOrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseDoubleOrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseDoubleOrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseGuidOrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseGuidOrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseInt16OrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseInt16OrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseInt32OrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseInt32OrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseInt64OrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseInt64OrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseSByteOrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseSByteOrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseSingleOrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseSingleOrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseTimeOnlyOrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseTimeOnlyOrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseUInt16OrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseUInt16OrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseUInt32OrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseUInt32OrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseUInt64OrNone(this string? candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.ParseExtensions.ParseUInt64OrNone(this System.ReadOnlySpan candidate, System.IFormatProvider? provider) -> Funcky.Monads.Option static Funcky.Extensions.QueryableExtensions.ElementAtOrNone(this System.Linq.IQueryable! source, int index) -> Funcky.Monads.Option static Funcky.Extensions.QueryableExtensions.ElementAtOrNone(this System.Linq.IQueryable! source, System.Index index) -> Funcky.Monads.Option static Funcky.Extensions.StreamExtensions.ReadByteOrNone(this System.IO.Stream! stream) -> Funcky.Monads.Option From e9ec7a57730cb688e49a2627b7d2224b5a3b5026 Mon Sep 17 00:00:00 2001 From: Ruben Schmidmeister <4602612+bash@users.noreply.github.com> Date: Wed, 2 Nov 2022 09:08:36 +0100 Subject: [PATCH 6/6] Enable package validation --- Funcky.Async/Funcky.Async.csproj | 1 + Funcky.Xunit/Funcky.Xunit.csproj | 1 + Funcky/Funcky.csproj | 1 + 3 files changed, 3 insertions(+) diff --git a/Funcky.Async/Funcky.Async.csproj b/Funcky.Async/Funcky.Async.csproj index 242e2e5e..361db122 100644 --- a/Funcky.Async/Funcky.Async.csproj +++ b/Funcky.Async/Funcky.Async.csproj @@ -12,6 +12,7 @@ true All + true $(NoWarn);RS0026 diff --git a/Funcky.Xunit/Funcky.Xunit.csproj b/Funcky.Xunit/Funcky.Xunit.csproj index 62af097c..a8fd65ef 100644 --- a/Funcky.Xunit/Funcky.Xunit.csproj +++ b/Funcky.Xunit/Funcky.Xunit.csproj @@ -11,6 +11,7 @@ Funcky + true $(NoWarn);NU5104 diff --git a/Funcky/Funcky.csproj b/Funcky/Funcky.csproj index 519c2fd8..237a31a1 100644 --- a/Funcky/Funcky.csproj +++ b/Funcky/Funcky.csproj @@ -14,6 +14,7 @@ true All + true $(DefineConstants);CONTRACTS_FULL