Improve enumeration of inferred types in pipeline (#17799)

This commit is contained in:
MartinGC94
2022-08-09 10:37:56 -07:00
committed by GitHub
parent 135e94d6cb
commit 15dcf634e2
4 changed files with 43 additions and 55 deletions
@@ -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 {