Skip to content

Commit

Permalink
feat: Fix dictionary custom methods false diagnostics
Browse files Browse the repository at this point in the history
  • Loading branch information
Meir017 committed Jul 19, 2024
1 parent ecde7ec commit 0f75588
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 2 deletions.
29 changes: 29 additions & 0 deletions src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,28 @@ public class DictionaryTests
[Implemented]
public void DictionaryShouldContainKey_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.DictionaryShouldContainKey_ContainsKeyShouldBeTrue);

[DataTestMethod]
[DataRow(
@"namespace TestNamespace
public class MultiKeyDict<TKey1, TKey2, TValue> : Dictionary<TKey1, Dictionary<TKey2, TValue>>
{
public bool ContainsKey(TKey1 key1, TKey2 key2) => false;
public bool ContainsValue(TKey1 key1, TKey2 key2) => false;
}
public class TestClass
{
public void TestMethod(MultiKeyDict<int, int, string> actual)
{
var dict = new MultiKeyDict<int, int, string>();
actual.ContainsKey(0, 1).Should().BeTrue();
actual.ContainsValue(0, 1).Should().BeTrue();
}
}")]
[Implemented]
public void DictionaryMethods_CustomMethods_TestNoAnalyzer(string code) => DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(code);

[DataTestMethod]
[AssertionCodeFix(
oldAssertion: "actual.ContainsKey(expectedKey).Should().BeTrue({0});",
Expand Down Expand Up @@ -159,6 +181,13 @@ public class DictionaryTests
[Implemented]
public void DictionaryShouldContainPair_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion);

private void VerifyCSharpNoDiagnostic(string sourceAssersion, string additionalCode)
{
var source = GenerateCode.GenericIDictionaryAssertion(sourceAssersion, additionalCode);

Check failure on line 186 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

No overload for method 'GenericIDictionaryAssertion' takes 2 arguments

Check failure on line 186 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

No overload for method 'GenericIDictionaryAssertion' takes 2 arguments

Check failure on line 186 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Debug)

No overload for method 'GenericIDictionaryAssertion' takes 2 arguments

Check failure on line 186 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Debug)

No overload for method 'GenericIDictionaryAssertion' takes 2 arguments

Check failure on line 186 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

No overload for method 'GenericIDictionaryAssertion' takes 2 arguments

Check failure on line 186 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

No overload for method 'GenericIDictionaryAssertion' takes 2 arguments

Check failure on line 186 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Release)

No overload for method 'GenericIDictionaryAssertion' takes 2 arguments

Check failure on line 186 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Release)

No overload for method 'GenericIDictionaryAssertion' takes 2 arguments

DiagnosticVerifier.VerifyCSharpDiagnostic(source);

Check failure on line 188 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

The type arguments for method 'DiagnosticVerifier.VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string, params DiagnosticResult[])' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Check failure on line 188 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Debug)

The type arguments for method 'DiagnosticVerifier.VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string, params DiagnosticResult[])' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Check failure on line 188 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Debug)

The type arguments for method 'DiagnosticVerifier.VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string, params DiagnosticResult[])' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Check failure on line 188 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Debug)

The type arguments for method 'DiagnosticVerifier.VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string, params DiagnosticResult[])' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Check failure on line 188 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

The type arguments for method 'DiagnosticVerifier.VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string, params DiagnosticResult[])' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Check failure on line 188 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Release)

The type arguments for method 'DiagnosticVerifier.VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string, params DiagnosticResult[])' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Check failure on line 188 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Release)

The type arguments for method 'DiagnosticVerifier.VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string, params DiagnosticResult[])' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Check failure on line 188 in src/FluentAssertions.Analyzers.Tests/Tips/DictionaryTests.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest, Release)

The type arguments for method 'DiagnosticVerifier.VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string, params DiagnosticResult[])' cannot be inferred from the usage. Try specifying the type arguments explicitly.
}

private void VerifyCSharpDiagnostic(string sourceAssersion, DiagnosticMetadata metadata)
{
var source = GenerateCode.GenericIDictionaryAssertion(sourceAssersion);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs
return;
}

context.Options.AnalyzerConfigOptionsProvider.GetOptions(invocation.Syntax.SyntaxTree).TryGetValue("use_diagnostic_per_assertion", out var useDiagnosticPerAssertion);
if (HasConditionalAccessAncestor(invocation))
{
var expressionStatement = invocation.GetFirstAncestor<IExpressionStatementOperation>();
Expand Down Expand Up @@ -211,10 +212,12 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs
case nameof(string.StartsWith) when invocationBeforeShould.IsContainedInType(SpecialType.System_String):
context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.StringShouldStartWith_StartsWithShouldBeTrue));
return;
case nameof(IDictionary<string, object>.ContainsKey) when invocationBeforeShould.ImplementsOrIsInterface(metadata.IDictionaryOfT2) || invocationBeforeShould.ImplementsOrIsInterface(metadata.IReadonlyDictionaryOfT2):
case nameof(Dictionary<string, object>.ContainsKey) when (invocationBeforeShould.ImplementsOrIsInterface(metadata.IDictionaryOfT2) || invocationBeforeShould.ImplementsOrIsInterface(metadata.IReadonlyDictionaryOfT2))
&& invocationBeforeShould.AreMethodParameterSameTypeAsContainingTypeArguments((parameter: 0, typeArgument: 0)):
context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.DictionaryShouldContainKey_ContainsKeyShouldBeTrue));
return;
case nameof(Dictionary<string, object>.ContainsValue) when invocationBeforeShould.IsContainedInType(metadata.DictionaryOfT2):
case nameof(Dictionary<string, object>.ContainsValue) when invocationBeforeShould.IsContainedInType(metadata.DictionaryOfT2)
&& invocationBeforeShould.AreMethodParameterSameTypeAsContainingTypeArguments((parameter: 0, typeArgument: 1)):
context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.DictionaryShouldContainValue_ContainsValueShouldBeTrue));
return;
}
Expand Down
17 changes: 17 additions & 0 deletions src/FluentAssertions.Analyzers/Utilities/OperartionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,23 @@ public static bool IsTypeof(this IArgumentOperation argument, SpecialType type)
public static bool IsTypeof(this IArgumentOperation argument, INamedTypeSymbol type)
=> argument.Value.UnwrapConversion().Type.EqualsSymbol(type);

public static bool AreMethodParameterSameTypeAsContainingTypeArguments(this IInvocationOperation invocation, params (int parameter, int typeArgument)[] parameters)
{
if (invocation.TargetMethod.Parameters.Length != parameters.Length)
return false;

if (invocation.TargetMethod.ContainingType.TypeArguments.Length < parameters.Max(x => x.typeArgument))
return false;

foreach (var (parameter, typeArgument) in parameters)
{
if (!invocation.TargetMethod.Parameters[parameter].Type.EqualsSymbol(invocation.TargetMethod.ContainingType.TypeArguments[typeArgument]))
return false;
}

return true;
}

public static bool IsSameArgumentReference(this IArgumentOperation argument1, IArgumentOperation argument2)
{
return argument1.TryGetFirstDescendent<IParameterReferenceOperation>(out var argument1Reference)
Expand Down

0 comments on commit 0f75588

Please sign in to comment.