Visitar URL original
Do not complete shadowed intrinsic extension methods on objects with type-native methods by senutpal · Pull Request #28019 · PowerShell/PowerShell · GitHub
Skip to content

Do not complete shadowed intrinsic extension methods on objects with type-native methods - #28019

Open
Utpal Sen (senutpal) wants to merge 2 commits into
PowerShell:masterfrom
senutpal:fix-shadowed-intrinsic-methods
Open

Utpal Sen (senutpal) wants to merge 2 commits into
PowerShell:masterfrom
senutpal:fix-shadowed-intrinsic-methods

Conversation

@senutpal

Copy link
Copy Markdown

PR Summary

Fixes PowerShell issue #24729 where tab completion unexpectedly returned intrinsic extension methods (.ForEach(), .Where(), .PSForEach(), .PSWhere()) on objects that already define a type-native method of the same name (e.g., System.Collections.Generic.List<T>.ForEach(Action<T>)).

Since PowerShell's method invocation binder always resolves and invokes the type-native method rather than the intrinsic extension method when a name collision occurs, offering the intrinsic method in tab completion is redundant and misleading.

With this change:

  • CompleteExtensionMethods checks whether a method of the same name already exists in results (or among hiddenMethods for instance reflection) using case-insensitive ordinal matching. If present, the intrinsic method is omitted.
  • Properties with matching names (e.g., a custom collection with a ForEach property) are not treated as collisions, allowing both the property and intrinsic method to be completed.
  • In CompleteMemberByInferredType, intrinsic extension method completion is executed once after iterating all inferred types (rather than per-type within the loop), and is skipped for static member completion requests (!isStatic).

PR Context

When working with collections that define their own methods with names matching PowerShell intrinsic extension methods (like List<T>.ForEach), users pressing <Tab> on $list.For received duplicate completion suggestions for ForEach(:

  1. Native method: void ForEach(System.Action[System.Object] action)
  2. Intrinsic method: ForEach(expression [, arguments...])

Attempting to invoke the intrinsic method signature results in a method binding failure because the type-native method takes precedence in PowerShell's binder. Filtering out shadowed intrinsic extension methods aligns tab completion with runtime dispatch behavior and eliminates confusion.

PR Checklist

PowerShell provides intrinsic extension methods (.ForEach(), .Where(),
.PSForEach(), .PSWhere()) on enumerable collections. When an object
type already implements a native method of the same name (such as
List<T>.ForEach(Action<T>)), PowerShell's dynamic binder always invokes
the type-native method, making the intrinsic method unreachable.

However, tab completion on instantiated collections returned both the
type-native method and the intrinsic method, causing confusion.

Update CompleteExtensionMethods to inspect the completion results list
before injecting intrinsic extension methods. If a method with the same
name already exists in the results (or among hidden type methods), the
intrinsic method candidate is skipped. Properties sharing the same name
are not treated as collisions and continue to be offered alongside the
method. Additionally, in CompleteMemberByInferredType, move extension
method completion outside the inferred type iteration loop and guard
against static member completion.

Fixes PowerShell#24729
Copilot AI lite review requested due to automatic review settings September 16, 2026 05:11
@senutpal
Utpal Sen (senutpal) requested a review from a team as a code owner September 16, 2026 05:11
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@senutpal

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Hidden native methods are not enumerated, so shadowed intrinsic completions may still appear.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Updates PowerShell tab completion to suppress intrinsic methods shadowed by native methods.

Changes:

  • Filters conflicting intrinsic extension methods.
  • Defers inferred-type completion and skips static members.
  • Adds regression coverage.
File summaries
File Summary
test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 Adds tab-completion regression tests.
src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs Implements collision-aware extension method completion.
Review details

Suppressed comments (4)

src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs:8824

  • PSObject.AsPSObject(value).Members is enumerated with MshMemberMatchOptions.None, so its enumerator skips every IsHidden member. This loop therefore can never populate hiddenMethods; a hidden native ForEach/Where method can still leave the intrinsic completion visible. Query the member collection with Members.Match(..., MshMemberMatchOptions.IncludeHidden) (or otherwise enumerate hidden adapter members) and add a regression test for this path.
                        if (memberInfo is PSMethodInfo)
                        {
                            hiddenMethods ??= new List<string>();
                            hiddenMethods.Add(memberInfo.Name);
                        }

test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1:167

  • $matches is PowerShell's automatic regex-capture variable, so assigning the completion results to it can overwrite state used by later -match operations and violates the repository's automatic-variable naming rule. Use a descriptive name such as $completionMatches throughout this assertion block.
            $forMatches = (TabExpansion2 -inputScript '[System.Collections.Generic.List[object]]::new().For').CompletionMatches

test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1:226

  • $matches is PowerShell's automatic regex-capture variable, so assigning the completion results to it can overwrite state used by later -match operations and violates the repository's automatic-variable naming rule. Use a descriptive name such as $completionMatches throughout this assertion block.
            $cMatches = (TabExpansion2 -inputScript '$c.For').CompletionMatches

test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1:248

  • $matches is PowerShell's automatic regex-capture variable, so assigning the completion results to it can overwrite state used by later -match operations and violates the repository's automatic-variable naming rule. Use a descriptive name such as $completionMatches throughout this assertion block.
            $whereMatches = (TabExpansion2 -inputScript '$c.Wh').CompletionMatches
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 Outdated
@senutpal
Utpal Sen (senutpal) requested a balanced review from Copilot September 16, 2026 16:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@microsoft-github-policy-service microsoft-github-policy-service Bot added the Review - Needed The PR is being reviewed label Sep 25, 2026
@microsoft-github-policy-service

Copy link
Copy Markdown
Contributor

This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days.
Maintainer, please provide feedback and/or mark it as Waiting on Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Review - Needed The PR is being reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants