Extension indexers: indexing with implicit indexers#82453
Extension indexers: indexing with implicit indexers#82453jcouv wants to merge 1 commit intodotnet:features/extensionsfrom
Conversation
9388e37 to
19e7436
Compare
|
This PR modifies public API files. Please follow the instructions at https://github.com/dotnet/roslyn/blob/main/docs/contributing/API%20Review%20Process.md for ensuring all public APIs are reviewed before merging. |
| if (arguments.Arguments.Count != 1) | ||
| { | ||
| return IndexOrRangeArgKind.None; | ||
| } | ||
|
|
||
| TypeSymbol? argType = arguments.Arguments[0].Type; | ||
| if (argType is null) |
There was a problem hiding this comment.
nit: could be simplified to
| if (arguments.Arguments.Count != 1) | |
| { | |
| return IndexOrRangeArgKind.None; | |
| } | |
| TypeSymbol? argType = arguments.Arguments[0].Type; | |
| if (argType is null) | |
| if (arguments.Arguments is not [{ Type: TypeSymbol argType }]) | |
| { | |
| return IndexOrRangeArgKind.None; | |
| } | |
| ``` #Pending |
| @@ -702,6 +701,41 @@ public int this[int i] | |||
|
|
|||
| [Fact] | |||
| public void Indexing_09() | |||
There was a problem hiding this comment.
Renumbering lots of tests seems unfortunate; next time consider some suffix like Indexing_09_02 to "insert" a new test.
| return IndexOrRangeArgKind.None; | ||
| } | ||
|
|
||
| if (TypeSymbol.Equals(argType, Compilation.GetWellKnownType(WellKnownType.System_Index), TypeCompareKind.ConsiderEverything)) |
| return IndexOrRangeArgKind.Index; | ||
| } | ||
|
|
||
| if (TypeSymbol.Equals(argType, Compilation.GetWellKnownType(WellKnownType.System_Range), TypeCompareKind.ConsiderEverything)) |
|
|
||
| lookupResult.Free(); | ||
| actualArguments?.Free(); | ||
| if (argKind != IndexOrRangeArgKind.None |
| LookupResultKind.Viable, hasErrors: false).MakeCompilerGenerated(); | ||
|
|
||
| lengthOrCountAccess = binder.CheckValue(lengthOrCountAccess, BindValueKind.RValue, diagnostics); | ||
|
|
There was a problem hiding this comment.
Would it be possible to extract a common helper to share with TryBindLengthOrCount?
There was a problem hiding this comment.
Reusing GetExtensionMemberAccess or its relevant portion would be fine too.
|
|
||
| candidates.Free(); | ||
| diagnostics.Add(syntax, useSiteInfo); | ||
| return lengthOrCountProperty is not null; |
There was a problem hiding this comment.
It looks like we are grabbing the first found property and ignoring any ambiguity across same name properties. Also, it looks like we are not checking applicability to the receiver type. It feels like we need to collect a group. run an overload resolution process to get a best match, then create a BoundPropertyAccess (hopefully reusing GetExtensionMemberAccess or its relevant portion). #Pending
There was a problem hiding this comment.
BTW, a true ambiguity should probably prevent us from going to the next scope.
There was a problem hiding this comment.
I hope we will be able to share/reuse code that deals with binding extension properties explicitly used in code.
There was a problem hiding this comment.
You're right. I missed the generic cases. Noticed the problem while working on next PR. Unlike instance scenario, we need to do overload resolution, that will give us type inference and deal with ambiguities
| candidates, WellKnownMemberNames.Indexer, alternativeName: null, | ||
| arity, lookupOptions, originalBinder: binder); | ||
|
|
||
| var intPlaceholder = new BoundImplicitIndexerValuePlaceholder(syntax, binder.Compilation.GetSpecialType(SpecialType.System_Int32)) { WasCompilerGenerated = true }; |
| analyzedArguments.Free(); | ||
| candidates.Free(); | ||
| diagnostics.Add(syntax, useSiteInfo); | ||
| return indexerOrSliceAccess is not null; |
| var properties = ArrayBuilder<PropertySymbol>.GetInstance(); | ||
| properties.Add(property); | ||
|
|
||
| OverloadResolutionResult<PropertySymbol> overloadResolutionResult = resolveIndexers(receiver, properties, binder, analyzedArguments, ref actualArguments, ref useSiteInfo); |
There was a problem hiding this comment.
OverloadResolutionResult overloadResolutionResult = resolveIndexers(receiver, properties, binder, analyzedArguments, ref actualArguments, ref useSiteInfo);
It feels like we should be able to share/reuse tryBindExtensionIndexersInScope or at least significant portion of it, rather than duplicating the logic here and below. #Pending
|
|
||
| candidates.Free(); | ||
| diagnostics.Add(syntax, useSiteInfo); | ||
| return true; |
| { | ||
| if (TryBindExtensionIndexer(syntax, receiver, analyzedArguments, diagnostics, out BoundIndexerAccess extensionIndexerAccess)) | ||
| if (TryBindNonExtensionImplicitIndexer(syntax, receiver, analyzedArguments, diagnostics, out var fallbackIndexerAccess) | ||
| || TryBindExtensionIndexer(syntax, receiver, analyzedArguments, diagnostics, out fallbackIndexerAccess)) |
There was a problem hiding this comment.
| } | ||
| } | ||
|
|
||
| internal static bool MethodHasValidSliceSignature(MethodSymbol method) |
| /// Checks whether a property has a valid getter for the Length/Count implicit indexer pattern | ||
| /// (non-static, returns int, non-ref). | ||
| /// </summary> | ||
| private static bool IsValidLengthOrCountGetter(PropertySymbol property) |
| /// Checks whether a property is a valid implicit Index indexer | ||
| /// (single int parameter, non-ref, non-static). | ||
| /// </summary> | ||
| private static bool IsValidImplicitIndexIndexer(PropertySymbol property) |
|
Done with review pass (commit 1) |
The implicit indexers in list-patterns will be addressed in a follow-up.
I'm still working on updating the spec. In short, for each scope (ie. instance, then iterative extension scopes), we look for real indexer and fall back to implicit indexer.
This means that both parts of an implicit indexer must be present in the same scope to count.
Relates to test plan #81505