diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFixTests.cs index 35eb6e92..87955a74 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFixTests.cs @@ -121,5 +121,21 @@ public void TestMethod() }"); AnalyzerAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: CodeFixConstants.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixPreservesLineBreakBeforeMessage() + { + var code = TestUtility.WrapInTestMethod($@" + Assert.AreEqual(2d, 3d, 0.0000001d, + ""message"", + Guid.NewGuid());"); + + var fixedCode = TestUtility.WrapInTestMethod(@" + Assert.That(3d, Is.EqualTo(2d).Within(0.0000001d), + ""message"", + Guid.NewGuid());"); + + AnalyzerAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: CodeFixConstants.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ConstraintsUsage/EqualConstraintUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ConstraintsUsage/EqualConstraintUsageCodeFixTests.cs index 9887e7d8..f09689f4 100644 --- a/src/nunit.analyzers.tests/ConstraintsUsage/EqualConstraintUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ConstraintsUsage/EqualConstraintUsageCodeFixTests.cs @@ -240,5 +240,23 @@ public void FixesEqualsMethodWithAssertFalseWithMessage() AnalyzerAssert.CodeFix(analyzer, fix, equalConstraintDiagnostic, code, fixedCode); } + + [Test] + public void CodeFixPreservesLineBreakBeforeMessage() + { + var code = TestUtility.WrapInTestMethod(@" + var actual = ""abc""; + + Assert.False(actual.Equals(""bcd""), + ""Assertion message from new line"");"); + + var fixedCode = TestUtility.WrapInTestMethod(@" + var actual = ""abc""; + + Assert.That(actual, Is.Not.EqualTo(""bcd""), + ""Assertion message from new line"");"); + + AnalyzerAssert.CodeFix(analyzer, fix, equalConstraintDiagnostic, code, fixedCode); + } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFix.cs index 214ffb3f..59bef257 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFix.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using NUnit.Analyzers.Constants; +using NUnit.Analyzers.Extensions; namespace NUnit.Analyzers.ClassicModelAssertUsage { @@ -41,26 +42,8 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var arguments = invocationNode.ArgumentList.Arguments.ToList(); this.UpdateArguments(diagnostic, arguments); - var totalArgumentCount = (2 * arguments.Count) - 1; - var newArgumentList = new SyntaxNodeOrToken[totalArgumentCount]; - - context.CancellationToken.ThrowIfCancellationRequested(); - - for (var x = 0; x < totalArgumentCount; x++) - { - if (x % 2 == 0) - { - newArgumentList[x] = arguments[x / 2]; - } - else - { - newArgumentList[x] = SyntaxFactory.Token(SyntaxKind.CommaToken); - } - } - - newInvocationNode = newInvocationNode.WithArgumentList( - SyntaxFactory.ArgumentList( - SyntaxFactory.SeparatedList(newArgumentList))); + var newArgumentsList = invocationNode.ArgumentList.WithArguments(arguments); + newInvocationNode = newInvocationNode.WithArgumentList(newArgumentsList); context.CancellationToken.ThrowIfCancellationRequested(); diff --git a/src/nunit.analyzers/ConstraintUsage/BaseConditionConstraintCodeFix.cs b/src/nunit.analyzers/ConstraintUsage/BaseConditionConstraintCodeFix.cs index db20891e..0e0c18eb 100644 --- a/src/nunit.analyzers/ConstraintUsage/BaseConditionConstraintCodeFix.cs +++ b/src/nunit.analyzers/ConstraintUsage/BaseConditionConstraintCodeFix.cs @@ -93,7 +93,7 @@ protected static InvocationExpressionSyntax UpdateAssertNode(InvocationExpressio SyntaxFactory.Argument(constraintExpression) }.Union(remainingArguments); - var newArgumentsList = SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(newArguments)); + var newArgumentsList = assertNode.ArgumentList.WithArguments(newArguments); return assertNode .WithExpression(newExpression) diff --git a/src/nunit.analyzers/Extensions/ArgumentListSyntaxExtensions.cs b/src/nunit.analyzers/Extensions/ArgumentListSyntaxExtensions.cs new file mode 100644 index 00000000..dffbd509 --- /dev/null +++ b/src/nunit.analyzers/Extensions/ArgumentListSyntaxExtensions.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace NUnit.Analyzers.Extensions +{ + internal static class ArgumentListSyntaxExtensions + { + public static ArgumentListSyntax WithArguments( + this ArgumentListSyntax @this, + IEnumerable newArguments) + { + var originalArguments = @this.Arguments; + var originalSeparators = originalArguments.GetSeparators(); + + var nodesAndTokens = new List { newArguments.First() }; + + foreach (var newArgument in newArguments.Skip(1)) + { + // If argument is not replaced - take original separator. Otherwise - comma + var oldIndex = originalArguments.IndexOf(newArgument); + var separator = originalSeparators.ElementAtOrDefault(oldIndex - 1); + + if (separator == default(SyntaxToken)) + { + separator = SyntaxFactory.Token(SyntaxKind.CommaToken); + } + + nodesAndTokens.Add(separator); + nodesAndTokens.Add(newArgument); + } + + var newSeparatedList = SyntaxFactory.SeparatedList(nodesAndTokens); + + return @this.WithArguments(newSeparatedList); + } + } +}