Skip to content

Commit 46b6b8a

Browse files
authored
infra: improve type comparison (#113)
1 parent b475b0f commit 46b6b8a

File tree

7 files changed

+66
-16
lines changed

7 files changed

+66
-16
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
using Microsoft.CodeAnalysis;
2-
using System.Linq;
1+
using FluentAssertions.Analyzers.Utilities;
2+
using Microsoft.CodeAnalysis;
33

44
namespace FluentAssertions.Analyzers
55
{
66
public abstract class CollectionAnalyzer : FluentAssertionsAnalyzer
77
{
88
protected override bool ShouldAnalyzeVariableType(INamedTypeSymbol type, SemanticModel semanticModel)
99
{
10-
return type.Name != "String"
11-
&& type.AllInterfaces.Any(@interface => @interface.Name == "IEnumerable")
12-
&& !type.AllInterfaces.Any(@interface => @interface.Name == "IDictionary");
10+
var iDictionaryType = semanticModel.GetGenericIDictionaryType();
11+
return type.SpecialType != SpecialType.System_String
12+
&& type.IsTypeOrConstructedFromTypeOrImplementsType(SpecialType.System_Collections_Generic_IEnumerable_T)
13+
&& !type.IsTypeOrConstructedFromTypeOrImplementsType(iDictionaryType);
1314
}
1415
}
1516
}

src/FluentAssertions.Analyzers/Tips/Collections/CollectionShouldHaveElementAt.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.CodeAnalysis;
1+
using FluentAssertions.Analyzers.Utilities;
2+
using Microsoft.CodeAnalysis;
23
using Microsoft.CodeAnalysis.CodeFixes;
34
using Microsoft.CodeAnalysis.CSharp.Syntax;
45
using Microsoft.CodeAnalysis.Diagnostics;
@@ -30,7 +31,10 @@ protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
3031

3132
protected override bool ShouldAnalyzeVariableType(INamedTypeSymbol type, SemanticModel semanticModel)
3233
{
33-
if (type.AllInterfaces.Any(@interface => @interface.Name == "IReadOnlyDictionary" || @interface.Name == "IDictionary"))
34+
var iReadOnlyDictionaryType = semanticModel.GetIReadOnlyDictionaryType();
35+
var iDictionaryType = semanticModel.GetGenericIDictionaryType();
36+
if (type.IsTypeOrConstructedFromTypeOrImplementsType(iReadOnlyDictionaryType)
37+
|| type.IsTypeOrConstructedFromTypeOrImplementsType(iDictionaryType))
3438
{
3539
return false;
3640
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
using System.Linq;
1+
using FluentAssertions.Analyzers.Utilities;
22
using Microsoft.CodeAnalysis;
33

44
namespace FluentAssertions.Analyzers
55
{
66
public abstract class DictionaryAnalyzer : FluentAssertionsAnalyzer
77
{
88
protected override bool ShouldAnalyzeVariableType(INamedTypeSymbol type, SemanticModel semanticModel)
9-
=> type.AllInterfaces.Any(@interface => @interface.Name == "IDictionary");
9+
{
10+
var iDictionaryType = semanticModel.GetGenericIDictionaryType();
11+
return type.IsTypeOrConstructedFromTypeOrImplementsType(iDictionaryType);
12+
}
1013
}
1114
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
using System;
1+
using FluentAssertions.Analyzers.Utilities;
22
using Microsoft.CodeAnalysis;
33

44
namespace FluentAssertions.Analyzers
55
{
66
public abstract class ExceptionAnalyzer : FluentAssertionsAnalyzer
77
{
8-
protected override bool ShouldAnalyzeVariableType(INamedTypeSymbol type, SemanticModel semanticModel) => type.Name == nameof(Action);
8+
protected override bool ShouldAnalyzeVariableType(INamedTypeSymbol type, SemanticModel semanticModel)
9+
{
10+
var actionType = semanticModel.GetActionType();
11+
return type.IsTypeOrConstructedFromTypeOrImplementsType(actionType);
12+
}
913
}
1014
}

src/FluentAssertions.Analyzers/Tips/Strings/StringAnalyzer.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ namespace FluentAssertions.Analyzers
44
{
55
public abstract class StringAnalyzer : FluentAssertionsAnalyzer
66
{
7-
protected override bool ShouldAnalyzeVariableType(INamedTypeSymbol type, SemanticModel semanticModel) => type.Name == "String";
7+
protected override bool ShouldAnalyzeVariableType(INamedTypeSymbol type, SemanticModel semanticModel) => type.SpecialType == SpecialType.System_String;
88
}
99
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using Microsoft.CodeAnalysis;
2+
using System;
3+
using System.Collections;
4+
using System.Collections.Generic;
5+
6+
namespace FluentAssertions.Analyzers.Utilities
7+
{
8+
internal static class SemanticModelTypeExtensions
9+
{
10+
public static INamedTypeSymbol GetActionType(this SemanticModel semanticModel)
11+
=> GetTypeFrom(semanticModel, typeof(Action));
12+
13+
public static INamedTypeSymbol GetGenericIEnumerableType(this SemanticModel semanticModel)
14+
=> GetTypeFrom(semanticModel, SpecialType.System_Collections_Generic_IEnumerable_T);
15+
16+
public static INamedTypeSymbol GetIEnumerableType(this SemanticModel semanticModel)
17+
=> GetTypeFrom(semanticModel, SpecialType.System_Collections_IEnumerable);
18+
19+
public static INamedTypeSymbol GetIDictionaryType(this SemanticModel semanticModel)
20+
=> GetTypeFrom(semanticModel, typeof(IDictionary));
21+
22+
public static INamedTypeSymbol GetGenericIDictionaryType(this SemanticModel semanticModel)
23+
=> GetTypeFrom(semanticModel, typeof(IDictionary<,>));
24+
25+
public static INamedTypeSymbol GetIReadOnlyDictionaryType(this SemanticModel semanticModel)
26+
=> GetTypeFrom(semanticModel, typeof(IReadOnlyDictionary<,>));
27+
28+
private static INamedTypeSymbol GetTypeFrom(SemanticModel semanticModel, Type type)
29+
=> semanticModel.Compilation.GetTypeByMetadataName(type.FullName);
30+
31+
private static INamedTypeSymbol GetTypeFrom(SemanticModel semanticModel, SpecialType type)
32+
=> semanticModel.Compilation.GetSpecialType(type);
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
using Microsoft.CodeAnalysis;
22
using System;
3-
using System.Collections.Generic;
43
using System.Linq;
5-
using System.Text;
6-
using System.Threading.Tasks;
74

85
namespace FluentAssertions.Analyzers.Utilities
96
{
107
internal static class TypesExtensions
118
{
12-
public static bool IsTypeOrConstructedFromTypeOrImplementsType(this ITypeSymbol type, SpecialType specialType)
9+
public static bool IsTypeOrConstructedFromTypeOrImplementsType(this INamedTypeSymbol type, SpecialType specialType)
1310
{
1411
var abstractType = type.OriginalDefinition;
1512
return abstractType.SpecialType == specialType
1613
|| abstractType.AllInterfaces.Any(@interface => @interface.OriginalDefinition.SpecialType == specialType);
1714
}
15+
16+
public static bool IsTypeOrConstructedFromTypeOrImplementsType(this INamedTypeSymbol type, INamedTypeSymbol other)
17+
{
18+
var abstractType = type.OriginalDefinition;
19+
return abstractType.Equals(other)
20+
|| abstractType.AllInterfaces.Any(@interface => @interface.OriginalDefinition.Equals(other));
21+
}
1822
}
1923
}

0 commit comments

Comments
 (0)