• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NetFabric / NetFabric.CodeAnalysis / 6277926364

22 Sep 2023 06:38PM UTC coverage: 73.953% (-9.2%) from 83.158%
6277926364

push

github

web-flow
Add IsIndexable (#28)

323 of 452 branches covered (0.0%)

Branch coverage included in aggregate %.

349 of 349 new or added lines in 18 files covered. (100.0%)

648 of 861 relevant lines covered (75.26%)

20.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.19
/NetFabric.CodeAnalysis/ITypeSymbolExtensions.cs
1
using Microsoft.CodeAnalysis;
2
using System;
3
using System.Collections.Immutable;
4
using System.Linq;
5
// ReSharper disable InvertIf
6

7
namespace NetFabric.CodeAnalysis;
8

9
public static partial class ITypeSymbolExtensions
10
{
11
    
12
    /// <summary>
13
    /// Gets a value indicating whether <see cref="Microsoft.CodeAnalysis.ITypeSymbol"/> implements the given interface type.
14
    /// </summary>
15
    /// <param name="typeSymbol">The <see cref="System.Type"/> to test.</param>
16
    /// <param name="interfaceType">The interface <see cref="System.Type"/> to test.</param>
17
    /// <param name="genericArguments">If methods returns <c>true</c> and interface type is generic, contains the generic arguments of the implemented interface.</param>
18
    /// <returns><c>true</c> if <see cref="Microsoft.CodeAnalysis.ITypeSymbol"/> implements interface type; otherwise, <c>false</c>.</returns>
19
    public static bool ImplementsInterface(this ITypeSymbol typeSymbol, SpecialType interfaceType, out ImmutableArray<ITypeSymbol> genericArguments)
20
    {
21
        if (typeSymbol is INamedTypeSymbol namedTypeSymbol 
31✔
22
            && namedTypeSymbol.OriginalDefinition.SpecialType == interfaceType)
31✔
23
        {
24
            genericArguments = namedTypeSymbol.TypeArguments;
2✔
25
            return true;
2✔
26
        }
27
        
28
        // ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
29
        foreach (var @interface in typeSymbol.AllInterfaces)
82✔
30
        {
31
            if (@interface.OriginalDefinition.SpecialType == interfaceType)
17✔
32
            {
33
                genericArguments = @interface.TypeArguments;
10✔
34
                return true;
10✔
35
            }
36
        }
37

38
        genericArguments = default;
19✔
39
        return false;
19✔
40
    }
41

42
    /// <summary>
43
    /// Gets a value indicating whether <see cref="Microsoft.CodeAnalysis.ITypeSymbol"/> implements the given interface type.
44
    /// </summary>
45
    /// <param name="typeSymbol">The <see cref="System.Type"/> to test.</param>
46
    /// <param name="interfaceType">The interface <see cref="System.Type"/> to test.</param>
47
    /// <param name="genericArguments">If methods returns <c>true</c> and interface type is generic, contains the generic arguments of the implemented interface.</param>
48
    /// <returns><c>true</c> if <see cref="Microsoft.CodeAnalysis.ITypeSymbol"/> implements interface type; otherwise, <c>false</c>.</returns>
49
    public static bool ImplementsInterface(this ITypeSymbol typeSymbol, INamedTypeSymbol interfaceType, out ImmutableArray<ITypeSymbol> genericArguments)
50
    {
51
        if (typeSymbol is INamedTypeSymbol namedTypeSymbol 
12✔
52
            && SymbolEqualityComparer.Default.Equals(namedTypeSymbol.OriginalDefinition, interfaceType))
12✔
53
        {
54
            genericArguments = namedTypeSymbol.TypeArguments;
1✔
55
            return true;
1✔
56
        }
57
        
58
        // ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
59
        foreach (var @interface in typeSymbol.AllInterfaces)
26✔
60
        {
61
            if (SymbolEqualityComparer.Default.Equals(@interface.OriginalDefinition, interfaceType))
4✔
62
            {
63
                genericArguments = @interface.TypeArguments;
4✔
64
                return true;
4✔
65
            }
66
        }
67

68
        genericArguments = default;
7✔
69
        return false;
7✔
70
    }
71

72
    internal static IPropertySymbol? GetPublicReadIndexer(this ITypeSymbol typeSymbol, params ITypeSymbol[] parameterTypes)
73
    {
74
        foreach (var member in typeSymbol.GetMembers().OfType<IPropertySymbol>())
38✔
75
        {
76
            if (member.IsIndexer && 
9✔
77
                !member.IsStatic && 
9✔
78
                member.DeclaredAccessibility == Accessibility.Public && 
9✔
79
                member.GetMethod is not null && 
9✔
80
                SequenceEqual(member.Parameters, parameterTypes))
9✔
81
            {
82
                return member;
6✔
83
            }
84
        }
85

86
        if (typeSymbol.TypeKind == TypeKind.Interface)
7✔
87
        {
88
            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
89
            foreach (var @interface in typeSymbol.AllInterfaces)
3✔
90
            {
91
                var property = @interface.GetPublicReadIndexer(parameterTypes);
1✔
92
                if (property is not null)
1✔
93
                    return property;
1✔
94
            }
95
        }
96
        else
97
        {
98
            var baseType = typeSymbol.BaseType;
6✔
99
            if (baseType is not null)
6✔
100
                return baseType.GetPublicReadIndexer(parameterTypes);
4✔
101
        }
102

103
        return null;
2✔
104
    }
6✔
105

106
    internal static IPropertySymbol? GetPublicReadIndexer(this ITypeSymbol typeSymbol, params Type[] parameterTypes)
107
    {
108
        foreach (var member in typeSymbol.GetMembers().OfType<IPropertySymbol>())
×
109
        {
110
            if (member.IsIndexer && 
×
111
                !member.IsStatic && 
×
112
                member.DeclaredAccessibility == Accessibility.Public && 
×
113
                member.GetMethod is not null && 
×
114
                SequenceEqual(member.Parameters, parameterTypes))
×
115
            {
116
                return member;
×
117
            }
118
        }
119

120
        if (typeSymbol.TypeKind == TypeKind.Interface)
×
121
        {
122
            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
123
            foreach (var @interface in typeSymbol.AllInterfaces)
×
124
            {
125
                var property = @interface.GetPublicReadIndexer(parameterTypes);
×
126
                if (property is not null)
×
127
                    return property;
×
128
            }
129
        }
130
        else
131
        {
132
            var baseType = typeSymbol.BaseType;
×
133
            if (baseType is not null)
×
134
                return baseType.GetPublicReadIndexer(parameterTypes);
×
135
        }
136

137
        return null;
×
138
    }
×
139

140
    internal static IPropertySymbol? GetPublicReadProperty(this ITypeSymbol typeSymbol, string name)
141
    {
142
        foreach (var member in typeSymbol.GetMembers(name).OfType<IPropertySymbol>())
220✔
143
        {
144
            if (!member.IsStatic && 
48✔
145
                member.DeclaredAccessibility == Accessibility.Public && 
48✔
146
                member.GetMethod is not null)
48✔
147
                return member;
48✔
148
        }
149

150
        if (typeSymbol.TypeKind == TypeKind.Interface)
38✔
151
        {
152
            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
153
            foreach (var @interface in typeSymbol.AllInterfaces)
11✔
154
            {
155
                var property = @interface.GetPublicReadProperty(name);
2✔
156
                if (property is not null)
2✔
157
                    return property;
1✔
158
            }
159
        }
160
        else
161
        {
162
            var baseType = typeSymbol.BaseType;
34✔
163
            if (baseType is not null)
34✔
164
                return baseType.GetPublicReadProperty(name);
23✔
165
        }
166

167
        return null;
14✔
168
    }
48✔
169

170
    internal static IMethodSymbol? GetPublicMethod(this ITypeSymbol typeSymbol, string name, params Type[] parameters)
171
    {
172
        foreach (var member in typeSymbol.GetMembers(name).OfType<IMethodSymbol>())
578✔
173
        {
174
            if (!member.IsStatic &&
111✔
175
                member.DeclaredAccessibility == Accessibility.Public &&
111✔
176
                SequenceEqual(member.Parameters, parameters))
111✔
177
            {
178
                return member;
98✔
179
            }
180
        }
181

182
        if (typeSymbol.TypeKind == TypeKind.Interface)
129✔
183
        {
184
            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
185
            foreach (var @interface in typeSymbol.AllInterfaces)
28✔
186
            {
187
                var method = @interface.GetPublicMethod(name, parameters);
8✔
188
                if (method is not null)
8✔
189
                    return method;
4✔
190
            }
191
        }
192
        else
193
        {
194
            var baseType = typeSymbol.BaseType;
121✔
195
            if (baseType is not null)
121✔
196
                return baseType.GetPublicMethod(name, parameters);
80✔
197
        }
198

199
        return null;
45✔
200
    }
98✔
201

202
    static bool SequenceEqual(ImmutableArray<IParameterSymbol> parameters, Type[] types)
203
    {
204
        if (parameters.Length != types.Length)
107✔
205
            return false;
9✔
206

207
        // ReSharper disable once LoopCanBeConvertedToQuery
208
        for (var index = 0; index < parameters.Length; index++)
214✔
209
        {
210
            if (parameters[index].Type.MetadataName != types[index].Name)
9!
211
                return false;
×
212
        }
213

214
        return true;
98✔
215
    }
216

217
    static bool SequenceEqual(ImmutableArray<IParameterSymbol> parameters, ITypeSymbol[] types)
218
    {
219
        if (parameters.Length != types.Length)
7!
220
            return false;
×
221

222
        // ReSharper disable once LoopCanBeConvertedToQuery
223
        for (var index = 0; index < parameters.Length; index++)
26✔
224
        {
225
            if (parameters[index].Type.MetadataName != types[index].MetadataName)
7✔
226
                return false;
1✔
227
        }
228

229
        return true;
6✔
230
    }
231

232
    public static bool IsSpanOrReadOnlySpanType(this ITypeSymbol typeSymbol)
233
    {
234
        if (typeSymbol.MetadataName == "System.Span" || typeSymbol.MetadataName == "System.ReadOnlySpan")
26!
235
        {
236
            var containingNamespace = typeSymbol.ContainingNamespace.ToDisplayString();
×
237
            return containingNamespace == "System" && 
×
238
                typeSymbol is INamedTypeSymbol namedType && 
×
239
                namedType.TypeArguments.Length == 1;
×
240
        }
241

242
        return false;
26✔
243
    }
244

245
    /// <summary>
246
    /// Determines whether the specified <see cref="ITypeSymbol"/> represents an integer type within the given <see cref="Compilation"/>.
247
    /// </summary>
248
    /// <param name="typeSymbol">The <see cref="ITypeSymbol"/> to check for an integer type.</param>
249
    /// <param name="compilation">The <see cref="Compilation"/> containing the semantic information about the code.</param>
250
    /// <returns>
251
    ///   <c>true</c> if the specified <see cref="ITypeSymbol"/> represents an integer type; otherwise, <c>false</c>.
252
    /// </returns>
253
    /// <remarks>
254
    /// This method checks if the <see cref="ITypeSymbol"/> represents an integer type within the context of the provided <see cref="Compilation"/>.
255
    /// Starting from .NET 7, it also checks if the <see cref="ITypeSymbol"/> implements the <see cref="System.Numerics.IBinaryInteger{T}"/> interface,
256
    /// which indicates support for binary integer operations.
257
    /// </remarks>
258
    public static bool IsIntegerType(this ITypeSymbol typeSymbol, Compilation compilation)
259
    {
260
        if (typeSymbol.MetadataName == "System.SByte" || 
×
261
            typeSymbol.MetadataName == "System.Byte" || 
×
262
            typeSymbol.MetadataName == "System.Int16" || 
×
263
            typeSymbol.MetadataName == "System.UInt16" || 
×
264
            typeSymbol.MetadataName == "System.Int32" || 
×
265
            typeSymbol.MetadataName == "System.UInt32" || 
×
266
            typeSymbol.MetadataName == "System.Int64" || 
×
267
            typeSymbol.MetadataName == "System.UInt64")
×
268
        {
269
            return true;
×
270
        }
271

272
        // supported starting from .NET 7
273
        var binaryIntegerType = compilation.GetTypeByMetadataName("System.Numerics.IBinaryInteger`1")!;
×
274
        if (binaryIntegerType is not null &&
×
275
            typeSymbol.ImplementsInterface(binaryIntegerType, out var arguments) && 
×
276
            arguments.Length == 1 &&
×
277
            arguments[0].MetadataName == typeSymbol.MetadataName)
×
278
        {
279
            return true;
×
280
        }
281

282
        return false;
×
283
    }
284

285
    /// <summary>
286
    /// Determines whether the specified <see cref="ITypeSymbol"/> represents a floating-point numeric type within the context of the provided <see cref="Compilation"/>.
287
    /// </summary>
288
    /// <param name="typeSymbol">The <see cref="ITypeSymbol"/> to check for a floating-point numeric type.</param>
289
    /// <param name="compilation">The <see cref="Compilation"/> containing the semantic information about the code.</param>
290
    /// <returns>
291
    ///   <c>true</c> if the specified <see cref="ITypeSymbol"/> represents a floating-point numeric type; otherwise, <c>false</c>.
292
    /// </returns>
293
    /// <remarks>
294
    /// Starting from .NET 7, this method checks if the <see cref="ITypeSymbol"/> represents a floating-point numeric type within the context of the provided <see cref="Compilation"/>.
295
    /// It also compares the <see cref="ITypeSymbol"/> to the <see cref="System.Numerics.IFloatingPoint{T}"/> interface,
296
    /// which indicates support for floating-point numeric operations.
297
    /// </remarks>
298
    public static bool IsFloatingPointType(this ITypeSymbol typeSymbol, Compilation compilation)
299
    {
300
        if (typeSymbol.MetadataName == "System.Half" || 
×
301
            typeSymbol.MetadataName == "System.Float" || 
×
302
            typeSymbol.MetadataName == "System.Double" || 
×
303
            typeSymbol.MetadataName == "System.Decimal")
×
304
        {
305
            return true;
×
306
        }
307

308
        // supported starting from .NET 7
309
        var floatingPointType = compilation.GetTypeByMetadataName("System.Numerics.IFloatingPoint`1")!;
×
310
        if (floatingPointType is not null &&
×
311
            typeSymbol.ImplementsInterface(floatingPointType, out var arguments) && 
×
312
            arguments.Length == 1 &&
×
313
            arguments[0].MetadataName == typeSymbol.MetadataName)
×
314
        {
315
            return true;
×
316
        }
317

318
        return false;
×
319
    }
320

321
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc