@@ -7970,7 +7970,7 @@ private BoundExpression MakeMemberAccessValue(BoundExpression expr, BindingDiagn
7970
7970
{
7971
7971
var methodGroup = (BoundMethodGroup)expr;
7972
7972
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
7973
- var resolution = this.ResolveMethodGroup(methodGroup, analyzedArguments: null, useSiteInfo: ref useSiteInfo, options: OverloadResolution.Options.None);
7973
+ var resolution = this.ResolveMethodGroup(methodGroup, analyzedArguments: null, useSiteInfo: ref useSiteInfo, options: OverloadResolution.Options.None, acceptOnlyMethods: true );
7974
7974
Debug.Assert(!resolution.IsNonMethodExtensionMember(out _));
7975
7975
diagnostics.Add(expr.Syntax, useSiteInfo);
7976
7976
if (!expr.HasAnyErrors)
@@ -8851,60 +8851,6 @@ private bool AllowRefOmittedArguments(BoundExpression receiver)
8851
8851
}
8852
8852
#nullable disable
8853
8853
8854
- private void PopulateExtensionMethodsFromSingleBinder(
8855
- ExtensionScope scope,
8856
- MethodGroup methodGroup,
8857
- SyntaxNode node,
8858
- BoundExpression left,
8859
- string rightName,
8860
- ImmutableArray<TypeWithAnnotations> typeArgumentsWithAnnotations,
8861
- BindingDiagnosticBag diagnostics)
8862
- {
8863
- int arity;
8864
- if (typeArgumentsWithAnnotations.IsDefault)
8865
- {
8866
- arity = 0;
8867
- }
8868
- else
8869
- {
8870
- arity = typeArgumentsWithAnnotations.Length;
8871
- }
8872
-
8873
- var lookupResult = LookupResult.GetInstance();
8874
- CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
8875
- this.LookupExtensionMethods(lookupResult, scope, rightName, arity, ref useSiteInfo);
8876
- diagnostics.Add(node, useSiteInfo);
8877
-
8878
- if (lookupResult.IsMultiViable)
8879
- {
8880
- Debug.Assert(lookupResult.Symbols.Any());
8881
- var members = ArrayBuilder<Symbol>.GetInstance();
8882
- bool wasError;
8883
- Symbol symbol = GetSymbolOrMethodOrPropertyGroup(lookupResult, node, rightName, arity, members, diagnostics, out wasError, qualifierOpt: null);
8884
- Debug.Assert((object)symbol == null);
8885
- Debug.Assert(members.Count > 0);
8886
- methodGroup.PopulateWithExtensionMethods(left, members, typeArgumentsWithAnnotations, lookupResult.Kind);
8887
- members.Free();
8888
- }
8889
-
8890
- lookupResult.Free();
8891
- }
8892
-
8893
- private void LookupExtensionMethods(LookupResult lookupResult, ExtensionScope scope, string rightName, int arity, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
8894
- {
8895
- LookupOptions options;
8896
- if (arity == 0)
8897
- {
8898
- options = LookupOptions.AllMethodsOnArityZero;
8899
- }
8900
- else
8901
- {
8902
- options = LookupOptions.Default;
8903
- }
8904
-
8905
- this.LookupExtensionMethodsInSingleBinder(scope, lookupResult, rightName, arity, options, ref useSiteInfo);
8906
- }
8907
-
8908
8854
protected BoundExpression BindFieldAccess(
8909
8855
SyntaxNode node,
8910
8856
BoundExpression receiver,
@@ -10520,7 +10466,8 @@ void makeCall(SyntaxNode syntax, BoundExpression receiver, MethodSymbol method,
10520
10466
indexerOrSliceAccess = BindMethodGroupInvocation(syntax, syntax, method.Name, boundMethodGroup, analyzedArguments,
10521
10467
diagnostics, queryClause: null, ignoreNormalFormIfHasValidParamsParameter: true, anyApplicableCandidates: out bool _,
10522
10468
disallowExpandedNonArrayParams: false,
10523
- acceptOnlyMethods: true).MakeCompilerGenerated(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test effect of acceptOnlyMethods value
10469
+ acceptOnlyMethods: true) // acceptOnlyMethods is not relevant since we won't search extensions
10470
+ .MakeCompilerGenerated();
10524
10471
10525
10472
analyzedArguments.Free();
10526
10473
}
@@ -10638,6 +10585,7 @@ internal MethodGroupResolution ResolveMethodGroup(
10638
10585
AnalyzedArguments analyzedArguments,
10639
10586
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo,
10640
10587
OverloadResolution.Options options,
10588
+ bool acceptOnlyMethods,
10641
10589
RefKind returnRefKind = default,
10642
10590
TypeSymbol returnType = null,
10643
10591
in CallingConventionInfo callingConventionInfo = default)
@@ -10649,7 +10597,7 @@ internal MethodGroupResolution ResolveMethodGroup(
10649
10597
return ResolveMethodGroup(
10650
10598
node, node.Syntax, node.Name, analyzedArguments, ref useSiteInfo,
10651
10599
options,
10652
- acceptOnlyMethods: true, // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Confirm this value is appropriate for all consumers of the enclosing method and test effect of this value for all of them
10600
+ acceptOnlyMethods: acceptOnlyMethods,
10653
10601
returnRefKind: returnRefKind, returnType: returnType,
10654
10602
callingConventionInfo: callingConventionInfo);
10655
10603
}
@@ -10900,22 +10848,50 @@ private MethodGroupResolution ResolveDefaultMethodGroup(
10900
10848
}
10901
10849
}
10902
10850
10903
- if (node.ReceiverOpt is not BoundTypeExpression && node. SearchExtensions)
10851
+ if (node.SearchExtensions)
10904
10852
{
10905
- var receiver = node.ReceiverOpt!;
10853
+ Debug.Assert(node.ReceiverOpt!.Type is not null); // extensions are only considered on member access
10854
+
10855
+ BoundExpression receiver = node.ReceiverOpt;
10856
+ ImmutableArray<TypeWithAnnotations> typeArguments = node.TypeArgumentsOpt;
10857
+ int arity = typeArguments.IsDefaultOrEmpty ? 0 : typeArguments.Length;
10858
+ LookupOptions options = arity == 0 ? LookupOptions.AllMethodsOnArityZero : LookupOptions.Default;
10859
+ var singleLookupResults = ArrayBuilder<SingleLookupResult>.GetInstance();
10860
+ CompoundUseSiteInfo<AssemblySymbol> discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
10861
+
10906
10862
foreach (var scope in new ExtensionScopes(this))
10907
10863
{
10908
10864
methods.Clear();
10909
- var methodGroup = MethodGroup.GetInstance();
10910
- PopulateExtensionMethodsFromSingleBinder(scope, methodGroup, node.Syntax, receiver, node.Name, node.TypeArgumentsOpt, BindingDiagnosticBag.Discarded);
10911
- foreach (var m in methodGroup.Methods)
10865
+ singleLookupResults.Clear();
10866
+ scope.Binder.EnumerateAllExtensionMembersInSingleBinder(singleLookupResults, node.Name, arity, options, originalBinder: this, ref discardedUseSiteInfo, ref discardedUseSiteInfo);
10867
+
10868
+ foreach (SingleLookupResult singleLookupResult in singleLookupResults)
10912
10869
{
10913
- if (m.ReduceExtensionMethod(receiver.Type, Compilation) is { } reduced)
10870
+ Symbol extensionMember = singleLookupResult.Symbol;
10871
+ if (IsStaticInstanceMismatchForUniqueSignatureFromMethodGroup(receiver, extensionMember))
10914
10872
{
10915
- methods.Add(reduced);
10873
+ // Remove static/instance mismatches
10874
+ continue;
10875
+ }
10876
+
10877
+ // Note: we only care about methods. If the expression resolved to a non-method extension member, we wouldn't get here to compute the function type for the expression.
10878
+ if (extensionMember is MethodSymbol m)
10879
+ {
10880
+ if (m.GetIsNewExtensionMember())
10881
+ {
10882
+ // Note: new extension methods are subject to more stringent checks
10883
+ var substituted = (MethodSymbol?)extensionMember.GetReducedAndFilteredSymbol(typeArguments, receiver.Type, Compilation, checkFullyInferred: true);
10884
+ if (substituted is not null)
10885
+ {
10886
+ methods.Add(substituted);
10887
+ }
10888
+ }
10889
+ else if (m.ReduceExtensionMethod(receiver.Type, Compilation) is { } reduced)
10890
+ {
10891
+ methods.Add(reduced);
10892
+ }
10916
10893
}
10917
10894
}
10918
- methodGroup.Free();
10919
10895
10920
10896
if (methods.Count == 0)
10921
10897
{
@@ -10926,6 +10902,7 @@ private MethodGroupResolution ResolveDefaultMethodGroup(
10926
10902
{
10927
10903
methods.Free();
10928
10904
useParams = false;
10905
+ singleLookupResults.Free();
10929
10906
return null;
10930
10907
}
10931
10908
@@ -10936,6 +10913,7 @@ private MethodGroupResolution ResolveDefaultMethodGroup(
10936
10913
{
10937
10914
methods.Free();
10938
10915
useParams = false;
10916
+ singleLookupResults.Free();
10939
10917
return null;
10940
10918
}
10941
10919
@@ -10948,10 +10926,13 @@ private MethodGroupResolution ResolveDefaultMethodGroup(
10948
10926
{
10949
10927
methods.Free();
10950
10928
useParams = false;
10929
+ singleLookupResults.Free();
10951
10930
return null;
10952
10931
}
10953
10932
}
10954
10933
}
10934
+
10935
+ singleLookupResults.Free();
10955
10936
}
10956
10937
10957
10938
methods.Free();
@@ -10989,6 +10970,17 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate)
10989
10970
}
10990
10971
}
10991
10972
10973
+ private static bool IsStaticInstanceMismatchForUniqueSignatureFromMethodGroup(BoundExpression receiver, Symbol extensionMember)
10974
+ {
10975
+ bool memberCountsAsStatic = extensionMember is MethodSymbol { IsExtensionMethod: true } ? false : extensionMember.IsStatic;
10976
+ return receiver switch
10977
+ {
10978
+ BoundTypeOrValueExpression => false,
10979
+ BoundTypeExpression => !memberCountsAsStatic,
10980
+ _ => memberCountsAsStatic,
10981
+ };
10982
+ }
10983
+
10992
10984
/// <summary>
10993
10985
/// For C# 13 onwards, returns one of the methods from the method group if all instance methods, or extension methods
10994
10986
/// in the nearest scope, have the same signature ignoring parameter names and custom modifiers.
@@ -11088,23 +11080,15 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate)
11088
11080
var methods = ArrayBuilder<MethodSymbol>.GetInstance(capacity: singleLookupResults.Count);
11089
11081
foreach (SingleLookupResult singleLookupResult in singleLookupResults)
11090
11082
{
11091
- // Remove static/instance mismatches
11092
11083
Symbol extensionMember = singleLookupResult.Symbol;
11093
- bool memberCountsAsStatic = extensionMember is MethodSymbol { IsExtensionMethod: true } ? false : extensionMember.IsStatic;
11094
- switch (node.ReceiverOpt)
11084
+ if (IsStaticInstanceMismatchForUniqueSignatureFromMethodGroup(receiver, extensionMember))
11095
11085
{
11096
- case BoundTypeOrValueExpression:
11097
- break;
11098
- case BoundTypeExpression:
11099
- if (!memberCountsAsStatic) continue;
11100
- break;
11101
- default:
11102
- if (memberCountsAsStatic) continue;
11103
- break;
11086
+ // Remove static/instance mismatches
11087
+ continue;
11104
11088
}
11105
11089
11106
11090
// Note: we only care about methods since we're already decided this is a method group (ie. not resolving to some other kind of extension member)
11107
- if (extensionMember is MethodSymbol method )
11091
+ if (extensionMember is MethodSymbol)
11108
11092
{
11109
11093
var substituted = (MethodSymbol?)extensionMember.GetReducedAndFilteredSymbol(typeArguments, receiver.Type, Compilation, checkFullyInferred: true);
11110
11094
if (substituted is not null)
0 commit comments