Skip to content

[VS] FOREACH VAR: resolve the loop variable of generic collections and arrays - #2129

Merged
RobertvanderHulst merged 1 commit into
X-Sharp:devfrom
hpetriffer:fix/foreach-var-generic-local
Oct 27, 2026
Merged

RobertvanderHulst merged 1 commit into
X-Sharp:devfrom
hpetriffer:fix/foreach-var-generic-local

Conversation

@hpetriffer

Copy link
Copy Markdown
Contributor

Problem

Member completion on the loop variable of FOREACH VAR showed nothing when the collection is a generic local or parameter:

LOCAL aList := System.Collections.Generic.List<Customer>{} AS System.Collections.Generic.List<Customer>
FOREACH VAR oItem IN aList
    ? oItem:     // no members
NEXT

Same for a Customer[] array (it offered the members of System.Array) and for Dictionary<STRING, Customer> (pair:Value: showed nothing).

Causes

  1. FindIdentifier replaced the TypeName of a local/parameter with the resolved type's full name. For a .NET generic that is List<T>, so GenericArgs became ["T"] and the element type T could not be found.
  2. ResolveImpliedCollection returned the array type for X[] instead of the element type.
  3. The element type was taken from the indexer. Dictionary<TKey,TValue>[TKey] returns TValue, but the loop enumerates KeyValuePair<TKey,TValue>.
  4. AdjustGenericMember only fixed the TypeName of the cloned member (QuickInfo was right). Completion reads ResolvedType, which was still resolved from TValue.

Changes (XSharpLookup.cs only)

  • Keep the declared type arguments when a local or parameter is resolved (WithTypeArguments, GetTypeArguments; type arguments are split at the top level and trimmed).
  • Arrays: the loop variable gets the element type.
  • Generic .NET collections: the element type comes from the IEnumerable<T> implementation in the metadata, with the collection's type arguments filled in. The indexer stays as the fallback.
  • After AdjustGenericMember, resolve the ResolvedType of the adjusted member.

Known limitation: the generic member adjustment still runs only at the end of a chain, so a deeper chain such as pair:Value:Name: is not covered yet.

Test

Checked manually in VS 2022 (experimental instance) with the old parser. Completion now lists the members of Customer for a local List<Customer>, a local Customer[], a parameter List<Customer> and pair:Value: over a Dictionary<STRING, Customer>. VAR x := Customer{} and FOR VAR n := 1 TO 3 work as before.

🤖 Generated with Claude Code

…d arrays

- FindIdentifier: keep the declared type arguments when a local or parameter is
  resolved (List<Customer> no longer becomes List<T>)
- ResolveImpliedCollection: element type of arrays instead of the array type;
  for generic .NET collections read the element type from IEnumerable<T> and
  substitute the type arguments (Dictionary -> KeyValuePair<TKey,TValue>)
- RetrieveElement: resolve the type of a member adjusted by AdjustGenericMember,
  so pair:Value: completes the members of TValue

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RobertvanderHulst
RobertvanderHulst merged commit 7da6a8b into X-Sharp:dev Oct 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants