@@ -127,7 +127,7 @@ public void Execute(GeneratorExecutionContext context)
127
127
128
128
internal static void GenerateSource ( Compilation compilation , TypeSymbolsCache typeSymbolsCache , List < MemberAccessExpressionSyntax > memberAccessExpressions , CodeBuilder builder , CancellationToken cancellationToken , bool isUnitTest = false )
129
129
{
130
- var generatedMethods = new HashSet < MethodSignature > ( ) ;
130
+ var generatedMethods = new Dictionary < MethodSignature , ValueEnumerableType > ( ) ;
131
131
132
132
_ = builder
133
133
. AppendLine ( "#nullable enable" )
@@ -143,29 +143,95 @@ internal static void GenerateSource(Compilation compilation, TypeSymbolsCache ty
143
143
{
144
144
foreach ( var expressionSyntax in memberAccessExpressions )
145
145
{
146
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
147
+
146
148
var semanticModel = compilation . GetSemanticModel ( expressionSyntax . SyntaxTree ) ;
147
149
148
150
_ = GenerateSource ( compilation , semanticModel , typeSymbolsCache , expressionSyntax , builder , generatedMethods , cancellationToken , isUnitTest ) ;
149
151
}
150
152
}
151
153
}
152
154
153
- static ValueEnumerableType ? GenerateSource ( Compilation compilation , SemanticModel semanticModel , TypeSymbolsCache typeSymbolsCache , MemberAccessExpressionSyntax expressionSyntax , CodeBuilder builder , HashSet < MethodSignature > generatedMethods , CancellationToken cancellationToken , bool isUnitTest )
154
- => expressionSyntax . Name . ToString ( ) switch
155
+ static ValueEnumerableType ? AsValueEnumerable ( MemberAccessExpressionSyntax memberAccessExpressionSyntax , Compilation compilation , SemanticModel semanticModel , TypeSymbolsCache typeSymbolsCache , MemberAccessExpressionSyntax expressionSyntax , CodeBuilder builder , Dictionary < MethodSignature , ValueEnumerableType > generatedMethods , CancellationToken cancellationToken , bool isUnitTest )
156
+ {
157
+ var typeSymbol = semanticModel . GetTypeInfo ( memberAccessExpressionSyntax . Expression , cancellationToken ) . Type ;
158
+ if ( typeSymbol is null )
159
+ return null ;
160
+
161
+ // Check if the receiver type implements IValueEnumerable<,>
162
+ if ( typeSymbol . ImplementsInterface ( typeSymbolsCache [ "NetFabric.Hyperlinq.IValueEnumerable`2" ] ! , out var _ ) )
163
+ return new ValueEnumerableType (
164
+ Name : typeSymbol . ToDisplayString ( ) ,
165
+ IsCollection : typeSymbol . ImplementsInterface ( typeSymbolsCache [ "NetFabric.Hyperlinq.IValueReadOnlyCollection`2" ] ! , out var _ ) ,
166
+ IsList : typeSymbol . ImplementsInterface ( typeSymbolsCache [ "NetFabric.Hyperlinq.IValueReadOnlyList`2" ] ! , out var _ ) ) ;
167
+
168
+ // Go up one layer. Generate method is required.
169
+ if ( expressionSyntax . Expression is InvocationExpressionSyntax { Expression : MemberAccessExpressionSyntax receiverSyntax } )
155
170
{
156
- "AsValueEnumerable" => GenerateAsValueEnumerable ( compilation , semanticModel , typeSymbolsCache , expressionSyntax , builder , generatedMethods , cancellationToken , isUnitTest ) ,
157
- _ => GenerateOperationSource ( compilation , semanticModel , expressionSyntax , builder , generatedMethods , cancellationToken , isUnitTest ) ,
158
- } ;
171
+ if ( GenerateSource ( compilation , semanticModel , typeSymbolsCache , receiverSyntax , builder , generatedMethods , cancellationToken , isUnitTest ) is { } valueEnumerableType )
172
+ return valueEnumerableType ; // Receiver type implements IValueEnumerable<,>
173
+ }
174
+
175
+ // Receiver type does not implement IValueEnumerable<,> so nothing else needs to be done
176
+ return null ;
177
+ }
159
178
160
- static ValueEnumerableType ? GenerateOperationSource ( Compilation compilation , SemanticModel semanticModel , MemberAccessExpressionSyntax expressionSyntax , CodeBuilder builder , HashSet < MethodSignature > generatedMethods , CancellationToken cancellationToken , bool isUnitTest )
179
+ static ValueEnumerableType ? GenerateSource ( Compilation compilation , SemanticModel semanticModel , TypeSymbolsCache typeSymbolsCache , MemberAccessExpressionSyntax expressionSyntax , CodeBuilder builder , Dictionary < MethodSignature , ValueEnumerableType > generatedMethods , CancellationToken cancellationToken , bool isUnitTest )
180
+ => expressionSyntax . Name . ToString ( ) switch
181
+ {
182
+ "AsValueEnumerable" => GenerateAsValueEnumerable ( compilation , semanticModel , typeSymbolsCache , expressionSyntax , builder , generatedMethods , cancellationToken , isUnitTest ) ,
183
+ _ => GenerateOperationSource ( compilation , semanticModel , typeSymbolsCache , expressionSyntax , builder , generatedMethods , cancellationToken , isUnitTest ) ,
184
+ } ;
185
+
186
+ static ValueEnumerableType ? GenerateOperationSource ( Compilation compilation , SemanticModel semanticModel , TypeSymbolsCache typeSymbolsCache , MemberAccessExpressionSyntax expressionSyntax , CodeBuilder builder , Dictionary < MethodSignature , ValueEnumerableType > generatedMethods , CancellationToken cancellationToken , bool isUnitTest )
161
187
{
162
- // Get the type this operator is applied to
163
- var receiverTypeSymbol = semanticModel . GetTypeInfo ( expressionSyntax . Expression , cancellationToken ) . Type ;
188
+ var valueEnumerableType = AsValueEnumerable ( expressionSyntax , compilation , semanticModel , typeSymbolsCache , expressionSyntax , builder , generatedMethods , cancellationToken , isUnitTest ) ;
189
+ if ( valueEnumerableType is null )
190
+ return null ;
191
+
192
+ var symbol = semanticModel . GetSymbolInfo ( expressionSyntax , cancellationToken ) . Symbol ;
193
+ if ( symbol is IMethodSymbol methodSymbol )
194
+ {
195
+ // Check if the generator already generated this method
196
+ var parameters = new string [ ] { valueEnumerableType . Name } . Concat ( methodSymbol . Parameters . Select ( parameter => parameter . Type . ToDisplayString ( ) ) ) . ToArray ( ) ;
197
+ var methodSignature = new MethodSignature ( expressionSyntax . Name . ToString ( ) , parameters ) ;
198
+ if ( generatedMethods . TryGetValue ( methodSignature , out var returnType ) )
199
+ return returnType ;
200
+
201
+ // Generate the extension method
202
+ _ = builder
203
+ . AppendLine ( )
204
+ . AppendLine ( "[MethodImpl(MethodImplOptions.AggressiveInlining)]" )
205
+ . AppendLine ( $ "public static { methodSymbol . ReturnType . ToDisplayString ( ) } { methodSymbol . Name } (this { valueEnumerableType . Name } source)")
206
+ . AppendIdentation ( ) . AppendLine ( $ "=> source.{ methodSymbol . Name } ();") ;
207
+
208
+ generatedMethods . Add ( methodSignature , returnType ) ;
209
+ return returnType ;
210
+ }
211
+
212
+
213
+ // TODO: when 'using System.Linq;' is not used...
214
+ if ( expressionSyntax . Parent is InvocationExpressionSyntax invocation )
215
+ {
216
+
217
+ var type = semanticModel . GetTypeInfo ( invocation . ArgumentList . Arguments [ 0 ] , cancellationToken ) . Type ;
218
+ var symbol2 = semanticModel . GetSymbolInfo ( invocation , cancellationToken ) . Symbol ;
219
+
220
+
221
+
222
+
223
+ // Check if the source already provides the implementation as an instance method
224
+
225
+ // Check if the source already provides the implementation as an extension method
226
+
227
+ // Generate the extension method
228
+
229
+ _ = builder
230
+ . AppendLine ( )
231
+ . AppendLine ( "// TODO" ) ;
232
+ }
164
233
165
234
return null ;
166
235
}
167
-
168
- static bool IsValueEnumerable ( ITypeSymbol symbol , TypeSymbolsCache typeSymbolsCache )
169
- => symbol . ImplementsInterface ( typeSymbolsCache [ "NetFabric.Hyperlinq.IValueEnumerable`2" ] ! , out var _ ) ;
170
236
}
171
237
}
0 commit comments