diff --git a/src/DotnetDocument.Tools/Handlers/ApplyDocumentHandler.cs b/src/DotnetDocument.Tools/Handlers/ApplyDocumentHandler.cs index a4fd5b3..fc5dd0b 100644 --- a/src/DotnetDocument.Tools/Handlers/ApplyDocumentHandler.cs +++ b/src/DotnetDocument.Tools/Handlers/ApplyDocumentHandler.cs @@ -32,7 +32,7 @@ public class ApplyDocumentHandler : IApplyDocumentHandler /// /// The service resolver /// - private readonly IServiceResolver _serviceResolver; + private readonly IServiceResolver _strategyResolver; /// /// The walker @@ -49,7 +49,7 @@ public class ApplyDocumentHandler : IApplyDocumentHandler public ApplyDocumentHandler(ILogger logger, IServiceResolver serviceResolver, DocumentationSyntaxWalker walker, IOptions appSettings) => - (_logger, _serviceResolver, _walker, _documentationSettings) = + (_logger, _strategyResolver, _walker, _documentationSettings) = (logger, serviceResolver, walker, appSettings.Value); /// @@ -73,11 +73,13 @@ public Result Apply(string? path, bool isDryRun) var memberDocStatusList = GetFilesDocumentationStatus(files); // Get the list of undocumented members - var undocumentedMembers = memberDocStatusList.Where(m => m.IsDocumented is not true).ToList(); + var undocumentedMembers = memberDocStatusList.Where(m => m.NeedsDocumentation).ToList(); foreach (var member in undocumentedMembers) - _logger.LogInformation(" {File} (ln {Line}): {MemberType} '{MemberName}' has no document", + { + _logger.LogInformation(" {File} (ln {Line}): {MemberType} '{MemberName}' has no documentation", member.FilePath, member.StartLine, member.Kind.ToString().Humanize(), member.Identifier); + } // If is dry run if (isDryRun) @@ -87,7 +89,7 @@ public Result Apply(string? path, bool isDryRun) // Don't go ahead with saving. // If there are members without doc return `undocumented members` - return memberDocStatusList.Any(m => m.IsDocumented is not true) + return memberDocStatusList.Any(m => m.NeedsDocumentation) ? Result.UndocumentedMembers : Result.Success; } @@ -110,10 +112,19 @@ public Result Apply(string? path, bool isDryRun) // Replace the var changedSyntaxTree = root.ReplaceNodes(_walker.NodesWithoutXmlDoc, - (node, syntaxNode) => _serviceResolver - .Resolve(syntaxNode.Kind().ToString()) - ? - .Apply(syntaxNode) ?? syntaxNode); + (originalNode, replacedNode) => + { + var documentationAttempt = _strategyResolver + .Resolve(replacedNode.Kind().ToString()) + ?.Apply(replacedNode); + + if (documentationAttempt is null || documentationAttempt.Value.IsChanged is false) + { + return replacedNode; + } + + return documentationAttempt.Value.NodeWithDocs; + }); // TODO: Don't write if no changes @@ -158,19 +169,32 @@ 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.Kind(), needsDocumentation: false, nodeWithoutDocument: null, documentedNode: node, node.GetLocation().GetLineSpan().StartLinePosition.ToString()); + } foreach (var node in _walker.NodesWithoutXmlDoc) { - var nodeWithDoc = _serviceResolver - .Resolve(node.Kind().ToString()) - ? - .Apply(node); + var documentationStrategy = _strategyResolver + .Resolve(node.Kind().ToString()); + + // If not documentation strategy found for this node type then skip it + if (documentationStrategy is null) + { + _logger.LogWarning("Documentation strategy is null"); + yield break; + } + + var (needsDocumentation, nodeWithDocs) = documentationStrategy.Apply(node); + + var nodeWithoutXmlDocs = needsDocumentation ? node : null; + var nodeWithXmlDocs = needsDocumentation ? nodeWithDocs : node; yield return new MemberDocumentationStatus(filePath, SyntaxUtils.FindMemberIdentifier(node), - node.Kind(), false, node, nodeWithDoc, + node.Kind(), needsDocumentation: needsDocumentation, nodeWithoutDocument: nodeWithoutXmlDocs, + documentedNode: nodeWithXmlDocs, node.GetLocation().GetLineSpan().StartLinePosition.ToString()); } } diff --git a/src/DotnetDocument.Tools/Handlers/MemberDocumentationStatus.cs b/src/DotnetDocument.Tools/Handlers/MemberDocumentationStatus.cs index bcdbf0b..6e01015 100644 --- a/src/DotnetDocument.Tools/Handlers/MemberDocumentationStatus.cs +++ b/src/DotnetDocument.Tools/Handlers/MemberDocumentationStatus.cs @@ -14,17 +14,17 @@ public class MemberDocumentationStatus /// The file path /// The identifier /// The kind - /// The is documented + /// The is documented /// The node without document /// The documented node /// The start line - public MemberDocumentationStatus(string filePath, string identifier, SyntaxKind kind, bool isDocumented, + public MemberDocumentationStatus(string filePath, string identifier, SyntaxKind kind, bool needsDocumentation, SyntaxNode? nodeWithoutDocument, SyntaxNode? documentedNode, string startLine) { FilePath = filePath; Identifier = identifier; Kind = kind; - IsDocumented = isDocumented; + NeedsDocumentation = needsDocumentation; NodeWithoutDocument = nodeWithoutDocument; DocumentedNode = documentedNode; StartLine = startLine; @@ -48,7 +48,7 @@ public MemberDocumentationStatus(string filePath, string identifier, SyntaxKind /// /// Gets or inits the value of the is documented /// - public bool IsDocumented { get; init; } + public bool NeedsDocumentation { get; init; } /// /// Gets the value of the node without document diff --git a/src/DotnetDocument/Configuration/DocumentationOptions.cs b/src/DotnetDocument/Configuration/DocumentationOptions.cs index 67a3f02..631117d 100644 --- a/src/DotnetDocument/Configuration/DocumentationOptions.cs +++ b/src/DotnetDocument/Configuration/DocumentationOptions.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Microsoft.CodeAnalysis.CSharp; @@ -18,6 +19,17 @@ public abstract class MemberDocumentationOptionsBase /// public bool Required { get; init; } = true; + /// + /// Gets or sets the value of the apply on modifiers + /// + public List ApplyOnModifiers { get; set; } = new() + { + "public", + //"private", + //"internal", + //"protected" + }; + /// /// Gets the syntax kind /// @@ -484,28 +496,22 @@ public class DocumentationOptions public Dictionary Verbs { get; init; } = new() { { - "to", - "returns" + "to", "returns" }, { - "from", - "creates" + "from", "creates" }, { - "as", - "converts" + "as", "converts" }, { - "with", - "adds" + "with", "adds" }, { - "setup", - "setup" + "setup", "setup" }, { - "main", - "main" + "main", "main" } }; @@ -515,8 +521,7 @@ public class DocumentationOptions public Dictionary Aliases { get; init; } = new() { { - "sut", - "system under test" + "sut", "system under test" } }; diff --git a/src/DotnetDocument/Strategies/Abstractions/AttributeServiceResolver.cs b/src/DotnetDocument/Strategies/Abstractions/AttributeServiceResolver.cs index e9dfe8b..fab24d7 100644 --- a/src/DotnetDocument/Strategies/Abstractions/AttributeServiceResolver.cs +++ b/src/DotnetDocument/Strategies/Abstractions/AttributeServiceResolver.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -9,6 +10,7 @@ namespace DotnetDocument.Strategies.Abstractions /// The attribute service resolver class /// /// + [SuppressMessage("Performance", "CA1848:Use the LoggerMessage delegates")] public class AttributeServiceResolver : IServiceResolver { /// @@ -45,11 +47,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/Abstractions/DocumentationStrategyBase.cs b/src/DotnetDocument/Strategies/Abstractions/DocumentationStrategyBase.cs index be37679..0979f4f 100644 --- a/src/DotnetDocument/Strategies/Abstractions/DocumentationStrategyBase.cs +++ b/src/DotnetDocument/Strategies/Abstractions/DocumentationStrategyBase.cs @@ -22,15 +22,16 @@ public abstract class DocumentationStrategyBase : IDocumentationStrategy wher /// Applies the node /// /// The node - /// The syntax node - public SyntaxNode Apply(SyntaxNode node) => Apply(node as T ?? throw new InvalidOperationException()); + /// The bool is changed syntax node node with docs + public (bool IsChanged, SyntaxNode NodeWithDocs) Apply(SyntaxNode node) => + Apply(node as T ?? throw new InvalidOperationException()); /// /// Applies the node /// /// The node - /// The - public abstract T Apply(T node); + /// The bool is changed node with docs + public abstract (bool IsChanged, T NodeWithDocs) Apply(T node); /// /// Gets the documentation builder diff --git a/src/DotnetDocument/Strategies/Abstractions/IDocumentationStrategy.cs b/src/DotnetDocument/Strategies/Abstractions/IDocumentationStrategy.cs index 64ca384..e18e0ed 100644 --- a/src/DotnetDocument/Strategies/Abstractions/IDocumentationStrategy.cs +++ b/src/DotnetDocument/Strategies/Abstractions/IDocumentationStrategy.cs @@ -15,11 +15,6 @@ public interface IDocumentationStrategy /// An enumerable of syntax kind IEnumerable GetSupportedKinds(); - /// - /// Applies the node - /// - /// The node - /// The syntax node - SyntaxNode Apply(SyntaxNode node); + (bool IsChanged, SyntaxNode NodeWithDocs) Apply(SyntaxNode node); } } diff --git a/src/DotnetDocument/Strategies/ClassDocumentationStrategy.cs b/src/DotnetDocument/Strategies/ClassDocumentationStrategy.cs index 2a37d1a..841529d 100644 --- a/src/DotnetDocument/Strategies/ClassDocumentationStrategy.cs +++ b/src/DotnetDocument/Strategies/ClassDocumentationStrategy.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using DotnetDocument.Configuration; @@ -55,9 +56,11 @@ public override IEnumerable GetSupportedKinds() => new[] /// Applies the node /// /// The node - /// The class declaration syntax - public override ClassDeclarationSyntax Apply(ClassDeclarationSyntax node) + /// The bool is changed class declaration syntax node with docs + public override (bool IsChanged, ClassDeclarationSyntax NodeWithDocs) Apply(ClassDeclarationSyntax node) { + ArgumentNullException.ThrowIfNull(node); + // Retrieve class name var className = node.Identifier.Text; @@ -82,7 +85,7 @@ public override ClassDeclarationSyntax Apply(ClassDeclarationSyntax node) builder.WithSeeAlso(baseTypes); } - return builder.Build(); + return (true, builder.Build()); } } } diff --git a/src/DotnetDocument/Strategies/ConstructorDocumentationStrategy.cs b/src/DotnetDocument/Strategies/ConstructorDocumentationStrategy.cs index 0c763e6..f89ec84 100644 --- a/src/DotnetDocument/Strategies/ConstructorDocumentationStrategy.cs +++ b/src/DotnetDocument/Strategies/ConstructorDocumentationStrategy.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using DotnetDocument.Configuration; @@ -55,16 +56,20 @@ public override IEnumerable GetSupportedKinds() => new[] /// Applies the node /// /// The node - /// The constructor declaration syntax - public override ConstructorDeclarationSyntax Apply(ConstructorDeclarationSyntax node) + /// The bool is changed constructor declaration syntax node with docs + public override (bool IsChanged, ConstructorDeclarationSyntax NodeWithDocs) Apply( + ConstructorDeclarationSyntax node) { + ArgumentNullException.ThrowIfNull(node); + // Retrieve constructor name var ctorName = SyntaxUtils.ExtractClassName(node); // Declare the summary by using the template from configuration var summary = new List { - _options.Summary.Template.Replace(TemplateKeys.Name, $"<<{ctorName}>>") + _options.Summary.Template.Replace(TemplateKeys.Name, $"<<{ctorName}>>", + StringComparison.InvariantCulture) }; var exceptions = new List<(string, string)>(); @@ -97,12 +102,14 @@ public override ConstructorDeclarationSyntax Apply(ConstructorDeclarationSyntax .Select(p => (p, _formatter .FormatName(_options.Parameters.Template, (TemplateKeys.Name, p)))); - return GetDocumentationBuilder() + var nodeWithDocs = GetDocumentationBuilder() .For(node) .WithSummary(summary.ToArray()) .WithParams(@params) .WithExceptions(exceptions.ToArray()) .Build(); + + return (true, nodeWithDocs); } } } diff --git a/src/DotnetDocument/Strategies/DefaultDocumentationStrategy.cs b/src/DotnetDocument/Strategies/DefaultDocumentationStrategy.cs index 0099176..eabfab6 100644 --- a/src/DotnetDocument/Strategies/DefaultDocumentationStrategy.cs +++ b/src/DotnetDocument/Strategies/DefaultDocumentationStrategy.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using System.Linq; using DotnetDocument.Configuration; using DotnetDocument.Format; using DotnetDocument.Strategies.Abstractions; @@ -65,8 +67,18 @@ public override IEnumerable GetSupportedKinds() => new[] /// /// The node /// The member declaration syntax - public override MemberDeclarationSyntax Apply(MemberDeclarationSyntax node) + public override (bool IsChanged, MemberDeclarationSyntax NodeWithDocs) Apply(MemberDeclarationSyntax node) { + ArgumentNullException.ThrowIfNull(node); + + var memberModifiers = node.Modifiers.Select(m => m.Text); + var allowedModifiers = _options.ApplyOnModifiers; + + if (memberModifiers.Any(m => allowedModifiers.Contains(m)) is false) + { + return (false, node); + } + // Retrieve member name var name = SyntaxUtils.FindMemberIdentifier(node); @@ -77,10 +89,12 @@ public override MemberDeclarationSyntax Apply(MemberDeclarationSyntax node) (TemplateKeys.Name, name)) }; - return GetDocumentationBuilder() + var nodeWithDocs = GetDocumentationBuilder() .For(node) .WithSummary(summary.ToArray()) .Build(); + + return (true, nodeWithDocs); } } } diff --git a/src/DotnetDocument/Strategies/EnumDocumentationStrategy.cs b/src/DotnetDocument/Strategies/EnumDocumentationStrategy.cs index 08cd3f3..e2fd34c 100644 --- a/src/DotnetDocument/Strategies/EnumDocumentationStrategy.cs +++ b/src/DotnetDocument/Strategies/EnumDocumentationStrategy.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using DotnetDocument.Configuration; using DotnetDocument.Format; @@ -54,8 +55,10 @@ public override IEnumerable GetSupportedKinds() => new[] /// /// The node /// The enum declaration syntax - public override EnumDeclarationSyntax Apply(EnumDeclarationSyntax node) + public override (bool IsChanged, EnumDeclarationSyntax NodeWithDocs) Apply(EnumDeclarationSyntax node) { + ArgumentNullException.ThrowIfNull(node); + // Retrieve class name var enumName = node.Identifier.Text; @@ -65,10 +68,12 @@ public override EnumDeclarationSyntax Apply(EnumDeclarationSyntax node) _formatter.FormatName(_options.Summary.Template, (TemplateKeys.Name, enumName)) }; - return GetDocumentationBuilder() + var nodeWithDocs = GetDocumentationBuilder() .For(node) .WithSummary(summary.ToArray()) .Build(); + + return (true, nodeWithDocs); } } } diff --git a/src/DotnetDocument/Strategies/EnumMemberDocumentationStrategy.cs b/src/DotnetDocument/Strategies/EnumMemberDocumentationStrategy.cs index 1b41999..47dd700 100644 --- a/src/DotnetDocument/Strategies/EnumMemberDocumentationStrategy.cs +++ b/src/DotnetDocument/Strategies/EnumMemberDocumentationStrategy.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using DotnetDocument.Configuration; using DotnetDocument.Format; @@ -54,8 +55,11 @@ public override IEnumerable GetSupportedKinds() => new[] /// /// The node /// The enum member declaration syntax - public override EnumMemberDeclarationSyntax Apply(EnumMemberDeclarationSyntax node) + public override (bool IsChanged, EnumMemberDeclarationSyntax NodeWithDocs) Apply( + EnumMemberDeclarationSyntax node) { + ArgumentNullException.ThrowIfNull(node); + // Retrieve class name var enumMemberName = node.Identifier.Text; var enumName = string.Empty; @@ -70,10 +74,12 @@ public override EnumMemberDeclarationSyntax Apply(EnumMemberDeclarationSyntax no (TemplateKeys.EnumName, enumName)) }; - return GetDocumentationBuilder() + var nodeWithDocs = GetDocumentationBuilder() .For(node) .WithSummary(summary.ToArray()) .Build(); + + return (true, nodeWithDocs); } } } diff --git a/src/DotnetDocument/Strategies/InterfaceDocumentationStrategy.cs b/src/DotnetDocument/Strategies/InterfaceDocumentationStrategy.cs index 85bf8b2..543d415 100644 --- a/src/DotnetDocument/Strategies/InterfaceDocumentationStrategy.cs +++ b/src/DotnetDocument/Strategies/InterfaceDocumentationStrategy.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using DotnetDocument.Configuration; @@ -56,8 +57,10 @@ public override IEnumerable GetSupportedKinds() => new[] /// /// The node /// The interface declaration syntax - public override InterfaceDeclarationSyntax Apply(InterfaceDeclarationSyntax node) + public override (bool IsChanged, InterfaceDeclarationSyntax NodeWithDocs) Apply(InterfaceDeclarationSyntax node) { + ArgumentNullException.ThrowIfNull(node); + // Retrieve class name var interfaceName = node.Identifier.Text; @@ -81,7 +84,9 @@ public override InterfaceDeclarationSyntax Apply(InterfaceDeclarationSyntax node builder.WithSeeAlso(baseTypes); } - return builder.Build(); + var nodeWithDocs = builder.Build(); + + return (true, nodeWithDocs); } } } diff --git a/src/DotnetDocument/Strategies/MethodDocumentationStrategy.cs b/src/DotnetDocument/Strategies/MethodDocumentationStrategy.cs index d57b7b7..4f4a13e 100644 --- a/src/DotnetDocument/Strategies/MethodDocumentationStrategy.cs +++ b/src/DotnetDocument/Strategies/MethodDocumentationStrategy.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using DotnetDocument.Configuration; @@ -57,8 +58,18 @@ public override IEnumerable GetSupportedKinds() => new[] /// /// The node /// The method declaration syntax - public override MethodDeclarationSyntax Apply(MethodDeclarationSyntax node) + public override (bool IsChanged, MethodDeclarationSyntax NodeWithDocs) Apply(MethodDeclarationSyntax node) { + ArgumentNullException.ThrowIfNull(node); + + var memberModifiers = node.Modifiers.Select(m => m.Text); + var allowedModifiers = _options.ApplyOnModifiers; + + if (memberModifiers.Any(m => allowedModifiers.Contains(m)) is false) + { + return (false, node); + } + // Get the doc builder for this node var builder = GetDocumentationBuilder() .For(node); @@ -75,6 +86,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 +95,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 @@ -156,10 +169,12 @@ public override MethodDeclarationSyntax Apply(MethodDeclarationSyntax node) } } - return builder + var nodeWithDocs = builder .WithTypeParams(typeParams) .WithParams(@params) .Build(); + + return (true, nodeWithDocs); } } } diff --git a/src/DotnetDocument/Strategies/PropertyDocumentationStrategy.cs b/src/DotnetDocument/Strategies/PropertyDocumentationStrategy.cs index 7f7dd44..6b66b77 100644 --- a/src/DotnetDocument/Strategies/PropertyDocumentationStrategy.cs +++ b/src/DotnetDocument/Strategies/PropertyDocumentationStrategy.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using DotnetDocument.Configuration; using DotnetDocument.Format; @@ -15,6 +17,7 @@ namespace DotnetDocument.Strategies /// /// [Strategy(nameof(SyntaxKind.PropertyDeclaration))] + [SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase")] public class PropertyDocumentationStrategy : DocumentationStrategyBase { /// @@ -56,13 +59,23 @@ public override IEnumerable GetSupportedKinds() => new[] /// /// The node /// The property declaration syntax - public override PropertyDeclarationSyntax Apply(PropertyDeclarationSyntax node) + public override (bool IsChanged, PropertyDeclarationSyntax NodeWithDocs) Apply(PropertyDeclarationSyntax node) { + ArgumentNullException.ThrowIfNull(node); + + var memberModifiers = node.Modifiers.Select(m => m.Text); + var allowedModifiers = _options.ApplyOnModifiers; + + if (memberModifiers.Any(m => allowedModifiers.Contains(m)) is false) + { + return (false, node); + } + // Retrieve constructor name var propertyName = node.Identifier.Text; // Humanize the constructor name - var humanizedPropertyName = propertyName.Humanize().ToLower(); + var humanizedPropertyName = propertyName.Humanize().ToLowerInvariant(); var accessorsDescription = ""; @@ -71,18 +84,22 @@ public override PropertyDeclarationSyntax Apply(PropertyDeclarationSyntax node) .ToList(); if (accessors is not null && accessors.Any()) + { accessorsDescription = string.Join(" or ", accessors) - .ToLower() + .ToLowerInvariant() .Humanize(); + } else + { accessorsDescription = _formatter.ConjugateThirdPersonSingular("Get"); + } var summary = new List { // Declare the summary by using the template from configuration _options.Summary.Template - .Replace(TemplateKeys.Accessors, accessorsDescription) - .Replace(TemplateKeys.Name, humanizedPropertyName) + .Replace(TemplateKeys.Accessors, accessorsDescription, StringComparison.InvariantCulture) + .Replace(TemplateKeys.Name, humanizedPropertyName, StringComparison.InvariantCulture) }; // Check if constructor has an expression body => {...} @@ -90,10 +107,12 @@ public override PropertyDeclarationSyntax Apply(PropertyDeclarationSyntax node) { } - return GetDocumentationBuilder() + var nodeWithDocs = GetDocumentationBuilder() .For(node) .WithSummary(summary.ToArray()) .Build(); + + return (true, nodeWithDocs); } } } diff --git a/src/DotnetDocument/Syntax/SyntaxUtils.cs b/src/DotnetDocument/Syntax/SyntaxUtils.cs index f88da92..ad52153 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(); } @@ -137,8 +139,10 @@ public static string ExtractClassName(ConstructorDeclarationSyntax constructorDe 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(); } @@ -276,8 +280,10 @@ 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; + } } /// 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"; diff --git a/test/DotnetDocument.Tests/Strategies/ClassDocumentationStrategyTests.cs b/test/DotnetDocument.Tests/Strategies/ClassDocumentationStrategyTests.cs index 600d5af..a66432f 100644 --- a/test/DotnetDocument.Tests/Strategies/ClassDocumentationStrategyTests.cs +++ b/test/DotnetDocument.Tests/Strategies/ClassDocumentationStrategyTests.cs @@ -34,10 +34,11 @@ public void ShouldDocument(string uncommentedCode, string expectedCommentedCode) new ClassDocumentationOptions()); // Act - var documentedSyntax = strategy.Apply(classDeclarationSyntax); + var documentationAttempt = strategy.Apply(classDeclarationSyntax); // Assert - documentedSyntax.ToFullString().ShouldBe(expectedCommentedCode); + documentationAttempt.IsChanged.ShouldBe(true); + documentationAttempt.NodeWithDocs.ToFullString().ShouldBe(expectedCommentedCode); } } } diff --git a/test/DotnetDocument.Tests/Strategies/ConstructorStrategyTestsBase.cs b/test/DotnetDocument.Tests/Strategies/ConstructorStrategyTestsBase.cs index d8f1cfd..169a9fa 100644 --- a/test/DotnetDocument.Tests/Strategies/ConstructorStrategyTestsBase.cs +++ b/test/DotnetDocument.Tests/Strategies/ConstructorStrategyTestsBase.cs @@ -52,10 +52,11 @@ public void ShouldDocument(string uncommentedCode, string expectedCommentedCode) options); // Act - var documentedSyntax = strategy.Apply(ctorDeclarationSyntax); + var documentationAttempt = strategy.Apply(ctorDeclarationSyntax); // Assert - documentedSyntax.ToFullString().Trim().ShouldBe(expectedCommentedCode.Trim()); + documentationAttempt.IsChanged.ShouldBe(true); + documentationAttempt.NodeWithDocs.ToFullString().Trim().ShouldBe(expectedCommentedCode.Trim()); } } }