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

NetFabric / NetFabric.CodeAnalysis / 6481688024

11 Oct 2023 11:08AM UTC coverage: 74.009% (+0.2%) from 73.784%
6481688024

push

github

aalmada
Add extension methods to ITypeSymbol

323 of 450 branches covered (0.0%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

648 of 862 relevant lines covered (75.17%)

20.91 hits per line

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

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

6
namespace NetFabric.CodeAnalysis;
7

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

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

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

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

71
    /// <summary>
72
    /// Gets a public readable indexer property with the specified parameter types from the given type symbol.
73
    /// </summary>
74
    /// <param name="typeSymbol">The type symbol from which to retrieve the indexer property.</param>
75
    /// <param name="parameterTypes">An array of parameter types, represented by <see cref="ITypeSymbol"/>, that the indexer should accept (empty array for parameterless).</param>
76
    /// <returns>
77
    /// An <see cref="IPropertySymbol"/> representing the public readable indexer property if found; otherwise, returns <c>null</c>.
78
    /// </returns>
79
    /// <remarks>
80
    /// This method searches for a public readable indexer property within the given type symbol with the specified parameter types.
81
    /// If a matching indexer property is found, and it has a getter, it returns an <see cref="IPropertySymbol"/> representing the indexer;
82
    /// otherwise, it returns <c>null</c>.
83
    /// </remarks>
84
    public static IPropertySymbol? GetPublicReadIndexer(this ITypeSymbol typeSymbol, params ITypeSymbol[] parameterTypes)
85
    {
86
        foreach (var member in typeSymbol.GetMembers().OfType<IPropertySymbol>())
38✔
87
        {
88
            if (member.IsIndexer && 
9✔
89
                !member.IsStatic && 
9✔
90
                member.DeclaredAccessibility == Accessibility.Public && 
9✔
91
                member.GetMethod is not null && 
9✔
92
                SequenceEqual(member.Parameters, parameterTypes))
9✔
93
            {
94
                return member;
6✔
95
            }
96
        }
97

98
        if (typeSymbol.TypeKind == TypeKind.Interface)
7✔
99
        {
100
            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
101
            foreach (var @interface in typeSymbol.AllInterfaces)
3✔
102
            {
103
                var property = @interface.GetPublicReadIndexer(parameterTypes);
1✔
104
                if (property is not null)
1✔
105
                    return property;
1✔
106
            }
107
        }
108
        else
109
        {
110
            var baseType = typeSymbol.BaseType;
6✔
111
            if (baseType is not null)
6✔
112
                return baseType.GetPublicReadIndexer(parameterTypes);
4✔
113
        }
114

115
        return null;
2✔
116
    }
6✔
117

118
    /// <summary>
119
    /// Gets a public readable indexer property with the specified parameter types from the given type symbol.
120
    /// </summary>
121
    /// <param name="typeSymbol">The type symbol from which to retrieve the indexer property.</param>
122
    /// <param name="parameterTypes">An array of parameter types that the indexer should accept (empty array for parameterless).</param>
123
    /// <returns>
124
    /// An <see cref="IPropertySymbol"/> representing the public readable indexer property if found; otherwise, returns <c>null</c>.
125
    /// </returns>
126
    /// <remarks>
127
    /// This method searches for a public readable indexer property within the given type symbol with the specified parameter types.
128
    /// If a matching indexer property is found, and it has a getter, it returns an <see cref="IPropertySymbol"/> representing the indexer;
129
    /// otherwise, it returns <c>null</c>.
130
    /// </remarks>
131
    public static IPropertySymbol? GetPublicReadIndexer(this ITypeSymbol typeSymbol, params Type[] parameterTypes)
132
    {
133
        foreach (var member in typeSymbol.GetMembers().OfType<IPropertySymbol>())
×
134
        {
135
            if (member.IsIndexer && 
×
136
                !member.IsStatic && 
×
137
                member.DeclaredAccessibility == Accessibility.Public && 
×
138
                member.GetMethod is not null && 
×
139
                SequenceEqual(member.Parameters, parameterTypes))
×
140
            {
141
                return member;
×
142
            }
143
        }
144

145
        if (typeSymbol.TypeKind == TypeKind.Interface)
×
146
        {
147
            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
148
            foreach (var @interface in typeSymbol.AllInterfaces)
×
149
            {
150
                var property = @interface.GetPublicReadIndexer(parameterTypes);
×
151
                if (property is not null)
×
152
                    return property;
×
153
            }
154
        }
155
        else
156
        {
157
            var baseType = typeSymbol.BaseType;
×
158
            if (baseType is not null)
×
159
                return baseType.GetPublicReadIndexer(parameterTypes);
×
160
        }
161

162
        return null;
×
163
    }
×
164

165
    /// <summary>
166
    /// Gets a public readable property with the specified name from the given type symbol.
167
    /// </summary>
168
    /// <param name="typeSymbol">The type symbol from which to retrieve the property.</param>
169
    /// <param name="name">The name of the property to search for.</param>
170
    /// <returns>
171
    /// An <see cref="IPropertySymbol"/> representing the public readable property if found; otherwise, returns <c>null</c>.
172
    /// </returns>
173
    /// <remarks>
174
    /// This method searches for a public readable property with the specified name within the given type symbol.
175
    /// If a matching property is found and it has a getter, it returns an <see cref="IPropertySymbol"/> representing the property;
176
    /// otherwise, it returns <c>null</c>.
177
    /// </remarks>
178
    public static IPropertySymbol? GetPublicReadProperty(this ITypeSymbol typeSymbol, string name)
179
    {
180
        foreach (var member in typeSymbol.GetMembers(name).OfType<IPropertySymbol>())
220✔
181
        {
182
            if (!member.IsStatic && 
48✔
183
                member.DeclaredAccessibility == Accessibility.Public && 
48✔
184
                member.GetMethod is not null)
48✔
185
                return member;
48✔
186
        }
187

188
        if (typeSymbol.TypeKind == TypeKind.Interface)
38✔
189
        {
190
            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
191
            foreach (var @interface in typeSymbol.AllInterfaces)
11✔
192
            {
193
                var property = @interface.GetPublicReadProperty(name);
2✔
194
                if (property is not null)
2✔
195
                    return property;
1✔
196
            }
197
        }
198
        else
199
        {
200
            var baseType = typeSymbol.BaseType;
34✔
201
            if (baseType is not null)
34✔
202
                return baseType.GetPublicReadProperty(name);
23✔
203
        }
204

205
        return null;
14✔
206
    }
48✔
207

208
    /// <summary>
209
    /// Gets a public method with the specified name and parameter types from the given type symbol.
210
    /// </summary>
211
    /// <param name="typeSymbol">The type symbol from which to retrieve the method.</param>
212
    /// <param name="name">The name of the method to search for.</param>
213
    /// <param name="parameters">An array of parameter types that the method should accept (empty array for parameterless).</param>
214
    /// <returns>
215
    /// An <see cref="IMethodSymbol"/> representing the public method if found; otherwise, returns <c>null</c>.
216
    /// </returns>
217
    /// <remarks>
218
    /// This method searches for a public method with the specified name and parameter types within the given type symbol.
219
    /// If a matching method is found, it returns an <see cref="IMethodSymbol"/> that represents the method;
220
    /// otherwise, it returns <c>null</c>.
221
    /// </remarks>
222
    public static IMethodSymbol? GetPublicMethod(this ITypeSymbol typeSymbol, string name, params Type[] parameters)
223
    {
224
        foreach (var member in typeSymbol.GetMembers(name).OfType<IMethodSymbol>())
578✔
225
        {
226
            if (!member.IsStatic &&
111✔
227
                member.DeclaredAccessibility == Accessibility.Public &&
111✔
228
                SequenceEqual(member.Parameters, parameters))
111✔
229
            {
230
                return member;
98✔
231
            }
232
        }
233

234
        if (typeSymbol.TypeKind == TypeKind.Interface)
129✔
235
        {
236
            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
237
            foreach (var @interface in typeSymbol.AllInterfaces)
28✔
238
            {
239
                var method = @interface.GetPublicMethod(name, parameters);
8✔
240
                if (method is not null)
8✔
241
                    return method;
4✔
242
            }
243
        }
244
        else
245
        {
246
            var baseType = typeSymbol.BaseType;
121✔
247
            if (baseType is not null)
121✔
248
                return baseType.GetPublicMethod(name, parameters);
80✔
249
        }
250

251
        return null;
45✔
252
    }
98✔
253

254
    static bool SequenceEqual(ImmutableArray<IParameterSymbol> parameters, Type[] types)
255
    {
256
        if (parameters.Length != types.Length)
107✔
257
            return false;
9✔
258

259
        // ReSharper disable once LoopCanBeConvertedToQuery
260
        for (var index = 0; index < parameters.Length; index++)
214✔
261
        {
262
            if (parameters[index].Type.MetadataName != types[index].Name)
9!
263
                return false;
×
264
        }
265

266
        return true;
98✔
267
    }
268

269
    static bool SequenceEqual(ImmutableArray<IParameterSymbol> parameters, ITypeSymbol[] types)
270
    {
271
        if (parameters.Length != types.Length)
7!
272
            return false;
×
273

274
        // ReSharper disable once LoopCanBeConvertedToQuery
275
        for (var index = 0; index < parameters.Length; index++)
26✔
276
        {
277
            if (parameters[index].Type.MetadataName != types[index].MetadataName)
7✔
278
                return false;
1✔
279
        }
280

281
        return true;
6✔
282
    }
283

284
    /// <summary>
285
    /// Determines whether the given type symbol represents a Span or ReadOnlySpan type.
286
    /// </summary>
287
    /// <param name="typeSymbol">The type symbol to be checked.</param>
288
    /// <returns>
289
    /// <c>true</c> if the type symbol represents a Span or ReadOnlySpan; otherwise, <c>false</c>.
290
    /// </returns>
291
    /// <remarks>
292
    /// This method checks whether the provided <paramref name="typeSymbol"/> corresponds to a type that is compatible with
293
    /// the Span and ReadOnlySpan types, which are used for efficient and safe data access in .NET.
294
    /// </remarks>
295
    public static bool IsSpanOrReadOnlySpanType(this ITypeSymbol typeSymbol)
296
    {
297
        if (typeSymbol.MetadataName == "System.Span" || typeSymbol.MetadataName == "System.ReadOnlySpan")
26!
298
        {
299
            return typeSymbol is INamedTypeSymbol namedType && 
×
300
                namedType.TypeArguments.Length == 1;
×
301
        }
302

303
        return false;
26✔
304
    }
305

306
    /// <summary>
307
    /// Determines whether the specified <see cref="ITypeSymbol"/> represents an integer type within the given <see cref="Compilation"/>.
308
    /// </summary>
309
    /// <param name="typeSymbol">The <see cref="ITypeSymbol"/> to check for an integer type.</param>
310
    /// <param name="compilation">The <see cref="Compilation"/> containing the semantic information about the code.</param>
311
    /// <returns>
312
    ///   <c>true</c> if the specified <see cref="ITypeSymbol"/> represents an integer type; otherwise, <c>false</c>.
313
    /// </returns>
314
    /// <remarks>
315
    /// This method checks if the <see cref="ITypeSymbol"/> represents an integer type within the context of the provided <see cref="Compilation"/>.
316
    /// Starting from .NET 7, it also checks if the <see cref="ITypeSymbol"/> implements the <see cref="System.Numerics.IBinaryInteger{T}"/> interface,
317
    /// which indicates support for binary integer operations.
318
    /// </remarks>
319
    public static bool IsIntegerType(this ITypeSymbol typeSymbol, Compilation compilation)
320
    {
321
        if (typeSymbol.MetadataName == "System.SByte" || 
×
322
            typeSymbol.MetadataName == "System.Byte" || 
×
323
            typeSymbol.MetadataName == "System.Int16" || 
×
324
            typeSymbol.MetadataName == "System.UInt16" || 
×
325
            typeSymbol.MetadataName == "System.Int32" || 
×
326
            typeSymbol.MetadataName == "System.UInt32" || 
×
327
            typeSymbol.MetadataName == "System.Int64" || 
×
328
            typeSymbol.MetadataName == "System.UInt64")
×
329
        {
330
            return true;
×
331
        }
332

333
        // supported starting from .NET 7
334
        var binaryIntegerType = compilation.GetTypeByMetadataName("System.Numerics.IBinaryInteger`1")!;
×
335
        if (binaryIntegerType is not null &&
×
336
            typeSymbol.ImplementsInterface(binaryIntegerType, out var arguments) && 
×
337
            arguments.Length == 1 &&
×
338
            arguments[0].MetadataName == typeSymbol.MetadataName)
×
339
        {
340
            return true;
×
341
        }
342

343
        return false;
×
344
    }
345

346
    /// <summary>
347
    /// Determines whether the specified <see cref="ITypeSymbol"/> represents a floating-point numeric type within the context of the provided <see cref="Compilation"/>.
348
    /// </summary>
349
    /// <param name="typeSymbol">The <see cref="ITypeSymbol"/> to check for a floating-point numeric type.</param>
350
    /// <param name="compilation">The <see cref="Compilation"/> containing the semantic information about the code.</param>
351
    /// <returns>
352
    ///   <c>true</c> if the specified <see cref="ITypeSymbol"/> represents a floating-point numeric type; otherwise, <c>false</c>.
353
    /// </returns>
354
    /// <remarks>
355
    /// 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"/>.
356
    /// It also compares the <see cref="ITypeSymbol"/> to the <see cref="System.Numerics.IFloatingPoint{T}"/> interface,
357
    /// which indicates support for floating-point numeric operations.
358
    /// </remarks>
359
    public static bool IsFloatingPointType(this ITypeSymbol typeSymbol, Compilation compilation)
360
    {
361
        if (typeSymbol.MetadataName == "System.Half" || 
×
362
            typeSymbol.MetadataName == "System.Float" || 
×
363
            typeSymbol.MetadataName == "System.Double" || 
×
364
            typeSymbol.MetadataName == "System.Decimal")
×
365
        {
366
            return true;
×
367
        }
368

369
        // supported starting from .NET 7
370
        var floatingPointType = compilation.GetTypeByMetadataName("System.Numerics.IFloatingPoint`1")!;
×
371
        if (floatingPointType is not null &&
×
372
            typeSymbol.ImplementsInterface(floatingPointType, out var arguments) && 
×
373
            arguments.Length == 1 &&
×
374
            arguments[0].MetadataName == typeSymbol.MetadataName)
×
375
        {
376
            return true;
×
377
        }
378

379
        return false;
×
380
    }
381

382
}
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