Skip to content

Commit

Permalink
Merge pull request #3264 from sharwell/record-order
Browse files Browse the repository at this point in the history
Support records in several additional rules
  • Loading branch information
sharwell authored Dec 5, 2020
2 parents 9b285fc + e6720f1 commit 3414e7a
Show file tree
Hide file tree
Showing 28 changed files with 528 additions and 590 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
/// Implements a code fix for <see cref="SA1400AccessModifierMustBeDeclared"/>.
Expand Down Expand Up @@ -82,6 +83,10 @@ private static Task<Document> 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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
/// Implements code fixes for <see cref="SA1205PartialElementsMustDeclareAccess"/>.
Expand All @@ -25,6 +26,7 @@ internal class SA1205CodeFixProvider : CodeFixProvider
private static readonly ImmutableArray<SyntaxKind> InternalAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.InternalKeyword);
private static readonly ImmutableArray<SyntaxKind> ProtectedAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.ProtectedKeyword);
private static readonly ImmutableArray<SyntaxKind> ProtectedOrInternalAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.ProtectedKeyword, SyntaxKind.InternalKeyword);
private static readonly ImmutableArray<SyntaxKind> ProtectedAndInternalAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.PrivateKeyword, SyntaxKind.ProtectedKeyword);
private static readonly ImmutableArray<SyntaxKind> PrivateAccessibilityKeywords = ImmutableArray.Create(SyntaxKind.PrivateKeyword);
private static readonly ImmutableArray<SyntaxKind> UnexpectedAccessibilityKeywords = ImmutableArray.Create<SyntaxKind>();

Expand Down Expand Up @@ -88,6 +90,8 @@ private static ImmutableArray<SyntaxKind> GetMissingAccessModifiers(Accessibilit
return ProtectedAccessibilityKeywords;
case Accessibility.ProtectedOrInternal:
return ProtectedOrInternalAccessibilityKeywords;
case Accessibility.ProtectedAndInternal:
return ProtectedAndInternalAccessibilityKeywords;
case Accessibility.Private:
return PrivateAccessibilityKeywords;
default:
Expand All @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<StyleCop.Analyzers.DocumentationRules.SA1604ElementDocumentationMustHaveSummary>;
Expand All @@ -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 = @"
Expand All @@ -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 = @"
Expand All @@ -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 = @"
Expand All @@ -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 = @"
Expand All @@ -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
{{
}}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<StyleCop.Analyzers.DocumentationRules.SA1605PartialElementDocumentationMustHaveSummary>;
Expand All @@ -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 = @"
Expand All @@ -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 = @"
Expand All @@ -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 = @"
Expand All @@ -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 = @"
Expand All @@ -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 = @"
Expand All @@ -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 = @"
Expand Down
Loading

0 comments on commit 3414e7a

Please sign in to comment.