mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Improve enumeration of inferred types in pipeline (#17799)
This commit is contained in:
@@ -3963,7 +3963,7 @@ namespace System.Management.Automation
|
||||
|
||||
private static void NativeCompletionMemberName(CompletionContext context, List<CompletionResult> result, CommandAst commandAst, AstParameterArgumentPair parameterInfo, bool propertiesOnly = true)
|
||||
{
|
||||
IEnumerable<PSTypeName> prevType = GetInferenceTypes(context, commandAst);
|
||||
IEnumerable<PSTypeName> prevType = TypeInferenceVisitor.GetInferredEnumeratedTypes(GetInferenceTypes(context, commandAst));
|
||||
if (prevType is not null)
|
||||
{
|
||||
HashSet<string> excludedMembers = null;
|
||||
@@ -3973,7 +3973,7 @@ namespace System.Management.Automation
|
||||
}
|
||||
|
||||
Func<object, bool> filter = propertiesOnly ? IsPropertyMember : null;
|
||||
CompleteMemberByInferredType(context.TypeInferenceContext, prevType, result, context.WordToComplete + "*", filter, isStatic: false, excludedMembers);
|
||||
CompleteMemberByInferredType(context.TypeInferenceContext, prevType, result, context.WordToComplete + "*", filter, isStatic: false, excludedMembers, addMethodParenthesis: false);
|
||||
}
|
||||
|
||||
result.Add(CompletionResult.Null);
|
||||
@@ -5901,22 +5901,25 @@ namespace System.Management.Automation
|
||||
/// <summary>
|
||||
/// Complete members against extension methods 'Where' and 'ForEach'
|
||||
/// </summary>
|
||||
private static void CompleteExtensionMethods(string memberName, List<CompletionResult> results)
|
||||
private static void CompleteExtensionMethods(string memberName, List<CompletionResult> results, bool addMethodParenthesis = true)
|
||||
{
|
||||
var pattern = WildcardPattern.Get(memberName, WildcardOptions.IgnoreCase);
|
||||
CompleteExtensionMethods(pattern, results);
|
||||
CompleteExtensionMethods(pattern, results, addMethodParenthesis);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Complete members against extension methods 'Where' and 'ForEach' based on the given pattern.
|
||||
/// </summary>
|
||||
private static void CompleteExtensionMethods(WildcardPattern pattern, List<CompletionResult> results)
|
||||
private static void CompleteExtensionMethods(WildcardPattern pattern, List<CompletionResult> results, bool addMethodParenthesis)
|
||||
{
|
||||
results.AddRange(from member in s_extensionMethods
|
||||
where pattern.IsMatch(member.Item1)
|
||||
select
|
||||
new CompletionResult(member.Item1 + "(", member.Item1,
|
||||
CompletionResultType.Method, member.Item2));
|
||||
foreach (var member in s_extensionMethods)
|
||||
{
|
||||
if (pattern.IsMatch(member.Item1))
|
||||
{
|
||||
string completionText = addMethodParenthesis ? $"{member.Item1}(" : member.Item1;
|
||||
results.Add(new CompletionResult(completionText, member.Item1, CompletionResultType.Method, member.Item2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -6035,7 +6038,15 @@ namespace System.Management.Automation
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CompleteMemberByInferredType(TypeInferenceContext context, IEnumerable<PSTypeName> inferredTypes, List<CompletionResult> results, string memberName, Func<object, bool> filter, bool isStatic, HashSet<string> excludedMembers = null)
|
||||
internal static void CompleteMemberByInferredType(
|
||||
TypeInferenceContext context,
|
||||
IEnumerable<PSTypeName> inferredTypes,
|
||||
List<CompletionResult> results,
|
||||
string memberName,
|
||||
Func<object, bool> filter,
|
||||
bool isStatic,
|
||||
HashSet<string> excludedMembers = null,
|
||||
bool addMethodParenthesis = true)
|
||||
{
|
||||
bool extensionMethodsAdded = false;
|
||||
HashSet<string> typeNameUsed = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
@@ -6051,7 +6062,7 @@ namespace System.Management.Automation
|
||||
var members = context.GetMembersByInferredType(psTypeName, isStatic, filter);
|
||||
foreach (var member in members)
|
||||
{
|
||||
AddInferredMember(member, memberNamePattern, results, excludedMembers);
|
||||
AddInferredMember(member, memberNamePattern, results, excludedMembers, addMethodParenthesis);
|
||||
}
|
||||
|
||||
// Check if we need to complete against the extension methods 'Where' and 'ForEach'
|
||||
@@ -6059,7 +6070,7 @@ namespace System.Management.Automation
|
||||
{
|
||||
// Complete extension methods 'Where' and 'ForEach' for Enumerable types
|
||||
extensionMethodsAdded = true;
|
||||
CompleteExtensionMethods(memberNamePattern, results);
|
||||
CompleteExtensionMethods(memberNamePattern, results, addMethodParenthesis);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6077,7 +6088,7 @@ namespace System.Management.Automation
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddInferredMember(object member, WildcardPattern memberNamePattern, List<CompletionResult> results, HashSet<string> excludedMembers)
|
||||
private static void AddInferredMember(object member, WildcardPattern memberNamePattern, List<CompletionResult> results, HashSet<string> excludedMembers, bool addMethodParenthesis)
|
||||
{
|
||||
string memberName = null;
|
||||
bool isMethod = false;
|
||||
@@ -6149,7 +6160,7 @@ namespace System.Management.Automation
|
||||
|
||||
var completionResultType = isMethod ? CompletionResultType.Method : CompletionResultType.Property;
|
||||
string completionText;
|
||||
if (isMethod)
|
||||
if (isMethod && addMethodParenthesis)
|
||||
{
|
||||
completionText = $"{memberName}(";
|
||||
}
|
||||
|
||||
@@ -1288,7 +1288,7 @@ namespace System.Management.Automation
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
inferredTypes.AddRange(InferTypes(parentPipeline.PipelineElements[i - 1]));
|
||||
inferredTypes.AddRange(GetInferredEnumeratedTypes(InferTypes(parentPipeline.PipelineElements[i - 1])));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1931,7 +1931,7 @@ namespace System.Management.Automation
|
||||
}
|
||||
else if (parent is not null)
|
||||
{
|
||||
AddInferredTypesForDollarUnderbar(parent, inferredTypes);
|
||||
inferredTypes.AddRange(GetInferredEnumeratedTypes(InferTypes(parent)));
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -2075,42 +2075,6 @@ namespace System.Management.Automation
|
||||
}
|
||||
}
|
||||
|
||||
private void AddInferredTypesForDollarUnderbar(Ast parentExpression, List<PSTypeName> results)
|
||||
{
|
||||
foreach (var result in InferTypes(parentExpression))
|
||||
{
|
||||
if (result.Type != null)
|
||||
{
|
||||
// Assume (because we're looking at $_ and we're inside a script block that is an
|
||||
// argument to some command) that the type we're getting is actually unrolled.
|
||||
// This might not be right in all cases, but with our simple analysis, it's
|
||||
// right more often than it's wrong.
|
||||
if (result.Type.IsArray)
|
||||
{
|
||||
results.Add(new PSTypeName(result.Type.GetElementType()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result.Type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(result.Type))
|
||||
{
|
||||
// We can't deduce much from IEnumerable, but we can if it's generic.
|
||||
var enumerableInterfaces = result.Type.GetInterfaces();
|
||||
foreach (var t in enumerableInterfaces)
|
||||
{
|
||||
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
|
||||
{
|
||||
results.Add(new PSTypeName(t.GetGenericArguments()[0]));
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
results.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the most specific array type possible from a group of inferred types.
|
||||
/// </summary>
|
||||
@@ -2363,7 +2327,7 @@ namespace System.Management.Automation
|
||||
/// The potentially enumerable types to infer enumerated type from.
|
||||
/// </param>
|
||||
/// <returns>The enumerated item types.</returns>
|
||||
private static IEnumerable<PSTypeName> GetInferredEnumeratedTypes(IEnumerable<PSTypeName> enumerableTypes)
|
||||
internal static IEnumerable<PSTypeName> GetInferredEnumeratedTypes(IEnumerable<PSTypeName> enumerableTypes)
|
||||
{
|
||||
foreach (PSTypeName maybeEnumerableType in enumerableTypes)
|
||||
{
|
||||
|
||||
@@ -504,7 +504,7 @@ switch ($x)
|
||||
|
||||
It 'ForEach-Object member completion results should include methods' {
|
||||
$res = TabExpansion2 -inputScript '1..10 | ForEach-Object -MemberName '
|
||||
$res.CompletionMatches.CompletionText | Should -Contain "GetType("
|
||||
$res.CompletionMatches.CompletionText | Should -Contain "GetType"
|
||||
}
|
||||
|
||||
It 'Should not complete void instance members' {
|
||||
@@ -571,6 +571,13 @@ ConstructorTestClass(int i, bool b)
|
||||
$res.CompletionMatches[0].CompletionText | Should -BeExactly '-Directory'
|
||||
}
|
||||
|
||||
It 'Should enumerate types when completing member names for Select-Object' {
|
||||
$TestString = '"Hello","World" | select-object '
|
||||
$res = TabExpansion2 -inputScript $TestString
|
||||
$res | Should -HaveCount 1
|
||||
$res.CompletionMatches[0].CompletionText | Should -BeExactly 'Length'
|
||||
}
|
||||
|
||||
Context "Format cmdlet's View paramter completion" {
|
||||
BeforeAll {
|
||||
$viewDefinition = @'
|
||||
|
||||
@@ -1342,6 +1342,12 @@ Describe "Type inference Tests" -tags "CI" {
|
||||
$res.Count | Should -Be 1
|
||||
$res.Name | Should -Be 'System.Security.AccessControl.AuthorizationRule'
|
||||
}
|
||||
|
||||
It 'Enumerates the inferred type after *-Object commands' {
|
||||
$res = [AstTypeInference]::InferTypeOf( { (([System.Management.Automation.Language.Ast]$null).FindAll() | Select-Object -First 1) }.Ast)
|
||||
$res.Count | Should -Be 1
|
||||
$res.Name | Should -Be 'System.Management.Automation.Language.Ast'
|
||||
}
|
||||
}
|
||||
|
||||
Describe "AstTypeInference tests" -Tags CI {
|
||||
|
||||
Reference in New Issue
Block a user