diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 200eebbed32..e2220750606 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -708,7 +708,18 @@ private static List GetParameterCompletionResults(string param break; } - Diagnostics.Assert(matchedParameterName != null, "we should find matchedParameterName from the BoundArguments"); + if (matchedParameterName is null) + { + // The pseudo binder has skipped a parameter + // This will happen when completing parameters for commands with dynamic parameters. + result = GetParameterCompletionResults( + parameterName, + bindingInfo.ValidParameterSetsFlags, + bindingInfo.UnboundParameters, + withColon); + return result; + } + MergedCompiledCommandParameter param = bindingInfo.BoundParameters[matchedParameterName]; WildcardPattern pattern = WildcardPattern.Get(parameterName + "*", WildcardOptions.IgnoreCase); diff --git a/src/System.Management.Automation/engine/CommandCompletion/PseudoParameterBinder.cs b/src/System.Management.Automation/engine/CommandCompletion/PseudoParameterBinder.cs index a581a11af92..4201be16f93 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/PseudoParameterBinder.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/PseudoParameterBinder.cs @@ -982,7 +982,7 @@ internal PseudoBindingInfo DoPseudoParameterBinding(CommandAst command, Type pip executionContext.LanguageMode = PSLanguageMode.ConstrainedLanguage; } - _bindingEffective = PrepareCommandElements(executionContext); + _bindingEffective = PrepareCommandElements(executionContext, paramAstAtCursor); } finally { @@ -1189,7 +1189,7 @@ private void InitializeMembers() _duplicateParameters.Clear(); } - private bool PrepareCommandElements(ExecutionContext context) + private bool PrepareCommandElements(ExecutionContext context, CommandParameterAst paramAtCursor) { int commandIndex = 0; bool dotSource = _commandAst.InvocationOperator == TokenKind.Dot; @@ -1216,6 +1216,13 @@ private bool PrepareCommandElements(ExecutionContext context) // Pre-processing the arguments -- command arguments for (commandIndex++; commandIndex < _commandElements.Count; commandIndex++) { + if (implementsDynamicParameters && _commandElements[commandIndex] == paramAtCursor) + { + // Commands with dynamic parameters will try to bind the command elements. + // A partially complete parameter will most likely cause a binding error and negatively affect the results. + continue; + } + var parameter = _commandElements[commandIndex] as CommandParameterAst; if (parameter != null) { diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index b2072345d91..d95bd05c943 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -992,6 +992,19 @@ class InheritedClassTest : System.Attribute $res.CompletionMatches.CompletionText | Should -Contain '-ProgressAction' } + It 'Should complete dynamic parameters with partial input' { + # See issue: #19498 + try + { + Push-Location function: + $res = TabExpansion2 -inputScript 'Get-ChildItem -LiteralPath $PSHOME -Fi' + $res.CompletionMatches[1].CompletionText | Should -Be '-File' + } + finally + { + Pop-Location + } + } it 'Should complete enum class members for Enums in script text' { $res = TabExpansion2 -inputScript 'enum Test1 {Val1};([Test1]"").' $res.CompletionMatches.CompletionText[0] | Should -Be 'value__'