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.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..364e2e6e 100644 --- a/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.cs +++ b/Funcky.SourceGenerator.Test/OrNoneGeneratorSnapshotTests.cs @@ -183,4 +183,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..f48f3427 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,27 @@ 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(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( + 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) { 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 3a33b07e..237a31a1 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 @@ -14,12 +14,13 @@ true All + true $(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