From 02da7dcd39c89afa68b868db9f9c01c870a5fc7e Mon Sep 17 00:00:00 2001 From: XiaoYun Zhang Date: Mon, 7 Oct 2024 09:13:26 -0700 Subject: [PATCH 1/4] remove merging primitive from .editorconfig and gitignore --- dotnet/.editorconfig | 8 +------- dotnet/.gitignore | 6 ------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/dotnet/.editorconfig b/dotnet/.editorconfig index 2ead8ad1bf47..b013b5202b89 100644 --- a/dotnet/.editorconfig +++ b/dotnet/.editorconfig @@ -1,5 +1,4 @@ -<<<<<<< HEAD -# EditorConfig is awesome:http://EditorConfig.org +# EditorConfig is awesome:http://EditorConfig.org # top-most EditorConfig file root = true @@ -93,7 +92,6 @@ csharp_style_inlined_variable_declaration = true:error csharp_style_throw_expression = true:suggestion csharp_style_conditional_delegate_call = true:suggestion csharp_style_deconstructed_variable_declaration = true:suggestion -======= ; EditorConfig to support per-solution formatting. ; Use the EditorConfig VS add-in to make this work. ; http://editorconfig.org/ @@ -137,7 +135,6 @@ csharp_style_var_elsewhere = true:suggestion # Disallow throw expressions. csharp_style_throw_expression = false:suggestion ->>>>>>> agnext_main # Newline settings csharp_new_line_before_open_brace = all @@ -146,7 +143,6 @@ csharp_new_line_before_catch = true csharp_new_line_before_finally = true csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_members_in_anonymous_types = true -<<<<<<< HEAD csharp_new_line_between_query_expression_clauses = true # Identation options @@ -236,7 +232,6 @@ dotnet_diagnostic.CA2016.severity = suggestion # disable check for generated code [*.generated.cs] generated_code = true -======= # Namespace settings csharp_style_namespace_declarations = file_scoped:silent @@ -708,4 +703,3 @@ generated_code = true # IDE1591 Missing XML comment for publicly visible type or member dotnet_diagnostic.CS1591.severity = none ->>>>>>> agnext_main diff --git a/dotnet/.gitignore b/dotnet/.gitignore index 962ccf134f2f..25f613c7945f 100644 --- a/dotnet/.gitignore +++ b/dotnet/.gitignore @@ -1,6 +1,4 @@ -<<<<<<< HEAD # gitignore file for C#/VS -======= ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## @@ -20,7 +18,6 @@ mono_crash.* output ->>>>>>> agnext_main # Build results [Dd]ebug/ @@ -29,7 +26,6 @@ output [Rr]eleases/ x64/ x86/ -<<<<<<< HEAD build/ bld/ [Bb]in/ @@ -51,7 +47,6 @@ output/ # JetBrains Rider .idea/ -======= [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ @@ -525,4 +520,3 @@ sk-azfunc-server/local.settings.json temp .mono/** **/values.xml ->>>>>>> agnext_main From ed9b1b977a191b1d460ec400f4ac786f79d00abd Mon Sep 17 00:00:00 2001 From: XiaoYun Zhang Date: Mon, 7 Oct 2024 09:33:29 -0700 Subject: [PATCH 2/4] use file scope namespace --- dotnet/src/AutoGen.Mistral/DTOs/Error.cs | 53 ++- .../DocumentCommentExtension.cs | 405 +++++++++--------- .../FunctionCallGenerator.cs | 393 +++++++++-------- 3 files changed, 424 insertions(+), 427 deletions(-) diff --git a/dotnet/src/AutoGen.Mistral/DTOs/Error.cs b/dotnet/src/AutoGen.Mistral/DTOs/Error.cs index 8bddcfc776c6..79a2c2e2f662 100644 --- a/dotnet/src/AutoGen.Mistral/DTOs/Error.cs +++ b/dotnet/src/AutoGen.Mistral/DTOs/Error.cs @@ -3,37 +3,36 @@ using System.Text.Json.Serialization; -namespace AutoGen.Mistral +namespace AutoGen.Mistral; + +public class Error { - public class Error + public Error(string type, string message, string? param = default(string), string? code = default(string)) { - public Error(string type, string message, string? param = default(string), string? code = default(string)) - { - Type = type; - Message = message; - Param = param; - Code = code; - } + Type = type; + Message = message; + Param = param; + Code = code; + } - [JsonPropertyName("type")] - public string Type { get; set; } + [JsonPropertyName("type")] + public string Type { get; set; } - /// - /// Gets or Sets Message - /// - [JsonPropertyName("message")] - public string Message { get; set; } + /// + /// Gets or Sets Message + /// + [JsonPropertyName("message")] + public string Message { get; set; } - /// - /// Gets or Sets Param - /// - [JsonPropertyName("param")] - public string? Param { get; set; } + /// + /// Gets or Sets Param + /// + [JsonPropertyName("param")] + public string? Param { get; set; } - /// - /// Gets or Sets Code - /// - [JsonPropertyName("code")] - public string? Code { get; set; } - } + /// + /// Gets or Sets Code + /// + [JsonPropertyName("code")] + public string? Code { get; set; } } diff --git a/dotnet/src/AutoGen.SourceGenerator/DocumentCommentExtension.cs b/dotnet/src/AutoGen.SourceGenerator/DocumentCommentExtension.cs index 0892d597a7bb..14db58a4b540 100644 --- a/dotnet/src/AutoGen.SourceGenerator/DocumentCommentExtension.cs +++ b/dotnet/src/AutoGen.SourceGenerator/DocumentCommentExtension.cs @@ -10,286 +10,285 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; // copyright: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/DocumentationCommentExtensions.cs#L17 -namespace AutoGen.SourceGenerator +namespace AutoGen.SourceGenerator; + +internal static class DocumentCommentExtension { - internal static class DocumentCommentExtension + public static bool IsMissingOrDefault(this SyntaxToken token) + { + return token.IsKind(SyntaxKind.None) + || token.IsMissing; + } + + public static string? GetParameterDescriptionFromDocumentationCommentTriviaSyntax(this DocumentationCommentTriviaSyntax documentationCommentTrivia, string parameterName) { - public static bool IsMissingOrDefault(this SyntaxToken token) + var parameterElements = documentationCommentTrivia.Content.GetXmlElements("param"); + + var parameter = parameterElements.FirstOrDefault(element => { - return token.IsKind(SyntaxKind.None) - || token.IsMissing; - } + var xml = XElement.Parse(element.ToString()); + var nameAttribute = xml.Attribute("name"); + return nameAttribute != null && nameAttribute.Value == parameterName; + }); - public static string? GetParameterDescriptionFromDocumentationCommentTriviaSyntax(this DocumentationCommentTriviaSyntax documentationCommentTrivia, string parameterName) + if (parameter is not null) { - var parameterElements = documentationCommentTrivia.Content.GetXmlElements("param"); + var xml = XElement.Parse(parameter.ToString()); - var parameter = parameterElements.FirstOrDefault(element => - { - var xml = XElement.Parse(element.ToString()); - var nameAttribute = xml.Attribute("name"); - return nameAttribute != null && nameAttribute.Value == parameterName; - }); + return xml.Nodes().OfType().FirstOrDefault()?.Value; + } - if (parameter is not null) - { - var xml = XElement.Parse(parameter.ToString()); + return null; + } - return xml.Nodes().OfType().FirstOrDefault()?.Value; - } + public static string? GetNamespaceNameFromClassDeclarationSyntax(this ClassDeclarationSyntax classDeclaration) + { + return classDeclaration.Parent is NamespaceDeclarationSyntax namespaceDeclarationSyntax ? namespaceDeclarationSyntax.Name.ToString() + : classDeclaration.Parent is FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax ? fileScopedNamespaceDeclarationSyntax.Name.ToString() + : null; + } + public static DocumentationCommentTriviaSyntax? GetDocumentationCommentTriviaSyntax(this SyntaxNode node) + { + if (node == null) + { return null; } - public static string? GetNamespaceNameFromClassDeclarationSyntax(this ClassDeclarationSyntax classDeclaration) + foreach (var leadingTrivia in node.GetLeadingTrivia()) { - return classDeclaration.Parent is NamespaceDeclarationSyntax namespaceDeclarationSyntax ? namespaceDeclarationSyntax.Name.ToString() - : classDeclaration.Parent is FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax ? fileScopedNamespaceDeclarationSyntax.Name.ToString() - : null; + if (leadingTrivia.GetStructure() is DocumentationCommentTriviaSyntax structure) + { + return structure; + } } - public static DocumentationCommentTriviaSyntax? GetDocumentationCommentTriviaSyntax(this SyntaxNode node) + return null; + } + + public static XmlNodeSyntax GetFirstXmlElement(this SyntaxList content, string elementName) + { + return content.GetXmlElements(elementName).FirstOrDefault(); + } + + public static IEnumerable GetXmlElements(this SyntaxList content, string elementName) + { + foreach (XmlNodeSyntax syntax in content) { - if (node == null) + if (syntax is XmlEmptyElementSyntax emptyElement) { - return null; + if (string.Equals(elementName, emptyElement.Name.ToString(), StringComparison.Ordinal)) + { + yield return emptyElement; + } + + continue; } - foreach (var leadingTrivia in node.GetLeadingTrivia()) + if (syntax is XmlElementSyntax elementSyntax) { - if (leadingTrivia.GetStructure() is DocumentationCommentTriviaSyntax structure) + if (string.Equals(elementName, elementSyntax.StartTag?.Name?.ToString(), StringComparison.Ordinal)) { - return structure; + yield return elementSyntax; } - } - return null; + continue; + } } + } + + public static T ReplaceExteriorTrivia(this T node, SyntaxTrivia trivia) + where T : XmlNodeSyntax + { + // Make sure to include a space after the '///' characters. + SyntaxTrivia triviaWithSpace = SyntaxFactory.DocumentationCommentExterior(trivia.ToString() + " "); + + return node.ReplaceTrivia( + node.DescendantTrivia(descendIntoTrivia: true).Where(i => i.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia)), + (originalTrivia, rewrittenTrivia) => SelectExteriorTrivia(rewrittenTrivia, trivia, triviaWithSpace)); + } - public static XmlNodeSyntax GetFirstXmlElement(this SyntaxList content, string elementName) + public static SyntaxList WithoutFirstAndLastNewlines(this SyntaxList summaryContent) + { + if (summaryContent.Count == 0) { - return content.GetXmlElements(elementName).FirstOrDefault(); + return summaryContent; } - public static IEnumerable GetXmlElements(this SyntaxList content, string elementName) + if (!(summaryContent[0] is XmlTextSyntax firstSyntax)) { - foreach (XmlNodeSyntax syntax in content) - { - if (syntax is XmlEmptyElementSyntax emptyElement) - { - if (string.Equals(elementName, emptyElement.Name.ToString(), StringComparison.Ordinal)) - { - yield return emptyElement; - } - - continue; - } - - if (syntax is XmlElementSyntax elementSyntax) - { - if (string.Equals(elementName, elementSyntax.StartTag?.Name?.ToString(), StringComparison.Ordinal)) - { - yield return elementSyntax; - } - - continue; - } - } + return summaryContent; } - public static T ReplaceExteriorTrivia(this T node, SyntaxTrivia trivia) - where T : XmlNodeSyntax + if (!(summaryContent[summaryContent.Count - 1] is XmlTextSyntax lastSyntax)) { - // Make sure to include a space after the '///' characters. - SyntaxTrivia triviaWithSpace = SyntaxFactory.DocumentationCommentExterior(trivia.ToString() + " "); - - return node.ReplaceTrivia( - node.DescendantTrivia(descendIntoTrivia: true).Where(i => i.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia)), - (originalTrivia, rewrittenTrivia) => SelectExteriorTrivia(rewrittenTrivia, trivia, triviaWithSpace)); + return summaryContent; } - public static SyntaxList WithoutFirstAndLastNewlines(this SyntaxList summaryContent) + SyntaxTokenList firstSyntaxTokens = firstSyntax.TextTokens; + + int removeFromStart; + if (IsXmlNewLine(firstSyntaxTokens[0])) + { + removeFromStart = 1; + } + else { - if (summaryContent.Count == 0) + if (!IsXmlWhitespace(firstSyntaxTokens[0])) { return summaryContent; } - if (!(summaryContent[0] is XmlTextSyntax firstSyntax)) + if (!IsXmlNewLine(firstSyntaxTokens[1])) { return summaryContent; } - if (!(summaryContent[summaryContent.Count - 1] is XmlTextSyntax lastSyntax)) - { - return summaryContent; - } + removeFromStart = 2; + } - SyntaxTokenList firstSyntaxTokens = firstSyntax.TextTokens; + SyntaxTokenList lastSyntaxTokens = lastSyntax.TextTokens; - int removeFromStart; - if (IsXmlNewLine(firstSyntaxTokens[0])) - { - removeFromStart = 1; - } - else + int removeFromEnd; + if (IsXmlNewLine(lastSyntaxTokens[lastSyntaxTokens.Count - 1])) + { + removeFromEnd = 1; + } + else + { + if (!IsXmlWhitespace(lastSyntaxTokens[lastSyntaxTokens.Count - 1])) { - if (!IsXmlWhitespace(firstSyntaxTokens[0])) - { - return summaryContent; - } - - if (!IsXmlNewLine(firstSyntaxTokens[1])) - { - return summaryContent; - } - - removeFromStart = 2; + return summaryContent; } - SyntaxTokenList lastSyntaxTokens = lastSyntax.TextTokens; - - int removeFromEnd; - if (IsXmlNewLine(lastSyntaxTokens[lastSyntaxTokens.Count - 1])) + if (!IsXmlNewLine(lastSyntaxTokens[lastSyntaxTokens.Count - 2])) { - removeFromEnd = 1; + return summaryContent; } - else - { - if (!IsXmlWhitespace(lastSyntaxTokens[lastSyntaxTokens.Count - 1])) - { - return summaryContent; - } - if (!IsXmlNewLine(lastSyntaxTokens[lastSyntaxTokens.Count - 2])) - { - return summaryContent; - } + removeFromEnd = 2; + } - removeFromEnd = 2; - } + for (int i = 0; i < removeFromStart; i++) + { + firstSyntaxTokens = firstSyntaxTokens.RemoveAt(0); + } - for (int i = 0; i < removeFromStart; i++) - { - firstSyntaxTokens = firstSyntaxTokens.RemoveAt(0); - } + if (firstSyntax == lastSyntax) + { + lastSyntaxTokens = firstSyntaxTokens; + } - if (firstSyntax == lastSyntax) + for (int i = 0; i < removeFromEnd; i++) + { + if (!lastSyntaxTokens.Any()) { - lastSyntaxTokens = firstSyntaxTokens; + break; } - for (int i = 0; i < removeFromEnd; i++) - { - if (!lastSyntaxTokens.Any()) - { - break; - } + lastSyntaxTokens = lastSyntaxTokens.RemoveAt(lastSyntaxTokens.Count - 1); + } - lastSyntaxTokens = lastSyntaxTokens.RemoveAt(lastSyntaxTokens.Count - 1); - } + summaryContent = summaryContent.RemoveAt(summaryContent.Count - 1); + if (lastSyntaxTokens.Count != 0) + { + summaryContent = summaryContent.Add(lastSyntax.WithTextTokens(lastSyntaxTokens)); + } - summaryContent = summaryContent.RemoveAt(summaryContent.Count - 1); - if (lastSyntaxTokens.Count != 0) + if (firstSyntax != lastSyntax) + { + summaryContent = summaryContent.RemoveAt(0); + if (firstSyntaxTokens.Count != 0) { - summaryContent = summaryContent.Add(lastSyntax.WithTextTokens(lastSyntaxTokens)); + summaryContent = summaryContent.Insert(0, firstSyntax.WithTextTokens(firstSyntaxTokens)); } + } - if (firstSyntax != lastSyntax) - { - summaryContent = summaryContent.RemoveAt(0); - if (firstSyntaxTokens.Count != 0) - { - summaryContent = summaryContent.Insert(0, firstSyntax.WithTextTokens(firstSyntaxTokens)); - } - } + if (summaryContent.Count > 0) + { + // Make sure to remove the leading trivia + summaryContent = summaryContent.Replace(summaryContent[0], summaryContent[0].WithLeadingTrivia()); - if (summaryContent.Count > 0) + // Remove leading spaces (between the start tag and the start of the paragraph content) + if (summaryContent[0] is XmlTextSyntax firstTextSyntax && firstTextSyntax.TextTokens.Count > 0) { - // Make sure to remove the leading trivia - summaryContent = summaryContent.Replace(summaryContent[0], summaryContent[0].WithLeadingTrivia()); - - // Remove leading spaces (between the start tag and the start of the paragraph content) - if (summaryContent[0] is XmlTextSyntax firstTextSyntax && firstTextSyntax.TextTokens.Count > 0) + SyntaxToken firstTextToken = firstTextSyntax.TextTokens[0]; + string firstTokenText = firstTextToken.Text; + string trimmed = firstTokenText.TrimStart(); + if (trimmed != firstTokenText) { - SyntaxToken firstTextToken = firstTextSyntax.TextTokens[0]; - string firstTokenText = firstTextToken.Text; - string trimmed = firstTokenText.TrimStart(); - if (trimmed != firstTokenText) - { - SyntaxToken newFirstToken = SyntaxFactory.Token( - firstTextToken.LeadingTrivia, - firstTextToken.Kind(), - trimmed, - firstTextToken.ValueText.TrimStart(), - firstTextToken.TrailingTrivia); - - summaryContent = summaryContent.Replace(firstTextSyntax, firstTextSyntax.ReplaceToken(firstTextToken, newFirstToken)); - } + SyntaxToken newFirstToken = SyntaxFactory.Token( + firstTextToken.LeadingTrivia, + firstTextToken.Kind(), + trimmed, + firstTextToken.ValueText.TrimStart(), + firstTextToken.TrailingTrivia); + + summaryContent = summaryContent.Replace(firstTextSyntax, firstTextSyntax.ReplaceToken(firstTextToken, newFirstToken)); } } - - return summaryContent; } - public static bool IsXmlNewLine(this SyntaxToken node) - { - return node.IsKind(SyntaxKind.XmlTextLiteralNewLineToken); - } + return summaryContent; + } + + public static bool IsXmlNewLine(this SyntaxToken node) + { + return node.IsKind(SyntaxKind.XmlTextLiteralNewLineToken); + } + + public static bool IsXmlWhitespace(this SyntaxToken node) + { + return node.IsKind(SyntaxKind.XmlTextLiteralToken) + && string.IsNullOrWhiteSpace(node.Text); + } - public static bool IsXmlWhitespace(this SyntaxToken node) + /// + /// Adjust the leading and trailing trivia associated with + /// tokens to ensure the formatter properly indents the exterior trivia. + /// + /// The type of syntax node. + /// The syntax node to adjust tokens. + /// A equivalent to the input , adjusted by moving any + /// trailing trivia from tokens to be leading trivia of the + /// following token. + public static T AdjustDocumentationCommentNewLineTrivia(this T node) + where T : SyntaxNode + { + var tokensForAdjustment = + from token in node.DescendantTokens() + where token.IsKind(SyntaxKind.XmlTextLiteralNewLineToken) + where token.HasTrailingTrivia + let next = token.GetNextToken(includeZeroWidth: true, includeSkipped: true, includeDirectives: true, includeDocumentationComments: true) + where !next.IsMissingOrDefault() + select new KeyValuePair(token, next); + + Dictionary replacements = new Dictionary(); + foreach (var pair in tokensForAdjustment) { - return node.IsKind(SyntaxKind.XmlTextLiteralToken) - && string.IsNullOrWhiteSpace(node.Text); + replacements[pair.Key] = pair.Key.WithTrailingTrivia(); + replacements[pair.Value] = pair.Value.WithLeadingTrivia(pair.Value.LeadingTrivia.InsertRange(0, pair.Key.TrailingTrivia)); } - /// - /// Adjust the leading and trailing trivia associated with - /// tokens to ensure the formatter properly indents the exterior trivia. - /// - /// The type of syntax node. - /// The syntax node to adjust tokens. - /// A equivalent to the input , adjusted by moving any - /// trailing trivia from tokens to be leading trivia of the - /// following token. - public static T AdjustDocumentationCommentNewLineTrivia(this T node) - where T : SyntaxNode - { - var tokensForAdjustment = - from token in node.DescendantTokens() - where token.IsKind(SyntaxKind.XmlTextLiteralNewLineToken) - where token.HasTrailingTrivia - let next = token.GetNextToken(includeZeroWidth: true, includeSkipped: true, includeDirectives: true, includeDocumentationComments: true) - where !next.IsMissingOrDefault() - select new KeyValuePair(token, next); - - Dictionary replacements = new Dictionary(); - foreach (var pair in tokensForAdjustment) - { - replacements[pair.Key] = pair.Key.WithTrailingTrivia(); - replacements[pair.Value] = pair.Value.WithLeadingTrivia(pair.Value.LeadingTrivia.InsertRange(0, pair.Key.TrailingTrivia)); - } + return node.ReplaceTokens(replacements.Keys, (originalToken, rewrittenToken) => replacements[originalToken]); + } - return node.ReplaceTokens(replacements.Keys, (originalToken, rewrittenToken) => replacements[originalToken]); - } + public static XmlNameSyntax? GetName(this XmlNodeSyntax element) + { + return (element as XmlElementSyntax)?.StartTag?.Name + ?? (element as XmlEmptyElementSyntax)?.Name; + } - public static XmlNameSyntax? GetName(this XmlNodeSyntax element) + private static SyntaxTrivia SelectExteriorTrivia(SyntaxTrivia rewrittenTrivia, SyntaxTrivia trivia, SyntaxTrivia triviaWithSpace) + { + // if the trivia had a trailing space, make sure to preserve it + if (rewrittenTrivia.ToString().EndsWith(" ")) { - return (element as XmlElementSyntax)?.StartTag?.Name - ?? (element as XmlEmptyElementSyntax)?.Name; + return triviaWithSpace; } - private static SyntaxTrivia SelectExteriorTrivia(SyntaxTrivia rewrittenTrivia, SyntaxTrivia trivia, SyntaxTrivia triviaWithSpace) - { - // if the trivia had a trailing space, make sure to preserve it - if (rewrittenTrivia.ToString().EndsWith(" ")) - { - return triviaWithSpace; - } - - // otherwise the space is part of the leading trivia of the following token, so don't add an extra one to - // the exterior trivia - return trivia; - } + // otherwise the space is part of the leading trivia of the following token, so don't add an extra one to + // the exterior trivia + return trivia; } } diff --git a/dotnet/src/AutoGen.SourceGenerator/FunctionCallGenerator.cs b/dotnet/src/AutoGen.SourceGenerator/FunctionCallGenerator.cs index 4fa6169f7c3c..d5813682030e 100644 --- a/dotnet/src/AutoGen.SourceGenerator/FunctionCallGenerator.cs +++ b/dotnet/src/AutoGen.SourceGenerator/FunctionCallGenerator.cs @@ -12,236 +12,235 @@ using Microsoft.CodeAnalysis.Text; using Newtonsoft.Json; -namespace AutoGen.SourceGenerator +namespace AutoGen.SourceGenerator; + +[Generator] +public partial class FunctionCallGenerator : IIncrementalGenerator { - [Generator] - public partial class FunctionCallGenerator : IIncrementalGenerator - { - private const string FUNCTION_CALL_ATTRIBUTION = "AutoGen.Core.FunctionAttribute"; + private const string FUNCTION_CALL_ATTRIBUTION = "AutoGen.Core.FunctionAttribute"; - public void Initialize(IncrementalGeneratorInitializationContext context) - { + public void Initialize(IncrementalGeneratorInitializationContext context) + { #if LAUNCH_DEBUGGER - if (!System.Diagnostics.Debugger.IsAttached) - { - System.Diagnostics.Debugger.Launch(); - } + if (!System.Diagnostics.Debugger.IsAttached) + { + System.Diagnostics.Debugger.Launch(); + } #endif - var optionProvider = context.AnalyzerConfigOptionsProvider.Select((provider, ct) => + var optionProvider = context.AnalyzerConfigOptionsProvider.Select((provider, ct) => + { + var generateFunctionDefinitionContract = provider.GlobalOptions.TryGetValue("build_property.EnableContract", out var value) && value?.ToLowerInvariant() == "true"; + + return generateFunctionDefinitionContract; + }); + // step 1 + // filter syntax tree and search syntax node that satisfied the following conditions + // - is partial class + var partialClassSyntaxProvider = context.SyntaxProvider.CreateSyntaxProvider( + (node, ct) => + { + return node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.Modifiers.Any(SyntaxKind.PartialKeyword); + }, + (ctx, ct) => { - var generateFunctionDefinitionContract = provider.GlobalOptions.TryGetValue("build_property.EnableContract", out var value) && value?.ToLowerInvariant() == "true"; + // first check if any method of the class has FunctionAttribution attribute + // if not, then return null + var filePath = ctx.Node.SyntaxTree.FilePath; + var fileName = Path.GetFileNameWithoutExtension(filePath); + + var classDeclarationSyntax = ctx.Node as ClassDeclarationSyntax; + var nameSpace = classDeclarationSyntax?.Parent as NamespaceDeclarationSyntax; + var fullClassName = $"{nameSpace?.Name}.{classDeclarationSyntax!.Identifier}"; + if (classDeclarationSyntax == null) + { + return null; + } - return generateFunctionDefinitionContract; - }); - // step 1 - // filter syntax tree and search syntax node that satisfied the following conditions - // - is partial class - var partialClassSyntaxProvider = context.SyntaxProvider.CreateSyntaxProvider( - (node, ct) => + if (!classDeclarationSyntax.Members.Any(member => member.AttributeLists.Any(attributeList => attributeList.Attributes.Any(attribute => { - return node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.Modifiers.Any(SyntaxKind.PartialKeyword); - }, - (ctx, ct) => + return ctx.SemanticModel.GetSymbolInfo(attribute).Symbol is IMethodSymbol methodSymbol && methodSymbol.ContainingType.ToDisplayString() == FUNCTION_CALL_ATTRIBUTION; + })))) { - // first check if any method of the class has FunctionAttribution attribute - // if not, then return null - var filePath = ctx.Node.SyntaxTree.FilePath; - var fileName = Path.GetFileNameWithoutExtension(filePath); - - var classDeclarationSyntax = ctx.Node as ClassDeclarationSyntax; - var nameSpace = classDeclarationSyntax?.Parent as NamespaceDeclarationSyntax; - var fullClassName = $"{nameSpace?.Name}.{classDeclarationSyntax!.Identifier}"; - if (classDeclarationSyntax == null) - { - return null; - } - - if (!classDeclarationSyntax.Members.Any(member => member.AttributeLists.Any(attributeList => attributeList.Attributes.Any(attribute => - { - return ctx.SemanticModel.GetSymbolInfo(attribute).Symbol is IMethodSymbol methodSymbol && methodSymbol.ContainingType.ToDisplayString() == FUNCTION_CALL_ATTRIBUTION; - })))) - { - return null; - } + return null; + } - // collect methods that has FunctionAttribution attribute - var methodDeclarationSyntaxes = classDeclarationSyntax.Members.Where(member => member.AttributeLists.Any(attributeList => attributeList.Attributes.Any(attribute => - { - return ctx.SemanticModel.GetSymbolInfo(attribute).Symbol is IMethodSymbol methodSymbol && methodSymbol.ContainingType.ToDisplayString() == FUNCTION_CALL_ATTRIBUTION; - }))) - .Select(member => member as MethodDeclarationSyntax) - .Where(method => method != null); - - var className = classDeclarationSyntax.Identifier.ToString(); - var namespaceName = classDeclarationSyntax.GetNamespaceNameFromClassDeclarationSyntax(); - var functionContracts = methodDeclarationSyntaxes.Select(method => CreateFunctionContract(method!, className, namespaceName)); - - return new PartialClassOutput(fullClassName, classDeclarationSyntax, functionContracts); - }) - .Where(node => node != null) - .Collect(); - - var aggregateProvider = optionProvider.Combine(partialClassSyntaxProvider); - // step 2 - context.RegisterSourceOutput(aggregateProvider, - (ctx, source) => + // collect methods that has FunctionAttribution attribute + var methodDeclarationSyntaxes = classDeclarationSyntax.Members.Where(member => member.AttributeLists.Any(attributeList => attributeList.Attributes.Any(attribute => { - var groups = source.Right.GroupBy(item => item!.FullClassName); - foreach (var group in groups) + return ctx.SemanticModel.GetSymbolInfo(attribute).Symbol is IMethodSymbol methodSymbol && methodSymbol.ContainingType.ToDisplayString() == FUNCTION_CALL_ATTRIBUTION; + }))) + .Select(member => member as MethodDeclarationSyntax) + .Where(method => method != null); + + var className = classDeclarationSyntax.Identifier.ToString(); + var namespaceName = classDeclarationSyntax.GetNamespaceNameFromClassDeclarationSyntax(); + var functionContracts = methodDeclarationSyntaxes.Select(method => CreateFunctionContract(method!, className, namespaceName)); + + return new PartialClassOutput(fullClassName, classDeclarationSyntax, functionContracts); + }) + .Where(node => node != null) + .Collect(); + + var aggregateProvider = optionProvider.Combine(partialClassSyntaxProvider); + // step 2 + context.RegisterSourceOutput(aggregateProvider, + (ctx, source) => + { + var groups = source.Right.GroupBy(item => item!.FullClassName); + foreach (var group in groups) + { + var functionContracts = group.SelectMany(item => item!.FunctionContracts).ToArray(); + var className = group.First()!.ClassDeclarationSyntax.Identifier.ToString(); + var namespaceName = group.First()!.ClassDeclarationSyntax.GetNamespaceNameFromClassDeclarationSyntax() ?? string.Empty; + var functionTT = new FunctionCallTemplate { - var functionContracts = group.SelectMany(item => item!.FunctionContracts).ToArray(); - var className = group.First()!.ClassDeclarationSyntax.Identifier.ToString(); - var namespaceName = group.First()!.ClassDeclarationSyntax.GetNamespaceNameFromClassDeclarationSyntax() ?? string.Empty; - var functionTT = new FunctionCallTemplate - { - NameSpace = namespaceName, - ClassName = className, - FunctionContracts = functionContracts.ToArray(), - }; + NameSpace = namespaceName, + ClassName = className, + FunctionContracts = functionContracts.ToArray(), + }; - var functionSource = functionTT.TransformText(); - var fileName = $"{className}.generated.cs"; + var functionSource = functionTT.TransformText(); + var fileName = $"{className}.generated.cs"; - ctx.AddSource(fileName, SourceText.From(functionSource, System.Text.Encoding.UTF8)); - File.WriteAllText(Path.Combine(Path.GetTempPath(), fileName), functionSource); - } + ctx.AddSource(fileName, SourceText.From(functionSource, System.Text.Encoding.UTF8)); + File.WriteAllText(Path.Combine(Path.GetTempPath(), fileName), functionSource); + } - if (source.Left) - { - var overallFunctionDefinition = source.Right.SelectMany(x => x!.FunctionContracts.Select(y => new { fullClassName = x.FullClassName, y = y })); - var overallFunctionDefinitionObject = overallFunctionDefinition.Select( - x => new + if (source.Left) + { + var overallFunctionDefinition = source.Right.SelectMany(x => x!.FunctionContracts.Select(y => new { fullClassName = x.FullClassName, y = y })); + var overallFunctionDefinitionObject = overallFunctionDefinition.Select( + x => new + { + fullClassName = x.fullClassName, + functionDefinition = new { - fullClassName = x.fullClassName, - functionDefinition = new + x.y.Name, + x.y.Description, + x.y.ReturnType, + Parameters = x.y.Parameters.Select(y => new { - x.y.Name, - x.y.Description, - x.y.ReturnType, - Parameters = x.y.Parameters.Select(y => new - { - y.Name, - y.Description, - y.JsonType, - y.JsonItemType, - y.Type, - y.IsOptional, - y.DefaultValue, - }), - }, - }); - - var json = JsonConvert.SerializeObject(overallFunctionDefinitionObject, formatting: Formatting.Indented); - // wrap json inside csharp block, as SG doesn't support generating non-source file - json = $@"/* wrap json inside csharp block, as SG doesn't support generating non-source file + y.Name, + y.Description, + y.JsonType, + y.JsonItemType, + y.Type, + y.IsOptional, + y.DefaultValue, + }), + }, + }); + + var json = JsonConvert.SerializeObject(overallFunctionDefinitionObject, formatting: Formatting.Indented); + // wrap json inside csharp block, as SG doesn't support generating non-source file + json = $@"/* wrap json inside csharp block, as SG doesn't support generating non-source file {json} */"; - ctx.AddSource("FunctionDefinition.json", SourceText.From(json, System.Text.Encoding.UTF8)); - } - }); - } + ctx.AddSource("FunctionDefinition.json", SourceText.From(json, System.Text.Encoding.UTF8)); + } + }); + } - private class PartialClassOutput + private class PartialClassOutput + { + public PartialClassOutput(string fullClassName, ClassDeclarationSyntax classDeclarationSyntax, IEnumerable functionContracts) { - public PartialClassOutput(string fullClassName, ClassDeclarationSyntax classDeclarationSyntax, IEnumerable functionContracts) - { - FullClassName = fullClassName; - ClassDeclarationSyntax = classDeclarationSyntax; - FunctionContracts = functionContracts; - } + FullClassName = fullClassName; + ClassDeclarationSyntax = classDeclarationSyntax; + FunctionContracts = functionContracts; + } - public string FullClassName { get; } + public string FullClassName { get; } - public ClassDeclarationSyntax ClassDeclarationSyntax { get; } + public ClassDeclarationSyntax ClassDeclarationSyntax { get; } - public IEnumerable FunctionContracts { get; } - } + public IEnumerable FunctionContracts { get; } + } - private SourceGeneratorFunctionContract CreateFunctionContract(MethodDeclarationSyntax method, string? className, string? namespaceName) - { - // get function_call attribute - var functionCallAttribute = method.AttributeLists.SelectMany(attributeList => attributeList.Attributes) - .FirstOrDefault(attribute => attribute.Name.ToString() == FUNCTION_CALL_ATTRIBUTION); - // get document string if exist - var documentationCommentTrivia = method.GetDocumentationCommentTriviaSyntax(); + private SourceGeneratorFunctionContract CreateFunctionContract(MethodDeclarationSyntax method, string? className, string? namespaceName) + { + // get function_call attribute + var functionCallAttribute = method.AttributeLists.SelectMany(attributeList => attributeList.Attributes) + .FirstOrDefault(attribute => attribute.Name.ToString() == FUNCTION_CALL_ATTRIBUTION); + // get document string if exist + var documentationCommentTrivia = method.GetDocumentationCommentTriviaSyntax(); - var functionName = method.Identifier.ToString(); - var functionDescription = functionCallAttribute?.ArgumentList?.Arguments.FirstOrDefault(argument => argument.NameEquals?.Name.ToString() == "Description")?.Expression.ToString() ?? string.Empty; + var functionName = method.Identifier.ToString(); + var functionDescription = functionCallAttribute?.ArgumentList?.Arguments.FirstOrDefault(argument => argument.NameEquals?.Name.ToString() == "Description")?.Expression.ToString() ?? string.Empty; - if (string.IsNullOrEmpty(functionDescription)) + if (string.IsNullOrEmpty(functionDescription)) + { + // if functionDescription is empty, then try to get it from documentationCommentTrivia + // firstly, try getting from tag + var summary = documentationCommentTrivia?.Content.GetFirstXmlElement("summary"); + if (summary is not null && XElement.Parse(summary.ToString()) is XElement element) { - // if functionDescription is empty, then try to get it from documentationCommentTrivia - // firstly, try getting from tag - var summary = documentationCommentTrivia?.Content.GetFirstXmlElement("summary"); - if (summary is not null && XElement.Parse(summary.ToString()) is XElement element) - { - functionDescription = element.Nodes().OfType().FirstOrDefault()?.Value; - - // remove [space...][//|///][space...] from functionDescription - // replace [^\S\r\n]+[\/]+\s* with empty string - functionDescription = System.Text.RegularExpressions.Regex.Replace(functionDescription, @"[^\S\r\n]+\/[\/]+\s*", string.Empty); - } - else - { - // if tag is not exist, then simply use the entire leading trivia as functionDescription - functionDescription = method.GetLeadingTrivia().ToString(); + functionDescription = element.Nodes().OfType().FirstOrDefault()?.Value; - // remove [space...][//|///][space...] from functionDescription - // replace [^\S\r\n]+[\/]+\s* with empty string - functionDescription = System.Text.RegularExpressions.Regex.Replace(functionDescription, @"[^\S\r\n]+\/[\/]+\s*", string.Empty); - } + // remove [space...][//|///][space...] from functionDescription + // replace [^\S\r\n]+[\/]+\s* with empty string + functionDescription = System.Text.RegularExpressions.Regex.Replace(functionDescription, @"[^\S\r\n]+\/[\/]+\s*", string.Empty); } - - // get parameters - var parameters = method.ParameterList.Parameters.Select(parameter => + else { - var description = $"{parameter.Identifier}. type is {parameter.Type}"; + // if tag is not exist, then simply use the entire leading trivia as functionDescription + functionDescription = method.GetLeadingTrivia().ToString(); - // try to get parameter description from documentationCommentTrivia - var parameterDocumentationComment = documentationCommentTrivia?.GetParameterDescriptionFromDocumentationCommentTriviaSyntax(parameter.Identifier.ToString()); - if (parameterDocumentationComment is not null) - { - description = parameterDocumentationComment.ToString(); - // remove [space...][//|///][space...] from functionDescription - // replace [^\S\r\n]+[\/]+\s* with empty string - description = System.Text.RegularExpressions.Regex.Replace(description, @"[^\S\r\n]+\/[\/]+\s*", string.Empty); - } - var jsonItemType = parameter.Type!.ToString().EndsWith("[]") ? parameter.Type!.ToString().Substring(0, parameter.Type!.ToString().Length - 2) : null; - return new SourceGeneratorParameterContract - { - Name = parameter.Identifier.ToString(), - JsonType = parameter.Type!.ToString() switch - { - "string" => "string", - "string[]" => "array", - "System.Int32" or "int" => "integer", - "System.Int64" or "long" => "integer", - "System.Single" or "float" => "number", - "System.Double" or "double" => "number", - "System.Boolean" or "bool" => "boolean", - "System.DateTime" => "string", - "System.Guid" => "string", - "System.Object" => "object", - _ => "object", - }, - JsonItemType = jsonItemType, - Type = parameter.Type!.ToString(), - Description = description, - IsOptional = parameter.Default != null, - // if Default is null or "null", then DefaultValue is null - DefaultValue = parameter.Default?.ToString() == "null" ? null : parameter.Default?.Value.ToString(), - }; - }); + // remove [space...][//|///][space...] from functionDescription + // replace [^\S\r\n]+[\/]+\s* with empty string + functionDescription = System.Text.RegularExpressions.Regex.Replace(functionDescription, @"[^\S\r\n]+\/[\/]+\s*", string.Empty); + } + } + + // get parameters + var parameters = method.ParameterList.Parameters.Select(parameter => + { + var description = $"{parameter.Identifier}. type is {parameter.Type}"; - return new SourceGeneratorFunctionContract + // try to get parameter description from documentationCommentTrivia + var parameterDocumentationComment = documentationCommentTrivia?.GetParameterDescriptionFromDocumentationCommentTriviaSyntax(parameter.Identifier.ToString()); + if (parameterDocumentationComment is not null) { - ClassName = className, - Namespace = namespaceName, - Name = functionName, - Description = functionDescription?.Trim() ?? functionName, - Parameters = parameters.ToArray(), - ReturnType = method.ReturnType.ToString(), + description = parameterDocumentationComment.ToString(); + // remove [space...][//|///][space...] from functionDescription + // replace [^\S\r\n]+[\/]+\s* with empty string + description = System.Text.RegularExpressions.Regex.Replace(description, @"[^\S\r\n]+\/[\/]+\s*", string.Empty); + } + var jsonItemType = parameter.Type!.ToString().EndsWith("[]") ? parameter.Type!.ToString().Substring(0, parameter.Type!.ToString().Length - 2) : null; + return new SourceGeneratorParameterContract + { + Name = parameter.Identifier.ToString(), + JsonType = parameter.Type!.ToString() switch + { + "string" => "string", + "string[]" => "array", + "System.Int32" or "int" => "integer", + "System.Int64" or "long" => "integer", + "System.Single" or "float" => "number", + "System.Double" or "double" => "number", + "System.Boolean" or "bool" => "boolean", + "System.DateTime" => "string", + "System.Guid" => "string", + "System.Object" => "object", + _ => "object", + }, + JsonItemType = jsonItemType, + Type = parameter.Type!.ToString(), + Description = description, + IsOptional = parameter.Default != null, + // if Default is null or "null", then DefaultValue is null + DefaultValue = parameter.Default?.ToString() == "null" ? null : parameter.Default?.Value.ToString(), }; - } + }); + + return new SourceGeneratorFunctionContract + { + ClassName = className, + Namespace = namespaceName, + Name = functionName, + Description = functionDescription?.Trim() ?? functionName, + Parameters = parameters.ToArray(), + ReturnType = method.ReturnType.ToString(), + }; } } From 4d9e9a5e573a9a9b33d2171598ef78ba757564af Mon Sep 17 00:00:00 2001 From: XiaoYun Zhang Date: Mon, 7 Oct 2024 09:39:27 -0700 Subject: [PATCH 3/4] remove file-scope name --- .../SourceGeneratorFunctionContract.cs | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/dotnet/src/AutoGen.SourceGenerator/SourceGeneratorFunctionContract.cs b/dotnet/src/AutoGen.SourceGenerator/SourceGeneratorFunctionContract.cs index 45650fb01146..7ce2f3e3b08b 100644 --- a/dotnet/src/AutoGen.SourceGenerator/SourceGeneratorFunctionContract.cs +++ b/dotnet/src/AutoGen.SourceGenerator/SourceGeneratorFunctionContract.cs @@ -1,40 +1,39 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // SourceGeneratorFunctionContract.cs -namespace AutoGen.SourceGenerator +namespace AutoGen.SourceGenerator; + +internal class SourceGeneratorFunctionContract { - internal class SourceGeneratorFunctionContract - { - public string? Namespace { get; set; } + public string? Namespace { get; set; } - public string? ClassName { get; set; } + public string? ClassName { get; set; } - public string? Name { get; set; } + public string? Name { get; set; } - public string? Description { get; set; } + public string? Description { get; set; } - public string? ReturnDescription { get; set; } + public string? ReturnDescription { get; set; } - public SourceGeneratorParameterContract[]? Parameters { get; set; } + public SourceGeneratorParameterContract[]? Parameters { get; set; } - public string? ReturnType { get; set; } - } + public string? ReturnType { get; set; } +} - internal class SourceGeneratorParameterContract - { - public string? Name { get; set; } +internal class SourceGeneratorParameterContract +{ + public string? Name { get; set; } - public string? Description { get; set; } + public string? Description { get; set; } - public string? JsonType { get; set; } + public string? JsonType { get; set; } - public string? JsonItemType { get; set; } + public string? JsonItemType { get; set; } - public string? Type { get; set; } + public string? Type { get; set; } - public bool IsOptional { get; set; } + public bool IsOptional { get; set; } - public string? DefaultValue { get; set; } + public string? DefaultValue { get; set; } - } } From 0bc6a756c9ed4fb91c9a98af5d034112108120b4 Mon Sep 17 00:00:00 2001 From: XiaoYun Zhang Date: Mon, 7 Oct 2024 09:54:00 -0700 Subject: [PATCH 4/4] fix format --- .../AutoGen.SourceGenerator/SourceGeneratorFunctionContract.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/AutoGen.SourceGenerator/SourceGeneratorFunctionContract.cs b/dotnet/src/AutoGen.SourceGenerator/SourceGeneratorFunctionContract.cs index 7ce2f3e3b08b..df954da4584e 100644 --- a/dotnet/src/AutoGen.SourceGenerator/SourceGeneratorFunctionContract.cs +++ b/dotnet/src/AutoGen.SourceGenerator/SourceGeneratorFunctionContract.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // SourceGeneratorFunctionContract.cs namespace AutoGen.SourceGenerator;