@@ -35,6 +35,7 @@ private ImmutableArray<Symbol> BindCrefInternal(CrefSyntax syntax, out Symbol? a
35
35
case SyntaxKind . IndexerMemberCref :
36
36
case SyntaxKind . OperatorMemberCref :
37
37
case SyntaxKind . ConversionOperatorMemberCref :
38
+ case SyntaxKind . ExtensionMemberCref :
38
39
return BindMemberCref ( ( MemberCrefSyntax ) syntax , containerOpt : null , ambiguityWinner : out ambiguityWinner , diagnostics : diagnostics ) ;
39
40
default :
40
41
throw ExceptionUtilities . UnexpectedValue ( syntax . Kind ( ) ) ;
@@ -125,6 +126,9 @@ private ImmutableArray<Symbol> BindMemberCref(MemberCrefSyntax syntax, Namespace
125
126
case SyntaxKind . ConversionOperatorMemberCref :
126
127
result = BindConversionOperatorMemberCref ( ( ConversionOperatorMemberCrefSyntax ) syntax , containerOpt , out ambiguityWinner , diagnostics ) ;
127
128
break ;
129
+ case SyntaxKind . ExtensionMemberCref :
130
+ result = BindExtensionMemberCref ( ( ExtensionMemberCrefSyntax ) syntax , containerOpt , out ambiguityWinner , diagnostics ) ;
131
+ break ;
128
132
default :
129
133
throw ExceptionUtilities . UnexpectedValue ( syntax . Kind ( ) ) ;
130
134
}
@@ -216,6 +220,137 @@ private ImmutableArray<Symbol> BindIndexerMemberCref(IndexerMemberCrefSyntax syn
216
220
diagnostics : diagnostics ) ;
217
221
}
218
222
223
+ private ImmutableArray < Symbol > BindExtensionMemberCref ( ExtensionMemberCrefSyntax syntax , NamespaceOrTypeSymbol ? containerOpt , out Symbol ? ambiguityWinner , BindingDiagnosticBag diagnostics )
224
+ {
225
+ if ( containerOpt is not NamedTypeSymbol namedContainer )
226
+ {
227
+ ambiguityWinner = null ;
228
+ return ImmutableArray < Symbol > . Empty ;
229
+ }
230
+
231
+ ImmutableArray < Symbol > sortedSymbols = default ;
232
+ int arity = 0 ;
233
+ TypeArgumentListSyntax ? typeArgumentListSyntax = null ;
234
+ CrefParameterListSyntax ? parameters = null ;
235
+
236
+ if ( syntax . Member is NameMemberCrefSyntax { Name : SimpleNameSyntax simpleName } nameMember )
237
+ {
238
+ arity = simpleName . Arity ;
239
+ typeArgumentListSyntax = simpleName is GenericNameSyntax genericName ? genericName . TypeArgumentList : null ;
240
+ parameters = nameMember . Parameters ;
241
+
242
+ TypeArgumentListSyntax ? extensionTypeArguments = syntax . TypeArgumentList ;
243
+ int extensionArity = extensionTypeArguments ? . Arguments . Count ?? 0 ;
244
+ sortedSymbols = computeSortedAndFilteredCrefExtensionMembers ( namedContainer , simpleName . Identifier . ValueText , extensionArity , arity , extensionTypeArguments , diagnostics , syntax ) ;
245
+ }
246
+
247
+ if ( sortedSymbols . IsDefaultOrEmpty )
248
+ {
249
+ ambiguityWinner = null ;
250
+ return [ ] ;
251
+ }
252
+
253
+ Debug . Assert ( sortedSymbols . All ( s => s . GetIsNewExtensionMember ( ) ) ) ;
254
+
255
+ return ProcessCrefMemberLookupResults ( sortedSymbols , arity , syntax , typeArgumentListSyntax , parameters , out ambiguityWinner , diagnostics ) ;
256
+
257
+ ImmutableArray < Symbol > computeSortedAndFilteredCrefExtensionMembers ( NamedTypeSymbol container , string name , int extensionArity , int arity , TypeArgumentListSyntax ? extensionTypeArguments , BindingDiagnosticBag diagnostics , ExtensionMemberCrefSyntax syntax )
258
+ {
259
+ Debug . Assert ( name is not null ) ;
260
+
261
+ LookupOptions options = LookupOptions . AllMethodsOnArityZero | LookupOptions . MustNotBeParameter ;
262
+ CompoundUseSiteInfo < AssemblySymbol > useSiteInfo = this . GetNewCompoundUseSiteInfo ( diagnostics ) ;
263
+ ArrayBuilder < Symbol > ? sortedSymbolsBuilder = null ;
264
+
265
+ foreach ( var nested in container . GetTypeMembers ( ) )
266
+ {
267
+ if ( ! nested . IsExtension || nested . Arity != extensionArity || nested . ExtensionParameter is null )
268
+ {
269
+ continue ;
270
+ }
271
+
272
+ var constructedNested = ( NamedTypeSymbol ) ConstructWithCrefTypeParameters ( extensionArity , extensionTypeArguments , nested ) ;
273
+
274
+ var candidateExtensionSignature = new SignatureOnlyMethodSymbol (
275
+ methodKind : MethodKind . Ordinary ,
276
+ typeParameters : IndexedTypeParameterSymbol . TakeSymbols ( constructedNested . Arity ) ,
277
+ parameters : [ constructedNested . ExtensionParameter ] ,
278
+ callingConvention : Cci . CallingConvention . Default ,
279
+ // These are ignored by this specific MemberSignatureComparer.
280
+ containingType : null ,
281
+ name : null ,
282
+ refKind : RefKind . None ,
283
+ isInitOnly : false ,
284
+ isStatic : false ,
285
+ returnType : default ,
286
+ refCustomModifiers : [ ] ,
287
+ explicitInterfaceImplementations : [ ] ) ;
288
+
289
+ ImmutableArray < ParameterSymbol > extensionParameterSymbols = syntax . Parameters is { } extensionParameterListSyntax ? BindCrefParameters ( extensionParameterListSyntax , diagnostics ) : default ;
290
+
291
+ var providedExtensionSignature = new SignatureOnlyMethodSymbol (
292
+ methodKind : MethodKind . Ordinary ,
293
+ typeParameters : IndexedTypeParameterSymbol . TakeSymbols ( constructedNested . Arity ) ,
294
+ parameters : extensionParameterSymbols ,
295
+ callingConvention : Cci . CallingConvention . Default ,
296
+ // These are ignored by this specific MemberSignatureComparer.
297
+ containingType : null ,
298
+ name : null ,
299
+ refKind : RefKind . None ,
300
+ isInitOnly : false ,
301
+ isStatic : false ,
302
+ returnType : default ,
303
+ refCustomModifiers : [ ] ,
304
+ explicitInterfaceImplementations : [ ] ) ;
305
+
306
+ if ( ! MemberSignatureComparer . CrefComparer . Equals ( candidateExtensionSignature , providedExtensionSignature ) )
307
+ {
308
+ continue ;
309
+ }
310
+
311
+ var candidates = constructedNested . GetMembers ( name ) ;
312
+
313
+ foreach ( var candidate in candidates )
314
+ {
315
+ if ( ! SourceMemberContainerTypeSymbol . IsAllowedExtensionMember ( candidate ) )
316
+ {
317
+ continue ;
318
+ }
319
+
320
+ if ( arity != 0 && candidate . GetArity ( ) != arity )
321
+ {
322
+ continue ;
323
+ }
324
+
325
+ // Note: we bypass the arity check here, as it would check for total arity (extension + member arity)
326
+ SingleLookupResult result = this . CheckViability ( candidate , arity : 0 , options , accessThroughType : null , diagnose : true , useSiteInfo : ref useSiteInfo ) ;
327
+
328
+ if ( result . Kind == LookupResultKind . Viable )
329
+ {
330
+ sortedSymbolsBuilder ??= ArrayBuilder < Symbol > . GetInstance ( ) ;
331
+ sortedSymbolsBuilder . Add ( result . Symbol ) ;
332
+ }
333
+ }
334
+ }
335
+
336
+ diagnostics . Add ( syntax , useSiteInfo ) ;
337
+
338
+ if ( sortedSymbolsBuilder is null )
339
+ {
340
+ return ImmutableArray < Symbol > . Empty ;
341
+ }
342
+
343
+ // Since we resolve ambiguities by just picking the first symbol we encounter,
344
+ // the order of the symbols matters for repeatability.
345
+ if ( sortedSymbolsBuilder . Count > 1 )
346
+ {
347
+ sortedSymbolsBuilder . Sort ( ConsistentSymbolOrder . Instance ) ;
348
+ }
349
+
350
+ return sortedSymbolsBuilder . ToImmutableAndFree ( ) ;
351
+ }
352
+ }
353
+
219
354
// NOTE: not guaranteed to be a method (e.g. class op_Addition)
220
355
// NOTE: constructor fallback logic applies
221
356
private ImmutableArray < Symbol > BindOperatorMemberCref ( OperatorMemberCrefSyntax syntax , NamespaceOrTypeSymbol ? containerOpt , out Symbol ? ambiguityWinner , BindingDiagnosticBag diagnostics )
0 commit comments