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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public abstract class TypeFileBuilderBase(StringBuilder sb)
{
public CodeWriter Writer { get; } = new(sb);

private bool _hasDescription;

protected abstract string OutputFieldDescriptorType { get; }

public void WriteHeader()
Expand Down Expand Up @@ -276,13 +278,19 @@ private void WriteResolverBindingExtendsWith(
var description = resolver.Description;
if (!string.IsNullOrEmpty(description))
{
Writer.WriteIndentedLine("configuration.Description = \"{0}\";", GeneratorUtils.EscapeForStringLiteral(description));
_hasDescription = true;
Writer.WriteIndentedLine(
"configuration.Description = GetDescription(\"{0}\", {1}, field.Context.Options.UseXmlDocumentation);",
GeneratorUtils.EscapeForStringLiteral(description),
resolver.IsDescriptionFromAttribute ? "false" : "true");
}

var deprecationReason = resolver.DeprecationReason;
if (!string.IsNullOrEmpty(deprecationReason))
{
Writer.WriteIndentedLine("configuration.DeprecationReason = \"{0}\";", GeneratorUtils.EscapeForStringLiteral(deprecationReason));
Writer.WriteIndentedLine(
"configuration.DeprecationReason = \"{0}\";",
GeneratorUtils.EscapeForStringLiteral(deprecationReason));
}

WriteResolverBindingDescriptor(type, resolver);
Expand Down Expand Up @@ -381,9 +389,11 @@ private void WriteResolverBindingExtendsWith(
description = parameter.Description;
if (!string.IsNullOrEmpty(description))
{
_hasDescription = true;
Writer.WriteIndentedLine(
"Description = \"{0}\",",
GeneratorUtils.EscapeForStringLiteral(description));
"Description = GetDescription(\"{0}\", {1}, field.Context.Options.UseXmlDocumentation),",
GeneratorUtils.EscapeForStringLiteral(description),
parameter.IsDescriptionFromAttribute ? "false" : "true");
}

deprecationReason = parameter.DeprecationReason;
Expand Down Expand Up @@ -618,6 +628,22 @@ public void WriteEndResolverClass()
Writer.WriteIndentedLine("}");
}

public void WriteGetDescriptionHelper()
{
if (!_hasDescription)
{
return;
}

Writer.WriteLine();
Writer.WriteIndentedLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]");
Writer.WriteIndentedLine("private static string? GetDescription(string value, bool isXmlDocumentation, bool useXmlDocumentation)");
using (Writer.IncreaseIndent())
{
Writer.WriteIndentedLine("=> !isXmlDocumentation || useXmlDocumentation ? value : null;");
}
}

public virtual void WriteResolverFields(IOutputTypeInfo type)
{
foreach (var resolver in type.Resolvers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ private static void WriteFile(TypeFileBuilderBase file, IOutputTypeInfo type, IL
file.WriteResolverConstructor(type, typeLookup);
file.WriteResolverMethods(type, typeLookup);
file.WriteEndResolverClass();
file.WriteGetDescriptionHelper();
file.WriteEndClass();
file.WriteEndNamespace();
file.Flush();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,21 +94,26 @@ public static bool TryGetGraphQLTypeName(
switch (symbol)
{
case IPropertySymbol property:
return new PropertyDescription(property.GetDescriptionFromAttribute());
return new PropertyDescription(
property.GetDescriptionFromAttribute(),
IsDescriptionFromAttribute: true);

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

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

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

default:
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,18 @@ public static MethodDescription GetDescription(this IMethodSymbol method)

public static MethodDescription GetDescription(this IMethodSymbol method, Compilation? compilation)
{
var methodDescription = ResolveDescriptionCore(method, compilation, ExtractSummaryDescriptionFunc());
var (methodDescription, isFromAttribute) = ResolveDescriptionCore(method, compilation, ExtractSummaryDescriptionFunc());

// Process parameter descriptions
var parameters = method.Parameters;
var paramDescriptions = ImmutableArray.CreateBuilder<string?>(parameters.Length);
var paramDescriptions = ImmutableArray.CreateBuilder<(string?, bool)>(parameters.Length);

foreach (var param in parameters)
{
var paramDescription = compilation?.GetDescription(param)?.Description ?? GetDescriptionFromAttribute(param);
var paramDesc = compilation?.GetDescription(param);
var paramDescription = paramDesc?.Description ?? GetDescriptionFromAttribute(param);
var paramIsFromAttribute = paramDesc?.IsDescriptionFromAttribute
?? (paramDescription != null && GetDescriptionFromAttribute(param) != null);
var commentXml = method.GetDocumentationCommentXml();

if (paramDescription == null && !string.IsNullOrEmpty(commentXml))
Expand All @@ -53,6 +56,7 @@ public static MethodDescription GetDescription(this IMethodSymbol method, Compil
var paramDoc = ExtractParameterDescriptionFunc(param)(doc);

paramDescription = GeneratorUtils.NormalizeXmlDocumentation(paramDoc);
paramIsFromAttribute = false;
}
catch
{
Expand All @@ -61,37 +65,37 @@ public static MethodDescription GetDescription(this IMethodSymbol method, Compil
}
}

paramDescriptions.Add(paramDescription);
paramDescriptions.Add((paramDescription, paramIsFromAttribute));
}

return new MethodDescription(methodDescription, paramDescriptions.ToImmutable());
return new MethodDescription(methodDescription, paramDescriptions.ToImmutable(), isFromAttribute);
}

public static PropertyDescription? GetDescription(this IPropertySymbol property)
=> property.GetDescription(null);

public static PropertyDescription? GetDescription(this IPropertySymbol property, Compilation? compilation)
{
var result = ResolveDescriptionCore(property, compilation, ExtractSummaryDescriptionFunc());
return result is null ? null : new PropertyDescription(result);
var (result, isFromAttribute) = ResolveDescriptionCore(property, compilation, ExtractSummaryDescriptionFunc());
return result is null ? null : new PropertyDescription(result, isFromAttribute);
}

public static ParameterDescription? GetDescription(this IParameterSymbol parameter)
=> parameter.GetDescription(null);

public static ParameterDescription? GetDescription(this IParameterSymbol parameter, Compilation? compilation)
{
var result = ResolveDescriptionCore(parameter, compilation, ExtractParameterDescriptionFunc(parameter));
return result is null ? null : new ParameterDescription(result);
var (result, isFromAttribute) = ResolveDescriptionCore(parameter, compilation, ExtractParameterDescriptionFunc(parameter));
return result is null ? null : new ParameterDescription(result, isFromAttribute);
}

public static string? GetDescription(this INamedTypeSymbol type)
=> type.GetDescription(null);

public static string? GetDescription(this INamedTypeSymbol type, Compilation? compilation)
=> ResolveDescriptionCore(type, compilation, ExtractSummaryDescriptionFunc());
=> ResolveDescriptionCore(type, compilation, ExtractSummaryDescriptionFunc()).Description;

private static string? ResolveDescriptionCore(
private static (string? Description, bool IsFromAttribute) ResolveDescriptionCore(
ISymbol symbol,
Compilation? compilation,
Func<XDocument, string?> xmlExtractor)
Expand All @@ -100,33 +104,33 @@ public static MethodDescription GetDescription(this IMethodSymbol method, Compil
var description = GetDescriptionFromAttribute(symbol);
if (description != null)
{
return description;
return (description, true);
}

// 2. Inheritance-aware resolution when compilation is available
if (compilation != null)
{
return GetDocumentationWithInheritance(symbol, compilation, xmlExtractor);
return (GetDocumentationWithInheritance(symbol, compilation, xmlExtractor), false);
}

// 3. Fallback to simple XML extraction without inheritdoc support
var xml = symbol.GetDocumentationCommentXml();
if (string.IsNullOrEmpty(xml))
{
return null;
return (null, false);
}

try
{
var doc = XDocument.Parse(xml);
var extracted = xmlExtractor(doc);
return GeneratorUtils.NormalizeXmlDocumentation(extracted);
return (GeneratorUtils.NormalizeXmlDocumentation(extracted), false);
}
catch
{
// XML documentation parsing is best-effort only.
// Malformed XML is ignored and we fall back to no description.
return null;
return (null, false);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,15 @@ private static Resolver CreateResolver(
var parameter = parameters[i];
var parameterKind = compilation.GetParameterKind(parameter, out var key);

var paramDesc = compilation.GetDescription(parameter);
buffer[i] = new ResolverParameter(
parameter,
parameterKind,
compilation.CreateTypeReference(parameter),
compilation.GetDescription(parameter)?.Description,
paramDesc?.Description,
compilation.GetDeprecationReason(parameter),
key);
key,
paramDesc?.IsDescriptionFromAttribute ?? false);
}

return new Resolver(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,13 +296,15 @@ public static Resolver CreateResolver(
var parameter = parameters[i];
var parameterKind = compilation.GetParameterKind(parameter, out var key);

var paramDesc = compilation.GetDescription(parameter);
buffer[i] = new ResolverParameter(
parameter,
parameterKind,
compilation.CreateTypeReference(parameter, isBatchResolver),
compilation.GetDescription(parameter)?.Description,
paramDesc?.Description,
compilation.GetDeprecationReason(parameter),
key);
key,
paramDesc?.IsDescriptionFromAttribute ?? false);
}

resolverTypeName ??= resolverType.Name;
Expand Down Expand Up @@ -342,13 +344,15 @@ private static Resolver CreateNodeResolver(
var parameter = parameters[i];
var parameterKind = compilation.GetParameterKind(parameter, out var key);

var paramDesc = compilation.GetDescription(parameter);
var resolverParameter = new ResolverParameter(
parameter,
parameterKind,
compilation.CreateTypeReference(parameter),
compilation.GetDescription(parameter)?.Description,
paramDesc?.Description,
compilation.GetDeprecationReason(parameter),
key);
key,
paramDesc?.IsDescriptionFromAttribute ?? false);

if (resolverParameter.Kind == ResolverParameterKind.Argument)
{
Expand All @@ -370,9 +374,10 @@ private static Resolver CreateNodeResolver(
parameter,
ResolverParameterKind.Argument,
compilation.CreateTypeReference(parameter),
compilation.GetDescription(parameter)?.Description,
paramDesc?.Description,
compilation.GetDeprecationReason(parameter),
key);
key,
paramDesc?.IsDescriptionFromAttribute ?? false);
}

buffer[i] = resolverParameter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,55 @@ namespace HotChocolate.Types.Analyzers.Models;
/// <param name="parameterDescriptions">
/// The parameter descriptions in the same order as the method parameters.
/// </param>
/// <param name="isDescriptionFromAttribute">
/// Whether the method description originates from a <c>[GraphQLDescription]</c> attribute.
/// </param>
public MethodDescription(
string? description,
ImmutableArray<string?> parameterDescriptions)
ImmutableArray<(string? Description, bool IsFromAttribute)> parameterDescriptions,
bool isDescriptionFromAttribute = false)
{
Description = description;
ParameterDescriptions = parameterDescriptions;
IsDescriptionFromAttribute = isDescriptionFromAttribute;
}

/// <summary>
/// Gets the method's summary documentation, or <c>null</c> if not documented.
/// </summary>
public string? Description { get; }

/// <summary>
/// Gets whether the description originates from a <c>[GraphQLDescription]</c> attribute.
/// </summary>
public bool IsDescriptionFromAttribute { get; }

/// <summary>
/// Gets the parameter descriptions in the same order as the method parameters.
/// Each element is <c>null</c> if the parameter is not documented.
/// </summary>
public ImmutableArray<string?> ParameterDescriptions { get; }
public ImmutableArray<(string? Description, bool IsFromAttribute)> ParameterDescriptions { get; }
}

public readonly record struct PropertyDescription(string? Description) : IMemberDescription
public readonly record struct PropertyDescription(
string? Description,
bool IsDescriptionFromAttribute = false) : IMemberDescription
{
public string? Description { get; } = Description;
public bool IsDescriptionFromAttribute { get; } = IsDescriptionFromAttribute;
}

public readonly record struct ParameterDescription(string? Description) : IMemberDescription
public readonly record struct ParameterDescription(
string? Description,
bool IsDescriptionFromAttribute = false) : IMemberDescription
{
public string? Description { get; } = Description;
public bool IsDescriptionFromAttribute { get; } = IsDescriptionFromAttribute;
}

public interface IMemberDescription
{
string? Description { get; }

bool IsDescriptionFromAttribute { get; }
}
9 changes: 8 additions & 1 deletion src/HotChocolate/Core/src/Types.Analyzers/Models/Resolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ public Resolver(
{
for (var i = 0; i < parameters.Length; i++)
{
parameters[i].Description ??= m.ParameterDescriptions[i];
if (parameters[i].Description is null)
{
var (paramDesc, paramIsFromAttr) = m.ParameterDescriptions[i];
parameters[i].Description = paramDesc;
parameters[i].IsDescriptionFromAttribute = paramIsFromAttr;
}
}
}

Expand All @@ -53,6 +58,8 @@ public Resolver(

public string? Description => _description?.Description;

public bool IsDescriptionFromAttribute => _description?.IsDescriptionFromAttribute ?? false;

public string? DeprecationReason { get; }

public ISymbol Member { get; }
Expand Down
Loading
Loading