Skip to content

Commit

Permalink
SourceGenerator no needs GenerateDocumentationFile
Browse files Browse the repository at this point in the history
  • Loading branch information
neuecc committed Mar 15, 2024
1 parent f23adc1 commit ae18e2f
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 15 deletions.
2 changes: 1 addition & 1 deletion sandbox/ConsoleApp1/ConsoleApp1.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<NoWarn>1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/Claudia.FunctionGenerator/DiagnosticDescriptors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ internal static class DiagnosticDescriptors
public static readonly DiagnosticDescriptor MethodNeedsDocumentationCommentXml = new(
id: "CLFG005",
title: "Method needs documentation comment xml",
messageFormat: "The '{0}' method has no documentation comment, define it and generate it(<GenerateDocumentationFile>true</GenerateDocumentationFile> in PropertyGroup of csproj)",
messageFormat: "The '{0}' method has no documentation comment",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
Expand Down
11 changes: 5 additions & 6 deletions src/Claudia.FunctionGenerator/Emitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,17 @@ public static class PromptXml

void EmitToolDescription(Method method)
{
var docComment = method.Symbol.GetDocumentationCommentXml();
var xml = XElement.Parse(docComment);
var docComment = method.Syntax.GetDocumentationCommentTriviaSyntax()!;

var description = ((string)xml.Element("summary")).Replace("\"", "'").Trim();
var description = docComment.GetSummary().Replace("\"", "'");

var parameters = new List<XElement>();
foreach (var p in xml.Elements("param"))
foreach (var p in docComment.GetParams())
{
var paramDescription = ((string)p).Replace("\"", "'").Trim();
var paramDescription = p.Description.Replace("\"", "'");

// type retrieve from method symbol
var name = p.Attribute("name").Value.Trim();
var name = p.Name;
var paramType = method.Symbol.Parameters.First(x => x.Name == name).Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);

parameters.Add(new XElement("parameter",
Expand Down
13 changes: 6 additions & 7 deletions src/Claudia.FunctionGenerator/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,15 @@ internal ParseResult[] Parse()
// source.TargetNode
var method = (IMethodSymbol)source.TargetSymbol;

var docXml = method.GetDocumentationCommentXml();
if (string.IsNullOrWhiteSpace(docXml))
var docComment = source.TargetNode.GetDocumentationCommentTriviaSyntax();
if (docComment == null)
{
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.MethodNeedsDocumentationCommentXml, method.Locations[0], method.Name));
continue;
}
else
{
var xml = XElement.Parse(docXml);
var description = ((string)xml.Element("summary")).Replace("\"", "'").Trim();
var description = docComment.GetSummary().Replace("\"", "'");
if (string.IsNullOrWhiteSpace(description))
{
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.MethodNeedsSummary, method.Locations[0], method.Name));
Expand All @@ -83,16 +82,16 @@ internal ParseResult[] Parse()
var parameterNames = new HashSet<string>(method.Parameters.Select(x => x.Name));
if (parameterNames.Count != 0)
{
foreach (var p in xml.Elements("param"))
foreach (var p in docComment.GetParams())
{
var desc = (string)p;
var desc = p.Description;
if (string.IsNullOrWhiteSpace(desc))
{
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ParameterNeedsDescription, method.Locations[0], method.Name, p.Name));
continue;
}

var name = p.Attribute("name").Value.Trim();
var name = p.Name;
parameterNames.Remove(name);
}

Expand Down
86 changes: 86 additions & 0 deletions src/Claudia.FunctionGenerator/RoslynExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.CodeAnalysis.CSharp;

namespace Claudia.FunctionGenerator;

internal static class RoslynExtensions
{
public static DocumentationCommentTriviaSyntax? GetDocumentationCommentTriviaSyntax(this SyntaxNode node)
{
// Hack note:
// ISymbol.GetDocumentationCommtentXml requires<GenerateDocumentaionFile>true </>.
// However, getting the DocumentationCommentTrivia of a SyntaxNode also requires the same condition.
// It can only be obtained when DocumentationMode is Parse or Diagnostic, but when<GenerateDocumentaionFile>false </>,
// it becomes None, and the necessary Trivia cannot be obtained.
// Therefore, we will attempt to reparse and retrieve it.

// About DocumentationMode and Trivia: https://github.com/dotnet/roslyn/issues/58210
if (node.SyntaxTree.Options.DocumentationMode == DocumentationMode.None)
{
var withDocumentationComment = node.SyntaxTree.Options.WithDocumentationMode(DocumentationMode.Parse);
var code = node.ToFullString();
var newTree = CSharpSyntaxTree.ParseText(code, (CSharpParseOptions)withDocumentationComment);
node = newTree.GetRoot();
}

foreach (var leadingTrivia in node.GetLeadingTrivia())
{
if (leadingTrivia.GetStructure() is DocumentationCommentTriviaSyntax structure)
{
return structure;
}
}

return null;
}

static IEnumerable<XmlNodeSyntax> GetXmlElements(this SyntaxList<XmlNodeSyntax> content, string elementName)
{
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;
}
}
}

public static string GetSummary(this DocumentationCommentTriviaSyntax docComment)
{
var summary = docComment.Content.GetXmlElements("summary").FirstOrDefault() as XmlElementSyntax;
if (summary == null) return "";

return summary.Content.ToString().Replace("///", "").Trim();
}

public static IEnumerable<(string Name, string Description)> GetParams(this DocumentationCommentTriviaSyntax docComment)
{
foreach (var item in docComment.Content.GetXmlElements("param").OfType<XmlElementSyntax>())
{
var name = item.StartTag.Attributes.OfType<XmlNameAttributeSyntax>().FirstOrDefault()?.Identifier.Identifier.ValueText.Replace("///", "").Trim() ?? "";
var desc = item.Content.ToString().Replace("///", "").Trim() ?? "";
yield return (name, desc);
}

yield break;
}
}

0 comments on commit ae18e2f

Please sign in to comment.