Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,14 @@ private List<CompletionResult> GetResultForIdentifier(CompletionContext completi
result = CompletionCompleters.CompleteCommandArgument(completionContext);
replacementIndex = completionContext.ReplacementIndex;
replacementLength = completionContext.ReplacementLength;

if (result.Count == 0
&& completionContext.TokenAtCursor.TokenFlags.HasFlag(TokenFlags.TypeName)
&& lastAst?.Find(a => a is MemberExpressionAst, searchNestedScriptBlocks: false) is not null)
{
result = CompletionCompleters.CompleteType(completionContext.TokenAtCursor.Text).ToList();
}

return result;
}

Expand Down
73 changes: 55 additions & 18 deletions src/System.Management.Automation/engine/CoreAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1394,32 +1394,37 @@ private static MethodInformation FindBestMethodImpl(
// be turned into an array.
// We also skip the optimization if the number of arguments and parameters is different
// so we let the loop deal with possible optional parameters.
if ((methods.Length == 1) &&
(!methods[0].hasVarArgs) &&
(!methods[0].isGeneric) &&
(methods[0].method == null || !(methods[0].method.DeclaringType.IsGenericTypeDefinition)) &&
if (methods.Length == 1
&& !methods[0].hasVarArgs
// generic methods need to be double checked in a loop below - generic methods can be rejected if type inference fails
(methods[0].parameters.Length == arguments.Length))
&& !methods[0].isGeneric
&& (methods[0].method is null || !methods[0].method.DeclaringType.IsGenericTypeDefinition)
&& methods[0].parameters.Length == arguments.Length)
{
return methods[0];
}

Type[] argumentTypes = arguments.Select(EffectiveArgumentType).ToArray();
Type[] genericParameters = invocationConstraints?.GenericTypeParameters ?? Array.Empty<Type>();
List<OverloadCandidate> candidates = new List<OverloadCandidate>();
for (int i = 0; i < methods.Length; i++)
{
MethodInformation method = methods[i];
MethodInformation methodInfo = methods[i];

if (method.method != null && method.method.DeclaringType.IsGenericTypeDefinition)
if (methodInfo.method?.DeclaringType.IsGenericTypeDefinition == true
|| (!methodInfo.isGeneric && genericParameters.Length > 0))
{
continue; // skip methods defined by an *open* generic type
// If method is defined by an *open* generic type, or
// if generic parameters were provided and this method isn't generic, skip it.
continue;
}

if (method.isGeneric)
if (methodInfo.isGeneric)
{
Type[] argumentTypesForTypeInference = new Type[argumentTypes.Length];
Array.Copy(argumentTypes, argumentTypesForTypeInference, argumentTypes.Length);
if (invocationConstraints != null && invocationConstraints.ParameterTypes != null)

if (invocationConstraints?.ParameterTypes is not null)
{
int parameterIndex = 0;
foreach (Type typeConstraintFromCallSite in invocationConstraints.ParameterTypes)
Expand All @@ -1433,41 +1438,57 @@ private static MethodInformation FindBestMethodImpl(
}
}

method = TypeInference.Infer(method, argumentTypesForTypeInference);
if (method == null)
if (genericParameters.Length > 0 && methodInfo.method is MethodInfo originalMethod)
{
try
{
methodInfo = new MethodInformation(
originalMethod.MakeGenericMethod(genericParameters),
parametersToIgnore: 0);
}
catch (ArgumentException)
{
// Just skip this possibility if the generic type parameters can't be used to make
// a valid generic method here.
continue;
}
}

methodInfo = TypeInference.Infer(methodInfo, argumentTypesForTypeInference);
if (methodInfo is null)
{
// Skip generic methods for which we cannot infer type arguments
continue;
}
}

if (!IsInvocationTargetConstraintSatisfied(method, invocationConstraints))
if (!IsInvocationTargetConstraintSatisfied(methodInfo, invocationConstraints))
{
continue;
}

ParameterInformation[] parameters = method.parameters;
ParameterInformation[] parameters = methodInfo.parameters;
if (arguments.Length != parameters.Length)
{
// Skip methods w/ an incorrect # of arguments.

if (arguments.Length > parameters.Length)
{
// If too many args,it's only OK if the method is varargs.
if (!method.hasVarArgs)
if (!methodInfo.hasVarArgs)
{
continue;
}
}
else
{
// Too few args, OK if there are optionals, or varargs with the param array omitted
if (!method.hasOptional && (!method.hasVarArgs || (arguments.Length + 1) != parameters.Length))
if (!methodInfo.hasOptional && (!methodInfo.hasVarArgs || (arguments.Length + 1) != parameters.Length))
{
continue;
}

if (method.hasOptional)
if (methodInfo.hasOptional)
{
// Count optionals. This code is rarely hit, mainly when calling code in the
// assembly Microsoft.VisualBasic. If it were more frequent, the optional count
Expand All @@ -1490,7 +1511,7 @@ private static MethodInformation FindBestMethodImpl(
}
}

OverloadCandidate candidate = new OverloadCandidate(method, arguments.Length);
OverloadCandidate candidate = new OverloadCandidate(methodInfo, arguments.Length);
for (int j = 0; candidate != null && j < parameters.Length; j++)
{
ParameterInformation parameter = parameters[j];
Expand Down Expand Up @@ -1590,6 +1611,22 @@ private static MethodInformation FindBestMethodImpl(
methods[0].method.DeclaringType.FullName);
return null;
}
else if (genericParameters.Length != 0 && genericParameters.Contains(null))
{
errorId = "TypeNotFoundForGenericMethod";
errorMsg = ExtendedTypeSystem.MethodGenericArgumentTypeNotFoundException;
return null;
}
else if (genericParameters.Length != 0)
{
errorId = "MethodCountCouldNotFindBestGeneric";
errorMsg = string.Format(
ExtendedTypeSystem.MethodGenericArgumentCountException,
methods[0].method.Name,
genericParameters.Length,
arguments.Length);
return null;
}
else
{
errorId = "MethodCountCouldNotFindBest";
Expand Down
60 changes: 41 additions & 19 deletions src/System.Management.Automation/engine/MshMemberInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1909,9 +1909,18 @@ internal class PSMethodInvocationConstraints
internal PSMethodInvocationConstraints(
Type methodTargetType,
Type[] parameterTypes)
: this(methodTargetType, genericTypeParameters: null, parameterTypes)
{
this.MethodTargetType = methodTargetType;
}

internal PSMethodInvocationConstraints(
Type methodTargetType,
Type[] genericTypeParameters,
Type[] parameterTypes)
{
MethodTargetType = methodTargetType;
_parameterTypes = parameterTypes;
GenericTypeParameters = genericTypeParameters;
}

/// <remarks>
Expand All @@ -1926,6 +1935,11 @@ internal PSMethodInvocationConstraints(

private readonly Type[] _parameterTypes;

/// <summary>
/// Gets the generic type parameters for the method invocation.
/// </summary>
public Type[] GenericTypeParameters { get; }

internal static bool EqualsForCollection<T>(ICollection<T> xs, ICollection<T> ys)
{
if (xs == null)
Expand All @@ -1946,8 +1960,6 @@ internal static bool EqualsForCollection<T>(ICollection<T> xs, ICollection<T> ys
return xs.SequenceEqual(ys);
}

// TODO: IEnumerable<Type> genericTypeParameters { get; private set; }

public bool Equals(PSMethodInvocationConstraints other)
{
if (other is null)
Expand All @@ -1970,6 +1982,11 @@ public bool Equals(PSMethodInvocationConstraints other)
return false;
}

if (!EqualsForCollection(GenericTypeParameters, other.GenericTypeParameters))
{
return false;
}

return true;
}

Expand All @@ -1994,18 +2011,7 @@ public override bool Equals(object obj)
}

public override int GetHashCode()
{
// algorithm based on https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode
unchecked
{
int result = 61;

result = result * 397 + (MethodTargetType != null ? MethodTargetType.GetHashCode() : 0);
result = result * 397 + ParameterTypes.SequenceGetHashCode();

return result;
}
}
=> HashCode.Combine(MethodTargetType, ParameterTypes, GenericTypeParameters);

public override string ToString()
{
Expand All @@ -2018,6 +2024,22 @@ public override string ToString()
separator = " ";
}

if (GenericTypeParameters != null)
{
sb.Append(separator);
sb.Append("genericTypeParams: ");

separator = string.Empty;
foreach (Type parameter in GenericTypeParameters)
{
sb.Append(separator);
sb.Append(ToStringCodeMethods.Type(parameter, dropNamespaces: true));
separator = ", ";
}

separator = " ";
}

if (_parameterTypes != null)
{
sb.Append(separator);
Expand Down Expand Up @@ -5035,7 +5057,7 @@ internal struct Enumerator : IEnumerator<T>
private readonly PSMemberInfoInternalCollection<T> _allMembers;

/// <summary>
/// Constructs this instance to enumerate over members.
/// Initializes a new instance of the <see cref="Enumerator"/> class to enumerate over members.
/// </summary>
/// <param name="integratingCollection">Members we are enumerating.</param>
internal Enumerator(PSMemberInfoIntegratingCollection<T> integratingCollection)
Expand Down Expand Up @@ -5063,8 +5085,8 @@ internal Enumerator(PSMemberInfoIntegratingCollection<T> integratingCollection)
/// Moves to the next element in the enumeration.
/// </summary>
/// <returns>
/// false if there are no more elements to enumerate
/// true otherwise
/// If there are no more elements to enumerate, returns false.
/// Returns true otherwise.
/// </returns>
public bool MoveNext()
{
Expand Down Expand Up @@ -5093,7 +5115,7 @@ public bool MoveNext()
}

/// <summary>
/// Current PSMemberInfo in the enumeration.
/// Gets the current PSMemberInfo in the enumeration.
/// </summary>
/// <exception cref="ArgumentException">For invalid arguments.</exception>
T IEnumerator<T>.Current
Expand Down
66 changes: 52 additions & 14 deletions src/System.Management.Automation/engine/parser/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1172,24 +1172,34 @@ internal static Type GetTypeConstraintForMethodResolution(ExpressionAst expr)
return firstConvert?.Type.TypeName.GetReflectionType();
}

internal static PSMethodInvocationConstraints CombineTypeConstraintForMethodResolution(Type targetType, Type argType)
internal static PSMethodInvocationConstraints CombineTypeConstraintForMethodResolution(
Type targetType,
Type argType,
Type[] genericArguments = null)
{
if (targetType == null && argType == null)
if (targetType is null
&& argType is null
&& (genericArguments is null || genericArguments.Length == 0))
{
return null;
}

return new PSMethodInvocationConstraints(targetType, new[] { argType });
return new PSMethodInvocationConstraints(targetType, genericArguments, new[] { argType });
}

internal static PSMethodInvocationConstraints CombineTypeConstraintForMethodResolution(Type targetType, Type[] argTypes)
internal static PSMethodInvocationConstraints CombineTypeConstraintForMethodResolution(
Type targetType,
Type[] argTypes,
Type[] genericArguments = null)
{
if (targetType == null && (argTypes == null || argTypes.Length == 0))
if (targetType is null
&& (argTypes is null || argTypes.Length == 0)
&& (genericArguments is null || genericArguments.Length == 0))
{
return null;
}

return new PSMethodInvocationConstraints(targetType, argTypes);
return new PSMethodInvocationConstraints(targetType, genericArguments, argTypes);
}

internal static Expression ConvertValue(TypeConstraintAst typeConstraint, Expression expr)
Expand Down Expand Up @@ -6310,17 +6320,47 @@ public object VisitMemberExpression(MemberExpressionAst memberExpressionAst)

internal static PSMethodInvocationConstraints GetInvokeMemberConstraints(InvokeMemberExpressionAst invokeMemberExpressionAst)
{
var arguments = invokeMemberExpressionAst.Arguments;
ReadOnlyCollection<ExpressionAst> arguments = invokeMemberExpressionAst.Arguments;
Type[] argumentTypes = null;
if (arguments is not null)
{
argumentTypes = new Type[arguments.Count];
for (var i = 0; i < arguments.Count; i++)
{
argumentTypes[i] = GetTypeConstraintForMethodResolution(arguments[i]);
}
}

var targetTypeConstraint = GetTypeConstraintForMethodResolution(invokeMemberExpressionAst.Expression);
return CombineTypeConstraintForMethodResolution(
targetTypeConstraint,
arguments?.Select(Compiler.GetTypeConstraintForMethodResolution).ToArray());

ReadOnlyCollection<ITypeName> genericArguments = invokeMemberExpressionAst.GenericTypeArguments;
Type[] genericTypeArguments = null;
if (genericArguments is not null)
{
genericTypeArguments = new Type[genericArguments.Count];
for (var i = 0; i < genericArguments.Count; i++)
{
genericTypeArguments[i] = genericArguments[i].GetReflectionType();
}
}

return CombineTypeConstraintForMethodResolution(targetTypeConstraint, argumentTypes, genericTypeArguments);
}

internal static PSMethodInvocationConstraints GetInvokeMemberConstraints(BaseCtorInvokeMemberExpressionAst invokeMemberExpressionAst)
{
Type targetTypeConstraint = null;
var arguments = invokeMemberExpressionAst.Arguments;
ReadOnlyCollection<ExpressionAst> arguments = invokeMemberExpressionAst.Arguments;
Type[] argumentTypes = null;
if (arguments is not null)
{
argumentTypes = new Type[arguments.Count];
for (var i = 0; i < arguments.Count; i++)
{
argumentTypes[i] = GetTypeConstraintForMethodResolution(arguments[i]);
}
}

TypeDefinitionAst typeDefinitionAst = Ast.GetAncestorTypeDefinitionAst(invokeMemberExpressionAst);
if (typeDefinitionAst != null)
{
Expand All @@ -6331,9 +6371,7 @@ internal static PSMethodInvocationConstraints GetInvokeMemberConstraints(BaseCto
Diagnostics.Assert(false, "BaseCtorInvokeMemberExpressionAst must be used only inside TypeDefinitionAst");
}

return CombineTypeConstraintForMethodResolution(
targetTypeConstraint,
arguments?.Select(Compiler.GetTypeConstraintForMethodResolution).ToArray());
return CombineTypeConstraintForMethodResolution(targetTypeConstraint, argumentTypes, genericArguments: null);
}

internal Expression InvokeMember(
Expand Down
Loading