From bcf02385b91620a83d1bcd47677429f08829d3c2 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 21 Sep 2018 07:06:38 -0500 Subject: [PATCH 1/3] Add inline code analyzers Closes #5 Closes #15 Closes #24 Closes #25 --- .../Helpers/XmlSyntaxFactory.cs | 5 + .../StyleRules/DOC104CodeFixProvider.cs | 58 ++++ .../StyleRules/DOC105CodeFixProvider.cs | 56 ++++ .../StyleRules/DOC106CodeFixProvider.cs | 56 ++++ .../StyleRules/DOC107CodeFixProvider.cs | 58 ++++ .../StyleRules/DOC104CSharp7UnitTests.cs | 11 + .../StyleRules/DOC105CSharp7UnitTests.cs | 11 + .../StyleRules/DOC106CSharp7UnitTests.cs | 11 + .../StyleRules/DOC107CSharp7UnitTests.cs | 11 + .../StyleRules/DOC104UnitTests.cs | 64 +++++ .../StyleRules/DOC105UnitTests.cs | 74 +++++ .../StyleRules/DOC106UnitTests.cs | 74 +++++ .../StyleRules/DOC107UnitTests.cs | 255 ++++++++++++++++++ .../StyleRules/DOC104UseSeeLangword.cs | 70 +++++ .../StyleRules/DOC105UseParamref.cs | 62 +++++ .../StyleRules/DOC106UseTypeparamref.cs | 62 +++++ .../StyleRules/DOC107UseSeeCref.cs | 85 ++++++ .../StyleRules/InlineCodeAnalyzerBase.cs | 45 ++++ .../StyleRules/StyleResources.Designer.cs | 144 ++++++++++ .../StyleRules/StyleResources.resx | 48 ++++ 20 files changed, 1260 insertions(+) create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC104CodeFixProvider.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC105CodeFixProvider.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC106CodeFixProvider.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC107CodeFixProvider.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC104CSharp7UnitTests.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC105CSharp7UnitTests.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC106CSharp7UnitTests.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC107CSharp7UnitTests.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC104UnitTests.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC105UnitTests.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC106UnitTests.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC107UnitTests.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC104UseSeeLangword.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC105UseParamref.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC106UseTypeparamref.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC107UseSeeCref.cs create mode 100644 DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/InlineCodeAnalyzerBase.cs diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/Helpers/XmlSyntaxFactory.cs b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/Helpers/XmlSyntaxFactory.cs index d54d27f..83eb116 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/Helpers/XmlSyntaxFactory.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/Helpers/XmlSyntaxFactory.cs @@ -268,6 +268,11 @@ public static XmlEmptyElementSyntax ParamRefElement(string parameterName) return EmptyElement("paramref").AddAttributes(NameAttribute(parameterName)); } + public static XmlEmptyElementSyntax TypeParamRefElement(string parameterName) + { + return EmptyElement(XmlCommentHelper.TypeParamRefXmlTag).AddAttributes(NameAttribute(parameterName)); + } + public static XmlEmptyElementSyntax SeeElement(CrefSyntax cref) { return EmptyElement("see").AddAttributes(CrefAttribute(cref)); diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC104CodeFixProvider.cs b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC104CodeFixProvider.cs new file mode 100644 index 0000000..c742e49 --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC104CodeFixProvider.cs @@ -0,0 +1,58 @@ +// 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.StyleRules +{ + using System.Collections.Immutable; + using System.Composition; + using System.Threading; + using System.Threading.Tasks; + using DocumentationAnalyzers.Helpers; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CodeActions; + using Microsoft.CodeAnalysis.CodeFixes; + using Microsoft.CodeAnalysis.CSharp.Syntax; + + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DOC104CodeFixProvider))] + [Shared] + internal class DOC104CodeFixProvider : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DOC104UseSeeLangword.DiagnosticId); + + public override FixAllProvider GetFixAllProvider() + => CustomFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + if (!FixableDiagnosticIds.Contains(diagnostic.Id)) + { + continue; + } + + context.RegisterCodeFix( + CodeAction.Create( + StyleResources.DOC104CodeFix, + token => GetTransformedDocumentAsync(context.Document, diagnostic, token), + nameof(DOC104CodeFixProvider)), + diagnostic); + } + + return SpecializedTasks.CompletedTask; + } + + private static async Task GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) + { + SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var xmlElement = (XmlElementSyntax)root.FindNode(diagnostic.Location.SourceSpan, findInsideTrivia: true, getInnermostNodeForTie: true); + + var newXmlElement = XmlSyntaxFactory.EmptyElement(XmlCommentHelper.SeeXmlTag) + .AddAttributes(XmlSyntaxFactory.TextAttribute("langword", xmlElement.Content.ToFullString())) + .WithTriviaFrom(xmlElement); + + return document.WithSyntaxRoot(root.ReplaceNode(xmlElement, newXmlElement)); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC105CodeFixProvider.cs b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC105CodeFixProvider.cs new file mode 100644 index 0000000..813d1dd --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC105CodeFixProvider.cs @@ -0,0 +1,56 @@ +// 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.StyleRules +{ + using System.Collections.Immutable; + using System.Composition; + using System.Threading; + using System.Threading.Tasks; + using DocumentationAnalyzers.Helpers; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CodeActions; + using Microsoft.CodeAnalysis.CodeFixes; + using Microsoft.CodeAnalysis.CSharp.Syntax; + + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DOC105CodeFixProvider))] + [Shared] + internal class DOC105CodeFixProvider : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DOC105UseParamref.DiagnosticId); + + public override FixAllProvider GetFixAllProvider() + => CustomFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + if (!FixableDiagnosticIds.Contains(diagnostic.Id)) + { + continue; + } + + context.RegisterCodeFix( + CodeAction.Create( + StyleResources.DOC105CodeFix, + token => GetTransformedDocumentAsync(context.Document, diagnostic, token), + nameof(DOC105CodeFixProvider)), + diagnostic); + } + + return SpecializedTasks.CompletedTask; + } + + private static async Task GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) + { + SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var xmlElement = (XmlElementSyntax)root.FindNode(diagnostic.Location.SourceSpan, findInsideTrivia: true, getInnermostNodeForTie: true); + + var newXmlElement = XmlSyntaxFactory.ParamRefElement(xmlElement.Content.ToFullString()).WithTriviaFrom(xmlElement); + + return document.WithSyntaxRoot(root.ReplaceNode(xmlElement, newXmlElement)); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC106CodeFixProvider.cs b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC106CodeFixProvider.cs new file mode 100644 index 0000000..9079f1f --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC106CodeFixProvider.cs @@ -0,0 +1,56 @@ +// 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.StyleRules +{ + using System.Collections.Immutable; + using System.Composition; + using System.Threading; + using System.Threading.Tasks; + using DocumentationAnalyzers.Helpers; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CodeActions; + using Microsoft.CodeAnalysis.CodeFixes; + using Microsoft.CodeAnalysis.CSharp.Syntax; + + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DOC106CodeFixProvider))] + [Shared] + internal class DOC106CodeFixProvider : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DOC106UseTypeparamref.DiagnosticId); + + public override FixAllProvider GetFixAllProvider() + => CustomFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + if (!FixableDiagnosticIds.Contains(diagnostic.Id)) + { + continue; + } + + context.RegisterCodeFix( + CodeAction.Create( + StyleResources.DOC106CodeFix, + token => GetTransformedDocumentAsync(context.Document, diagnostic, token), + nameof(DOC106CodeFixProvider)), + diagnostic); + } + + return SpecializedTasks.CompletedTask; + } + + private static async Task GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) + { + SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var xmlElement = (XmlElementSyntax)root.FindNode(diagnostic.Location.SourceSpan, findInsideTrivia: true, getInnermostNodeForTie: true); + + var newXmlElement = XmlSyntaxFactory.TypeParamRefElement(xmlElement.Content.ToFullString()).WithTriviaFrom(xmlElement); + + return document.WithSyntaxRoot(root.ReplaceNode(xmlElement, newXmlElement)); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC107CodeFixProvider.cs b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC107CodeFixProvider.cs new file mode 100644 index 0000000..85b3ed5 --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC107CodeFixProvider.cs @@ -0,0 +1,58 @@ +// 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.StyleRules +{ + using System.Collections.Immutable; + using System.Composition; + using System.Threading; + using System.Threading.Tasks; + using DocumentationAnalyzers.Helpers; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CodeActions; + using Microsoft.CodeAnalysis.CodeFixes; + using Microsoft.CodeAnalysis.CSharp.Syntax; + + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DOC107CodeFixProvider))] + [Shared] + internal class DOC107CodeFixProvider : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DOC107UseSeeCref.DiagnosticId); + + public override FixAllProvider GetFixAllProvider() + => CustomFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + if (!FixableDiagnosticIds.Contains(diagnostic.Id)) + { + continue; + } + + context.RegisterCodeFix( + CodeAction.Create( + StyleResources.DOC107CodeFix, + token => GetTransformedDocumentAsync(context.Document, diagnostic, token), + nameof(DOC107CodeFixProvider)), + diagnostic); + } + + return SpecializedTasks.CompletedTask; + } + + private static async Task GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) + { + SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var xmlElement = (XmlElementSyntax)root.FindNode(diagnostic.Location.SourceSpan, findInsideTrivia: true, getInnermostNodeForTie: true); + + var newXmlElement = XmlSyntaxFactory.EmptyElement(XmlCommentHelper.SeeXmlTag) + .AddAttributes(XmlSyntaxFactory.TextAttribute("cref", xmlElement.Content.ToFullString())) + .WithTriviaFrom(xmlElement); + + return document.WithSyntaxRoot(root.ReplaceNode(xmlElement, newXmlElement)); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC104CSharp7UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC104CSharp7UnitTests.cs new file mode 100644 index 0000000..0bac951 --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC104CSharp7UnitTests.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.StyleRules +{ + using DocumentationAnalyzers.Test.StyleRules; + + public class DOC104CSharp7UnitTests : DOC104UnitTests + { + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC105CSharp7UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC105CSharp7UnitTests.cs new file mode 100644 index 0000000..5c7822f --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC105CSharp7UnitTests.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.StyleRules +{ + using DocumentationAnalyzers.Test.StyleRules; + + public class DOC105CSharp7UnitTests : DOC105UnitTests + { + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC106CSharp7UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC106CSharp7UnitTests.cs new file mode 100644 index 0000000..534a083 --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC106CSharp7UnitTests.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.StyleRules +{ + using DocumentationAnalyzers.Test.StyleRules; + + public class DOC106CSharp7UnitTests : DOC106UnitTests + { + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC107CSharp7UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC107CSharp7UnitTests.cs new file mode 100644 index 0000000..2120ea6 --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC107CSharp7UnitTests.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.StyleRules +{ + using DocumentationAnalyzers.Test.StyleRules; + + public class DOC107CSharp7UnitTests : DOC107UnitTests + { + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC104UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC104UnitTests.cs new file mode 100644 index 0000000..cc6a943 --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC104UnitTests.cs @@ -0,0 +1,64 @@ +// 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.StyleRules +{ + using System.Threading.Tasks; + using DocumentationAnalyzers.StyleRules; + using Xunit; + using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixVerifier; + + /// + /// This class contains unit tests for . + /// + public class DOC104UnitTests + { + [Theory] + [InlineData("null")] + [InlineData("static")] + [InlineData("virtual")] + [InlineData("true")] + [InlineData("false")] + [InlineData("abstract")] + [InlineData("sealed")] + [InlineData("async")] + [InlineData("await")] + public async Task TestRecognizedKeywordAsync(string keyword) + { + var testCode = $@" +/// +/// The keyword is [|{keyword}|]. +/// +class TestClass +{{ +}} +"; + var fixedCode = $@" +/// +/// The keyword is . +/// +class TestClass +{{ +}} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + + [Theory] + [InlineData("public")] + public async Task TestNonKeywordsAsync(string keyword) + { + var testCode = $@" +/// +/// The keyword is {keyword}. +/// +class TestClass +{{ +}} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC105UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC105UnitTests.cs new file mode 100644 index 0000000..8b22bad --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC105UnitTests.cs @@ -0,0 +1,74 @@ +// 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.StyleRules +{ + using System.Threading.Tasks; + using DocumentationAnalyzers.StyleRules; + using Xunit; + using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixVerifier; + + /// + /// This class contains unit tests for . + /// + public class DOC105UnitTests + { + [Fact] + public async Task TestParameterNameAsync() + { + var testCode = @" +class TestClass +{ + /// + /// Consider a parameter [|a|]. + /// + void Method(int a) + { + } +} +"; + var fixedCode = @" +class TestClass +{ + /// + /// Consider a parameter . + /// + void Method(int a) + { + } +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + + [Fact] + public async Task TestParameterNameMatchesKeywordAsync() + { + var testCode = @" +class TestClass +{ + /// + /// Consider parameter [|true|]. + /// + void Method(int @true) + { + } +} +"; + var fixedCode = @" +class TestClass +{ + /// + /// Consider parameter . + /// + void Method(int @true) + { + } +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC106UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC106UnitTests.cs new file mode 100644 index 0000000..0923426 --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC106UnitTests.cs @@ -0,0 +1,74 @@ +// 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.StyleRules +{ + using System.Threading.Tasks; + using DocumentationAnalyzers.StyleRules; + using Xunit; + using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixVerifier; + + /// + /// This class contains unit tests for . + /// + public class DOC106UnitTests + { + [Fact] + public async Task TestTypeParameterNameAsync() + { + var testCode = @" +class TestClass +{ + /// + /// Consider a type parameter [|a|]. + /// + void Method() + { + } +} +"; + var fixedCode = @" +class TestClass +{ + /// + /// Consider a type parameter . + /// + void Method() + { + } +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + + [Fact] + public async Task TestTypeParameterNameMatchesKeywordAsync() + { + var testCode = @" +class TestClass +{ + /// + /// Consider type parameter [|true|]. + /// + void Method<@true>() + { + } +} +"; + var fixedCode = @" +class TestClass +{ + /// + /// Consider type parameter . + /// + void Method<@true>() + { + } +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC107UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC107UnitTests.cs new file mode 100644 index 0000000..972d57a --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC107UnitTests.cs @@ -0,0 +1,255 @@ +// 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.StyleRules +{ + using System.Threading.Tasks; + using DocumentationAnalyzers.StyleRules; + using Xunit; + using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixVerifier; + + /// + /// This class contains unit tests for . + /// + public class DOC107UnitTests + { + [Fact] + public async Task TestPropertyNameAsync() + { + var testCode = @" +class TestClass +{ + int a => 3; + + /// + /// Consider a property [|a|]. + /// + void Method() + { + } +} +"; + var fixedCode = @" +class TestClass +{ + int a => 3; + + /// + /// Consider a property . + /// + void Method() + { + } +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + + [Fact] + public async Task TestFieldNameAsync() + { + var testCode = @" +class TestClass +{ + int a = 3; + + /// + /// Consider a member [|a|]. + /// + void Method() + { + } +} +"; + var fixedCode = @" +class TestClass +{ + int a = 3; + + /// + /// Consider a member . + /// + void Method() + { + } +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + + [Fact] + public async Task TestEventNameAsync() + { + var testCode = @" +class TestClass +{ + event System.EventHandler a; + + /// + /// Consider a member [|a|]. + /// + void Method() + { + } +} +"; + var fixedCode = @" +class TestClass +{ + event System.EventHandler a; + + /// + /// Consider a member . + /// + void Method() + { + } +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + + [Fact] + public async Task TestMethodNameAsync() + { + var testCode = @" +class TestClass +{ + int a() => 3; + + /// + /// Consider a member a. + /// + void Method() + { + } +} +"; + + // Methods are not currently checked + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestContainingNamespaceNameAsync() + { + var testCode = @" +namespace a +{ + class TestClass + { + /// + /// Consider a containing namespace a. + /// + void Method() + { + } + } +} +"; + + // Containing namespaces are not currently checked + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestContainingTypeNameAsync() + { + var testCode = @" +class a +{ + class TestClass + { + /// + /// Consider a containing type a. + /// + void Method() + { + } + } +} +"; + + // Containing types are not currently checked + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestNestedTypeNameAsync() + { + var testCode = @" +class TestClass +{ + /// + /// Consider a nested type a. + /// + void Method() + { + } + + class a { } +} +"; + + // Nested types are not currently checked + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestSiblingTypeNameAsync() + { + var testCode = @" +class TestClass +{ + /// + /// Consider a type a. + /// + void Method() + { + } +} + +class a { } +"; + + // Types are not currently checked + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestPropertyNameMatchesKeywordAsync() + { + var testCode = @" +class TestClass +{ + int @true => 3; + + /// + /// Consider property [|true|]. + /// + void Method() + { + } +} +"; + var fixedCode = @" +class TestClass +{ + int @true => 3; + + /// + /// Consider property . + /// + void Method() + { + } +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC104UseSeeLangword.cs b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC104UseSeeLangword.cs new file mode 100644 index 0000000..74ef4dd --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC104UseSeeLangword.cs @@ -0,0 +1,70 @@ +// 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.StyleRules +{ + using System.Collections.Immutable; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using Microsoft.CodeAnalysis.Diagnostics; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class DOC104UseSeeLangword : InlineCodeAnalyzerBase + { + /// + /// The ID for diagnostics produced true by the analyzer. + /// + public const string DiagnosticId = "DOC104"; + private const string HelpLink = "https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC104.md"; + + private static readonly LocalizableString Title = new LocalizableResourceString(nameof(StyleResources.DOC104Title), StyleResources.ResourceManager, typeof(StyleResources)); + private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(StyleResources.DOC104MessageFormat), StyleResources.ResourceManager, typeof(StyleResources)); + private static readonly LocalizableString Description = new LocalizableResourceString(nameof(StyleResources.DOC104Description), StyleResources.ResourceManager, typeof(StyleResources)); + + private static readonly DiagnosticDescriptor Descriptor = + new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.StyleRules, DiagnosticSeverity.Info, AnalyzerConstants.EnabledByDefault, Description, HelpLink); + + /// + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Descriptor); + + protected override void HandleInlineCodeElement(ref SyntaxNodeAnalysisContext context, XmlElementSyntax xmlElement) + { + // This rule will only apply if the content is a single XmlTextSyntax containing a single + // XmlTextLiteralToken token + if (xmlElement.Content.Count != 1) + { + return; + } + + if (!(xmlElement.Content[0] is XmlTextSyntax xmlText)) + { + return; + } + + if (xmlText.TextTokens.Count != 1) + { + return; + } + + switch (xmlText.TextTokens[0].ValueText) + { + case "null": + case "static": + case "virtual": + case "true": + case "false": + case "abstract": + case "sealed": + case "async": + case "await": + break; + + default: + return; + } + + context.ReportDiagnostic(Diagnostic.Create(Descriptor, xmlElement.GetLocation())); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC105UseParamref.cs b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC105UseParamref.cs new file mode 100644 index 0000000..afbe30f --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC105UseParamref.cs @@ -0,0 +1,62 @@ +// 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.StyleRules +{ + using System; + using System.Collections.Immutable; + using DocumentationAnalyzers.Helpers; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using Microsoft.CodeAnalysis.Diagnostics; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class DOC105UseParamref : InlineCodeAnalyzerBase + { + /// + /// The ID for diagnostics produced by the analyzer. + /// + public const string DiagnosticId = "DOC105"; + private const string HelpLink = "https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC105.md"; + + private static readonly LocalizableString Title = new LocalizableResourceString(nameof(StyleResources.DOC105Title), StyleResources.ResourceManager, typeof(StyleResources)); + private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(StyleResources.DOC105MessageFormat), StyleResources.ResourceManager, typeof(StyleResources)); + private static readonly LocalizableString Description = new LocalizableResourceString(nameof(StyleResources.DOC105Description), StyleResources.ResourceManager, typeof(StyleResources)); + + private static readonly DiagnosticDescriptor Descriptor = + new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.StyleRules, DiagnosticSeverity.Info, AnalyzerConstants.EnabledByDefault, Description, HelpLink); + + /// + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Descriptor); + + protected override void HandleInlineCodeElement(ref SyntaxNodeAnalysisContext context, XmlElementSyntax xmlElement) + { + // This rule will only apply if the content is a single XmlTextSyntax containing a single + // XmlTextLiteralToken token + if (xmlElement.Content.Count != 1) + { + return; + } + + if (!(xmlElement.Content[0] is XmlTextSyntax xmlText)) + { + return; + } + + if (xmlText.TextTokens.Count != 1) + { + return; + } + + var semanticModel = context.SemanticModel; + var documentedSymbol = semanticModel.GetDeclaredSymbol(xmlElement.FirstAncestorOrSelf(SyntaxNodeExtensionsEx.IsSymbolDeclaration), context.CancellationToken); + if (!documentedSymbol.HasAnyParameter(xmlText.TextTokens[0].ValueText, StringComparer.Ordinal)) + { + return; + } + + context.ReportDiagnostic(Diagnostic.Create(Descriptor, xmlElement.GetLocation())); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC106UseTypeparamref.cs b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC106UseTypeparamref.cs new file mode 100644 index 0000000..ccd4dab --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC106UseTypeparamref.cs @@ -0,0 +1,62 @@ +// 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.StyleRules +{ + using System; + using System.Collections.Immutable; + using DocumentationAnalyzers.Helpers; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using Microsoft.CodeAnalysis.Diagnostics; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class DOC106UseTypeparamref : InlineCodeAnalyzerBase + { + /// + /// The ID for diagnostics produced by the analyzer. + /// + public const string DiagnosticId = "DOC106"; + private const string HelpLink = "https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC106.md"; + + private static readonly LocalizableString Title = new LocalizableResourceString(nameof(StyleResources.DOC106Title), StyleResources.ResourceManager, typeof(StyleResources)); + private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(StyleResources.DOC106MessageFormat), StyleResources.ResourceManager, typeof(StyleResources)); + private static readonly LocalizableString Description = new LocalizableResourceString(nameof(StyleResources.DOC106Description), StyleResources.ResourceManager, typeof(StyleResources)); + + private static readonly DiagnosticDescriptor Descriptor = + new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.StyleRules, DiagnosticSeverity.Info, AnalyzerConstants.EnabledByDefault, Description, HelpLink); + + /// + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Descriptor); + + protected override void HandleInlineCodeElement(ref SyntaxNodeAnalysisContext context, XmlElementSyntax xmlElement) + { + // This rule will only apply if the content is a single XmlTextSyntax containing a single + // XmlTextLiteralToken token + if (xmlElement.Content.Count != 1) + { + return; + } + + if (!(xmlElement.Content[0] is XmlTextSyntax xmlText)) + { + return; + } + + if (xmlText.TextTokens.Count != 1) + { + return; + } + + var semanticModel = context.SemanticModel; + var documentedSymbol = semanticModel.GetDeclaredSymbol(xmlElement.FirstAncestorOrSelf(SyntaxNodeExtensionsEx.IsSymbolDeclaration), context.CancellationToken); + if (!documentedSymbol.HasAnyTypeParameter(xmlText.TextTokens[0].ValueText, StringComparer.Ordinal)) + { + return; + } + + context.ReportDiagnostic(Diagnostic.Create(Descriptor, xmlElement.GetLocation())); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC107UseSeeCref.cs b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC107UseSeeCref.cs new file mode 100644 index 0000000..c1a5477 --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/DOC107UseSeeCref.cs @@ -0,0 +1,85 @@ +// 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.StyleRules +{ + using System.Collections.Immutable; + using DocumentationAnalyzers.Helpers; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using Microsoft.CodeAnalysis.Diagnostics; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class DOC107UseSeeCref : InlineCodeAnalyzerBase + { + /// + /// The ID for diagnostics produced by the analyzer. + /// + public const string DiagnosticId = "DOC107"; + private const string HelpLink = "https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC107.md"; + + private static readonly LocalizableString Title = new LocalizableResourceString(nameof(StyleResources.DOC107Title), StyleResources.ResourceManager, typeof(StyleResources)); + private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(StyleResources.DOC107MessageFormat), StyleResources.ResourceManager, typeof(StyleResources)); + private static readonly LocalizableString Description = new LocalizableResourceString(nameof(StyleResources.DOC107Description), StyleResources.ResourceManager, typeof(StyleResources)); + + private static readonly DiagnosticDescriptor Descriptor = + new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.StyleRules, DiagnosticSeverity.Info, AnalyzerConstants.EnabledByDefault, Description, HelpLink); + + /// + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Descriptor); + + protected override void HandleInlineCodeElement(ref SyntaxNodeAnalysisContext context, XmlElementSyntax xmlElement) + { + // Currently this rule will only apply if the content is a single XmlTextSyntax containing a single + // XmlTextLiteralToken token + if (xmlElement.Content.Count != 1) + { + return; + } + + if (!(xmlElement.Content[0] is XmlTextSyntax xmlText)) + { + return; + } + + if (xmlText.TextTokens.Count != 1) + { + return; + } + + var semanticModel = context.SemanticModel; + var documentedSymbol = semanticModel.GetDeclaredSymbol(xmlElement.FirstAncestorOrSelf(SyntaxNodeExtensionsEx.IsSymbolDeclaration), context.CancellationToken); + var name = xmlText.TextTokens[0].ValueText; + for (var currentSymbol = documentedSymbol; currentSymbol != null; currentSymbol = currentSymbol?.ContainingSymbol) + { + switch (currentSymbol.Kind) + { + case SymbolKind.NamedType: + var namedType = (INamedTypeSymbol)currentSymbol; + var matchingMembers = namedType.GetMembers(name); + if (matchingMembers.Length != 1) + { + return; + } + + if (matchingMembers[0].Kind == SymbolKind.Property + || matchingMembers[0].Kind == SymbolKind.Field + || matchingMembers[0].Kind == SymbolKind.Event) + { + context.ReportDiagnostic(Diagnostic.Create(Descriptor, xmlElement.GetLocation())); + } + + return; + + case SymbolKind.Namespace: + case SymbolKind.NetModule: + return; + + default: + continue; + } + } + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/InlineCodeAnalyzerBase.cs b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/InlineCodeAnalyzerBase.cs new file mode 100644 index 0000000..2d5419b --- /dev/null +++ b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/InlineCodeAnalyzerBase.cs @@ -0,0 +1,45 @@ +// 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.StyleRules +{ + using DocumentationAnalyzers.Helpers; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using Microsoft.CodeAnalysis.Diagnostics; + + /// + /// This is the base class for diagnostic analyzers which report diagnostics for use of <c> which a + /// more appropriate inline element is available. + /// + internal abstract class InlineCodeAnalyzerBase : DiagnosticAnalyzer + { + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSyntaxNodeAction(HandleXmlElementSyntax, SyntaxKind.XmlElement); + } + + protected abstract void HandleInlineCodeElement(ref SyntaxNodeAnalysisContext context, XmlElementSyntax xmlElement); + + private void HandleXmlElementSyntax(SyntaxNodeAnalysisContext context) + { + var xmlElement = (XmlElementSyntax)context.Node; + var name = xmlElement.StartTag?.Name; + if (name is null || name.Prefix != null) + { + return; + } + + if (name.LocalName.ValueText != XmlCommentHelper.CXmlTag) + { + return; + } + + HandleInlineCodeElement(ref context, xmlElement); + } + } +} diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/StyleResources.Designer.cs b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/StyleResources.Designer.cs index bdd33ea..d16c578 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/StyleResources.Designer.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/StyleResources.Designer.cs @@ -187,6 +187,150 @@ internal static string DOC103Title { } } + /// + /// Looks up a localized string similar to Use 'see langword'. + /// + internal static string DOC104CodeFix { + get { + return ResourceManager.GetString("DOC104CodeFix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prefer '<see langword="keyword"/>' to '<c>keyword</c>' for referencing language keywords. + /// + internal static string DOC104Description { + get { + return ResourceManager.GetString("DOC104Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prefer '<see langword="keyword"/>' to '<c>keyword</c>' for referencing language keywords. + /// + internal static string DOC104MessageFormat { + get { + return ResourceManager.GetString("DOC104MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use 'see langword'. + /// + internal static string DOC104Title { + get { + return ResourceManager.GetString("DOC104Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use 'paramref'. + /// + internal static string DOC105CodeFix { + get { + return ResourceManager.GetString("DOC105CodeFix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prefer '<paramref name="parameter"/>' to '<c>parameter</c>' for referencing parameters. + /// + internal static string DOC105Description { + get { + return ResourceManager.GetString("DOC105Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prefer '<paramref name="parameter"/>' to '<c>parameter</c>' for referencing parameters. + /// + internal static string DOC105MessageFormat { + get { + return ResourceManager.GetString("DOC105MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use 'paramref'. + /// + internal static string DOC105Title { + get { + return ResourceManager.GetString("DOC105Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use 'typeparamref'. + /// + internal static string DOC106CodeFix { + get { + return ResourceManager.GetString("DOC106CodeFix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prefer '<typeparamref name="type_parameter"/>' to '<c>type_parameter</c>' for referencing type parameters. + /// + internal static string DOC106Description { + get { + return ResourceManager.GetString("DOC106Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prefer '<typeparamref name="type_parameter"/>' to '<c>type_parameter</c>' for referencing type parameters. + /// + internal static string DOC106MessageFormat { + get { + return ResourceManager.GetString("DOC106MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use 'typeparamref'. + /// + internal static string DOC106Title { + get { + return ResourceManager.GetString("DOC106Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use 'see cref'. + /// + internal static string DOC107CodeFix { + get { + return ResourceManager.GetString("DOC107CodeFix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prefer '<see cref="target"/>' to '<c>target</c>' for referencing code elements. + /// + internal static string DOC107Description { + get { + return ResourceManager.GetString("DOC107Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prefer '<see cref="target"/>' to '<c>target</c>' for referencing code elements. + /// + internal static string DOC107MessageFormat { + get { + return ResourceManager.GetString("DOC107MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use 'see cref'. + /// + internal static string DOC107Title { + get { + return ResourceManager.GetString("DOC107Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Wrap text in paragraph element. /// diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/StyleResources.resx b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/StyleResources.resx index cd7c46d..6d5f2b5 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/StyleResources.resx +++ b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/StyleResources.resx @@ -159,6 +159,54 @@ Use Unicode characters + + Use 'see langword' + + + Prefer '<see langword="keyword"/>' to '<c>keyword</c>' for referencing language keywords + + + Prefer '<see langword="keyword"/>' to '<c>keyword</c>' for referencing language keywords + + + Use 'see langword' + + + Use 'paramref' + + + Prefer '<paramref name="parameter"/>' to '<c>parameter</c>' for referencing parameters + + + Prefer '<paramref name="parameter"/>' to '<c>parameter</c>' for referencing parameters + + + Use 'paramref' + + + Use 'typeparamref' + + + Prefer '<typeparamref name="type_parameter"/>' to '<c>type_parameter</c>' for referencing type parameters + + + Prefer '<typeparamref name="type_parameter"/>' to '<c>type_parameter</c>' for referencing type parameters + + + Use 'typeparamref' + + + Use 'see cref' + + + Prefer '<see cref="target"/>' to '<c>target</c>' for referencing code elements + + + Prefer '<see cref="target"/>' to '<c>target</c>' for referencing code elements + + + Use 'see cref' + Wrap text in paragraph element From 1a7e90abc0203e590b91acaa5f619825b95a9206 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 21 Sep 2018 10:49:50 -0500 Subject: [PATCH 2/3] Add documentation for DOC104-DOC107 --- docs/DOC104.md | 61 +++++++++++++++++++++++++++++++++++++++++ docs/DOC105.md | 61 +++++++++++++++++++++++++++++++++++++++++ docs/DOC106.md | 61 +++++++++++++++++++++++++++++++++++++++++ docs/DOC107.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++ docs/StyleRules.md | 4 +++ 5 files changed, 254 insertions(+) create mode 100644 docs/DOC104.md create mode 100644 docs/DOC105.md create mode 100644 docs/DOC106.md create mode 100644 docs/DOC107.md diff --git a/docs/DOC104.md b/docs/DOC104.md new file mode 100644 index 0000000..fdf6777 --- /dev/null +++ b/docs/DOC104.md @@ -0,0 +1,61 @@ +# DOC104 + + + + + + + + + + + + + + +
TypeNameDOC104UseSeeLangword
CheckIdDOC104
CategoryStyle Rules
+ +## Cause + +The contains a language keyword reference using `keyword` that can be converted to the preferred form +``. + +## Rule description + +A violation of this rule occurs when documentation a language keyword reference written in inline code that can be +written in a preferred form using `see langword`. + +```csharp +/// +/// This type is sealed. +/// +public sealed class SomeType +{ +} +``` + +## How to fix violations + +To fix a violation of this rule, replace the inline code with the equivalent `see langword` syntax. + +```csharp +/// +/// This type is . +/// +public sealed class SomeType +{ +} +``` + +## How to suppress violations + +```csharp +#pragma warning disable DOC104 // Use 'see langword' +/// +/// This type is sealed. +/// +public sealed class SomeType +#pragma warning restore DOC104 // Use 'see langword' +{ +} +``` diff --git a/docs/DOC105.md b/docs/DOC105.md new file mode 100644 index 0000000..d744188 --- /dev/null +++ b/docs/DOC105.md @@ -0,0 +1,61 @@ +# DOC105 + + + + + + + + + + + + + + +
TypeNameDOC105UseParamref
CheckIdDOC105
CategoryStyle Rules
+ +## Cause + +The contains a parameter reference using `name` that can be converted to the preferred form +``. + +## Rule description + +A violation of this rule occurs when documentation contains a parameter reference written in inline code that can be +written in a preferred form using `paramref`. + +```csharp +/// +/// Pass in a value. +/// +public void Method(int value) +{ +} +``` + +## How to fix violations + +To fix a violation of this rule, replace the inline code with the equivalent `paramref` syntax. + +```csharp +/// +/// Pass in a . +/// +public void Method(int value) +{ +} +``` + +## How to suppress violations + +```csharp +#pragma warning disable DOC105 // Use 'paramref' +/// +/// Pass in a value. +/// +public void Method(int value) +#pragma warning restore DOC104 // Use 'paramref' +{ +} +``` diff --git a/docs/DOC106.md b/docs/DOC106.md new file mode 100644 index 0000000..ac8eb1d --- /dev/null +++ b/docs/DOC106.md @@ -0,0 +1,61 @@ +# DOC106 + + + + + + + + + + + + + + +
TypeNameDOC106UseTypeparamref
CheckIdDOC106
CategoryStyle Rules
+ +## Cause + +The contains a type parameter reference using `T` that can be converted to the preferred form +``. + +## Rule description + +A violation of this rule occurs when documentation contains a type parameter reference written in inline code that can +be written in a preferred form using `typeparamref`. + +```csharp +/// +/// Pass in a T. +/// +public void Method() +{ +} +``` + +## How to fix violations + +To fix a violation of this rule, replace the inline code with the equivalent `typeparamref` syntax. + +```csharp +/// +/// Pass in a . +/// +public void Method() +{ +} +``` + +## How to suppress violations + +```csharp +#pragma warning disable DOC106 // Use 'typeparamref' +/// +/// Pass in a T. +/// +public void Method() +#pragma warning restore DOC106 // Use 'typeparamref' +{ +} +``` diff --git a/docs/DOC107.md b/docs/DOC107.md new file mode 100644 index 0000000..777bd13 --- /dev/null +++ b/docs/DOC107.md @@ -0,0 +1,67 @@ +# DOC107 + + + + + + + + + + + + + + +
TypeNameDOC107UseSeeCref
CheckIdDOC107
CategoryStyle Rules
+ +## Cause + +The contains a code element reference using `name` that can be converted to the preferred form +``. + +## Rule description + +A violation of this rule occurs when documentation contains a code element reference written in inline code that can +be written in a preferred form using `see cref`. + +```csharp +int SomeValue { get; } + +/// +/// Depends on SomeValue. +/// +public void Method() +{ +} +``` + +## How to fix violations + +To fix a violation of this rule, replace the inline code with the equivalent `see cref` syntax. + +```csharp +int SomeValue { get; } + +/// +/// Depends on . +/// +public void Method() +{ +} +``` + +## How to suppress violations + +```csharp +int SomeValue { get; } + +#pragma warning disable DOC107 // Use 'see cref' +/// +/// Depends on SomeValue. +/// +public void Method() +#pragma warning restore DOC107 // Use 'see cref' +{ +} +``` diff --git a/docs/StyleRules.md b/docs/StyleRules.md index 5fea148..00bd71a 100644 --- a/docs/StyleRules.md +++ b/docs/StyleRules.md @@ -8,4 +8,8 @@ Identifier | Name | Description [DOC101](DOC101.md) | UseChildBlocksConsistently | The documentation for the element contains some text which is wrapped in block-level elements, and other text which is written inline. [DOC102](DOC102.md) | UseChildBlocksConsistentlyAcrossElementsOfTheSameKind | The documentation for the element contains inline text, but the documentation for a sibling element of the same kind uses block-level elements. [DOC103](DOC103.md) | UseUnicodeCharacters | The documentation contains an unnecessary or unrecognized HTML character entity. +[DOC104](DOC104.md) | UseSeeLangword | The contains a language keyword reference using `keyword` that can be converted to the preferred form ``. +[DOC105](DOC105.md) | UseParamref | The contains a parameter reference using `name` that can be converted to the preferred form ``. +[DOC106](DOC106.md) | UseTypeparamref | The contains a type parameter reference using `T` that can be converted to the preferred form ``. +[DOC107](DOC107.md) | UseSeeCref | The contains a code element reference using `name` that can be converted to the preferred form ``. [DOC108](DOC108.md) | AvoidEmptyParagraphs | The documentation contains an empty paragraph element (`` or `

`) used as a paragraph separator. From 9e41513532fc8f8889c680b11befff343ccb0d6e Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 21 Sep 2018 11:33:54 -0500 Subject: [PATCH 3/3] Add tests based on code coverage information --- .../StyleRules/DOC104CodeFixProvider.cs | 6 +- .../StyleRules/DOC105CodeFixProvider.cs | 6 +- .../StyleRules/DOC106CodeFixProvider.cs | 6 +- .../StyleRules/DOC107CodeFixProvider.cs | 11 +- .../StyleRules/DOC104UnitTests.cs | 68 +++++++++++ .../StyleRules/DOC105UnitTests.cs | 99 ++++++++++++++++ .../StyleRules/DOC106UnitTests.cs | 99 ++++++++++++++++ .../StyleRules/DOC107UnitTests.cs | 109 ++++++++++++++++++ .../StyleRules/InlineCodeAnalyzerBase.cs | 4 +- 9 files changed, 389 insertions(+), 19 deletions(-) diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC104CodeFixProvider.cs b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC104CodeFixProvider.cs index c742e49..e28f2ba 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC104CodeFixProvider.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC104CodeFixProvider.cs @@ -5,6 +5,7 @@ namespace DocumentationAnalyzers.StyleRules { using System.Collections.Immutable; using System.Composition; + using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using DocumentationAnalyzers.Helpers; @@ -27,10 +28,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) { foreach (var diagnostic in context.Diagnostics) { - if (!FixableDiagnosticIds.Contains(diagnostic.Id)) - { - continue; - } + Debug.Assert(FixableDiagnosticIds.Contains(diagnostic.Id), "Assertion failed: FixableDiagnosticIds.Contains(diagnostic.Id)"); context.RegisterCodeFix( CodeAction.Create( diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC105CodeFixProvider.cs b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC105CodeFixProvider.cs index 813d1dd..46289c8 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC105CodeFixProvider.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC105CodeFixProvider.cs @@ -5,6 +5,7 @@ namespace DocumentationAnalyzers.StyleRules { using System.Collections.Immutable; using System.Composition; + using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using DocumentationAnalyzers.Helpers; @@ -27,10 +28,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) { foreach (var diagnostic in context.Diagnostics) { - if (!FixableDiagnosticIds.Contains(diagnostic.Id)) - { - continue; - } + Debug.Assert(FixableDiagnosticIds.Contains(diagnostic.Id), "Assertion failed: FixableDiagnosticIds.Contains(diagnostic.Id)"); context.RegisterCodeFix( CodeAction.Create( diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC106CodeFixProvider.cs b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC106CodeFixProvider.cs index 9079f1f..dee37ef 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC106CodeFixProvider.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC106CodeFixProvider.cs @@ -5,6 +5,7 @@ namespace DocumentationAnalyzers.StyleRules { using System.Collections.Immutable; using System.Composition; + using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using DocumentationAnalyzers.Helpers; @@ -27,10 +28,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) { foreach (var diagnostic in context.Diagnostics) { - if (!FixableDiagnosticIds.Contains(diagnostic.Id)) - { - continue; - } + Debug.Assert(FixableDiagnosticIds.Contains(diagnostic.Id), "Assertion failed: FixableDiagnosticIds.Contains(diagnostic.Id)"); context.RegisterCodeFix( CodeAction.Create( diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC107CodeFixProvider.cs b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC107CodeFixProvider.cs index 85b3ed5..e2e9b90 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC107CodeFixProvider.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC107CodeFixProvider.cs @@ -5,12 +5,14 @@ namespace DocumentationAnalyzers.StyleRules { using System.Collections.Immutable; using System.Composition; + using System.Diagnostics; using System.Threading; using System.Threading.Tasks; 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(DOC107CodeFixProvider))] @@ -27,10 +29,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) { foreach (var diagnostic in context.Diagnostics) { - if (!FixableDiagnosticIds.Contains(diagnostic.Id)) - { - continue; - } + Debug.Assert(FixableDiagnosticIds.Contains(diagnostic.Id), "Assertion failed: FixableDiagnosticIds.Contains(diagnostic.Id)"); context.RegisterCodeFix( CodeAction.Create( @@ -49,7 +48,9 @@ private static async Task GetTransformedDocumentAsync(Document documen var xmlElement = (XmlElementSyntax)root.FindNode(diagnostic.Location.SourceSpan, findInsideTrivia: true, getInnermostNodeForTie: true); var newXmlElement = XmlSyntaxFactory.EmptyElement(XmlCommentHelper.SeeXmlTag) - .AddAttributes(XmlSyntaxFactory.TextAttribute("cref", xmlElement.Content.ToFullString())) + .AddAttributes(XmlSyntaxFactory.TextAttribute( + "cref", + SyntaxFactory.Token(SyntaxTriviaList.Empty, SyntaxKind.XmlTextLiteralToken, xmlElement.Content.ToFullString(), xmlElement.Content.ToFullString(), SyntaxTriviaList.Empty))) .WithTriviaFrom(xmlElement); return document.WithSyntaxRoot(root.ReplaceNode(xmlElement, newXmlElement)); diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC104UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC104UnitTests.cs index cc6a943..6a2886d 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC104UnitTests.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC104UnitTests.cs @@ -56,6 +56,74 @@ public async Task TestNonKeywordsAsync(string keyword) class TestClass {{ }} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestNonDiagnosticPotentialCasesAsync() + { + // These cases could qualify for this diagnostic, but currently do not. + var testCode = @" +class TestClass +{ + ///

+ /// The keyword is await. + /// The keyword is true. + /// The keyword is true . + /// The keyword is true . + /// The keyword is true + /// . + /// The keyword is + /// true. + /// The keyword is + /// true + /// . + /// + void Method
() + { + } +} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestNonDiagnosticCasesAsync() + { + // These cases *shouldn't* qualify for this diagnostic. + var testCode = @" +class TestClass +{ + /// + /// The keyword is not-keyword. + /// The keyword is True. + /// The keyword is true>. + /// The keyword is >true. + /// The keyword is true. + /// The keyword is truetrue. + /// + void Method() + { + } +} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestNonCodeAsync() + { + var testCode = @" +/// +/// The keyword is true. +/// +class TestClass +{ +} "; await Verify.VerifyAnalyzerAsync(testCode); diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC105UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC105UnitTests.cs index 8b22bad..bd67324 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC105UnitTests.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC105UnitTests.cs @@ -42,6 +42,35 @@ void Method(int a) await Verify.VerifyCodeFixAsync(testCode, fixedCode); } + [Fact] + public async Task TestParameterNameEncodedAsync() + { + var testCode = @" +class TestClass +{ + /// + /// Consider a parameter [|a|]. + /// + void Method(int a) + { + } +} +"; + var fixedCode = @" +class TestClass +{ + /// + /// Consider a parameter . + /// + void Method(int a) + { + } +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + [Fact] public async Task TestParameterNameMatchesKeywordAsync() { @@ -70,5 +99,75 @@ void Method(int @true) await Verify.VerifyCodeFixAsync(testCode, fixedCode); } + + [Fact] + public async Task TestNonDiagnosticPotentialCasesAsync() + { + // These cases could qualify for this diagnostic, but currently do not. + var testCode = @" +class TestClass +{ + /// + /// Consider a parameter a. + /// Consider a parameter a . + /// Consider a parameter a . + /// Consider a parameter a + /// . + /// Consider a parameter + /// a. + /// Consider a parameter + /// a + /// . + /// + void Method(int a) + { + } +} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestNonDiagnosticCasesAsync() + { + // These cases *shouldn't* qualify for this diagnostic. + var testCode = @" +class TestClass +{ + /// + /// Consider a parameter b. + /// Consider a parameter A. + /// Consider a parameter a>. + /// Consider a parameter >a. + /// Consider a parameter a. + /// Consider a parameter aa. + /// + void Method(int a) + { + } +} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestNonCodeAsync() + { + var testCode = @" +class TestClass +{ + /// + /// Consider a parameter a. + /// + void Method(int a) + { + } +} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } } } diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC106UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC106UnitTests.cs index 0923426..1ed2b6f 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC106UnitTests.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC106UnitTests.cs @@ -42,6 +42,35 @@ void Method() await Verify.VerifyCodeFixAsync(testCode, fixedCode); } + [Fact] + public async Task TestTypeParameterNameEncodedAsync() + { + var testCode = @" +class TestClass +{ + /// + /// Consider a type parameter [|a|]. + /// + void Method() + { + } +} +"; + var fixedCode = @" +class TestClass +{ + /// + /// Consider a type parameter . + /// + void Method() + { + } +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + [Fact] public async Task TestTypeParameterNameMatchesKeywordAsync() { @@ -70,5 +99,75 @@ void Method<@true>() await Verify.VerifyCodeFixAsync(testCode, fixedCode); } + + [Fact] + public async Task TestNonDiagnosticPotentialCasesAsync() + { + // These cases could qualify for this diagnostic, but currently do not. + var testCode = @" +class TestClass +{ + /// + /// Consider a type parameter a. + /// Consider a type parameter a . + /// Consider a type parameter a . + /// Consider a type parameter a + /// . + /// Consider a type parameter + /// a. + /// Consider a type parameter + /// a + /// . + /// + void Method() + { + } +} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestNonDiagnosticCasesAsync() + { + // These cases *shouldn't* qualify for this diagnostic. + var testCode = @" +class TestClass +{ + /// + /// Consider a type parameter b. + /// Consider a type parameter A. + /// Consider a type parameter a>. + /// Consider a type parameter >a. + /// Consider a type parameter a. + /// Consider a type parameter aa. + /// + void Method() + { + } +} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestNonCodeAsync() + { + var testCode = @" +class TestClass +{ + /// + /// Consider a type parameter a. + /// + void Method() + { + } +} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } } } diff --git a/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC107UnitTests.cs b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC107UnitTests.cs index 972d57a..455f38c 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC107UnitTests.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC107UnitTests.cs @@ -46,6 +46,39 @@ void Method() await Verify.VerifyCodeFixAsync(testCode, fixedCode); } + [Fact] + public async Task TestPropertyNameEncodedAsync() + { + var testCode = @" +class TestClass +{ + int a => 3; + + /// + /// Consider a property [|a|]. + /// + void Method() + { + } +} +"; + var fixedCode = @" +class TestClass +{ + int a => 3; + + /// + /// Consider a property . + /// + void Method() + { + } +} +"; + + await Verify.VerifyCodeFixAsync(testCode, fixedCode); + } + [Fact] public async Task TestFieldNameAsync() { @@ -251,5 +284,81 @@ void Method() await Verify.VerifyCodeFixAsync(testCode, fixedCode); } + + [Fact] + public async Task TestNonDiagnosticPotentialCasesAsync() + { + // These cases could qualify for this diagnostic, but currently do not. + var testCode = @" +class TestClass +{ + int a => 3; + + /// + /// Consider a property a. + /// Consider a property a . + /// Consider a property a . + /// Consider a property a + /// . + /// Consider a property + /// a. + /// Consider a property + /// a + /// . + /// + void Method() + { + } +} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestNonDiagnosticCasesAsync() + { + // These cases *shouldn't* qualify for this diagnostic. + var testCode = @" +class TestClass +{ + int a => 3; + + /// + /// Consider a property b. + /// Consider a property A. + /// Consider a property a>. + /// Consider a property >a. + /// Consider a property a. + /// Consider a property aa. + /// + void Method() + { + } +} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } + + [Fact] + public async Task TestNonCodeAsync() + { + var testCode = @" +class TestClass +{ + int a => 3; + + /// + /// Consider a property a. + /// + void Method() + { + } +} +"; + + await Verify.VerifyAnalyzerAsync(testCode); + } } } diff --git a/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/InlineCodeAnalyzerBase.cs b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/InlineCodeAnalyzerBase.cs index 2d5419b..28a9791 100644 --- a/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/InlineCodeAnalyzerBase.cs +++ b/DocumentationAnalyzers/DocumentationAnalyzers/StyleRules/InlineCodeAnalyzerBase.cs @@ -28,8 +28,8 @@ public override void Initialize(AnalysisContext context) private void HandleXmlElementSyntax(SyntaxNodeAnalysisContext context) { var xmlElement = (XmlElementSyntax)context.Node; - var name = xmlElement.StartTag?.Name; - if (name is null || name.Prefix != null) + var name = xmlElement.StartTag.Name; + if (name.Prefix != null) { return; }