From 7adfdd5ef63c63c7c93c9dbeb1b7bf42ae45e5b6 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 2 Oct 2020 11:57:41 -0700 Subject: [PATCH] Update SA1649 to support record types Fixes #3213 --- .../SA1649CSharp9UnitTests.cs | 79 +++++++++++++++++++ .../DocumentationRules/SA1649UnitTests.cs | 71 +++++++++-------- .../Helpers/NamedTypeHelpers.cs | 1 + 3 files changed, 120 insertions(+), 31 deletions(-) diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/DocumentationRules/SA1649CSharp9UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/DocumentationRules/SA1649CSharp9UnitTests.cs index 446a490b9..b23bb8d7d 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/DocumentationRules/SA1649CSharp9UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/DocumentationRules/SA1649CSharp9UnitTests.cs @@ -3,9 +3,88 @@ namespace StyleCop.Analyzers.Test.CSharp9.DocumentationRules { + using System.Collections.Generic; + using System.Threading.Tasks; + using Microsoft.CodeAnalysis.CSharp; using StyleCop.Analyzers.Test.CSharp8.DocumentationRules; + using Xunit; public class SA1649CSharp9UnitTests : SA1649CSharp8UnitTests { + public static IEnumerable CSharp9TypeKeywords + { + get + { + foreach (var keyword in TypeKeywords) + { + yield return keyword; + } + + yield return new object[] { "record", LanguageVersion.CSharp9 }; + } + } + + [Theory] + [MemberData(nameof(CSharp9TypeKeywords))] + public override async Task VerifyWrongFileNameAsync(string typeKeyword, LanguageVersion languageVersion) + { + await base.VerifyWrongFileNameAsync(typeKeyword, languageVersion).ConfigureAwait(false); + } + + [Theory] + [MemberData(nameof(CSharp9TypeKeywords))] + public override async Task VerifyWrongFileNameMultipleExtensionsAsync(string typeKeyword, LanguageVersion languageVersion) + { + await base.VerifyWrongFileNameMultipleExtensionsAsync(typeKeyword, languageVersion).ConfigureAwait(false); + } + + [Theory] + [MemberData(nameof(CSharp9TypeKeywords))] + public override async Task VerifyWrongFileNameNoExtensionAsync(string typeKeyword, LanguageVersion languageVersion) + { + await base.VerifyWrongFileNameNoExtensionAsync(typeKeyword, languageVersion).ConfigureAwait(false); + } + + [Theory] + [MemberData(nameof(CSharp9TypeKeywords))] + public override async Task VerifyCaseInsensitivityAsync(string typeKeyword, LanguageVersion languageVersion) + { + await base.VerifyCaseInsensitivityAsync(typeKeyword, languageVersion).ConfigureAwait(false); + } + + [Theory] + [MemberData(nameof(CSharp9TypeKeywords))] + public override async Task VerifyFirstTypeIsUsedAsync(string typeKeyword, LanguageVersion languageVersion) + { + await base.VerifyFirstTypeIsUsedAsync(typeKeyword, languageVersion).ConfigureAwait(false); + } + + [Theory] + [MemberData(nameof(CSharp9TypeKeywords))] + public override async Task VerifyThatPartialTypesAreIgnoredAsync(string typeKeyword, LanguageVersion languageVersion) + { + await base.VerifyThatPartialTypesAreIgnoredAsync(typeKeyword, languageVersion).ConfigureAwait(false); + } + + [Theory] + [MemberData(nameof(CSharp9TypeKeywords))] + public override async Task VerifyStyleCopNamingConventionForGenericTypeAsync(string typeKeyword, LanguageVersion languageVersion) + { + await base.VerifyStyleCopNamingConventionForGenericTypeAsync(typeKeyword, languageVersion).ConfigureAwait(false); + } + + [Theory] + [MemberData(nameof(CSharp9TypeKeywords))] + public override async Task VerifyMetadataNamingConventionForGenericTypeAsync(string typeKeyword, LanguageVersion languageVersion) + { + await base.VerifyMetadataNamingConventionForGenericTypeAsync(typeKeyword, languageVersion).ConfigureAwait(false); + } + + [Theory] + [MemberData(nameof(CSharp9TypeKeywords))] + public override async Task VerifyMetadataNamingConventionForGenericTypeMultipleExtensionsAsync(string typeKeyword, LanguageVersion languageVersion) + { + await base.VerifyMetadataNamingConventionForGenericTypeMultipleExtensionsAsync(typeKeyword, languageVersion).ConfigureAwait(false); + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1649UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1649UnitTests.cs index 8c51db55d..c0f9554a0 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1649UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1649UnitTests.cs @@ -6,10 +6,10 @@ namespace StyleCop.Analyzers.Test.DocumentationRules using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; + using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.DocumentationRules; using StyleCop.Analyzers.Test.Verifiers; - using TestHelper; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier; @@ -42,9 +42,9 @@ public static IEnumerable TypeKeywords { get { - yield return new object[] { "class" }; - yield return new object[] { "struct" }; - yield return new object[] { "interface" }; + yield return new object[] { "class", LanguageVersion.CSharp6 }; + yield return new object[] { "struct", LanguageVersion.CSharp6 }; + yield return new object[] { "interface", LanguageVersion.CSharp6 }; } } @@ -52,10 +52,11 @@ public static IEnumerable TypeKeywords /// Verifies that a wrong file name is correctly reported. /// /// The type keyword to use during the test. + /// The language version to test with. /// A representing the asynchronous unit test. [Theory] [MemberData(nameof(TypeKeywords))] - public async Task VerifyWrongFileNameAsync(string typeKeyword) + public virtual async Task VerifyWrongFileNameAsync(string typeKeyword, LanguageVersion languageVersion) { var testCode = $@"namespace TestNamespace {{ @@ -74,7 +75,7 @@ public async Task VerifyWrongFileNameAsync(string typeKeyword) "; var expectedDiagnostic = Diagnostic().WithLocation("WrongFileName.cs", 3, 13 + typeKeyword.Length); - await VerifyCSharpFixAsync("WrongFileName.cs", testCode, StyleCopSettings, expectedDiagnostic, "TestType.cs", fixedCode, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpFixAsync(languageVersion, "WrongFileName.cs", testCode, StyleCopSettings, expectedDiagnostic, "TestType.cs", fixedCode, CancellationToken.None).ConfigureAwait(false); } /// @@ -82,10 +83,11 @@ public async Task VerifyWrongFileNameAsync(string typeKeyword) /// regression test for DotNetAnalyzers/StyleCopAnalyzers#1829. /// /// The type keyword to use during the test. + /// The language version to test with. /// A representing the asynchronous unit test. [Theory] [MemberData(nameof(TypeKeywords))] - public async Task VerifyWrongFileNameMultipleExtensionsAsync(string typeKeyword) + public virtual async Task VerifyWrongFileNameMultipleExtensionsAsync(string typeKeyword, LanguageVersion languageVersion) { var testCode = $@"namespace TestNamespace {{ @@ -104,7 +106,7 @@ public async Task VerifyWrongFileNameMultipleExtensionsAsync(string typeKeyword) "; var expectedDiagnostic = Diagnostic().WithLocation("WrongFileName.svc.cs", 3, 13 + typeKeyword.Length); - await VerifyCSharpFixAsync("WrongFileName.svc.cs", testCode, StyleCopSettings, expectedDiagnostic, "TestType.svc.cs", fixedCode, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpFixAsync(languageVersion, "WrongFileName.svc.cs", testCode, StyleCopSettings, expectedDiagnostic, "TestType.svc.cs", fixedCode, CancellationToken.None).ConfigureAwait(false); } /// @@ -112,10 +114,11 @@ public async Task VerifyWrongFileNameMultipleExtensionsAsync(string typeKeyword) /// for DotNetAnalyzers/StyleCopAnalyzers#1829. /// /// The type keyword to use during the test. + /// The language version to test with. /// A representing the asynchronous unit test. [Theory] [MemberData(nameof(TypeKeywords))] - public async Task VerifyWrongFileNameNoExtensionAsync(string typeKeyword) + public virtual async Task VerifyWrongFileNameNoExtensionAsync(string typeKeyword, LanguageVersion languageVersion) { var testCode = $@"namespace TestNamespace {{ @@ -134,17 +137,18 @@ public async Task VerifyWrongFileNameNoExtensionAsync(string typeKeyword) "; var expectedDiagnostic = Diagnostic().WithLocation("WrongFileName", 3, 13 + typeKeyword.Length); - await VerifyCSharpFixAsync("WrongFileName", testCode, StyleCopSettings, expectedDiagnostic, "TestType", fixedCode, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpFixAsync(languageVersion, "WrongFileName", testCode, StyleCopSettings, expectedDiagnostic, "TestType", fixedCode, CancellationToken.None).ConfigureAwait(false); } /// /// Verifies that the file name is not case sensitive. /// /// The type keyword to use during the test. + /// The language version to test with. /// A representing the asynchronous unit test. [Theory] [MemberData(nameof(TypeKeywords))] - public async Task VerifyCaseInsensitivityAsync(string typeKeyword) + public virtual async Task VerifyCaseInsensitivityAsync(string typeKeyword, LanguageVersion languageVersion) { var testCode = $@"namespace TestNamespace {{ @@ -154,17 +158,18 @@ public async Task VerifyCaseInsensitivityAsync(string typeKeyword) }} "; - await VerifyCSharpDiagnosticAsync("testtype.cs", testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpDiagnosticAsync(languageVersion, "testtype.cs", testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } /// /// Verifies that the file name is based on the first type. /// /// The type keyword to use during the test. + /// The language version to test with. /// A representing the asynchronous unit test. [Theory] [MemberData(nameof(TypeKeywords))] - public async Task VerifyFirstTypeIsUsedAsync(string typeKeyword) + public virtual async Task VerifyFirstTypeIsUsedAsync(string typeKeyword, LanguageVersion languageVersion) { var testCode = $@"namespace TestNamespace {{ @@ -178,17 +183,18 @@ public async Task VerifyFirstTypeIsUsedAsync(string typeKeyword) }} "; - await VerifyCSharpDiagnosticAsync("TestType.cs", testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpDiagnosticAsync(languageVersion, "TestType.cs", testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } /// /// Verifies that partial types are ignored. /// /// The type keyword to use during the test. + /// The language version to test with. /// A representing the asynchronous unit test. [Theory] [MemberData(nameof(TypeKeywords))] - public async Task VerifyThatPartialTypesAreIgnoredAsync(string typeKeyword) + public virtual async Task VerifyThatPartialTypesAreIgnoredAsync(string typeKeyword, LanguageVersion languageVersion) { var testCode = $@"namespace TestNamespace {{ @@ -198,17 +204,18 @@ public partial {typeKeyword} TestType }} "; - await VerifyCSharpDiagnosticAsync("WrongFileName.cs", testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpDiagnosticAsync(languageVersion, "WrongFileName.cs", testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } /// /// Verifies that the StyleCop file name convention for a generic type is handled correctly. /// /// The type keyword to use during the test. + /// The language version to test with. /// A representing the asynchronous unit test. [Theory] [MemberData(nameof(TypeKeywords))] - public async Task VerifyStyleCopNamingConventionForGenericTypeAsync(string typeKeyword) + public virtual async Task VerifyStyleCopNamingConventionForGenericTypeAsync(string typeKeyword, LanguageVersion languageVersion) { var testCode = $@"namespace TestNamespace {{ @@ -219,18 +226,19 @@ public async Task VerifyStyleCopNamingConventionForGenericTypeAsync(string typeK "; var expectedDiagnostic = Diagnostic().WithLocation("TestType`3.cs", 3, 13 + typeKeyword.Length); - await VerifyCSharpDiagnosticAsync("TestType.cs", testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); - await VerifyCSharpFixAsync("TestType`3.cs", testCode, StyleCopSettings, expectedDiagnostic, "TestType{T1,T2,T3}.cs", testCode, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpDiagnosticAsync(languageVersion, "TestType.cs", testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpFixAsync(languageVersion, "TestType`3.cs", testCode, StyleCopSettings, expectedDiagnostic, "TestType{T1,T2,T3}.cs", testCode, CancellationToken.None).ConfigureAwait(false); } /// /// Verifies that the metadata file name convention for a generic type is handled correctly. /// /// The type keyword to use during the test. + /// The language version to test with. /// A representing the asynchronous unit test. [Theory] [MemberData(nameof(TypeKeywords))] - public async Task VerifyMetadataNamingConventionForGenericTypeAsync(string typeKeyword) + public virtual async Task VerifyMetadataNamingConventionForGenericTypeAsync(string typeKeyword, LanguageVersion languageVersion) { var testCode = $@"namespace TestNamespace {{ @@ -241,10 +249,10 @@ public async Task VerifyMetadataNamingConventionForGenericTypeAsync(string typeK "; var expectedDiagnostic = Diagnostic().WithLocation("TestType{T1,T2,T3}.cs", 3, 13 + typeKeyword.Length); - await VerifyCSharpFixAsync("TestType{T1,T2,T3}.cs", testCode, MetadataSettings, expectedDiagnostic, "TestType`3.cs", testCode, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpFixAsync(languageVersion, "TestType{T1,T2,T3}.cs", testCode, MetadataSettings, expectedDiagnostic, "TestType`3.cs", testCode, CancellationToken.None).ConfigureAwait(false); expectedDiagnostic = Diagnostic().WithLocation("TestType.cs", 3, 13 + typeKeyword.Length); - await VerifyCSharpFixAsync("TestType.cs", testCode, MetadataSettings, expectedDiagnostic, "TestType`3.cs", testCode, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpFixAsync(languageVersion, "TestType.cs", testCode, MetadataSettings, expectedDiagnostic, "TestType`3.cs", testCode, CancellationToken.None).ConfigureAwait(false); } /// @@ -252,10 +260,11 @@ public async Task VerifyMetadataNamingConventionForGenericTypeAsync(string typeK /// regression test for DotNetAnalyzers/StyleCopAnalyzers#1829. /// /// The type keyword to use during the test. + /// The language version to test with. /// A representing the asynchronous unit test. [Theory] [MemberData(nameof(TypeKeywords))] - public async Task VerifyMetadataNamingConventionForGenericTypeMultipleExtensionsAsync(string typeKeyword) + public virtual async Task VerifyMetadataNamingConventionForGenericTypeMultipleExtensionsAsync(string typeKeyword, LanguageVersion languageVersion) { var testCode = $@"namespace TestNamespace {{ @@ -274,7 +283,7 @@ public async Task VerifyMetadataNamingConventionForGenericTypeMultipleExtensions "; var expectedDiagnostic = Diagnostic().WithLocation("TestType.svc.cs", 3, 13 + typeKeyword.Length); - await VerifyCSharpFixAsync("TestType.svc.cs", testCode, MetadataSettings, expectedDiagnostic, "TestType`1.svc.cs", fixedCode, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpFixAsync(languageVersion, "TestType.svc.cs", testCode, MetadataSettings, expectedDiagnostic, "TestType`1.svc.cs", fixedCode, CancellationToken.None).ConfigureAwait(false); } /// @@ -289,12 +298,12 @@ public async Task VerifyWithoutFirstTypeAsync() } "; - await VerifyCSharpDiagnosticAsync("Test0.cs", testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpDiagnosticAsync(LanguageVersion.CSharp6, "Test0.cs", testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } - internal static Task VerifyCSharpDiagnosticAsync(string fileName, string source, DiagnosticResult[] expected, CancellationToken cancellationToken) + internal static Task VerifyCSharpDiagnosticAsync(LanguageVersion languageVersion, string fileName, string source, DiagnosticResult[] expected, CancellationToken cancellationToken) { - var test = new StyleCopCodeFixVerifier.CSharpTest + var test = new StyleCopCodeFixVerifier.CSharpTest(languageVersion) { TestSources = { (fileName, source) }, }; @@ -303,12 +312,12 @@ internal static Task VerifyCSharpDiagnosticAsync(string fileName, string source, return test.RunAsync(cancellationToken); } - internal static Task VerifyCSharpFixAsync(string oldFileName, string source, string testSettings, DiagnosticResult expected, string newFileName, string fixedSource, CancellationToken cancellationToken) - => VerifyCSharpFixAsync(oldFileName, source, testSettings, new[] { expected }, newFileName, fixedSource, cancellationToken); + internal static Task VerifyCSharpFixAsync(LanguageVersion languageVersion, string oldFileName, string source, string testSettings, DiagnosticResult expected, string newFileName, string fixedSource, CancellationToken cancellationToken) + => VerifyCSharpFixAsync(languageVersion, oldFileName, source, testSettings, new[] { expected }, newFileName, fixedSource, cancellationToken); - internal static Task VerifyCSharpFixAsync(string oldFileName, string source, string testSettings, DiagnosticResult[] expected, string newFileName, string fixedSource, CancellationToken cancellationToken) + internal static Task VerifyCSharpFixAsync(LanguageVersion languageVersion, string oldFileName, string source, string testSettings, DiagnosticResult[] expected, string newFileName, string fixedSource, CancellationToken cancellationToken) { - var test = new StyleCopCodeFixVerifier.CSharpTest + var test = new StyleCopCodeFixVerifier.CSharpTest(languageVersion) { TestSources = { (oldFileName, source) }, FixedSources = { (newFileName, fixedSource) }, diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/NamedTypeHelpers.cs b/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/NamedTypeHelpers.cs index 23f6b24b8..f0dabde55 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/NamedTypeHelpers.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/NamedTypeHelpers.cs @@ -86,6 +86,7 @@ internal static string GetNameOrIdentifier(MemberDeclarationSyntax member) case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.StructDeclaration: + case SyntaxKindEx.RecordDeclaration: return ((TypeDeclarationSyntax)member).Identifier.Text; case SyntaxKind.EnumDeclaration: