diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1502CodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1502CodeFixProvider.cs index c0f7e3e8f..2b5ea7cba 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1502CodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1502CodeFixProvider.cs @@ -69,6 +69,7 @@ private Document CreateCodeFix(Document document, IndentationSettings indentatio case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.StructDeclaration: + case SyntaxKindEx.RecordDeclaration: case SyntaxKind.EnumDeclaration: newSyntaxRoot = this.RegisterBaseTypeDeclarationCodeFix(syntaxRoot, (BaseTypeDeclarationSyntax)node, indentationSettings); break; diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/MaintainabilityRules/SA1400CodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/MaintainabilityRules/SA1400CodeFixProvider.cs index c46e0c5a7..8ca6a3930 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/MaintainabilityRules/SA1400CodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/MaintainabilityRules/SA1400CodeFixProvider.cs @@ -13,6 +13,7 @@ namespace StyleCop.Analyzers.MaintainabilityRules using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using StyleCop.Analyzers.Helpers; + using StyleCop.Analyzers.Lightup; /// /// Implements a code fix for . @@ -82,6 +83,10 @@ private static Task GetTransformedDocumentAsync(Document document, Syn updatedDeclarationNode = HandleStructDeclaration((StructDeclarationSyntax)declarationNode); break; + case SyntaxKindEx.RecordDeclaration: + updatedDeclarationNode = HandleRecordDeclaration((RecordDeclarationSyntaxWrapper)declarationNode); + break; + case SyntaxKind.DelegateDeclaration: updatedDeclarationNode = HandleDelegateDeclaration((DelegateDeclarationSyntax)declarationNode); break; @@ -194,6 +199,23 @@ private static SyntaxNode HandleStructDeclaration(StructDeclarationSyntax node) .WithoutFormatting(); } + private static SyntaxNode HandleRecordDeclaration(RecordDeclarationSyntaxWrapper node) + { + SyntaxToken triviaToken = node.Keyword; + if (triviaToken.IsMissing) + { + return null; + } + + SyntaxKind defaultVisibility = IsNestedType(node) ? SyntaxKind.PrivateKeyword : SyntaxKind.InternalKeyword; + SyntaxTokenList modifiers = DeclarationModifiersHelper.AddModifier(node.Modifiers, ref triviaToken, defaultVisibility); + return node + .WithKeyword(triviaToken) + .WithModifiers(modifiers) + .SyntaxNode + .WithoutFormatting(); + } + private static SyntaxNode HandleDelegateDeclaration(DelegateDeclarationSyntax node) { SyntaxToken triviaToken = node.DelegateKeyword; @@ -355,6 +377,7 @@ private static SyntaxNode FindParentDeclarationNode(SyntaxNode node) case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.StructDeclaration: + case SyntaxKindEx.RecordDeclaration: case SyntaxKind.DelegateDeclaration: case SyntaxKind.EventDeclaration: case SyntaxKind.EventFieldDeclaration: diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/SA1205CodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/SA1205CodeFixProvider.cs index 3bb258490..7dd09d5ce 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/SA1205CodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/SA1205CodeFixProvider.cs @@ -13,6 +13,7 @@ namespace StyleCop.Analyzers.OrderingRules using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using StyleCop.Analyzers.Helpers; + using StyleCop.Analyzers.Lightup; /// /// Implements code fixes for . @@ -25,6 +26,7 @@ internal class SA1205CodeFixProvider : CodeFixProvider private static readonly ImmutableArray InternalAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.InternalKeyword); private static readonly ImmutableArray ProtectedAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.ProtectedKeyword); private static readonly ImmutableArray ProtectedOrInternalAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.ProtectedKeyword, SyntaxKind.InternalKeyword); + private static readonly ImmutableArray ProtectedAndInternalAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.PrivateKeyword, SyntaxKind.ProtectedKeyword); private static readonly ImmutableArray PrivateAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.PrivateKeyword); private static readonly ImmutableArray UnexpectedAccessibilityKeywords = ImmutableArray.Create(); @@ -88,6 +90,8 @@ private static ImmutableArray GetMissingAccessModifiers(Accessibilit return ProtectedAccessibilityKeywords; case Accessibility.ProtectedOrInternal: return ProtectedOrInternalAccessibilityKeywords; + case Accessibility.ProtectedAndInternal: + return ProtectedAndInternalAccessibilityKeywords; case Accessibility.Private: return PrivateAccessibilityKeywords; default: @@ -108,6 +112,8 @@ private static TypeDeclarationSyntax ReplaceModifiers(TypeDeclarationSyntax node return ((InterfaceDeclarationSyntax)node).WithModifiers(modifiers); case SyntaxKind.StructDeclaration: return ((StructDeclarationSyntax)node).WithModifiers(modifiers); + case SyntaxKindEx.RecordDeclaration: + return ((RecordDeclarationSyntaxWrapper)node).WithModifiers(modifiers); } return node; @@ -125,6 +131,8 @@ private static TypeDeclarationSyntax ReplaceKeyword(TypeDeclarationSyntax node, return ((InterfaceDeclarationSyntax)node).WithKeyword(keyword); case SyntaxKind.StructDeclaration: return ((StructDeclarationSyntax)node).WithKeyword(keyword); + case SyntaxKindEx.RecordDeclaration: + return ((RecordDeclarationSyntaxWrapper)node).WithKeyword(keyword); } return node; diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/OrderingRules/SA1201CSharp9UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/OrderingRules/SA1201CSharp9UnitTests.cs index 9b146ef14..00ee16983 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/OrderingRules/SA1201CSharp9UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/OrderingRules/SA1201CSharp9UnitTests.cs @@ -3,9 +3,139 @@ namespace StyleCop.Analyzers.Test.CSharp9.OrderingRules { + using System.Threading; + using System.Threading.Tasks; + using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.Test.CSharp8.OrderingRules; + using Xunit; + using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< + StyleCop.Analyzers.OrderingRules.SA1201ElementsMustAppearInTheCorrectOrder, + StyleCop.Analyzers.OrderingRules.ElementOrderCodeFixProvider>; public class SA1201CSharp9UnitTests : SA1201CSharp8UnitTests { + [Fact] + [WorkItem(3236, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3236")] + public async Task TestOuterOrderWithRecordCorrectOrderAsync() + { + string testCode = @"namespace Foo { } +public delegate void bar(); +public enum TestEnum { } +public interface IFoo { } +public struct FooStruct { } +public record FooClass { } +"; + + await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpDiagnosticAsync("namespace OuterNamespace { " + testCode + " }", DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + [WorkItem(3236, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3236")] + public async Task TestOuterOrderWithRecordWrongOrderAsync() + { + string testCode = @" +namespace Foo { } +public enum TestEnum { } +public delegate void {|#0:bar|}(); +public interface IFoo { } +public record FooClass { } +public struct {|#1:FooStruct|} { } +"; + var expected = new[] + { + Diagnostic().WithLocation(0).WithArguments("delegate", "enum"), + Diagnostic().WithLocation(1).WithArguments("struct", "record"), + }; + + await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await VerifyCSharpDiagnosticAsync("namespace OuterNamespace { " + testCode + " }", expected, CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestTypeMemberOrderCorrectOrderRecordAsync() + { + string testCode = @"public record OuterType +{ + public string TestField; + public OuterType(int argument) { TestField = ""foo""; TestProperty = """"; } + public delegate void TestDelegate(); + public event TestDelegate TestEvent { add { } remove { } } + public enum TestEnum { } + public interface ITest { } + public string TestProperty { get; set; } + public string this[string arg] { get { return ""foo""; } set { } } + public static explicit operator bool(OuterType t1) { return t1.TestField != null; } + public static OuterType operator +(OuterType t1, OuterType t2) { return t1; } + public void TestMethod () { } + public struct TestStruct { } + public class TestClass1 { } + public record TestRecord1 { } + public class TestClass2 { } + public record TestRecord2 { } +} +"; + + await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestTypeMemberOrderWrongOrderRecordAsync() + { + string testCode = @"public record OuterType +{ + public string TestField; + public OuterType(int argument) { TestField = ""foo""; TestProperty = ""bar""; } + public interface ITest { } + public delegate void TestDelegate(); + public event TestDelegate TestEvent { add { } remove { } } + public enum TestEnum { } + public static OuterType operator +(OuterType t1, OuterType t2) { return t1; } + public static explicit operator bool(OuterType t1) { return t1.TestField != null; } + public string TestProperty { get; set; } + public struct TestStruct { } + public void TestMethod () { } + public class TestClass { } + public string this[string arg] { get { return ""foo""; } set { } } +} +"; + var expected = new[] + { + Diagnostic().WithLocation(6, 26).WithArguments("delegate", "interface"), + Diagnostic().WithLocation(10, 5).WithArguments("conversion", "operator"), + Diagnostic().WithLocation(11, 19).WithArguments("property", "conversion"), + Diagnostic().WithLocation(13, 17).WithArguments("method", "struct"), + Diagnostic().WithLocation(15, 19).WithArguments("indexer", "class"), + }; + + string fixedCode = @"public record OuterType +{ + public string TestField; + public OuterType(int argument) { TestField = ""foo""; TestProperty = ""bar""; } + public delegate void TestDelegate(); + public event TestDelegate TestEvent { add { } remove { } } + public enum TestEnum { } + public interface ITest { } + public string TestProperty { get; set; } + public string this[string arg] { get { return ""foo""; } set { } } + public static explicit operator bool(OuterType t1) { return t1.TestField != null; } + public static OuterType operator +(OuterType t1, OuterType t2) { return t1; } + public void TestMethod () { } + public struct TestStruct { } + public class TestClass { } +} +"; + + var test = new CSharpTest + { + TestCode = testCode, + FixedCode = fixedCode, + NumberOfIncrementalIterations = 7, + NumberOfFixAllIterations = 3, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1604UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1604UnitTests.cs index 470986eaa..98656c4a7 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1604UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1604UnitTests.cs @@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.DocumentationRules; + using StyleCop.Analyzers.Test.Helpers; using StyleCop.Analyzers.Test.Verifiers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier; @@ -17,10 +18,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules public class SA1604UnitTests { [Theory] - [InlineData("enum")] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeNoDocumentationAsync(string typeName) { var testCode = @" @@ -31,10 +29,7 @@ public async Task TestTypeNoDocumentationAsync(string typeName) } [Theory] - [InlineData("enum")] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithDocumentationAsync(string typeName) { var testCode = @" @@ -48,10 +43,7 @@ public async Task TestTypeWithDocumentationAsync(string typeName) } [Theory] - [InlineData("enum")] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithInheritedDocumentationAsync(string typeName) { var testCode = @" @@ -63,10 +55,7 @@ public async Task TestTypeWithInheritedDocumentationAsync(string typeName) } [Theory] - [InlineData("enum")] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithoutDocumentationAsync(string typeName) { var testCode = @" @@ -82,14 +71,12 @@ public async Task TestTypeWithoutDocumentationAsync(string typeName) } [Theory] - [InlineData("partial class")] - [InlineData("partial struct")] - [InlineData("partial interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestPartialTypeWithoutDocumentationAsync(string typeName) { var testCode = @" /// -{0} +partial {0} TypeName {{ }}"; diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1605UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1605UnitTests.cs index c23a0a5e0..ccdbe6831 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1605UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1605UnitTests.cs @@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.DocumentationRules; + using StyleCop.Analyzers.Test.Helpers; using StyleCop.Analyzers.Test.Verifiers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier; @@ -27,9 +28,7 @@ public class SA1605UnitTests "; [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeNoDocumentationAsync(string typeName) { var testCode = @" @@ -40,9 +39,7 @@ public async Task TestTypeNoDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithSummaryDocumentationAsync(string typeName) { var testCode = @" @@ -56,9 +53,7 @@ public async Task TestTypeWithSummaryDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithContentDocumentationAsync(string typeName) { var testCode = @" @@ -72,9 +67,7 @@ public async Task TestTypeWithContentDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithInheritedDocumentationAsync(string typeName) { var testCode = @" @@ -86,9 +79,7 @@ public async Task TestTypeWithInheritedDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithoutDocumentationAsync(string typeName) { var testCode = @" @@ -104,10 +95,7 @@ public async Task TestTypeWithoutDocumentationAsync(string typeName) } [Theory] - [InlineData("enum")] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestNonPartialTypeWithoutDocumentationAsync(string typeName) { var testCode = @" diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1606UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1606UnitTests.cs index ef71a454b..fdf015ff8 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1606UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1606UnitTests.cs @@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.DocumentationRules; + using StyleCop.Analyzers.Test.Helpers; using StyleCop.Analyzers.Test.Verifiers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier; @@ -17,10 +18,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules public class SA1606UnitTests { [Theory] - [InlineData("enum")] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeNoDocumentationAsync(string typeName) { var testCode = @" @@ -31,10 +29,7 @@ public async Task TestTypeNoDocumentationAsync(string typeName) } [Theory] - [InlineData("enum")] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithDocumentationAsync(string typeName) { var testCode = @" @@ -48,10 +43,7 @@ public async Task TestTypeWithDocumentationAsync(string typeName) } [Theory] - [InlineData("enum")] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithInheritedDocumentationAsync(string typeName) { var testCode = @" @@ -63,10 +55,7 @@ public async Task TestTypeWithInheritedDocumentationAsync(string typeName) } [Theory] - [InlineData("enum")] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithoutDocumentationAsync(string typeName) { var testCode = @" @@ -84,16 +73,14 @@ public async Task TestTypeWithoutDocumentationAsync(string typeName) } [Theory] - [InlineData("partial class")] - [InlineData("partial struct")] - [InlineData("partial interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestPartialTypeWithoutDocumentationAsync(string typeName) { var testCode = @" /// /// /// -{0} +partial {0} TypeName {{ }}"; diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1607UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1607UnitTests.cs index dc31ac889..e7f30cd19 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1607UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1607UnitTests.cs @@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.DocumentationRules; + using StyleCop.Analyzers.Test.Helpers; using StyleCop.Analyzers.Test.Verifiers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier; @@ -17,9 +18,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules public class SA1607UnitTests { [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeNoDocumentationAsync(string typeName) { var testCode = @" @@ -30,9 +29,7 @@ public async Task TestTypeNoDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithSummaryDocumentationAsync(string typeName) { var testCode = @" @@ -46,9 +43,7 @@ public async Task TestTypeWithSummaryDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithContentDocumentationAsync(string typeName) { var testCode = @" @@ -62,9 +57,7 @@ public async Task TestTypeWithContentDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithInheritedDocumentationAsync(string typeName) { var testCode = @" @@ -76,9 +69,7 @@ public async Task TestTypeWithInheritedDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithoutSummaryDocumentationAsync(string typeName) { var testCode = @" @@ -96,10 +87,7 @@ public async Task TestTypeWithoutSummaryDocumentationAsync(string typeName) } [Theory] - [InlineData("enum")] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestNonPartialTypeWithoutSummaryDocumentationAsync(string typeName) { var testCode = @" @@ -114,9 +102,7 @@ public async Task TestNonPartialTypeWithoutSummaryDocumentationAsync(string type } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithoutContentDocumentationAsync(string typeName) { var testCode = @" @@ -134,10 +120,7 @@ public async Task TestTypeWithoutContentDocumentationAsync(string typeName) } [Theory] - [InlineData("enum")] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestNonPartialTypeWithoutContentDocumentationAsync(string typeName) { var testCode = @" @@ -388,10 +371,10 @@ public partial class ClassName await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } - private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult expected, CancellationToken cancellationToken) + protected static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult expected, CancellationToken cancellationToken) => VerifyCSharpDiagnosticAsync(source, new[] { expected }, cancellationToken); - private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[] expected, CancellationToken cancellationToken) + protected static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[] expected, CancellationToken cancellationToken) { string contentWithoutSummaryOrContent = @" diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1608UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1608UnitTests.cs index 2890f74f4..6bff418db 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1608UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1608UnitTests.cs @@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.DocumentationRules; + using StyleCop.Analyzers.Test.Helpers; using StyleCop.Analyzers.Test.Verifiers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier; @@ -17,9 +18,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules public class SA1608UnitTests { [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeNoDocumentationAsync(string typeName) { var testCode = @" @@ -30,9 +29,7 @@ public async Task TestTypeNoDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithSummaryDocumentationAsync(string typeName) { var testCode = @" @@ -46,9 +43,7 @@ public async Task TestTypeWithSummaryDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithContentDocumentationAsync(string typeName) { var testCode = @" @@ -62,9 +57,7 @@ public async Task TestTypeWithContentDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithInheritedDocumentationAsync(string typeName) { var testCode = @" @@ -76,9 +69,7 @@ public async Task TestTypeWithInheritedDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithoutSummaryDocumentationAsync(string typeName) { var testCode = @" @@ -93,9 +84,7 @@ public async Task TestTypeWithoutSummaryDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithoutContentDocumentationAsync(string typeName) { var testCode = @" @@ -110,9 +99,7 @@ public async Task TestTypeWithoutContentDocumentationAsync(string typeName) } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithDefaultDocumentationAsync(string typeName) { var testCode = $@" diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1618UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1618UnitTests.cs index f82c48385..a36b30ac1 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1618UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1618UnitTests.cs @@ -8,6 +8,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.DocumentationRules; + using StyleCop.Analyzers.Lightup; using StyleCop.Analyzers.Test.Verifiers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier; @@ -39,6 +40,11 @@ public static IEnumerable Types yield return new object[] { "class Foo { }" }; yield return new object[] { "struct Foo { }" }; yield return new object[] { "interface Foo { }" }; + if (LightupHelpers.SupportsCSharp9) + { + yield return new object[] { "record Foo { }" }; + yield return new object[] { "record Foo { }" }; + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1619UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1619UnitTests.cs index be9e4707f..c5e996eb8 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1619UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1619UnitTests.cs @@ -8,6 +8,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.DocumentationRules; + using StyleCop.Analyzers.Lightup; using StyleCop.Analyzers.Test.Verifiers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier; @@ -27,6 +28,11 @@ public static IEnumerable Types yield return new object[] { "class Foo { }" }; yield return new object[] { "struct Foo { }" }; yield return new object[] { "interface Foo { }" }; + if (LightupHelpers.SupportsCSharp9) + { + yield return new object[] { "record Foo { }" }; + yield return new object[] { "record Foo { }" }; + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1625UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1625UnitTests.cs index 1129fec6b..1b14516cc 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1625UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1625UnitTests.cs @@ -8,6 +8,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.DocumentationRules; + using StyleCop.Analyzers.Lightup; using StyleCop.Analyzers.Test.Verifiers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier; @@ -28,6 +29,10 @@ public static IEnumerable Members yield return new[] { "public struct Test { }" }; yield return new[] { "public enum Test { }" }; yield return new[] { "public delegate void Test();" }; + if (LightupHelpers.SupportsCSharp9) + { + yield return new[] { "public record Test { }" }; + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/Helpers/CommonMemberData.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/Helpers/CommonMemberData.cs new file mode 100644 index 000000000..92f3d655d --- /dev/null +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/Helpers/CommonMemberData.cs @@ -0,0 +1,43 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +namespace StyleCop.Analyzers.Test.Helpers +{ + using System.Collections.Generic; + using System.Linq; + using StyleCop.Analyzers.Lightup; + + public static class CommonMemberData + { + public static IEnumerable DataTypeDeclarationKeywords + { + get + { + yield return new[] { "class" }; + yield return new[] { "struct" }; + if (LightupHelpers.SupportsCSharp9) + { + yield return new[] { "record" }; + } + } + } + + public static IEnumerable TypeDeclarationKeywords + { + get + { + return DataTypeDeclarationKeywords + .Concat(new[] { new[] { "interface" } }); + } + } + + public static IEnumerable BaseTypeDeclarationKeywords + { + get + { + return TypeDeclarationKeywords + .Concat(new[] { new[] { "enum" } }); + } + } + } +} diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1500/SA1500UnitTests.Classes.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1500/SA1500UnitTests.Classes.cs deleted file mode 100644 index b609a7c36..000000000 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1500/SA1500UnitTests.Classes.cs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. -// Licensed under the MIT License. See LICENSE in the project root for license information. - -namespace StyleCop.Analyzers.Test.LayoutRules -{ - using System.Threading; - using System.Threading.Tasks; - using Microsoft.CodeAnalysis.Testing; - using StyleCop.Analyzers.LayoutRules; - using Xunit; - using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< - StyleCop.Analyzers.LayoutRules.SA1500BracesForMultiLineStatementsMustNotShareLine, - StyleCop.Analyzers.LayoutRules.SA1500CodeFixProvider>; - - /// - /// Unit tests for . - /// - public partial class SA1500UnitTests - { - /// - /// Verifies that no diagnostics are reported for the valid classes defined in this test. - /// - /// - /// These are valid for SA1500 only, some will report other diagnostics from the layout (SA15xx) - /// series. - /// - /// A representing the asynchronous unit test. - [Fact] - public async Task TestClassValidAsync() - { - var testCode = @"public class Foo -{ - public class ValidClass1 - { - } - - public class ValidClass2 - { - public int Field; - } - - public class ValidClass3 { } /* Valid only for SA1500 */ - - public class ValidClass4 { public int Field; } /* Valid only for SA1500 */ - - public class ValidClass5 - { public int Field; } /* Valid only for SA1500 */ -}"; - - await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); - } - - /// - /// Verifies diagnostics and codefixes for all invalid class definitions. - /// - /// - /// These will normally also report SA1401, but not in the unit test. - /// - /// A representing the asynchronous unit test. - [Fact] - public async Task TestClassInvalidAsync() - { - var testCode = @"public class Foo -{ - public class InvalidClass1 { - } - - public class InvalidClass2 { - public int Field; - } - - public class InvalidClass3 { - public int Field; } - - public class InvalidClass4 { public int Field; - } - - public class InvalidClass5 - { - public int Field; } - - public class InvalidClass6 - { public int Field; - } -}"; - - var fixedTestCode = @"public class Foo -{ - public class InvalidClass1 - { - } - - public class InvalidClass2 - { - public int Field; - } - - public class InvalidClass3 - { - public int Field; - } - - public class InvalidClass4 - { - public int Field; - } - - public class InvalidClass5 - { - public int Field; - } - - public class InvalidClass6 - { - public int Field; - } -}"; - - DiagnosticResult[] expectedDiagnostics = - { - // InvalidClass1 - Diagnostic().WithLocation(3, 32), - - // InvalidClass2 - Diagnostic().WithLocation(6, 32), - - // InvalidClass3 - Diagnostic().WithLocation(10, 32), - Diagnostic().WithLocation(11, 27), - - // InvalidClass4 - Diagnostic().WithLocation(13, 32), - - // InvalidClass5 - Diagnostic().WithLocation(18, 27), - - // InvalidClass6 - Diagnostic().WithLocation(21, 5), - }; - - await VerifyCSharpFixAsync(testCode, expectedDiagnostics, fixedTestCode, CancellationToken.None).ConfigureAwait(false); - } - } -} diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1500/SA1500UnitTests.Structs.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1500/SA1500UnitTests.DataTypes.cs similarity index 50% rename from StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1500/SA1500UnitTests.Structs.cs rename to StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1500/SA1500UnitTests.DataTypes.cs index 234c5e613..d4efb240b 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1500/SA1500UnitTests.Structs.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1500/SA1500UnitTests.DataTypes.cs @@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.LayoutRules using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.LayoutRules; + using StyleCop.Analyzers.Test.Helpers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< StyleCop.Analyzers.LayoutRules.SA1500BracesForMultiLineStatementsMustNotShareLine, @@ -18,123 +19,127 @@ namespace StyleCop.Analyzers.Test.LayoutRules public partial class SA1500UnitTests { /// - /// Verifies that no diagnostics are reported for the valid structs defined in this test. + /// Verifies that no diagnostics are reported for the valid data types defined in this test. /// /// /// These are valid for SA1500 only, some will report other diagnostics. /// + /// The data type keyword. /// A representing the asynchronous unit test. - [Fact] - public async Task TestStructValidAsync() + [Theory] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + public async Task TestDataTypeValidAsync(string keyword) { - var testCode = @"public class Foo -{ - public struct ValidStruct1 - { - } - - public struct ValidStruct2 - { + var testCode = $@"public class Foo +{{ + public {keyword} ValidStruct1 + {{ + }} + + public {keyword} ValidStruct2 + {{ public int Field; - } + }} - public struct ValidStruct3 { } /* Valid only for SA1500 */ + public {keyword} ValidStruct3 {{ }} /* Valid only for SA1500 */ - public struct ValidStruct4 { public int Field; } /* Valid only for SA1500 */ + public {keyword} ValidStruct4 {{ public int Field; }} /* Valid only for SA1500 */ - public struct ValidStruct5 /* Valid only for SA1500 */ - { public int Field; } -}"; + public {keyword} ValidStruct5 /* Valid only for SA1500 */ + {{ public int Field; }} +}}"; await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } /// - /// Verifies that diagnostics will be reported for all invalid struct definitions. + /// Verifies that diagnostics will be reported for all invalid data type definitions. /// /// /// These will normally also report SA1401, but not in the unit test. /// + /// The data type keyword. /// A representing the asynchronous unit test. - [Fact] - public async Task TestStructInvalidAsync() + [Theory] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + public async Task TestDataTypeInvalidAsync(string keyword) { - var testCode = @"public class Foo -{ - public struct InvalidStruct1 { - } + var testCode = $@"public class Foo +{{ + public {keyword} InvalidStruct1 {{|#0:{{|}} + }} - public struct InvalidStruct2 { + public {keyword} InvalidStruct2 {{|#1:{{|}} public int Field; - } + }} - public struct InvalidStruct3 { - public int Field; } + public {keyword} InvalidStruct3 {{|#2:{{|}} + public int Field; {{|#3:}}|}} - public struct InvalidStruct4 { public int Field; - } + public {keyword} InvalidStruct4 {{|#4:{{|}} public int Field; + }} - public struct InvalidStruct5 - { - public int Field; } + public {keyword} InvalidStruct5 + {{ + public int Field; {{|#5:}}|}} - public struct InvalidStruct6 - { public int Field; - } -}"; + public {keyword} InvalidStruct6 + {{|#6:{{|}} public int Field; + }} +}}"; - var fixedTestCode = @"public class Foo -{ - public struct InvalidStruct1 - { - } + var fixedTestCode = $@"public class Foo +{{ + public {keyword} InvalidStruct1 + {{ + }} - public struct InvalidStruct2 - { + public {keyword} InvalidStruct2 + {{ public int Field; - } + }} - public struct InvalidStruct3 - { + public {keyword} InvalidStruct3 + {{ public int Field; - } + }} - public struct InvalidStruct4 - { + public {keyword} InvalidStruct4 + {{ public int Field; - } + }} - public struct InvalidStruct5 - { + public {keyword} InvalidStruct5 + {{ public int Field; - } + }} - public struct InvalidStruct6 - { + public {keyword} InvalidStruct6 + {{ public int Field; - } -}"; + }} +}}"; DiagnosticResult[] expectedDiagnostics = { // InvalidStruct1 - Diagnostic().WithLocation(3, 34), + Diagnostic().WithLocation(0), // InvalidStruct2 - Diagnostic().WithLocation(6, 34), + Diagnostic().WithLocation(1), // InvalidStruct3 - Diagnostic().WithLocation(10, 34), - Diagnostic().WithLocation(11, 27), + Diagnostic().WithLocation(2), + Diagnostic().WithLocation(3), // InvalidStruct4 - Diagnostic().WithLocation(13, 34), + Diagnostic().WithLocation(4), // InvalidStruct5 - Diagnostic().WithLocation(18, 27), + Diagnostic().WithLocation(5), // InvalidStruct6 - Diagnostic().WithLocation(21, 5), + Diagnostic().WithLocation(6), }; await VerifyCSharpFixAsync(testCode, expectedDiagnostics, fixedTestCode, CancellationToken.None).ConfigureAwait(false); diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1502/SA1502UnitTests.Constructors.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1502/SA1502UnitTests.Constructors.cs index 874cfdb07..559808109 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1502/SA1502UnitTests.Constructors.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1502/SA1502UnitTests.Constructors.cs @@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.LayoutRules using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.LayoutRules; + using StyleCop.Analyzers.Test.Helpers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< StyleCop.Analyzers.LayoutRules.SA1502ElementMustNotBeOnASingleLine, @@ -23,8 +24,7 @@ public partial class SA1502UnitTests /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestValidEmptyConstructorAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -43,8 +43,7 @@ public Foo(int parameter) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestEmptyConstructorOnSingleLineAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -62,8 +61,7 @@ public Foo(int parameter) { } /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestConstructorOnSingleLineAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -81,8 +79,7 @@ public async Task TestConstructorOnSingleLineAsync(string elementType) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestConstructorWithBlockOnSingleLineAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -101,8 +98,7 @@ public Foo(int parameter) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestConstructorWithBlockStartOnSameLineAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -120,8 +116,7 @@ public Foo(int parameter) { /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestEmptyConstructorOnSingleLineCodeFixAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -145,8 +140,7 @@ public Foo(int parameter) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestConstructorOnSingleLineCodeFixAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -171,8 +165,7 @@ public Foo(int parameter) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestConstructorWithBlockOnSingleLineCodeFixAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -198,8 +191,7 @@ public Foo(int parameter) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestConstructorWithLotsOfTriviaCodeFixAsync(string elementType) { var testCode = @"public ##PH## Foo diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1502/SA1502UnitTests.Methods.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1502/SA1502UnitTests.Methods.cs index 957dc9733..85f2c8378 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1502/SA1502UnitTests.Methods.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1502/SA1502UnitTests.Methods.cs @@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.LayoutRules using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.LayoutRules; + using StyleCop.Analyzers.Test.Helpers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< StyleCop.Analyzers.LayoutRules.SA1502ElementMustNotBeOnASingleLine, @@ -23,8 +24,7 @@ public partial class SA1502UnitTests /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestValidEmptyMethodAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -43,8 +43,7 @@ public void Bar() /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestEmptyMethodOnSingleLineAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -62,8 +61,7 @@ public void Bar() { } /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestMethodOnSingleLineAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -81,8 +79,7 @@ public async Task TestMethodOnSingleLineAsync(string elementType) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestMethodWithBlockOnSingleLineAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -101,8 +98,7 @@ public bool Bar() /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestMethodWithBlockStartOnSameLineAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -120,8 +116,7 @@ public bool Bar() { /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestMethodWithExpressionBodyAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -138,8 +133,7 @@ public async Task TestMethodWithExpressionBodyAsync(string elementType) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestEmptyMethodOnSingleLineCodeFixAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -163,8 +157,7 @@ public void Bar() /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestMethodOnSingleLineCodeFixAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -189,8 +182,7 @@ public bool Bar() /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestMethodWithBlockOnSingleLineCodeFixAsync(string elementType) { var testCode = @"public ##PH## Foo @@ -216,8 +208,7 @@ public bool Bar() /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [InlineData("class")] - [InlineData("struct")] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestMethodWithLotsOfTriviaCodeFixAsync(string elementType) { var testCode = @"public ##PH## Foo diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1502/SA1502UnitTests.TypeDeclarations.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1502/SA1502UnitTests.TypeDeclarations.cs index dcc8b3d9b..bd31070c8 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1502/SA1502UnitTests.TypeDeclarations.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1502/SA1502UnitTests.TypeDeclarations.cs @@ -3,11 +3,11 @@ namespace StyleCop.Analyzers.Test.LayoutRules { - using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.LayoutRules; + using StyleCop.Analyzers.Test.Helpers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< StyleCop.Analyzers.LayoutRules.SA1502ElementMustNotBeOnASingleLine, @@ -18,22 +18,13 @@ namespace StyleCop.Analyzers.Test.LayoutRules /// public partial class SA1502UnitTests { - public static IEnumerable TokensToTest - { - get - { - yield return new[] { "class" }; - yield return new[] { "struct" }; - } - } - /// /// Verifies that a correct empty type will pass without diagnostic. /// /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [MemberData(nameof(TokensToTest))] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestValidEmptyTypeAsync(string token) { var testCode = @"public ##PH## Foo @@ -49,7 +40,7 @@ public async Task TestValidEmptyTypeAsync(string token) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [MemberData(nameof(TokensToTest))] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestEmptyTypeOnSingleLineAsync(string token) { var testCode = "public ##PH## Foo { }"; @@ -64,7 +55,7 @@ public async Task TestEmptyTypeOnSingleLineAsync(string token) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [MemberData(nameof(TokensToTest))] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeOnSingleLineAsync(string token) { var testCode = "public ##PH## Foo { private int bar; }"; @@ -79,7 +70,7 @@ public async Task TestTypeOnSingleLineAsync(string token) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [MemberData(nameof(TokensToTest))] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithBlockOnSingleLineAsync(string token) { var testCode = @"public ##PH## Foo @@ -95,7 +86,7 @@ public async Task TestTypeWithBlockOnSingleLineAsync(string token) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [MemberData(nameof(TokensToTest))] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithBlockStartOnSameLineAsync(string token) { var testCode = @"public ##PH## Foo { @@ -111,7 +102,7 @@ public async Task TestTypeWithBlockStartOnSameLineAsync(string token) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [MemberData(nameof(TokensToTest))] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestEmptyTypeOnSingleLineCodeFixAsync(string token) { var testCode = "public ##PH## Foo { }"; @@ -130,7 +121,7 @@ public async Task TestEmptyTypeOnSingleLineCodeFixAsync(string token) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [MemberData(nameof(TokensToTest))] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeOnSingleLineCodeFixAsync(string token) { var testCode = "public ##PH## Foo { private int bar; }"; @@ -150,7 +141,7 @@ public async Task TestTypeOnSingleLineCodeFixAsync(string token) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [MemberData(nameof(TokensToTest))] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeOnSingleLineWithMultipleStatementsCodeFixAsync(string token) { var testCode = "public ##PH## Foo { private int bar; private bool baz; }"; @@ -170,7 +161,7 @@ public async Task TestTypeOnSingleLineWithMultipleStatementsCodeFixAsync(string /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [MemberData(nameof(TokensToTest))] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithBlockOnSingleLineCodeFixAsync(string token) { var testCode = @"public ##PH## Foo @@ -191,7 +182,7 @@ public async Task TestTypeWithBlockOnSingleLineCodeFixAsync(string token) /// The type of element to test. /// A representing the asynchronous unit test. [Theory] - [MemberData(nameof(TokensToTest))] + [MemberData(nameof(CommonMemberData.DataTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeWithLotsOfTriviaCodeFixAsync(string token) { var testCode = @"public ##PH## Foo /* TR1 */ { /* TR2 */ private int bar; /* TR3 */ private int baz; /* TR4 */ } /* TR5 */"; diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1505UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1505UnitTests.cs index 9a50f9fcb..3124c7f32 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1505UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1505UnitTests.cs @@ -4,10 +4,12 @@ namespace StyleCop.Analyzers.Test.LayoutRules { using System.Collections.Generic; + using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.LayoutRules; + using StyleCop.Analyzers.Test.Helpers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< StyleCop.Analyzers.LayoutRules.SA1505OpeningBracesMustNotBeFollowedByBlankLine, @@ -22,9 +24,17 @@ public static IEnumerable TypeTestData { get { - yield return new object[] { "class", "public " }; - yield return new object[] { "struct", "public " }; - yield return new object[] { "interface", string.Empty }; + foreach (var data in CommonMemberData.TypeDeclarationKeywords) + { + var keyword = (string)data.Single(); + var accessModifier = keyword switch + { + "interface" => string.Empty, + _ => "public ", + }; + + yield return new[] { keyword, accessModifier }; + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1508UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1508UnitTests.cs index 3093fc4c9..c1a0aeb2d 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1508UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1508UnitTests.cs @@ -4,10 +4,12 @@ namespace StyleCop.Analyzers.Test.LayoutRules { using System.Collections.Generic; + using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.LayoutRules; + using StyleCop.Analyzers.Test.Helpers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< StyleCop.Analyzers.LayoutRules.SA1508ClosingBracesMustNotBePrecededByBlankLine, @@ -22,9 +24,17 @@ public static IEnumerable TypeTestData { get { - yield return new object[] { "class", "public " }; - yield return new object[] { "struct", "public " }; - yield return new object[] { "interface", string.Empty }; + foreach (var data in CommonMemberData.TypeDeclarationKeywords) + { + var keyword = (string)data.Single(); + var accessModifier = keyword switch + { + "interface" => string.Empty, + _ => "public ", + }; + + yield return new[] { keyword, accessModifier }; + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/SA1400UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/SA1400UnitTests.cs index acc450c04..50c8fec14 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/SA1400UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/SA1400UnitTests.cs @@ -6,6 +6,7 @@ namespace StyleCop.Analyzers.Test.MaintainabilityRules using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; + using StyleCop.Analyzers.Test.Helpers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< StyleCop.Analyzers.MaintainabilityRules.SA1400AccessModifierMustBeDeclared, @@ -15,202 +16,67 @@ public class SA1400UnitTests { private const string Tab = "\t"; - [Fact] - public async Task TestClassDeclarationAsync() + [Theory] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + public async Task TestTypeDeclarationAsync(string typeName) { - await this.TestTypeDeclarationAsync("class").ConfigureAwait(false); + await this.TestTypeDeclarationImplAsync(typeName).ConfigureAwait(false); } - [Fact] - public async Task TestClassDeclarationWithAttributesAsync() + [Theory] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + public async Task TestPartialTypeDeclarationAsync(string typeName) { - await this.TestTypeDeclarationWithAttributesAsync("class").ConfigureAwait(false); + await this.TestTypeDeclarationImplAsync($"partial {typeName}", warning: false).ConfigureAwait(false); } - [Fact] - public async Task TestClassDeclarationWithDirectivesAsync() - { - await this.TestTypeDeclarationWithDirectivesAsync("class").ConfigureAwait(false); - } - - [Fact] - public async Task TestNestedClassDeclarationAsync() + [Theory] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + public async Task TestTypeDeclarationWithAttributesAsync(string typeName) { - await this.TestNestedTypeDeclarationAsync("class").ConfigureAwait(false); + await this.TestTypeDeclarationWithAttributesImplAsync(typeName).ConfigureAwait(false); } - [Fact] - public async Task TestNestedClassDeclarationWithAttributesAsync() + [Theory] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + public async Task TestPartialTypeDeclarationWithAttributesAsync(string typeName) { - await this.TestNestedTypeDeclarationWithAttributesAsync("class").ConfigureAwait(false); + await this.TestTypeDeclarationWithAttributesImplAsync($"partial {typeName}", warning: false).ConfigureAwait(false); } - [Fact] - public async Task TestNestedClassDeclarationWithDirectivesAsync() + [Theory] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + public async Task TestTypeDeclarationWithDirectivesAsync(string typeName) { - await this.TestNestedTypeDeclarationWithDirectivesAsync("class").ConfigureAwait(false); + await this.TestTypeDeclarationWithDirectivesImplAsync(typeName).ConfigureAwait(false); } - [Fact] - public async Task TestPartialClassDeclarationAsync() + [Theory] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + public async Task TestPartialTypeDeclarationWithDirectivesAsync(string typeName) { - await this.TestTypeDeclarationAsync("partial class", warning: false).ConfigureAwait(false); + await this.TestTypeDeclarationWithDirectivesImplAsync($"partial {typeName}", warning: false).ConfigureAwait(false); } - [Fact] - public async Task TestPartialClassDeclarationWithAttributesAsync() + [Theory] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + public async Task TestNestedTypeDeclarationAsync(string typeName) { - await this.TestTypeDeclarationWithAttributesAsync("partial class", warning: false).ConfigureAwait(false); + await this.TestNestedTypeDeclarationImplAsync(typeName).ConfigureAwait(false); } - [Fact] - public async Task TestPartialClassDeclarationWithDirectivesAsync() + [Theory] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + public async Task TestNestedTypeDeclarationWithAttributesAsync(string typeName) { - await this.TestTypeDeclarationWithDirectivesAsync("partial class", warning: false).ConfigureAwait(false); + await this.TestNestedTypeDeclarationWithAttributesImplAsync(typeName).ConfigureAwait(false); } - [Fact] - public async Task TestInterfaceDeclarationAsync() - { - await this.TestTypeDeclarationAsync("interface").ConfigureAwait(false); - } - - [Fact] - public async Task TestInterfaceDeclarationWithAttributesAsync() - { - await this.TestTypeDeclarationWithAttributesAsync("interface").ConfigureAwait(false); - } - - [Fact] - public async Task TestInterfaceDeclarationWithDirectivesAsync() - { - await this.TestTypeDeclarationWithDirectivesAsync("interface").ConfigureAwait(false); - } - - [Fact] - public async Task TestNestedInterfaceDeclarationAsync() - { - await this.TestNestedTypeDeclarationAsync("interface").ConfigureAwait(false); - } - - [Fact] - public async Task TestNestedInterfaceDeclarationWithAttributesAsync() - { - await this.TestNestedTypeDeclarationWithAttributesAsync("interface").ConfigureAwait(false); - } - - [Fact] - public async Task TestNestedInterfaceDeclarationWithDirectivesAsync() - { - await this.TestNestedTypeDeclarationWithDirectivesAsync("interface").ConfigureAwait(false); - } - - [Fact] - public async Task TestPartialInterfaceDeclarationAsync() - { - await this.TestTypeDeclarationAsync("partial interface", warning: false).ConfigureAwait(false); - } - - [Fact] - public async Task TestPartialInterfaceDeclarationWithAttributesAsync() - { - await this.TestTypeDeclarationWithAttributesAsync("partial interface", warning: false).ConfigureAwait(false); - } - - [Fact] - public async Task TestPartialInterfaceDeclarationWithDirectivesAsync() - { - await this.TestTypeDeclarationWithDirectivesAsync("partial interface", warning: false).ConfigureAwait(false); - } - - [Fact] - public async Task TestStructDeclarationAsync() - { - await this.TestTypeDeclarationAsync("struct").ConfigureAwait(false); - } - - [Fact] - public async Task TestStructDeclarationWithAttributesAsync() - { - await this.TestTypeDeclarationWithAttributesAsync("struct").ConfigureAwait(false); - } - - [Fact] - public async Task TestStructDeclarationWithDirectivesAsync() - { - await this.TestTypeDeclarationWithDirectivesAsync("struct").ConfigureAwait(false); - } - - [Fact] - public async Task TestNestedStructDeclarationAsync() - { - await this.TestNestedTypeDeclarationAsync("struct").ConfigureAwait(false); - } - - [Fact] - public async Task TestNestedStructDeclarationWithAttributesAsync() - { - await this.TestNestedTypeDeclarationWithAttributesAsync("struct").ConfigureAwait(false); - } - - [Fact] - public async Task TestNestedStructDeclarationWithDirectivesAsync() - { - await this.TestNestedTypeDeclarationWithDirectivesAsync("struct").ConfigureAwait(false); - } - - [Fact] - public async Task TestPartialStructDeclarationAsync() - { - await this.TestTypeDeclarationAsync("partial struct", warning: false).ConfigureAwait(false); - } - - [Fact] - public async Task TestPartialStructDeclarationWithAttributesAsync() - { - await this.TestTypeDeclarationWithAttributesAsync("partial struct", warning: false).ConfigureAwait(false); - } - - [Fact] - public async Task TestPartialStructDeclarationWithDirectivesAsync() - { - await this.TestTypeDeclarationWithDirectivesAsync("partial struct", warning: false).ConfigureAwait(false); - } - - [Fact] - public async Task TestEnumDeclarationAsync() - { - await this.TestTypeDeclarationAsync("enum").ConfigureAwait(false); - } - - [Fact] - public async Task TestEnumDeclarationWithAttributesAsync() - { - await this.TestTypeDeclarationWithAttributesAsync("enum").ConfigureAwait(false); - } - - [Fact] - public async Task TestEnumDeclarationWithDirectivesAsync() - { - await this.TestTypeDeclarationWithDirectivesAsync("enum").ConfigureAwait(false); - } - - [Fact] - public async Task TestNestedEnumDeclarationAsync() - { - await this.TestNestedTypeDeclarationAsync("enum").ConfigureAwait(false); - } - - [Fact] - public async Task TestNestedEnumDeclarationWithAttributesAsync() - { - await this.TestNestedTypeDeclarationWithAttributesAsync("enum").ConfigureAwait(false); - } - - [Fact] - public async Task TestNestedEnumDeclarationWithDirectivesAsync() + [Theory] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + public async Task TestNestedTypeDeclarationWithDirectivesAsync(string typeName) { - await this.TestNestedTypeDeclarationWithDirectivesAsync("enum").ConfigureAwait(false); + await this.TestNestedTypeDeclarationWithDirectivesImplAsync(typeName).ConfigureAwait(false); } [Fact] @@ -651,32 +517,32 @@ public async Task TestStaticConstructorDeclarationWithDirectivesAsync() await this.TestNestedDeclarationWithDirectivesAsync("private", "OuterTypeName", "static OuterTypeName(", " ) { }", warning: false).ConfigureAwait(false); } - private async Task TestTypeDeclarationAsync(string keyword, bool warning = true) + private async Task TestTypeDeclarationImplAsync(string keyword, bool warning = true) { await this.TestDeclarationAsync("internal", "TypeName", $"{keyword} TypeName", "{\n}", warning: warning).ConfigureAwait(false); } - private async Task TestTypeDeclarationWithAttributesAsync(string keyword, bool warning = true) + private async Task TestTypeDeclarationWithAttributesImplAsync(string keyword, bool warning = true) { await this.TestDeclarationWithAttributesAsync("internal", "TypeName", $"{keyword} TypeName", "{\n}", warning: warning).ConfigureAwait(false); } - private async Task TestTypeDeclarationWithDirectivesAsync(string keyword, bool warning = true) + private async Task TestTypeDeclarationWithDirectivesImplAsync(string keyword, bool warning = true) { await this.TestDeclarationWithDirectivesAsync("internal", "TypeName", $"{keyword} TypeName", "{\n}", warning: warning).ConfigureAwait(false); } - private async Task TestNestedTypeDeclarationAsync(string keyword, bool warning = true) + private async Task TestNestedTypeDeclarationImplAsync(string keyword, bool warning = true) { await this.TestNestedDeclarationAsync("private", "TypeName", $"{keyword} TypeName", "{\n}", warning: warning).ConfigureAwait(false); } - private async Task TestNestedTypeDeclarationWithAttributesAsync(string keyword, bool warning = true) + private async Task TestNestedTypeDeclarationWithAttributesImplAsync(string keyword, bool warning = true) { await this.TestNestedDeclarationWithAttributesAsync("private", "TypeName", $"{keyword} TypeName", "{\n}", warning: warning).ConfigureAwait(false); } - private async Task TestNestedTypeDeclarationWithDirectivesAsync(string keyword, bool warning = true) + private async Task TestNestedTypeDeclarationWithDirectivesImplAsync(string keyword, bool warning = true) { await this.TestNestedDeclarationWithDirectivesAsync("private", "TypeName", $"{keyword} TypeName", "{\n}", warning: warning).ConfigureAwait(false); } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1205UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1205UnitTests.cs index 4514a8253..62417a7e8 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1205UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1205UnitTests.cs @@ -6,7 +6,9 @@ namespace StyleCop.Analyzers.Test.OrderingRules using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; + using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Testing; + using StyleCop.Analyzers.Lightup; using StyleCop.Analyzers.OrderingRules; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< @@ -45,6 +47,14 @@ public static IEnumerable ValidDeclarations yield return new object[] { "class" }; yield return new object[] { "struct" }; yield return new object[] { "interface" }; + if (LightupHelpers.SupportsCSharp9) + { + yield return new object[] { "public partial record" }; + yield return new object[] { "internal partial record" }; + yield return new object[] { "public sealed partial record" }; + yield return new object[] { "internal sealed partial record" }; + yield return new object[] { "record" }; + } } } @@ -57,6 +67,11 @@ public static IEnumerable InvalidDeclarations yield return new object[] { "static partial class" }; yield return new object[] { "partial struct" }; yield return new object[] { "partial interface" }; + if (LightupHelpers.SupportsCSharp9) + { + yield return new object[] { "partial record" }; + yield return new object[] { "sealed partial record" }; + } } } @@ -81,6 +96,23 @@ public static IEnumerable ValidNestedDeclarations yield return new object[] { "internal", "interface" }; yield return new object[] { "protected internal", "interface" }; yield return new object[] { "private", "interface" }; + + if (LightupHelpers.SupportsCSharp72) + { + yield return new object[] { "private protected", "class" }; + yield return new object[] { "private protected", "struct" }; + yield return new object[] { "private protected", "interface" }; + } + + if (LightupHelpers.SupportsCSharp9) + { + yield return new object[] { "public", "record" }; + yield return new object[] { "protected", "record" }; + yield return new object[] { "internal", "record" }; + yield return new object[] { "protected internal", "record" }; + yield return new object[] { "private", "record" }; + yield return new object[] { "private protected", "record" }; + } } } @@ -202,7 +234,14 @@ internal static partial class TestPartial }} "; - await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + var languageVersion = (LightupHelpers.SupportsCSharp8, LightupHelpers.SupportsCSharp72) switch + { + // Make sure to use C# 7.2 if supported, unless we are going to default to something greater + (false, true) => LanguageVersionEx.CSharp7_2, + _ => (LanguageVersion?)null, + }; + + await VerifyCSharpDiagnosticAsync(languageVersion, testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } /// @@ -271,7 +310,14 @@ public class Foo }} "; - await VerifyCSharpFixAsync(testCode, Diagnostic().WithLocation(8, 14 + typeKeyword.Length), fixedTestCode, CancellationToken.None).ConfigureAwait(false); + var languageVersion = (LightupHelpers.SupportsCSharp8, LightupHelpers.SupportsCSharp72) switch + { + // Make sure to use C# 7.2 if supported, unless we are going to default to something greater + (false, true) => LanguageVersionEx.CSharp7_2, + _ => (LanguageVersion?)null, + }; + + await VerifyCSharpFixAsync(languageVersion, testCode, Diagnostic().WithLocation(8, 14 + typeKeyword.Length), fixedTestCode, CancellationToken.None).ConfigureAwait(false); } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1106UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1106UnitTests.cs index b33af2468..53629ecd2 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1106UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1106UnitTests.cs @@ -6,6 +6,7 @@ namespace StyleCop.Analyzers.Test.ReadabilityRules using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; + using StyleCop.Analyzers.Test.Helpers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< StyleCop.Analyzers.ReadabilityRules.SA1106CodeMustNotContainEmptyStatements, @@ -306,17 +307,14 @@ public void TestMethod() } [Theory] - [InlineData("class Foo { }")] - [InlineData("struct Foo { }")] - [InlineData("interface IFoo { }")] - [InlineData("enum Foo { }")] - [InlineData("namespace Foo { }")] - public async Task TestMemberAsync(string declaration) + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + [InlineData("namespace")] + public async Task TestMemberAsync(string declarationKeyword) { - var testCode = declaration + ";"; - var fixedCode = declaration; + var testCode = declarationKeyword + " Foo { }{|#0:;|}"; + var fixedCode = declarationKeyword + " Foo { }"; - DiagnosticResult expected = Diagnostic().WithLocation(1, declaration.Length + 1); + DiagnosticResult expected = Diagnostic().WithLocation(0); await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false); } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1137UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1137UnitTests.cs index 931c7cb8a..cd8c69d52 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1137UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1137UnitTests.cs @@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.ReadabilityRules using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.ReadabilityRules; + using StyleCop.Analyzers.Test.Helpers; using Xunit; using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< StyleCop.Analyzers.ReadabilityRules.SA1137ElementsShouldHaveTheSameIndentation, @@ -18,10 +19,7 @@ namespace StyleCop.Analyzers.Test.ReadabilityRules public class SA1137UnitTests { [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] - [InlineData("enum")] + [MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestNamespaceDeclarationAsync(string baseTypeKind) { string testCode = $@" @@ -148,9 +146,7 @@ class MyAttribute : Attribute {{ }} } [Theory] - [InlineData("class")] - [InlineData("struct")] - [InlineData("interface")] + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] public async Task TestTypeDeclarationConstraintClausesAsync(string typeKind) { string testCode = $@" @@ -219,11 +215,20 @@ public async Task TestTypeDeclarationConstraintClausesAsync(string typeKind) } [Theory] - [InlineData("class", "int", " { }")] - [InlineData("struct", "int", " { }")] - [InlineData("interface", "event System.EventHandler", ";")] - public async Task TestTypeDeclarationMembersAsync(string typeKind, string fieldType, string methodBody) + [MemberData(nameof(CommonMemberData.TypeDeclarationKeywords), MemberType = typeof(CommonMemberData))] + public async Task TestTypeDeclarationMembersAsync(string typeKind) { + string fieldType = typeKind switch + { + "interface" => "event System.EventHandler", + _ => "int", + }; + string methodBody = typeKind switch + { + "interface" => ";", + _ => " { }", + }; + string testCode = $@" {typeKind} Container1 {{ diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SyntaxKinds.cs b/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SyntaxKinds.cs index ca92e8560..c9bd85aaa 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SyntaxKinds.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SyntaxKinds.cs @@ -6,6 +6,7 @@ namespace StyleCop.Analyzers.Helpers using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; + using StyleCop.Analyzers.Lightup; internal static class SyntaxKinds { @@ -22,7 +23,8 @@ internal static class SyntaxKinds SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.InterfaceDeclaration, - SyntaxKind.EnumDeclaration); + SyntaxKind.EnumDeclaration, + SyntaxKindEx.RecordDeclaration); /// /// Gets a collection of values which appear in the syntax tree as a @@ -36,7 +38,8 @@ internal static class SyntaxKinds ImmutableArray.Create( SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration, - SyntaxKind.InterfaceDeclaration); + SyntaxKind.InterfaceDeclaration, + SyntaxKindEx.RecordDeclaration); /// /// Gets a collection of values which appear in the syntax tree as a diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/OrderingRules/SA1201ElementsMustAppearInTheCorrectOrder.cs b/StyleCop.Analyzers/StyleCop.Analyzers/OrderingRules/SA1201ElementsMustAppearInTheCorrectOrder.cs index 7000709c8..97f314aca 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/OrderingRules/SA1201ElementsMustAppearInTheCorrectOrder.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/OrderingRules/SA1201ElementsMustAppearInTheCorrectOrder.cs @@ -11,6 +11,7 @@ namespace StyleCop.Analyzers.OrderingRules using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using StyleCop.Analyzers.Helpers; + using StyleCop.Analyzers.Lightup; using StyleCop.Analyzers.Settings.ObjectModel; /// @@ -120,9 +121,6 @@ internal class SA1201ElementsMustAppearInTheCorrectOrder : DiagnosticAnalyzer private static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.OrderingRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink); - private static readonly ImmutableArray TypeDeclarationKinds = - ImmutableArray.Create(SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.InterfaceDeclaration); - // extern alias and usings are missing here because the compiler itself is enforcing the right order. private static readonly ImmutableArray OuterOrder = ImmutableArray.Create( SyntaxKind.NamespaceDeclaration, @@ -156,11 +154,13 @@ internal class SA1201ElementsMustAppearInTheCorrectOrder : DiagnosticAnalyzer [SyntaxKind.InterfaceDeclaration] = "interface", [SyntaxKind.StructDeclaration] = "struct", [SyntaxKind.ClassDeclaration] = "class", + [SyntaxKindEx.RecordDeclaration] = "record", [SyntaxKind.FieldDeclaration] = "field", [SyntaxKind.ConstructorDeclaration] = "constructor", [SyntaxKind.DestructorDeclaration] = "destructor", [SyntaxKind.DelegateDeclaration] = "delegate", [SyntaxKind.EventDeclaration] = "event", + [SyntaxKind.EventFieldDeclaration] = "event", [SyntaxKind.EnumDeclaration] = "enum", [SyntaxKind.InterfaceDeclaration] = "interface", [SyntaxKind.PropertyDeclaration] = "property", @@ -186,7 +186,7 @@ public override void Initialize(AnalysisContext context) context.RegisterSyntaxNodeAction(CompilationUnitAction, SyntaxKind.CompilationUnit); context.RegisterSyntaxNodeAction(NamespaceDeclarationAction, SyntaxKind.NamespaceDeclaration); - context.RegisterSyntaxNodeAction(TypeDeclarationAction, TypeDeclarationKinds); + context.RegisterSyntaxNodeAction(TypeDeclarationAction, SyntaxKinds.TypeDeclaration); } private static void HandleTypeDeclaration(SyntaxNodeAnalysisContext context, StyleCopSettings settings) @@ -287,12 +287,10 @@ private static void HandleMemberList(SyntaxNodeAnalysisContext context, Immutabl } var elementSyntaxKind = members[i].Kind(); - elementSyntaxKind = elementSyntaxKind == SyntaxKind.EventFieldDeclaration ? SyntaxKind.EventDeclaration : elementSyntaxKind; - int index = order.IndexOf(elementSyntaxKind); + int index = order.IndexOf(GetSyntaxKindForOrdering(elementSyntaxKind)); var nextElementSyntaxKind = members[i + 1].Kind(); - nextElementSyntaxKind = nextElementSyntaxKind == SyntaxKind.EventFieldDeclaration ? SyntaxKind.EventDeclaration : nextElementSyntaxKind; - int nextIndex = order.IndexOf(nextElementSyntaxKind); + int nextIndex = order.IndexOf(GetSyntaxKindForOrdering(nextElementSyntaxKind)); if (index > nextIndex) { @@ -304,5 +302,15 @@ private static void HandleMemberList(SyntaxNodeAnalysisContext context, Immutabl } } } + + private static SyntaxKind GetSyntaxKindForOrdering(SyntaxKind syntaxKind) + { + return syntaxKind switch + { + SyntaxKind.EventFieldDeclaration => SyntaxKind.EventDeclaration, + SyntaxKindEx.RecordDeclaration => SyntaxKind.ClassDeclaration, + _ => syntaxKind, + }; + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1137ElementsShouldHaveTheSameIndentation.cs b/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1137ElementsShouldHaveTheSameIndentation.cs index f803492c2..e51db24c3 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1137ElementsShouldHaveTheSameIndentation.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1137ElementsShouldHaveTheSameIndentation.cs @@ -308,6 +308,7 @@ private static void AddMemberAndAttributes(ImmutableList.Builder ele case SyntaxKind.StructDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: + case SyntaxKindEx.RecordDeclaration: elements.AddRange(((BaseTypeDeclarationSyntax)member).AttributeLists); break; diff --git a/documentation/SA1201.md b/documentation/SA1201.md index f66c76706..ccf2d2c51 100644 --- a/documentation/SA1201.md +++ b/documentation/SA1201.md @@ -49,6 +49,8 @@ Within a class, struct, or interface, elements should be positioned in the follo * Structs * Classes* +> 📝 For ordering purposes, C# 9 records are treated as classes. + Complying with a standard ordering scheme based on element type can increase the readability and maintainability of the file and encourage code reuse. When implementing an interface, it is sometimes desirable to group all members of the interface next to one another. This will sometimes require violating this rule, if the interface contains elements of different types. This problem can be solved through the use of partial classes.