Skip to content
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

Correctly Resolve Assemblies for Types #312

Merged
merged 3 commits into from
Oct 10, 2024
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
93 changes: 93 additions & 0 deletions ArchUnitNET/Domain/FunctionPointer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2019 Florian Gather <[email protected]>
// Copyright 2019 Fritz Brandhuber <[email protected]>
// Copyright 2020 Pavel Fischer <[email protected]>
//
// SPDX-License-Identifier: Apache-2.0

using System.Collections.Generic;
using System.Linq;
using ArchUnitNET.Domain.Dependencies;

namespace ArchUnitNET.Domain
{
public class FunctionPointer : IType
{
private readonly IType _type;

public FunctionPointer(
IType type,
ITypeInstance<IType> returnTypeInstance,
List<ITypeInstance<IType>> parameterTypeInstances
)
{
_type = type;
ReturnTypeInstance = returnTypeInstance;
ParameterTypeInstances = parameterTypeInstances;
}

public Namespace Namespace => _type.Namespace;
public Assembly Assembly => _type.Assembly;
public MemberList Members => _type.Members;
public IEnumerable<IType> ImplementedInterfaces => _type.ImplementedInterfaces;
public bool IsNested => _type.IsNested;
public bool IsStub => _type.IsStub;
public bool IsGenericParameter => _type.IsGenericParameter;
public string Name => _type.Name;
public string FullName => _type.FullName;
public Visibility Visibility => _type.Visibility;
public bool IsGeneric => _type.IsGeneric;
public List<GenericParameter> GenericParameters => _type.GenericParameters;
public bool IsCompilerGenerated => _type.IsCompilerGenerated;
public List<ITypeDependency> Dependencies => _type.Dependencies;
public List<ITypeDependency> BackwardsDependencies => _type.BackwardsDependencies;
public IEnumerable<Attribute> Attributes => _type.Attributes;
public List<AttributeInstance> AttributeInstances => _type.AttributeInstances;
public ITypeInstance<IType> ReturnTypeInstance { get; }
public List<ITypeInstance<IType>> ParameterTypeInstances { get; }

public bool Equals(FunctionPointer other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Equals(_type, other._type)
&& Equals(ReturnTypeInstance, other.ReturnTypeInstance)
&& ParameterTypeInstances.SequenceEqual(other.ParameterTypeInstances);
}

public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj.GetType() == GetType() && Equals((FunctionPointer)obj);
}

public override int GetHashCode()
{
unchecked
{
var hashCode = _type.GetHashCode();
hashCode =
(hashCode * 397)
^ (ReturnTypeInstance != null ? ReturnTypeInstance.GetHashCode() : 0);
hashCode = ParameterTypeInstances.Aggregate(
hashCode,
(current, typeInstance) =>
(current * 397) ^ (typeInstance != null ? typeInstance.GetHashCode() : 0)
);
return hashCode;
}
}
}
}
17 changes: 17 additions & 0 deletions ArchUnitNET/Loader/ArchLoaderException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2019 Florian Gather <[email protected]>
// Copyright 2019 Fritz Brandhuber <[email protected]>
// Copyright 2020 Pavel Fischer <[email protected]>
//
// SPDX-License-Identifier: Apache-2.0

namespace ArchUnitNET.Loader
{
public class ArchLoaderException : System.Exception
{
public ArchLoaderException(string message)
: base(message) { }

public ArchLoaderException(string message, System.Exception innerException)
: base(message, innerException) { }
}
}
86 changes: 59 additions & 27 deletions ArchUnitNET/Loader/TypeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//
// SPDX-License-Identifier: Apache-2.0

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -199,18 +200,40 @@ bool isStub
}
}

if (
typeReference.IsByReference
|| typeReference.IsPointer
|| typeReference.IsPinned
|| typeReference.IsRequiredModifier
)
{
return CreateTypeFromTypeReference(typeReference.GetElementType(), isStub);
}

if (typeReference is FunctionPointerType functionPointerType)
{
return GetOrCreateTypeInstance(functionPointerType);
}

TypeDefinition typeDefinition;
try
{
typeDefinition = typeReference.Resolve();
}
catch (AssemblyResolutionException)
catch (AssemblyResolutionException e)
{
throw new ArchLoaderException(
$"Could not resolve type {typeReference.FullName}",
e
);
}
if (typeDefinition == null)
{
typeDefinition = null;
throw new ArchLoaderException($"Could not resolve type {typeReference.FullName}");
}

var typeName = typeReference.BuildFullName();
var declaringTypeReference = typeReference;
var typeName = typeDefinition.BuildFullName();
var declaringTypeReference = typeDefinition;
while (declaringTypeReference.IsNested)
{
declaringTypeReference = declaringTypeReference.DeclaringType;
Expand All @@ -220,8 +243,8 @@ bool isStub
declaringTypeReference.Namespace
);
var currentAssembly = _assemblyRegistry.GetOrCreateAssembly(
typeReference.Module.Assembly.Name.FullName,
typeReference.Module.Assembly.FullName,
typeDefinition.Module.Assembly.Name.FullName,
typeDefinition.Module.Assembly.FullName,
true,
null
);
Expand All @@ -231,26 +254,6 @@ bool isStub
isNested,
isGeneric;

if (typeDefinition == null)
{
isCompilerGenerated = typeReference.IsCompilerGenerated();
isNested = typeReference.IsNested;
isGeneric = typeReference.HasGenericParameters;
type = new Type(
typeName,
typeReference.Name,
currentAssembly,
currentNamespace,
NotAccessible,
isNested,
isGeneric,
true,
isCompilerGenerated
);

return new TypeInstance<IType>(type);
}

const string fixedElementField = "FixedElementField";

if (
Expand Down Expand Up @@ -312,7 +315,7 @@ bool isStub
isGeneric = typeDefinition.HasGenericParameters;
type = new Type(
typeName,
typeReference.Name,
typeDefinition.Name,
currentAssembly,
currentNamespace,
visibility,
Expand Down Expand Up @@ -373,6 +376,35 @@ bool isStub
return createdTypeInstance;
}

[NotNull]
private ITypeInstance<FunctionPointer> GetOrCreateTypeInstance(
FunctionPointerType functionPointerType
)
{
var type = new Type(
functionPointerType.FullName,
functionPointerType.Name,
null,
null,
Public,
false,
false,
false,
false
);
var returnTypeInstance = GetOrCreateStubTypeInstanceFromTypeReference(
functionPointerType.ReturnType
);
var parameterTypeInstances = functionPointerType
.Parameters.Select(parameter =>
GetOrCreateStubTypeInstanceFromTypeReference(parameter.ParameterType)
)
.ToList();
return new TypeInstance<FunctionPointer>(
new FunctionPointer(type, returnTypeInstance, parameterTypeInstances)
);
}

[NotNull]
private MethodMemberInstance CreateMethodMemberFromMethodReference(
[NotNull] ITypeInstance<IType> typeInstance,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ MethodCallDependency expectedDependency
Assert.Contains(expectedDependency, originMember.GetMethodCallDependencies());
}

[Theory]
[SkipInReleaseBuildTheory]
[ClassData(typeof(MethodDependencyTestBuild.MethodCallDependencyInAsyncMethodTestData))]
public void MethodCallDependenciesAreFoundInAsyncMethod(
IMember originMember,
Expand Down
16 changes: 16 additions & 0 deletions ArchUnitNETTests/Loader/ArchLoaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

using System.Linq;
using ArchUnitNET.Loader;
using ArchUnitNET.xUnit;
using ArchUnitNETTests.Domain.Dependencies.Members;
using Xunit;
using static ArchUnitNET.Fluent.ArchRuleDefinition;
using static ArchUnitNETTests.StaticTestArchitectures;

namespace ArchUnitNETTests.Loader
Expand Down Expand Up @@ -82,5 +84,19 @@ public void LoadAssembliesRecursively_NestedDependencyOnly()

Assert.Single(architecture.Assemblies);
}

[Fact]
public void TypesAreAssignedToCorrectAssemblies()
{
// https://github.com/TNG/ArchUnitNET/issues/302
var architecture = FullArchUnitNETArchitecture;
Types()
.That()
.ResideInAssembly(architecture.GetType().Assembly)
.Should()
.NotDependOnAnyTypesThat()
.ResideInAssembly(GetType().Assembly)
.Check(architecture);
}
}
}
12 changes: 12 additions & 0 deletions ArchUnitNETTests/Loader/TypeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//
// SPDX-License-Identifier: Apache-2.0

using System.Linq;
using ArchUnitNET.Domain;
using ArchUnitNET.Domain.Dependencies;
using ArchUnitNET.Domain.Extensions;
Expand Down Expand Up @@ -122,6 +123,17 @@ public void NotAssignableToNull()
{
Assert.False(_type.IsAssignableTo(null));
}

[Fact]
public void TypesAreNotLoadedFromMultipleAssemblies()
{
var booleanType = _architecture
.Types.Concat(_architecture.ReferencedTypes)
.Where(type => type.FullName == "System.Boolean")
.ToList();
Assert.Single(booleanType);
Assert.False(booleanType.First().Assembly.Name.StartsWith("ArchUnitNET"));
}
}

[Example]
Expand Down
10 changes: 10 additions & 0 deletions ArchUnitNETTests/SkipInReleaseBuild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ public SkipInReleaseBuild()
{
#if !DEBUG
Skip = "This test only works in debug build";
#endif
}
}

public sealed class SkipInReleaseBuildTheory : TheoryAttribute
{
public SkipInReleaseBuildTheory()
{
#if !DEBUG
Skip = "This test only works in debug build";
#endif
}
}
Expand Down
Loading