Skip to content

Conversation

@jcouv
Copy link
Member

@jcouv jcouv commented Dec 9, 2025

The main changes in this PR:

  • addition of TryBindExtensionIndexer
  • add new feature IDS_FeatureExtensionIndexers and check LangVer (declarations and usages, see CheckExtensionMembers and ReportDiagnosticsIfDisallowedExtensionIndexer)
  • round-tripping of indexers in metadata: emit DefaultMemberAttribute on grouping type, which contains the extension indexer(s)
  • simplify RefSafetyAnalysis.MethodInfo and fix analysis of assignments

Identified some follow-ups around nullability, CREF binding, GetMemberGroup, "countable" properties, interpolation handlers, improving diagnostic quality, an existing crash with a method named this[] in metadata.

The usages of extension indexers: element access, null-conditional indexing and assignment, list and spread pattern, and object initializer.

Corresponding spec is incoming here. Some notable choices (will confirm with LDM):

  • extension indexers come before implicit indexers
  • no extension indexers on base

Fixes #81796 which was opened for tracking purpose

Relates to test plan #81505

@jcouv jcouv self-assigned this Dec 9, 2025
@jcouv jcouv changed the title Extension indexers: resolve extension indexers Extension indexers: allow and resolve extension indexers Dec 9, 2025
@jcouv jcouv force-pushed the extension-indexers branch 3 times, most recently from 37bad6f to 344fa46 Compare December 9, 2025 22:59
@jcouv jcouv force-pushed the extension-indexers branch from 344fa46 to f504f6e Compare December 9, 2025 23:28
@jcouv jcouv marked this pull request as ready for review December 10, 2025 02:19
@jcouv jcouv requested a review from a team as a code owner December 10, 2025 02:19
@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 10, 2025

It looks like the speclet says nothing about consumption scenarios #Closed

@jcouv
Copy link
Member Author

jcouv commented Dec 10, 2025

It looks like the speclet says nothing about consumption scenarios

Yes, the speclet was not yet updated.

Corresponding spec is incoming. Some notable choices (will confirm with LDM):

  • extension indexers come before implicit indexers
  • no extension indexers on base

if (member is PropertySymbol)
{
diagnostics.Add(new CSDiagnostic(
new CSDiagnosticInfo(ErrorCode.ERR_ExtensionDisallowsIndexerMember, new CSharpRequiredLanguageVersion(MessageID.IDS_FeatureExtensionIndexers.RequiredVersion())),
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new CSDiagnosticInfo(ErrorCode.ERR_ExtensionDisallowsIndexerMember, new CSharpRequiredLanguageVersion(MessageID.IDS_FeatureExtensionIndexers.RequiredVersion())),

I think we can use regular language version error here #Closed

if (!IsAllowedExtensionMember(member, languageVersion))
{
diagnostics.Add(ErrorCode.ERR_ExtensionDisallowsMember, member.GetFirstLocation());
if (member is PropertySymbol)
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

member is PropertySymbol

It looks like we are assuming that this is an indexer. Consider asserting the fact, or including this as a condition. #Closed

<data name="IDS_FeatureExtensionIndexers" xml:space="preserve">
<value>extension indexers</value>
</data>
<data name="ERR_ExtensionDisallowsIndexerMember" xml:space="preserve">
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ERR_ExtensionDisallowsIndexerMember

It doesn't look like it is necessary to have this error, a regular language version error for the feature would work as good. #Closed


internal static void ReportDiagnosticsIfDisallowedExtensionIndexer(BindingDiagnosticBag diagnostics, PropertySymbol property, SyntaxNode syntax)
{
if (property.IsExtensionBlockMember())
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

property

How do we know that this is an indexer? #Closed

var setMethod = replace(SetMethod);
Symbol symbol = ReferenceEquals(Symbol, Method) && method is not null ? method : Symbol;

Debug.Assert(SetMethod?.IsExtensionBlockMember() != true);
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug.Assert(SetMethod?.IsExtensionBlockMember() != true);

It is not obvious why this condition was correct for properties and isn't for indexers. Is it still worth asserting for properties? #Closed

Copy link
Member Author

@jcouv jcouv Dec 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We analyze the invocation for property access and indexer access in VisitPropertyAccess, VisitIndexerAccess and VisitAssignmentOperator.
In VisitPropertyAccess there is no argument mixing to analyze.
In VisitAssignmentOperator we only analyze the setter (so Method is set, but SetMethod isn't).
This PR added argument mixing handling to VisitIndexerAccess, which is the first time we'd hit this logic with extension member get and set both used.

I have to say I found this Method/SetMethod design confusing. So I made a separate commit that simplifies this design by removing SetMethod. For a given scenario, we can always analyze arg mixing for one specific method, we don't need two.
After that change, we analyze the getter invocation in VisitIndexerAccess (when a getter is involved) and we analyze the setter invocation in VisitAssignmentOperator (which we were already doing previously, but only for extension members).
The only downside is that for certain operations (increment operator) we now report two sets of diagnostics: one for the getter and one for the setter, so there's a bit of redundancy. I think that's okay, but I could leave a PROTOTYPE to further refine if you want.
This also fixed spurious diagnostics (see removed diagnostics in RefEscapingTests.cs

lookupResult, WellKnownMemberNames.Indexer, arity: 0, LookupOptions.AllMethodsOnArityZero,
originalBinder: binder, useSiteInfo: ref useSiteInfo);

if (!lookupResult.IsMultiViable || lookupResult.Symbols.All(s => s.Kind != SymbolKind.Property))
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s.Kind != SymbolKind.Property

Should we also check that it is an indexer? #Closed


MemberResolutionResult<PropertySymbol> resolutionResult = result.ValidResult;
Debug.Assert(resolutionResult.Result.ConversionForArg(0).Exists);
resolutionResult = resolutionResult.WithResult(resolutionResult.Result.WithoutReceiverArgument());
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolutionResult = resolutionResult.WithResult(resolutionResult.Result.WithoutReceiverArgument());

Since we are dropping receiver argument, should argumentNames and argumentRefKinds be adjusted as well? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. We're getting argumentNames and argumentRefKinds from analyzedArguments (not from actualArguments or some other source).

ArrayBuilder<PropertySymbol>? properties = null;
foreach (var member in lookupResult.Symbols)
{
if (member is PropertySymbol property)
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

member is PropertySymbol property

Should we check that it is an indexer? #Closed


overloadResolutionResult.Free();
return propertyAccess;
bool isNewExtensionMember = property.IsExtensionBlockMember();
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isNewExtensionMember

Consider avoiding using "newExtension" term. All features are new only until they are shipped. Consider using terms that "survive" long term #Closed


// Make sure that the result of overload resolution is valid.
var gotError = MemberGroupFinalValidationAccessibilityChecks(receiver, property, syntax, diagnostics, invokedAsExtensionMethod: false);
private BoundExpression BindIndexerOrIndexedPropertyAccessContinued(SyntaxNode syntax, BoundExpression receiver, AnalyzedArguments analyzedArguments, BindingDiagnosticBag diagnostics, ImmutableArray<string> argumentNames, ImmutableArray<RefKind> argumentRefKinds, MemberResolutionResult<PropertySymbol> resolutionResult)
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BoundExpression

BoundIndexerAccess? #Closed

Binder binder,
ExtensionScope scope,
BindingDiagnosticBag diagnostics,
out BoundExpression? extensionIndexerAccess)
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BoundExpression

BoundIndexerAccess? #Closed

}
}

private bool TryBindExtensionIndexer(SyntaxNode syntax, BoundExpression left, AnalyzedArguments analyzedArguments, BindingDiagnosticBag diagnostics, [NotNullWhen(true)] out BoundExpression? extensionIndexerAccess)
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BoundExpression

BoundIndexerAccess? #Closed

singleLookupResults.Free();
}

private void LookupAllNewExtensionMembersInSingleBinder(LookupResult result, string? name,
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NewExtensionMembers

Similar comment for "NewExtensionMembers" term. It looks like this is applicable to other new APIs. #Closed

int arity, LookupOptions options, Binder originalBinder, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
var singleLookupResults = ArrayBuilder<SingleLookupResult>.GetInstance();
EnumerateAllNewExtensionMembersInSingleBinder(singleLookupResults, name, arity, options, originalBinder, ref useSiteInfo);
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EnumerateAllNewExtensionMembersInSingleBinder

Consider asserting that result is null. Or conditionally Free it. #Closed

CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = binder.GetNewCompoundUseSiteInfo(diagnostics);

scope.Binder.LookupAllNewExtensionMembersInSingleBinder(
lookupResult, WellKnownMemberNames.Indexer, arity: 0, LookupOptions.AllMethodsOnArityZero,
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LookupOptions.AllMethodsOnArityZero

Do we actually expect to get back any methods? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could get a method back. The logic below filters them out.

But your question made me realize we were misusing the AllMethodsOnArityZero flag, so removed it here.
For methods and named types, explicit type arguments can be used, so we need the option of looking up with or without explicit arity. But for properties/indexers, we don't need such an option. Adjusted extensionMemberMatches in NamedTypeSymbol.GetExtensionMembers to always find properties/indexers from generic extension blocks given arity zero.

return;
case BoundIndexerAccess { Indexer.SetMethod: { } indexerSet } indexer when indexerSet.IsExtensionBlockMember():
methodInvocationInfo = MethodInvocationInfo.FromIndexerAccess(indexer);
Debug.Assert(ReferenceEquals(methodInvocationInfo.MethodInfo.Method, indexerSet));
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug.Assert(ReferenceEquals(methodInvocationInfo.MethodInfo.Method, indexerSet));

What method do we get now? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We get an identical but not reference-equal method. SubstitutedPropertySymbol.GetMethod and .SetMethod return new symbols every time they are called.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's assert the equality fact then?

// Tracked by https://github.com/dotnet/roslyn/issues/71056
AddPlaceholderReplacement(argumentPlaceholder, integerArgument);
// https://github.com/dotnet/roslyn/issues/78829 - Do we need to do something special for recievers of extension indexers here?
Debug.Assert(!indexerAccess.Indexer.IsExtensionBlockMember());
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug.Assert(!indexerAccess.Indexer.IsExtensionBlockMember());

This looks suspicious. Are we saying that extension indexers will not participate in implicit indexing pattern? #Closed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the comment is useful until the design decision is pending. Consider transforming it into a PROTOTYPE comment, if you simply want to remove a link to the issue.

Debug.Assert(member.Kind != SymbolKind.NamedType);
if (member.Kind == SymbolKind.Method
&& (options & LookupOptions.AllMethodsOnArityZero) == 0
&& arity != member.GetMemberArityIncludingExtension())
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arity != member.GetMemberArityIncludingExtension()

Shouldn't non methods be filtered by this condition as well? #Closed

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 22, 2025

    CreateCompilation([src, libSrc], parseOptions: TestOptions.Regular14).VerifyEmitDiagnostics(

Are we covering RegularNext and Preview for this scenario? #Closed


Refers to: src/Compilers/CSharp/Test/CSharp15/ExtensionIndexersTests.cs:63 in 8dad62b. [](commit_id = 8dad62b, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 22, 2025

    var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.Skipped).VerifyDiagnostics();

Why is verification skipped? #Resolved


Refers to: src/Compilers/CSharp/Test/CSharp15/ExtensionIndexersTests.cs:2598 in 8dad62b. [](commit_id = 8dad62b, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 22, 2025

    var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("False ^1 False"), verify: Verification.Skipped).VerifyDiagnostics();

Why is verification skipped? #Resolved


Refers to: src/Compilers/CSharp/Test/CSharp15/ExtensionIndexersTests.cs:2654 in 8dad62b. [](commit_id = 8dad62b, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 22, 2025

    var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("1..^0"), verify: Verification.Skipped).VerifyDiagnostics();

Why is verification skipped? #Resolved


Refers to: src/Compilers/CSharp/Test/CSharp15/ExtensionIndexersTests.cs:2763 in 8dad62b. [](commit_id = 8dad62b, deletion_comment = False)


comp = CreateCompilation(src, targetFramework: TargetFramework.Net70);
comp.VerifyEmitDiagnostics();
CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.Skipped).VerifyDiagnostics();
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verification.Skipped

Why is verification skipped?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I skipped verification in tests that use .NET Core TFM and therefore cannot verify on Desktop.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use FailsPEVerify instead?


var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100);
comp.VerifyEmitDiagnostics(
// (5,21): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// (5,21): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope

Shouldn't we get the same error for an assignment through property? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do. I think that was already tested elsewhere. It would be something like _ = s.P; with an already created instance of S (which determines its escape scope).
The object creation scenario however is different. The created instance has its escape scoped computed from the inputs, which includes initializers.


var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100);
comp.VerifyEmitDiagnostics(
// (5,25): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// (5,25): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope

Shouldn't we get the same error for an assignment through property? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Answered elsewhere


var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100);
comp.VerifyEmitDiagnostics(
// (5,21): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// (5,21): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope

Shouldn't we get the same error for an assignment through property? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Answered elsewhere

";
var comp = CreateCompilationWithMscorlibAndSpan(csharp, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), options: TestOptions.UnsafeDebugDll);
comp.VerifyDiagnostics(
// (11,15): warning CS9080: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// (11,15): warning CS9080: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope

Was the warning wrong? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the struct is readonly so all its members are readonly. A readonly accessor does not risk capturing and exposing the referenced variable.

";
var comp = CreateCompilationWithMscorlibAndSpan(csharp, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), options: TestOptions.UnsafeDebugDll);
comp.VerifyDiagnostics(
// (11,15): warning CS9080: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// (11,15): warning CS9080: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope

Was the warning wrong? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the property is readonly so the accessors are readonly

";
var comp = CreateCompilationWithMscorlibAndSpan(csharp, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), options: TestOptions.UnsafeDebugDll);
comp.VerifyDiagnostics(
// (11,15): warning CS9080: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// (11,15): warning CS9080: Use of variable 'x' in this context may expose referenced variables outside of their declaration scope

Was the warning wrong? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the setter is readonly

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 7)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Dec 22, 2025

It looks like some of the ref safety analysis changes are desirable regardless of extension indexers work. I think it would be good to open issues for corresponding scenarios and link them to this PR. Also, we should consider if it is worth making those changes in main directly #Closed

@jcouv
Copy link
Member Author

jcouv commented Dec 25, 2025

    var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.Skipped).VerifyDiagnostics();

Answered elsewhere


In reply to: 3684111329


Refers to: src/Compilers/CSharp/Test/CSharp15/ExtensionIndexersTests.cs:2598 in 8dad62b. [](commit_id = 8dad62b, deletion_comment = False)

@jcouv
Copy link
Member Author

jcouv commented Dec 25, 2025

    var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("False ^1 False"), verify: Verification.Skipped).VerifyDiagnostics();

Same


In reply to: 3684119503


Refers to: src/Compilers/CSharp/Test/CSharp15/ExtensionIndexersTests.cs:2654 in 8dad62b. [](commit_id = 8dad62b, deletion_comment = False)

@jcouv
Copy link
Member Author

jcouv commented Dec 25, 2025

    var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("1..^0"), verify: Verification.Skipped).VerifyDiagnostics();

Same


In reply to: 3684123771


Refers to: src/Compilers/CSharp/Test/CSharp15/ExtensionIndexersTests.cs:2763 in 8dad62b. [](commit_id = 8dad62b, deletion_comment = False)

@jcouv
Copy link
Member Author

jcouv commented Dec 25, 2025

Opened #81796
Given this was not reported by users, I'm fine to wait for the feature to get merged to main for the fix to flow.


In reply to: 3684255145

@jcouv jcouv requested review from a team and AlekseyTs December 25, 2025 18:08
Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (commit 8), with suggestion to use Verification.FailsPEVerify instead of Verification.Skipped

@jcouv
Copy link
Member Author

jcouv commented Dec 27, 2025

@dotnet/roslyn-compiler for second review. Someone comfortable with ref analysis would be great. Thanks

var accessorKind = property.RefKind == RefKind.None ? AccessorKind.Set : AccessorKind.Get;
var methodInfo = MethodInfo.Create(property, accessorKind);
bool isSet = property.RefKind == RefKind.None;
var accessorKind = isSet ? AccessorKind.Set : AccessorKind.Get;
Copy link
Member

@jjonescz jjonescz Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable looks unused. #Resolved

=> new MethodInvocationInfo
public static MethodInvocationInfo FromIndexerReadAccess(BoundIndexerAccess indexerAccess, BoundExpression? substitutedReceiver = null)
{
Debug.Assert(indexerAccess.AccessorKind != AccessorKind.Set);
Copy link
Member

@jjonescz jjonescz Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we get AccessorKind.Both here? Then this doesn't seem to be "indexer read access." #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Both" does include a read, so the name of the method seems okay. But I could rename to FromIndexerGetter to avoid any ambiguity...

OverloadResolutionResult<PropertySymbol> overloadResolutionResult = OverloadResolutionResult<PropertySymbol>.GetInstance();
// We don't consider when we're in default parameter values or attribute arguments so that we avoid cycles. This is an error scenario,
// so we don't care if we accidentally miss a parameter being applicable.
bool allowRefOmittedArguments = !InParameterDefaultValue && !InAttributeArgument && receiver.IsExpressionOfComImportType();
Copy link
Member

@jjonescz jjonescz Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the comment above this variable be removed too? #Resolved

Diagnostic(ErrorCode.ERR_CallArgMixing, "local[stackSpan]").WithArguments("S3.this[System.Span<int>]", "span").WithLocation(48, 9),
// (48,15): error CS8352: Cannot use variable 'stackSpan' in this context because it may expose referenced variables outside of their declaration scope
// local[stackSpan]++; // 4
Diagnostic(ErrorCode.ERR_EscapeVariable, "stackSpan").WithArguments("stackSpan").WithLocation(48, 15),
Copy link
Member

@jjonescz jjonescz Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we produce these duplicate diagnostics now? #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both the getter and the setter produce a diagnostic

@jcouv jcouv requested a review from jjonescz January 5, 2026 19:40
@jcouv jcouv merged commit 9efc8da into dotnet:features/extensions Jan 6, 2026
25 checks passed
@jcouv jcouv deleted the extension-indexers branch January 6, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants