diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer.CodeFixes/AttributesOnSeparateLines.cs b/IntelliTect.Analyzer/IntelliTect.Analyzer.CodeFixes/AttributesOnSeparateLines.cs index d8f6e9d..c3f7b86 100644 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer.CodeFixes/AttributesOnSeparateLines.cs +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer.CodeFixes/AttributesOnSeparateLines.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Linq; @@ -48,8 +47,8 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) attributeList = attributeList.Parent; } - // Get the class, method or property adjacent to the AttributeList - if (attributeList?.Parent is not SyntaxNode parentDeclaration) + // Get the member declaration adjacent to the AttributeList + if (attributeList?.Parent is not MemberDeclarationSyntax parentDeclaration) { return; } @@ -63,12 +62,12 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) diagnostic); } - private static async Task PutOnSeparateLine(Document document, SyntaxNode parentDeclaration, CancellationToken cancellationToken) + private static async Task PutOnSeparateLine(Document document, MemberDeclarationSyntax parentDeclaration, CancellationToken cancellationToken) { var attributeLists = new SyntaxList(); // put every attribute into it's own attributelist eg.: [A,B,C] => [A][B][C] - foreach (AttributeSyntax attribute in GetAttributeListSyntaxes(parentDeclaration).SelectMany(l => l.Attributes)) + foreach (AttributeSyntax attribute in parentDeclaration.AttributeLists.SelectMany(l => l.Attributes)) { attributeLists = attributeLists.Add( SyntaxFactory.AttributeList( @@ -80,7 +79,7 @@ private static async Task PutOnSeparateLine(Document document, SyntaxN } // the formatter-annotation will wrap every attribute on a separate line - SyntaxNode newNode = BuildNodeWithAttributeLists(parentDeclaration, attributeLists) + MemberDeclarationSyntax newNode = parentDeclaration.WithAttributeLists(attributeLists) .WithAdditionalAnnotations(Formatter.Annotation); // Replace the old local declaration with the new local declaration. @@ -90,29 +89,5 @@ private static async Task PutOnSeparateLine(Document document, SyntaxN return document.WithSyntaxRoot(newRoot); } - - private static IEnumerable GetAttributeListSyntaxes(SyntaxNode node) - { - return node switch - { - ClassDeclarationSyntax c => c.AttributeLists, - MethodDeclarationSyntax m => m.AttributeLists, - PropertyDeclarationSyntax p => p.AttributeLists, - FieldDeclarationSyntax f => f.AttributeLists, - _ => throw new NotImplementedException(), - }; - } - - private static SyntaxNode BuildNodeWithAttributeLists(SyntaxNode node, SyntaxList attributeLists) - { - return node switch - { - ClassDeclarationSyntax c => c.WithAttributeLists(attributeLists), - MethodDeclarationSyntax m => m.WithAttributeLists(attributeLists), - PropertyDeclarationSyntax p => p.WithAttributeLists(attributeLists), - FieldDeclarationSyntax f => f.WithAttributeLists(attributeLists), - _ => throw new NotImplementedException(), - }; - } } } diff --git a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/AttributesOnSeparateLinesTests.cs b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/AttributesOnSeparateLinesTests.cs index b64e6ad..5125273 100644 --- a/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/AttributesOnSeparateLinesTests.cs +++ b/IntelliTect.Analyzer/IntelliTect.Analyzer.Test/AttributesOnSeparateLinesTests.cs @@ -390,6 +390,312 @@ static void Main() await VerifyCSharpFix(test, fixTest); } + [TestMethod] + public async Task StructAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines() + { + string test = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + [A][B] + struct MyStruct + { + } +}"; + + string fixTest = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + [A] + [B] + struct MyStruct + { + } +}"; + await VerifyCSharpFix(test, fixTest); + } + + [TestMethod] + public async Task RecordAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines() + { + string test = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + [A][B] + record MyRecord + { + } +}"; + + string fixTest = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + [A] + [B] + record MyRecord + { + } +}"; + await VerifyCSharpFix(test, fixTest); + } + + [TestMethod] + public async Task InterfaceAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines() + { + string test = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + [A][B] + interface IMyInterface + { + } +}"; + + string fixTest = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + [A] + [B] + interface IMyInterface + { + } +}"; + await VerifyCSharpFix(test, fixTest); + } + + [TestMethod] + public async Task EnumAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines() + { + string test = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + [A][B] + enum MyEnum + { + Foo + } +}"; + + string fixTest = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + [A] + [B] + enum MyEnum + { + Foo + } +}"; + await VerifyCSharpFix(test, fixTest); + } + + [TestMethod] + public async Task ConstructorAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines() + { + string test = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + class Program + { + [A][B] + Program() + { + } + } +}"; + + string fixTest = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + class Program + { + [A] + [B] + Program() + { + } + } +}"; + await VerifyCSharpFix(test, fixTest); + } + + [TestMethod] + public async Task EnumMemberAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines() + { + string test = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + enum MyEnum + { + [A][B] + Bar + } +}"; + + string fixTest = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + enum MyEnum + { + [A] + [B] + Bar + } +}"; + await VerifyCSharpFix(test, fixTest); + } + + [TestMethod] + public async Task IndexerAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines() + { + string test = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + class Program + { + [A][B] + int this[int i] { get => 0; } + } +}"; + + string fixTest = @"using System; + +namespace ConsoleApp +{ + class AAttribute : Attribute + { + } + + class BAttribute : Attribute + { + } + + class Program + { + [A] + [B] + int this[int i] { get => 0; } + } +}"; + await VerifyCSharpFix(test, fixTest); + } + [TestMethod] [Description("Analyzer should not report on generated code")] public void AttributesOnSameLine_InGeneratedCode_NoDiagnostic()