@@ -418,17 +418,40 @@ private static async Task<Document> AddCancellationTokenAsync(Document document,
418418 newRoot = newRoot . AddUsings ( usingDirective ) ;
419419 }
420420
421+ var parameters = methodDecl . ParameterList . Parameters ;
422+
421423 var ctType = SyntaxFactory . IdentifierName ( "CancellationToken" ) ;
422424 var ctParam = SyntaxFactory . Parameter ( SyntaxFactory . Identifier ( "ct" ) )
423- . WithType ( ctType ) ;
425+ . WithType ( ctType )
426+ . WithDefault (
427+ SyntaxFactory . EqualsValueClause (
428+ SyntaxFactory . LiteralExpression ( SyntaxKind . DefaultLiteralExpression ) ) ) ;
429+
430+ // Insert CT before any params parameter; otherwise at the end.
431+ var insertIndex = parameters . Count ;
432+ for ( var i = 0 ; i < parameters . Count ; i ++ )
433+ {
434+ if ( ! parameters [ i ]
435+ . Modifiers
436+ . Any ( SyntaxKind . ParamsKeyword ) )
437+ {
438+ continue ;
439+ }
440+
441+ insertIndex = i ;
442+ break ;
443+ }
424444
425- var newParamList = methodDecl . ParameterList . AddParameters ( ctParam ) ;
426- var newMethod = methodDecl . WithParameterList ( newParamList ) ;
445+ var newParameters = parameters . Insert ( insertIndex , ctParam ) ;
446+
447+ var newMethod = methodDecl . WithParameterList (
448+ methodDecl . ParameterList . WithParameters ( newParameters ) ) ;
427449
428450 newRoot = newRoot . ReplaceNode ( methodDecl , newMethod ) ;
429451 return document . WithSyntaxRoot ( newRoot ) ;
430452 }
431453
454+
432455 private static bool HasSystemThreadingUsing ( CompilationUnitSyntax root )
433456 {
434457 foreach ( var u in root . Usings )
@@ -468,14 +491,61 @@ private static async Task<Document> MoveCancellationTokenToLastAsync(Document do
468491 break ;
469492 }
470493
471- if ( ctIndex < 0 || ctIndex == parameters . Count - 1 )
494+ if ( ctIndex < 0 )
495+ {
496+ return document ;
497+ }
498+
499+ // Find params parameter (if any)
500+ var paramsIndex = - 1 ;
501+ for ( var i = 0 ; i < parameters . Count ; i ++ )
502+ {
503+ if ( ! parameters [ i ]
504+ . Modifiers
505+ . Any ( SyntaxKind . ParamsKeyword ) )
506+ {
507+ continue ;
508+ }
509+
510+ paramsIndex = i ;
511+ break ;
512+ }
513+
514+ // Already in the correct spot?
515+ var alreadyCorrect =
516+ ( paramsIndex < 0 && ctIndex == parameters . Count - 1 ) ||
517+ ( paramsIndex >= 0 && ctIndex == paramsIndex - 1 ) ;
518+
519+ if ( alreadyCorrect )
472520 {
473521 return document ;
474522 }
475523
476524 var ctSyntax = parameters [ ctIndex ] ;
477- var newParameters = parameters . RemoveAt ( ctIndex )
478- . Add ( ctSyntax ) ;
525+ var withoutCt = parameters . RemoveAt ( ctIndex ) ;
526+
527+ // Recompute params index after removal
528+ var newParamsIndex = - 1 ;
529+ for ( var i = 0 ; i < withoutCt . Count ; i ++ )
530+ {
531+ if ( ! withoutCt [ i ]
532+ . Modifiers
533+ . Any ( SyntaxKind . ParamsKeyword ) )
534+ {
535+ continue ;
536+ }
537+
538+ newParamsIndex = i ;
539+ break ;
540+ }
541+
542+ // Insert CT just before params
543+ var newParameters =
544+ newParamsIndex >= 0
545+ ? withoutCt . Insert ( newParamsIndex , ctSyntax )
546+ :
547+ // No params -> CT becomes last
548+ withoutCt . Add ( ctSyntax ) ;
479549
480550 var newMethod = methodDecl . WithParameterList (
481551 methodDecl . ParameterList . WithParameters ( newParameters ) ) ;
@@ -491,6 +561,7 @@ private static async Task<Document> MoveCancellationTokenToLastAsync(Document do
491561 return document . WithSyntaxRoot ( newRoot ) ;
492562 }
493563
564+
494565 // ----------------------------------------------------------------------
495566 // Syntax-level helpers – lambdas / anonymous functions
496567 // ----------------------------------------------------------------------
@@ -659,20 +730,34 @@ private static async Task<Solution> AddCancellationTokenForInterfaceAndImplement
659730 }
660731 }
661732
662- // Build parameter differently for interface vs implementation
663- var ctParam = SyntaxFactory . Parameter ( SyntaxFactory . Identifier ( "ct" ) )
664- . WithType ( ctType ) ;
733+ var parameters = original . ParameterList . Parameters ;
665734
666- if ( original . Parent is InterfaceDeclarationSyntax )
735+ // Always: CancellationToken ct = default
736+ var ctParam = SyntaxFactory . Parameter ( SyntaxFactory . Identifier ( "ct" ) )
737+ . WithType ( ctType )
738+ . WithDefault (
739+ SyntaxFactory . EqualsValueClause (
740+ SyntaxFactory . LiteralExpression ( SyntaxKind . DefaultLiteralExpression ) ) ) ;
741+
742+ // Insert CT before any params parameter; otherwise at the end.
743+ var insertIndex = parameters . Count ;
744+ for ( var i = 0 ; i < parameters . Count ; i ++ )
667745 {
668- // interface: CancellationToken ct = default
669- ctParam = ctParam . WithDefault (
670- SyntaxFactory . EqualsValueClause (
671- SyntaxFactory . LiteralExpression ( SyntaxKind . DefaultLiteralExpression ) ) ) ;
746+ if ( ! parameters [ i ]
747+ . Modifiers
748+ . Any ( SyntaxKind . ParamsKeyword ) )
749+ {
750+ continue ;
751+ }
752+
753+ insertIndex = i ;
754+ break ;
672755 }
673756
674- var newParamList = original . ParameterList . AddParameters ( ctParam ) ;
675- return original . WithParameterList ( newParamList ) ;
757+ var newParameters = parameters . Insert ( insertIndex , ctParam ) ;
758+
759+ return original . WithParameterList (
760+ original . ParameterList . WithParameters ( newParameters ) ) ;
676761 } ) ;
677762
678763 solution = solution . WithDocumentSyntaxRoot ( docId , updatedRoot ) ;
@@ -705,21 +790,9 @@ private static async Task<Solution> MoveCancellationTokenToLastForInterfaceAndIm
705790 }
706791
707792 var methodsByDocument = new Dictionary < DocumentId , ImmutableArray < MethodDeclarationSyntax > . Builder > ( ) ;
708- var ctIndexBySyntax = new Dictionary < MethodDeclarationSyntax , int > ( ) ;
709793
710794 foreach ( var method in allMethods )
711795 {
712- var ctParam = method . Parameters . FirstOrDefault ( p => IsCancellationToken ( p . Type ) ) ;
713- if ( ctParam is null )
714- {
715- continue ;
716- }
717-
718- if ( ctParam . Ordinal == method . Parameters . Length - 1 )
719- {
720- continue ;
721- }
722-
723796 foreach ( var syntaxRef in method . DeclaringSyntaxReferences )
724797 {
725798 var syntax = await syntaxRef . GetSyntaxAsync ( cancellationToken )
@@ -744,7 +817,6 @@ private static async Task<Solution> MoveCancellationTokenToLastForInterfaceAndIm
744817 }
745818
746819 list . Add ( methodDecl ) ;
747- ctIndexBySyntax [ methodDecl ] = ctParam . Ordinal ;
748820 }
749821 }
750822
@@ -770,20 +842,69 @@ private static async Task<Solution> MoveCancellationTokenToLastForInterfaceAndIm
770842 methodDecls ,
771843 ( original , _ ) =>
772844 {
773- if ( ! ctIndexBySyntax . TryGetValue ( original , out var ctIndex ) )
845+ var parameters = original . ParameterList . Parameters ;
846+
847+ // Find CT parameter syntax
848+ var ctIndex = - 1 ;
849+ for ( var i = 0 ; i < parameters . Count ; i ++ )
850+ {
851+ var p = parameters [ i ] ;
852+ if ( p . Type is IdentifierNameSyntax id &&
853+ string . Equals ( id . Identifier . Text , "CancellationToken" , StringComparison . Ordinal ) )
854+ {
855+ ctIndex = i ;
856+ break ;
857+ }
858+ }
859+
860+ if ( ctIndex < 0 )
774861 {
775862 return original ;
776863 }
777864
778- var parameters = original . ParameterList . Parameters ;
779- if ( ctIndex < 0 || ctIndex >= parameters . Count || ctIndex == parameters . Count - 1 )
865+ // Find params parameter
866+ var paramsIndex = - 1 ;
867+ for ( var i = 0 ; i < parameters . Count ; i ++ )
868+ {
869+ if ( parameters [ i ]
870+ . Modifiers
871+ . Any ( SyntaxKind . ParamsKeyword ) )
872+ {
873+ paramsIndex = i ;
874+ break ;
875+ }
876+ }
877+
878+ var alreadyCorrect =
879+ ( paramsIndex < 0 && ctIndex == parameters . Count - 1 ) ||
880+ ( paramsIndex >= 0 && ctIndex == paramsIndex - 1 ) ;
881+
882+ if ( alreadyCorrect )
780883 {
781884 return original ;
782885 }
783886
784887 var ctSyntax = parameters [ ctIndex ] ;
785- var newParameters = parameters . RemoveAt ( ctIndex )
786- . Add ( ctSyntax ) ;
888+ var withoutCt = parameters . RemoveAt ( ctIndex ) ;
889+
890+ // Recompute params index after removal
891+ var newParamsIndex = - 1 ;
892+ for ( var i = 0 ; i < withoutCt . Count ; i ++ )
893+ {
894+ if ( ! withoutCt [ i ]
895+ . Modifiers
896+ . Any ( SyntaxKind . ParamsKeyword ) )
897+ {
898+ continue ;
899+ }
900+
901+ newParamsIndex = i ;
902+ break ;
903+ }
904+
905+ var newParameters = newParamsIndex >= 0
906+ ? withoutCt . Insert ( newParamsIndex , ctSyntax )
907+ : withoutCt . Add ( ctSyntax ) ;
787908
788909 return original . WithParameterList (
789910 original . ParameterList . WithParameters ( newParameters ) ) ;
0 commit comments