Skip to content

Commit b759c78

Browse files
Fix issue with iteration numbers in CodeFix with multiple similar diagnostics.
1 parent f338cfb commit b759c78

File tree

2 files changed

+54
-23
lines changed

2 files changed

+54
-23
lines changed

src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Analyzers/MissingControlPropertySerializationConfiguration/ControlPropertySerializationDiagnosticAnalyzerTest.cs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,16 @@ public static void Main()
2323
{
2424
var control = new ScalableControl();
2525
26-
control.ScaleFactor = 1.5f;
27-
control.ScaledSize = new SizeF(100, 100);
28-
control.ScaledLocation = new PointF(10, 10);
26+
// We deliberately format this weirdly, to make sure we only format code our code fix touches.
27+
control.ScaleFactor = 1.5f;
28+
control.ScaledSize = new SizeF(100, 100);
29+
control.ScaledLocation = new PointF(10, 10);
2930
}
3031
}
3132
32-
public class ScalableControl : Control
33+
// We are writing the fully-qualified name here to make sure, the Simplifier doesn't remove it,
34+
// since this is nothing our code fix touches.
35+
public class ScalableControl : System.Windows.Forms.Control
3336
{
3437
public float [|ScaleFactor|] { get; set; } = 1.0f;
3538
@@ -77,22 +80,25 @@ public class ScalableControl : Control
7780
using System.ComponentModel;
7881
using System.Drawing;
7982
using System.Windows.Forms;
80-
83+
8184
namespace CSharpControls;
8285
8386
public static class Program
8487
{
8588
public static void Main()
8689
{
8790
var control = new ScalableControl();
88-
89-
control.ScaleFactor = 1.5f;
90-
control.ScaledSize = new SizeF(100, 100);
91-
control.ScaledLocation = new PointF(10, 10);
91+
92+
// We deliberately format this weirdly, to make sure we only format code our code fix touches.
93+
control.ScaleFactor = 1.5f;
94+
control.ScaledSize = new SizeF(100, 100);
95+
control.ScaledLocation = new PointF(10, 10);
9296
}
9397
}
94-
95-
public class ScalableControl : Control
98+
99+
// We are writing the fully-qualified name here to make sure, the Simplifier doesn't remove it,
100+
// since this is nothing our code fix touches.
101+
public class ScalableControl : System.Windows.Forms.Control
96102
{
97103
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
98104
public float ScaleFactor { get; set; } = 1.0f;
@@ -109,9 +115,9 @@ public class ScalableControl : Control
109115
// We are testing the analyzer with all versions of the .NET SDK from 6.0 on.
110116
public static IEnumerable<object[]> GetReferenceAssemblies()
111117
{
112-
yield return [ReferenceAssemblies.Net.Net60Windows];
113-
yield return [ReferenceAssemblies.Net.Net70Windows];
114-
yield return [ReferenceAssemblies.Net.Net80Windows];
118+
// yield return [ReferenceAssemblies.Net.Net60Windows];
119+
// yield return [ReferenceAssemblies.Net.Net70Windows];
120+
// yield return [ReferenceAssemblies.Net.Net80Windows];
115121
yield return [ReferenceAssemblies.Net.Net90Windows];
116122
}
117123

@@ -168,7 +174,8 @@ public async Task CS_AddDesignerSerializationVisibilityCodeFix(ReferenceAssembli
168174
{
169175
OutputKind = OutputKind.WindowsApplication,
170176
},
171-
ReferenceAssemblies = referenceAssemblies
177+
ReferenceAssemblies = referenceAssemblies,
178+
NumberOfFixAllIterations = 2
172179
};
173180

174181
await context.RunAsync();

src/System.Windows.Forms.Analyzers.CodeFixes.CSharp/System/Windows/Forms/CSharp/CodeFixes/AddDesignerSerializationVisibility/AddDesignerSerializationVisibilityCodeFixProvider.cs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@
1111
using Microsoft.CodeAnalysis.CSharp;
1212
using Microsoft.CodeAnalysis.CSharp.Syntax;
1313
using Microsoft.CodeAnalysis.Text;
14+
using System.Diagnostics;
15+
using Microsoft.CodeAnalysis.Simplification;
16+
using Microsoft.CodeAnalysis.Formatting;
1417

1518
namespace System.Windows.Forms.CSharp.CodeFixes.AddDesignerSerializationVisibility;
1619

1720
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AddDesignerSerializationVisibilityCodeFixProvider)), Shared]
1821
internal sealed class AddDesignerSerializationVisibilityCodeFixProvider : CodeFixProvider
1922
{
2023
private const string SystemComponentModelName = "System.ComponentModel";
24+
private const string DesignerSerializationVisibilityAttributeName = "DesignerSerializationVisibility";
2125

2226
public sealed override ImmutableArray<string> FixableDiagnosticIds
2327
=> [DiagnosticIDs.MissingPropertySerializationConfiguration];
@@ -62,9 +66,19 @@ private static async Task<Document> AddDesignerSerializationAttribute(
6266
return document;
6367
}
6468

69+
// Let's make sure, the attribute we want to add is not already there:
70+
if (propertyDeclarationSyntax.AttributeLists
71+
.SelectMany(al => al.Attributes)
72+
.Any(a => a.Name.ToString() == DesignerSerializationVisibilityAttributeName))
73+
{
74+
Debug.Assert(false, "The attribute should not be there.");
75+
76+
return document;
77+
}
78+
6579
// Generate the Attribute we need to put on the property
6680
AttributeSyntax designerSerializationVisibilityAttribute = SyntaxFactory.Attribute(
67-
SyntaxFactory.ParseName("DesignerSerializationVisibility"),
81+
SyntaxFactory.ParseName(DesignerSerializationVisibilityAttributeName),
6882
SyntaxFactory.ParseAttributeArgumentList("(DesignerSerializationVisibility.Hidden)"));
6983

7084
// Make sure, we keep the white spaces before and after the property
@@ -77,31 +91,41 @@ private static async Task<Document> AddDesignerSerializationAttribute(
7791
SyntaxFactory.AttributeList(
7892
SyntaxFactory.SingletonSeparatedList(designerSerializationVisibilityAttribute)));
7993

80-
// Let's restore the trivia:
94+
// Let's format the property, so the attribute is on top of it:
95+
newProperty = newProperty.NormalizeWhitespace();
96+
97+
// Let's restore the original trivia:
8198
newProperty = newProperty.WithLeadingTrivia(leadingTrivia);
8299
newProperty = newProperty.WithTrailingTrivia(trailingTrivia);
83-
84-
UsingDirectiveSyntax usingDirective = SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(SystemComponentModelName));
100+
newProperty = newProperty.WithAdditionalAnnotations(Formatter.Annotation);
85101

86102
// Let's check, if we already have the using directive or if we need to add it:
87103
// (Remember: We can't throw here, as we are in a code fixer. But this also cannot be null.)
88104
SyntaxNode root = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false))!;
89105

106+
// Produce a new root, which has the updated property with the attribute.
107+
root = root.ReplaceNode(propertyDeclarationSyntax, newProperty);
108+
90109
// Let's check if we already have the using directive:
91110
if (!root.DescendantNodes()
92111
.OfType<UsingDirectiveSyntax>()
93112
.Any(u => u?.Name?.ToString() == SystemComponentModelName))
94113
{
114+
UsingDirectiveSyntax usingDirective = SyntaxFactory
115+
.UsingDirective(SyntaxFactory.ParseName(SystemComponentModelName));
116+
117+
usingDirective = usingDirective
118+
.WithAdditionalAnnotations(Simplifier.Annotation)
119+
.WithAdditionalAnnotations(Formatter.Annotation);
120+
95121
// We need to add the using directive:
96122
SyntaxNode firstNode = root.DescendantNodes().First();
97123
root = root.InsertNodesBefore(firstNode, [usingDirective]);
98124
}
99125

100-
// Produce a new root:
101-
SyntaxNode originalRoot = (await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false))!;
102-
SyntaxNode newRoot = originalRoot.ReplaceNode(propertyDeclarationSyntax, newProperty);
126+
document = document.WithSyntaxRoot(root);
103127

104128
// Generate the new document:
105-
return document.WithSyntaxRoot(newRoot);
129+
return document;
106130
}
107131
}

0 commit comments

Comments
 (0)