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