Skip to content

Commit 3ff5a90

Browse files
authored
Fix source generator emitting enum members as numeric values (#9727)
1 parent 485752e commit 3ff5a90

3 files changed

Lines changed: 263 additions & 2 deletions

File tree

src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/TypeFileBuilderBase.cs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,8 +1978,7 @@ private static string FormatTypedConstant(TypedConstant constant)
19781978
return FormatPrimitive(constant.Value);
19791979

19801980
case TypedConstantKind.Enum:
1981-
var enumType = constant.Type?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
1982-
return $"{enumType}.{constant.Value}";
1981+
return FormatEnumConstant(constant);
19831982

19841983
case TypedConstantKind.Type:
19851984
var typeArg = ((ITypeSymbol)constant.Value!).ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
@@ -2023,6 +2022,30 @@ private static string FormatPrimitive(object? value)
20232022
};
20242023
}
20252024

2025+
private static string FormatEnumConstant(TypedConstant constant)
2026+
{
2027+
if (constant.Type is not INamedTypeSymbol enumSymbol)
2028+
{
2029+
return FormatPrimitive(constant.Value);
2030+
}
2031+
2032+
var enumType = enumSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
2033+
2034+
if (constant.Value is not null)
2035+
{
2036+
foreach (var member in enumSymbol.GetMembers())
2037+
{
2038+
if (member is IFieldSymbol { HasConstantValue: true } field
2039+
&& Equals(field.ConstantValue, constant.Value))
2040+
{
2041+
return $"{enumType}.{field.Name}";
2042+
}
2043+
}
2044+
}
2045+
2046+
return $"({enumType}){constant.Value}";
2047+
}
2048+
20262049
private static string EscapeString(string s)
20272050
{
20282051
return s.Replace("\\", "\\\\")

src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,79 @@ internal static partial class BookNode
540540
""").MatchMarkdownAsync();
541541
}
542542

543+
[Fact]
544+
public async Task CustomAttribute_With_Enum_And_Composite_Enum_Arguments_On_Parameter_MatchesSnapshot()
545+
{
546+
await TestHelper.GetGeneratedSourceSnapshot(
547+
"""
548+
using System;
549+
using System.Collections.Generic;
550+
using System.Threading;
551+
using System.Threading.Tasks;
552+
using HotChocolate;
553+
using HotChocolate.Types;
554+
555+
namespace TestNamespace;
556+
557+
public enum Visibility
558+
{
559+
Public = 0,
560+
Internal = 1,
561+
Private = 2
562+
}
563+
564+
[Flags]
565+
public enum Access
566+
{
567+
None = 0,
568+
Read = 1,
569+
Write = 2,
570+
Execute = 4
571+
}
572+
573+
public sealed class FooAttribute : Attribute
574+
{
575+
public FooAttribute(Visibility visibility)
576+
{
577+
Visibility = visibility;
578+
}
579+
580+
public Visibility Visibility { get; }
581+
582+
public Visibility NamedVisibility { get; set; }
583+
584+
public Access AccessFlags { get; set; }
585+
}
586+
587+
public sealed class Author
588+
{
589+
public int Id { get; set; }
590+
public string Name { get; set; }
591+
}
592+
593+
public sealed class Book
594+
{
595+
public int Id { get; set; }
596+
public string Title { get; set; }
597+
public int AuthorId { get; set; }
598+
}
599+
600+
[ObjectType<Book>]
601+
internal static partial class BookNode
602+
{
603+
[BindMember(nameof(Book.AuthorId))]
604+
public static Task<Author?> GetAuthorAsync(
605+
[Parent] Book book,
606+
[Foo(
607+
Visibility.Internal,
608+
NamedVisibility = Visibility.Private,
609+
AccessFlags = Access.Read | Access.Write)] int version,
610+
CancellationToken cancellationToken)
611+
=> default;
612+
}
613+
""").MatchMarkdownAsync();
614+
}
615+
543616
[Fact]
544617
public async Task GraphQLType_On_Parameter_MatchesSnapshot()
545618
{
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# CustomAttribute_With_Enum_And_Composite_Enum_Arguments_On_Parameter_MatchesSnapshot
2+
3+
## BookNode.WaAdMHmlGJHjtEI4nqY7WA.hc.g.cs
4+
5+
```csharp
6+
// <auto-generated/>
7+
8+
#nullable enable
9+
#pragma warning disable
10+
11+
using System;
12+
using System.Runtime.CompilerServices;
13+
using HotChocolate;
14+
using HotChocolate.Types;
15+
using HotChocolate.Execution.Configuration;
16+
using Microsoft.Extensions.DependencyInjection;
17+
using HotChocolate.Internal;
18+
19+
namespace TestNamespace
20+
{
21+
internal static partial class BookNode
22+
{
23+
internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor<global::TestNamespace.Book> descriptor)
24+
{
25+
var extension = descriptor.Extend();
26+
var configuration = extension.Configuration;
27+
var thisType = typeof(global::TestNamespace.BookNode);
28+
var bindingResolver = extension.Context.ParameterBindingResolver;
29+
var resolvers = new __Resolvers(bindingResolver);
30+
31+
var naming = descriptor.Extend().Context.Naming;
32+
var ignoredFields = new global::System.Collections.Generic.HashSet<string>();
33+
ignoredFields.Add(naming.GetMemberName("AuthorId", global::HotChocolate.Types.MemberKind.ObjectField));
34+
35+
foreach(string fieldName in ignoredFields)
36+
{
37+
descriptor.Field(fieldName).Ignore();
38+
}
39+
40+
descriptor
41+
.Field(naming.GetMemberName("Author", global::HotChocolate.Types.MemberKind.ObjectField))
42+
.ExtendWith(static (field, context) =>
43+
{
44+
var configuration = field.Configuration;
45+
var typeInspector = field.Context.TypeInspector;
46+
var bindingResolver = field.Context.ParameterBindingResolver;
47+
var naming = field.Context.Naming;
48+
49+
configuration.Type = typeInspector.GetTypeRef(typeof(global::TestNamespace.Author), HotChocolate.Types.TypeContext.Output);
50+
configuration.ResultType = typeof(global::TestNamespace.Author);
51+
52+
configuration.SetSourceGeneratorFlags();
53+
54+
var bindingInfo = field.Context.ParameterBindingResolver;
55+
var parameter = context.Resolvers.CreateParameterDescriptor_GetAuthorAsync_version();
56+
var parameterInfo = bindingInfo.GetBindingInfo(parameter);
57+
58+
if(parameterInfo.Kind is global::HotChocolate.Internal.ArgumentKind.Argument)
59+
{
60+
var argumentConfiguration = new global::HotChocolate.Types.Descriptors.Configurations.ArgumentConfiguration
61+
{
62+
Name = naming.GetMemberName("version", global::HotChocolate.Types.MemberKind.Argument),
63+
Type = global::HotChocolate.Types.Descriptors.TypeReference.Create(
64+
typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Input),
65+
new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))),
66+
RuntimeType = typeof(int)
67+
};
68+
69+
configuration.Arguments.Add(argumentConfiguration);
70+
}
71+
72+
configuration.Member = context.ThisType.GetMethod(
73+
"GetAuthorAsync",
74+
global::HotChocolate.Utilities.ReflectionUtils.StaticMemberFlags,
75+
new global::System.Type[]
76+
{
77+
typeof(global::TestNamespace.Book),
78+
typeof(int),
79+
typeof(global::System.Threading.CancellationToken)
80+
})!;
81+
82+
var fieldDescriptor = global::HotChocolate.Types.Descriptors.ObjectFieldDescriptor.From(field.Context, configuration);
83+
HotChocolate.Internal.ConfigurationHelper.ApplyConfiguration(
84+
field.Context,
85+
fieldDescriptor,
86+
configuration.Member,
87+
new global::HotChocolate.Types.BindMemberAttribute("AuthorId"));
88+
configuration.ConfigurationsAreApplied = true;
89+
fieldDescriptor.CreateConfiguration();
90+
91+
configuration.Resolvers = context.Resolvers.GetAuthorAsync();
92+
},
93+
(Resolvers: resolvers, ThisType: thisType));
94+
95+
Configure(descriptor);
96+
}
97+
98+
static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor<global::TestNamespace.Book> descriptor);
99+
100+
private sealed class __Resolvers
101+
{
102+
private readonly global::HotChocolate.Internal.IParameterBinding _binding_GetAuthorAsync_version;
103+
104+
public __Resolvers(global::HotChocolate.Resolvers.ParameterBindingResolver bindingResolver)
105+
{
106+
_binding_GetAuthorAsync_version = bindingResolver.GetBinding(CreateParameterDescriptor_GetAuthorAsync_version());
107+
}
108+
109+
public global::HotChocolate.Internal.ParameterDescriptor CreateParameterDescriptor_GetAuthorAsync_version()
110+
=> new HotChocolate.Internal.ParameterDescriptor(
111+
"version",
112+
typeof(int),
113+
isNullable: false,
114+
[
115+
new global::TestNamespace.FooAttribute(global::TestNamespace.Visibility.Internal) { NamedVisibility = global::TestNamespace.Visibility.Private, AccessFlags = (global::TestNamespace.Access)3 }
116+
]);
117+
118+
public HotChocolate.Resolvers.FieldResolverDelegates GetAuthorAsync()
119+
=> new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: GetAuthorAsync);
120+
121+
private async global::System.Threading.Tasks.ValueTask<global::System.Object?> GetAuthorAsync(global::HotChocolate.Resolvers.IResolverContext context)
122+
{
123+
var args0 = context.Parent<global::TestNamespace.Book>();
124+
var args1 = _binding_GetAuthorAsync_version.Execute<int>(context);
125+
var args2 = context.RequestAborted;
126+
var result = await global::TestNamespace.BookNode.GetAuthorAsync(args0, args1, args2);
127+
return result;
128+
}
129+
}
130+
}
131+
}
132+
133+
134+
```
135+
136+
## HotChocolateTypeModule.735550c.g.cs
137+
138+
```csharp
139+
// <auto-generated/>
140+
141+
#nullable enable
142+
#pragma warning disable
143+
144+
using System;
145+
using System.Runtime.CompilerServices;
146+
using HotChocolate;
147+
using HotChocolate.Types;
148+
using HotChocolate.Execution.Configuration;
149+
150+
namespace Microsoft.Extensions.DependencyInjection
151+
{
152+
public static partial class TestsTypesRequestExecutorBuilderExtensions
153+
{
154+
public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder)
155+
{
156+
builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd<global::TestNamespace.Book>(
157+
"Tests::TestNamespace.BookNode",
158+
() => global::TestNamespace.BookNode.Initialize));
159+
builder.AddType<ObjectType<global::TestNamespace.Book>>();
160+
return builder;
161+
}
162+
}
163+
}
164+
165+
```

0 commit comments

Comments
 (0)