• 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

98.11
/NetFabric.CodeAnalysis/ITypeSymbolExtensions.IsEnumerable.cs
1
using Microsoft.CodeAnalysis;
2
using System;
3
using System.Diagnostics.CodeAnalysis;
4
using System.Linq;
5

6
namespace NetFabric.CodeAnalysis;
7

8
public static partial class ITypeSymbolExtensions
9
{
10
    /// <summary>
11
    /// Checks if the provided <paramref name="typeSymbol"/> can be used as a source in a <c>foreach</c>
12
    /// statement, indicating whether it is enumerable.
13
    /// </summary>
14
    /// <param name="typeSymbol">The <see cref="ITypeSymbol"/> to be checked for enumerability.</param>
15
    /// <param name="compilation">The <see cref="Compilation"/> representing the current
16
    /// compilation context.</param>
17
    /// <returns>
18
    /// <c>true</c> if the <paramref name="typeSymbol"/> is enumerable; otherwise, <c>false</c>.
19
    /// </returns>
20
    /// <remarks>
21
    /// This method examines the provided <paramref name="typeSymbol"/> to determine if it can be
22
    /// used as a source in a <c>foreach</c> statement, indicating whether it is enumerable. To be
23
    /// considered enumerable, the type should support iteration, typically by implementing the
24
    /// <see cref="System.Collections.IEnumerable"/> or <see cref="System.Collections.Generic.IEnumerable{T}"/>
25
    /// interface, or by providing a suitable GetEnumerator method.
26
    /// </remarks>
27
    public static bool IsEnumerable(this ITypeSymbol typeSymbol, Compilation compilation)
28
        => IsEnumerable(typeSymbol, compilation, out _, out _);
×
29

30
    /// <summary>
31
    /// Checks if the provided <paramref name="typeSymbol"/> can be used as a source in a <c>foreach</c>
32
    /// statement, indicating whether it is enumerable, and if so, retrieves information about
33
    /// the enumerable symbols.
34
    /// </summary>
35
    /// <param name="typeSymbol">The <see cref="ITypeSymbol"/> to be checked for enumerability.</param>
36
    /// <param name="compilation">The <see cref="Compilation"/> representing the current
37
    /// compilation context.</param>
38
    /// <param name="enumerableSymbols">
39
    /// When the method returns <c>true</c>, this parameter will contain information about the
40
    /// enumerable symbols associated with the <paramref name="typeSymbol"/>. If the method returns
41
    /// <c>false</c>, this parameter will be <c>null</c>.
42
    /// </param>
43
    /// <returns>
44
    /// <c>true</c> if the <paramref name="typeSymbol"/> is enumerable; otherwise, <c>false</c>.
45
    /// </returns>
46
    /// <remarks>
47
    /// This method examines the provided <paramref name="typeSymbol"/> to determine if it can be
48
    /// used as a source in a <c>foreach</c> statement, indicating whether it is enumerable. To be
49
    /// considered enumerable, the type should support iteration, typically by implementing the
50
    /// <see cref="System.Collections.IEnumerable"/> or <see cref="System.Collections.Generic.IEnumerable{T}"/>
51
    /// interface, or by providing a suitable GetEnumerator method.
52
    /// 
53
    /// If the type is enumerable, the method provides information about the enumerable symbols
54
    /// associated with it, which can be useful for various code analysis tasks.
55
    /// </remarks>
56
    public static bool IsEnumerable(this ITypeSymbol typeSymbol, Compilation compilation,
57
        [NotNullWhen(true)] out EnumerableSymbols? enumerableSymbols)
58
        => IsEnumerable(typeSymbol, compilation, out enumerableSymbols, out _);
×
59

60
    /// <summary>
61
    /// Checks if the provided <paramref name="typeSymbol"/> can be used as a source in a <c>foreach</c>
62
    /// statement, indicating whether it is enumerable, and if so, retrieves information about
63
    /// the enumerable symbols and any potential error.
64
    /// </summary>
65
    /// <param name="typeSymbol">The <see cref="ITypeSymbol"/> to be checked for enumerability.</param>
66
    /// <param name="compilation">The <see cref="Compilation"/> representing the current
67
    /// compilation context.</param>
68
    /// <param name="enumerableSymbols">
69
    /// When the method returns <c>true</c>, this parameter will contain information about the
70
    /// enumerable symbols associated with the <paramref name="typeSymbol"/>. If the method returns
71
    /// <c>false</c>, this parameter will be <c>null</c>.
72
    /// </param>
73
    /// <param name="error">
74
    /// When the method returns <c>false</c>, this parameter will contain information about any
75
    /// error encountered while determining enumerability. If the method returns <c>true</c>, this
76
    /// parameter will be <see cref="IsEnumerableError.None"/>.
77
    /// </param>
78
    /// <returns>
79
    /// <c>true</c> if the <paramref name="typeSymbol"/> is enumerable; otherwise, <c>false</c>.
80
    /// </returns>
81
    /// <remarks>
82
    /// This method examines the provided <paramref name="typeSymbol"/> to determine if it can be
83
    /// used as a source in a <c>foreach</c> statement, indicating whether it is enumerable. To be
84
    /// considered enumerable, the type should support iteration, typically by implementing the
85
    /// <see cref="System.Collections.IEnumerable"/> or <see cref="System.Collections.Generic.IEnumerable{T}"/>
86
    /// interface, or by providing a suitable GetEnumerator method.
87
    /// 
88
    /// If the type is enumerable, the method provides information about the enumerable symbols
89
    /// associated with it, which can be useful for various code analysis tasks.
90
    /// </remarks>
91
    public static bool IsEnumerable(this ITypeSymbol typeSymbol, Compilation compilation,
92
    [NotNullWhen(true)] out EnumerableSymbols? enumerableSymbols,
93
        out IsEnumerableError error)
94
    {
95
        var forEachUsesIndexer = typeSymbol.TypeKind == TypeKind.Array || typeSymbol.IsSpanOrReadOnlySpanType();
27✔
96

97
        if (typeSymbol.TypeKind != TypeKind.Interface)
27✔
98
        {
99
            var getEnumerator = typeSymbol.GetPublicMethod(NameOf.GetEnumerator);
24✔
100
            if (getEnumerator is not null)
24✔
101
            {
102
                return HandleGetEnumerator(getEnumerator, compilation, out enumerableSymbols, out error);
20✔
103
            }
104

105
            getEnumerator = compilation.GetExtensionMethodsWithName(typeSymbol, NameOf.GetEnumerator)
4✔
106
                .FirstOrDefault(methodSymbol => methodSymbol.Parameters.Length == 1);
5✔
107
            if (getEnumerator is not null)
4✔
108
            {
109
                return HandleGetEnumerator(getEnumerator, compilation, out enumerableSymbols, out error);
1✔
110
            }
111
        }
112

113
        if (typeSymbol.ImplementsInterface(SpecialType.System_Collections_Generic_IEnumerable_T, out var genericArguments))
6✔
114
        {
115
            var genericEnumerableType = compilation
3✔
116
                .GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T)
3✔
117
                .Construct(genericArguments[0]);
3✔
118
            var genericEnumeratorType = compilation
3✔
119
                .GetSpecialType(SpecialType.System_Collections_Generic_IEnumerator_T)
3✔
120
                .Construct(genericArguments[0]);
3✔
121
            var enumeratorType = compilation.GetSpecialType(SpecialType.System_Collections_IEnumerator);
3✔
122
            var disposableType = compilation.GetSpecialType(SpecialType.System_IDisposable);
3✔
123

124
            enumerableSymbols = new EnumerableSymbols(
3✔
125
                forEachUsesIndexer,
3✔
126
                genericEnumerableType.GetPublicMethod(NameOf.GetEnumerator, Type.EmptyTypes)!,
3✔
127
                new EnumeratorSymbols(
3✔
128
                    genericEnumeratorType.GetPublicReadProperty(NameOf.Current)!,
3✔
129
                    enumeratorType.GetPublicMethod(NameOf.MoveNext, Type.EmptyTypes)!)
3✔
130
                    {
3✔
131
                        Reset = enumeratorType.GetPublicMethod(NameOf.Reset, Type.EmptyTypes),
3✔
132
                        Dispose = disposableType.GetPublicMethod(NameOf.Dispose, Type.EmptyTypes),
3✔
133
                        IsGenericsEnumeratorInterface = true,
3✔
134
                        IsEnumeratorInterface = true,
3✔
135
                    }
3✔
136
            );
3✔
137
            error = IsEnumerableError.None; 
3✔
138
            return true;
3✔
139
        }
140

141
        if (typeSymbol.ImplementsInterface(SpecialType.System_Collections_IEnumerable, out _))
3✔
142
        {
143
            var enumerableType = compilation.GetSpecialType(SpecialType.System_Collections_IEnumerable);
2✔
144
            var enumeratorType = compilation.GetSpecialType(SpecialType.System_Collections_IEnumerator);
2✔
145

146
            enumerableSymbols = new EnumerableSymbols(
2✔
147
                forEachUsesIndexer,
2✔
148
                enumerableType.GetPublicMethod(NameOf.GetEnumerator, Type.EmptyTypes)!,
2✔
149
                new EnumeratorSymbols(
2✔
150
                    enumeratorType.GetPublicReadProperty(NameOf.Current)!,
2✔
151
                    enumeratorType.GetPublicMethod(NameOf.MoveNext, Type.EmptyTypes)!)
2✔
152
                {
2✔
153
                    Reset = enumeratorType.GetPublicMethod(NameOf.Reset, Type.EmptyTypes),
2✔
154
                    IsEnumeratorInterface = true,
2✔
155
                }                   
2✔
156
            );
2✔
157
            error = IsEnumerableError.None; 
2✔
158
            return true;
2✔
159
        }
160

161
        enumerableSymbols = default;
1✔
162
        error = IsEnumerableError.MissingGetEnumerator;
1✔
163
        return false;
1✔
164
    }
165

166
    static bool HandleGetEnumerator(IMethodSymbol getEnumerator, 
167
        Compilation compilation,
168
        [NotNullWhen(true)] out EnumerableSymbols? enumerableSymbols,
169
        out IsEnumerableError error)
170
    {
171
        var enumeratorType = getEnumerator.ReturnType;
21✔
172

173
        var current = enumeratorType.GetPublicReadProperty(NameOf.Current);
21✔
174
        if (current is null)
21✔
175
        {
176
            enumerableSymbols = default;
2✔
177
            error = IsEnumerableError.MissingCurrent;
2✔
178
            return false;
2✔
179
        }
180

181
        var moveNext = enumeratorType.GetPublicMethod(NameOf.MoveNext);
19✔
182
        if (moveNext is null || moveNext.ReturnType.ToDisplayString() != "bool")
19✔
183
        {   
184
            enumerableSymbols = default;
2✔
185
            error = IsEnumerableError.MissingMoveNext;
2✔
186
            return false;
2✔
187
        }
188

189
        var reset = enumeratorType.GetPublicMethod("Reset");
17✔
190
        _ = enumeratorType.IsDisposable(compilation, out var dispose);
17✔
191

192
        enumerableSymbols = new EnumerableSymbols(
17✔
193
            false,
17✔
194
            getEnumerator,
17✔
195
            new EnumeratorSymbols(current, moveNext)
17✔
196
            {
17✔
197
                Reset = reset,
17✔
198
                Dispose = dispose,
17✔
199
                IsValueType = enumeratorType.IsValueType,
17✔
200
                IsRefLikeType = enumeratorType.IsRefLikeType,
17✔
201
                IsGenericsEnumeratorInterface =
17✔
202
                    enumeratorType.TypeKind == TypeKind.Interface
17✔
203
                    && enumeratorType.ImplementsInterface(
17✔
204
                        SpecialType.System_Collections_Generic_IEnumerable_T, out _),
17✔
205
                IsEnumeratorInterface =
17✔
206
                    enumeratorType.TypeKind == TypeKind.Interface,
17✔
207
            }
17✔
208
        );
17✔
209
        error = IsEnumerableError.None;
17✔
210
        return true;
17✔
211
    }
212
}
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