@@ -127,7 +127,7 @@ public void Execute(GeneratorExecutionContext context)
127127
128128 internal static void GenerateSource ( Compilation compilation , TypeSymbolsCache typeSymbolsCache , List < MemberAccessExpressionSyntax > memberAccessExpressions , CodeBuilder builder , CancellationToken cancellationToken , bool isUnitTest = false )
129129 {
130- var generatedMethods = new HashSet < MethodSignature > ( ) ;
130+ var generatedMethods = new Dictionary < MethodSignature , ValueEnumerableType > ( ) ;
131131
132132 _ = builder
133133 . AppendLine ( "#nullable enable" )
@@ -143,29 +143,95 @@ internal static void GenerateSource(Compilation compilation, TypeSymbolsCache ty
143143 {
144144 foreach ( var expressionSyntax in memberAccessExpressions )
145145 {
146+ cancellationToken . ThrowIfCancellationRequested ( ) ;
147+
146148 var semanticModel = compilation . GetSemanticModel ( expressionSyntax . SyntaxTree ) ;
147149
148150 _ = GenerateSource ( compilation , semanticModel , typeSymbolsCache , expressionSyntax , builder , generatedMethods , cancellationToken , isUnitTest ) ;
149151 }
150152 }
151153 }
152154
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 } )
155170 {
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+ }
159178
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 )
161187 {
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+ }
164233
165234 return null ;
166235 }
167-
168- static bool IsValueEnumerable ( ITypeSymbol symbol , TypeSymbolsCache typeSymbolsCache )
169- => symbol . ImplementsInterface ( typeSymbolsCache [ "NetFabric.Hyperlinq.IValueEnumerable`2" ] ! , out var _ ) ;
170236 }
171237}
0 commit comments