Skip to content

Commit

Permalink
Added Interface Field Inheritance (#7237)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib authored Jul 9, 2024
1 parent 20e69df commit 5536d20
Show file tree
Hide file tree
Showing 41 changed files with 2,458 additions and 169 deletions.
19 changes: 19 additions & 0 deletions src/HotChocolate/Core/src/Types.Analyzers/Errors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@ public static class Errors
DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor InterfaceTypePartialKeywordMissing =
new(
id: "HC0080",
title: "Partial Keyword Missing.",
messageFormat: "A split object type class needs to be a partial class.",
category: "TypeSystem",
DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor InterfaceTypeStaticKeywordMissing =
new(
id: "HC0081",
title: "Static Keyword Missing.",
messageFormat: "A split object type class needs to be a static class.",
category: "TypeSystem",
DiagnosticSeverity.Error,
isEnabledByDefault: true);


public static readonly DiagnosticDescriptor TooManyNodeResolverArguments =
new(
id: "HCXXXX",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using HotChocolate.Types.Analyzers.Models;

namespace HotChocolate.Types.Analyzers.FileBuilders;

public interface IOutputTypeFileBuilder
{
void WriteHeader();

void WriteBeginNamespace();
void WriteEndNamespace();

string WriteBeginClass(string typeName);
void WriteEndClass();

void WriteInitializeMethod(IOutputTypeInfo typeInfo);

void WriteConfigureMethod(IOutputTypeInfo typeInfo);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using System.Text;
using HotChocolate.Types.Analyzers.Helpers;
using HotChocolate.Types.Analyzers.Models;

namespace HotChocolate.Types.Analyzers.FileBuilders;

public sealed class InterfaceTypeExtensionFileBuilder(StringBuilder sb, string ns) : IOutputTypeFileBuilder
{
private readonly CodeWriter _writer = new(sb);

public void WriteHeader()
{
_writer.WriteFileHeader();
_writer.WriteIndentedLine("using Microsoft.Extensions.DependencyInjection;");
_writer.WriteLine();
}

public void WriteBeginNamespace()
{
_writer.WriteIndentedLine("namespace {0}", ns);
_writer.WriteIndentedLine("{");
_writer.IncreaseIndent();
}

public void WriteEndNamespace()
{
_writer.DecreaseIndent();
_writer.WriteIndentedLine("}");
_writer.WriteLine();
}

public string WriteBeginClass(string typeName)
{
_writer.WriteIndentedLine("public static partial class {0}", typeName);
_writer.WriteIndentedLine("{");
_writer.IncreaseIndent();
return typeName;
}

public void WriteEndClass()
{
_writer.DecreaseIndent();
_writer.WriteIndentedLine("}");
}

public void WriteInitializeMethod(IOutputTypeInfo typeInfo)
{
_writer.WriteIndentedLine(
"internal static void Initialize(global::HotChocolate.Types.IInterfaceTypeDescriptor<{0}> descriptor)",
typeInfo.RuntimeType.ToFullyQualified());
_writer.WriteIndentedLine("{");

using (_writer.IncreaseIndent())
{
if (typeInfo.Resolvers.Length > 0)
{
_writer.WriteIndentedLine("const global::{0} bindingFlags =", WellKnownTypes.BindingFlags);
using (_writer.IncreaseIndent())
{
_writer.WriteIndentedLine("global::{0}.Public", WellKnownTypes.BindingFlags);
using (_writer.IncreaseIndent())
{
_writer.WriteIndentedLine("| global::{0}.NonPublic", WellKnownTypes.BindingFlags);
_writer.WriteIndentedLine("| global::{0}.Static;", WellKnownTypes.BindingFlags);
}
}

_writer.WriteLine();

_writer.WriteIndentedLine(
"var thisType = typeof({0});",
typeInfo.Type.ToFullyQualified());
_writer.WriteIndentedLine(
"var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver;");
_writer.WriteIndentedLine(
"global::{0}Resolvers.InitializeBindings(bindingResolver);",
typeInfo.Type.ToDisplayString());
}

if (typeInfo.Resolvers.Length > 0)
{
foreach (var resolver in typeInfo.Resolvers)
{
_writer.WriteLine();
_writer.WriteIndentedLine("descriptor");

using (_writer.IncreaseIndent())
{
_writer.WriteIndentedLine(
".Field(thisType.GetMember(\"{0}\", bindingFlags)[0])",
resolver.Member.Name);

_writer.WriteIndentedLine(".ExtendWith(c =>");
_writer.WriteIndentedLine("{");
using (_writer.IncreaseIndent())
{
_writer.WriteIndentedLine("c.Definition.SetSourceGeneratorFlags();");
_writer.WriteIndentedLine(
"c.Definition.Resolvers = {0}Resolvers.{1}_{2}();",
typeInfo.Type.ToFullyQualified(),
typeInfo.Type.Name,
resolver.Member.Name);

if (resolver.ResultKind is not ResolverResultKind.Pure
&& !resolver.Member.HasPostProcessorAttribute()
&& resolver.Member.IsListType(out var elementType))
{
_writer.WriteIndentedLine(
"c.Definition.ResultPostProcessor = global::{0}<{1}>.Default;",
WellKnownTypes.ListPostProcessor,
elementType);
}
}
_writer.WriteIndentedLine("});");
}
}
}

_writer.WriteLine();
_writer.WriteIndentedLine("Configure(descriptor);");
}

_writer.WriteIndentedLine("}");
}

public void WriteConfigureMethod(IOutputTypeInfo typeInfo)
{
_writer.WriteIndentedLine(
"static partial void Configure(global::HotChocolate.Types.IInterfaceTypeDescriptor<{0}> descriptor);",
typeInfo.RuntimeType.ToFullyQualified());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,23 @@ public void WriteRegisterTypeExtension(string typeName, bool staticType)
public void WriteRegisterObjectTypeExtension(string runtimeTypeName, string extensionType)
{
_writer.WriteIndentedLine(
"AddTypeExtension_8734371<{0}>(builder, {1}.Initialize);",
"AddObjectTypeExtension_8734371<{0}>(builder, {1}.Initialize);",
runtimeTypeName,
extensionType);
}

public void WriteRegisterInterfaceTypeExtension(string runtimeTypeName, string extensionType)
{
_writer.WriteIndentedLine(
"AddInterfaceTypeExtension_8734371<{0}>(builder, {1}.Initialize);",
runtimeTypeName,
extensionType);
}

public void WriteRegisterObjectTypeExtensionHelpers()
{
_writer.WriteLine();
_writer.WriteIndentedLine("private static void AddTypeExtension_8734371<T>(");
_writer.WriteIndentedLine("private static void AddObjectTypeExtension_8734371<T>(");

using (_writer.IncreaseIndent())
{
Expand Down Expand Up @@ -171,6 +179,90 @@ public void WriteRegisterObjectTypeExtensionHelpers()
_writer.WriteIndentedLine("}");
}

public void WriteRegisterInterfaceTypeExtensionHelpers()
{
_writer.WriteLine();
_writer.WriteIndentedLine("private static void AddInterfaceTypeExtension_8734371<T>(");

using (_writer.IncreaseIndent())
{
_writer.WriteIndentedLine("global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder,");
_writer.WriteIndentedLine("Action<IInterfaceTypeDescriptor<T>> initialize)");
}

_writer.WriteIndentedLine("{");

using (_writer.IncreaseIndent())
{
_writer.WriteIndentedLine("builder.ConfigureSchema(sb =>");
_writer.WriteIndentedLine("{");

using (_writer.IncreaseIndent())
{
_writer.WriteIndentedLine("string typeName = typeof(T).FullName!;");
_writer.WriteIndentedLine("string typeKey = $\"8734371_Type_InterfaceType<{typeName}>\";");
_writer.WriteIndentedLine("string hooksKey = $\"8734371_Hooks_InterfaceType<{typeName}>\";");
_writer.WriteLine();
_writer.WriteIndentedLine("if (!sb.ContextData.ContainsKey(typeKey))");
_writer.WriteIndentedLine("{");

using (_writer.IncreaseIndent())
{
_writer.WriteIndentedLine("sb.AddInterfaceType<T>(");
using (_writer.IncreaseIndent())
{
_writer.WriteIndentedLine("descriptor =>");
_writer.WriteIndentedLine("{");

using (_writer.IncreaseIndent())
{
_writer.WriteIndentedLine(
"var hooks = (global::System.Collections.Generic.List<" +
"Action<IInterfaceTypeDescriptor<T>>>)" +
"descriptor.Extend().Context.ContextData[hooksKey]!;");
_writer.WriteIndentedLine("foreach (var configure in hooks)");
_writer.WriteIndentedLine("{");

using (_writer.IncreaseIndent())
{
_writer.WriteIndentedLine("configure(descriptor);");
}

_writer.WriteIndentedLine("};");
}

_writer.WriteIndentedLine("});");
}

_writer.WriteIndentedLine("sb.ContextData.Add(typeKey, null);");
}

_writer.WriteIndentedLine("}");
_writer.WriteLine();

_writer.WriteIndentedLine("if (!sb.ContextData.TryGetValue(hooksKey, out var value))");
_writer.WriteIndentedLine("{");

using (_writer.IncreaseIndent())
{
_writer.WriteIndentedLine(
"value = new System.Collections.Generic.List<Action<IInterfaceTypeDescriptor<T>>>();");
_writer.WriteIndentedLine("sb.ContextData.Add(hooksKey, value);");
}

_writer.WriteIndentedLine("}");
_writer.WriteLine();
_writer.WriteIndentedLine(
"((System.Collections.Generic.List<Action<IInterfaceTypeDescriptor<T>>>)value!)" +
".Add(initialize);");
}

_writer.WriteIndentedLine("});");
}

_writer.WriteIndentedLine("}");
}

public void WriteRegisterDataLoader(string typeName)
=> _writer.WriteIndentedLine("builder.AddDataLoader<global::{0}>();", typeName);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace HotChocolate.Types.Analyzers.FileBuilders;

public sealed class ObjectTypeExtensionFileBuilder(StringBuilder sb, string ns)
public sealed class ObjectTypeExtensionFileBuilder(StringBuilder sb, string ns) : IOutputTypeFileBuilder
{
private readonly CodeWriter _writer = new(sb);

Expand Down Expand Up @@ -43,8 +43,13 @@ public void WriteEndClass()
_writer.WriteIndentedLine("}");
}

public void WriteInitializeMethod(ObjectTypeExtensionInfo objectTypeExtension)
public void WriteInitializeMethod(IOutputTypeInfo typeInfo)
{
if (typeInfo is not ObjectTypeExtensionInfo objectTypeExtension)
{
return;
}

_writer.WriteIndentedLine(
"internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor<{0}> descriptor)",
objectTypeExtension.RuntimeType.ToFullyQualified());
Expand Down Expand Up @@ -126,6 +131,7 @@ public void WriteInitializeMethod(ObjectTypeExtensionInfo objectTypeExtension)
elementType);
}
}

_writer.WriteIndentedLine("});");
}
}
Expand All @@ -138,10 +144,10 @@ public void WriteInitializeMethod(ObjectTypeExtensionInfo objectTypeExtension)
_writer.WriteIndentedLine("}");
}

public void WriteConfigureMethod(ObjectTypeExtensionInfo objectTypeExtension)
public void WriteConfigureMethod(IOutputTypeInfo typeInfo)
{
_writer.WriteIndentedLine(
"static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor<{0}> descriptor);",
objectTypeExtension.RuntimeType.ToFullyQualified());
typeInfo.RuntimeType.ToFullyQualified());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ private static void WriteConfiguration(
generator.WriteBeginRegistrationMethod();

var operations = OperationType.No;
var hasObjectTypeExtensions = false;
var hasInterfaceTypes = false;

foreach (var syntaxInfo in syntaxInfos)
{
Expand Down Expand Up @@ -119,11 +121,24 @@ private static void WriteConfiguration(
ModuleOptions.RegisterTypes &&
objectTypeExtension.Diagnostics.Length == 0)
{
hasObjectTypeExtensions = true;
generator.WriteRegisterObjectTypeExtension(
objectTypeExtension.RuntimeType.ToFullyQualified(),
objectTypeExtension.Type.ToFullyQualified());
}
break;

case InterfaceTypeExtensionInfo interfaceType:
if ((module.Options & ModuleOptions.RegisterTypes) ==
ModuleOptions.RegisterTypes &&
interfaceType.Diagnostics.Length == 0)
{
hasInterfaceTypes = true;
generator.WriteRegisterInterfaceTypeExtension(
interfaceType.RuntimeType.ToFullyQualified(),
interfaceType.Type.ToFullyQualified());
}
break;
}
}

Expand All @@ -144,11 +159,16 @@ private static void WriteConfiguration(

generator.WriteEndRegistrationMethod();

if (syntaxInfos.OfType<ObjectTypeExtensionInfo>().Any())
if (hasObjectTypeExtensions)
{
generator.WriteRegisterObjectTypeExtensionHelpers();
}

if (hasInterfaceTypes)
{
generator.WriteRegisterInterfaceTypeExtensionHelpers();
}

generator.WriteEndClass();
generator.WriteEndNamespace();

Expand Down
Loading

0 comments on commit 5536d20

Please sign in to comment.