From 89eec163552bcf31aa863e3654bc6488919a0ea5 Mon Sep 17 00:00:00 2001 From: Cory Johnson Date: Fri, 4 Oct 2019 21:23:24 -0500 Subject: [PATCH 1/3] #903 - Fixed the duplication of the readonly keyword when applying the readonly fix (single and multiple). --- .../Usage/ReadonlyFieldCodeFixProvider.cs | 13 ++-- .../Usage/ReadonlyFieldTests.cs | 71 +++++++++++++++++++ 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/CSharp/CodeCracker/Usage/ReadonlyFieldCodeFixProvider.cs b/src/CSharp/CodeCracker/Usage/ReadonlyFieldCodeFixProvider.cs index 1b9e85a58..445fd87ca 100644 --- a/src/CSharp/CodeCracker/Usage/ReadonlyFieldCodeFixProvider.cs +++ b/src/CSharp/CodeCracker/Usage/ReadonlyFieldCodeFixProvider.cs @@ -44,10 +44,13 @@ private async static Task MakeFieldReadonlyAsync(Document document, Di private static SyntaxNode MakeSingleFieldReadonly(SyntaxNode root, FieldDeclarationSyntax fieldDeclaration) { - var newFieldDeclaration = fieldDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)) - .WithTrailingTrivia(fieldDeclaration.GetTrailingTrivia()) - .WithLeadingTrivia(fieldDeclaration.GetLeadingTrivia()) - .WithAdditionalAnnotations(Formatter.Annotation); + + var newFieldDeclaration = fieldDeclaration + .WithoutLeadingTrivia() + .AddModifiers(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)) + .WithTrailingTrivia(fieldDeclaration.GetTrailingTrivia()) + .WithLeadingTrivia(fieldDeclaration.GetLeadingTrivia()) + .WithAdditionalAnnotations(Formatter.Annotation); var newRoot = root.ReplaceNode(fieldDeclaration, newFieldDeclaration); return newRoot; } @@ -57,7 +60,7 @@ private static SyntaxNode MakeMultipleFieldsReadonly(SyntaxNode root, FieldDecla var newFieldDeclaration = fieldDeclaration.WithDeclaration(newDeclaration); var newReadonlyFieldDeclaration = fieldDeclaration.WithDeclaration(SyntaxFactory.VariableDeclaration(fieldDeclaration.Declaration.Type, SyntaxFactory.SeparatedList(new[] { variableToMakeReadonly }))) .WithoutLeadingTrivia() - .WithTrailingTrivia(SyntaxFactory.ParseTrailingTrivia("\n")) + .WithTrailingTrivia(SyntaxFactory.ParseTrailingTrivia("\r\n")) .AddModifiers(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)) .WithAdditionalAnnotations(Formatter.Annotation); var newRoot = root.ReplaceNode(fieldDeclaration, new[] { newFieldDeclaration, newReadonlyFieldDeclaration }); diff --git a/test/CSharp/CodeCracker.Test/Usage/ReadonlyFieldTests.cs b/test/CSharp/CodeCracker.Test/Usage/ReadonlyFieldTests.cs index 8639541c5..3375774f4 100644 --- a/test/CSharp/CodeCracker.Test/Usage/ReadonlyFieldTests.cs +++ b/test/CSharp/CodeCracker.Test/Usage/ReadonlyFieldTests.cs @@ -433,6 +433,77 @@ class TypeName await VerifyCSharpFixAsync(source, fixtest); } + + [Fact] + public async Task DoesNotDuplicateLeadingDirectivesOnFixSingle() + { + const string source = @" + namespace ConsoleApplication1 + { + public class Foo + { + #region standard + Foo foo1 = new Foo(); + #endregion + + #region withAccessModifier + private Foo foo2 = new Foo(); + #endregion + + #region withStatic + private static Foo foo3 = new Foo(); + #endregion + } + }"; + const string fixtest = @" + namespace ConsoleApplication1 + { + public class Foo + { + #region standard + readonly Foo foo1 = new Foo(); + #endregion + + #region withAccessModifier + private readonly Foo foo2 = new Foo(); + #endregion + + #region withStatic + private static readonly Foo foo3 = new Foo(); + #endregion + } + }"; + await VerifyCSharpFixAsync(source, fixtest); + } + + + [Fact] + public async Task DoesNotDuplicateLeadingDirectivesOnFixAll() + { + const string source = @" + namespace ConsoleApplication1 + { + public class Foo + { + #region standard + Foo foo1, foo2 = new Foo(), foo3; + #endregion + } + }"; + const string fixtest = @" + namespace ConsoleApplication1 + { + public class Foo + { + #region standard + Foo foo1, foo3; + readonly Foo foo2 = new Foo(); + #endregion + } + }"; + await VerifyCSharpFixAsync(source, fixtest); + } + [Fact] public async Task FieldsWithAssignmentOnDeclarationWithSingleDeclarationCreatesDiagnostic() { From 1d19e2c704a5627a4763f36880f883b778e5b126 Mon Sep 17 00:00:00 2001 From: Cory Johnson Date: Fri, 4 Oct 2019 21:23:24 -0500 Subject: [PATCH 2/3] #903 - Fixed the duplication of directives when applying 'readonly' fix. --- .../Usage/ReadonlyFieldCodeFixProvider.cs | 13 ++-- .../Usage/ReadonlyFieldTests.cs | 71 +++++++++++++++++++ 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/CSharp/CodeCracker/Usage/ReadonlyFieldCodeFixProvider.cs b/src/CSharp/CodeCracker/Usage/ReadonlyFieldCodeFixProvider.cs index 1b9e85a58..445fd87ca 100644 --- a/src/CSharp/CodeCracker/Usage/ReadonlyFieldCodeFixProvider.cs +++ b/src/CSharp/CodeCracker/Usage/ReadonlyFieldCodeFixProvider.cs @@ -44,10 +44,13 @@ private async static Task MakeFieldReadonlyAsync(Document document, Di private static SyntaxNode MakeSingleFieldReadonly(SyntaxNode root, FieldDeclarationSyntax fieldDeclaration) { - var newFieldDeclaration = fieldDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)) - .WithTrailingTrivia(fieldDeclaration.GetTrailingTrivia()) - .WithLeadingTrivia(fieldDeclaration.GetLeadingTrivia()) - .WithAdditionalAnnotations(Formatter.Annotation); + + var newFieldDeclaration = fieldDeclaration + .WithoutLeadingTrivia() + .AddModifiers(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)) + .WithTrailingTrivia(fieldDeclaration.GetTrailingTrivia()) + .WithLeadingTrivia(fieldDeclaration.GetLeadingTrivia()) + .WithAdditionalAnnotations(Formatter.Annotation); var newRoot = root.ReplaceNode(fieldDeclaration, newFieldDeclaration); return newRoot; } @@ -57,7 +60,7 @@ private static SyntaxNode MakeMultipleFieldsReadonly(SyntaxNode root, FieldDecla var newFieldDeclaration = fieldDeclaration.WithDeclaration(newDeclaration); var newReadonlyFieldDeclaration = fieldDeclaration.WithDeclaration(SyntaxFactory.VariableDeclaration(fieldDeclaration.Declaration.Type, SyntaxFactory.SeparatedList(new[] { variableToMakeReadonly }))) .WithoutLeadingTrivia() - .WithTrailingTrivia(SyntaxFactory.ParseTrailingTrivia("\n")) + .WithTrailingTrivia(SyntaxFactory.ParseTrailingTrivia("\r\n")) .AddModifiers(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)) .WithAdditionalAnnotations(Formatter.Annotation); var newRoot = root.ReplaceNode(fieldDeclaration, new[] { newFieldDeclaration, newReadonlyFieldDeclaration }); diff --git a/test/CSharp/CodeCracker.Test/Usage/ReadonlyFieldTests.cs b/test/CSharp/CodeCracker.Test/Usage/ReadonlyFieldTests.cs index 8639541c5..3375774f4 100644 --- a/test/CSharp/CodeCracker.Test/Usage/ReadonlyFieldTests.cs +++ b/test/CSharp/CodeCracker.Test/Usage/ReadonlyFieldTests.cs @@ -433,6 +433,77 @@ class TypeName await VerifyCSharpFixAsync(source, fixtest); } + + [Fact] + public async Task DoesNotDuplicateLeadingDirectivesOnFixSingle() + { + const string source = @" + namespace ConsoleApplication1 + { + public class Foo + { + #region standard + Foo foo1 = new Foo(); + #endregion + + #region withAccessModifier + private Foo foo2 = new Foo(); + #endregion + + #region withStatic + private static Foo foo3 = new Foo(); + #endregion + } + }"; + const string fixtest = @" + namespace ConsoleApplication1 + { + public class Foo + { + #region standard + readonly Foo foo1 = new Foo(); + #endregion + + #region withAccessModifier + private readonly Foo foo2 = new Foo(); + #endregion + + #region withStatic + private static readonly Foo foo3 = new Foo(); + #endregion + } + }"; + await VerifyCSharpFixAsync(source, fixtest); + } + + + [Fact] + public async Task DoesNotDuplicateLeadingDirectivesOnFixAll() + { + const string source = @" + namespace ConsoleApplication1 + { + public class Foo + { + #region standard + Foo foo1, foo2 = new Foo(), foo3; + #endregion + } + }"; + const string fixtest = @" + namespace ConsoleApplication1 + { + public class Foo + { + #region standard + Foo foo1, foo3; + readonly Foo foo2 = new Foo(); + #endregion + } + }"; + await VerifyCSharpFixAsync(source, fixtest); + } + [Fact] public async Task FieldsWithAssignmentOnDeclarationWithSingleDeclarationCreatesDiagnostic() { From 72326900e6d562a1c64b77db5fad5e94329fa34a Mon Sep 17 00:00:00 2001 From: Cory Johnson Date: Sat, 5 Oct 2019 10:27:59 -0500 Subject: [PATCH 3/3] Replaced the hard-coded '\n' with the user's platform eol-of-choice. --- .../Usage/ReadonlyFieldCodeFixProvider.cs | 3 ++- .../CodeCracker.Test/Usage/ReadonlyFieldTests.cs | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/CSharp/CodeCracker/Usage/ReadonlyFieldCodeFixProvider.cs b/src/CSharp/CodeCracker/Usage/ReadonlyFieldCodeFixProvider.cs index 445fd87ca..b54cb0463 100644 --- a/src/CSharp/CodeCracker/Usage/ReadonlyFieldCodeFixProvider.cs +++ b/src/CSharp/CodeCracker/Usage/ReadonlyFieldCodeFixProvider.cs @@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; +using System; using System.Collections.Immutable; using System.Composition; using System.Linq; @@ -60,8 +61,8 @@ private static SyntaxNode MakeMultipleFieldsReadonly(SyntaxNode root, FieldDecla var newFieldDeclaration = fieldDeclaration.WithDeclaration(newDeclaration); var newReadonlyFieldDeclaration = fieldDeclaration.WithDeclaration(SyntaxFactory.VariableDeclaration(fieldDeclaration.Declaration.Type, SyntaxFactory.SeparatedList(new[] { variableToMakeReadonly }))) .WithoutLeadingTrivia() - .WithTrailingTrivia(SyntaxFactory.ParseTrailingTrivia("\r\n")) .AddModifiers(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)) + .WithTrailingTrivia(SyntaxFactory.ParseTrailingTrivia(Environment.NewLine)) .WithAdditionalAnnotations(Formatter.Annotation); var newRoot = root.ReplaceNode(fieldDeclaration, new[] { newFieldDeclaration, newReadonlyFieldDeclaration }); return newRoot; diff --git a/test/CSharp/CodeCracker.Test/Usage/ReadonlyFieldTests.cs b/test/CSharp/CodeCracker.Test/Usage/ReadonlyFieldTests.cs index 3375774f4..ed92be9ee 100644 --- a/test/CSharp/CodeCracker.Test/Usage/ReadonlyFieldTests.cs +++ b/test/CSharp/CodeCracker.Test/Usage/ReadonlyFieldTests.cs @@ -483,12 +483,12 @@ public async Task DoesNotDuplicateLeadingDirectivesOnFixAll() const string source = @" namespace ConsoleApplication1 { - public class Foo - { - #region standard - Foo foo1, foo2 = new Foo(), foo3; + public class Foo + { + #region standard + Foo foo1, foo2 = new Foo(), foo3; #endregion - } + } }"; const string fixtest = @" namespace ConsoleApplication1 @@ -502,7 +502,7 @@ public class Foo } }"; await VerifyCSharpFixAsync(source, fixtest); - } + } [Fact] public async Task FieldsWithAssignmentOnDeclarationWithSingleDeclarationCreatesDiagnostic()