Skip to content

Commit

Permalink
Improve generic support
Browse files Browse the repository at this point in the history
  • Loading branch information
Youssef1313 committed Jan 21, 2025
1 parent 332e796 commit 5f7012f
Showing 1 changed file with 55 additions and 15 deletions.
70 changes: 55 additions & 15 deletions src/Adapter/MSTest.TestAdapter/Extensions/MethodInfoExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,42 @@ internal static void InvokeAsSynchronousTask(this MethodInfo methodInfo, object?
}
}

private static void InferGenerics(Type parameterType, Type argumentType, List<(Type ParameterType, Type Substitution)> result)
{
if (parameterType.IsGenericMethodParameter())
{
// We found a generic parameter. The argument type should be the substitution for it.
result.Add((parameterType, argumentType));
return;
}

if (!parameterType.ContainsGenericParameters)
{
// We don't have any generics.
return;
}

if (parameterType.GetElementType() is { } parameterTypeElementType &&
argumentType.GetElementType() is { } argumentTypeElementType)
{
// If we have arrays, we need to infer the generic types for the element types.
// For example, if parameterType is `T[]` and argumentType is `string[]`, we need to infer that `T` is `string`.
// So, we call InferGenerics with `T` and `string`.
InferGenerics(parameterTypeElementType, argumentTypeElementType, result);
return;
}
else if (parameterType.GenericTypeArguments.Length == argumentType.GenericTypeArguments.Length)
{
for (int i = 0; i < parameterType.GenericTypeArguments.Length; i++)
{
if (parameterType.GenericTypeArguments[i].ContainsGenericParameters)
{
InferGenerics(parameterType.GenericTypeArguments[i], argumentType.GenericTypeArguments[i], result);
}
}
}
}

// Scenarios to test:
//
// [DataRow(null, "Hello")]
Expand Down Expand Up @@ -228,26 +264,30 @@ private static MethodInfo ConstructGenericMethod(MethodInfo methodInfo, object?[
for (int i = 0; i < parameters.Length; i++)
{
Type parameterType = parameters[i].ParameterType;
if (!parameterType.IsGenericMethodParameter() || arguments[i] is null)
if (!parameterType.ContainsGenericParameters || arguments[i] is null)
{
continue;
}

Type substitution = arguments[i]!/*Very strange nullability warning*/.GetType();
int mapIndexForParameter = GetMapIndexForParameterType(parameterType, map);
Type? existingSubstitution = map[mapIndexForParameter].Substitution;

if (existingSubstitution is null || substitution.IsAssignableFrom(existingSubstitution))
{
map[mapIndexForParameter] = (parameterType, substitution);
}
else if (existingSubstitution.IsAssignableFrom(substitution))
{
// Do nothing. We already have a good existing substitution.
}
else
var result = new List<(Type ParameterType, Type Substitution)>();
InferGenerics(parameterType, arguments[i]!/*Very strange nullability warning*/.GetType(), result);
foreach ((Type genericParameterType, Type substitution) in result)
{
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resource.GenericParameterConflict, parameterType.Name, existingSubstitution, substitution));
int mapIndexForParameter = GetMapIndexForParameterType(genericParameterType, map);
Type? existingSubstitution = map[mapIndexForParameter].Substitution;

if (existingSubstitution is null || substitution.IsAssignableFrom(existingSubstitution))
{
map[mapIndexForParameter] = (genericParameterType, substitution);
}
else if (existingSubstitution.IsAssignableFrom(substitution))
{
// Do nothing. We already have a good existing substitution.
}
else
{
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resource.GenericParameterConflict, parameterType.Name, existingSubstitution, substitution));
}
}
}

Expand Down

0 comments on commit 5f7012f

Please sign in to comment.