@@ -350,33 +350,70 @@ public static void IsOrdered(IEnumerable collection, IComparer comparer)
350350 return null ;
351351 }
352352
353- /* TODO:
354- StringAssert:
355-
356- public static void Contains(string expected, string actual, string message, params object[] args)
357- public static void Contains(string expected, string actual)
358- public static void DoesNotContain(string expected, string actual, string message, params object[] args)
359- public static void DoesNotContain(string expected, string actual)
360- public static void StartsWith(string expected, string actual, string message, params object[] args)
361- public static void StartsWith(string expected, string actual)
362- public static void DoesNotStartWith(string expected, string actual, string message, params object[] args)
363- public static void DoesNotStartWith(string expected, string actual)
364- public static void EndsWith(string expected, string actual, string message, params object[] args)
365- public static void EndsWith(string expected, string actual)
366- public static void DoesNotEndWith(string expected, string actual, string message, params object[] args)
367- public static void DoesNotEndWith(string expected, string actual)
368- public static void AreEqualIgnoringCase(string expected, string actual, string message, params object[] args)
369- public static void AreEqualIgnoringCase(string expected, string actual)
370- public static void AreNotEqualIgnoringCase(string expected, string actual, string message, params object[] args)
371- public static void AreNotEqualIgnoringCase(string expected, string actual)
372- public static void IsMatch(string pattern, string actual, string message, params object[] args)
373- public static void IsMatch(string pattern, string actual)
374- public static void DoesNotMatch(string pattern, string actual, string message, params object[] args)
375- public static void DoesNotMatch(string pattern, string actual)
376- */
353+ /* TODO:
354+ StringAssert:
355+
356+ public static void Contains(string expected, string actual, string message, params object[] args)
357+ public static void Contains(string expected, string actual)
358+ public static void DoesNotContain(string expected, string actual, string message, params object[] args)
359+ public static void DoesNotContain(string expected, string actual)
360+ public static void StartsWith(string expected, string actual, string message, params object[] args)
361+ public static void StartsWith(string expected, string actual)
362+ public static void DoesNotStartWith(string expected, string actual, string message, params object[] args)
363+ public static void DoesNotStartWith(string expected, string actual)
364+ public static void EndsWith(string expected, string actual, string message, params object[] args)
365+ public static void EndsWith(string expected, string actual)
366+ public static void DoesNotEndWith(string expected, string actual, string message, params object[] args)
367+ public static void DoesNotEndWith(string expected, string actual)
368+ public static void AreEqualIgnoringCase(string expected, string actual, string message, params object[] args)
369+ public static void AreEqualIgnoringCase(string expected, string actual)
370+ public static void AreNotEqualIgnoringCase(string expected, string actual, string message, params object[] args)
371+ public static void AreNotEqualIgnoringCase(string expected, string actual)
372+ public static void IsMatch(string pattern, string actual, string message, params object[] args)
373+ public static void IsMatch(string pattern, string actual)
374+ public static void DoesNotMatch(string pattern, string actual, string message, params object[] args)
375+ public static void DoesNotMatch(string pattern, string actual)
376+ */
377377
378378 private CreateChangedDocument TryComputeFixForNunitThat ( IInvocationOperation invocation , CodeFixContext context , NunitCodeFixContext t )
379379 {
380+ /*
381+ public static ConstraintExpression All;
382+ public static DefaultConstraint Default;
383+ public static GreaterThanConstraint Positive;
384+ public static LessThanConstraint Negative;
385+ public static NaNConstraint NaN;
386+ public static EmptyConstraint Empty;
387+ public static UniqueItemsConstraint Unique;
388+ public static XmlSerializableConstraint XmlSerializable;
389+ public static CollectionOrderedConstraint Ordered;
390+ public static EqualConstraint EqualTo(object? expected);
391+ public static SameAsConstraint SameAs(object? expected);
392+ public static GreaterThanConstraint GreaterThan(object expected);
393+ public static GreaterThanOrEqualConstraint GreaterThanOrEqualTo(object expected);
394+ public static GreaterThanOrEqualConstraint AtLeast(object expected);
395+ public static LessThanConstraint LessThan(object expected);
396+ public static LessThanOrEqualConstraint LessThanOrEqualTo(object expected);
397+ public static LessThanOrEqualConstraint AtMost(object expected);
398+ public static ExactTypeConstraint TypeOf(Type expectedType);
399+ public static ExactTypeConstraint TypeOf<TExpected>();
400+ public static InstanceOfTypeConstraint InstanceOf(Type expectedType);
401+ public static InstanceOfTypeConstraint InstanceOf<TExpected>();
402+ public static AssignableFromConstraint AssignableFrom(Type expectedType);
403+ public static AssignableFromConstraint AssignableFrom<TExpected>();
404+ public static AssignableToConstraint AssignableTo(Type expectedType);
405+ public static AssignableToConstraint AssignableTo<TExpected>();
406+ public static CollectionEquivalentConstraint EquivalentTo(IEnumerable expected);
407+ public static CollectionSubsetConstraint SubsetOf(IEnumerable expected);
408+ public static CollectionSupersetConstraint SupersetOf(IEnumerable expected);
409+ public static SamePathConstraint SamePath(string expected);
410+ public static SubPathConstraint SubPathOf(string expected);
411+ public static SamePathOrUnderConstraint SamePathOrUnder(string expected);
412+ public static RangeConstraint InRange(object from, object to);
413+ public static AnyOfConstraint AnyOf(params object?[]? expected);
414+ public static AnyOfConstraint AnyOf(ICollection expected);
415+ */
416+
380417 // Assert.That(condition)
381418 if ( invocation . Arguments [ 0 ] . Value . Type . EqualsSymbol ( t . Boolean )
382419 && ( invocation . Arguments . Length is 1
@@ -393,29 +430,29 @@ private CreateChangedDocument TryComputeFixForNunitThat(IInvocationOperation inv
393430 if ( invocation . Arguments [ 1 ] . Value . UnwrapConversion ( ) is not IPropertyReferenceOperation constraint ) return null ;
394431 var subject = invocation . Arguments [ 0 ] . Value ;
395432
396- if ( IsPropertyOfSymbol ( constraint , "True" , t . Is ) // Assert.That(subject, Is.True)
397- || IsPropertyOfSymbol ( constraint , "Not" , "False" , t . Is ) ) // Assert.That(subject, Is.False)
433+ if ( MatchesProperties ( constraint , t . Is , "True" ) // Assert.That(subject, Is.True)
434+ || MatchesProperties ( constraint , t . Is , "Not" , "False" ) ) // Assert.That(subject, Is.Not .False)
398435 return RenameAssertThatAssertionToSubjectShouldAssertion ( "BeTrue" ) ;
399- else if ( IsPropertyOfSymbol ( constraint , "False" , t . Is ) // Assert.That(subject, Is.False)
400- || IsPropertyOfSymbol ( constraint , "Not" , "True" , t . Is ) ) // Assert.That(subject, Is.Not.True)
436+ else if ( MatchesProperties ( constraint , t . Is , "False" ) // Assert.That(subject, Is.False)
437+ || MatchesProperties ( constraint , t . Is , "Not" , "True" ) ) // Assert.That(subject, Is.Not.True)
401438 return RenameAssertThatAssertionToSubjectShouldAssertion ( "BeFalse" ) ;
402- else if ( IsPropertyOfSymbol ( constraint , "Null" , t . Is ) ) // Assert.That(subject, Is.Null)
439+ else if ( MatchesProperties ( constraint , t . Is , "Null" ) ) // Assert.That(subject, Is.Null)
403440 return RenameAssertThatAssertionToSubjectShouldAssertion ( "BeNull" ) ;
404- else if ( IsPropertyOfSymbol ( constraint , "Not" , "Null" , t . Is ) ) // Assert.That(subject, Is.Not.Null)
441+ else if ( MatchesProperties ( constraint , t . Is , "Not" , "Null" ) ) // Assert.That(subject, Is.Not.Null)
405442 return RenameAssertThatAssertionToSubjectShouldAssertion ( "NotBeNull" ) ;
406443 else if ( ! IsArgumentTypeOfNonGenericEnumerable ( invocation , argumentIndex : 0 ) )
407444 {
408- if ( IsPropertyOfSymbol ( constraint , "Empty" , t . Is ) ) // Assert.That(subject, Is.Empty)
445+ if ( MatchesProperties ( constraint , t . Is , "Empty" ) ) // Assert.That(subject, Is.Empty)
409446 return RenameAssertThatAssertionToSubjectShouldAssertion ( "BeEmpty" ) ;
410- else if ( IsPropertyOfSymbol ( constraint , "Not" , "Empty" , t . Is ) ) // Assert.That(subject, Is.Not.Empty)
447+ else if ( MatchesProperties ( constraint , t . Is , "Not" , "Empty" ) ) // Assert.That(subject, Is.Not.Empty)
411448 return RenameAssertThatAssertionToSubjectShouldAssertion ( "NotBeEmpty" ) ;
412449 }
413- if ( IsPropertyOfSymbol ( constraint , "Zero" , t . Is ) )
450+ if ( MatchesProperties ( constraint , t . Is , "Zero" ) )
414451 return DocumentEditorUtils . RewriteExpression ( invocation , [
415452 EditAction . ReplaceAssertionArgument ( index : 1 , generator => generator . LiteralExpression ( 0 ) ) ,
416453 EditAction . SubjectShouldAssertion ( argumentIndex : 0 , "Be" ) ,
417454 ] , context ) ;
418- else if ( IsPropertyOfSymbol ( constraint , "Not" , "Zero" , t . Is ) )
455+ else if ( MatchesProperties ( constraint , t . Is , "Not" , "Zero" ) )
419456 return DocumentEditorUtils . RewriteExpression ( invocation , [
420457 EditAction . ReplaceAssertionArgument ( index : 1 , generator => generator . LiteralExpression ( 0 ) ) ,
421458 EditAction . SubjectShouldAssertion ( argumentIndex : 0 , "NotBe" ) ,
@@ -453,12 +490,44 @@ private CreateChangedDocument RewriteContainsAssertion(IInvocationOperation invo
453490 ] , context ) ;
454491 }
455492
456- private static bool IsPropertyReferencedFromType ( IPropertyReferenceOperation propertyReference , INamedTypeSymbol type )
457- => propertyReference . Property . ContainingType . EqualsSymbol ( type ) ;
458- private static bool IsPropertyOfSymbol ( IPropertyReferenceOperation propertyReference , string firstProperty , string secondProperty , INamedTypeSymbol type )
459- => propertyReference . Property . Name == secondProperty && IsPropertyOfSymbol ( propertyReference . Instance , firstProperty , type ) ;
460- private static bool IsPropertyOfSymbol ( IOperation operation , string property , INamedTypeSymbol type )
461- => operation is IPropertyReferenceOperation propertyReference && propertyReference . Property . Name == property && IsPropertyReferencedFromType ( propertyReference , type ) ;
493+ private interface IOperationMatcher
494+ {
495+ ( IOperation op , ISymbol containingType ) TryGetNext ( IOperation operation ) ;
496+ }
497+ private class MethodInvocationMatcher ( string name ) : IOperationMatcher
498+ {
499+ public ( IOperation op , ISymbol containingType ) TryGetNext ( IOperation operation )
500+ => operation is IInvocationOperation invocation && invocation . TargetMethod . Name == name ? ( invocation . Instance , invocation . TargetMethod . ContainingType ) : ( null , null ) ;
501+ }
502+ private class PropertyReferenceMatcher ( string name ) : IOperationMatcher
503+ {
504+ public ( IOperation op , ISymbol containingType ) TryGetNext ( IOperation operation )
505+ => operation is IPropertyReferenceOperation propertyReference && propertyReference . Property . Name == name ? ( propertyReference . Instance , propertyReference . Member . ContainingType ) : ( null , null ) ;
506+ }
507+ private static IOperationMatcher Method ( string name ) => new MethodInvocationMatcher ( name ) ;
508+ private static IOperationMatcher Property ( string name ) => new PropertyReferenceMatcher ( name ) ;
509+ private static bool MatchesProperties ( IOperation constraint , INamedTypeSymbol type , params string [ ] matchers )
510+ => Matches ( constraint , type , Array . ConvertAll ( matchers , matcher => new PropertyReferenceMatcher ( matcher ) ) ) ;
511+ private static bool Matches ( IOperation constraint , INamedTypeSymbol type , params IOperationMatcher [ ] matchers )
512+ {
513+ var currentOp = constraint ;
514+ for ( var i = matchers . Length - 1 ; i >= 0 ; i -- )
515+ {
516+ var ( nextOp , containingType ) = matchers [ i ] . TryGetNext ( currentOp ) ;
517+ if ( containingType is null ) return false ;
518+
519+ if ( i is 0 )
520+ {
521+ return containingType . EqualsSymbol ( type ) ;
522+ }
523+
524+ if ( nextOp is null ) return false ;
525+
526+ currentOp = nextOp ;
527+ }
528+
529+ return false ;
530+ }
462531
463532 private static bool IsArgumentTypeOfNonGenericEnumerable ( IInvocationOperation invocation , int argumentIndex ) => IsArgumentTypeOf ( invocation , argumentIndex , SpecialType . System_Collections_IEnumerable ) ;
464533 private static bool IsArgumentTypeOf ( IInvocationOperation invocation , int argumentIndex , SpecialType type )
0 commit comments