From 0d32236e2543d13ed66adca286275d038a87133d Mon Sep 17 00:00:00 2001 From: NOlbert <45466413+N-Olbert@users.noreply.github.com> Date: Fri, 5 Dec 2025 22:36:18 +0100 Subject: [PATCH 1/2] Adjust source generator to behave like XmlDocumentationProvider --- .../src/CookieCrumble/Snapshot.cs | 8 + .../Types.Analyzers/Helpers/GeneratorUtils.cs | 8 +- .../Helpers/SymbolExtensions.cs | 247 ++++++------ .../ObjectTypeTests.XmlDocInference.Ported.cs | 367 +++++++++++++++++ .../ObjectTypeTests.XmlDocInference.cs | 237 +++++++++++ ..._With_InheritdocCref_AllPossibleTargets.md | 379 ++++++++++++++++++ 6 files changed, 1128 insertions(+), 118 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.Ported.cs create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.cs create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.XmlDocumentation_With_InheritdocCref_AllPossibleTargets.md diff --git a/src/CookieCrumble/src/CookieCrumble/Snapshot.cs b/src/CookieCrumble/src/CookieCrumble/Snapshot.cs index 52413ca9d5d..736abbcda5e 100644 --- a/src/CookieCrumble/src/CookieCrumble/Snapshot.cs +++ b/src/CookieCrumble/src/CookieCrumble/Snapshot.cs @@ -370,6 +370,14 @@ public void MatchInline(string expected) } } + public string Render() + { + var writer = new ArrayBufferWriter(); + WriteSegments(writer); + EnsureEndOfBufferNewline(writer); + return Encoding.UTF8.GetString(writer.WrittenSpan); + } + private void WriteSegments(IBufferWriter writer) { if (_segments.Count == 1) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/GeneratorUtils.cs b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/GeneratorUtils.cs index 31cc518252b..25638e28c71 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/GeneratorUtils.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/GeneratorUtils.cs @@ -128,7 +128,7 @@ public static string ConvertDefaultValueToString(object? defaultValue, ITypeSymb if (type.IsNullableValueType()) { - return ConvertDefaultValueToString(defaultValue, namedTypeSymbol.TypeArguments[0]!); + return ConvertDefaultValueToString(defaultValue, namedTypeSymbol.TypeArguments[0]); } } @@ -159,7 +159,11 @@ public static string SanitizeIdentifier(string input) } // Normalize line endings and trim outer newlines - var normalized = "\n" + documentation!.Replace("\r", string.Empty).Trim('\n'); + var normalized = documentation!.Replace("\r", string.Empty); + if (normalized[0] == ' ') + { + normalized = "\n" + normalized; + } // Find common leading whitespace pattern var whitespace = s_xmlWhitespaceRegex.Match(normalized).Value; diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/SymbolExtensions.cs b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/SymbolExtensions.cs index bc975c8a46e..f7eaa1841f7 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/SymbolExtensions.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/SymbolExtensions.cs @@ -1,5 +1,6 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.Text; using System.Xml.Linq; using HotChocolate.Types.Analyzers.Models; using Microsoft.CodeAnalysis; @@ -37,7 +38,7 @@ public static MethodDescription GetDescription(this IMethodSymbol method, Compil if (methodDescription == null && compilation != null) { // Try inheritance-aware resolution with Compilation - methodDescription = GetDocumentationWithInheritance(method, compilation); + methodDescription = GetSummaryDocumentationWithInheritance(method, compilation); } else if (methodDescription == null) { @@ -109,7 +110,7 @@ public static MethodDescription GetDescription(this IMethodSymbol method, Compil if (compilation != null) { // Try inheritance-aware resolution with Compilation - return new PropertyDescription(GetDocumentationWithInheritance(property, compilation)); + return new PropertyDescription(GetSummaryDocumentationWithInheritance(property, compilation)); } // Fallback to simple XML extraction without inheritdoc support @@ -148,7 +149,7 @@ public static MethodDescription GetDescription(this IMethodSymbol method, Compil if (compilation != null) { // Try inheritance-aware resolution with Compilation - return GetDocumentationWithInheritance(type, compilation); + return GetSummaryDocumentationWithInheritance(type, compilation); } // Fallback to simple XML extraction without inheritdoc support @@ -202,18 +203,18 @@ public static MethodDescription GetDescription(this IMethodSymbol method, Compil } /// - /// Extracts summary text from XML documentation, resolving inheritdoc tags. + /// Extracts summary text from XML documentation, resolving tags with semantic relevance (f. e. inheritdoc or see). /// - private static string? GetDocumentationWithInheritance(ISymbol symbol, Compilation compilation) + private static string? GetSummaryDocumentationWithInheritance(ISymbol symbol, Compilation compilation) { var visited = new HashSet(SymbolEqualityComparer.Default); - return GetDocumentationWithInheritanceCore(symbol, compilation, visited); + return GetSummaryDocumentationWithInheritanceCore(symbol, compilation, visited); } /// /// Core implementation with cycle detection. /// - private static string? GetDocumentationWithInheritanceCore( + private static string? GetSummaryDocumentationWithInheritanceCore( ISymbol symbol, Compilation compilation, HashSet visited) @@ -232,31 +233,135 @@ public static MethodDescription GetDescription(this IMethodSymbol method, Compil try { - var doc = XDocument.Parse(xml); + var doc = XDocument.Parse(xml, LoadOptions.PreserveWhitespace); + + // Materialize relevant XML elements (-> replace their element with the actual textual representation) + MaterializeInheritdocElements(doc); + MaterializeSeeElements(doc); + MaterializeParamRefElements(doc); - // Check for inheritdoc element - var inheritdocElement = doc.Descendants("inheritdoc").FirstOrDefault(); + var summaryText = + doc.Descendants("summary").FirstOrDefault()?.Value ?? + doc.Descendants("member").FirstOrDefault()?.Value; - if (inheritdocElement != null) + summaryText += GetReturnsElementText(doc); + + var exceptionDoc = GetExceptionDocumentation(doc); + if (!string.IsNullOrEmpty(exceptionDoc)) { + summaryText += "\n\n**Errors:**\n" + exceptionDoc; + } + + return GeneratorUtils.NormalizeXmlDocumentation(summaryText); + } + catch + { + // XML documentation parsing is best-effort only. + return null; + } + + void MaterializeInheritdocElements(XDocument doc1) + { + foreach (var inheritdocElement in doc1.Descendants("inheritdoc").ToArray()) + { + if (inheritdocElement == null) + { + continue; + } + // Try to resolve the inherited documentation var inheritedDoc = ResolveInheritdoc(symbol, inheritdocElement, compilation, visited); if (inheritedDoc != null) { - return inheritedDoc; + inheritdocElement.ReplaceWith(inheritedDoc); } - // If resolution fails, return null (no description) - return null; } + } - // No inheritdoc - extract summary normally - var summaryText = doc.Descendants("summary").FirstOrDefault()?.Value; - return GeneratorUtils.NormalizeXmlDocumentation(summaryText); + static void MaterializeSeeElements(XDocument xDocument) + { + foreach (var seeElement in xDocument.Descendants("see").ToArray()) + { + if (seeElement == null) + { + continue; + } + + if (!string.IsNullOrEmpty(seeElement.Value)) + { + seeElement.ReplaceWith(seeElement.Value); + continue; + } + + var attribute = seeElement.Attribute("langword") ?? seeElement.Attribute("href"); + if (attribute != null) + { + seeElement.ReplaceWith(attribute.Value); + continue; + } + + attribute = seeElement.Attribute("cref"); + if (attribute?.Value != null) + { + var index = attribute.Value.LastIndexOf('.'); + seeElement.ReplaceWith(attribute.Value.Substring(index + 1)); + } + } } - catch + + static void MaterializeParamRefElements(XDocument xDocument) { - // XML documentation parsing is best-effort only. - return null; + foreach (var paramref in xDocument.Descendants("paramref").ToArray()) + { + var attribute = paramref?.Attribute("name"); + if (attribute != null) + { + paramref!.ReplaceWith(attribute.Value); + } + } + } + + static string GetExceptionDocumentation(XDocument xDocument) + { + StringBuilder? builder = null; + var errorCount = 0; + var exceptionElements = xDocument.Descendants("exception"); + foreach (var exceptionElement in exceptionElements) + { + if (string.IsNullOrEmpty(exceptionElement.Value)) + { + continue; + } + + var code = exceptionElement.Attribute("code"); + if (string.IsNullOrEmpty(code?.Value)) + { + continue; + } + + builder ??= new StringBuilder(); + builder.Append(builder.Length > 0 ? "\n" : string.Empty) + .Append(++errorCount) + .Append('.') + .Append(' ') + .Append(code!.Value) + .Append(':') + .Append(' ') + .Append(exceptionElement.Value); + } + + return builder?.ToString() ?? string.Empty; + } + + static string GetReturnsElementText(XDocument doc) + { + var xElement = doc.Descendants("returns").FirstOrDefault(); + if (xElement?.Value != null) + { + return "\n\n**Returns:**\n" + xElement.Value; + } + + return string.Empty; } } @@ -273,10 +378,10 @@ public static MethodDescription GetDescription(this IMethodSymbol method, Compil var crefAttr = inheritdocElement.Attribute("cref"); if (crefAttr != null) { - var referencedSymbol = ResolveDocumentationId(crefAttr.Value, compilation, symbol); + var referencedSymbol = ResolveDocumentationId(crefAttr.Value, compilation); if (referencedSymbol != null) { - return GetDocumentationWithInheritanceCore(referencedSymbol, compilation, visited); + return GetSummaryDocumentationWithInheritanceCore(referencedSymbol, compilation, visited); } return null; } @@ -285,7 +390,7 @@ public static MethodDescription GetDescription(this IMethodSymbol method, Compil var baseMember = FindBaseMember(symbol); if (baseMember != null) { - return GetDocumentationWithInheritanceCore(baseMember, compilation, visited); + return GetSummaryDocumentationWithInheritanceCore(baseMember, compilation, visited); } return null; @@ -389,7 +494,7 @@ public static MethodDescription GetDescription(this IMethodSymbol method, Compil /// Resolves a documentation ID (cref value) to a symbol. /// Handles format like "T:Namespace.Type", "M:Namespace.Type.Method", "T:Namespace.Type`1", etc. /// - private static ISymbol? ResolveDocumentationId(string documentationId, Compilation compilation, ISymbol contextSymbol) + private static ISymbol? ResolveDocumentationId(string documentationId, Compilation compilation) { if (string.IsNullOrEmpty(documentationId)) { @@ -397,98 +502,8 @@ public static MethodDescription GetDescription(this IMethodSymbol method, Compil } // Documentation ID format: Prefix:FullyQualifiedName - // Prefixes: T: (type), M: (method), P: (property), F: (field), E: (event) - - // Remove prefix if present - var colonIndex = documentationId.IndexOf(':'); - if (colonIndex > 0) - { - documentationId = documentationId.Substring(colonIndex + 1); - } - - // Handle generic types - convert `2 to format for lookup - // For now, try exact match first - var symbol = compilation.GetTypeByMetadataName(documentationId); - if (symbol != null) - { - return symbol; - } - - // Try without generic arity marker - var backtickIndex = documentationId.IndexOf('`'); - if (backtickIndex > 0) - { - var typeNameWithoutArity = documentationId.Substring(0, backtickIndex); - symbol = compilation.GetTypeByMetadataName(typeNameWithoutArity); - if (symbol != null) - { - return symbol; - } - } - - // Try resolving through context symbol's containing namespace - if (contextSymbol.ContainingNamespace != null) - { - var namespaceName = contextSymbol.ContainingNamespace.ToDisplayString(); - var fullName = $"{namespaceName}.{documentationId}"; - symbol = compilation.GetTypeByMetadataName(fullName); - if (symbol != null) - { - return symbol; - } - } - - // Best effort - search for type by name in compilation - var typeName = documentationId.Split('.').LastOrDefault(); - if (!string.IsNullOrEmpty(typeName)) - { - // Remove generic arity from type name - var genericIndex = typeName.IndexOf('`'); - if (genericIndex > 0) - { - typeName = typeName.Substring(0, genericIndex); - } - - // Search in all referenced assemblies - foreach (var assembly in compilation.References) - { - if (compilation.GetAssemblyOrModuleSymbol(assembly) is IAssemblySymbol assemblySymbol) - { - var foundSymbol = FindTypeByName(assemblySymbol.GlobalNamespace, typeName); - if (foundSymbol != null) - { - return foundSymbol; - } - } - } - } - - return null; - } - - /// - /// Recursively searches for a type by name in a namespace. - /// - private static INamedTypeSymbol? FindTypeByName(INamespaceSymbol namespaceSymbol, string typeName) - { - foreach (var member in namespaceSymbol.GetMembers()) - { - if (member is INamedTypeSymbol type && type.Name == typeName) - { - return type; - } - - if (member is INamespaceSymbol childNamespace) - { - var found = FindTypeByName(childNamespace, typeName); - if (found != null) - { - return found; - } - } - } - - return null; + // Prefixes: T: (type), M: (method), P: (property), F: (field), E: (event)# + return DocumentationCommentId.GetSymbolsForDeclarationId(documentationId, compilation).FirstOrDefault(); } public static bool IsNullableType(this ITypeSymbol typeSymbol) diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.Ported.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.Ported.cs new file mode 100644 index 00000000000..242266d126f --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.Ported.cs @@ -0,0 +1,367 @@ +namespace HotChocolate.Types; + +//// Ported tests from XmlDocumentationProviderTests to ensure identical behavior between both documentation providers. + +public partial class ObjectTypeXmlDocInferenceTests +{ + [Fact] + public void When_xml_doc_is_missing_then_description_is_empty() + { + var snap = 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 + { + public static string GetUser() + => "User"; + } + """); + + Assert.Empty(DescriptionExtractorRegex().Matches(snap.Render())); + } + + [Fact] + public void When_xml_doc_with_multiple_breaks_is_read_then_they_are_not_stripped_away() + { + var snap = 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 + { + /// + /// Query and manages users. + /// + /// Please note: + /// * Users ... + /// * Users ... + /// * Users ... + /// * Users ... + /// + /// You need one of the following role: Owner, + /// Editor, use XYZ to manage permissions. + /// + public static string? Foo { get; set; } + } + """); + + const string expected = + "Query and manages users.\\n \\nPlease note:\\n* Users ...\\n* Users ...\\n * Users ...\\n" + + " * Users ...\\n \\nYou need one of the following role: Owner,\\n" + + "Editor, use XYZ to manage permissions."; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [Fact] + public void When_description_has_see_tag_then_it_is_converted() + { + var snap = TestHelper.GetGeneratedSourceSnapshot( + """ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + public class Record; + + [QueryType] + internal static partial class Query + { + /// + /// for the default . + /// See this and + /// this at + /// . + /// + public static string? Foo { get; set; } + } + """); + + const string expected = "null for the default Record.\\nSee this and\\nthis at\\nhttps://foo.com/bar/baz."; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [Fact] + public void When_description_has_paramref_tag_then_it_is_converted() + { + var snap = 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 + { + /// + /// This is a parameter reference to . + /// + public int Foo(int id) => id; + } + """); + + const string expected = "This is a parameter reference to id."; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [Fact] + public void When_description_has_generic_tags_then_it_is_converted() + { + var snap = 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 + { + /// These are some tags. + public int Foo() => 0; + } + """); + + const string expected = "These are some tags."; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [Fact] + public void When_method_has_inheritdoc_then_it_is_resolved() + { + var snap = TestHelper.GetGeneratedSourceSnapshot( + """ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + /// + /// I am the most base class. + /// + public class BaseBaseClass + { + /// Method doc. + /// Parameter details. + public virtual void Bar(string baz) { } + } + + [QueryType] + internal static partial class Query + { + /// + public static int Bar(string baz) => 0; + } + """); + + const string expected = "Method doc."; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [Fact] + public void When_class_has_description_then_it_is_converted() + { + var snap = 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 + { + /// + /// I am a test class. This should not be escaped: > + /// + public static int Bar() => 0; + } + """); + + const string expected = "I am a test class. This should not be escaped: >"; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [Fact] + public void When_method_has_returns_then_it_is_converted() + { + var snap = 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 + { + /// + /// Query and manages users. + /// + /// Bar + public static int Bar() => 0; + } + """); + + const string expected = "Query and manages users.\\n\\n\\n**Returns:**\\nBar"; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [Fact] + public void When_method_has_exceptions_then_it_is_converted() + { + var snap = 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 + { + /// + /// Query and manages users. + /// + /// Bar + /// Foo Error + /// Bar Error + public static int Bar() => 0; + } + """); + + const string expected = "Query and manages users.\\n\\n\\n**Returns:**\\nBar\\n\\n**Errors:**\\n1. FOO_ERROR: Foo Error\\n2. BAR_ERROR: Bar Error"; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [Fact] + public void When_method_has_exceptions_then_exceptions_with_no_code_will_be_ignored() + { + var snap = 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 + { + /// + /// Query and manages users. + /// + /// Bar + /// Foo Error + /// Foo Error + /// Bar Error + public static int Bar() => 0; + } + """); + + const string expected = "Query and manages users.\\n\\n\\n**Returns:**\\nBar\\n\\n**Errors:**\\n1. FOO_ERROR: Foo Error\\n2. BAR_ERROR: Bar Error"; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [Fact] + public void When_method_has_only_exceptions_with_no_code_then_error_section_will_not_be_written() + { + var snap = 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 + { + /// + /// Query and manages users. + /// + /// Bar + /// Foo Error + public static int Bar() => 0; + } + """); + + const string expected = "Query and manages users.\\n\\n\\n**Returns:**\\nBar"; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [System.Text.RegularExpressions.GeneratedRegex("configuration.Description = \"(.*)\";")] + private static partial System.Text.RegularExpressions.Regex DescriptionExtractorRegex(); +} diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.cs new file mode 100644 index 00000000000..298b6d39812 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.cs @@ -0,0 +1,237 @@ +namespace HotChocolate.Types; + +public partial class ObjectTypeXmlDocInferenceTests +{ + [Fact] + public void Method_WithInheritdoc_And_MultipleLayersOfInheritance() + { + var snap = TestHelper.GetGeneratedSourceSnapshot( + """ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + public class BaseBaseClass + { + /// Method doc. + public virtual void Bar() { } + } + + public class BaseClass : BaseBaseClass + { + /// + public override void Bar() { } + } + + [QueryType] + internal static partial class Query + { + /// + public static int Bar(string baz) => 0; + } + """); + + const string expected = "Method doc."; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [Fact] + public void Method_WithInheritdoc_ThatContainsInheritdoc() + { + var snap = TestHelper.GetGeneratedSourceSnapshot( + """ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + public class BaseBaseClass + { + /// Method doc. + public virtual void Bar() { } + } + + public class BaseClass : BaseBaseClass + { + /// + /// Concrete Method doc. + /// + /// + public override void Bar() { } + } + + public class ConcreteClass : BaseClass + { + /// + public override void Bar() { } + } + + [QueryType] + internal static partial class Query + { + /// + public static int Bar(string baz) => 0; + } + """); + + const string expected = "Concrete Method doc.\\nMethod doc."; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [Fact] + public void XmlDocumentation_Is_Overriden_By_DescriptionAttribute() + { + var snap = 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 + { + /// + /// This is ... + /// + [GraphQLDescription("Nothing")] + public static string GetUser() => "User"; + } + """); + + const string expected = "Nothing"; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } + + [Fact] + public async Task XmlDocumentation_With_InheritdocCref_AllPossibleTargets() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using System; + using HotChocolate.Types; + + namespace TestNamespace; + + /// + /// Type description. + /// + public class Foo + { + /// + /// Field description. + /// + public static int F = 0; + + /// + /// Property description. + /// + public static int P => 0; + + /// + /// Method description. + /// + public static int M() => 0; + + /// + /// Event description. + /// + public event EventHandler E; + + /// + /// Int-overloaded method description. + /// + public static Bar GetBar(int id) => new Bar(); + + /// + /// String-overloaded method description. + /// + public static Bar GetBar(string id) => new Bar(); + + public class Bar + { + /// + /// Nested type instance field description. + /// + public int B = 0; + } + } + + [QueryType] + public static partial class Query + { + /// + public static string? T() => null; + /// + public static string? F() => null; + /// + public static string? P() => null; + /// + public static string? M() => null; + /// + public static string? E() => null; + /// + public static string? Nested() => null; + /// + public static string? Overloaded(int id) => null; + } + """).MatchMarkdownAsync(); + } + + [Fact] + public void XmlDocumentation_With_Nested_InheritdocCref() + { + var snap = TestHelper.GetGeneratedSourceSnapshot( + """ + using System; + using HotChocolate.Types; + + namespace TestNamespace; + + /// + /// This type is similar useless to ''. + /// + public class FooType + { + } + + /// + /// The Bar type. + /// + public class BarType + { + } + + [QueryType] + public static partial class Query + { + /// + public static string? Foo() => null; + } + """); + + const string expected = "This type is similar useless to 'The Bar type.'."; + + var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; + Assert.Equal(expected, emitted[1].Value); + } +} diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.XmlDocumentation_With_InheritdocCref_AllPossibleTargets.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.XmlDocumentation_With_InheritdocCref_AllPossibleTargets.md new file mode 100644 index 00000000000..773e7c221b8 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.XmlDocumentation_With_InheritdocCref_AllPossibleTargets.md @@ -0,0 +1,379 @@ +# XmlDocumentation_With_InheritdocCref_AllPossibleTargets + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 + +```csharp +// + +#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 +{ + public 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(bindingResolver); + + 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("T", 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 = "Type description."; + configuration.Type = typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Output); + configuration.ResultType = typeof(string); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.T(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + descriptor + .Field(naming.GetMemberName("F", 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 = "Field description."; + configuration.Type = typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Output); + configuration.ResultType = typeof(string); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.F(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + descriptor + .Field(naming.GetMemberName("P", 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 = "Property description."; + configuration.Type = typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Output); + configuration.ResultType = typeof(string); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.P(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + descriptor + .Field(naming.GetMemberName("M", 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 = "Method description."; + configuration.Type = typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Output); + configuration.ResultType = typeof(string); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.M(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + descriptor + .Field(naming.GetMemberName("E", 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 = "Event description."; + configuration.Type = typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Output); + configuration.ResultType = typeof(string); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.E(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + descriptor + .Field(naming.GetMemberName("Nested", 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 = "Nested type instance field description."; + configuration.Type = typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Output); + configuration.ResultType = typeof(string); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.Nested(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + descriptor + .Field(naming.GetMemberName("Overloaded", 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 = "Int-overloaded method description."; + configuration.Type = typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Output); + configuration.ResultType = typeof(string); + + configuration.SetSourceGeneratorFlags(); + + var bindingInfo = field.Context.ParameterBindingResolver; + var parameter = context.Resolvers.CreateParameterDescriptor_Overloaded_id(); + var parameterInfo = bindingInfo.GetBindingInfo(parameter); + + if(parameterInfo.Kind is global::HotChocolate.Internal.ArgumentKind.Argument) + { + var argumentConfiguration = new global::HotChocolate.Types.Descriptors.Configurations.ArgumentConfiguration + { + Name = naming.GetMemberName("id", global::HotChocolate.Types.MemberKind.Argument), + Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Input), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))), + RuntimeType = typeof(int) + }; + + configuration.Arguments.Add(argumentConfiguration); + } + + configuration.Resolvers = context.Resolvers.Overloaded(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + private readonly global::HotChocolate.Internal.IParameterBinding _binding_Overloaded_id; + + public __Resolvers(global::HotChocolate.Resolvers.ParameterBindingResolver bindingResolver) + { + _binding_Overloaded_id = bindingResolver.GetBinding(CreateParameterDescriptor_Overloaded_id()); + } + + public HotChocolate.Resolvers.FieldResolverDelegates T() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: T); + } + + private global::System.Object? T(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.T(); + return result; + } + + public HotChocolate.Resolvers.FieldResolverDelegates F() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: F); + } + + private global::System.Object? F(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.F(); + return result; + } + + public HotChocolate.Resolvers.FieldResolverDelegates P() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: P); + } + + private global::System.Object? P(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.P(); + return result; + } + + public HotChocolate.Resolvers.FieldResolverDelegates M() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: M); + } + + private global::System.Object? M(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.M(); + return result; + } + + public HotChocolate.Resolvers.FieldResolverDelegates E() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: E); + } + + private global::System.Object? E(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.E(); + return result; + } + + public HotChocolate.Resolvers.FieldResolverDelegates Nested() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Nested); + } + + private global::System.Object? Nested(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.Nested(); + return result; + } + + public global::HotChocolate.Internal.ParameterDescriptor CreateParameterDescriptor_Overloaded_id() + => new HotChocolate.Internal.ParameterDescriptor( + "id", + typeof(int), + isNullable: false, + []); + + public HotChocolate.Resolvers.FieldResolverDelegates Overloaded() + { + var isPureResolver = _binding_Overloaded_id.IsPure; + + return isPureResolver + ? new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Overloaded) + : new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: c => new(Overloaded(c))); + } + + private global::System.Object? Overloaded(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = _binding_Overloaded_id.Execute(context); + var result = global::TestNamespace.Query.Overloaded(args0); + return result; + } + } + } +} + + +``` + +## Compilation Diagnostics + +```json +[ + { + "Id": "CS0067", + "Title": "Event is never used", + "Severity": "Warning", + "WarningLevel": 3, + "Location": ": (28,30)-(28,31)", + "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0067)", + "MessageFormat": "The event '{0}' is never used", + "Message": "The event 'Foo.E' is never used", + "Category": "Compiler", + "CustomTags": [ + "Compiler", + "Telemetry" + ] + } +] +``` + +## Assembly Emit Diagnostics + +```json +[ + { + "Id": "CS0067", + "Title": "Event is never used", + "Severity": "Warning", + "WarningLevel": 3, + "Location": ": (28,30)-(28,31)", + "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0067)", + "MessageFormat": "The event '{0}' is never used", + "Message": "The event 'Foo.E' is never used", + "Category": "Compiler", + "CustomTags": [ + "Compiler", + "Telemetry" + ] + } +] +``` + From f73d6ebeea216e216ee9710f3090850dcf7f15d5 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 8 Dec 2025 09:02:26 +0100 Subject: [PATCH 2/2] Reworked tests --- .../src/CookieCrumble/Snapshot.cs | 28 +- .../ObjectTypeTests.XmlDocInference.Ported.cs | 613 +++++++++--------- .../ObjectTypeTests.XmlDocInference.cs | 256 ++++---- .../test/Types.Analyzers.Tests/TestHelper.cs | 4 +- ...itdoc_And_MultipleLayersOfInheritance.snap | 152 +++++ ...WithInheritdoc_ThatContainsInheritdoc.snap | 152 +++++ ..._has_description_then_it_is_converted.snap | 115 ++++ ...has_generic_tags_then_it_is_converted.snap | 175 +++++ ...has_paramref_tag_then_it_is_converted.snap | 212 ++++++ ...tion_has_see_tag_then_it_is_converted.snap | 111 ++++ ...ceptions_with_no_code_will_be_ignored.snap | 115 ++++ ...d_has_exceptions_then_it_is_converted.snap | 115 ++++ ...od_has_inheritdoc_then_it_is_resolved.snap | 152 +++++ ...hen_error_section_will_not_be_written.snap | 115 ++++ ...thod_has_returns_then_it_is_converted.snap | 115 ++++ ..._is_missing_then_description_is_empty.snap | 114 ++++ ..._read_then_they_are_not_stripped_away.snap | 111 ++++ ..._Is_Overriden_By_DescriptionAttribute.snap | 115 ++++ ...umentation_With_Nested_InheritdocCref.snap | 113 ++++ 19 files changed, 2434 insertions(+), 449 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.Method_WithInheritdoc_And_MultipleLayersOfInheritance.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.Method_WithInheritdoc_ThatContainsInheritdoc.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_class_has_description_then_it_is_converted.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_description_has_generic_tags_then_it_is_converted.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_description_has_paramref_tag_then_it_is_converted.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_description_has_see_tag_then_it_is_converted.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_exceptions_then_exceptions_with_no_code_will_be_ignored.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_exceptions_then_it_is_converted.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_inheritdoc_then_it_is_resolved.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_only_exceptions_with_no_code_then_error_section_will_not_be_written.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_returns_then_it_is_converted.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_xml_doc_is_missing_then_description_is_empty.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_xml_doc_with_multiple_breaks_is_read_then_they_are_not_stripped_away.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.XmlDocumentation_Is_Overriden_By_DescriptionAttribute.snap create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.XmlDocumentation_With_Nested_InheritdocCref.snap diff --git a/src/CookieCrumble/src/CookieCrumble/Snapshot.cs b/src/CookieCrumble/src/CookieCrumble/Snapshot.cs index 736abbcda5e..b23fa3d2082 100644 --- a/src/CookieCrumble/src/CookieCrumble/Snapshot.cs +++ b/src/CookieCrumble/src/CookieCrumble/Snapshot.cs @@ -15,8 +15,12 @@ namespace CookieCrumble; public class Snapshot { +#if NET10_0_OR_GREATER + private static readonly Lock s_sync = new(); +#else private static readonly object s_sync = new(); - private static readonly UTF8Encoding s_encoding = new(); +#endif + private static readonly Encoding s_utf8 = Encoding.UTF8; private static ImmutableStack s_formatters = CreateRange(new ISnapshotValueFormatter[] { @@ -237,7 +241,7 @@ public async ValueTask MatchAsync(CancellationToken cancellationToken = default) EnsureFileDoesNotExist(mismatchFile); var before = await File.ReadAllTextAsync(snapshotFile, cancellationToken); - var after = s_encoding.GetString(writer.WrittenSpan); + var after = s_utf8.GetString(writer.WrittenSpan); if (!MatchSnapshot(before, after, false, out var diff)) { @@ -249,7 +253,7 @@ public async ValueTask MatchAsync(CancellationToken cancellationToken = default) } } - public void Match() + public string Match() { var writer = new ArrayBufferWriter(); WriteSegments(writer); @@ -269,7 +273,7 @@ public void Match() var mismatchFile = Combine(CreateMismatchDirectoryName(), CreateSnapshotFileName()); EnsureFileDoesNotExist(mismatchFile); var before = File.ReadAllText(snapshotFile); - var after = s_encoding.GetString(writer.WrittenSpan); + var after = s_utf8.GetString(writer.WrittenSpan); if (!MatchSnapshot(before, after, false, out var diff)) { @@ -279,6 +283,8 @@ public void Match() s_testFramework.ThrowTestException(diff); } } + + return s_utf8.GetString(writer.WrittenSpan); } public async ValueTask MatchMarkdownAsync(CancellationToken cancellationToken = default) @@ -305,7 +311,7 @@ public async ValueTask MatchMarkdownAsync(CancellationToken cancellationToken = var mismatchFile = Combine(CreateMismatchDirectoryName(), CreateMarkdownSnapshotFileName()); EnsureFileDoesNotExist(mismatchFile); var before = await File.ReadAllTextAsync(snapshotFile, cancellationToken); - var after = s_encoding.GetString(writer.WrittenSpan); + var after = s_utf8.GetString(writer.WrittenSpan); if (MatchSnapshot(before, after, false, out var diff)) { @@ -343,7 +349,7 @@ public void MatchMarkdown() var mismatchFile = Combine(CreateMismatchDirectoryName(), CreateMarkdownSnapshotFileName()); EnsureFileDoesNotExist(mismatchFile); var before = File.ReadAllText(snapshotFile); - var after = s_encoding.GetString(writer.WrittenSpan); + var after = s_utf8.GetString(writer.WrittenSpan); if (MatchSnapshot(before, after, false, out var diff)) { @@ -362,7 +368,7 @@ public void MatchInline(string expected) var writer = new ArrayBufferWriter(); WriteSegments(writer); - var after = s_encoding.GetString(writer.WrittenSpan); + var after = s_utf8.GetString(writer.WrittenSpan); if (!MatchSnapshot(expected, after, true, out var diff)) { @@ -370,14 +376,6 @@ public void MatchInline(string expected) } } - public string Render() - { - var writer = new ArrayBufferWriter(); - WriteSegments(writer); - EnsureEndOfBufferNewline(writer); - return Encoding.UTF8.GetString(writer.WrittenSpan); - } - private void WriteSegments(IBufferWriter writer) { if (_segments.Count == 1) diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.Ported.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.Ported.cs index 242266d126f..43bfa1ccf9b 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.Ported.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.Ported.cs @@ -7,359 +7,362 @@ public partial class ObjectTypeXmlDocInferenceTests [Fact] public void When_xml_doc_is_missing_then_description_is_empty() { - var snap = 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 - { - public static string GetUser() - => "User"; - } - """); - - Assert.Empty(DescriptionExtractorRegex().Matches(snap.Render())); + 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 + { + public static string GetUser() + => "User"; + } + """); + + var content = snapshot.Match(); + Assert.Empty(s_description.Matches(content)); } [Fact] public void When_xml_doc_with_multiple_breaks_is_read_then_they_are_not_stripped_away() { - var snap = 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 - { - /// - /// Query and manages users. - /// - /// Please note: - /// * Users ... - /// * Users ... - /// * Users ... - /// * Users ... - /// - /// You need one of the following role: Owner, - /// Editor, use XYZ to manage permissions. - /// - public static string? Foo { get; set; } - } - """); - - const string expected = + 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 + { + /// + /// Query and manages users. + /// + /// Please note: + /// * Users ... + /// * Users ... + /// * Users ... + /// * Users ... + /// + /// You need one of the following role: Owner, + /// Editor, use XYZ to manage permissions. + /// + public static string? Foo { get; set; } + } + """); + + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal( "Query and manages users.\\n \\nPlease note:\\n* Users ...\\n* Users ...\\n * Users ...\\n" + " * Users ...\\n \\nYou need one of the following role: Owner,\\n" - + "Editor, use XYZ to manage permissions."; - - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + + "Editor, use XYZ to manage permissions.", + emitted[1].Value); } [Fact] public void When_description_has_see_tag_then_it_is_converted() { - var snap = TestHelper.GetGeneratedSourceSnapshot( - """ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using HotChocolate; - using HotChocolate.Types; - - namespace TestNamespace; - - public class Record; - - [QueryType] - internal static partial class Query - { - /// - /// for the default . - /// See this and - /// this at - /// . - /// - public static string? Foo { get; set; } - } - """); - - const string expected = "null for the default Record.\\nSee this and\\nthis at\\nhttps://foo.com/bar/baz."; - - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + var snapshot = + TestHelper.GetGeneratedSourceSnapshot( + """ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + public class Record; + + [QueryType] + internal static partial class Query + { + /// + /// for the default . + /// See this and + /// this at + /// . + /// + public static string? Foo { get; set; } + } + """); + + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("null for the default Record.\\nSee this and\\nthis at\\nhttps://foo.com/bar/baz.", emitted[1].Value); } [Fact] public void When_description_has_paramref_tag_then_it_is_converted() { - var snap = 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 - { - /// - /// This is a parameter reference to . - /// - public int Foo(int id) => id; - } - """); - - const string expected = "This is a parameter reference to id."; - - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + 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 + { + /// + /// This is a parameter reference to . + /// + public int Foo(int id) => id; + } + """); + + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("This is a parameter reference to id.", emitted[1].Value); } [Fact] public void When_description_has_generic_tags_then_it_is_converted() { - var snap = 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 - { - /// These are some tags. - public int Foo() => 0; - } - """); - - const string expected = "These are some tags."; - - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + 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 + { + /// These are some tags. + public int Foo() => 0; + } + """); + + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("These are some tags.", emitted[1].Value); } [Fact] public void When_method_has_inheritdoc_then_it_is_resolved() { - var snap = TestHelper.GetGeneratedSourceSnapshot( - """ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using HotChocolate; - using HotChocolate.Types; - - namespace TestNamespace; - - /// - /// I am the most base class. - /// - public class BaseBaseClass - { - /// Method doc. - /// Parameter details. - public virtual void Bar(string baz) { } - } - - [QueryType] - internal static partial class Query - { - /// - public static int Bar(string baz) => 0; - } - """); - - const string expected = "Method doc."; - - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + var snapshot = + TestHelper.GetGeneratedSourceSnapshot( + """ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + /// + /// I am the most base class. + /// + public class BaseBaseClass + { + /// Method doc. + /// Parameter details. + public virtual void Bar(string baz) { } + } + + [QueryType] + internal static partial class Query + { + /// + public static int Bar(string baz) => 0; + } + """); + + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("Method doc.", emitted[1].Value); } [Fact] public void When_class_has_description_then_it_is_converted() { - var snap = 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 - { - /// - /// I am a test class. This should not be escaped: > - /// - public static int Bar() => 0; - } - """); - - const string expected = "I am a test class. This should not be escaped: >"; - - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + 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 + { + /// + /// I am a test class. This should not be escaped: > + /// + public static int Bar() => 0; + } + """); + + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("I am a test class. This should not be escaped: >", emitted[1].Value); } - [Fact] - public void When_method_has_returns_then_it_is_converted() - { - var snap = 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 - { - /// - /// Query and manages users. - /// - /// Bar - public static int Bar() => 0; - } - """); - - const string expected = "Query and manages users.\\n\\n\\n**Returns:**\\nBar"; - - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); - } + [Fact] + public void When_method_has_returns_then_it_is_converted() + { + 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 + { + /// + /// Query and manages users. + /// + /// Bar + public static int Bar() => 0; + } + """); + + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("Query and manages users.\\n\\n\\n**Returns:**\\nBar", emitted[1].Value); + } [Fact] public void When_method_has_exceptions_then_it_is_converted() { - var snap = 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 - { - /// - /// Query and manages users. - /// - /// Bar - /// Foo Error - /// Bar Error - public static int Bar() => 0; - } - """); - - const string expected = "Query and manages users.\\n\\n\\n**Returns:**\\nBar\\n\\n**Errors:**\\n1. FOO_ERROR: Foo Error\\n2. BAR_ERROR: Bar Error"; - - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + 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 + { + /// + /// Query and manages users. + /// + /// Bar + /// Foo Error + /// Bar Error + public static int Bar() => 0; + } + """); + + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("Query and manages users.\\n\\n\\n**Returns:**\\nBar\\n\\n**Errors:**\\n1. FOO_ERROR: Foo Error\\n2. BAR_ERROR: Bar Error", emitted[1].Value); } [Fact] public void When_method_has_exceptions_then_exceptions_with_no_code_will_be_ignored() { - var snap = 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 - { - /// - /// Query and manages users. - /// - /// Bar - /// Foo Error - /// Foo Error - /// Bar Error - public static int Bar() => 0; - } - """); - - const string expected = "Query and manages users.\\n\\n\\n**Returns:**\\nBar\\n\\n**Errors:**\\n1. FOO_ERROR: Foo Error\\n2. BAR_ERROR: Bar Error"; - - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + 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 + { + /// + /// Query and manages users. + /// + /// Bar + /// Foo Error + /// Foo Error + /// Bar Error + public static int Bar() => 0; + } + """); + + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("Query and manages users.\\n\\n\\n**Returns:**\\nBar\\n\\n**Errors:**\\n1. FOO_ERROR: Foo Error\\n2. BAR_ERROR: Bar Error", emitted[1].Value); } [Fact] public void When_method_has_only_exceptions_with_no_code_then_error_section_will_not_be_written() { - var snap = 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 - { - /// - /// Query and manages users. - /// - /// Bar - /// Foo Error - public static int Bar() => 0; - } - """); - - const string expected = "Query and manages users.\\n\\n\\n**Returns:**\\nBar"; - - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + 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 + { + /// + /// Query and manages users. + /// + /// Bar + /// Foo Error + public static int Bar() => 0; + } + """); + + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("Query and manages users.\\n\\n\\n**Returns:**\\nBar", emitted[1].Value); } [System.Text.RegularExpressions.GeneratedRegex("configuration.Description = \"(.*)\";")] diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.cs index 298b6d39812..60474f8691b 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ObjectTypeTests.XmlDocInference.cs @@ -1,125 +1,129 @@ -namespace HotChocolate.Types; +using System.Text.RegularExpressions; + +namespace HotChocolate.Types; public partial class ObjectTypeXmlDocInferenceTests { + private static readonly Regex s_description = DescriptionExtractorRegex(); + [Fact] public void Method_WithInheritdoc_And_MultipleLayersOfInheritance() { - var snap = TestHelper.GetGeneratedSourceSnapshot( - """ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using HotChocolate; - using HotChocolate.Types; - - namespace TestNamespace; - - public class BaseBaseClass - { - /// Method doc. - public virtual void Bar() { } - } - - public class BaseClass : BaseBaseClass - { - /// - public override void Bar() { } - } + var snapshot = + TestHelper.GetGeneratedSourceSnapshot( + """ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + public class BaseBaseClass + { + /// Method doc. + public virtual void Bar() { } + } - [QueryType] - internal static partial class Query - { - /// - public static int Bar(string baz) => 0; - } - """); + public class BaseClass : BaseBaseClass + { + /// + public override void Bar() { } + } - const string expected = "Method doc."; + [QueryType] + internal static partial class Query + { + /// + public static int Bar(string baz) => 0; + } + """); - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("Method doc.", emitted[1].Value); } [Fact] public void Method_WithInheritdoc_ThatContainsInheritdoc() { - var snap = TestHelper.GetGeneratedSourceSnapshot( - """ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using HotChocolate; - using HotChocolate.Types; - - namespace TestNamespace; - - public class BaseBaseClass - { - /// Method doc. - public virtual void Bar() { } - } - - public class BaseClass : BaseBaseClass - { - /// - /// Concrete Method doc. - /// - /// - public override void Bar() { } - } + var snapshot = + TestHelper.GetGeneratedSourceSnapshot( + """ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + public class BaseBaseClass + { + /// Method doc. + public virtual void Bar() { } + } - public class ConcreteClass : BaseClass - { - /// - public override void Bar() { } - } + public class BaseClass : BaseBaseClass + { + /// + /// Concrete Method doc. + /// + /// + public override void Bar() { } + } - [QueryType] - internal static partial class Query - { - /// - public static int Bar(string baz) => 0; - } - """); + public class ConcreteClass : BaseClass + { + /// + public override void Bar() { } + } - const string expected = "Concrete Method doc.\\nMethod doc."; + [QueryType] + internal static partial class Query + { + /// + public static int Bar(string baz) => 0; + } + """); - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("Concrete Method doc.\\nMethod doc.", emitted[1].Value); } [Fact] public void XmlDocumentation_Is_Overriden_By_DescriptionAttribute() { - var snap = 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 - { - /// - /// This is ... - /// - [GraphQLDescription("Nothing")] - public static string GetUser() => "User"; - } - """); - - const string expected = "Nothing"; + 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 + { + /// + /// This is ... + /// + [GraphQLDescription("Nothing")] + public static string GetUser() => "User"; + } + """); - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("Nothing", emitted[1].Value); } [Fact] @@ -200,38 +204,38 @@ public static partial class Query [Fact] public void XmlDocumentation_With_Nested_InheritdocCref() { - var snap = TestHelper.GetGeneratedSourceSnapshot( - """ - using System; - using HotChocolate.Types; - - namespace TestNamespace; + var snapshot = + TestHelper.GetGeneratedSourceSnapshot( + """ + using System; + using HotChocolate.Types; - /// - /// This type is similar useless to ''. - /// - public class FooType - { - } + namespace TestNamespace; - /// - /// The Bar type. - /// - public class BarType - { - } + /// + /// This type is similar useless to ''. + /// + public class FooType + { + } - [QueryType] - public static partial class Query - { - /// - public static string? Foo() => null; - } - """); + /// + /// The Bar type. + /// + public class BarType + { + } - const string expected = "This type is similar useless to 'The Bar type.'."; + [QueryType] + public static partial class Query + { + /// + public static string? Foo() => null; + } + """); - var emitted = DescriptionExtractorRegex().Matches(snap.Render()).Single().Groups; - Assert.Equal(expected, emitted[1].Value); + var content = snapshot.Match(); + var emitted = s_description.Matches(content).Single().Groups; + Assert.Equal("This type is similar useless to 'The Bar type.'.", emitted[1].Value); } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs index 2bebf1442d6..c37a7671d79 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs @@ -30,9 +30,7 @@ internal static partial class TestHelper private static readonly HashSet s_ignoreCodes = ["CS8652", "CS8632", "CS5001", "CS8019"]; public static Snapshot GetGeneratedSourceSnapshot([StringSyntax("csharp")] string sourceText) - { - return GetGeneratedSourceSnapshot([sourceText]); - } + => GetGeneratedSourceSnapshot([sourceText]); public static Snapshot GetGeneratedSourceSnapshot( string[] sourceTexts, diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.Method_WithInheritdoc_And_MultipleLayersOfInheritance.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.Method_WithInheritdoc_And_MultipleLayersOfInheritance.snap new file mode 100644 index 00000000000..2c6c23b4cf9 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.Method_WithInheritdoc_And_MultipleLayersOfInheritance.snap @@ -0,0 +1,152 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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(bindingResolver); + + 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("Bar", 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 = "Method doc."; + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + var bindingInfo = field.Context.ParameterBindingResolver; + var parameter = context.Resolvers.CreateParameterDescriptor_Bar_baz(); + var parameterInfo = bindingInfo.GetBindingInfo(parameter); + + if(parameterInfo.Kind is global::HotChocolate.Internal.ArgumentKind.Argument) + { + var argumentConfiguration = new global::HotChocolate.Types.Descriptors.Configurations.ArgumentConfiguration + { + Name = naming.GetMemberName("baz", global::HotChocolate.Types.MemberKind.Argument), + Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Input), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("string"))), + RuntimeType = typeof(string) + }; + + configuration.Arguments.Add(argumentConfiguration); + } + + configuration.Resolvers = context.Resolvers.Bar(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + private readonly global::HotChocolate.Internal.IParameterBinding _binding_Bar_baz; + + public __Resolvers(global::HotChocolate.Resolvers.ParameterBindingResolver bindingResolver) + { + _binding_Bar_baz = bindingResolver.GetBinding(CreateParameterDescriptor_Bar_baz()); + } + + public global::HotChocolate.Internal.ParameterDescriptor CreateParameterDescriptor_Bar_baz() + => new HotChocolate.Internal.ParameterDescriptor( + "baz", + typeof(string), + isNullable: false, + []); + + public HotChocolate.Resolvers.FieldResolverDelegates Bar() + { + var isPureResolver = _binding_Bar_baz.IsPure; + + return isPureResolver + ? new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Bar) + : new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: c => new(Bar(c))); + } + + private global::System.Object? Bar(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = _binding_Bar_baz.Execute(context); + var result = global::TestNamespace.Query.Bar(args0); + return result; + } + } + } +} + + +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.Method_WithInheritdoc_ThatContainsInheritdoc.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.Method_WithInheritdoc_ThatContainsInheritdoc.snap new file mode 100644 index 00000000000..1127734e654 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.Method_WithInheritdoc_ThatContainsInheritdoc.snap @@ -0,0 +1,152 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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(bindingResolver); + + 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("Bar", 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 = "Concrete Method doc.\nMethod doc."; + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + var bindingInfo = field.Context.ParameterBindingResolver; + var parameter = context.Resolvers.CreateParameterDescriptor_Bar_baz(); + var parameterInfo = bindingInfo.GetBindingInfo(parameter); + + if(parameterInfo.Kind is global::HotChocolate.Internal.ArgumentKind.Argument) + { + var argumentConfiguration = new global::HotChocolate.Types.Descriptors.Configurations.ArgumentConfiguration + { + Name = naming.GetMemberName("baz", global::HotChocolate.Types.MemberKind.Argument), + Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Input), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("string"))), + RuntimeType = typeof(string) + }; + + configuration.Arguments.Add(argumentConfiguration); + } + + configuration.Resolvers = context.Resolvers.Bar(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + private readonly global::HotChocolate.Internal.IParameterBinding _binding_Bar_baz; + + public __Resolvers(global::HotChocolate.Resolvers.ParameterBindingResolver bindingResolver) + { + _binding_Bar_baz = bindingResolver.GetBinding(CreateParameterDescriptor_Bar_baz()); + } + + public global::HotChocolate.Internal.ParameterDescriptor CreateParameterDescriptor_Bar_baz() + => new HotChocolate.Internal.ParameterDescriptor( + "baz", + typeof(string), + isNullable: false, + []); + + public HotChocolate.Resolvers.FieldResolverDelegates Bar() + { + var isPureResolver = _binding_Bar_baz.IsPure; + + return isPureResolver + ? new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Bar) + : new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: c => new(Bar(c))); + } + + private global::System.Object? Bar(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = _binding_Bar_baz.Execute(context); + var result = global::TestNamespace.Query.Bar(args0); + return result; + } + } + } +} + + +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_class_has_description_then_it_is_converted.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_class_has_description_then_it_is_converted.snap new file mode 100644 index 00000000000..6d2e6b0abd4 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_class_has_description_then_it_is_converted.snap @@ -0,0 +1,115 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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("Bar", 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 = "I am a test class. This should not be escaped: >"; + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.Bar(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + public HotChocolate.Resolvers.FieldResolverDelegates Bar() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Bar); + } + + private global::System.Object? Bar(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.Bar(); + return result; + } + } + } +} + + +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_description_has_generic_tags_then_it_is_converted.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_description_has_generic_tags_then_it_is_converted.snap new file mode 100644 index 00000000000..b9aa135aa25 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_description_has_generic_tags_then_it_is_converted.snap @@ -0,0 +1,175 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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("Foo", 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 = "These are some tags."; + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.Foo(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + public HotChocolate.Resolvers.FieldResolverDelegates Foo() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Foo); + } + + private global::System.Object? Foo(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = context.Parent().Foo(); + return result; + } + } + } +} + + +--------------- + +Compilation Diagnostics +--------------- +[ + { + "Id": "CS0708", + "Title": "", + "Severity": "Error", + "WarningLevel": 0, + "Location": ": (13,15)-(13,18)", + "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0708)", + "MessageFormat": "'{0}': cannot declare instance members in a static class", + "Message": "'Foo': cannot declare instance members in a static class", + "Category": "Compiler", + "CustomTags": [ + "Compiler", + "Telemetry", + "NotConfigurable" + ] + } +] +--------------- + +Assembly Emit Diagnostics +--------------- +[ + { + "Id": "CS0708", + "Title": "", + "Severity": "Error", + "WarningLevel": 0, + "Location": ": (13,15)-(13,18)", + "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0708)", + "MessageFormat": "'{0}': cannot declare instance members in a static class", + "Message": "'Foo': cannot declare instance members in a static class", + "Category": "Compiler", + "CustomTags": [ + "Compiler", + "Telemetry", + "NotConfigurable" + ] + }, + { + "Id": "CS0718", + "Title": "", + "Severity": "Error", + "WarningLevel": 0, + "Location": "Query.WaAdMHmlGJHjtEI4nqY7WA.hc.g.cs: (69,37)-(69,72)", + "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0718)", + "MessageFormat": "'{0}': static types cannot be used as type arguments", + "Message": "'Query': static types cannot be used as type arguments", + "Category": "Compiler", + "CustomTags": [ + "Compiler", + "Telemetry", + "NotConfigurable" + ] + } +] +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_description_has_paramref_tag_then_it_is_converted.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_description_has_paramref_tag_then_it_is_converted.snap new file mode 100644 index 00000000000..37a1e1bffbc --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_description_has_paramref_tag_then_it_is_converted.snap @@ -0,0 +1,212 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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(bindingResolver); + + 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("Foo", 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 = "This is a parameter reference to id."; + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + var bindingInfo = field.Context.ParameterBindingResolver; + var parameter = context.Resolvers.CreateParameterDescriptor_Foo_id(); + var parameterInfo = bindingInfo.GetBindingInfo(parameter); + + if(parameterInfo.Kind is global::HotChocolate.Internal.ArgumentKind.Argument) + { + var argumentConfiguration = new global::HotChocolate.Types.Descriptors.Configurations.ArgumentConfiguration + { + Name = naming.GetMemberName("id", global::HotChocolate.Types.MemberKind.Argument), + Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Input), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))), + RuntimeType = typeof(int) + }; + + configuration.Arguments.Add(argumentConfiguration); + } + + configuration.Resolvers = context.Resolvers.Foo(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + private readonly global::HotChocolate.Internal.IParameterBinding _binding_Foo_id; + + public __Resolvers(global::HotChocolate.Resolvers.ParameterBindingResolver bindingResolver) + { + _binding_Foo_id = bindingResolver.GetBinding(CreateParameterDescriptor_Foo_id()); + } + + public global::HotChocolate.Internal.ParameterDescriptor CreateParameterDescriptor_Foo_id() + => new HotChocolate.Internal.ParameterDescriptor( + "id", + typeof(int), + isNullable: false, + []); + + public HotChocolate.Resolvers.FieldResolverDelegates Foo() + { + var isPureResolver = _binding_Foo_id.IsPure; + + return isPureResolver + ? new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Foo) + : new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: c => new(Foo(c))); + } + + private global::System.Object? Foo(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = _binding_Foo_id.Execute(context); + var result = context.Parent().Foo(args0); + return result; + } + } + } +} + + +--------------- + +Compilation Diagnostics +--------------- +[ + { + "Id": "CS0708", + "Title": "", + "Severity": "Error", + "WarningLevel": 0, + "Location": ": (15,15)-(15,18)", + "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0708)", + "MessageFormat": "'{0}': cannot declare instance members in a static class", + "Message": "'Foo': cannot declare instance members in a static class", + "Category": "Compiler", + "CustomTags": [ + "Compiler", + "Telemetry", + "NotConfigurable" + ] + } +] +--------------- + +Assembly Emit Diagnostics +--------------- +[ + { + "Id": "CS0708", + "Title": "", + "Severity": "Error", + "WarningLevel": 0, + "Location": ": (15,15)-(15,18)", + "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0708)", + "MessageFormat": "'{0}': cannot declare instance members in a static class", + "Message": "'Foo': cannot declare instance members in a static class", + "Category": "Compiler", + "CustomTags": [ + "Compiler", + "Telemetry", + "NotConfigurable" + ] + }, + { + "Id": "CS0718", + "Title": "", + "Severity": "Error", + "WarningLevel": 0, + "Location": "Query.WaAdMHmlGJHjtEI4nqY7WA.hc.g.cs: (106,37)-(106,72)", + "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0718)", + "MessageFormat": "'{0}': static types cannot be used as type arguments", + "Message": "'Query': static types cannot be used as type arguments", + "Category": "Compiler", + "CustomTags": [ + "Compiler", + "Telemetry", + "NotConfigurable" + ] + } +] +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_description_has_see_tag_then_it_is_converted.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_description_has_see_tag_then_it_is_converted.snap new file mode 100644 index 00000000000..9953efdcecc --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_description_has_see_tag_then_it_is_converted.snap @@ -0,0 +1,111 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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("Foo", 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 = "null for the default Record.\nSee this and\nthis at\nhttps://foo.com/bar/baz."; + configuration.Type = typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Output); + configuration.ResultType = typeof(string); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.Foo(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + public HotChocolate.Resolvers.FieldResolverDelegates Foo() + => new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Foo); + + private global::System.Object? Foo(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.Foo; + return result; + } + } + } +} + + +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_exceptions_then_exceptions_with_no_code_will_be_ignored.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_exceptions_then_exceptions_with_no_code_will_be_ignored.snap new file mode 100644 index 00000000000..c76ff06a4ff --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_exceptions_then_exceptions_with_no_code_will_be_ignored.snap @@ -0,0 +1,115 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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("Bar", 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 = "Query and manages users.\n\n\n**Returns:**\nBar\n\n**Errors:**\n1. FOO_ERROR: Foo Error\n2. BAR_ERROR: Bar Error"; + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.Bar(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + public HotChocolate.Resolvers.FieldResolverDelegates Bar() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Bar); + } + + private global::System.Object? Bar(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.Bar(); + return result; + } + } + } +} + + +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_exceptions_then_it_is_converted.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_exceptions_then_it_is_converted.snap new file mode 100644 index 00000000000..c76ff06a4ff --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_exceptions_then_it_is_converted.snap @@ -0,0 +1,115 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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("Bar", 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 = "Query and manages users.\n\n\n**Returns:**\nBar\n\n**Errors:**\n1. FOO_ERROR: Foo Error\n2. BAR_ERROR: Bar Error"; + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.Bar(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + public HotChocolate.Resolvers.FieldResolverDelegates Bar() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Bar); + } + + private global::System.Object? Bar(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.Bar(); + return result; + } + } + } +} + + +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_inheritdoc_then_it_is_resolved.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_inheritdoc_then_it_is_resolved.snap new file mode 100644 index 00000000000..2c6c23b4cf9 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_inheritdoc_then_it_is_resolved.snap @@ -0,0 +1,152 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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(bindingResolver); + + 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("Bar", 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 = "Method doc."; + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + var bindingInfo = field.Context.ParameterBindingResolver; + var parameter = context.Resolvers.CreateParameterDescriptor_Bar_baz(); + var parameterInfo = bindingInfo.GetBindingInfo(parameter); + + if(parameterInfo.Kind is global::HotChocolate.Internal.ArgumentKind.Argument) + { + var argumentConfiguration = new global::HotChocolate.Types.Descriptors.Configurations.ArgumentConfiguration + { + Name = naming.GetMemberName("baz", global::HotChocolate.Types.MemberKind.Argument), + Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Input), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("string"))), + RuntimeType = typeof(string) + }; + + configuration.Arguments.Add(argumentConfiguration); + } + + configuration.Resolvers = context.Resolvers.Bar(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + private readonly global::HotChocolate.Internal.IParameterBinding _binding_Bar_baz; + + public __Resolvers(global::HotChocolate.Resolvers.ParameterBindingResolver bindingResolver) + { + _binding_Bar_baz = bindingResolver.GetBinding(CreateParameterDescriptor_Bar_baz()); + } + + public global::HotChocolate.Internal.ParameterDescriptor CreateParameterDescriptor_Bar_baz() + => new HotChocolate.Internal.ParameterDescriptor( + "baz", + typeof(string), + isNullable: false, + []); + + public HotChocolate.Resolvers.FieldResolverDelegates Bar() + { + var isPureResolver = _binding_Bar_baz.IsPure; + + return isPureResolver + ? new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Bar) + : new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: c => new(Bar(c))); + } + + private global::System.Object? Bar(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = _binding_Bar_baz.Execute(context); + var result = global::TestNamespace.Query.Bar(args0); + return result; + } + } + } +} + + +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_only_exceptions_with_no_code_then_error_section_will_not_be_written.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_only_exceptions_with_no_code_then_error_section_will_not_be_written.snap new file mode 100644 index 00000000000..a5217435470 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_only_exceptions_with_no_code_then_error_section_will_not_be_written.snap @@ -0,0 +1,115 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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("Bar", 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 = "Query and manages users.\n\n\n**Returns:**\nBar"; + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.Bar(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + public HotChocolate.Resolvers.FieldResolverDelegates Bar() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Bar); + } + + private global::System.Object? Bar(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.Bar(); + return result; + } + } + } +} + + +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_returns_then_it_is_converted.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_returns_then_it_is_converted.snap new file mode 100644 index 00000000000..a5217435470 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_method_has_returns_then_it_is_converted.snap @@ -0,0 +1,115 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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("Bar", 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 = "Query and manages users.\n\n\n**Returns:**\nBar"; + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.Bar(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + public HotChocolate.Resolvers.FieldResolverDelegates Bar() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Bar); + } + + private global::System.Object? Bar(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.Bar(); + return result; + } + } + } +} + + +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_xml_doc_is_missing_then_description_is_empty.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_xml_doc_is_missing_then_description_is_empty.snap new file mode 100644 index 00000000000..c9162f9697c --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_xml_doc_is_missing_then_description_is_empty.snap @@ -0,0 +1,114 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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.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; + } + } + } +} + + +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_xml_doc_with_multiple_breaks_is_read_then_they_are_not_stripped_away.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_xml_doc_with_multiple_breaks_is_read_then_they_are_not_stripped_away.snap new file mode 100644 index 00000000000..b4de7dc642b --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.When_xml_doc_with_multiple_breaks_is_read_then_they_are_not_stripped_away.snap @@ -0,0 +1,111 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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("Foo", 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 = "Query and manages users.\n \nPlease note:\n* Users ...\n* Users ...\n * Users ...\n * Users ...\n \nYou need one of the following role: Owner,\nEditor, use XYZ to manage permissions."; + configuration.Type = typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Output); + configuration.ResultType = typeof(string); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.Foo(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + public HotChocolate.Resolvers.FieldResolverDelegates Foo() + => new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Foo); + + private global::System.Object? Foo(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.Foo; + return result; + } + } + } +} + + +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.XmlDocumentation_Is_Overriden_By_DescriptionAttribute.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.XmlDocumentation_Is_Overriden_By_DescriptionAttribute.snap new file mode 100644 index 00000000000..e99c170da9b --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.XmlDocumentation_Is_Overriden_By_DescriptionAttribute.snap @@ -0,0 +1,115 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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 = "Nothing"; + 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; + } + } + } +} + + +--------------- diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.XmlDocumentation_With_Nested_InheritdocCref.snap b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.XmlDocumentation_With_Nested_InheritdocCref.snap new file mode 100644 index 00000000000..e20785c3e13 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeXmlDocInferenceTests.XmlDocumentation_With_Nested_InheritdocCref.snap @@ -0,0 +1,113 @@ +HotChocolateTypeModule.735550c.g.cs +--------------- +// + +#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 TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(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 +--------------- +// + +#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 +{ + public 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("Foo", 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 = "This type is similar useless to 'The Bar type.'."; + configuration.Type = typeInspector.GetTypeRef(typeof(string), HotChocolate.Types.TypeContext.Output); + configuration.ResultType = typeof(string); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.Foo(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + public HotChocolate.Resolvers.FieldResolverDelegates Foo() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Foo); + } + + private global::System.Object? Foo(global::HotChocolate.Resolvers.IResolverContext context) + { + var result = global::TestNamespace.Query.Foo(); + return result; + } + } + } +} + + +---------------