Skip to content

Break Recursion in Resolving Generic Arguments of Nested Functions #363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
46 changes: 31 additions & 15 deletions ArchUnitNET/Domain/GenericParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,37 @@ namespace ArchUnitNET.Domain
{
public class GenericParameter : IType
{
private readonly string _declarerFullName;
internal readonly IEnumerable<ITypeInstance<IType>> TypeInstanceConstraints;

public GenericParameter(
ITypeInstance<IType> declaringTypeInstance,
[CanBeNull] MethodMemberInstance declaringMethodInstance,
string fullName,
string declarerFullName,
string name,
GenericParameterVariance variance,
IEnumerable<ITypeInstance<IType>> typeConstraints,
bool hasReferenceTypeConstraint,
bool hasNotNullableValueTypeConstraint,
bool hasDefaultConstructorConstraint,
bool isCompilerGenerated
bool isCompilerGenerated,
bool declarerIsMethod
)
{
DeclaringTypeInstance = declaringTypeInstance;
DeclaringMethodInstance = declaringMethodInstance;
FullName = fullName;
_declarerFullName = declarerFullName;
Name = name;
Variance = variance;
TypeInstanceConstraints = typeConstraints;
HasReferenceTypeConstraint = hasReferenceTypeConstraint;
HasNotNullableValueTypeConstraint = hasNotNullableValueTypeConstraint;
HasDefaultConstructorConstraint = hasDefaultConstructorConstraint;
IsCompilerGenerated = isCompilerGenerated;
DeclarerIsMethod = declarerIsMethod;
}

public ITypeInstance<IType> DeclaringTypeInstance { get; }
public IType DeclaringType => DeclaringTypeInstance.Type;
public IType DeclaringType { get; private set; }

[CanBeNull]
public MethodMemberInstance DeclaringMethodInstance { get; }

[CanBeNull]
public IMember DeclaringMethod => DeclaringMethodInstance?.Member;
public bool DeclarerIsMethod => DeclaringMethodInstance != null;
public IMember DeclaringMethod { get; private set; }
public bool DeclarerIsMethod { get; }
public GenericParameterVariance Variance { get; }
public IEnumerable<IType> TypeConstraints =>
TypeInstanceConstraints.Select(instance => instance.Type);
Expand All @@ -58,7 +53,7 @@ bool isCompilerGenerated
|| TypeConstraints.Any();

public string Name { get; }
public string FullName { get; }
public string FullName => _declarerFullName + "+<" + Name + ">";
public string AssemblyQualifiedName =>
System.Reflection.Assembly.CreateQualifiedName(
DeclaringType.Assembly.FullName,
Expand All @@ -83,6 +78,27 @@ bool isCompilerGenerated
public bool IsNested => true;
public bool IsStub => true;

internal void AssignDeclarer(IMember declaringMethod)
{
if (!declaringMethod.FullName.Equals(_declarerFullName))
{
throw new InvalidOperationException("Full name of declaring member doesn't match.");
}

DeclaringType = declaringMethod.DeclaringType;
DeclaringMethod = declaringMethod;
}

internal void AssignDeclarer(IType declaringType)
{
if (!declaringType.FullName.Equals(_declarerFullName))
{
throw new InvalidOperationException("Full name of declaring type doesn't match.");
}

DeclaringType = declaringType;
}

public bool Equals(GenericParameter other)
{
if (ReferenceEquals(null, other))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ private void AddTypeGenericParameterDependencies()
{
foreach (var genericParameter in _type.GenericParameters)
{
genericParameter.AssignDeclarer(_type);
foreach (var typeInstanceConstraint in genericParameter.TypeInstanceConstraints)
{
var dependency = new TypeGenericParameterTypeConstraintDependency(
Expand All @@ -41,6 +42,7 @@ private void AddMemberGenericParameterDependencies()
{
foreach (var genericParameter in member.GenericParameters)
{
genericParameter.AssignDeclarer(member);
foreach (var typeInstanceConstraint in genericParameter.TypeInstanceConstraints)
{
var dependency = new MemberGenericParameterTypeConstraintDependency(
Expand Down
71 changes: 24 additions & 47 deletions ArchUnitNET/Loader/TypeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,31 +130,37 @@ TypeReference typeReference
{
var genericParameter = (Mono.Cecil.GenericParameter)typeReference;
var declarerIsMethod = genericParameter.Type == GenericParameterType.Method;
var declaringType = GetOrCreateStubTypeInstanceFromTypeReference(
declarerIsMethod
? genericParameter.DeclaringMethod.DeclaringType
: genericParameter.DeclaringType
);
var declaringMethod = declarerIsMethod
? GetOrCreateMethodMemberFromMethodReference(
declaringType,
genericParameter.DeclaringMethod
)
: null;
var declarerFullName =
declaringMethod != null
? declaringMethod.Member.FullName
: declaringType.Type.FullName;
var declarerFullName = declarerIsMethod
? genericParameter.DeclaringMethod.BuildFullName()
: genericParameter.DeclaringType.BuildFullName();
var declaringTypeAssemblyName = declarerIsMethod
? genericParameter.DeclaringMethod.DeclaringType.Module.Assembly.FullName
: genericParameter.DeclaringType.Module.Assembly.FullName;
var assemblyQualifiedName = System.Reflection.Assembly.CreateQualifiedName(
declaringType.Type.Assembly.FullName,
declaringTypeAssemblyName,
$"{declarerFullName}+<{genericParameter.Name}>"
);
if (_allTypes.TryGetValue(assemblyQualifiedName, out var existingTypeInstance))
{
return existingTypeInstance;
}
var isCompilerGenerated = genericParameter.IsCompilerGenerated();
var variance = genericParameter.GetVariance();
var typeConstraints = genericParameter.Constraints.Select(con =>
GetOrCreateStubTypeInstanceFromTypeReference(con.ConstraintType)
);
var result = new TypeInstance<GenericParameter>(
CreateGenericParameter(genericParameter, declaringType, declaringMethod)
new GenericParameter(
declarerFullName,
genericParameter.Name,
variance,
typeConstraints,
genericParameter.HasReferenceTypeConstraint,
genericParameter.HasNotNullableValueTypeConstraint,
genericParameter.HasDefaultConstructorConstraint,
isCompilerGenerated,
declarerIsMethod
)
);
_allTypes.Add(assemblyQualifiedName, result);
return result;
Expand Down Expand Up @@ -199,7 +205,7 @@ TypeReference typeReference
} while (elementType.IsArray);
var elementTypeInstance = GetOrCreateStubTypeInstanceFromTypeReference(elementType);
var assemblyQualifiedName = System.Reflection.Assembly.CreateQualifiedName(
elementTypeInstance.Type.Assembly.FullName,
elementTypeInstance.Type.Assembly?.FullName ?? "",
typeReference.BuildFullName()
);
if (_allTypes.TryGetValue(assemblyQualifiedName, out var existingTypeInstance))
Expand Down Expand Up @@ -609,35 +615,6 @@ IGenericParameterProvider genericParameterProvider
.Cast<GenericParameter>();
}

private GenericParameter CreateGenericParameter(
Mono.Cecil.GenericParameter genericParameter,
ITypeInstance<IType> declaringTypeInstance,
[CanBeNull] MethodMemberInstance declaringMethodInstance
)
{
var isCompilerGenerated = genericParameter.IsCompilerGenerated();
var variance = genericParameter.GetVariance();
var typeConstraints = genericParameter.Constraints.Select(con =>
GetOrCreateStubTypeInstanceFromTypeReference(con.ConstraintType)
);
var declarerFullName =
declaringMethodInstance != null
? declaringMethodInstance.Member.FullName
: declaringTypeInstance.Type.FullName;
return new GenericParameter(
declaringTypeInstance,
declaringMethodInstance,
$"{declarerFullName}+<{genericParameter.Name}>",
genericParameter.Name,
variance,
typeConstraints,
genericParameter.HasReferenceTypeConstraint,
genericParameter.HasNotNullableValueTypeConstraint,
genericParameter.HasDefaultConstructorConstraint,
isCompilerGenerated
);
}

internal GenericArgument CreateGenericArgumentFromTypeReference(TypeReference typeReference)
{
return new GenericArgument(GetOrCreateStubTypeInstanceFromTypeReference(typeReference));
Expand Down
15 changes: 15 additions & 0 deletions TestAssemblies/DependencyAssembly/TypeDependency.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,18 @@ public class ChildClassOfGeneric : GenericBaseClass<ChildClassOfGeneric> { }
public class ClassWithoutDependencies { }

public class OtherClassWithoutDependencies { }

// https://github.com/TNG/ArchUnitNET/issues/351
class Issue351
{
public void OuterFunc()
{
LocalFunc<string>();

void LocalFunc<T>()
{
var list = new List<string>();
list.GroupBy(x => x).ToDictionary(g => g.Key, g => (IReadOnlyCollection<T>)g.ToList());
}
}
}