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
7 changes: 6 additions & 1 deletion src/HotChocolate/Core/src/Abstractions/ModuleOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,10 @@ public enum ModuleOptions
/// <summary>
/// Register DataLoader with the source generated module.
/// </summary>
RegisterDataLoader = 2
RegisterDataLoader = 2,

/// <summary>
/// Include internal resolver members when discovering source generated types.
/// </summary>
IncludeInternalMembers = 4
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using HotChocolate.Types.Analyzers.Models;
using Microsoft.CodeAnalysis;
using static System.StringComparison;
using static HotChocolate.Types.Analyzers.WellKnownAttributes;

namespace HotChocolate.Types.Analyzers.Helpers;

internal static class ModuleOptionsHelper
{
public static bool IncludeInternalMembers(this Compilation compilation)
=> (GetModuleOptions(compilation) & ModuleOptions.IncludeInternalMembers)
== ModuleOptions.IncludeInternalMembers;

private static ModuleOptions GetModuleOptions(Compilation compilation)
{
foreach (var attribute in compilation.Assembly.GetAttributes())
{
if (!string.Equals(attribute.AttributeClass?.ToDisplayString(), ModuleAttribute, Ordinal))
{
continue;
}

if (attribute.ConstructorArguments.Length > 1
&& attribute.ConstructorArguments[1].Value is int optionsValue)
{
return (ModuleOptions)optionsValue;
}

return ModuleOptions.Default;
}

return ModuleOptions.Default;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class InterfaceTypeInfoInspector : ISyntaxInspector
public bool TryHandle(GeneratorSyntaxContext context, [NotNullWhen(true)] out SyntaxInfo? syntaxInfo)
{
var diagnostics = ImmutableArray<Diagnostic>.Empty;
var includeInternalMembers = context.SemanticModel.Compilation.IncludeInternalMembers();

if (!IsInterfaceType(context, out var possibleType, out var classSymbol, out var runtimeType))
{
Expand Down Expand Up @@ -48,10 +49,7 @@ public bool TryHandle(GeneratorSyntaxContext context, [NotNullWhen(true)] out Sy

foreach (var member in members)
{
if (member.DeclaredAccessibility is
Accessibility.Public or
Accessibility.Internal or
Accessibility.ProtectedAndInternal)
if (IsVisibleResolverMember(member, includeInternalMembers))
{
if (member is IMethodSymbol { MethodKind: MethodKind.Ordinary } methodSymbol)
{
Expand Down Expand Up @@ -99,6 +97,16 @@ Accessibility.Internal or
return true;
}

private static bool IsVisibleResolverMember(ISymbol member, bool includeInternalMembers)
=> member.DeclaredAccessibility switch
{
Accessibility.Public => true,
Accessibility.Internal => includeInternalMembers,
Accessibility.ProtectedOrInternal => includeInternalMembers,
Accessibility.ProtectedAndInternal => includeInternalMembers,
_ => false
};

private static bool IsInterfaceType(
GeneratorSyntaxContext context,
[NotNullWhen(true)] out ClassDeclarationSyntax? resolverTypeSyntax,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public bool TryHandle(GeneratorSyntaxContext context, [NotNullWhen(true)] out Sy
{
var diagnostics = ImmutableArray<Diagnostic>.Empty;
var isOperationType = false;
var includeInternalMembers = context.SemanticModel.Compilation.IncludeInternalMembers();

OperationType? operationType = null;
if (!IsObjectTypeExtension(context, out var possibleType, out var classSymbol, out var runtimeType))
Expand Down Expand Up @@ -58,44 +59,58 @@ public bool TryHandle(GeneratorSyntaxContext context, [NotNullWhen(true)] out Sy

foreach (var member in members)
{
if (member.DeclaredAccessibility is
Accessibility.Public or
Accessibility.Internal or
Accessibility.ProtectedAndInternal
&& !member.IsIgnored())
if (member.IsIgnored())
{
if (member is IMethodSymbol { MethodKind: MethodKind.Ordinary } methodSymbol)
continue;
}

if (member is IMethodSymbol { MethodKind: MethodKind.Ordinary } methodSymbol)
{
var hasNodeResolverAttribute = methodSymbol.IsNodeResolver();
var includeInternalNodeResolver = hasNodeResolverAttribute
&& methodSymbol.DeclaredAccessibility is
Accessibility.Internal
or Accessibility.ProtectedOrInternal
or Accessibility.ProtectedAndInternal;

if (!IsVisibleResolverMember(member, includeInternalMembers) && !includeInternalNodeResolver)
{
if (methodSymbol.Skip())
{
continue;
}
continue;
}

if (!isOperationType && methodSymbol.IsNodeResolver())
{
nodeResolver = CreateNodeResolver(context, classSymbol, methodSymbol, ref diagnostics);
}
else
{
resolvers[i++] = CreateResolver(context, classSymbol, methodSymbol);
continue;
}
if (methodSymbol.Skip())
{
continue;
}

if (member is IPropertySymbol)
if (!isOperationType && hasNodeResolverAttribute)
{
var compilation = context.SemanticModel.Compilation;

resolvers[i++] = new Resolver(
classSymbol.Name,
member,
compilation.GetDescription(member, []),
compilation.GetDeprecationReason(member),
ResolverResultKind.Pure,
[],
member.GetMemberBindings(),
compilation.CreateTypeReference(member));
nodeResolver = CreateNodeResolver(context, classSymbol, methodSymbol, ref diagnostics);
continue;
}

resolvers[i++] = CreateResolver(context, classSymbol, methodSymbol);
continue;
}

if (member is IPropertySymbol)
{
if (!IsVisibleResolverMember(member, includeInternalMembers))
{
continue;
}

var compilation = context.SemanticModel.Compilation;

resolvers[i++] = new Resolver(
classSymbol.Name,
member,
compilation.GetDescription(member, []),
compilation.GetDeprecationReason(member),
ResolverResultKind.Pure,
[],
member.GetMemberBindings(),
compilation.CreateTypeReference(member));
}
}

Expand Down Expand Up @@ -189,6 +204,16 @@ private static bool IsObjectTypeExtension(
return false;
}

private static bool IsVisibleResolverMember(ISymbol member, bool includeInternalMembers)
=> member.DeclaredAccessibility switch
{
Accessibility.Public => true,
Accessibility.Internal => includeInternalMembers,
Accessibility.ProtectedOrInternal => includeInternalMembers,
Accessibility.ProtectedAndInternal => includeInternalMembers,
_ => false
};

private static bool IsOperationType(
GeneratorSyntaxContext context,
[NotNullWhen(true)] out ClassDeclarationSyntax? resolverTypeSyntax,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ public enum ModuleOptions
Default = RegisterDataLoader | RegisterTypes,
RegisterTypes = 1,
RegisterDataLoader = 2,
Disabled = 4
IncludeInternalMembers = 4,
Disabled = 8
}
74 changes: 74 additions & 0 deletions src/HotChocolate/Core/test/Types.Analyzers.Tests/OperationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,80 @@ public static int GetTest(string arg)
""").MatchMarkdownAsync();
}

[Fact]
public async Task Internal_Resolvers_Are_Ignored_By_Default()
{
await TestHelper.GetGeneratedSourceSnapshot(
"""
using HotChocolate.Types;

namespace TestNamespace;

[QueryType]
public static partial class Query
{
public static int GetPublic()
=> 1;

internal static int GetInternal()
=> 2;
}
""").MatchMarkdownAsync();
}

[Fact]
public async Task Internal_Resolvers_Can_Be_Included_With_ModuleOptions()
{
await TestHelper.GetGeneratedSourceSnapshot(
"""
using HotChocolate;
using HotChocolate.Types;

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

namespace TestNamespace;

[QueryType]
public static partial class Query
{
public static int GetPublic()
=> 1;

internal static int GetInternal()
=> 2;
}
""").MatchMarkdownAsync();
}

[Fact]
public async Task Internal_NodeResolver_Is_Added_Without_InternalMember_Option()
{
await TestHelper.GetGeneratedSourceSnapshot(
"""
using System.Threading.Tasks;
using HotChocolate.Types;
using HotChocolate.Types.Relay;

namespace TestNamespace;

[QueryType]
public static partial class Query
{
[NodeResolver]
internal static Task<Foo?> GetFooById(int id)
=> Task.FromResult<Foo?>(null);

internal static int GetHiddenValue()
=> 123;
}

public class Foo
{
public string Id { get; set; }
}
""").MatchMarkdownAsync();
}

[Fact]
public async Task Root_Projection_Single_Entity()
{
Expand Down
Loading
Loading