Skip to content

Commit f2abf0c

Browse files
authored
Fix ElementAt suggestion with field reference (#68)
* Fix ElementAt suggestion with field reference * Fix copy and paste error in NumericTests
1 parent bf42cc2 commit f2abf0c

File tree

4 files changed

+35
-57
lines changed

4 files changed

+35
-57
lines changed

src/AwesomeAssertions.Analyzers.Tests/Tips/CollectionTests.cs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -846,20 +846,22 @@ public void CollectionShouldHaveElementAt_AccessPropertyOfIndexedValue_TestNoAna
846846
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(GenerateCode.GenericIListCodeBlockAssertion(assertion));
847847

848848
[TestMethod]
849+
[DataRow("List<Value>")]
850+
[DataRow("Value[]")]
849851
[Implemented]
850-
public void CollectionShouldHaveElementAt_AccessObjectPropertyOfIndexedValue_TestNoAnalyzer()
852+
public void CollectionShouldHaveElementAt_AccessObjectPropertyOfIndexedValue_TestNoAnalyzer(string collectionType)
851853
{
852854
// I cannot see the relevant difference to CollectionShouldHaveElementAt_AccessPropertyOfIndexedValue_TestNoAnalyzer,
853855
// but it makes a difference.
854-
string source = """
856+
string source = $$"""
855857
using System.Collections.Generic;
856858
using AwesomeAssertions;
857859
858860
public record Value(object InstanceValue);
859861
860862
public sealed class TestClass
861863
{
862-
public void TestMethod(List<Value> list)
864+
public void TestMethod({{collectionType}} list)
863865
{
864866
list[0].InstanceValue.Should().Be(1);
865867
}
@@ -868,6 +870,29 @@ public void TestMethod(List<Value> list)
868870
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source);
869871
}
870872

873+
[TestMethod]
874+
[DataRow("List<Value>")]
875+
[DataRow("Value[]")]
876+
[Implemented]
877+
public void CollectionShouldHaveElementAt_AccessObjectFieldOfIndexedValue_TestNoAnalyzer(string collectionType)
878+
{
879+
string source = $$"""
880+
using System.Collections.Generic;
881+
using AwesomeAssertions;
882+
883+
using Value = System.ValueTuple<object>;
884+
885+
public sealed class TestClass
886+
{
887+
public void TestMethod({{collectionType}} list)
888+
{
889+
list[0].Item1.Should().Be(1);
890+
}
891+
}
892+
""";
893+
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source);
894+
}
895+
871896
[TestMethod]
872897
[Implemented]
873898
public void CollectionShouldHaveElementAt_ClassDoesNotImplementIList_TestNoAnalyzer()

src/AwesomeAssertions.Analyzers.Tests/Tips/NumericTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public class NumericTests
5353

5454
[TestMethod]
5555
[DataRow("actual.Should().BeLessThanOrEqualTo(upper, \"because reason 1\").And.BeGreaterThanOrEqualTo(lower, \"because reason 2\");")]
56-
[DataRow("actual.Should().BeLessThanOrEqualTo(upper, \"because reason 1\").And.BeGreaterThanOrEqualTo(lower, \"because reason 2\");")]
56+
[DataRow("actual.Should().BeGreaterThanOrEqualTo(lower, \"because reason 1\").And.BeLessThanOrEqualTo(upper, \"because reason 2\");")]
5757
[Implemented]
5858
public void NumericShouldBeInRange_BeLessThanOrEqualToAndBeGreaterThanOrEqualTo_WithMessagesInBothAssertions_TestAnalyzer(string assertion)
5959
{

src/AwesomeAssertions.Analyzers/Tips/AwesomeAssertionsAnalyzer.cs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -388,16 +388,12 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, AwesomeA
388388
}
389389
}
390390

391-
if (subject.TryGetSingleChildOrSelf<IPropertyReferenceOperation>(out var propertyReference) && !propertyReference.Property.IsIndexer)
392-
{
393-
return;
394-
}
395-
else if (subject.TryGetFirstDescendent<IArrayElementReferenceOperation>(out _))
396-
{
397-
context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveElementAt_IndexerShouldBe));
398-
}
399-
else if (subject.TryGetFirstDescendentOrSelf(out propertyReference) && propertyReference.Property.IsIndexer &&
400-
(propertyReference.Instance.Type.ImplementsOrIsInterface(metadata.IListOfT) || propertyReference.Instance.Type.ImplementsOrIsInterface(metadata.IReadonlyListOfT)))
391+
subject = subject.UnwrapConversion();
392+
393+
bool isListIndexer = subject is IPropertyReferenceOperation propertyReference && propertyReference.Property.IsIndexer &&
394+
(propertyReference.Instance.Type.ImplementsOrIsInterface(metadata.IListOfT) || propertyReference.Instance.Type.ImplementsOrIsInterface(metadata.IReadonlyListOfT));
395+
396+
if (subject is IArrayElementReferenceOperation || isListIndexer)
401397
{
402398
context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveElementAt_IndexerShouldBe));
403399
}

src/AwesomeAssertions.Analyzers/Utilities/OperationExtensions.cs

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -7,49 +7,6 @@ namespace AwesomeAssertions.Analyzers;
77

88
internal static class OperationExtensions
99
{
10-
/// <summary>
11-
/// Tries to get the parent operation or its first child if it's of the requested type.
12-
/// <typeparamref name="TOperation"/>.
13-
/// </summary>
14-
public static bool TryGetSingleChildOrSelf<TOperation>(this IOperation parent, out TOperation operation) where TOperation : IOperation
15-
{
16-
if (parent is TOperation op)
17-
{
18-
operation = op;
19-
return true;
20-
}
21-
return parent.TryGetSingleChild(out operation);
22-
}
23-
24-
/// <summary>
25-
/// Tries to get the first child of the parent operation if it's of the requested type
26-
/// <typeparamref name="TOperation"/>.
27-
/// </summary>
28-
public static bool TryGetSingleChild<TOperation>(this IOperation parent, out TOperation operation) where TOperation : IOperation
29-
{
30-
if (parent.ChildOperations.Count == 1 && parent.ChildOperations.First() is TOperation op)
31-
{
32-
operation = op;
33-
return true;
34-
}
35-
36-
operation = default;
37-
return false;
38-
}
39-
40-
/// <summary>
41-
/// Tries to get the first descendent of the parent operation or the parent operation itself. where each operation has only one child.
42-
/// </summary>
43-
public static bool TryGetFirstDescendentOrSelf<TOperation>(this IOperation parent, out TOperation operation) where TOperation : IOperation
44-
{
45-
if (parent is TOperation op)
46-
{
47-
operation = op;
48-
return true;
49-
}
50-
return parent.TryGetFirstDescendent(out operation);
51-
}
52-
5310
/// <summary>
5411
/// Tries to get the first descendent of the parent operation. where each operation has only one child.
5512
/// </summary>

0 commit comments

Comments
 (0)