-
-
Notifications
You must be signed in to change notification settings - Fork 723
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reworked Types Source Generator (#5040)
- Loading branch information
1 parent
f9dd6a6
commit 1096ec7
Showing
21 changed files
with
657 additions
and
262 deletions.
There are no files selected for viewing
23 changes: 23 additions & 0 deletions
23
src/HotChocolate/Core/src/Types.Analyzers/Generators/ISyntaxGenerator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using HotChocolate.Types.Analyzers.Inspectors; | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace HotChocolate.Types.Analyzers.Generators; | ||
|
||
/// <summary> | ||
/// A syntax generator produces C# code from the consumed syntax infos. | ||
/// </summary> | ||
public interface ISyntaxGenerator | ||
{ | ||
/// <summary> | ||
/// Specifies if the given <paramref name="syntaxInfo"/> will be consumed by this generator. | ||
/// </summary> | ||
bool Consume(ISyntaxInfo syntaxInfo); | ||
|
||
/// <summary> | ||
/// Generates the C# source code for the consumed syntax infos. | ||
/// </summary> | ||
void Generate( | ||
SourceProductionContext context, | ||
Compilation compilation, | ||
IReadOnlyCollection<ISyntaxInfo> consumed); | ||
} |
110 changes: 110 additions & 0 deletions
110
src/HotChocolate/Core/src/Types.Analyzers/Generators/ModuleGenerator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
using System.Text; | ||
using HotChocolate.Types.Analyzers.Inspectors; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Text; | ||
using static HotChocolate.Types.Analyzers.StringConstants; | ||
using static HotChocolate.Types.Analyzers.WellKnownFileNames; | ||
using TypeInfo = HotChocolate.Types.Analyzers.Inspectors.TypeInfo; | ||
|
||
namespace HotChocolate.Types.Analyzers.Generators; | ||
|
||
public class ModuleGenerator : ISyntaxGenerator | ||
{ | ||
public bool Consume(ISyntaxInfo syntaxInfo) | ||
=> syntaxInfo is TypeInfo or TypeExtensionInfo or DataLoaderInfo or ModuleInfo; | ||
|
||
public void Generate( | ||
SourceProductionContext context, | ||
Compilation compilation, | ||
IReadOnlyCollection<ISyntaxInfo> syntaxInfos) | ||
{ | ||
ModuleInfo module = | ||
syntaxInfos.OfType<ModuleInfo>().FirstOrDefault() ?? | ||
new ModuleInfo( | ||
compilation.AssemblyName is null | ||
? "AssemblyTypes" | ||
: compilation.AssemblyName?.Split('.').Last() + "Types", | ||
ModuleOptions.Default); | ||
|
||
var batch = new List<ISyntaxInfo>(syntaxInfos.Where(static t => t is not ModuleInfo)); | ||
if (batch.Count == 0) | ||
{ | ||
return; | ||
} | ||
|
||
var code = new StringBuilder(); | ||
code.AppendLine("using System;"); | ||
code.AppendLine("using HotChocolate.Execution.Configuration;"); | ||
|
||
code.AppendLine(); | ||
code.AppendLine("namespace Microsoft.Extensions.DependencyInjection"); | ||
code.AppendLine("{"); | ||
|
||
code.Append(Indent) | ||
.Append("public static class ") | ||
.Append(module.ModuleName) | ||
.AppendLine("RequestExecutorBuilderExtensions"); | ||
|
||
code.Append(Indent) | ||
.AppendLine("{"); | ||
|
||
code.Append(Indent) | ||
.Append(Indent) | ||
.Append("public static IRequestExecutorBuilder Add") | ||
.Append(module.ModuleName) | ||
.AppendLine("(this IRequestExecutorBuilder builder)"); | ||
|
||
code.Append(Indent).Append(Indent).AppendLine("{"); | ||
|
||
foreach (ISyntaxInfo syntaxInfo in batch.Distinct()) | ||
{ | ||
switch (syntaxInfo) | ||
{ | ||
case TypeInfo type: | ||
if ((module.Options & ModuleOptions.RegisterTypes) == | ||
ModuleOptions.RegisterTypes) | ||
{ | ||
code.Append(Indent) | ||
.Append(Indent) | ||
.Append(Indent) | ||
.Append("builder.AddType<") | ||
.Append(type.Name) | ||
.AppendLine(">();"); | ||
} | ||
break; | ||
|
||
case TypeExtensionInfo extension: | ||
if ((module.Options & ModuleOptions.RegisterTypes) == | ||
ModuleOptions.RegisterTypes) | ||
{ | ||
code.Append(Indent) | ||
.Append(Indent) | ||
.Append(Indent) | ||
.Append("builder.AddTypeExtension<") | ||
.Append(extension.Name) | ||
.AppendLine(">();"); | ||
} | ||
break; | ||
|
||
case DataLoaderInfo dataLoader: | ||
if ((module.Options & ModuleOptions.RegisterDataLoader) == | ||
ModuleOptions.RegisterDataLoader) | ||
{ | ||
code.Append(Indent) | ||
.Append(Indent) | ||
.Append(Indent) | ||
.Append("builder.AddDataLoader<") | ||
.Append(dataLoader.Name) | ||
.AppendLine(">();"); | ||
} | ||
break; | ||
} | ||
} | ||
code.Append(Indent).Append(Indent).Append(Indent).AppendLine("return builder;"); | ||
code.Append(Indent).Append(Indent).AppendLine("}"); | ||
code.Append(Indent).AppendLine("}"); | ||
code.AppendLine("}"); | ||
|
||
context.AddSource(TypeModuleFile, SourceText.From(code.ToString(), Encoding.UTF8)); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
src/HotChocolate/Core/src/Types.Analyzers/Inspectors/AggregateInfo.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
namespace HotChocolate.Types.Analyzers.Inspectors; | ||
|
||
public sealed class AggregateInfo : ISyntaxInfo, IEquatable<AggregateInfo> | ||
{ | ||
private readonly IReadOnlyList<ISyntaxInfo> _syntaxInfos; | ||
|
||
public AggregateInfo(IReadOnlyList<ISyntaxInfo> syntaxInfos) | ||
{ | ||
_syntaxInfos = syntaxInfos; | ||
} | ||
|
||
public IReadOnlyList<ISyntaxInfo> Items => _syntaxInfos; | ||
|
||
public bool Equals(AggregateInfo? other) | ||
{ | ||
if (ReferenceEquals(null, other)) | ||
{ | ||
return false; | ||
} | ||
|
||
if (ReferenceEquals(this, other)) | ||
{ | ||
return true; | ||
} | ||
|
||
return _syntaxInfos == other._syntaxInfos; | ||
} | ||
|
||
public override bool Equals(object? obj) | ||
=> ReferenceEquals(this, obj) || | ||
obj is AggregateInfo other && Equals(other); | ||
|
||
public override int GetHashCode() | ||
=> _syntaxInfos.GetHashCode(); | ||
|
||
public static bool operator ==(AggregateInfo? left, AggregateInfo? right) | ||
=> Equals(left, right); | ||
|
||
public static bool operator !=(AggregateInfo? left, AggregateInfo? right) | ||
=> !Equals(left, right); | ||
} |
74 changes: 74 additions & 0 deletions
74
src/HotChocolate/Core/src/Types.Analyzers/Inspectors/ClassBaseClassInspector.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
using System.Diagnostics.CodeAnalysis; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
namespace HotChocolate.Types.Analyzers.Inspectors; | ||
|
||
public class ClassBaseClassInspector : ISyntaxInspector | ||
{ | ||
public bool TryHandle( | ||
GeneratorSyntaxContext context, | ||
[NotNullWhen(true)] out ISyntaxInfo? syntaxInfo) | ||
{ | ||
if (context.Node is ClassDeclarationSyntax { BaseList.Types.Count: > 0 } possibleType) | ||
{ | ||
var model = context.SemanticModel.GetDeclaredSymbol(possibleType); | ||
if (model is { IsAbstract: false } type) | ||
{ | ||
var typeDisplayString = type.ToDisplayString(); | ||
var processing = new Queue<INamedTypeSymbol>(); | ||
processing.Enqueue(type); | ||
|
||
var current = type.BaseType; | ||
|
||
while (current is not null) | ||
{ | ||
processing.Enqueue(current); | ||
|
||
var displayString = current.ToDisplayString(); | ||
|
||
if (displayString.Equals(WellKnownTypes.SystemObject, StringComparison.Ordinal)) | ||
{ | ||
break; | ||
} | ||
|
||
if (WellKnownTypes.TypeClass.Contains(displayString)) | ||
{ | ||
syntaxInfo = new TypeInfo(typeDisplayString); | ||
return true; | ||
} | ||
|
||
if (WellKnownTypes.TypeExtensionClass.Contains(displayString)) | ||
{ | ||
syntaxInfo = new TypeExtensionInfo(typeDisplayString); | ||
return true; | ||
} | ||
|
||
current = current.BaseType; | ||
} | ||
|
||
while (processing.Count > 0) | ||
{ | ||
current = processing.Dequeue(); | ||
|
||
var displayString = current.ToDisplayString(); | ||
|
||
if (displayString.Equals(WellKnownTypes.DataLoader, StringComparison.Ordinal)) | ||
{ | ||
syntaxInfo = new DataLoaderInfo(typeDisplayString); | ||
return true; | ||
} | ||
|
||
foreach (var interfaceType in current.Interfaces) | ||
{ | ||
processing.Enqueue(interfaceType); | ||
} | ||
} | ||
} | ||
} | ||
|
||
syntaxInfo = null; | ||
return false; | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
src/HotChocolate/Core/src/Types.Analyzers/Inspectors/DataLoaderInfo.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
namespace HotChocolate.Types.Analyzers.Inspectors; | ||
|
||
public sealed class DataLoaderInfo : ISyntaxInfo, IEquatable<DataLoaderInfo> | ||
{ | ||
public DataLoaderInfo(string name) | ||
{ | ||
Name = name; | ||
} | ||
|
||
public string Name { get; } | ||
|
||
public bool Equals(DataLoaderInfo? other) | ||
{ | ||
if (ReferenceEquals(null, other)) | ||
{ | ||
return false; | ||
} | ||
|
||
if (ReferenceEquals(this, other)) | ||
{ | ||
return true; | ||
} | ||
|
||
return Name == other.Name; | ||
} | ||
|
||
public override bool Equals(object? obj) | ||
=> ReferenceEquals(this, obj) || | ||
obj is DataLoaderInfo other && Equals(other); | ||
|
||
public override int GetHashCode() | ||
=> Name.GetHashCode(); | ||
|
||
public static bool operator ==(DataLoaderInfo? left, DataLoaderInfo? right) | ||
=> Equals(left, right); | ||
|
||
public static bool operator !=(DataLoaderInfo? left, DataLoaderInfo? right) | ||
=> !Equals(left, right); | ||
} |
5 changes: 5 additions & 0 deletions
5
src/HotChocolate/Core/src/Types.Analyzers/Inspectors/ISyntaxInfo.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
namespace HotChocolate.Types.Analyzers.Inspectors; | ||
|
||
public interface ISyntaxInfo | ||
{ | ||
} |
22 changes: 22 additions & 0 deletions
22
src/HotChocolate/Core/src/Types.Analyzers/Inspectors/ISyntaxInspector.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using System.Diagnostics.CodeAnalysis; | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace HotChocolate.Types.Analyzers.Inspectors; | ||
|
||
/// <summary> | ||
/// The syntax inspector will analyze a syntax node and try to reason out the semantics in a | ||
/// Hot Chocolate server context. | ||
/// </summary> | ||
public interface ISyntaxInspector | ||
{ | ||
/// <summary> | ||
/// <para> | ||
/// Inspects the current syntax node and if the current inspector can handle | ||
/// the syntax will produce a syntax info. | ||
/// </para> | ||
/// <para>The syntax info is used by a syntax generator to produce source code.</para> | ||
/// </summary> | ||
bool TryHandle( | ||
GeneratorSyntaxContext context, | ||
[NotNullWhen(true)] out ISyntaxInfo? syntaxInfo); | ||
} |
47 changes: 47 additions & 0 deletions
47
src/HotChocolate/Core/src/Types.Analyzers/Inspectors/ModuleInfo.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
namespace HotChocolate.Types.Analyzers.Inspectors; | ||
|
||
public sealed class ModuleInfo : ISyntaxInfo, IEquatable<ModuleInfo> | ||
{ | ||
public ModuleInfo(string moduleName, ModuleOptions options) | ||
{ | ||
ModuleName = moduleName; | ||
Options = options; | ||
} | ||
|
||
public string ModuleName { get; } | ||
|
||
public ModuleOptions Options { get; } | ||
|
||
public bool Equals(ModuleInfo? other) | ||
{ | ||
if (ReferenceEquals(null, other)) | ||
{ | ||
return false; | ||
} | ||
|
||
if (ReferenceEquals(this, other)) | ||
{ | ||
return true; | ||
} | ||
|
||
return ModuleName == other.ModuleName && Options == other.Options; | ||
} | ||
|
||
public override bool Equals(object? obj) | ||
=> ReferenceEquals(this, obj) || | ||
(obj is ModuleInfo other && Equals(other)); | ||
|
||
public override int GetHashCode() | ||
{ | ||
unchecked | ||
{ | ||
return (ModuleName.GetHashCode() * 397) ^ (int)Options; | ||
} | ||
} | ||
|
||
public static bool operator ==(ModuleInfo? left, ModuleInfo? right) | ||
=> Equals(left, right); | ||
|
||
public static bool operator !=(ModuleInfo? left, ModuleInfo? right) | ||
=> !Equals(left, right); | ||
} |
Oops, something went wrong.