Skip to content

Commit a254af7

Browse files
Fix INTL0101 code fix NotImplementedException for non-class declaration types
The AttributesOnSeparateLines code fix only handled ClassDeclarationSyntax, MethodDeclarationSyntax, PropertyDeclarationSyntax, and FieldDeclarationSyntax. Any other declaration type flagged by the analyzer (structs, records, interfaces, enums, constructors, enum members, indexers, etc.) would throw NotImplementedException when a user tried to apply the code fix. Replace the two switch expressions with a MemberDeclarationSyntax base class pattern match in RegisterCodeFixesAsync, so unsupported node types silently skip registering the code fix rather than crash. PutOnSeparateLine is updated to accept MemberDeclarationSyntax directly, calling AttributeLists and WithAttributeLists() without any helper switch methods. Add code fix tests for: struct, record, interface, enum type, constructor, enum member, and indexer declarations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4569a5b commit a254af7

2 files changed

Lines changed: 311 additions & 30 deletions

File tree

IntelliTect.Analyzer/IntelliTect.Analyzer.CodeFixes/AttributesOnSeparateLines.cs

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.Collections.Immutable;
43
using System.Composition;
54
using System.Linq;
@@ -48,8 +47,8 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
4847
attributeList = attributeList.Parent;
4948
}
5049

51-
// Get the class, method or property adjacent to the AttributeList
52-
if (attributeList?.Parent is not SyntaxNode parentDeclaration)
50+
// Get the member declaration adjacent to the AttributeList
51+
if (attributeList?.Parent is not MemberDeclarationSyntax parentDeclaration)
5352
{
5453
return;
5554
}
@@ -63,12 +62,12 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
6362
diagnostic);
6463
}
6564

66-
private static async Task<Document> PutOnSeparateLine(Document document, SyntaxNode parentDeclaration, CancellationToken cancellationToken)
65+
private static async Task<Document> PutOnSeparateLine(Document document, MemberDeclarationSyntax parentDeclaration, CancellationToken cancellationToken)
6766
{
6867
var attributeLists = new SyntaxList<AttributeListSyntax>();
6968

7069
// put every attribute into it's own attributelist eg.: [A,B,C] => [A][B][C]
71-
foreach (AttributeSyntax attribute in GetAttributeListSyntaxes(parentDeclaration).SelectMany(l => l.Attributes))
70+
foreach (AttributeSyntax attribute in parentDeclaration.AttributeLists.SelectMany(l => l.Attributes))
7271
{
7372
attributeLists = attributeLists.Add(
7473
SyntaxFactory.AttributeList(
@@ -80,7 +79,7 @@ private static async Task<Document> PutOnSeparateLine(Document document, SyntaxN
8079
}
8180

8281
// the formatter-annotation will wrap every attribute on a separate line
83-
SyntaxNode newNode = BuildNodeWithAttributeLists(parentDeclaration, attributeLists)
82+
MemberDeclarationSyntax newNode = parentDeclaration.WithAttributeLists(attributeLists)
8483
.WithAdditionalAnnotations(Formatter.Annotation);
8584

8685
// Replace the old local declaration with the new local declaration.
@@ -90,29 +89,5 @@ private static async Task<Document> PutOnSeparateLine(Document document, SyntaxN
9089

9190
return document.WithSyntaxRoot(newRoot);
9291
}
93-
94-
private static IEnumerable<AttributeListSyntax> GetAttributeListSyntaxes(SyntaxNode node)
95-
{
96-
return node switch
97-
{
98-
ClassDeclarationSyntax c => c.AttributeLists,
99-
MethodDeclarationSyntax m => m.AttributeLists,
100-
PropertyDeclarationSyntax p => p.AttributeLists,
101-
FieldDeclarationSyntax f => f.AttributeLists,
102-
_ => throw new NotImplementedException(),
103-
};
104-
}
105-
106-
private static SyntaxNode BuildNodeWithAttributeLists(SyntaxNode node, SyntaxList<AttributeListSyntax> attributeLists)
107-
{
108-
return node switch
109-
{
110-
ClassDeclarationSyntax c => c.WithAttributeLists(attributeLists),
111-
MethodDeclarationSyntax m => m.WithAttributeLists(attributeLists),
112-
PropertyDeclarationSyntax p => p.WithAttributeLists(attributeLists),
113-
FieldDeclarationSyntax f => f.WithAttributeLists(attributeLists),
114-
_ => throw new NotImplementedException(),
115-
};
116-
}
11792
}
11893
}

IntelliTect.Analyzer/IntelliTect.Analyzer.Test/AttributesOnSeparateLinesTests.cs

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,312 @@ static void Main()
390390
await VerifyCSharpFix(test, fixTest);
391391
}
392392

393+
[TestMethod]
394+
public async Task StructAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines()
395+
{
396+
string test = @"using System;
397+
398+
namespace ConsoleApp
399+
{
400+
class AAttribute : Attribute
401+
{
402+
}
403+
404+
class BAttribute : Attribute
405+
{
406+
}
407+
408+
[A][B]
409+
struct MyStruct
410+
{
411+
}
412+
}";
413+
414+
string fixTest = @"using System;
415+
416+
namespace ConsoleApp
417+
{
418+
class AAttribute : Attribute
419+
{
420+
}
421+
422+
class BAttribute : Attribute
423+
{
424+
}
425+
426+
[A]
427+
[B]
428+
struct MyStruct
429+
{
430+
}
431+
}";
432+
await VerifyCSharpFix(test, fixTest);
433+
}
434+
435+
[TestMethod]
436+
public async Task RecordAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines()
437+
{
438+
string test = @"using System;
439+
440+
namespace ConsoleApp
441+
{
442+
class AAttribute : Attribute
443+
{
444+
}
445+
446+
class BAttribute : Attribute
447+
{
448+
}
449+
450+
[A][B]
451+
record MyRecord
452+
{
453+
}
454+
}";
455+
456+
string fixTest = @"using System;
457+
458+
namespace ConsoleApp
459+
{
460+
class AAttribute : Attribute
461+
{
462+
}
463+
464+
class BAttribute : Attribute
465+
{
466+
}
467+
468+
[A]
469+
[B]
470+
record MyRecord
471+
{
472+
}
473+
}";
474+
await VerifyCSharpFix(test, fixTest);
475+
}
476+
477+
[TestMethod]
478+
public async Task InterfaceAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines()
479+
{
480+
string test = @"using System;
481+
482+
namespace ConsoleApp
483+
{
484+
class AAttribute : Attribute
485+
{
486+
}
487+
488+
class BAttribute : Attribute
489+
{
490+
}
491+
492+
[A][B]
493+
interface IMyInterface
494+
{
495+
}
496+
}";
497+
498+
string fixTest = @"using System;
499+
500+
namespace ConsoleApp
501+
{
502+
class AAttribute : Attribute
503+
{
504+
}
505+
506+
class BAttribute : Attribute
507+
{
508+
}
509+
510+
[A]
511+
[B]
512+
interface IMyInterface
513+
{
514+
}
515+
}";
516+
await VerifyCSharpFix(test, fixTest);
517+
}
518+
519+
[TestMethod]
520+
public async Task EnumAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines()
521+
{
522+
string test = @"using System;
523+
524+
namespace ConsoleApp
525+
{
526+
class AAttribute : Attribute
527+
{
528+
}
529+
530+
class BAttribute : Attribute
531+
{
532+
}
533+
534+
[A][B]
535+
enum MyEnum
536+
{
537+
Foo
538+
}
539+
}";
540+
541+
string fixTest = @"using System;
542+
543+
namespace ConsoleApp
544+
{
545+
class AAttribute : Attribute
546+
{
547+
}
548+
549+
class BAttribute : Attribute
550+
{
551+
}
552+
553+
[A]
554+
[B]
555+
enum MyEnum
556+
{
557+
Foo
558+
}
559+
}";
560+
await VerifyCSharpFix(test, fixTest);
561+
}
562+
563+
[TestMethod]
564+
public async Task ConstructorAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines()
565+
{
566+
string test = @"using System;
567+
568+
namespace ConsoleApp
569+
{
570+
class AAttribute : Attribute
571+
{
572+
}
573+
574+
class BAttribute : Attribute
575+
{
576+
}
577+
578+
class Program
579+
{
580+
[A][B]
581+
Program()
582+
{
583+
}
584+
}
585+
}";
586+
587+
string fixTest = @"using System;
588+
589+
namespace ConsoleApp
590+
{
591+
class AAttribute : Attribute
592+
{
593+
}
594+
595+
class BAttribute : Attribute
596+
{
597+
}
598+
599+
class Program
600+
{
601+
[A]
602+
[B]
603+
Program()
604+
{
605+
}
606+
}
607+
}";
608+
await VerifyCSharpFix(test, fixTest);
609+
}
610+
611+
[TestMethod]
612+
public async Task EnumMemberAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines()
613+
{
614+
string test = @"using System;
615+
616+
namespace ConsoleApp
617+
{
618+
class AAttribute : Attribute
619+
{
620+
}
621+
622+
class BAttribute : Attribute
623+
{
624+
}
625+
626+
enum MyEnum
627+
{
628+
[A][B]
629+
Bar
630+
}
631+
}";
632+
633+
string fixTest = @"using System;
634+
635+
namespace ConsoleApp
636+
{
637+
class AAttribute : Attribute
638+
{
639+
}
640+
641+
class BAttribute : Attribute
642+
{
643+
}
644+
645+
enum MyEnum
646+
{
647+
[A]
648+
[B]
649+
Bar
650+
}
651+
}";
652+
await VerifyCSharpFix(test, fixTest);
653+
}
654+
655+
[TestMethod]
656+
public async Task IndexerAttributeLineViolation_CodeFix_TwoAttributesOnSameLine_TwoAttributesOnSeparateLines()
657+
{
658+
string test = @"using System;
659+
660+
namespace ConsoleApp
661+
{
662+
class AAttribute : Attribute
663+
{
664+
}
665+
666+
class BAttribute : Attribute
667+
{
668+
}
669+
670+
class Program
671+
{
672+
[A][B]
673+
int this[int i] { get => 0; }
674+
}
675+
}";
676+
677+
string fixTest = @"using System;
678+
679+
namespace ConsoleApp
680+
{
681+
class AAttribute : Attribute
682+
{
683+
}
684+
685+
class BAttribute : Attribute
686+
{
687+
}
688+
689+
class Program
690+
{
691+
[A]
692+
[B]
693+
int this[int i] { get => 0; }
694+
}
695+
}";
696+
await VerifyCSharpFix(test, fixTest);
697+
}
698+
393699
[TestMethod]
394700
[Description("Analyzer should not report on generated code")]
395701
public void AttributesOnSameLine_InGeneratedCode_NoDiagnostic()

0 commit comments

Comments
 (0)