diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/TypeFileBuilderBase.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/TypeFileBuilderBase.cs index d2d3a2fe655..a575e85e16b 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/TypeFileBuilderBase.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/TypeFileBuilderBase.cs @@ -1978,8 +1978,7 @@ private static string FormatTypedConstant(TypedConstant constant) return FormatPrimitive(constant.Value); case TypedConstantKind.Enum: - var enumType = constant.Type?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - return $"{enumType}.{constant.Value}"; + return FormatEnumConstant(constant); case TypedConstantKind.Type: var typeArg = ((ITypeSymbol)constant.Value!).ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); @@ -2023,6 +2022,30 @@ private static string FormatPrimitive(object? value) }; } + private static string FormatEnumConstant(TypedConstant constant) + { + if (constant.Type is not INamedTypeSymbol enumSymbol) + { + return FormatPrimitive(constant.Value); + } + + var enumType = enumSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + + if (constant.Value is not null) + { + foreach (var member in enumSymbol.GetMembers()) + { + if (member is IFieldSymbol { HasConstantValue: true } field + && Equals(field.ConstantValue, constant.Value)) + { + return $"{enumType}.{field.Name}"; + } + } + } + + return $"({enumType}){constant.Value}"; + } + private static string EscapeString(string s) { return s.Replace("\\", "\\\\") diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.cs index c941025f3dc..02dbb22ac3b 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.cs @@ -540,6 +540,79 @@ internal static partial class BookNode """).MatchMarkdownAsync(); } + [Fact] + public async Task CustomAttribute_With_Enum_And_Composite_Enum_Arguments_On_Parameter_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + public enum Visibility + { + Public = 0, + Internal = 1, + Private = 2 + } + + [Flags] + public enum Access + { + None = 0, + Read = 1, + Write = 2, + Execute = 4 + } + + public sealed class FooAttribute : Attribute + { + public FooAttribute(Visibility visibility) + { + Visibility = visibility; + } + + public Visibility Visibility { get; } + + public Visibility NamedVisibility { get; set; } + + public Access AccessFlags { get; set; } + } + + public sealed class Author + { + public int Id { get; set; } + public string Name { get; set; } + } + + public sealed class Book + { + public int Id { get; set; } + public string Title { get; set; } + public int AuthorId { get; set; } + } + + [ObjectType] + internal static partial class BookNode + { + [BindMember(nameof(Book.AuthorId))] + public static Task GetAuthorAsync( + [Parent] Book book, + [Foo( + Visibility.Internal, + NamedVisibility = Visibility.Private, + AccessFlags = Access.Read | Access.Write)] int version, + CancellationToken cancellationToken) + => default; + } + """).MatchMarkdownAsync(); + } + [Fact] public async Task GraphQLType_On_Parameter_MatchesSnapshot() { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeTests.CustomAttribute_With_Enum_And_Composite_Enum_Arguments_On_Parameter_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeTests.CustomAttribute_With_Enum_And_Composite_Enum_Arguments_On_Parameter_MatchesSnapshot.md new file mode 100644 index 00000000000..09626d7bda4 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeTests.CustomAttribute_With_Enum_And_Composite_Enum_Arguments_On_Parameter_MatchesSnapshot.md @@ -0,0 +1,165 @@ +# CustomAttribute_With_Enum_And_Composite_Enum_Arguments_On_Parameter_MatchesSnapshot + +## BookNode.WaAdMHmlGJHjtEI4nqY7WA.hc.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static partial class BookNode + { + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + var extension = descriptor.Extend(); + var configuration = extension.Configuration; + var thisType = typeof(global::TestNamespace.BookNode); + var bindingResolver = extension.Context.ParameterBindingResolver; + var resolvers = new __Resolvers(bindingResolver); + + var naming = descriptor.Extend().Context.Naming; + var ignoredFields = new global::System.Collections.Generic.HashSet(); + ignoredFields.Add(naming.GetMemberName("AuthorId", global::HotChocolate.Types.MemberKind.ObjectField)); + + foreach(string fieldName in ignoredFields) + { + descriptor.Field(fieldName).Ignore(); + } + + descriptor + .Field(naming.GetMemberName("Author", global::HotChocolate.Types.MemberKind.ObjectField)) + .ExtendWith(static (field, context) => + { + var configuration = field.Configuration; + var typeInspector = field.Context.TypeInspector; + var bindingResolver = field.Context.ParameterBindingResolver; + var naming = field.Context.Naming; + + configuration.Type = typeInspector.GetTypeRef(typeof(global::TestNamespace.Author), HotChocolate.Types.TypeContext.Output); + configuration.ResultType = typeof(global::TestNamespace.Author); + + configuration.SetSourceGeneratorFlags(); + + var bindingInfo = field.Context.ParameterBindingResolver; + var parameter = context.Resolvers.CreateParameterDescriptor_GetAuthorAsync_version(); + var parameterInfo = bindingInfo.GetBindingInfo(parameter); + + if(parameterInfo.Kind is global::HotChocolate.Internal.ArgumentKind.Argument) + { + var argumentConfiguration = new global::HotChocolate.Types.Descriptors.Configurations.ArgumentConfiguration + { + Name = naming.GetMemberName("version", global::HotChocolate.Types.MemberKind.Argument), + Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Input), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))), + RuntimeType = typeof(int) + }; + + configuration.Arguments.Add(argumentConfiguration); + } + + configuration.Member = context.ThisType.GetMethod( + "GetAuthorAsync", + global::HotChocolate.Utilities.ReflectionUtils.StaticMemberFlags, + new global::System.Type[] + { + typeof(global::TestNamespace.Book), + typeof(int), + typeof(global::System.Threading.CancellationToken) + })!; + + var fieldDescriptor = global::HotChocolate.Types.Descriptors.ObjectFieldDescriptor.From(field.Context, configuration); + HotChocolate.Internal.ConfigurationHelper.ApplyConfiguration( + field.Context, + fieldDescriptor, + configuration.Member, + new global::HotChocolate.Types.BindMemberAttribute("AuthorId")); + configuration.ConfigurationsAreApplied = true; + fieldDescriptor.CreateConfiguration(); + + configuration.Resolvers = context.Resolvers.GetAuthorAsync(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + private readonly global::HotChocolate.Internal.IParameterBinding _binding_GetAuthorAsync_version; + + public __Resolvers(global::HotChocolate.Resolvers.ParameterBindingResolver bindingResolver) + { + _binding_GetAuthorAsync_version = bindingResolver.GetBinding(CreateParameterDescriptor_GetAuthorAsync_version()); + } + + public global::HotChocolate.Internal.ParameterDescriptor CreateParameterDescriptor_GetAuthorAsync_version() + => new HotChocolate.Internal.ParameterDescriptor( + "version", + typeof(int), + isNullable: false, + [ + new global::TestNamespace.FooAttribute(global::TestNamespace.Visibility.Internal) { NamedVisibility = global::TestNamespace.Visibility.Private, AccessFlags = (global::TestNamespace.Access)3 } + ]); + + public HotChocolate.Resolvers.FieldResolverDelegates GetAuthorAsync() + => new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: GetAuthorAsync); + + private async global::System.Threading.Tasks.ValueTask GetAuthorAsync(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = context.Parent(); + var args1 = _binding_GetAuthorAsync_version.Execute(context); + var args2 = context.RequestAborted; + var result = await global::TestNamespace.BookNode.GetAuthorAsync(args0, args1, args2); + return result; + } + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.BookNode", + () => global::TestNamespace.BookNode.Initialize)); + builder.AddType>(); + return builder; + } + } +} + +```