From 6fbb1f46908e0a6d63ccf1d7e7c55200ddc21efa Mon Sep 17 00:00:00 2001 From: Stefano Piva Date: Thu, 23 Dec 2021 14:29:35 +0100 Subject: [PATCH 1/2] #4 Support type params in ctor for generic classes --- .../Handlers/ApplyDocumentHandler.cs | 4 +++ .../Abstractions/AttributeServiceResolver.cs | 4 +++ .../ConstructorDocumentationStrategy.cs | 2 +- .../Strategies/MethodDocumentationStrategy.cs | 2 ++ .../PropertyDocumentationStrategy.cs | 4 +++ src/DotnetDocument/Syntax/SyntaxUtils.cs | 27 +++++++++++++++++++ src/DotnetDocument/Utils/EnglishUtils.cs | 2 ++ 7 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/DotnetDocument.Tools/Handlers/ApplyDocumentHandler.cs b/src/DotnetDocument.Tools/Handlers/ApplyDocumentHandler.cs index a4fd5b3..c5daf9e 100644 --- a/src/DotnetDocument.Tools/Handlers/ApplyDocumentHandler.cs +++ b/src/DotnetDocument.Tools/Handlers/ApplyDocumentHandler.cs @@ -76,8 +76,10 @@ public Result Apply(string? path, bool isDryRun) var undocumentedMembers = memberDocStatusList.Where(m => m.IsDocumented is not true).ToList(); foreach (var member in undocumentedMembers) + { _logger.LogInformation(" {File} (ln {Line}): {MemberType} '{MemberName}' has no document", member.FilePath, member.StartLine, member.Kind.ToString().Humanize(), member.Identifier); + } // If is dry run if (isDryRun) @@ -158,9 +160,11 @@ private IEnumerable GetFileDocumentationStatus(string _walker.Visit(root); foreach (var node in _walker.NodesWithXmlDoc) + { yield return new MemberDocumentationStatus(filePath, SyntaxUtils.FindMemberIdentifier(node), node.Kind(), true, null, node, node.GetLocation().GetLineSpan().StartLinePosition.ToString()); + } foreach (var node in _walker.NodesWithoutXmlDoc) { diff --git a/src/DotnetDocument/Strategies/Abstractions/AttributeServiceResolver.cs b/src/DotnetDocument/Strategies/Abstractions/AttributeServiceResolver.cs index 130bc14..3571bd2 100644 --- a/src/DotnetDocument/Strategies/Abstractions/AttributeServiceResolver.cs +++ b/src/DotnetDocument/Strategies/Abstractions/AttributeServiceResolver.cs @@ -45,11 +45,15 @@ public AttributeServiceResolver(IServiceProvider provider) => .Any(a => a.Key == key)); if (service is null) + { logger.LogWarning("No {ServiceType} implementation resolved matching {KeyType} key: '{Key}'", typeof(TService).Name, key.GetType().Name, key); + } else + { logger.LogTrace("Resolved implementation of {ServiceType} with key '{Key}': {ImplementationType}", typeof(TService).Name, key, service.GetType().Name); + } return service; } diff --git a/src/DotnetDocument/Strategies/ConstructorDocumentationStrategy.cs b/src/DotnetDocument/Strategies/ConstructorDocumentationStrategy.cs index a29c216..0c763e6 100644 --- a/src/DotnetDocument/Strategies/ConstructorDocumentationStrategy.cs +++ b/src/DotnetDocument/Strategies/ConstructorDocumentationStrategy.cs @@ -59,7 +59,7 @@ public override IEnumerable GetSupportedKinds() => new[] public override ConstructorDeclarationSyntax Apply(ConstructorDeclarationSyntax node) { // Retrieve constructor name - var ctorName = node.Identifier.Text; + var ctorName = SyntaxUtils.ExtractClassName(node); // Declare the summary by using the template from configuration var summary = new List diff --git a/src/DotnetDocument/Strategies/MethodDocumentationStrategy.cs b/src/DotnetDocument/Strategies/MethodDocumentationStrategy.cs index d57b7b7..63a269a 100644 --- a/src/DotnetDocument/Strategies/MethodDocumentationStrategy.cs +++ b/src/DotnetDocument/Strategies/MethodDocumentationStrategy.cs @@ -75,6 +75,7 @@ public override MethodDeclarationSyntax Apply(MethodDeclarationSyntax node) var returns = string.Empty; if (node.Body is not null) + { // Extract the last return statement which returns a variable // and humanize the name of the variable which will be used as // returns descriptions. Empty otherwise. @@ -83,6 +84,7 @@ public override MethodDeclarationSyntax Apply(MethodDeclarationSyntax node) .Select(r => _formatter .FormatName(_options.Returns.Template, (TemplateKeys.Name, r))) .LastOrDefault(); + } // TODO: Handle case where node.ExpressionBody is not null diff --git a/src/DotnetDocument/Strategies/PropertyDocumentationStrategy.cs b/src/DotnetDocument/Strategies/PropertyDocumentationStrategy.cs index 7f7dd44..4ee31db 100644 --- a/src/DotnetDocument/Strategies/PropertyDocumentationStrategy.cs +++ b/src/DotnetDocument/Strategies/PropertyDocumentationStrategy.cs @@ -71,11 +71,15 @@ public override PropertyDeclarationSyntax Apply(PropertyDeclarationSyntax node) .ToList(); if (accessors is not null && accessors.Any()) + { accessorsDescription = string.Join(" or ", accessors) .ToLower() .Humanize(); + } else + { accessorsDescription = _formatter.ConjugateThirdPersonSingular("Get"); + } var summary = new List { diff --git a/src/DotnetDocument/Syntax/SyntaxUtils.cs b/src/DotnetDocument/Syntax/SyntaxUtils.cs index 05620fa..4e92176 100644 --- a/src/DotnetDocument/Syntax/SyntaxUtils.cs +++ b/src/DotnetDocument/Syntax/SyntaxUtils.cs @@ -104,8 +104,10 @@ public static string FindMemberIdentifier(SyntaxNode node) public static IEnumerable ExtractBaseTypes(ClassDeclarationSyntax classDeclarationSyntax) { if (classDeclarationSyntax.BaseList is not null) + { return classDeclarationSyntax.BaseList.Types .Select(t => t.Type.ToString().Replace("<", "{").Replace(">", "}").Trim()); + } return new List(); } @@ -118,8 +120,10 @@ public static IEnumerable ExtractBaseTypes(ClassDeclarationSyntax classD public static IEnumerable ExtractBaseTypes(InterfaceDeclarationSyntax interfaceDeclarationSyntax) { if (interfaceDeclarationSyntax.BaseList is not null) + { return interfaceDeclarationSyntax.BaseList.Types .Select(t => t.Type.ToString().Replace("<", "{").Replace(">", "}").Trim()); + } return new List(); } @@ -257,8 +261,31 @@ public static IEnumerable ExtractReturnStatements(BlockSyntax body) if (body is null) yield break; foreach (var returnStatement in body.Statements.OfType()) + { if (returnStatement.Expression is IdentifierNameSyntax identifierName) yield return identifierName.Identifier.Text; + } + } + + /// + /// Extracts the class name using the specified constructor declaration syntax + /// + /// The constructor declaration syntax + /// The class name + public static string ExtractClassName(ConstructorDeclarationSyntax constructorDeclarationSyntax) + { + // Check if parent of this syntax is a class declaration with type params + if (constructorDeclarationSyntax.Parent is ClassDeclarationSyntax { TypeParameterList: { } } classDeclarationSyntax) + { + // Try to extract type params from the class declaration rather than from ctor + var typeParams = string + .Join(",", classDeclarationSyntax.TypeParameterList.Parameters + .Select(x => x.Identifier.Text)); + + return $"{constructorDeclarationSyntax.Identifier.Text}{{{typeParams}}}"; + } + + return constructorDeclarationSyntax.Identifier.Text; } /// diff --git a/src/DotnetDocument/Utils/EnglishUtils.cs b/src/DotnetDocument/Utils/EnglishUtils.cs index 7139c7a..6c9525e 100644 --- a/src/DotnetDocument/Utils/EnglishUtils.cs +++ b/src/DotnetDocument/Utils/EnglishUtils.cs @@ -18,7 +18,9 @@ public static string ConjugateToThirdPersonSingular(string verb) // Check if verb ends with one of the following chars if (verb.EndsWith("ch") || verb.EndsWith("s") || verb.EndsWith("sh") || verb.EndsWith("x") || verb.EndsWith("z") || verb.EndsWith("o")) + { return $"{verb}es"; + } // Check if verb is at least 3 chars long, ends with a consonant then y if (verb.Length > 2 && verb.Last() == 'y' && verb[^2].IsConsonant()) return $"{verb.RemoveEnd("y")}ies"; From 18dbb6e1c748df0e977345a048c942281a127dab Mon Sep 17 00:00:00 2001 From: Stefano Piva Date: Thu, 23 Dec 2021 14:38:45 +0100 Subject: [PATCH 2/2] Use null forgiving operator to avoid warnings --- src/DotnetDocument/Syntax/SyntaxUtils.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/DotnetDocument/Syntax/SyntaxUtils.cs b/src/DotnetDocument/Syntax/SyntaxUtils.cs index 4e92176..23e968f 100644 --- a/src/DotnetDocument/Syntax/SyntaxUtils.cs +++ b/src/DotnetDocument/Syntax/SyntaxUtils.cs @@ -278,8 +278,9 @@ public static string ExtractClassName(ConstructorDeclarationSyntax constructorDe if (constructorDeclarationSyntax.Parent is ClassDeclarationSyntax { TypeParameterList: { } } classDeclarationSyntax) { // Try to extract type params from the class declaration rather than from ctor + // Added the ! operator to make sure no warnings are thrown during CI var typeParams = string - .Join(",", classDeclarationSyntax.TypeParameterList.Parameters + .Join(",", classDeclarationSyntax.TypeParameterList.Parameters! .Select(x => x.Identifier.Text)); return $"{constructorDeclarationSyntax.Identifier.Text}{{{typeParams}}}";