Skip to content

Commit

Permalink
Support Classic model for Same on Value types.
Browse files Browse the repository at this point in the history
  • Loading branch information
manfred-brands committed Aug 10, 2020
1 parent a8e24a6 commit 0c2b2d7
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,53 @@ public void AnalyzeWhenCollectionsWithValueTypesProvided()

AnalyzerAssert.Valid(analyzer, testCode);
}

[Test]
public void AnalyzeClassicWhenLiteralTypesProvided()
{
var testCode = TestUtility.WrapInTestMethod(
"↓Assert.AreSame(1, 1);");

AnalyzerAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

[Test]
public void AnalyzeClassicWhenStructTypesProvided()
{
var testCode = TestUtility.WrapInTestMethod(@"
var expected = Guid.Empty;
var actual = expected;
↓Assert.AreSame(expected, actual);");

AnalyzerAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

[Test]
public void AnalyzeClassicWhenActualIsReferenceTypeAndExpectedIsValueType()
{
var testCode = TestUtility.WrapInTestMethod(
@"↓Assert.AreNotSame(3, ""3"");");

AnalyzerAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

[Test]
public void AnalyzeClassicWhenActualIsValueTypeAndExpectedIsReferenceType()
{
var testCode = TestUtility.WrapInTestMethod(
@"↓Assert.AreNotSame(""3"", 3);");

AnalyzerAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

[Test]
public void AnalyzeClassicWhenBothArgumentsAreReferenceType()
{
var testCode = TestUtility.WrapInTestMethod(
@"Assert.AreSame(""3"", ""3"");");

AnalyzerAssert.Valid(analyzer, testCode);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,55 @@ public void VerifySameAsIntoEqualToFixWithMessageAndParams()
");
AnalyzerAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: CodeFixConstants.UseIsEqualToDescription);
}

[Test]
public void VerifyAreSameIntoAreEqualFix()
{
var code = TestUtility.WrapInTestMethod(@"
var expected = Guid.Empty;
var actual = expected;
↓Assert.AreSame(expected, actual);
");
var fixedCode = TestUtility.WrapInTestMethod(@"
var expected = Guid.Empty;
var actual = expected;
Assert.AreEqual(expected, actual);
");
AnalyzerAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: CodeFixConstants.UseIsEqualToDescription);
}

[Test]
public void VerifyAreNotSameIntoAreNotEqualFixWithMessage()
{
var code = TestUtility.WrapInTestMethod(@"
var expected = Guid.Empty;
↓Assert.AreNotSame(expected, Guid.NewGuid(), ""message"");
");
var fixedCode = TestUtility.WrapInTestMethod(@"
var expected = Guid.Empty;
Assert.AreNotEqual(expected, Guid.NewGuid(), ""message"");
");
AnalyzerAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: CodeFixConstants.UseIsEqualToDescription);
}

[Test]
public void VerifyAreSameIntoAreEqualFixWithMessageAndParams()
{
var code = TestUtility.WrapInTestMethod(@"
var expected = Guid.Empty;
↓Assert.AreSame(expected, expected, ""message"", Guid.Empty);
");
var fixedCode = TestUtility.WrapInTestMethod(@"
var expected = Guid.Empty;
Assert.AreEqual(expected, expected, ""message"", Guid.Empty);
");
AnalyzerAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: CodeFixConstants.UseIsEqualToDescription);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,46 +30,59 @@ protected override void AnalyzeAssertInvocation(SyntaxNodeAnalysisContext contex
var cancellationToken = context.CancellationToken;
var semanticModel = context.SemanticModel;

if (!AssertHelper.TryGetActualAndConstraintExpressions(assertExpression, semanticModel,
out var actualExpression, out var constraintExpression))
ExpressionSyntax? actualExpression;
ExpressionSyntax? expectedExpression = null;

if (methodSymbol.Name.Equals(NunitFrameworkConstants.NameOfAssertAreSame) ||
methodSymbol.Name.Equals(NunitFrameworkConstants.NameOfAssertAreNotSame))
{
return;
actualExpression = assertExpression.GetArgumentExpression(methodSymbol, NunitFrameworkConstants.NameOfActualParameter);
expectedExpression = assertExpression.GetArgumentExpression(methodSymbol, NunitFrameworkConstants.NameOfExpectedParameter);
}

foreach (var constraintPartExpression in constraintExpression.ConstraintParts)
else
{
if (HasIncompatiblePrefixes(constraintPartExpression)
|| constraintPartExpression.HasUnknownExpressions())
if (!AssertHelper.TryGetActualAndConstraintExpressions(assertExpression, semanticModel,
out actualExpression, out var constraintExpression))
{
return;
}

var constraintMethod = constraintPartExpression.GetConstraintMethod();

if (constraintMethod?.Name != NunitFrameworkConstants.NameOfIsSameAs
|| constraintMethod.ReturnType?.GetFullMetadataName() != NunitFrameworkConstants.FullNameOfSameAsConstraint)
foreach (var constraintPartExpression in constraintExpression.ConstraintParts)
{
continue;
}
if (HasIncompatiblePrefixes(constraintPartExpression)
|| constraintPartExpression.HasUnknownExpressions())
{
return;
}

var constraintMethod = constraintPartExpression.GetConstraintMethod();

if (constraintMethod?.Name != NunitFrameworkConstants.NameOfIsSameAs
|| constraintMethod.ReturnType?.GetFullMetadataName() != NunitFrameworkConstants.FullNameOfSameAsConstraint)
{
continue;
}

var expectedArgumentExpression = constraintPartExpression.GetExpectedArgumentExpression();
expectedExpression = constraintPartExpression.GetExpectedArgumentExpression();
break;
}
}

if (expectedArgumentExpression == null)
continue;
if (actualExpression == null || expectedExpression == null)
return;

var actualType = AssertHelper.GetUnwrappedActualType(actualExpression, semanticModel, cancellationToken);
var actualType = AssertHelper.GetUnwrappedActualType(actualExpression, semanticModel, cancellationToken);

if (actualType == null)
continue;
var expectedType = semanticModel.GetTypeInfo(expectedExpression, cancellationToken).Type;

var expectedType = semanticModel.GetTypeInfo(expectedArgumentExpression, cancellationToken).Type;
if (actualType == null || expectedType == null)
return;

if (actualType.IsValueType || expectedType.IsValueType)
{
context.ReportDiagnostic(Diagnostic.Create(
descriptor,
assertExpression.GetLocation()));
}
if (actualType.IsValueType || expectedType.IsValueType)
{
context.ReportDiagnostic(Diagnostic.Create(
descriptor,
assertExpression.GetLocation()));
}
}

Expand Down
46 changes: 35 additions & 11 deletions src/nunit.analyzers/SameAsOnValueTypes/SameAsOnValueTypesCodeFix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,46 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
if (invocationNode == null)
return;

// Find the 'SameAs' member access expression
var constraintArgument = invocationNode.ArgumentList.Arguments[1];
var isSameExpression = constraintArgument
.DescendantNodesAndSelf()
.Where(s => s.IsKind(SyntaxKind.SimpleMemberAccessExpression))
.OfType<MemberAccessExpressionSyntax>()
.SingleOrDefault(m => m.Name.ToString() == NunitFrameworkConstants.NameOfIsSameAs);

if (isSameExpression == null)
var assertExpression = invocationNode.Expression as MemberAccessExpressionSyntax;

if (assertExpression == null)
return;

var isEqualToExpression = isSameExpression.WithName(SyntaxFactory.IdentifierName(NunitFrameworkConstants.NameOfIsEqualTo));
ExpressionSyntax? original;
ExpressionSyntax? replacement;

switch (assertExpression.Name.ToString())
{
case NunitFrameworkConstants.NameOfAssertAreSame:
original = assertExpression;
replacement = assertExpression.WithName(SyntaxFactory.IdentifierName(NunitFrameworkConstants.NameOfAssertAreEqual));
break;
case NunitFrameworkConstants.NameOfAssertAreNotSame:
original = assertExpression;
replacement = assertExpression.WithName(SyntaxFactory.IdentifierName(NunitFrameworkConstants.NameOfAssertAreNotEqual));
break;
case NunitFrameworkConstants.NameOfAssertThat:
// Find the 'SameAs' member access expression
var constraintArgument = invocationNode.ArgumentList.Arguments[1];
var isSameExpression = constraintArgument
.DescendantNodesAndSelf()
.Where(s => s.IsKind(SyntaxKind.SimpleMemberAccessExpression))
.OfType<MemberAccessExpressionSyntax>()
.SingleOrDefault(m => m.Name.ToString() == NunitFrameworkConstants.NameOfIsSameAs);

if (isSameExpression == null)
return;

original = isSameExpression;
replacement = isSameExpression.WithName(SyntaxFactory.IdentifierName(NunitFrameworkConstants.NameOfIsEqualTo));
break;
default:
return;
}

context.CancellationToken.ThrowIfCancellationRequested();

var newRoot = root.ReplaceNode(isSameExpression, isEqualToExpression);
var newRoot = root.ReplaceNode(original, replacement);

context.RegisterCodeFix(
CodeAction.Create(
Expand Down

0 comments on commit 0c2b2d7

Please sign in to comment.