Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/HotChocolate/Core/src/Abstractions/ModuleOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,12 @@ public enum ModuleOptions
/// <summary>
/// Include internal resolver members when discovering source generated types.
/// </summary>
IncludeInternalMembers = 4
IncludeInternalMembers = 4,

/// <summary>
/// Disable XML documentation comment extraction for source generated types.
/// When set, only explicit <see cref="GraphQLDescriptionAttribute"/> values
/// will be used as descriptions in the generated schema.
/// </summary>
DisableXmlDocumentation = 8
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using HotChocolate.Types.Analyzers.Models;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -88,6 +89,32 @@ public static bool TryGetGraphQLTypeName(
this Compilation compilation,
ISymbol symbol)
{
if (compilation.DisableXmlDocumentation())
{
switch (symbol)
{
case IPropertySymbol property:
return new PropertyDescription(property.GetDescriptionFromAttribute());

case IMethodSymbol method:
var paramDescs = ImmutableArray.CreateBuilder<string?>(method.Parameters.Length);
foreach (var p in method.Parameters)
{
paramDescs.Add(p.GetDescriptionFromAttribute());
}

return new MethodDescription(
method.GetDescriptionFromAttribute(),
paramDescs.ToImmutable());

case IParameterSymbol parameter:
return new ParameterDescription(parameter.GetDescriptionFromAttribute());

default:
return null;
}
}

switch (symbol)
{
case IPropertySymbol property:
Expand All @@ -104,6 +131,18 @@ public static bool TryGetGraphQLTypeName(
}
}

public static string? GetDescription(
this Compilation compilation,
INamedTypeSymbol type)
{
if (compilation.DisableXmlDocumentation())
{
return type.GetDescriptionFromAttribute();
}

return type.GetDescription(compilation);
}

public static string? GetDeprecationReason(this Compilation compilation, ISymbol symbol)
{
var graphQLDeprecatedAttribute = compilation.GetTypeByMetadataName(WellKnownAttributes.GraphQLDeprecatedAttribute);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ public static bool IncludeInternalMembers(this Compilation compilation)
=> (GetModuleOptions(compilation) & ModuleOptions.IncludeInternalMembers)
== ModuleOptions.IncludeInternalMembers;

public static bool DisableXmlDocumentation(this Compilation compilation)
=> (GetModuleOptions(compilation) & ModuleOptions.DisableXmlDocumentation)
== ModuleOptions.DisableXmlDocumentation;

private static ModuleOptions GetModuleOptions(Compilation compilation)
{
foreach (var attribute in compilation.Assembly.GetAttributes())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public bool TryHandle(GeneratorSyntaxContext context, [NotNullWhen(true)] out Sy
}

var interfaceTypeInfo = new InterfaceTypeInfo(
context.SemanticModel.Compilation,
classSymbol,
runtimeType,
possibleType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ or Accessibility.ProtectedOrInternal
if (runtimeType is not null)
{
var objectTypeInfo = new ObjectTypeInfo(
context.SemanticModel.Compilation,
classSymbol,
runtimeType,
nodeResolver,
Expand All @@ -139,6 +140,7 @@ or Accessibility.ProtectedOrInternal
}

var rootType = new RootTypeInfo(
context.SemanticModel.Compilation,
classSymbol,
operationType!.Value,
possibleType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ private static EdgeTypeInfo Create(
edgeName,
nameFormat,
@namespace,
runtimeType.GetDescription(),
compilation.GetDescription(runtimeType),
runtimeType,
classDeclaration,
resolvers.ToImmutable(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace HotChocolate.Types.Analyzers.Models;
public sealed class InterfaceTypeInfo : SyntaxInfo, IOutputTypeInfo
{
public InterfaceTypeInfo(
Compilation compilation,
INamedTypeSymbol schemaType,
INamedTypeSymbol runtimeType,
ClassDeclarationSyntax classDeclarationSyntax,
Expand All @@ -20,7 +21,7 @@ public InterfaceTypeInfo(
RuntimeTypeFullName = runtimeType.ToDisplayString();
ClassDeclaration = classDeclarationSyntax;
Resolvers = resolvers;
Description = schemaType.GetDescription();
Description = compilation.GetDescription(schemaType);
// sharable directives are only allowed on object types and field definitions
Shareable = DirectiveScope.None;
Attributes = attributes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ public enum ModuleOptions
RegisterTypes = 1,
RegisterDataLoader = 2,
IncludeInternalMembers = 4,
Disabled = 8
DisableXmlDocumentation = 8,
Disabled = 16
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public sealed class ObjectTypeInfo
, IOutputTypeInfo
{
public ObjectTypeInfo(
Compilation compilation,
INamedTypeSymbol schemaType,
INamedTypeSymbol runtimeType,
Resolver? nodeResolver,
Expand All @@ -24,7 +25,7 @@ public ObjectTypeInfo(
NodeResolver = nodeResolver;
ClassDeclaration = classDeclarationSyntax;
Resolvers = resolvers;
Description = schemaType.GetDescription();
Description = compilation.GetDescription(schemaType);
Attributes = attributes;
Shareable = attributes.GetShareableScope();
Inaccessible = attributes.GetInaccessibleScope();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public sealed class RootTypeInfo
, IOutputTypeInfo
{
public RootTypeInfo(
Compilation compilation,
INamedTypeSymbol schemaType,
OperationType operationType,
ClassDeclarationSyntax classDeclarationSyntax,
Expand All @@ -21,7 +22,7 @@ public RootTypeInfo(
SchemaTypeFullName = schemaType.ToDisplayString();
ClassDeclaration = classDeclarationSyntax;
Resolvers = resolvers;
Description = schemaType.GetDescription();
Description = compilation.GetDescription(schemaType);
Attributes = attributes;
Shareable = attributes.GetShareableScope();
Inaccessible = attributes.GetInaccessibleScope();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.Text.RegularExpressions;

namespace HotChocolate.Types;

public partial class ObjectTypeDisableXmlDocumentationTests
{
private static readonly Regex s_description = DescriptionExtractorRegex();

[Fact]
public void XmlDocumentation_Is_Suppressed_When_DisableXmlDocumentation_Is_Set()
{
var snapshot =
TestHelper.GetGeneratedSourceSnapshot(
"""
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using HotChocolate;
using HotChocolate.Types;

[assembly: Module("Test", ModuleOptions.Default | ModuleOptions.DisableXmlDocumentation)]

namespace TestNamespace;

[QueryType]
internal static partial class Query
{
/// <summary>
/// This should NOT appear in the schema.
/// </summary>
public static string GetUser() => "User";
}
""");

var content = snapshot.Match();
var matches = s_description.Matches(content);
Assert.Empty(matches);
}

[Fact]
public void GraphQLDescription_Attribute_Still_Works_When_DisableXmlDocumentation_Is_Set()
{
var snapshot =
TestHelper.GetGeneratedSourceSnapshot(
"""
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using HotChocolate;
using HotChocolate.Types;

[assembly: Module("Test", ModuleOptions.Default | ModuleOptions.DisableXmlDocumentation)]

namespace TestNamespace;

[QueryType]
internal static partial class Query
{
/// <summary>
/// This should NOT appear in the schema.
/// </summary>
[GraphQLDescription("Explicit description")]
public static string GetUser() => "User";
}
""");

var content = snapshot.Match();
var emitted = s_description.Matches(content).Single().Groups;
Assert.Equal("Explicit description", emitted[1].Value);
}

[Fact]
public void XmlDocumentation_Is_Emitted_When_DisableXmlDocumentation_Is_Not_Set()
{
var snapshot =
TestHelper.GetGeneratedSourceSnapshot(
"""
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using HotChocolate;
using HotChocolate.Types;

namespace TestNamespace;

[QueryType]
internal static partial class Query
{
/// <summary>
/// User description from XML doc.
/// </summary>
public static string GetUser() => "User";
}
""");

var content = snapshot.Match();
var emitted = s_description.Matches(content).Single().Groups;
Assert.Equal("User description from XML doc.", emitted[1].Value);
}

[GeneratedRegex("configuration.Description = \"(.*)\";")]
private static partial Regex DescriptionExtractorRegex();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
HotChocolateTypeModule.735550c.g.cs
---------------
// <auto-generated/>

#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 TestRequestExecutorBuilderExtensions
{
public static IRequestExecutorBuilder AddTest(this IRequestExecutorBuilder builder)
{
builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd(
"Tests::TestNamespace.Query",
global::HotChocolate.Types.OperationTypeNames.Query,
() => global::TestNamespace.Query.Initialize));
builder.ConfigureSchema(
b => b.TryAddRootType(
() => new global::HotChocolate.Types.ObjectType(
d => d.Name(global::HotChocolate.Types.OperationTypeNames.Query)),
HotChocolate.Language.OperationType.Query));
return builder;
}
}
}

---------------

Query.WaAdMHmlGJHjtEI4nqY7WA.hc.g.cs
---------------
// <auto-generated/>

#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 Query
{
internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor)
{
var extension = descriptor.Extend();
var configuration = extension.Configuration;
var thisType = typeof(global::TestNamespace.Query);
var bindingResolver = extension.Context.ParameterBindingResolver;
var resolvers = new __Resolvers();

HotChocolate.Internal.ConfigurationHelper.ApplyConfiguration(
extension.Context,
descriptor,
null,
new global::HotChocolate.Types.QueryTypeAttribute());
configuration.ConfigurationsAreApplied = true;

var naming = descriptor.Extend().Context.Naming;

descriptor
.Field(naming.GetMemberName("User", 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.Description = "Explicit description";
configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create(
typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Output),
new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("string")));
configuration.ResultType = typeof(string);

configuration.SetSourceGeneratorFlags();

configuration.Resolvers = context.Resolvers.GetUser();
},
(Resolvers: resolvers, ThisType: thisType));

Configure(descriptor);
}

static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor);

private sealed class __Resolvers
{
public HotChocolate.Resolvers.FieldResolverDelegates GetUser()
{
return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: GetUser);
}

private global::System.Object? GetUser(global::HotChocolate.Resolvers.IResolverContext context)
{
var result = global::TestNamespace.Query.GetUser();
return result;
}
}
}
}


---------------
Loading
Loading