@@ -431,82 +431,50 @@ private CreateChangedDocument TryComputeFixForNunitThat(IInvocationOperation inv
431431 var constraint = invocation . Arguments [ 1 ] . Value . UnwrapConversion ( ) ;
432432 var subject = invocation . Arguments [ 0 ] . Value ;
433433
434- if ( MatchesProperties ( t . Is , "True" ) // Assert.That(subject, Is.True)
435- || MatchesProperties ( t . Is , "Not" , "False" ) ) // Assert.That(subject, Is.Not.False)
436- return RenameAssertThatAssertionToShould ( "BeTrue" ) ;
437- else if ( MatchesProperties ( t . Is , "False" ) // Assert.That(subject, Is.False)
438- || MatchesProperties ( t . Is , "Not" , "True" ) ) // Assert.That(subject, Is.Not.True)
439- return RenameAssertThatAssertionToShould ( "BeFalse" ) ;
440- else if ( MatchesProperties ( t . Is , "Null" ) ) // Assert.That(subject, Is.Null)
441- return RenameAssertThatAssertionToShould ( "BeNull" ) ;
442- else if ( MatchesProperties ( t . Is , "Not" , "Null" ) ) // Assert.That(subject, Is.Not.Null)
443- return RenameAssertThatAssertionToShould ( "NotBeNull" ) ;
434+ var rewriter = new AssertThatRewriter ( invocation , context , constraint ) ;
435+ var matcher = new AssertThatMatcher ( constraint , t ) ;
436+
437+ if ( matcher . Is ( "True" ) // Assert.That(subject, Is.True)
438+ || matcher . Is ( "Not" , "False" ) ) // Assert.That(subject, Is.Not.False)
439+ return rewriter . Should ( "BeTrue" ) ;
440+ else if ( matcher . Is ( "False" ) // Assert.That(subject, Is.False)
441+ || matcher . Is ( "Not" , "True" ) ) // Assert.That(subject, Is.Not.True)
442+ return rewriter . Should ( "BeFalse" ) ;
443+ else if ( matcher . Is ( "Null" ) ) // Assert.That(subject, Is.Null)
444+ return rewriter . Should ( "BeNull" ) ;
445+ else if ( matcher . Is ( "Not" , "Null" ) ) // Assert.That(subject, Is.Not.Null)
446+ return rewriter . Should ( "NotBeNull" ) ;
444447 else if ( ! IsArgumentTypeOfNonGenericEnumerable ( invocation , argumentIndex : 0 ) )
445448 {
446- if ( MatchesProperties ( t . Is , "Empty" ) // Assert.That(subject, Is.Empty)
447- || MatchesProperties ( t . Has , "Count" , "Zero" ) ) // Assert.That(subject, Has.Count.Zero)
448- return RenameAssertThatAssertionToShould ( "BeEmpty" ) ;
449- else if ( MatchesProperties ( t . Is , "Not" , "Empty" ) // Assert.That(subject, Is.Not.Empty)
450- || MatchesProperties ( t . Has , "Count" , "Not" , "Zero" ) ) // Assert.That(subject, Has.Not.Zero)
451- return RenameAssertThatAssertionToShould ( "NotBeEmpty" ) ;
452- else if ( MatchesMethod ( t . Has , [ Property ( "Count" ) ] , Method ( "EqualTo" ) , out var argument ) )
449+ if ( matcher . Is ( "Empty" ) // Assert.That(subject, Is.Empty)
450+ || matcher . Has ( "Count" , "Zero" ) ) // Assert.That(subject, Has.Count.Zero)
451+ return rewriter . Should ( "BeEmpty" ) ;
452+ else if ( matcher . Is ( "Not" , "Empty" ) // Assert.That(subject, Is.Not.Empty)
453+ || matcher . Has ( "Count" , "Not" , "Zero" ) ) // Assert.That(subject, Has.Not.Zero)
454+ return rewriter . Should ( "NotBeEmpty" ) ;
455+ else if ( matcher . Has ( [ Property ( "Count" ) ] , Method ( "EqualTo" ) , out var argument ) )
453456 {
454457 if ( argument . IsLiteralValue ( 0 ) )
455- return RenameAssertThatAssertionToShould ( "BeEmpty" ) ;
458+ return rewriter . Should ( "BeEmpty" ) ;
456459 else if ( argument . IsLiteralValue ( 1 ) )
457- return RenameAssertThatAssertionToShould ( "ContainSingle" ) ;
460+ return rewriter . Should ( "ContainSingle" ) ;
458461 else
459- return RenameAssertThatToShouldWithArgument ( "HaveCount" , generator => argument . Syntax ) ;
462+ return rewriter . Should ( "HaveCount" , argument ) ;
460463 }
461- else if ( MatchesMethod ( t . Has , [ Property ( "Count" ) ] , Method ( "GreaterThan" ) , out argument ) && argument . IsLiteralValue ( 0 ) )
462- return RenameAssertThatAssertionToShould ( "NotBeEmpty" ) ;
463- }
464- if ( MatchesProperties ( t . Is , "Zero" ) )
465- return RenameAssertThatToShouldWithArgument ( "Be" , generator => generator . LiteralExpression ( 0 ) ) ;
466- else if ( MatchesProperties ( t . Is , "Not" , "Zero" ) )
467- return RenameAssertThatToShouldWithArgument ( "NotBe" , generator => generator . LiteralExpression ( 0 ) ) ;
468-
469- return null ;
470-
471- CreateChangedDocument RenameAssertThatAssertionToShould ( string assertionName )
472- => DocumentEditorUtils . RenameMethodToSubjectShouldAssertion ( invocation , context , assertionName , subjectIndex : 0 , argumentsToRemove : [ 1 ] ) ;
473- CreateChangedDocument RenameAssertThatToShouldWithArgument ( string assertionName , Func < SyntaxGenerator , SyntaxNode > argumentGenerator )
474- {
475- return DocumentEditorUtils . RewriteExpression ( invocation , [
476- EditAction . ReplaceAssertionArgument ( index : 1 , argumentGenerator ) ,
477- EditAction . SubjectShouldAssertion ( argumentIndex : 0 , assertionName ) ,
478- ] , context ) ;
479- }
480-
481- bool MatchesProperties ( INamedTypeSymbol type , params string [ ] matchers )
482- => Matches ( type , Array . ConvertAll ( matchers , matcher => new PropertyReferenceMatcher ( matcher ) ) ) ;
483- bool MatchesMethod ( INamedTypeSymbol type , IOperationMatcher [ ] matchers , MethodInvocationMatcher methodMatcher , out IArgumentOperation argument )
484- {
485- argument = null ;
486- var result = Matches ( type , [ .. matchers , methodMatcher ] ) ;
487- if ( result ) argument = methodMatcher . Argument ;
488- return result ;
489- }
490- bool Matches ( INamedTypeSymbol type , params IOperationMatcher [ ] matchers )
491- {
492- IOperation currentOp = constraint ;
493- for ( var i = matchers . Length - 1 ; i >= 0 ; i -- )
464+ else if ( matcher . Has ( [ Property ( "Count" ) ] , Method ( "GreaterThan" ) , out argument ) )
494465 {
495- var ( nextOp , containingType ) = matchers [ i ] . TryGetNext ( currentOp ) ;
496- if ( containingType is null ) return false ;
497-
498- if ( i is 0 )
499- {
500- return containingType . EqualsSymbol ( type ) ;
501- }
502-
503- if ( nextOp is null ) return false ;
504-
505- currentOp = nextOp ;
466+ if ( argument . IsLiteralValue ( 0 ) )
467+ return rewriter . Should ( "NotBeEmpty" ) ;
468+ else
469+ return rewriter . Should ( "HaveCountGreaterThan" , argument ) ;
506470 }
507-
508- return false ;
509471 }
472+ if ( matcher . Is ( "Zero" ) )
473+ return rewriter . Should ( "Be" , g => g . LiteralExpression ( 0 ) ) ;
474+ else if ( matcher . Is ( "Not" , "Zero" ) )
475+ return rewriter . Should ( "NotBe" , g => g . LiteralExpression ( 0 ) ) ;
476+
477+ return null ;
510478 }
511479
512480 private CreateChangedDocument RewriteContainsAssertion ( IInvocationOperation invocation , CodeFixContext context , string assertion , IArgumentOperation subject , IArgumentOperation expectation )
@@ -576,4 +544,57 @@ public class NunitCodeFixContext(Compilation compilation) : TestingFrameworkCode
576544 public INamedTypeSymbol ConstraintExpression { get ; } = compilation . GetTypeByMetadataName ( "NUnit.Framework.Constraints.ConstraintExpression" ) ;
577545 public INamedTypeSymbol NUnitString { get ; } = compilation . GetTypeByMetadataName ( "NUnit.Framework.NUnitString" ) ;
578546 }
547+
548+ private class AssertThatMatcher ( IOperation constraint , NunitCodeFixContext t )
549+ {
550+ public bool Is ( params string [ ] matchers ) => Matches ( t . Is , matchers ) ;
551+ public bool Has ( params string [ ] matchers ) => Matches ( t . Has , matchers ) ;
552+ public bool Has ( IOperationMatcher [ ] matchers , MethodInvocationMatcher methodMatcher , out IArgumentOperation argument ) => Matches ( t . Has , matchers , methodMatcher , out argument ) ;
553+
554+ public bool Matches ( INamedTypeSymbol type , params string [ ] matchers )
555+ => Matches ( type , Array . ConvertAll ( matchers , matcher => new PropertyReferenceMatcher ( matcher ) ) ) ;
556+ public bool Matches ( INamedTypeSymbol type , IOperationMatcher [ ] matchers , MethodInvocationMatcher methodMatcher , out IArgumentOperation argument )
557+ {
558+ argument = null ;
559+ var result = Matches ( type , [ .. matchers , methodMatcher ] ) ;
560+ if ( result ) argument = methodMatcher . Argument ;
561+ return result ;
562+ }
563+ public bool Matches ( INamedTypeSymbol type , params IOperationMatcher [ ] matchers )
564+ {
565+ IOperation currentOp = constraint ;
566+ for ( var i = matchers . Length - 1 ; i >= 0 ; i -- )
567+ {
568+ var ( nextOp , containingType ) = matchers [ i ] . TryGetNext ( currentOp ) ;
569+ if ( containingType is null ) return false ;
570+
571+ if ( i is 0 )
572+ {
573+ return containingType . EqualsSymbol ( type ) ;
574+ }
575+
576+ if ( nextOp is null ) return false ;
577+
578+ currentOp = nextOp ;
579+ }
580+
581+ return false ;
582+ }
583+ }
584+ private class AssertThatRewriter ( IInvocationOperation invocation , CodeFixContext context , IOperation constraint )
585+ {
586+ public CreateChangedDocument Should ( string assertion )
587+ {
588+ return DocumentEditorUtils . RenameMethodToSubjectShouldAssertion ( invocation , context , assertion , subjectIndex : 0 , argumentsToRemove : [ 1 ] ) ;
589+ }
590+
591+ public CreateChangedDocument Should ( string assertion , IArgumentOperation argument ) => Should ( assertion , _ => argument . Syntax ) ;
592+ public CreateChangedDocument Should ( string assertion , Func < SyntaxGenerator , SyntaxNode > argumentGenerator )
593+ {
594+ return DocumentEditorUtils . RewriteExpression ( invocation , [
595+ EditAction . ReplaceAssertionArgument ( index : 1 , argumentGenerator ) ,
596+ EditAction . SubjectShouldAssertion ( argumentIndex : 0 , assertion ) ,
597+ ] , context ) ;
598+ }
599+ }
579600}
0 commit comments