@@ -70,16 +70,16 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
7070 switch ( mode )
7171 {
7272 case UseProperAssertMethodsAnalyzer . CodeFixModeSimple :
73- createChangedDocument = ct => FixAssertMethodForSimpleModeAsync ( context . Document , diagnostic . AdditionalLocations [ 0 ] , diagnostic . AdditionalLocations [ 1 ] , root , simpleNameSyntax , properAssertMethodName , ct ) ;
73+ createChangedDocument = ct => FixAssertMethodForSimpleModeAsync ( context . Document , diagnostic . AdditionalLocations , root , simpleNameSyntax , properAssertMethodName , ct ) ;
7474 break ;
7575 case UseProperAssertMethodsAnalyzer . CodeFixModeAddArgument :
7676 createChangedDocument = ct => FixAssertMethodForAddArgumentModeAsync ( context . Document , diagnostic . AdditionalLocations [ 0 ] , diagnostic . AdditionalLocations [ 1 ] , diagnostic . AdditionalLocations [ 2 ] , root , simpleNameSyntax , properAssertMethodName , ct ) ;
7777 break ;
7878 case UseProperAssertMethodsAnalyzer . CodeFixModeRemoveArgument :
7979 createChangedDocument = ct => FixAssertMethodForRemoveArgumentModeAsync ( context . Document , diagnostic . AdditionalLocations , root , simpleNameSyntax , properAssertMethodName , diagnostic . Properties . ContainsKey ( UseProperAssertMethodsAnalyzer . NeedsNullableBooleanCastKey ) , ct ) ;
8080 break ;
81- case UseProperAssertMethodsAnalyzer . CodeFixModeCollectionCount :
82- createChangedDocument = ct => FixAssertMethodForCollectionCountModeAsync ( context . Document , diagnostic . AdditionalLocations , root , simpleNameSyntax , properAssertMethodName , ct ) ;
81+ case UseProperAssertMethodsAnalyzer . CodeFixModeRemoveArgumentAndReplaceArgument :
82+ createChangedDocument = ct => FixAssertMethodForRemoveArgumentAndReplaceArgumentModeAsync ( context . Document , diagnostic . AdditionalLocations , root , simpleNameSyntax , properAssertMethodName , ct ) ;
8383 break ;
8484 default :
8585 break ;
@@ -96,25 +96,29 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
9696 }
9797 }
9898
99- private static async Task < Document > FixAssertMethodForSimpleModeAsync ( Document document , Location conditionLocationToBeReplaced , Location replacementExpressionLocation , SyntaxNode root , SimpleNameSyntax simpleNameSyntax , string properAssertMethodName , CancellationToken cancellationToken )
99+ private static async Task < Document > FixAssertMethodForSimpleModeAsync ( Document document , IReadOnlyList < Location > additionalLocations , SyntaxNode root , SimpleNameSyntax simpleNameSyntax , string properAssertMethodName , CancellationToken cancellationToken )
100100 {
101- // This doesn't properly handle cases like Assert.IsTrue(message: "My message", condition: x == null)
102- // The proper handling of this may be Assert.IsNull(message: "My message", value: x)
103- // Or: Assert.IsNull(x, "My message")
104- // For now this is not handled.
105- if ( root . FindNode ( conditionLocationToBeReplaced . SourceSpan ) is not ArgumentSyntax conditionNodeToBeReplaced )
106- {
107- return document ;
108- }
101+ DocumentEditor editor = await DocumentEditor . CreateAsync ( document , cancellationToken ) . ConfigureAwait ( false ) ;
102+ FixInvocationMethodName ( editor , simpleNameSyntax , properAssertMethodName ) ;
109103
110- if ( root . FindNode ( replacementExpressionLocation . SourceSpan ) is not ExpressionSyntax replacementExpressionNode )
104+ for ( int i = 0 ; i < additionalLocations . Count ; i += 2 )
111105 {
112- return document ;
113- }
106+ // This doesn't properly handle cases like Assert.IsTrue(message: "My message", condition: x == null)
107+ // The proper handling of this may be Assert.IsNull(message: "My message", value: x)
108+ // Or: Assert.IsNull(x, "My message")
109+ // For now this is not handled.
110+ if ( root . FindNode ( additionalLocations [ i ] . SourceSpan ) is not ArgumentSyntax conditionNodeToBeReplaced )
111+ {
112+ return document ;
113+ }
114114
115- DocumentEditor editor = await DocumentEditor . CreateAsync ( document , cancellationToken ) . ConfigureAwait ( false ) ;
116- FixInvocationMethodName ( editor , simpleNameSyntax , properAssertMethodName ) ;
117- editor . ReplaceNode ( conditionNodeToBeReplaced , SyntaxFactory . Argument ( replacementExpressionNode ) . WithAdditionalAnnotations ( Formatter . Annotation ) ) ;
115+ if ( root . FindNode ( additionalLocations [ i + 1 ] . SourceSpan , getInnermostNodeForTie : true ) is not ExpressionSyntax replacementExpressionNode )
116+ {
117+ return document ;
118+ }
119+
120+ editor . ReplaceNode ( conditionNodeToBeReplaced , SyntaxFactory . Argument ( replacementExpressionNode ) . WithAdditionalAnnotations ( Formatter . Annotation ) ) ;
121+ }
118122
119123 return editor . GetChangedDocument ( ) ;
120124 }
@@ -213,7 +217,7 @@ private static async Task<Document> FixAssertMethodForRemoveArgumentModeAsync(
213217 return editor . GetChangedDocument ( ) ;
214218 }
215219
216- private static async Task < Document > FixAssertMethodForCollectionCountModeAsync (
220+ private static async Task < Document > FixAssertMethodForRemoveArgumentAndReplaceArgumentModeAsync (
217221 Document document ,
218222 IReadOnlyList < Location > additionalLocations ,
219223 SyntaxNode root ,
@@ -223,61 +227,29 @@ private static async Task<Document> FixAssertMethodForCollectionCountModeAsync(
223227 {
224228 // Handle collection count transformations:
225229 // Assert.AreEqual(0, list.Count) -> Assert.IsEmpty(list)
226- // Assert.AreEqual(3, list.Count) -> Assert.HasCount(3, list)
227230 // Assert.AreEqual(list.Count, 0) -> Assert.IsEmpty(list)
228- // Assert.AreEqual(list.Count, 3) -> Assert.HasCount(3, list)
229- if ( root . FindNode ( additionalLocations [ 0 ] . SourceSpan ) is not ArgumentSyntax firstArgument ||
230- root . FindNode ( additionalLocations [ 1 ] . SourceSpan ) is not ArgumentSyntax ||
231- firstArgument . Parent is not ArgumentListSyntax argumentList )
231+ if ( root . FindNode ( additionalLocations [ 0 ] . SourceSpan ) is not ArgumentSyntax expectedArgumentToRemove )
232232 {
233233 return document ;
234234 }
235235
236- if ( root . FindNode ( additionalLocations [ 2 ] . SourceSpan ) is not ExpressionSyntax collectionExpression )
236+ if ( root . FindNode ( additionalLocations [ 1 ] . SourceSpan ) is not ArgumentSyntax argumentToBeReplaced ||
237+ root . FindNode ( additionalLocations [ 2 ] . SourceSpan ) is not ExpressionSyntax replacement )
237238 {
238239 return document ;
239240 }
240241
241- DocumentEditor editor = await DocumentEditor . CreateAsync ( document , cancellationToken ) . ConfigureAwait ( false ) ;
242- FixInvocationMethodName ( editor , simpleNameSyntax , properAssertMethodName ) ;
243-
244- // Preserve any additional arguments beyond the first two (expected/actual)
245- var additionalArguments = argumentList . Arguments . Skip ( 2 ) . ToList ( ) ;
246-
247- ArgumentListSyntax newArgumentList ;
248-
249- if ( properAssertMethodName == "IsEmpty" )
250- {
251- // For IsEmpty, we just need the collection argument plus any additional arguments
252- var newArguments = new List < ArgumentSyntax >
253- {
254- SyntaxFactory . Argument ( collectionExpression ) . WithAdditionalAnnotations ( Formatter . Annotation ) ,
255- } ;
256- newArguments . AddRange ( additionalArguments ) ;
257- newArgumentList = argumentList . WithArguments ( SyntaxFactory . SeparatedList ( newArguments ) ) ;
258- }
259- else // HasCount
242+ if ( expectedArgumentToRemove . Parent is not ArgumentListSyntax argumentList )
260243 {
261- // For HasCount, we need count and collection arguments plus any additional arguments
262- // additionalLocations[3] should contain the count expression
263- if ( additionalLocations . Count > 3 &&
264- root . FindNode ( additionalLocations [ 3 ] . SourceSpan ) is ArgumentSyntax countArgument )
265- {
266- var newArguments = new List < ArgumentSyntax >
267- {
268- SyntaxFactory . Argument ( countArgument . Expression ) . WithAdditionalAnnotations ( Formatter . Annotation ) ,
269- SyntaxFactory . Argument ( collectionExpression ) . WithAdditionalAnnotations ( Formatter . Annotation ) ,
270- } ;
271- newArguments . AddRange ( additionalArguments ) ;
272- newArgumentList = argumentList . WithArguments ( SyntaxFactory . SeparatedList ( newArguments ) ) ;
273- }
274- else
275- {
276- // Fallback: something went wrong, don't apply the fix
277- return document ;
278- }
244+ return document ;
279245 }
280246
247+ DocumentEditor editor = await DocumentEditor . CreateAsync ( document , cancellationToken ) . ConfigureAwait ( false ) ;
248+ FixInvocationMethodName ( editor , simpleNameSyntax , properAssertMethodName ) ;
249+
250+ int argumentIndexToRemove = argumentList . Arguments . IndexOf ( expectedArgumentToRemove ) ;
251+ ArgumentListSyntax newArgumentList = argumentList . ReplaceNode ( argumentToBeReplaced , argumentToBeReplaced . WithExpression ( replacement ) ) ;
252+ newArgumentList = newArgumentList . WithArguments ( newArgumentList . Arguments . RemoveAt ( argumentIndexToRemove ) ) ;
281253 editor . ReplaceNode ( argumentList , newArgumentList ) ;
282254
283255 return editor . GetChangedDocument ( ) ;
0 commit comments