diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/PortabilityRules/DOC205CodeFixProvider.cs b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/PortabilityRules/DOC205CodeFixProvider.cs index 217b977..91b3059 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/PortabilityRules/DOC205CodeFixProvider.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/PortabilityRules/DOC205CodeFixProvider.cs @@ -54,7 +54,7 @@ private static async Task GetTransformedDocumentAsync(Document documen var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var documentedSymbol = semanticModel.GetDeclaredSymbol(xmlNode.FirstAncestorOrSelf(SyntaxNodeExtensionsEx.IsSymbolDeclaration), cancellationToken); - var candidateSymbol = GetCandidateSymbol(documentedSymbol); + var candidateSymbol = InheritdocHelper.GetCandidateSymbol(documentedSymbol); var candidateDocumentation = candidateSymbol.GetDocumentationCommentXml(expandIncludes: false, cancellationToken: cancellationToken); var xmlDocumentation = XElement.Parse(candidateDocumentation); @@ -63,13 +63,13 @@ private static async Task GetTransformedDocumentAsync(Document documen var content = new List(); content.AddRange(xmlDocumentation.Elements().Select(element => XmlSyntaxFactory.Node(newLineText, element))); - var newStartToken = SyntaxFactory.Identifier(oldStartToken.LeadingTrivia, "autoinheritdoc", oldStartToken.TrailingTrivia); + var newStartToken = SyntaxFactory.Identifier(oldStartToken.LeadingTrivia, XmlCommentHelper.AutoinheritdocXmlTag, oldStartToken.TrailingTrivia); var newXmlNode = xmlNode.ReplaceToken(oldStartToken, newStartToken); if (newXmlNode is XmlElementSyntax newXmlElement) { var oldEndToken = newXmlElement.EndTag.Name.LocalName; - var newEndToken = SyntaxFactory.Identifier(oldEndToken.LeadingTrivia, "autoinheritdoc", oldEndToken.TrailingTrivia); + var newEndToken = SyntaxFactory.Identifier(oldEndToken.LeadingTrivia, XmlCommentHelper.AutoinheritdocXmlTag, oldEndToken.TrailingTrivia); newXmlNode = newXmlNode.ReplaceToken(oldEndToken, newEndToken); } @@ -78,78 +78,5 @@ private static async Task GetTransformedDocumentAsync(Document documen return document.WithSyntaxRoot(root.ReplaceNode(xmlNode, content)); } - - private static ISymbol GetCandidateSymbol(ISymbol memberSymbol) - { - if (memberSymbol is IMethodSymbol methodSymbol) - { - if (methodSymbol.MethodKind == MethodKind.Constructor || methodSymbol.MethodKind == MethodKind.StaticConstructor) - { - var baseType = memberSymbol.ContainingType.BaseType; - return baseType.Constructors.Where(c => IsSameSignature(methodSymbol, c)).FirstOrDefault(); - } - else if (!methodSymbol.ExplicitInterfaceImplementations.IsEmpty) - { - // prototype(inheritdoc): do we need 'OrDefault'? - return methodSymbol.ExplicitInterfaceImplementations.FirstOrDefault(); - } - else if (methodSymbol.IsOverride) - { - return methodSymbol.OverriddenMethod; - } - else - { - // prototype(inheritdoc): check for implicit interface - return null; - } - } - else if (memberSymbol is INamedTypeSymbol typeSymbol) - { - if (typeSymbol.TypeKind == TypeKind.Class) - { - // prototype(inheritdoc): when does base class take precedence over interface? - return typeSymbol.BaseType; - } - else if (typeSymbol.TypeKind == TypeKind.Interface) - { - return typeSymbol.Interfaces.FirstOrDefault(); - } - else - { - // This includes structs, enums, and delegates as mentioned in the inheritdoc spec - return null; - } - } - - return null; - } - - private static bool IsSameSignature(IMethodSymbol left, IMethodSymbol right) - { - if (left.Parameters.Length != right.Parameters.Length) - { - return false; - } - - if (left.IsStatic != right.IsStatic) - { - return false; - } - - if (!left.ReturnType.Equals(right.ReturnType)) - { - return false; - } - - for (int i = 0; i < left.Parameters.Length; i++) - { - if (!left.Parameters[i].Type.Equals(right.Parameters[i].Type)) - { - return false; - } - } - - return true; - } } } diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/PortabilityRules/DOC206CodeFixProvider.cs b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/PortabilityRules/DOC206CodeFixProvider.cs new file mode 100644 index 0000000..ecdc55b --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/PortabilityRules/DOC206CodeFixProvider.cs @@ -0,0 +1,73 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT license. See LICENSE in the project root for license information. + +namespace DocumentationAnalyzers.PortabilityRules +{ + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Composition; + using System.Diagnostics; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using System.Xml.Linq; + using DocumentationAnalyzers.Helpers; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CodeActions; + using Microsoft.CodeAnalysis.CodeFixes; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DOC206CodeFixProvider))] + [Shared] + internal class DOC206CodeFixProvider : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DOC206SynchronizeDocumentation.DiagnosticId); + + public override FixAllProvider GetFixAllProvider() + => CustomFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + Debug.Assert(FixableDiagnosticIds.Contains(diagnostic.Id), "Assertion failed: FixableDiagnosticIds.Contains(diagnostic.Id)"); + + context.RegisterCodeFix( + CodeAction.Create( + PortabilityResources.DOC206CodeFix, + token => GetTransformedDocumentAsync(context.Document, diagnostic, token), + nameof(DOC206CodeFixProvider)), + diagnostic); + } + + return SpecializedTasks.CompletedTask; + } + + private static async Task GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) + { + SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var xmlNode = (XmlNodeSyntax)root.FindNode(diagnostic.Location.SourceSpan, findInsideTrivia: true, getInnermostNodeForTie: true); + var oldStartToken = xmlNode.GetName().LocalName; + + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var documentedSymbol = semanticModel.GetDeclaredSymbol(xmlNode.FirstAncestorOrSelf(SyntaxNodeExtensionsEx.IsSymbolDeclaration), cancellationToken); + var candidateSymbol = InheritdocHelper.GetCandidateSymbol(documentedSymbol); + var candidateDocumentation = candidateSymbol.GetDocumentationCommentXml(expandIncludes: false, cancellationToken: cancellationToken); + + var xmlDocumentation = XElement.Parse(candidateDocumentation); + var newLineText = Environment.NewLine; + + var content = new List(); + content.AddRange(xmlDocumentation.Elements().Select(element => XmlSyntaxFactory.Node(newLineText, element))); + content.Add(XmlSyntaxFactory.NewLine(newLineText)); + content.Add(xmlNode); + + return document.WithSyntaxRoot(root.ReplaceNode( + xmlNode.FirstAncestorOrSelf(), + XmlSyntaxFactory.DocumentationComment(newLineText, content.ToArray()))); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/PortabilityRules/DOC206CSharp7UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/PortabilityRules/DOC206CSharp7UnitTests.cs new file mode 100644 index 0000000..8ce703d --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/PortabilityRules/DOC206CSharp7UnitTests.cs @@ -0,0 +1,11 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT license. See LICENSE in the project root for license information. + +namespace DocumentationAnalyzers.Test.CSharp7.PortabilityRules +{ + using DocumentationAnalyzers.Test.PortabilityRules; + + public class DOC206CSharp7UnitTests : DOC206UnitTests + { + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test/PortabilityRules/DOC206UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test/PortabilityRules/DOC206UnitTests.cs new file mode 100644 index 0000000..507ef89 --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test/PortabilityRules/DOC206UnitTests.cs @@ -0,0 +1,112 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT license. See LICENSE in the project root for license information. + +namespace DocumentationAnalyzers.Test.PortabilityRules +{ + using System.Threading.Tasks; + using Microsoft.CodeAnalysis.CSharp.Testing; + using Microsoft.CodeAnalysis.Testing; + using Xunit; + using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixVerifier; + + public class DOC206UnitTests + { + [Fact] + public async Task TestInheritSummaryAsync() + { + var testCode = @" +/// +/// Summary text. +/// +/// +class TestClass : BaseClass +{ +} + +/// +/// Summary text. +/// +class BaseClass +{ +} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestIncorrectSummaryAsync() + { + var testCode = @" +/// +/// Incorrect summary text. +/// +/// [||] +class TestClass : BaseClass +{ +} + +/// +/// Summary text. +/// +class BaseClass +{ +} +"; + var fixedCode = @" +/// +/// Summary text. +/// +/// +class TestClass : BaseClass +{ +} + +/// +/// Summary text. +/// +class BaseClass +{ +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + + [Fact] + public async Task TestMissingSummaryAsync() + { + var testCode = @" +/// [||] +class TestClass : BaseClass +{ +} + +/// +/// Summary text. +/// +class BaseClass +{ +} +"; + var fixedCode = @" +/// +/// Summary text. +/// +/// +class TestClass : BaseClass +{ +} + +/// +/// Summary text. +/// +class BaseClass +{ +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/Helpers/InheritdocHelper.cs b/DocumentationAnalyzers/DocumentationAnalyzers/Helpers/InheritdocHelper.cs new file mode 100644 index 0000000..dd9e5cf --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers/Helpers/InheritdocHelper.cs @@ -0,0 +1,139 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT license. See LICENSE in the project root for license information. + +namespace DocumentationAnalyzers.Helpers +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Threading; + using System.Xml.Linq; + using Microsoft.CodeAnalysis; + + internal static class InheritdocHelper + { + internal static ISymbol GetCandidateSymbol(ISymbol memberSymbol) + { + if (memberSymbol is IMethodSymbol methodSymbol) + { + if (methodSymbol.MethodKind == MethodKind.Constructor || methodSymbol.MethodKind == MethodKind.StaticConstructor) + { + var baseType = memberSymbol.ContainingType.BaseType; + return baseType.Constructors.Where(c => IsSameSignature(methodSymbol, c)).FirstOrDefault(); + } + else if (!methodSymbol.ExplicitInterfaceImplementations.IsEmpty) + { + // prototype(inheritdoc): do we need 'OrDefault'? + return methodSymbol.ExplicitInterfaceImplementations.FirstOrDefault(); + } + else if (methodSymbol.IsOverride) + { + return methodSymbol.OverriddenMethod; + } + else + { + // prototype(inheritdoc): check for implicit interface + return null; + } + } + else if (memberSymbol is INamedTypeSymbol typeSymbol) + { + if (typeSymbol.TypeKind == TypeKind.Class) + { + // prototype(inheritdoc): when does base class take precedence over interface? + return typeSymbol.BaseType; + } + else if (typeSymbol.TypeKind == TypeKind.Interface) + { + return typeSymbol.Interfaces.FirstOrDefault(); + } + else + { + // This includes structs, enums, and delegates as mentioned in the inheritdoc spec + return null; + } + } + + return null; + } + + internal static string GetDocumentationCommentXml(ISymbol symbol, CultureInfo preferredCulture, bool expandInheritdoc, bool expandIncludes, CancellationToken cancellationToken) + { + var result = symbol.GetDocumentationCommentXml(preferredCulture, expandIncludes, cancellationToken); + if (expandInheritdoc && !string.IsNullOrEmpty(result)) + { + var element = XElement.Parse(result); + var inheritedDocumentation = GetDocumentationCommentXml(GetCandidateSymbol(symbol), preferredCulture, expandInheritdoc: true, expandIncludes: true, cancellationToken); + if (element.Elements(XmlCommentHelper.InheritdocXmlTag).Any()) + { + if (!string.IsNullOrEmpty(inheritedDocumentation)) + { + IEnumerable content = element.Attributes(); + content = content.Concat(XElement.Parse(inheritedDocumentation).Nodes()); + content = content.Concat(new[] { new XElement(XmlCommentHelper.AutoinheritdocXmlTag) }); + element.ReplaceAll(content); + } + else + { + IEnumerable content = element.Attributes(); + content = content.Concat(new[] { new XElement(XmlCommentHelper.AutoinheritdocXmlTag) }); + element.ReplaceAll(content); + } + } + else if (element.Elements(XmlCommentHelper.AutoinheritdocXmlTag).Any()) + { + if (!string.IsNullOrEmpty(inheritedDocumentation)) + { + IEnumerable content = element.Attributes(); + content = content.Concat(XElement.Parse(inheritedDocumentation).Nodes()); + content = content.Concat(new[] { new XElement(XmlCommentHelper.AutoinheritdocXmlTag) }); + element.ReplaceAll(content); + } + else + { + IEnumerable content = element.Attributes(); + content = content.Concat(new[] { new XElement(XmlCommentHelper.AutoinheritdocXmlTag) }); + element.ReplaceAll(content); + } + } + + result = element.ToString(); + } + else if (!string.IsNullOrEmpty(result)) + { + result = XElement.Parse(result).ToString(); + } + + return result; + } + + private static bool IsSameSignature(IMethodSymbol left, IMethodSymbol right) + { + if (left.Parameters.Length != right.Parameters.Length) + { + return false; + } + + if (left.IsStatic != right.IsStatic) + { + return false; + } + + if (!left.ReturnType.Equals(right.ReturnType)) + { + return false; + } + + for (int i = 0; i < left.Parameters.Length; i++) + { + if (!left.Parameters[i].Type.Equals(right.Parameters[i].Type)) + { + return false; + } + } + + return true; + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/Helpers/XmlCommentHelper.cs b/DocumentationAnalyzers/DocumentationAnalyzers/Helpers/XmlCommentHelper.cs index 3d9e794..052bdd8 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers/Helpers/XmlCommentHelper.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers/Helpers/XmlCommentHelper.cs @@ -18,6 +18,7 @@ internal static class XmlCommentHelper internal const string SummaryXmlTag = "summary"; internal const string ContentXmlTag = "content"; internal const string InheritdocXmlTag = "inheritdoc"; + internal const string AutoinheritdocXmlTag = "autoinheritdoc"; internal const string ReturnsXmlTag = "returns"; internal const string ValueXmlTag = "value"; internal const string CXmlTag = "c"; diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/DOC205InheritDocumentation.cs b/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/DOC205InheritDocumentation.cs index 5e95c52..7dcbe09 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/DOC205InheritDocumentation.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/DOC205InheritDocumentation.cs @@ -24,7 +24,7 @@ internal class DOC205InheritDocumentation : DiagnosticAnalyzer private static readonly LocalizableString Description = new LocalizableResourceString(nameof(PortabilityResources.DOC205Description), PortabilityResources.ResourceManager, typeof(PortabilityResources)); private static readonly DiagnosticDescriptor Descriptor = - new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.PortabilityRules, DiagnosticSeverity.Info, AnalyzerConstants.EnabledByDefault, Description, HelpLink); + new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.PortabilityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink); /// public override ImmutableArray SupportedDiagnostics { get; } diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/DOC206SynchronizeDocumentation.cs b/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/DOC206SynchronizeDocumentation.cs new file mode 100644 index 0000000..0adf255 --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/DOC206SynchronizeDocumentation.cs @@ -0,0 +1,67 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT license. See LICENSE in the project root for license information. + +namespace DocumentationAnalyzers.PortabilityRules +{ + using System.Collections.Immutable; + using System.Globalization; + using DocumentationAnalyzers.Helpers; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using Microsoft.CodeAnalysis.Diagnostics; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class DOC206SynchronizeDocumentation : DiagnosticAnalyzer + { + /// + /// The ID for diagnostics produced by the analyzer. + /// + public const string DiagnosticId = "DOC206"; + private const string HelpLink = "https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC206.md"; + + private static readonly LocalizableString Title = new LocalizableResourceString(nameof(PortabilityResources.DOC206Title), PortabilityResources.ResourceManager, typeof(PortabilityResources)); + private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(PortabilityResources.DOC206MessageFormat), PortabilityResources.ResourceManager, typeof(PortabilityResources)); + private static readonly LocalizableString Description = new LocalizableResourceString(nameof(PortabilityResources.DOC206Description), PortabilityResources.ResourceManager, typeof(PortabilityResources)); + + private static readonly DiagnosticDescriptor Descriptor = + new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.PortabilityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink); + + /// + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Descriptor); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSyntaxNodeAction(HandleXmlNodeSyntax, SyntaxKind.XmlElement, SyntaxKind.XmlEmptyElement); + } + + private static void HandleXmlNodeSyntax(SyntaxNodeAnalysisContext context) + { + var xmlNode = (XmlNodeSyntax)context.Node; + var name = xmlNode.GetName(); + if (name.Prefix != null) + { + return; + } + + if (name.LocalName.ValueText != XmlCommentHelper.AutoinheritdocXmlTag) + { + return; + } + + var documentedSymbol = context.SemanticModel.GetDeclaredSymbol(xmlNode.FirstAncestorOrSelf(SyntaxNodeExtensionsEx.IsSymbolDeclaration), context.CancellationToken); + var currentDocumentation = InheritdocHelper.GetDocumentationCommentXml(documentedSymbol, CultureInfo.CurrentCulture, expandInheritdoc: false, expandIncludes: false, context.CancellationToken); + var expectedDocumentation = InheritdocHelper.GetDocumentationCommentXml(documentedSymbol, CultureInfo.CurrentCulture, expandInheritdoc: true, expandIncludes: false, context.CancellationToken); + if (currentDocumentation == expectedDocumentation) + { + return; + } + + context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation())); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/PortabilityResources.Designer.cs b/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/PortabilityResources.Designer.cs index 91e4cec..c18e032 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/PortabilityResources.Designer.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/PortabilityResources.Designer.cs @@ -277,6 +277,42 @@ internal static string DOC205Title { } } + /// + /// Looks up a localized string similar to Synchronize documentation. + /// + internal static string DOC206CodeFix { + get { + return ResourceManager.GetString("DOC206CodeFix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Synchronize documentation. + /// + internal static string DOC206Description { + get { + return ResourceManager.GetString("DOC206Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Synchronize documentation. + /// + internal static string DOC206MessageFormat { + get { + return ResourceManager.GetString("DOC206MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Synchronize documentation. + /// + internal static string DOC206Title { + get { + return ResourceManager.GetString("DOC206Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to 'langword' attribute value should be a language keyword. /// diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/PortabilityResources.resx b/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/PortabilityResources.resx index 622cef3..32f7ae7 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/PortabilityResources.resx +++ b/DocumentationAnalyzers/DocumentationAnalyzers/PortabilityRules/PortabilityResources.resx @@ -189,6 +189,18 @@ Inherit documentation + + Synchronize documentation + + + Synchronize documentation + + + Synchronize documentation + + + Synchronize documentation + 'langword' attribute value should be a language keyword diff --git a/docs/DOC206.md b/docs/DOC206.md index fa68c23..116d082 100644 --- a/docs/DOC206.md +++ b/docs/DOC206.md @@ -17,8 +17,8 @@ ## Cause -The documentation contains an `` element, but the included documentation is out-of-date with respect to -the source documentation. +The documentation contains an `` element, but the included documentation is out-of-date with respect +to the source documentation. ## Rule description