Skip to content

File tree

1 file changed

+109
-40
lines changed

1 file changed

+109
-40
lines changed

src/FluentAssertions.Analyzers/Tips/NunitCodeFixProvider.cs

Lines changed: 109 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)