diff --git a/ArchUnitNET/Domain/Architecture.cs b/ArchUnitNET/Domain/Architecture.cs index 1f5ea465f..aea22c1b3 100644 --- a/ArchUnitNET/Domain/Architecture.cs +++ b/ArchUnitNET/Domain/Architecture.cs @@ -17,20 +17,28 @@ public class Architecture private readonly ObjectProviderCache _objectProviderCache; public Architecture(IEnumerable allAssemblies, IEnumerable namespaces, - IEnumerable types) + IEnumerable types, IEnumerable genericParameters, + IEnumerable referencedTypes) { _allAssemblies = allAssemblies; Namespaces = namespaces; Types = types; + GenericParameters = genericParameters; + ReferencedTypes = referencedTypes; _objectProviderCache = new ObjectProviderCache(this); } public IEnumerable Assemblies => _allAssemblies.Where(assembly => !assembly.IsOnlyReferenced); public IEnumerable Namespaces { get; } public IEnumerable Types { get; } + public IEnumerable GenericParameters { get; } + public IEnumerable ReferencedTypes { get; } public IEnumerable Classes => Types.OfType(); public IEnumerable Interfaces => Types.OfType(); public IEnumerable Attributes => Types.OfType(); + public IEnumerable ReferencedClasses => ReferencedTypes.OfType(); + public IEnumerable ReferencedInterfaces => ReferencedTypes.OfType(); + public IEnumerable ReferencedAttributes => ReferencedTypes.OfType(); public IEnumerable PropertyMembers => Members.OfType(); public IEnumerable FieldMembers => Members.OfType(); public IEnumerable MethodMembers => Members.OfType(); diff --git a/ArchUnitNET/Domain/Class.cs b/ArchUnitNET/Domain/Class.cs index 0b17f2a44..823cac8ec 100644 --- a/ArchUnitNET/Domain/Class.cs +++ b/ArchUnitNET/Domain/Class.cs @@ -64,6 +64,10 @@ public bool? IsStruct public Visibility Visibility => Type.Visibility; public bool IsNested => Type.IsNested; + public bool IsGeneric => Type.IsGeneric; + public bool IsGenericParameter => Type.IsGenericParameter; + public bool IsStub => Type.IsStub; + public bool IsCompilerGenerated => Type.IsCompilerGenerated; public string Name => Type.Name; public string FullName => Type.FullName; @@ -78,10 +82,7 @@ public bool? IsStruct public IEnumerable ImplementedInterfaces => Type.ImplementedInterfaces; public MemberList Members => Type.Members; - - public List GenericTypeParameters => Type.GenericTypeParameters; - public IType GenericType => Type.GenericType; - public List GenericTypeArguments => Type.GenericTypeArguments; + public List GenericParameters => Type.GenericParameters; public bool ImplementsInterface(Interface intf) { diff --git a/ArchUnitNET/Domain/Dependencies/AccessFieldDependency.cs b/ArchUnitNET/Domain/Dependencies/AccessFieldDependency.cs new file mode 100644 index 000000000..682523919 --- /dev/null +++ b/ArchUnitNET/Domain/Dependencies/AccessFieldDependency.cs @@ -0,0 +1,63 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Collections.Generic; +using System.Linq; + +namespace ArchUnitNET.Domain.Dependencies +{ + public class AccessFieldDependency : IMemberMemberDependency + { + // ReSharper disable once SuggestBaseTypeForParameter + public AccessFieldDependency(IMember originMember, FieldMember accessedField) + { + OriginMember = originMember; + TargetMember = accessedField; + } + + public IMember OriginMember { get; } + public IMember TargetMember { get; } + + public IEnumerable TargetGenericArguments => Enumerable.Empty(); + public IEnumerable TargetMemberGenericArguments => Enumerable.Empty(); + + public IType Origin => OriginMember.DeclaringType; + public IType Target => TargetMember.DeclaringType; + + public bool TargetIsArray => false; + public IEnumerable TargetArrayDimensions => Enumerable.Empty(); + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + return obj.GetType() == GetType() && Equals((AccessFieldDependency) obj); + } + + private bool Equals(IMemberMemberDependency other) + { + return Equals(TargetMember, other.TargetMember) && Equals(OriginMember, other.OriginMember); + } + + public override int GetHashCode() + { + unchecked + { + return ((TargetMember != null ? TargetMember.GetHashCode() : 0) * 397) ^ + (OriginMember != null ? OriginMember.GetHashCode() : 0); + } + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/AttributeMemberDependency.cs b/ArchUnitNET/Domain/Dependencies/AttributeMemberDependency.cs index b470e8544..7bcc9dcd5 100644 --- a/ArchUnitNET/Domain/Dependencies/AttributeMemberDependency.cs +++ b/ArchUnitNET/Domain/Dependencies/AttributeMemberDependency.cs @@ -6,63 +6,11 @@ namespace ArchUnitNET.Domain.Dependencies { - public class AttributeMemberDependency : IMemberTypeDependency + public class AttributeMemberDependency : MemberTypeInstanceDependency { - public AttributeMemberDependency(IMember member, Attribute attribute) + public AttributeMemberDependency(IMember member, ITypeInstance attributeInstance) + : base(member, attributeInstance) { - OriginMember = member; - Target = attribute; - } - - public IType Target { get; } //attribute - - public IMember OriginMember { get; } //object with attribute - - public IType Origin => OriginMember.DeclaringType; //class of object with attribute - - public bool Equals(AttributeMemberDependency other) - { - if (ReferenceEquals(null, other)) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return Equals(Target, other.Target) && Equals(OriginMember, other.OriginMember); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - return Equals((AttributeMemberDependency) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = Target != null ? Target.GetHashCode() : 0; - hashCode = (hashCode * 397) ^ (OriginMember != null ? OriginMember.GetHashCode() : 0); - return hashCode; - } } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/AttributeTypeDependency.cs b/ArchUnitNET/Domain/Dependencies/AttributeTypeDependency.cs index 44f29c2fd..216abad67 100644 --- a/ArchUnitNET/Domain/Dependencies/AttributeTypeDependency.cs +++ b/ArchUnitNET/Domain/Dependencies/AttributeTypeDependency.cs @@ -6,60 +6,11 @@ namespace ArchUnitNET.Domain.Dependencies { - public class AttributeTypeDependency : ITypeDependency + public class AttributeTypeDependency : TypeInstanceDependency { - public AttributeTypeDependency(IType origin, Attribute target) + public AttributeTypeDependency(IType origin, ITypeInstance attributeInstance) + : base(origin, attributeInstance) { - Origin = origin; - Target = target; - } - - public IType Origin { get; } - public IType Target { get; } - - public bool Equals(AttributeTypeDependency other) - { - if (ReferenceEquals(null, other)) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return Equals(Target, other.Target) && Equals(Origin, other.Origin); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - return Equals((AttributeTypeDependency) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = Target != null ? Target.GetHashCode() : 0; - hashCode = (hashCode * 397) ^ (Origin != null ? Origin.GetHashCode() : 0); - return hashCode; - } } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/BodyTypeMemberDependency.cs b/ArchUnitNET/Domain/Dependencies/BodyTypeMemberDependency.cs index 6f48db7b1..925a794c1 100644 --- a/ArchUnitNET/Domain/Dependencies/BodyTypeMemberDependency.cs +++ b/ArchUnitNET/Domain/Dependencies/BodyTypeMemberDependency.cs @@ -6,62 +6,12 @@ namespace ArchUnitNET.Domain.Dependencies { - public class BodyTypeMemberDependency : IMemberTypeDependency + public class BodyTypeMemberDependency : MemberTypeInstanceDependency { - public BodyTypeMemberDependency(MethodMember method, IType target) + // ReSharper disable once SuggestBaseTypeForParameter + public BodyTypeMemberDependency(MethodMember method, ITypeInstance targetTypeInstance) + : base(method, targetTypeInstance) { - OriginMember = method; - Target = target; - } - - public IMember OriginMember { get; } - - public IType Origin => OriginMember.DeclaringType; - public IType Target { get; } - - public bool Equals(BodyTypeMemberDependency other) - { - if (ReferenceEquals(null, other)) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return Equals(OriginMember, other.OriginMember) && Equals(Target, other.Target); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - return Equals((BodyTypeMemberDependency) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = OriginMember != null ? OriginMember.GetHashCode() : 0; - hashCode = (hashCode * 397) ^ (Target != null ? Target.GetHashCode() : 0); - return hashCode; - } } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/CastTypeDependency.cs b/ArchUnitNET/Domain/Dependencies/CastTypeDependency.cs new file mode 100644 index 000000000..7384a68e8 --- /dev/null +++ b/ArchUnitNET/Domain/Dependencies/CastTypeDependency.cs @@ -0,0 +1,17 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +namespace ArchUnitNET.Domain.Dependencies +{ + public class CastTypeDependency : MemberTypeInstanceDependency + { + public CastTypeDependency(IMember originMember, ITypeInstance castTypeInstance) + : base(originMember, castTypeInstance) + { + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/FieldTypeDependency.cs b/ArchUnitNET/Domain/Dependencies/FieldTypeDependency.cs index 5d424056a..eacadf666 100644 --- a/ArchUnitNET/Domain/Dependencies/FieldTypeDependency.cs +++ b/ArchUnitNET/Domain/Dependencies/FieldTypeDependency.cs @@ -6,58 +6,11 @@ namespace ArchUnitNET.Domain.Dependencies { - public class FieldTypeDependency : IMemberTypeDependency + public class FieldTypeDependency : MemberTypeInstanceDependency { - private readonly FieldMember _originMember; - public FieldTypeDependency(FieldMember field) + : base(field, field) { - _originMember = field; - } - - public IType Target => _originMember.Type; - public IMember OriginMember => _originMember; - - public IType Origin => OriginMember.DeclaringType; - - public bool Equals(FieldTypeDependency other) - { - if (ReferenceEquals(null, other)) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return Equals(_originMember, other._originMember); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - return Equals((FieldTypeDependency) obj); - } - - public override int GetHashCode() - { - return _originMember != null ? _originMember.GetHashCode() : 0; } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/GenericArgumentMemberDependency.cs b/ArchUnitNET/Domain/Dependencies/GenericArgumentMemberDependency.cs new file mode 100644 index 000000000..8ff27e542 --- /dev/null +++ b/ArchUnitNET/Domain/Dependencies/GenericArgumentMemberDependency.cs @@ -0,0 +1,17 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +namespace ArchUnitNET.Domain.Dependencies +{ + public class GenericArgumentMemberDependency : MemberTypeInstanceDependency + { + public GenericArgumentMemberDependency(IMember originMember, GenericArgument genericArgument) + : base(originMember, genericArgument) + { + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/GenericArgumentTypeDependency.cs b/ArchUnitNET/Domain/Dependencies/GenericArgumentTypeDependency.cs new file mode 100644 index 000000000..b2e473141 --- /dev/null +++ b/ArchUnitNET/Domain/Dependencies/GenericArgumentTypeDependency.cs @@ -0,0 +1,17 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +namespace ArchUnitNET.Domain.Dependencies +{ + public class GenericArgumentTypeDependency : TypeInstanceDependency + { + public GenericArgumentTypeDependency(IType origin, GenericArgument genericArgument) + : base(origin, genericArgument) + { + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/IMemberMemberDependency.cs b/ArchUnitNET/Domain/Dependencies/IMemberMemberDependency.cs index cb0bf2ece..c240e9fb5 100644 --- a/ArchUnitNET/Domain/Dependencies/IMemberMemberDependency.cs +++ b/ArchUnitNET/Domain/Dependencies/IMemberMemberDependency.cs @@ -4,10 +4,13 @@ // // SPDX-License-Identifier: Apache-2.0 +using System.Collections.Generic; + namespace ArchUnitNET.Domain.Dependencies { public interface IMemberMemberDependency : IMemberTypeDependency { IMember TargetMember { get; } + IEnumerable TargetMemberGenericArguments { get; } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/ITypeDependency.cs b/ArchUnitNET/Domain/Dependencies/ITypeDependency.cs index a1d22a9d9..6f212bd24 100644 --- a/ArchUnitNET/Domain/Dependencies/ITypeDependency.cs +++ b/ArchUnitNET/Domain/Dependencies/ITypeDependency.cs @@ -4,11 +4,17 @@ // // SPDX-License-Identifier: Apache-2.0 +using System.Collections.Generic; + namespace ArchUnitNET.Domain.Dependencies { public interface ITypeDependency { IType Origin { get; } IType Target { get; } + + IEnumerable TargetGenericArguments { get; } + bool TargetIsArray { get; } + IEnumerable TargetArrayDimensions { get; } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/ImplementsInterfaceDependency.cs b/ArchUnitNET/Domain/Dependencies/ImplementsInterfaceDependency.cs index 3a63c2861..fd768bc18 100644 --- a/ArchUnitNET/Domain/Dependencies/ImplementsInterfaceDependency.cs +++ b/ArchUnitNET/Domain/Dependencies/ImplementsInterfaceDependency.cs @@ -6,60 +6,11 @@ namespace ArchUnitNET.Domain.Dependencies { - public class ImplementsInterfaceDependency : ITypeDependency + public class ImplementsInterfaceDependency : TypeInstanceDependency { - public ImplementsInterfaceDependency(IType origin, IType target) + public ImplementsInterfaceDependency(IType origin, ITypeInstance implementedInterface) : base(origin, + implementedInterface) { - Origin = origin; - Target = target; - } - - public IType Origin { get; } - public IType Target { get; } - - public bool Equals(ImplementsInterfaceDependency other) - { - if (ReferenceEquals(null, other)) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return Equals(Target, other.Target) && Equals(Origin, other.Origin); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - return Equals((ImplementsInterfaceDependency) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = Target != null ? Target.GetHashCode() : 0; - hashCode = (hashCode * 397) ^ (Origin != null ? Origin.GetHashCode() : 0); - return hashCode; - } } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/InheritsBaseClassDependency.cs b/ArchUnitNET/Domain/Dependencies/InheritsBaseClassDependency.cs index 2bcce2c21..b670b91b4 100644 --- a/ArchUnitNET/Domain/Dependencies/InheritsBaseClassDependency.cs +++ b/ArchUnitNET/Domain/Dependencies/InheritsBaseClassDependency.cs @@ -6,60 +6,12 @@ namespace ArchUnitNET.Domain.Dependencies { - public class InheritsBaseClassDependency : ITypeDependency + public class InheritsBaseClassDependency : TypeInstanceDependency { - public InheritsBaseClassDependency(Class origin, Class target) + // ReSharper disable SuggestBaseTypeForParameter + public InheritsBaseClassDependency(Class origin, ITypeInstance targetInstance) + : base(origin, targetInstance) { - Origin = origin; - Target = target; - } - - public IType Origin { get; } - public IType Target { get; } - - public bool Equals(InheritsBaseClassDependency other) - { - if (ReferenceEquals(null, other)) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return Equals(Target, other.Target) && Equals(Origin, other.Origin); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - return Equals((InheritsBaseClassDependency) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = Target != null ? Target.GetHashCode() : 0; - hashCode = (hashCode * 397) ^ (Origin != null ? Origin.GetHashCode() : 0); - return hashCode; - } } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/MemberGenericParameterTypeConstraintDependency.cs b/ArchUnitNET/Domain/Dependencies/MemberGenericParameterTypeConstraintDependency.cs new file mode 100644 index 000000000..b8f237299 --- /dev/null +++ b/ArchUnitNET/Domain/Dependencies/MemberGenericParameterTypeConstraintDependency.cs @@ -0,0 +1,60 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System; + +namespace ArchUnitNET.Domain.Dependencies +{ + public class MemberGenericParameterTypeConstraintDependency : MemberTypeInstanceDependency + { + public MemberGenericParameterTypeConstraintDependency(GenericParameter originGenericParameter, + ITypeInstance typeConstraintInstance) + : base(originGenericParameter.DeclaringMethod, typeConstraintInstance) + { + if (!originGenericParameter.DeclarerIsMethod) + { + throw new ArgumentException( + "Use TypeGenericParameterTypeConstraintDependency for Generic Parameters of Types."); + } + + OriginGenericParameter = originGenericParameter; + } + + public GenericParameter OriginGenericParameter { get; } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + return obj.GetType() == GetType() && Equals((MemberGenericParameterTypeConstraintDependency) obj); + } + + private bool Equals(MemberGenericParameterTypeConstraintDependency other) + { + return Equals(OriginGenericParameter, other.OriginGenericParameter) && + Equals(TargetInstance, other.TargetInstance); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = OriginGenericParameter != null ? OriginGenericParameter.GetHashCode() : 0; + hashCode = (hashCode * 397) ^ (TargetInstance != null ? TargetInstance.GetHashCode() : 0); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/MemberTypeInstanceDependency.cs b/ArchUnitNET/Domain/Dependencies/MemberTypeInstanceDependency.cs new file mode 100644 index 000000000..15de8fcf5 --- /dev/null +++ b/ArchUnitNET/Domain/Dependencies/MemberTypeInstanceDependency.cs @@ -0,0 +1,50 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +namespace ArchUnitNET.Domain.Dependencies +{ + public abstract class MemberTypeInstanceDependency : TypeInstanceDependency, IMemberTypeDependency + { + protected MemberTypeInstanceDependency(IMember originMember, ITypeInstance targetInstance) + : base(originMember.DeclaringType, targetInstance) + { + OriginMember = originMember; + } + + public IMember OriginMember { get; } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + return obj.GetType() == GetType() && Equals((MemberTypeInstanceDependency) obj); + } + + private bool Equals(MemberTypeInstanceDependency other) + { + return Equals(OriginMember, other.OriginMember) && Equals(TargetInstance, other.TargetInstance); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = OriginMember != null ? OriginMember.GetHashCode() : 0; + hashCode = (hashCode * 397) ^ (TargetInstance != null ? TargetInstance.GetHashCode() : 0); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/MetaDataDependency.cs b/ArchUnitNET/Domain/Dependencies/MetaDataDependency.cs new file mode 100644 index 000000000..fa02647ee --- /dev/null +++ b/ArchUnitNET/Domain/Dependencies/MetaDataDependency.cs @@ -0,0 +1,17 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +namespace ArchUnitNET.Domain.Dependencies +{ + public class MetaDataDependency : MemberTypeInstanceDependency + { + public MetaDataDependency(IMember originMember, ITypeInstance metaDataTypeInstance) + : base(originMember, metaDataTypeInstance) + { + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/MethodCallDependency.cs b/ArchUnitNET/Domain/Dependencies/MethodCallDependency.cs index bc41be84a..b3958ae55 100644 --- a/ArchUnitNET/Domain/Dependencies/MethodCallDependency.cs +++ b/ArchUnitNET/Domain/Dependencies/MethodCallDependency.cs @@ -4,49 +4,21 @@ // // SPDX-License-Identifier: Apache-2.0 +using System.Collections.Generic; +using ArchUnitNET.Loader; + namespace ArchUnitNET.Domain.Dependencies { - public class MethodCallDependency : IMemberMemberDependency + public class MethodCallDependency : MemberTypeInstanceDependency, IMemberMemberDependency { - public MethodCallDependency(IMember originMember, MethodMember calledMethod) + public MethodCallDependency(IMember originMember, MethodMemberInstance calledMethodInstance) + : base(originMember, calledMethodInstance) { - OriginMember = originMember; - TargetMember = calledMethod; + TargetMember = calledMethodInstance.Member; + TargetMemberGenericArguments = calledMethodInstance.GenericArguments; } public IMember TargetMember { get; } - public IMember OriginMember { get; } - - public IType Origin => OriginMember.DeclaringType; - public IType Target => TargetMember.DeclaringType; - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - return obj.GetType() == GetType() && Equals((MethodCallDependency) obj); - } - - private bool Equals(IMemberMemberDependency other) - { - return Equals(TargetMember, other.TargetMember) && Equals(OriginMember, other.OriginMember); - } - - public override int GetHashCode() - { - unchecked - { - return ((TargetMember != null ? TargetMember.GetHashCode() : 0) * 397) ^ - (OriginMember != null ? OriginMember.GetHashCode() : 0); - } - } + public IEnumerable TargetMemberGenericArguments { get; } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/MethodSignatureDependency.cs b/ArchUnitNET/Domain/Dependencies/MethodSignatureDependency.cs index 80a5a9447..272e36910 100644 --- a/ArchUnitNET/Domain/Dependencies/MethodSignatureDependency.cs +++ b/ArchUnitNET/Domain/Dependencies/MethodSignatureDependency.cs @@ -6,62 +6,12 @@ namespace ArchUnitNET.Domain.Dependencies { - public class MethodSignatureDependency : IMemberTypeDependency + public class MethodSignatureDependency : MemberTypeInstanceDependency { - public MethodSignatureDependency(MethodMember method, IType target) + // ReSharper disable once SuggestBaseTypeForParameter + public MethodSignatureDependency(MethodMember method, ITypeInstance signatureTypeInstance) : base(method, + signatureTypeInstance) { - OriginMember = method; - Target = target; - } - - public IMember OriginMember { get; } - - public IType Origin => OriginMember.DeclaringType; - public IType Target { get; } - - public bool Equals(MethodSignatureDependency other) - { - if (ReferenceEquals(null, other)) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return Equals(OriginMember, other.OriginMember) && Equals(Target, other.Target); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - return Equals((MethodSignatureDependency) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = OriginMember != null ? OriginMember.GetHashCode() : 0; - hashCode = (hashCode * 397) ^ (Target != null ? Target.GetHashCode() : 0); - return hashCode; - } } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/PropertyTypeDependency.cs b/ArchUnitNET/Domain/Dependencies/PropertyTypeDependency.cs index 936db324a..4f3a06ab4 100644 --- a/ArchUnitNET/Domain/Dependencies/PropertyTypeDependency.cs +++ b/ArchUnitNET/Domain/Dependencies/PropertyTypeDependency.cs @@ -6,58 +6,11 @@ namespace ArchUnitNET.Domain.Dependencies { - public class PropertyTypeDependency : IMemberTypeDependency + public class PropertyTypeDependency : MemberTypeInstanceDependency { - private readonly PropertyMember _originMember; - public PropertyTypeDependency(PropertyMember property) + : base(property, property) { - _originMember = property; - } - - public IType Target => _originMember.Type; - public IMember OriginMember => _originMember; - - public IType Origin => _originMember.DeclaringType; - - public bool Equals(PropertyTypeDependency other) - { - if (ReferenceEquals(null, other)) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return Equals(_originMember, other._originMember); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - return Equals((PropertyTypeDependency) obj); - } - - public override int GetHashCode() - { - return _originMember != null ? _originMember.GetHashCode() : 0; } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/TypeCheckDependency.cs b/ArchUnitNET/Domain/Dependencies/TypeCheckDependency.cs new file mode 100644 index 000000000..a71cba385 --- /dev/null +++ b/ArchUnitNET/Domain/Dependencies/TypeCheckDependency.cs @@ -0,0 +1,17 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +namespace ArchUnitNET.Domain.Dependencies +{ + public class TypeCheckDependency : MemberTypeInstanceDependency + { + public TypeCheckDependency(IMember originMember, ITypeInstance typeCheckTypeInstance) + : base(originMember, typeCheckTypeInstance) + { + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/TypeGenericParameterTypeConstraintDependency.cs b/ArchUnitNET/Domain/Dependencies/TypeGenericParameterTypeConstraintDependency.cs new file mode 100644 index 000000000..6f53b19ff --- /dev/null +++ b/ArchUnitNET/Domain/Dependencies/TypeGenericParameterTypeConstraintDependency.cs @@ -0,0 +1,60 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System; + +namespace ArchUnitNET.Domain.Dependencies +{ + public class TypeGenericParameterTypeConstraintDependency : TypeInstanceDependency + { + public TypeGenericParameterTypeConstraintDependency(GenericParameter originGenericParameter, + ITypeInstance typeConstraintInstance) + : base(originGenericParameter, typeConstraintInstance) + { + if (originGenericParameter.DeclarerIsMethod) + { + throw new ArgumentException( + "Use MemberGenericParameterTypeConstraintDependency for Generic Parameters of Methods."); + } + + OriginGenericParameter = originGenericParameter; + } + + public GenericParameter OriginGenericParameter { get; } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + return obj.GetType() == GetType() && Equals((TypeGenericParameterTypeConstraintDependency) obj); + } + + private bool Equals(TypeGenericParameterTypeConstraintDependency other) + { + return Equals(OriginGenericParameter, other.OriginGenericParameter) && + Equals(TargetInstance, other.TargetInstance); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = OriginGenericParameter != null ? OriginGenericParameter.GetHashCode() : 0; + hashCode = (hashCode * 397) ^ (TargetInstance != null ? TargetInstance.GetHashCode() : 0); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/TypeInstanceDependency.cs b/ArchUnitNET/Domain/Dependencies/TypeInstanceDependency.cs new file mode 100644 index 000000000..bc44afcfd --- /dev/null +++ b/ArchUnitNET/Domain/Dependencies/TypeInstanceDependency.cs @@ -0,0 +1,58 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Collections.Generic; + +namespace ArchUnitNET.Domain.Dependencies +{ + public abstract class TypeInstanceDependency : ITypeDependency + { + protected readonly ITypeInstance TargetInstance; + + protected TypeInstanceDependency(IType origin, ITypeInstance targetInstance) + { + Origin = origin; + TargetInstance = targetInstance; + } + + public IType Origin { get; } + public IType Target => TargetInstance.Type; + public IEnumerable TargetGenericArguments => TargetInstance.GenericArguments; + public bool TargetIsArray => TargetInstance.IsArray; + public IEnumerable TargetArrayDimensions => TargetInstance.ArrayDimensions; + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + return obj.GetType() == GetType() && Equals((TypeInstanceDependency) obj); + } + + private bool Equals(TypeInstanceDependency other) + { + return Equals(Origin, other.Origin) && Equals(TargetInstance, other.TargetInstance); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Origin != null ? Origin.GetHashCode() : 0; + hashCode = (hashCode * 397) ^ (TargetInstance != null ? TargetInstance.GetHashCode() : 0); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/Dependencies/TypeReferenceDependency.cs b/ArchUnitNET/Domain/Dependencies/TypeReferenceDependency.cs index 51d5e25c5..5896e68a5 100644 --- a/ArchUnitNET/Domain/Dependencies/TypeReferenceDependency.cs +++ b/ArchUnitNET/Domain/Dependencies/TypeReferenceDependency.cs @@ -6,60 +6,11 @@ namespace ArchUnitNET.Domain.Dependencies { - public class TypeReferenceDependency : ITypeDependency + public class TypeReferenceDependency : TypeInstanceDependency { - public TypeReferenceDependency(IType origin, IType target) + public TypeReferenceDependency(IType origin, ITypeInstance targetInstance) + : base(origin, targetInstance) { - Origin = origin; - Target = target; - } - - public IType Origin { get; } - public IType Target { get; } - - public bool Equals(TypeReferenceDependency other) - { - if (ReferenceEquals(null, other)) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return Equals(Target, other.Target) && Equals(Origin, other.Origin); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - return Equals((TypeReferenceDependency) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = Target != null ? Target.GetHashCode() : 0; - hashCode = (hashCode * 397) ^ (Origin != null ? Origin.GetHashCode() : 0); - return hashCode; - } } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Extensions/ArchitectureExtensions.cs b/ArchUnitNET/Domain/Extensions/ArchitectureExtensions.cs index be9729e5e..e6f2f91aa 100644 --- a/ArchUnitNET/Domain/Extensions/ArchitectureExtensions.cs +++ b/ArchUnitNET/Domain/Extensions/ArchitectureExtensions.cs @@ -5,6 +5,8 @@ // SPDX-License-Identifier: Apache-2.0 using System; +using System.Collections.Generic; +using System.Linq; using ArchUnitNET.Domain.Exceptions; using JetBrains.Annotations; @@ -12,6 +14,26 @@ namespace ArchUnitNET.Domain.Extensions { public static class ArchitectureExtensions { + public static IEnumerable AllTypes(Architecture architecture) + { + return architecture.Types.Concat(architecture.ReferencedTypes); + } + + public static IEnumerable AllClasses(Architecture architecture) + { + return architecture.Classes.Concat(architecture.ReferencedClasses); + } + + public static IEnumerable AllInterfaces(Architecture architecture) + { + return architecture.Interfaces.Concat(architecture.ReferencedInterfaces); + } + + public static IEnumerable AllAttributes(Architecture architecture) + { + return architecture.Attributes.Concat(architecture.ReferencedAttributes); + } + [NotNull] public static IType GetITypeOfType([NotNull] this Architecture architecture, [NotNull] Type type) { @@ -27,7 +49,7 @@ public static IType GetITypeOfType([NotNull] this Architecture architecture, [No try { - var foundType = architecture.Types.WhereFullNameIs(type.FullName); + var foundType = AllTypes(architecture).WhereFullNameIs(type.FullName); if (foundType != null) { return foundType; @@ -49,7 +71,7 @@ public static Class GetClassOfType([NotNull] this Architecture architecture, [No { try { - var cls = architecture.Classes.WhereFullNameIs(type.FullName); + var cls = AllClasses(architecture).WhereFullNameIs(type.FullName); if (cls != null) { return cls; @@ -71,7 +93,7 @@ public static Interface GetInterfaceOfType([NotNull] this Architecture architect { try { - var intf = architecture.Interfaces.WhereFullNameIs(type.FullName); + var intf = AllInterfaces(architecture).WhereFullNameIs(type.FullName); if (intf != null) { return intf; @@ -93,7 +115,7 @@ public static Attribute GetAttributeOfType([NotNull] this Architecture architect { try { - var attribute = architecture.Attributes.WhereFullNameIs(type.FullName); + var attribute = AllAttributes(architecture).WhereFullNameIs(type.FullName); if (attribute != null) { return attribute; diff --git a/ArchUnitNET/Domain/Extensions/DependencyExtensions.cs b/ArchUnitNET/Domain/Extensions/DependencyExtensions.cs index 6a6f03967..6e3e8ba85 100644 --- a/ArchUnitNET/Domain/Extensions/DependencyExtensions.cs +++ b/ArchUnitNET/Domain/Extensions/DependencyExtensions.cs @@ -24,6 +24,12 @@ public static IEnumerable GetCalledMethods(this IHasDependencies t .Select(dependency => (MethodMember) dependency.TargetMember); } + public static IEnumerable GetAccessedFieldMembers(this IHasDependencies type) + { + return type.Dependencies.OfType() + .Select(dependency => (FieldMember) dependency.TargetMember); + } + public static bool DependsOn(this IHasDependencies c, string pattern, bool useRegularExpressions = false) { return c.GetTypeDependencies().Any(d => d.FullNameMatches(pattern, useRegularExpressions)); diff --git a/ArchUnitNET/Domain/Extensions/TypeExtensions.cs b/ArchUnitNET/Domain/Extensions/TypeExtensions.cs index df73463f5..2c9142302 100644 --- a/ArchUnitNET/Domain/Extensions/TypeExtensions.cs +++ b/ArchUnitNET/Domain/Extensions/TypeExtensions.cs @@ -12,6 +12,11 @@ namespace ArchUnitNET.Domain.Extensions { public static class TypeExtensions { + public static bool IsAnonymousType(this IType type) + { + return type.NameStartsWith("<>f__AnonymousType"); + } + public static IEnumerable> SlicedBy(this IEnumerable source, string fullName) { return source.GroupBy(type => type.FullName) diff --git a/ArchUnitNET/Domain/FieldMember.cs b/ArchUnitNET/Domain/FieldMember.cs index 144f5018e..ad32d9a77 100644 --- a/ArchUnitNET/Domain/FieldMember.cs +++ b/ArchUnitNET/Domain/FieldMember.cs @@ -5,32 +5,47 @@ // SPDX-License-Identifier: Apache-2.0 using System.Collections.Generic; +using System.Linq; using ArchUnitNET.Domain.Dependencies; namespace ArchUnitNET.Domain { - public class FieldMember : IMember + public class FieldMember : IMember, ITypeInstance { - public FieldMember(IType declaringType, string name, string fullName, Visibility visibility, IType type) + private readonly ITypeInstance _typeInstance; + + public FieldMember(IType declaringType, string name, string fullName, Visibility visibility, + ITypeInstance typeInstance, bool isCompilerGenerated) { DeclaringType = declaringType; Name = name; FullName = fullName; Visibility = visibility; - Type = type; + IsCompilerGenerated = isCompilerGenerated; + _typeInstance = typeInstance; } - public IType Type { get; } public Visibility Visibility { get; } public IType DeclaringType { get; } public string Name { get; } public string FullName { get; } + + public bool IsCompilerGenerated { get; } + public bool IsGeneric => false; + public List GenericParameters => new List(); public List Attributes { get; } = new List(); public List MemberDependencies { get; } = new List(); public List MemberBackwardsDependencies { get; } = new List(); - public List Dependencies { get; } = new List(); - public List BackwardsDependencies { get; } = new List(); + public List Dependencies => MemberDependencies.Cast().ToList(); + + public List BackwardsDependencies => + MemberBackwardsDependencies.Cast().ToList(); + + public IType Type => _typeInstance.Type; + public IEnumerable GenericArguments => _typeInstance.GenericArguments; + public IEnumerable ArrayDimensions => _typeInstance.ArrayDimensions; + public bool IsArray => _typeInstance.IsArray; public override string ToString() { @@ -54,23 +69,12 @@ public override bool Equals(object obj) private bool Equals(FieldMember other) { - return Equals(DeclaringType, other.DeclaringType) && string.Equals(Name, other.Name) - && string.Equals(FullName, other.FullName) - && Equals(Type, other.Type) - && Visibility == other.Visibility; + return FullName.Equals(other.FullName); } public override int GetHashCode() { - unchecked - { - var hashCode = DeclaringType != null ? DeclaringType.GetHashCode() : 0; - hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (FullName != null ? FullName.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Type != null ? Type.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (int) Visibility; - return hashCode; - } + return FullName.GetHashCode(); } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/GenericArgument.cs b/ArchUnitNET/Domain/GenericArgument.cs new file mode 100644 index 000000000..4e9a1057c --- /dev/null +++ b/ArchUnitNET/Domain/GenericArgument.cs @@ -0,0 +1,72 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Collections.Generic; +using System.Linq; + +namespace ArchUnitNET.Domain +{ + public class GenericArgument : ITypeInstance + { + public GenericArgument(ITypeInstance typeInstance) + { + Type = typeInstance.Type; + GenericArguments = typeInstance.GenericArguments; + } + + public IType Type { get; } + public IEnumerable GenericArguments { get; } + public bool IsArray { get; } + public IEnumerable ArrayDimensions { get; } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + return obj.GetType() == GetType() && Equals((GenericArgument) obj); + } + + private bool Equals(GenericArgument other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return Equals(Type, other.Type) && GenericArguments.SequenceEqual(other.GenericArguments); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Type != null ? Type.GetHashCode() : 0; + hashCode = GenericArguments.Aggregate(hashCode, + (current, type) => (current * 397) ^ (type != null ? type.GetHashCode() : 0)); + return hashCode; + } + } + + public override string ToString() + { + return Type.FullName; + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/GenericParameter.cs b/ArchUnitNET/Domain/GenericParameter.cs index 496c7f78e..5e00f8964 100644 --- a/ArchUnitNET/Domain/GenericParameter.cs +++ b/ArchUnitNET/Domain/GenericParameter.cs @@ -4,15 +4,157 @@ // // SPDX-License-Identifier: Apache-2.0 +using System; +using System.Collections.Generic; +using System.Linq; +using ArchUnitNET.Domain.Dependencies; +using JetBrains.Annotations; + namespace ArchUnitNET.Domain { - public class GenericParameter + public class GenericParameter : IType { - public GenericParameter(string name) + private readonly string _declarerFullName; + internal readonly IEnumerable> TypeInstanceConstraints; + + public GenericParameter(string declarerFullName, string name, GenericParameterVariance variance, + IEnumerable> typeConstraints, bool hasReferenceTypeConstraint, + bool hasNotNullableValueTypeConstraint, bool hasDefaultConstructorConstraint, bool isCompilerGenerated, + bool declarerIsMethod) { + _declarerFullName = declarerFullName; Name = name; + Variance = variance; + TypeInstanceConstraints = typeConstraints; + HasReferenceTypeConstraint = hasReferenceTypeConstraint; + HasNotNullableValueTypeConstraint = hasNotNullableValueTypeConstraint; + HasDefaultConstructorConstraint = hasDefaultConstructorConstraint; + IsCompilerGenerated = isCompilerGenerated; + DeclarerIsMethod = declarerIsMethod; } + public IType DeclaringType { get; private set; } + [CanBeNull] public IMember DeclaringMethod { get; private set; } + public bool DeclarerIsMethod { get; } + public GenericParameterVariance Variance { get; } + public IEnumerable TypeConstraints => TypeInstanceConstraints.Select(instance => instance.Type); + public bool HasReferenceTypeConstraint { get; } + public bool HasNotNullableValueTypeConstraint { get; } + public bool HasDefaultConstructorConstraint { get; } + + public bool HasConstraints => HasReferenceTypeConstraint || HasNotNullableValueTypeConstraint || + HasDefaultConstructorConstraint || TypeConstraints.Any(); + public string Name { get; } + public string FullName => _declarerFullName + "+<" + Name + ">"; + public bool IsCompilerGenerated { get; } + public List Attributes { get; } = new List(); + + public List Dependencies { get; } = new List(); + public List BackwardsDependencies { get; } = new List(); + public Visibility Visibility => Visibility.NotAccessible; + public bool IsGeneric => false; + public bool IsGenericParameter => true; + public List GenericParameters => new List(); + public Namespace Namespace => DeclaringType?.Namespace; + public Assembly Assembly => DeclaringType?.Assembly; + public MemberList Members => new MemberList(); + public IEnumerable ImplementedInterfaces => Enumerable.Empty(); + + public bool IsNested => true; + public bool IsStub => true; + + public bool ImplementsInterface(Interface intf) + { + return false; + } + + public bool ImplementsInterface(string pattern, bool useRegularExpressions = false) + { + return false; + } + + public bool IsAssignableTo(IType assignableToType) + { + return TypeConstraints.All(type => type.IsAssignableTo(assignableToType)); + } + + public bool IsAssignableTo(string pattern, bool useRegularExpressions = false) + { + return pattern != null && TypeConstraints.All(type => type.IsAssignableTo(pattern, useRegularExpressions)); + } + + 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)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return Equals(FullName, other.FullName); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + + return Equals((GenericParameter) obj); + } + + public override int GetHashCode() + { + return FullName.GetHashCode(); + } + + public override string ToString() + { + return FullName; + } + } + + public enum GenericParameterVariance + { + NonVariant, + Covariant, + Contravariant } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/ICanBeAnalyzed.cs b/ArchUnitNET/Domain/ICanBeAnalyzed.cs index 6bc7b5076..00f4f3613 100644 --- a/ArchUnitNET/Domain/ICanBeAnalyzed.cs +++ b/ArchUnitNET/Domain/ICanBeAnalyzed.cs @@ -7,7 +7,8 @@ namespace ArchUnitNET.Domain { public interface - ICanBeAnalyzed : IHasName, IHasDependencies, IHasAttributes, IHasVisibility + ICanBeAnalyzed : IHasName, IHasDependencies, IHasAttributes, IHasVisibility, ICanBeGeneric, + ICanBeCompilerGenerated { } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/ICanBeCompilerGenerated.cs b/ArchUnitNET/Domain/ICanBeCompilerGenerated.cs new file mode 100644 index 000000000..13d9ad6cc --- /dev/null +++ b/ArchUnitNET/Domain/ICanBeCompilerGenerated.cs @@ -0,0 +1,14 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +namespace ArchUnitNET.Domain +{ + public interface ICanBeCompilerGenerated + { + bool IsCompilerGenerated { get; } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/ICanBeGeneric.cs b/ArchUnitNET/Domain/ICanBeGeneric.cs new file mode 100644 index 000000000..3a2c91c4e --- /dev/null +++ b/ArchUnitNET/Domain/ICanBeGeneric.cs @@ -0,0 +1,17 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Collections.Generic; + +namespace ArchUnitNET.Domain +{ + public interface ICanBeGeneric + { + bool IsGeneric { get; } + List GenericParameters { get; } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/IType.cs b/ArchUnitNET/Domain/IType.cs index 378488014..d5126bc6b 100644 --- a/ArchUnitNET/Domain/IType.cs +++ b/ArchUnitNET/Domain/IType.cs @@ -5,7 +5,6 @@ // SPDX-License-Identifier: Apache-2.0 using System.Collections.Generic; -using JetBrains.Annotations; namespace ArchUnitNET.Domain { @@ -14,11 +13,10 @@ public interface IType : ICanBeAnalyzed Namespace Namespace { get; } Assembly Assembly { get; } MemberList Members { get; } - List GenericTypeParameters { get; } - List GenericTypeArguments { get; } - [CanBeNull] IType GenericType { get; } - bool IsNested { get; } IEnumerable ImplementedInterfaces { get; } + bool IsNested { get; } + bool IsStub { get; } + bool IsGenericParameter { get; } bool ImplementsInterface(Interface intf); bool ImplementsInterface(string pattern, bool useRegularExpressions = false); bool IsAssignableTo(IType assignableToType); diff --git a/ArchUnitNET/Domain/ITypeInstance.cs b/ArchUnitNET/Domain/ITypeInstance.cs new file mode 100644 index 000000000..eee503e48 --- /dev/null +++ b/ArchUnitNET/Domain/ITypeInstance.cs @@ -0,0 +1,19 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Collections.Generic; + +namespace ArchUnitNET.Domain +{ + public interface ITypeInstance where T : IType + { + T Type { get; } + IEnumerable GenericArguments { get; } + bool IsArray { get; } + IEnumerable ArrayDimensions { get; } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Domain/Interface.cs b/ArchUnitNET/Domain/Interface.cs index b2128bd8f..3c68114f6 100644 --- a/ArchUnitNET/Domain/Interface.cs +++ b/ArchUnitNET/Domain/Interface.cs @@ -23,6 +23,10 @@ public Interface(IType type) public Visibility Visibility => Type.Visibility; public bool IsNested => Type.IsNested; + public bool IsGeneric => Type.IsGeneric; + public bool IsGenericParameter => Type.IsGenericParameter; + public bool IsStub => Type.IsStub; + public bool IsCompilerGenerated => Type.IsCompilerGenerated; public Namespace Namespace => Type.Namespace; public Assembly Assembly => Type.Assembly; @@ -34,9 +38,7 @@ public Interface(IType type) public IEnumerable ImplementedInterfaces => Type.ImplementedInterfaces; public MemberList Members => Type.Members; - public List GenericTypeParameters => Type.GenericTypeParameters; - public IType GenericType => Type.GenericType; - public List GenericTypeArguments => Type.GenericTypeArguments; + public List GenericParameters => Type.GenericParameters; public bool ImplementsInterface(Interface intf) { diff --git a/ArchUnitNET/Domain/MethodMember.cs b/ArchUnitNET/Domain/MethodMember.cs index 7f3c2ae7b..2bb731d65 100644 --- a/ArchUnitNET/Domain/MethodMember.cs +++ b/ArchUnitNET/Domain/MethodMember.cs @@ -5,38 +5,50 @@ // SPDX-License-Identifier: Apache-2.0 using System.Collections.Generic; +using System.Linq; using ArchUnitNET.Domain.Dependencies; namespace ArchUnitNET.Domain { public class MethodMember : IMember { + private readonly ITypeInstance _returnTypeInstance; + public MethodMember(string name, string fullName, IType declaringType, Visibility visibility, - List parameters, IType returnType, bool isVirtual, MethodForm methodForm, - List genericParameters) + ITypeInstance returnType, bool isVirtual, MethodForm methodForm, bool isGeneric, bool isStub, + bool isCompilerGenerated) { Name = name; FullName = fullName; DeclaringType = declaringType; Visibility = visibility; - Parameters = parameters; - ReturnType = returnType; + _returnTypeInstance = returnType; IsVirtual = isVirtual; MethodForm = methodForm; - GenericParameters = genericParameters; + IsGeneric = isGeneric; + IsStub = isStub; + IsCompilerGenerated = isCompilerGenerated; } public bool IsVirtual { get; } public MethodForm MethodForm { get; } - public List Parameters { get; } - public IType ReturnType { get; } - public List GenericParameters { get; } + + public List> ParameterInstances { get; } = new List>(); + public IEnumerable Parameters => ParameterInstances.Select(instance => instance.Type); + public IType ReturnType => _returnTypeInstance.Type; + public bool IsStub { get; } + public bool IsCompilerGenerated { get; } + public bool IsGeneric { get; } + public List GenericParameters { get; } = new List(); public Visibility Visibility { get; } public List Attributes { get; } = new List(); public List MemberDependencies { get; } = new List(); public List MemberBackwardsDependencies { get; } = new List(); - public List Dependencies { get; } = new List(); - public List BackwardsDependencies { get; } = new List(); + public List Dependencies => MemberDependencies.Cast().ToList(); + + public List BackwardsDependencies => + MemberBackwardsDependencies.Cast().ToList(); + public string Name { get; } public string FullName { get; } public IType DeclaringType { get; } @@ -63,26 +75,12 @@ public override bool Equals(object obj) private bool Equals(MethodMember other) { - return IsVirtual == other.IsVirtual && MethodForm == other.MethodForm && Visibility == other.Visibility - && Equals(Parameters, other.Parameters) && Equals(ReturnType, other.ReturnType) - && string.Equals(Name, other.Name) && string.Equals(FullName, other.FullName) - && Equals(DeclaringType, other.DeclaringType); + return FullName.Equals(other.FullName); } public override int GetHashCode() { - unchecked - { - var hashCode = IsVirtual.GetHashCode(); - hashCode = (hashCode * 397) ^ (int) MethodForm; - hashCode = (hashCode * 397) ^ (int) Visibility; - hashCode = (hashCode * 397) ^ (Parameters != null ? Parameters.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (ReturnType != null ? ReturnType.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (FullName != null ? FullName.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (DeclaringType != null ? DeclaringType.GetHashCode() : 0); - return hashCode; - } + return FullName.GetHashCode(); } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/Namespace.cs b/ArchUnitNET/Domain/Namespace.cs index cc7095f11..2d690b97e 100644 --- a/ArchUnitNET/Domain/Namespace.cs +++ b/ArchUnitNET/Domain/Namespace.cs @@ -17,5 +17,10 @@ public Namespace(string name, List types) : base(name, types) public string Name => SliceKey; public string FullName => Name; + + public override string ToString() + { + return FullName; + } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/PropertyMember.cs b/ArchUnitNET/Domain/PropertyMember.cs index 2f3ecec91..54b37a70b 100644 --- a/ArchUnitNET/Domain/PropertyMember.cs +++ b/ArchUnitNET/Domain/PropertyMember.cs @@ -5,46 +5,73 @@ // SPDX-License-Identifier: Apache-2.0 using System.Collections.Generic; +using System.Linq; using ArchUnitNET.Domain.Dependencies; using JetBrains.Annotations; using static ArchUnitNET.Domain.Visibility; namespace ArchUnitNET.Domain { - public class PropertyMember : IMember + public class PropertyMember : IMember, ITypeInstance { - public PropertyMember(IType declaringType, string name, string fullName, IType type, - bool isVirtual, [CanBeNull] MethodMember getter, [CanBeNull] MethodMember setter) + private readonly ITypeInstance _typeInstance; + + public PropertyMember(IType declaringType, string name, string fullName, ITypeInstance type, + bool isCompilerGenerated) { Name = name; FullName = fullName; - Type = type; + _typeInstance = type; DeclaringType = declaringType; - IsVirtual = isVirtual; - Getter = getter; - Setter = setter; + IsCompilerGenerated = isCompilerGenerated; + PropertyTypeDependency = new PropertyTypeDependency(this); } - public IType Type { get; } - public bool IsVirtual { get; } + public bool IsVirtual { get; internal set; } + public bool IsAutoProperty { get; internal set; } = true; public Visibility SetterVisibility => Setter?.Visibility ?? NotAccessible; public Visibility GetterVisibility => Getter?.Visibility ?? NotAccessible; - [CanBeNull] public MethodMember Getter { get; } + [CanBeNull] public MethodMember Getter { get; internal set; } + + [CanBeNull] public MethodMember Setter { get; internal set; } - [CanBeNull] public MethodMember Setter { get; } + public List AttributeDependencies { get; } = new List(); - public FieldMember BackingField { get; internal set; } + public IMemberTypeDependency PropertyTypeDependency { get; } + public bool IsCompilerGenerated { get; } + + public bool IsGeneric => false; + public List GenericParameters => new List(); public Visibility Visibility => GetterVisibility < SetterVisibility ? GetterVisibility : SetterVisibility; public string Name { get; } public string FullName { get; } public IType DeclaringType { get; } public List Attributes { get; } = new List(); - public List MemberDependencies { get; } = new List(); + + public List MemberDependencies + { + get + { + var setterDependencies = Setter?.MemberDependencies ?? Enumerable.Empty(); + var getterDependencies = Getter?.MemberDependencies ?? Enumerable.Empty(); + return setterDependencies.Concat(getterDependencies).Concat(AttributeDependencies) + .Append(PropertyTypeDependency).ToList(); + } + } + public List MemberBackwardsDependencies { get; } = new List(); - public List Dependencies { get; } = new List(); - public List BackwardsDependencies { get; } = new List(); + + public List Dependencies => MemberDependencies.Cast().ToList(); + + public List BackwardsDependencies => + MemberBackwardsDependencies.Cast().ToList(); + + public IType Type => _typeInstance.Type; + public IEnumerable GenericArguments => _typeInstance.GenericArguments; + public IEnumerable ArrayDimensions => _typeInstance.ArrayDimensions; + public bool IsArray => _typeInstance.IsArray; public override string ToString() { @@ -68,27 +95,12 @@ public override bool Equals(object obj) private bool Equals(PropertyMember other) { - return Equals(Type, other.Type) && IsVirtual == other.IsVirtual - && Equals(Getter, other.Getter) && Equals(Setter, other.Setter) - && Equals(BackingField, other.BackingField) && - string.Equals(Name, other.Name) - && string.Equals(FullName, other.FullName) && - Equals(DeclaringType, other.DeclaringType); + return Equals(FullName, other.FullName); } public override int GetHashCode() { - unchecked - { - var hashCode = IsVirtual.GetHashCode(); - hashCode = (hashCode * 397) ^ (Type != null ? Type.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Getter != null ? Getter.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Setter != null ? Setter.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (FullName != null ? FullName.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (DeclaringType != null ? DeclaringType.GetHashCode() : 0); - return hashCode; - } + return FullName.GetHashCode(); } } } \ No newline at end of file diff --git a/ArchUnitNET/Fluent/Syntax/Elements/ObjectConditionsDefinition.cs b/ArchUnitNET/Fluent/Syntax/Elements/ObjectConditionsDefinition.cs index 7de8b78c1..f60ba30af 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/ObjectConditionsDefinition.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/ObjectConditionsDefinition.cs @@ -1439,7 +1439,7 @@ IEnumerable Condition(IEnumerable ruleTypes, Archite { var dynamicFailDescription = "does depend on"; var first = true; - foreach (var type in failedObject.GetTypeDependencies().Intersect(typeList)) + foreach (var type in failedObject.GetTypeDependencies().Union(typeList)) { dynamicFailDescription += first ? " " + type.FullName : " and " + type.FullName; first = false; @@ -1471,7 +1471,7 @@ IEnumerable Condition(IEnumerable ruleTypes) { var dynamicFailDescription = "does depend on"; var first = true; - foreach (var type in failedObject.GetTypeDependencies().Intersect(typeList)) + foreach (var type in failedObject.GetTypeDependencies().Union(typeList)) { dynamicFailDescription += first ? " " + type.FullName : " and " + type.FullName; first = false; @@ -1516,7 +1516,7 @@ IEnumerable Condition(IEnumerable ruleTypes, Archite { var dynamicFailDescription = "does depend on"; var first = true; - foreach (var type in failedObject.GetTypeDependencies().Intersect(iTypeList)) + foreach (var type in failedObject.GetTypeDependencies().Union(iTypeList)) { dynamicFailDescription += first ? " " + type.FullName : " and " + type.FullName; first = false; diff --git a/ArchUnitNET/Loader/ArchBuilder.cs b/ArchUnitNET/Loader/ArchBuilder.cs index dde4fe916..049aed468 100644 --- a/ArchUnitNET/Loader/ArchBuilder.cs +++ b/ArchUnitNET/Loader/ArchBuilder.cs @@ -11,6 +11,7 @@ using ArchUnitNET.Loader.LoadTasks; using JetBrains.Annotations; using Mono.Cecil; +using GenericParameter = ArchUnitNET.Domain.GenericParameter; namespace ArchUnitNET.Loader { @@ -30,9 +31,10 @@ public ArchBuilder() _namespaceRegistry = new NamespaceRegistry(); _loadTaskRegistry = new LoadTaskRegistry(); var typeRegistry = new TypeRegistry(); - _typeFactory = new TypeFactory(typeRegistry, _loadTaskRegistry, _assemblyRegistry, _namespaceRegistry); + var methodMemberRegistry = new MethodMemberRegistry(); + _typeFactory = new TypeFactory(typeRegistry, methodMemberRegistry, _loadTaskRegistry, _assemblyRegistry, + _namespaceRegistry); _architectureCacheKey = new ArchitectureCacheKey(); - _architectureCache = ArchitectureCache.Instance; } @@ -50,16 +52,27 @@ public void LoadTypesForModule(ModuleDefinition module, string namespaceFilter) { _architectureCacheKey.Add(module.Name, namespaceFilter); - var types = module.Types; + ICollection types; + + if (module.Types.First().FullName.Contains("")) + { + types = module.Types.Skip(1).ToList(); + } + else + { + types = module.Types; + } + - var allTypes = types.Concat(types.SelectMany(typeDefinition => typeDefinition.NestedTypes)); + var allTypes = types.Concat(types.SelectMany(typeDefinition => + typeDefinition.NestedTypes.Where(type => !type.IsCompilerGenerated()))); allTypes .Where(typeDefinition => RegexUtils.MatchNamespaces(namespaceFilter, typeDefinition.Namespace)) .ForEach(typeDefinition => { var type = _typeFactory.GetOrCreateTypeFromTypeReference(typeDefinition); - if (!_architectureTypes.Contains(type)) + if (!_architectureTypes.Contains(type) && !type.IsCompilerGenerated) { _architectureTypes.Add(type); } @@ -79,9 +92,11 @@ private void UpdateTypeDefinitions() _loadTaskRegistry.ExecuteTasks(new List { typeof(AddMembers), + typeof(AddGenericParameterDependencies), typeof(AddAttributesAndAttributeDependencies), typeof(AddFieldAndPropertyDependencies), typeof(AddMethodDependencies), + typeof(AddGenericArgumentDependencies), typeof(AddClassDependencies), typeof(AddBackwardsDependencies), typeof(AddTypesToNamespace) @@ -97,8 +112,10 @@ public Architecture Build() } UpdateTypeDefinitions(); - var newArchitecture = - new Architecture(Assemblies, Namespaces, Types.Skip(1)); //Skip first Type to ignore + var allTypes = _typeFactory.GetAllNonCompilerGeneratedTypes().ToList(); + var genericParameters = allTypes.OfType().ToList(); + var referencedTypes = allTypes.Except(Types).Except(genericParameters); + var newArchitecture = new Architecture(Assemblies, Namespaces, Types, genericParameters, referencedTypes); _architectureCache.Add(_architectureCacheKey, newArchitecture); return newArchitecture; } diff --git a/ArchUnitNET/Loader/LoadTasks/AddAttributesAndAttributeDependencies.cs b/ArchUnitNET/Loader/LoadTasks/AddAttributesAndAttributeDependencies.cs index d4235a793..f9b93e8e1 100644 --- a/ArchUnitNET/Loader/LoadTasks/AddAttributesAndAttributeDependencies.cs +++ b/ArchUnitNET/Loader/LoadTasks/AddAttributesAndAttributeDependencies.cs @@ -30,70 +30,112 @@ public AddAttributesAndAttributeDependencies(IType type, TypeDefinition typeDefi public void Execute() { _typeDefinition.CustomAttributes.ForEach(AddAttributeArgumentReferenceDependenciesToOriginType); - var typeAttributes = CreateAttributesFromCustomAttributes(_typeDefinition.CustomAttributes).ToList(); - _type.Attributes.AddRange(typeAttributes); + var typeAttributeInstances = + CreateAttributesFromCustomAttributes(_typeDefinition.CustomAttributes).ToList(); + _type.Attributes.AddRange(typeAttributeInstances.Select(instance => instance.Type)); var typeAttributeDependencies = - typeAttributes.Select(attribute => new AttributeTypeDependency(_type, attribute)); + typeAttributeInstances.Select(attribute => new AttributeTypeDependency(_type, attribute)); _type.Dependencies.AddRange(typeAttributeDependencies); + SetUpAttributesForTypeGenericParameters(); CollectAttributesForMembers(); } + private void SetUpAttributesForTypeGenericParameters() + { + foreach (var genericParameter in _typeDefinition.GenericParameters) + { + var param = _type.GenericParameters.First(parameter => parameter.Name == genericParameter.Name); + var attributeInstances = + CreateAttributesFromCustomAttributes(genericParameter.CustomAttributes).ToList(); + var attributes = attributeInstances.Select(instance => instance.Type).ToList(); + _type.Attributes.AddRange(attributes); + param.Attributes.AddRange(attributes); + var genericParameterAttributeDependencies = attributeInstances.Select(attribute => + new AttributeTypeDependency(_type, attribute)); + _type.Dependencies.AddRange(genericParameterAttributeDependencies); + } + } + private void CollectAttributesForMembers() { - _typeDefinition.Fields.Where(x => !x.IsBackingField()).ForEach(SetUpAttributesForFields); + _typeDefinition.Fields.Where(x => !x.IsBackingField() && !x.IsCompilerGenerated()) + .ForEach(SetUpAttributesForFields); - _typeDefinition.Properties.ForEach(SetUpAttributesForProperties); + _typeDefinition.Properties.Where(x => !x.IsCompilerGenerated()).ForEach(SetUpAttributesForProperties); - _typeDefinition.Methods.ForEach(SetUpAttributesForMethods); + _typeDefinition.Methods.Where(x => !x.IsCompilerGenerated()).ForEach(SetUpAttributesForMethods); } private void SetUpAttributesForFields(FieldDefinition fieldDefinition) { var fieldMember = _type.GetFieldMembers().WhereFullNameIs(fieldDefinition.FullName) .RequiredNotNull(); - CollectMemberAttributesAndDependencies(fieldMember, fieldDefinition.CustomAttributes.ToList()); + CollectMemberAttributesAndDependencies(fieldMember, fieldDefinition.CustomAttributes.ToList(), + fieldMember.MemberDependencies); } private void SetUpAttributesForProperties(PropertyDefinition propertyDefinition) { var propertyMember = _type.GetPropertyMembers().WhereFullNameIs(propertyDefinition.FullName) .RequiredNotNull(); - CollectMemberAttributesAndDependencies(propertyMember, propertyDefinition.CustomAttributes.ToList()); + CollectMemberAttributesAndDependencies(propertyMember, propertyDefinition.CustomAttributes.ToList(), + propertyMember.AttributeDependencies); } private void SetUpAttributesForMethods(MethodDefinition methodDefinition) { - var methodMember = _type.GetMethodMembers().WhereFullNameIs(methodDefinition.GetFullName()) + var methodMember = _type.GetMethodMembers().WhereFullNameIs(methodDefinition.BuildFullName()) .RequiredNotNull(); var memberCustomAttributes = methodDefinition.GetAllMethodCustomAttributes().ToList(); - CollectMemberAttributesAndDependencies(methodMember, memberCustomAttributes); + SetUpAttributesForMethodGenericParameters(methodDefinition, methodMember); + CollectMemberAttributesAndDependencies(methodMember, memberCustomAttributes, + methodMember.MemberDependencies); + } + + private void SetUpAttributesForMethodGenericParameters(MethodDefinition methodDefinition, + MethodMember methodMember) + { + foreach (var genericParameter in methodDefinition.GenericParameters) + { + var param = methodMember.GenericParameters.First(parameter => parameter.Name == genericParameter.Name); + var customAttributes = genericParameter.CustomAttributes; + customAttributes.ForEach(AddAttributeArgumentReferenceDependenciesToOriginType); + var attributeInstances = CreateAttributesFromCustomAttributes(customAttributes).ToList(); + var attributes = attributeInstances.Select(instance => instance.Type).ToList(); + methodMember.Attributes.AddRange(attributes); + param.Attributes.AddRange(attributes); + var genericParameterAttributeDependencies = attributeInstances.Select(attribute => + new AttributeMemberDependency(methodMember, attribute)); + methodMember.MemberDependencies.AddRange(genericParameterAttributeDependencies); + } } private void CollectMemberAttributesAndDependencies(IMember methodMember, - List memberCustomAttributes) + List memberCustomAttributes, List attributeDependencies) { memberCustomAttributes.ForEach(AddAttributeArgumentReferenceDependenciesToOriginType); - var memberAttributes = CreateAttributesFromCustomAttributes(memberCustomAttributes).ToList(); - methodMember.Attributes.AddRange(memberAttributes); - var methodAttributeDependencies = CreateMemberAttributeDependencies(methodMember, memberAttributes); - methodMember.Dependencies.AddRange(methodAttributeDependencies); + var memberAttributeInstances = CreateAttributesFromCustomAttributes(memberCustomAttributes).ToList(); + methodMember.Attributes.AddRange(memberAttributeInstances.Select(instance => instance.Type)); + var methodAttributeDependencies = CreateMemberAttributeDependencies(methodMember, memberAttributeInstances); + attributeDependencies.AddRange(methodAttributeDependencies); } [NotNull] - private IEnumerable CreateAttributesFromCustomAttributes( + private IEnumerable> CreateAttributesFromCustomAttributes( IEnumerable customAttributes) { return customAttributes.Select(customAttribute => { var attributeTypeReference = customAttribute.AttributeType; - var attributeType = _typeFactory.GetOrCreateStubTypeFromTypeReference(attributeTypeReference); - return new Attribute(attributeType as Class); + var attributeType = _typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(attributeTypeReference); + return new TypeInstance(new Attribute(attributeType.Type as Class), + attributeType.GenericArguments, attributeType.ArrayDimensions); }); } [NotNull] private static IEnumerable CreateMemberAttributeDependencies(IMember member, - IEnumerable attributes) + IEnumerable> attributes) { return attributes.Select(attribute => new AttributeMemberDependency(member, attribute)); } @@ -107,12 +149,13 @@ private void AddAttributeArgumentReferenceDependenciesToOriginType(ICustomAttrib var attributeConstructorArgs = customAttribute.ConstructorArguments; attributeConstructorArgs - .Where(attributeArgument => attributeArgument.Value is TypeReference) + .Where(attributeArgument => attributeArgument.Value is TypeReference typeReference && + !typeReference.IsCompilerGenerated()) .Select(attributeArgument => (typeReference: attributeArgument.Value as TypeReference, attributeArgument)) .ForEach(tuple => { - var argumentType = _typeFactory.GetOrCreateStubTypeFromTypeReference(tuple.typeReference); + var argumentType = _typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(tuple.typeReference); var dependency = new TypeReferenceDependency(_type, argumentType); _type.Dependencies.Add(dependency); }); diff --git a/ArchUnitNET/Loader/LoadTasks/AddBaseClassDependency.cs b/ArchUnitNET/Loader/LoadTasks/AddBaseClassDependency.cs index 14b63a539..321d857c1 100644 --- a/ArchUnitNET/Loader/LoadTasks/AddBaseClassDependency.cs +++ b/ArchUnitNET/Loader/LoadTasks/AddBaseClassDependency.cs @@ -34,13 +34,15 @@ public void Execute() return; } - var baseType = _typeFactory.GetOrCreateStubTypeFromTypeReference(typeDefinitionBaseType); - if (!(baseType is Class baseClass)) + var baseType = _typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(typeDefinitionBaseType); + if (!(baseType.Type is Class baseClass)) { return; } - var dependency = new InheritsBaseClassDependency(_cls, baseClass); + var dependency = + new InheritsBaseClassDependency(_cls, + new TypeInstance(baseClass, baseType.GenericArguments, baseType.ArrayDimensions)); _type.Dependencies.Add(dependency); } } diff --git a/ArchUnitNET/Loader/LoadTasks/AddClassDependencies.cs b/ArchUnitNET/Loader/LoadTasks/AddClassDependencies.cs index d3abe71fb..555baa7ab 100644 --- a/ArchUnitNET/Loader/LoadTasks/AddClassDependencies.cs +++ b/ArchUnitNET/Loader/LoadTasks/AddClassDependencies.cs @@ -37,19 +37,15 @@ public void Execute() private void AddMemberDependencies() { - _type.Members.ForEach(member => - { - _dependencies.AddRange(member.MemberDependencies); - _dependencies.AddRange(member.Dependencies); - }); + _type.Members.ForEach(member => { _dependencies.AddRange(member.Dependencies); }); } private void AddInterfaceDependencies() { GetInterfacesImplementedByClass(_typeDefinition).ForEach(target => { - var targetType = _typeFactory.GetOrCreateStubTypeFromTypeReference(target); - _dependencies.Add(new ImplementsInterfaceDependency(_type, targetType)); + var targetType = _typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(target); + _dependencies.Add(new ImplementsInterfaceDependency(_type, (ITypeInstance) targetType)); }); } diff --git a/ArchUnitNET/Loader/LoadTasks/AddGenericArgumentDependencies.cs b/ArchUnitNET/Loader/LoadTasks/AddGenericArgumentDependencies.cs new file mode 100644 index 000000000..81a904cf4 --- /dev/null +++ b/ArchUnitNET/Loader/LoadTasks/AddGenericArgumentDependencies.cs @@ -0,0 +1,94 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Collections.Generic; +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Dependencies; +using JetBrains.Annotations; + +namespace ArchUnitNET.Loader.LoadTasks +{ + public class AddGenericArgumentDependencies : ILoadTask + { + [NotNull] private readonly IType _type; + + public AddGenericArgumentDependencies([NotNull] IType type) + { + _type = type; + } + + public void Execute() + { + AddTypeGenericArgumentDependencies(); + AddMemberGenericArgumentDependencies(); + + var typeDependencies = new List(); + foreach (var dependency in _type.Dependencies) + { + FindGenericArgumentsInTypeDependenciesRecursive(dependency.TargetGenericArguments, typeDependencies); + } + + _type.Dependencies.AddRange(typeDependencies); + + + foreach (var member in _type.Members) + { + var memberDependencies = new List(); + foreach (var dependency in member.Dependencies) + { + FindGenericArgumentsInMemberDependenciesRecursive(member, dependency.TargetGenericArguments, + memberDependencies); + } + + member.MemberDependencies.AddRange(memberDependencies); + } + } + + private void AddTypeGenericArgumentDependencies() + { + foreach (var parameter in _type.GenericParameters) + { + _type.Dependencies.AddRange(parameter.Dependencies); + } + } + + private void FindGenericArgumentsInTypeDependenciesRecursive( + IEnumerable targetGenericArguments, + ICollection createdDependencies) + { + foreach (var genericArgument in targetGenericArguments.Where(argument => !argument.Type.IsGenericParameter)) + { + createdDependencies.Add(new GenericArgumentTypeDependency(_type, genericArgument)); + FindGenericArgumentsInTypeDependenciesRecursive(genericArgument.GenericArguments, createdDependencies); + } + } + + private void AddMemberGenericArgumentDependencies() + { + foreach (var member in _type.Members) + { + foreach (var parameter in member.GenericParameters) + { + member.MemberDependencies.AddRange(parameter.Dependencies.Cast()); + } + } + } + + private static void FindGenericArgumentsInMemberDependenciesRecursive(IMember member, + IEnumerable targetGenericArguments, + ICollection createdDependencies) + { + foreach (var genericArgument in targetGenericArguments.Where(argument => !argument.Type.IsGenericParameter)) + { + createdDependencies.Add(new GenericArgumentMemberDependency(member, genericArgument)); + FindGenericArgumentsInMemberDependenciesRecursive(member, genericArgument.GenericArguments, + createdDependencies); + } + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Loader/LoadTasks/AddGenericParameterDependencies.cs b/ArchUnitNET/Loader/LoadTasks/AddGenericParameterDependencies.cs new file mode 100644 index 000000000..f42468043 --- /dev/null +++ b/ArchUnitNET/Loader/LoadTasks/AddGenericParameterDependencies.cs @@ -0,0 +1,61 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Dependencies; +using JetBrains.Annotations; + +namespace ArchUnitNET.Loader.LoadTasks +{ + public class AddGenericParameterDependencies : ILoadTask + { + [NotNull] private readonly IType _type; + + public AddGenericParameterDependencies([NotNull] IType type) + { + _type = type; + } + + public void Execute() + { + AddTypeGenericParameterDependencies(); + AddMemberGenericParameterDependencies(); + } + + private void AddTypeGenericParameterDependencies() + { + foreach (var genericParameter in _type.GenericParameters) + { + genericParameter.AssignDeclarer(_type); + foreach (var typeInstanceConstraint in genericParameter.TypeInstanceConstraints) + { + var dependency = + new TypeGenericParameterTypeConstraintDependency(genericParameter, typeInstanceConstraint); + genericParameter.Dependencies.Add(dependency); + } + } + } + + private void AddMemberGenericParameterDependencies() + { + foreach (var member in _type.Members) + { + foreach (var genericParameter in member.GenericParameters) + { + genericParameter.AssignDeclarer(member); + foreach (var typeInstanceConstraint in genericParameter.TypeInstanceConstraints) + { + var dependency = + new MemberGenericParameterTypeConstraintDependency(genericParameter, + typeInstanceConstraint); + genericParameter.Dependencies.Add(dependency); + } + } + } + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Loader/LoadTasks/AddMembers.cs b/ArchUnitNET/Loader/LoadTasks/AddMembers.cs index fb9e20348..d8293f1ba 100644 --- a/ArchUnitNET/Loader/LoadTasks/AddMembers.cs +++ b/ArchUnitNET/Loader/LoadTasks/AddMembers.cs @@ -11,7 +11,6 @@ using JetBrains.Annotations; using Mono.Cecil; using static ArchUnitNET.Domain.Visibility; -using GenericParameter = ArchUnitNET.Domain.GenericParameter; namespace ArchUnitNET.Loader.LoadTasks { @@ -41,17 +40,32 @@ private IEnumerable CreateMembers([NotNull] TypeDefinition typeDefiniti { return typeDefinition.Fields.Where(fieldDefinition => !fieldDefinition.IsBackingField()) .Select(CreateFieldMember).Concat(typeDefinition.Properties.Select(CreatePropertyMember) - .Concat(typeDefinition.Methods.Select(CreateMethodMember))); + .Concat(typeDefinition.Methods.Select(method => + _typeFactory.GetOrCreateMethodMemberFromMethodReference(_type, method).Member))) + .Where(member => !member.IsCompilerGenerated); } [NotNull] private IMember CreateFieldMember([NotNull] FieldDefinition fieldDefinition) { var typeReference = fieldDefinition.FieldType; - var fieldType = _typeFactory.GetOrCreateStubTypeFromTypeReference(typeReference); + var fieldType = _typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(typeReference); var visibility = GetVisibilityFromFieldDefinition(fieldDefinition); + var isCompilerGenerated = fieldDefinition.IsCompilerGenerated(); - return new FieldMember(_type, fieldDefinition.Name, fieldDefinition.FullName, visibility, fieldType); + return new FieldMember(_type, fieldDefinition.Name, fieldDefinition.FullName, visibility, fieldType, + isCompilerGenerated); + } + + [NotNull] + private IMember CreatePropertyMember(PropertyDefinition propertyDefinition) + { + var typeReference = propertyDefinition.PropertyType; + var propertyType = _typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(typeReference); + var isCompilerGenerated = propertyDefinition.IsCompilerGenerated(); + + return new PropertyMember(_type, propertyDefinition.Name, propertyDefinition.FullName, propertyType, + isCompilerGenerated); } private static Visibility GetVisibilityFromFieldDefinition([NotNull] FieldDefinition fieldDefinition) @@ -88,50 +102,5 @@ private static Visibility GetVisibilityFromFieldDefinition([NotNull] FieldDefini throw new ArgumentException("The field definition seems to have no visibility."); } - - [NotNull] - private IMember CreatePropertyMember(PropertyDefinition propertyDefinition) - { - var typeReference = propertyDefinition.PropertyType; - var propertyType = _typeFactory.GetOrCreateStubTypeFromTypeReference(typeReference); - - MethodMember getter = null; - var isVirtual = false; - - if (propertyDefinition.GetMethod != null) - { - isVirtual = propertyDefinition.GetMethod.IsVirtual; - getter = CreateMethodMember(propertyDefinition.GetMethod); - } - - MethodMember setter = null; - - if (propertyDefinition.SetMethod != null) - { - isVirtual = isVirtual || propertyDefinition.SetMethod.IsVirtual; - setter = CreateMethodMember(propertyDefinition.SetMethod); - } - - return new PropertyMember(_type, propertyDefinition.Name, propertyDefinition.FullName, - propertyType, isVirtual, getter, setter); - } - - [NotNull] - private MethodMember CreateMethodMember(MethodDefinition methodDefinition) - { - var typeReference = methodDefinition.ReturnType; - var returnType = _typeFactory.GetOrCreateStubTypeFromTypeReference(typeReference); - var parameters = methodDefinition.GetParameters(_typeFactory).ToList(); - - var visibility = methodDefinition.GetVisibility(); - var methodForm = methodDefinition.GetMethodForm(); - var methodName = methodDefinition.BuildMethodMemberName(); - var methodDefinitionFullName = methodDefinition.GetFullName(); - var genericParameters = - methodDefinition.GenericParameters.Select(parameter => new GenericParameter(parameter.Name)).ToList(); - - return new MethodMember(methodName, methodDefinitionFullName, _type, visibility, - parameters, returnType, methodDefinition.IsVirtual, methodForm, genericParameters); - } } } \ No newline at end of file diff --git a/ArchUnitNET/Loader/LoadTasks/AddMethodDependencies.cs b/ArchUnitNET/Loader/LoadTasks/AddMethodDependencies.cs index 150ab4fa9..ea5da33c6 100644 --- a/ArchUnitNET/Loader/LoadTasks/AddMethodDependencies.cs +++ b/ArchUnitNET/Loader/LoadTasks/AddMethodDependencies.cs @@ -9,12 +9,10 @@ using System.Linq; using ArchUnitNET.Domain; using ArchUnitNET.Domain.Dependencies; -using ArchUnitNET.Domain.Exceptions; using ArchUnitNET.Domain.Extensions; using JetBrains.Annotations; using Mono.Cecil; using Mono.Cecil.Cil; -using GenericParameter = ArchUnitNET.Domain.GenericParameter; namespace ArchUnitNET.Loader.LoadTasks { @@ -34,15 +32,21 @@ public AddMethodDependencies(IType type, TypeDefinition typeDefinition, TypeFact public void Execute() { _typeDefinition.Methods - .Where(methodDefinition => _type.GetMemberWithFullName(methodDefinition.FullName) is MethodMember) - .Select(definition => (methodMember: _type.GetMemberWithFullName(definition.FullName) as MethodMember, + .Where(methodDefinition => + _type.GetMemberWithFullName(methodDefinition.BuildFullName()) is MethodMember) + .Select(definition => ( + methodMember: _type.GetMemberWithFullName(definition.BuildFullName()) as MethodMember, methodDefinition: definition)) .Select(tuple => { var (methodMember, methodDefinition) = tuple; var dependencies = CreateMethodSignatureDependencies(methodDefinition, methodMember) - .Concat(CreateMethodBodyTypeDependencies(methodDefinition, methodMember)) - .Concat(CreateMethodCallDependencies(methodDefinition, methodMember)); + .Concat(CreateMethodBodyDependencies(methodDefinition, methodMember)); + if (methodDefinition.IsSetter || methodDefinition.IsGetter) + { + AssignDependenciesToProperty(methodMember, methodDefinition); + } + return (methodMember, dependencies); }) .ForEach(tuple => @@ -52,6 +56,46 @@ public void Execute() }); } + private void AssignDependenciesToProperty(MethodMember methodMember, MethodDefinition methodDefinition) + { + var methodForm = methodDefinition.GetMethodForm(); + var matchFunction = GetMatchFunction(methodForm); + matchFunction.RequiredNotNull(); + + var accessedProperty = + MatchToPropertyMember(methodMember.Name, methodMember.FullName, matchFunction); + if (accessedProperty == null) + { + return; + } + + accessedProperty.IsVirtual = accessedProperty.IsVirtual || methodMember.IsVirtual; + + switch (methodForm) + { + case MethodForm.Getter: + accessedProperty.Getter = methodMember; + break; + case MethodForm.Setter: + accessedProperty.Setter = methodMember; + break; + } + + var methodBody = methodDefinition.Body; + + if (methodBody == null) + { + return; + } + + if (!methodBody.Instructions + .Select(instruction => instruction.Operand).OfType() + .Any(definition => definition.IsBackingField())) + { + accessedProperty.IsAutoProperty = false; + } + } + [NotNull] private IEnumerable CreateMethodSignatureDependencies( MethodReference methodReference, MethodMember methodMember) @@ -62,125 +106,116 @@ private IEnumerable CreateMethodSignatureDependencies } [NotNull] - private IEnumerable CreateMethodBodyTypeDependencies( - MethodDefinition methodDefinition, - MethodMember methodMember) - { - return methodDefinition - .GetBodyTypes(_typeFactory) - .Select(bodyType => new BodyTypeMemberDependency(methodMember, bodyType)); - } - - [NotNull] - private IEnumerable CreateMethodCallDependencies(MethodDefinition methodDefinition, + private IEnumerable CreateMethodBodyDependencies(MethodDefinition methodDefinition, MethodMember methodMember) { var methodBody = methodDefinition.Resolve().Body; if (methodBody == null) { - return Enumerable.Empty(); + yield break; } - if (methodDefinition.IsSetter || methodDefinition.IsGetter) - { - AssignDependenciesToAccessedProperty(methodMember, methodBody, methodDefinition.GetMethodForm()); - return Enumerable.Empty(); - } + var visitedMethodReferences = new List {methodDefinition}; - HandlePropertyBackingFieldDependencies(methodBody); + var bodyTypes = methodDefinition.GetBodyTypes(_typeFactory).ToList(); - return CreateMethodCallDependenciesFromBody(methodMember, methodBody); - } + var castTypes = methodDefinition.GetCastTypes(_typeFactory).ToList(); - private void AssignDependenciesToAccessedProperty(MethodMember methodMember, - MethodBody methodBody, MethodForm methodForm) - { - var matchFunction = GetMatchFunction(methodForm); - matchFunction.RequiredNotNull(); + var typeCheckTypes = methodDefinition.GetTypeCheckTypes(_typeFactory).ToList(); - var accessedProperty = - MatchToPropertyMember(methodMember.Name, methodMember.FullName, matchFunction); - if (accessedProperty == null) + var metaDataTypes = methodDefinition.GetMetaDataTypes(_typeFactory).ToList(); + + var accessedFieldMembers = methodDefinition.GetAccessedFieldMembers(_typeFactory).ToList(); + + var calledMethodMembers = CreateMethodBodyDependenciesRecursive(methodBody, visitedMethodReferences, + bodyTypes, castTypes, typeCheckTypes, metaDataTypes, accessedFieldMembers); + + foreach (var calledMethodMember in calledMethodMembers.Where(method => !method.Member.IsCompilerGenerated) + .Distinct()) { - return; + yield return new MethodCallDependency(methodMember, calledMethodMember); } - var memberDependenciesToAdd = CreateMethodCallDependenciesForProperty(accessedProperty, methodBody) - .ToList(); + foreach (var bodyType in bodyTypes.Where(instance => !instance.Type.IsCompilerGenerated).Distinct()) + { + yield return new BodyTypeMemberDependency(methodMember, bodyType); + } - methodBody.Instructions - .Select(instruction => instruction.Operand) - .OfType() - .ForEach(fieldDefinition => - { - var backingField = FindMatchingField(fieldDefinition); - accessedProperty.BackingField = backingField; - }); - if (accessedProperty.BackingField != null && accessedProperty.BackingField.MemberDependencies.Count != 0) + foreach (var castType in castTypes.Where(instance => !instance.Type.IsCompilerGenerated).Distinct()) { - memberDependenciesToAdd.AddRange(accessedProperty.BackingField.MemberDependencies); + yield return new CastTypeDependency(methodMember, castType); } - if (methodForm == MethodForm.Getter) + foreach (var typeCheckType in typeCheckTypes.Where(instance => !instance.Type.IsCompilerGenerated) + .Distinct()) { - accessedProperty.Getter?.MemberDependencies.AddRange(memberDependenciesToAdd); + yield return new TypeCheckDependency(methodMember, typeCheckType); } - else if (methodForm == MethodForm.Setter) + + foreach (var metaDataType in metaDataTypes.Where(instance => !instance.Type.IsCompilerGenerated).Distinct()) { - accessedProperty.Setter?.MemberDependencies.AddRange(memberDependenciesToAdd); + yield return new MetaDataDependency(methodMember, metaDataType); } - accessedProperty.MemberDependencies.AddRange(memberDependenciesToAdd); + foreach (var fieldMember in accessedFieldMembers.Where(field => !field.IsCompilerGenerated).Distinct()) + { + yield return new AccessFieldDependency(methodMember, fieldMember); + } } - private void HandlePropertyBackingFieldDependencies(MethodBody methodBody) + + private IEnumerable CreateMethodBodyDependenciesRecursive(MethodBody methodBody, + ICollection visitedMethodReferences, List> bodyTypes, + List> castTypes, List> typeCheckTypes, + List> metaDataTypes, List accessedFieldMembers) { - methodBody.Instructions - .Where(instruction => instruction.Operand is MethodReference - && instruction.IsOperationForBackedProperty()) - .Select(instruction => (methodReference: instruction.Operand as MethodReference, - methodBodyInstruction: instruction)) - .ForEach(tuple => + var calledMethodReferences = methodBody.Instructions.Select(instruction => instruction.Operand) + .OfType(); + + foreach (var calledMethodReference in calledMethodReferences.Except(visitedMethodReferences)) + { + visitedMethodReferences.Add(calledMethodReference); + + if (calledMethodReference.IsCompilerGenerated()) { - var (methodReference, methodBodyInstruction) = tuple; - var fieldDefinitionOp = methodBodyInstruction.GetAssigneeFieldDefinition(); - if (fieldDefinitionOp == null) + MethodDefinition calledMethodDefinition; + try { - return; + calledMethodDefinition = calledMethodReference.Resolve(); } - - var backedProperty = - MatchToPropertyMember(fieldDefinitionOp.Name, fieldDefinitionOp.FullName, - GetFieldMatchFunctions()); - if (backedProperty == null) + catch (AssemblyResolutionException) { - return; + calledMethodDefinition = null; } - var calledType = - _typeFactory.GetOrCreateStubTypeFromTypeReference(methodReference.DeclaringType); + if (calledMethodDefinition == null) + { + //MethodReference to compiler generated type not resolvable, skip + continue; + } - var dependency = - CreateStubMethodCallDependencyForProperty(calledType, methodReference, backedProperty); - backedProperty.MemberDependencies.Add(dependency); - }); - } + bodyTypes.AddRange(calledMethodDefinition.GetBodyTypes(_typeFactory)); + castTypes.AddRange(calledMethodDefinition.GetCastTypes(_typeFactory)); + typeCheckTypes.AddRange(calledMethodDefinition.GetTypeCheckTypes(_typeFactory)); + metaDataTypes.AddRange(calledMethodDefinition.GetMetaDataTypes(_typeFactory)); + accessedFieldMembers.AddRange(calledMethodDefinition.GetAccessedFieldMembers(_typeFactory)); - private IEnumerable CreateMethodCallDependenciesFromBody(MethodMember methodMember, - MethodBody methodBody) - { - return methodBody.Instructions - .Select(instruction => instruction.Operand) - .OfType() - .Select(methodReference => + foreach (var dep in CreateMethodBodyDependenciesRecursive(calledMethodDefinition.Body, + visitedMethodReferences, bodyTypes, castTypes, typeCheckTypes, metaDataTypes, + accessedFieldMembers)) + { + yield return dep; + } + } + else { var calledType = - _typeFactory.GetOrCreateStubTypeFromTypeReference(methodReference.DeclaringType); - - return GetMethodMemberWithMethodReference(calledType, methodReference); - }) - .Where(calledMethodMember => calledMethodMember != null) - .Select(calledMethodMember => new MethodCallDependency(methodMember, calledMethodMember)); + _typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(calledMethodReference.DeclaringType); + var calledMethodMember = + _typeFactory.GetOrCreateMethodMemberFromMethodReference(calledType, calledMethodReference); + yield return calledMethodMember; + } + } } private static MatchFunction GetMatchFunction(MethodForm methodForm) @@ -202,78 +237,6 @@ private static MatchFunction GetMatchFunction(MethodForm methodForm) return matchFunction.RequiredNotNull(); } - private static MatchFunction GetFieldMatchFunctions() - { - return new MatchFunction(RegexUtils.MatchFieldName); - } - - private IEnumerable CreateMethodCallDependenciesForProperty( - PropertyMember accessedProperty, MethodBody methodBody) - { - return methodBody.Instructions - .Where(instruction => instruction.Operand is MethodReference) - .Where(instruction => instruction.IsMethodCallAssignment()) - .Select(instruction => (methodReference: instruction.Operand as MethodReference, - instruction)) - .Select<(MethodReference, Instruction), IMemberTypeDependency>(tuple => - { - var (methodReference, _) = tuple; - var calledType = _typeFactory.GetOrCreateStubTypeFromTypeReference(methodReference.DeclaringType); - - return CreateStubMethodCallDependencyForProperty(calledType, methodReference, accessedProperty); - }); - } - - private MethodMember GetMethodMemberWithMethodReference(IType type, MethodReference methodReference) - { - var matchingMethods = type.GetMethodMembers().Where(member => MatchesGeneric(member, methodReference)) - .ToList(); - if (matchingMethods.Count > 1) - { - throw new MultipleOccurrencesInSequenceException( - $"Multiple Methods matching {methodReference.FullName} found in provided type."); - } - - return matchingMethods.FirstOrDefault(); - } - - private bool MatchesGeneric(MethodMember methodMember, MethodReference methodReference) - { - var referenceFullName = methodReference.GetElementMethod().GetFullName(); - var memberFullName = methodMember.FullName; - var count = methodReference.GetElementMethod().GenericParameters.Count; - if (methodMember.GenericParameters.Count != count) - { - return false; - } - - var parameters = new List(); - for (var i = 0; i < count; i++) - { - parameters.Add(new[] - { - new GenericParameter(methodReference.GetElementMethod().GenericParameters[i].Name), - methodMember.GenericParameters[i] - }); - } - - parameters = parameters.OrderByDescending(genericParameters => genericParameters[0].Name.Length).ToList(); - - foreach (var genericParameters in parameters.Where(genericParameters => genericParameters[0] != null) - ) - { - referenceFullName = referenceFullName.Replace(genericParameters[0].Name, genericParameters[1].Name); - memberFullName = memberFullName.Replace(genericParameters[0].Name, genericParameters[1].Name); - } - - return memberFullName.Equals(referenceFullName); - } - - private FieldMember FindMatchingField(FieldDefinition fieldDefinition) - { - return _type.GetFieldMembersWithName(fieldDefinition.Name).SingleOrDefault(); - } - private PropertyMember MatchToPropertyMember(string name, string fullName, MatchFunction matchFunction) { try @@ -298,16 +261,6 @@ private PropertyMember MatchToPropertyMember(string name, string fullName, Match : null; } - private MethodCallDependency CreateStubMethodCallDependencyForProperty(IType calledType, - MethodReference methodReference, - PropertyMember backedProperty) - { - var calledMethodMember = - _typeFactory.CreateStubMethodMemberFromMethodReference(calledType, methodReference); - var dependency = new MethodCallDependency(backedProperty, calledMethodMember); - return dependency; - } - private PropertyMember GetPropertyMemberWithFullNameEndingWith(IType type, string detailedName) { return type.Members.OfType().FirstOrDefault(propertyMember => diff --git a/ArchUnitNET/Loader/LoadTasks/AddTypesToNamespace.cs b/ArchUnitNET/Loader/LoadTasks/AddTypesToNamespace.cs index 1b13b8a3e..aa79fc343 100644 --- a/ArchUnitNET/Loader/LoadTasks/AddTypesToNamespace.cs +++ b/ArchUnitNET/Loader/LoadTasks/AddTypesToNamespace.cs @@ -23,7 +23,7 @@ public AddTypesToNamespace(Namespace ns, List types) public void Execute() { - ((List) _ns.Types).AddRange(_types.Where(type => type.Namespace == _ns)); + ((List) _ns.Types).AddRange(_types.Where(type => type.Namespace.Equals(_ns))); } } } \ No newline at end of file diff --git a/ArchUnitNET/Loader/MethodMemberInstance.cs b/ArchUnitNET/Loader/MethodMemberInstance.cs new file mode 100644 index 000000000..2b88a34ad --- /dev/null +++ b/ArchUnitNET/Loader/MethodMemberInstance.cs @@ -0,0 +1,81 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Collections.Generic; +using System.Linq; +using ArchUnitNET.Domain; + +namespace ArchUnitNET.Loader +{ + public class MethodMemberInstance : ITypeInstance + { + public MethodMemberInstance(MethodMember member, IEnumerable declaringTypeGenericArguments, + IEnumerable memberGenericArguments) + { + Member = member; + GenericArguments = declaringTypeGenericArguments; + MemberGenericArguments = memberGenericArguments; + } + + public MethodMember Member { get; } + public IEnumerable MemberGenericArguments { get; } + public IType Type => Member.DeclaringType; + public IEnumerable GenericArguments { get; } + public bool IsArray => false; + public IEnumerable ArrayDimensions => Enumerable.Empty(); + + public bool Equals(MethodMemberInstance other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return Equals(Member, other.Member) && + GenericArguments.SequenceEqual(other.GenericArguments) && + MemberGenericArguments.SequenceEqual(other.MemberGenericArguments); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + + return Equals((MethodMemberInstance) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Member != null ? Member.GetHashCode() : 0; + hashCode = GenericArguments.Aggregate(hashCode, + (current, type) => (current * 397) ^ (type != null ? type.GetHashCode() : 0)); + hashCode = MemberGenericArguments.Aggregate(hashCode, + (current, type) => (current * 397) ^ (type != null ? type.GetHashCode() : 0)); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Loader/MethodMemberRegistry.cs b/ArchUnitNET/Loader/MethodMemberRegistry.cs new file mode 100644 index 000000000..07c49f01f --- /dev/null +++ b/ArchUnitNET/Loader/MethodMemberRegistry.cs @@ -0,0 +1,33 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System; +using System.Collections.Generic; +using System.Linq; +using ArchUnitNET.Domain; +using JetBrains.Annotations; +using Mono.Cecil; + +namespace ArchUnitNET.Loader +{ + internal class MethodMemberRegistry + { + private readonly Dictionary _allMethods = + new Dictionary(); + + public MethodMemberInstance GetOrCreateMethodFromMethodReference([NotNull] MethodReference methodReference, + [NotNull] Func createFunc) + { + return RegistryUtils.GetFromDictOrCreateAndAdd(methodReference.BuildFullName(), _allMethods, createFunc); + } + + public IEnumerable GetAllMethodMembers() + { + return _allMethods.Values.Select(instance => instance.Member).Distinct(); + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Loader/MonoCecilMemberExtensions.cs b/ArchUnitNET/Loader/MonoCecilMemberExtensions.cs index 3b4f14bec..a92c6928f 100644 --- a/ArchUnitNET/Loader/MonoCecilMemberExtensions.cs +++ b/ArchUnitNET/Loader/MonoCecilMemberExtensions.cs @@ -9,15 +9,29 @@ using System.Linq; using System.Text; using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Exceptions; +using ArchUnitNET.Domain.Extensions; using JetBrains.Annotations; using Mono.Cecil; +using Mono.Cecil.Cil; using static ArchUnitNET.Domain.Visibility; -using GenericParameter = ArchUnitNET.Domain.GenericParameter; namespace ArchUnitNET.Loader { - public static class MonoCecilMemberExtensions + internal static class MonoCecilMemberExtensions { + private static readonly OpCode[] BodyTypeOpCodes = + { + OpCodes.Box, OpCodes.Newarr, OpCodes.Initobj, OpCodes.Unbox, OpCodes.Unbox_Any, OpCodes.Ldelem_Any, + OpCodes.Ldobj, OpCodes.Stelem_Any, OpCodes.Ldelema, OpCodes.Stobj + }; //maybe not complete + + internal static string BuildFullName(this MethodReference methodReference) + { + return methodReference.FullName + methodReference.GenericParameters.Aggregate(string.Empty, + (current, newElement) => current + "<" + newElement.Name + ">"); + } + [NotNull] internal static string BuildMethodMemberName(this MethodReference methodReference) { @@ -49,26 +63,6 @@ internal static string BuildMethodMemberName(this MethodReference methodReferenc return builder.ToString(); } - internal static MethodMember CreateStubMethodMemberFromMethodReference(this TypeFactory typeFactory, IType type, - MethodReference methodReference) - { - if (type == null || methodReference == null) - { - return null; - } - - var typeReference = methodReference.ReturnType; - var returnType = typeFactory.GetOrCreateStubTypeFromTypeReference(typeReference); - var parameters = methodReference.GetParameters(typeFactory).ToList(); - var genericParameters = methodReference.GenericParameters - .Select(parameter => new GenericParameter(parameter.Name)).ToList(); - - var methodForm = methodReference.HasConstructorName() ? MethodForm.Constructor : MethodForm.Normal; - - return new MethodMember(methodReference.BuildMethodMemberName(), methodReference.FullName, type, - Public, parameters, returnType, false, methodForm, genericParameters); - } - [NotNull] internal static IEnumerable GetAllMethodCustomAttributes( this MethodDefinition methodDefinition) @@ -80,7 +74,7 @@ internal static IEnumerable GetAllMethodCustomAttributes( } [NotNull] - internal static IEnumerable GetSignatureTypes(this MethodReference methodReference, + internal static IEnumerable> GetSignatureTypes(this MethodReference methodReference, TypeFactory typeFactory) { var parameters = GetAllParameters(methodReference, typeFactory).ToList(); @@ -93,15 +87,15 @@ internal static IEnumerable GetSignatureTypes(this MethodReference method return parameters; } - private static IType GetReturnType(this MethodReference methodReference, TypeFactory typeFactory) + private static ITypeInstance GetReturnType(this MethodReference methodReference, TypeFactory typeFactory) { return ReturnsVoid(methodReference) ? null - : typeFactory.GetOrCreateStubTypeFromTypeReference(methodReference.MethodReturnType.ReturnType); + : typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(methodReference.MethodReturnType.ReturnType); } [NotNull] - private static IEnumerable GetAllParameters(this MethodReference methodReference, + private static IEnumerable> GetAllParameters(this MethodReference methodReference, TypeFactory typeFactory) { var parameters = methodReference.GetParameters(typeFactory).ToList(); @@ -111,36 +105,126 @@ private static IEnumerable GetAllParameters(this MethodReference methodRe } [NotNull] - internal static IEnumerable GetParameters(this MethodReference method, TypeFactory typeFactory) + internal static IEnumerable> GetParameters(this MethodReference method, + TypeFactory typeFactory) { return method.Parameters.Select(parameter => { var typeReference = parameter.ParameterType; - return typeFactory.GetOrCreateStubTypeFromTypeReference(typeReference); + return typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(typeReference); }).Distinct(); } [NotNull] - private static IEnumerable GetGenericParameters(this MethodReference method, TypeFactory typeFactory) + private static IEnumerable> GetGenericParameters(this MethodReference method, + TypeFactory typeFactory) { return method.GenericParameters.Select(parameter => { var typeReference = parameter.GetElementType(); - return typeFactory.GetOrCreateStubTypeFromTypeReference(typeReference); + return typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(typeReference); }).Distinct(); } [NotNull] - internal static IEnumerable GetBodyTypes(this MethodDefinition methodDefinition, TypeFactory typeFactory) + internal static IEnumerable> GetBodyTypes(this MethodDefinition methodDefinition, + TypeFactory typeFactory) { - return methodDefinition.Body?.Variables.Select(variableDefinition => + var instructions = methodDefinition.Body?.Instructions ?? Enumerable.Empty(); + + var bodyTypes = instructions + .Where(inst => BodyTypeOpCodes.Contains(inst.OpCode) && inst.Operand is TypeReference) + .Select(inst => typeFactory.GetOrCreateStubTypeInstanceFromTypeReference((TypeReference) inst.Operand)); + + //OpCodes.Ldstr should create a dependency to string, but it does not have a TypeReference as Operand so no Type can be created + + bodyTypes = bodyTypes.Union(methodDefinition.Body?.Variables.Select(variableDefinition => { var variableTypeReference = variableDefinition.VariableType; - return typeFactory.GetOrCreateStubTypeFromTypeReference(variableTypeReference); - }).Distinct() ?? Enumerable.Empty(); + return typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(variableTypeReference); + }) ?? Enumerable.Empty>()).Distinct(); + + return bodyTypes; } - public static MethodForm GetMethodForm(this MethodDefinition methodDefinition) + [NotNull] + internal static IEnumerable> GetCastTypes(this MethodDefinition methodDefinition, + TypeFactory typeFactory) + { + var instructions = methodDefinition.Body?.Instructions ?? Enumerable.Empty(); + + return instructions.Where(inst => inst.OpCode == OpCodes.Castclass && inst.Operand is TypeReference) + .Select(inst => typeFactory.GetOrCreateStubTypeInstanceFromTypeReference((TypeReference) inst.Operand)); + } + + [NotNull] + internal static IEnumerable> GetMetaDataTypes(this MethodDefinition methodDefinition, + TypeFactory typeFactory) + { + var instructions = methodDefinition.Body?.Instructions ?? Enumerable.Empty(); + + return instructions.Where(inst => inst.OpCode == OpCodes.Ldtoken && inst.Operand is TypeReference) + .Select(inst => typeFactory.GetOrCreateStubTypeInstanceFromTypeReference((TypeReference) inst.Operand)); + } + + [NotNull] + internal static IEnumerable> GetTypeCheckTypes(this MethodDefinition methodDefinition, + TypeFactory typeFactory) + { + var instructions = methodDefinition.Body?.Instructions ?? Enumerable.Empty(); + + return instructions.Where(inst => inst.OpCode == OpCodes.Isinst && inst.Operand is TypeReference) + .Select(inst => typeFactory.GetOrCreateStubTypeInstanceFromTypeReference((TypeReference) inst.Operand)); + } + + [NotNull] + internal static IEnumerable GetAccessedFieldMembers(this MethodDefinition methodDefinition, + TypeFactory typeFactory) + { + var accessedFieldMembers = new List(); + var instructions = methodDefinition.Body?.Instructions.ToList() ?? new List(); + var accessedFieldReferences = + instructions.Select(inst => inst.Operand).OfType().Distinct(); + + foreach (var fieldReference in accessedFieldReferences) + { + var declaringType = + typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(fieldReference.DeclaringType); + var matchingFieldMembers = declaringType.Type.GetFieldMembers() + .Where(member => member.Name == fieldReference.Name).ToList(); + + switch (matchingFieldMembers.Count) + { + case 0: + var stubFieldMember = + typeFactory.CreateStubFieldMemberFromFieldReference(declaringType.Type, fieldReference); + accessedFieldMembers.Add(stubFieldMember); + break; + case 1: + accessedFieldMembers.Add(matchingFieldMembers.First()); + break; + default: + throw new MultipleOccurrencesInSequenceException( + $"Multiple Fields matching {fieldReference.FullName} found in provided type."); + } + } + + return accessedFieldMembers.Distinct(); + } + + internal static bool IsCompilerGenerated(this MemberReference memberReference) + { + var declaringType = memberReference.Resolve()?.DeclaringType ?? memberReference.DeclaringType; + return declaringType != null && declaringType.Name.HasCompilerGeneratedName() || + memberReference.Name.HasCompilerGeneratedName(); + } + + internal static bool HasCompilerGeneratedName(this string name) + { + return name.StartsWith("<") || name.StartsWith("!"); + } + + internal static MethodForm GetMethodForm(this MethodDefinition methodDefinition) { if (methodDefinition.IsConstructor) { @@ -160,18 +244,23 @@ private static bool ReturnsVoid(this IMethodSignature methodSignature) return methodSignature.MethodReturnType.ReturnType.FullName.Equals("System.Void"); } - public static bool HasConstructorName(this MethodReference methodReference) + internal static bool HasConstructorName(this MethodReference methodReference) { return methodReference.Name == ".ctor" || methodReference.Name == ".cctor"; } - public static bool IsBackingField(this FieldReference fieldReference) + internal static bool IsBackingField(this FieldReference fieldReference) { return fieldReference.FullName.Contains(StaticConstants.BackingField); } - public static Visibility GetVisibility(this MethodDefinition methodDefinition) + internal static Visibility GetVisibility([CanBeNull] this MethodDefinition methodDefinition) { + if (methodDefinition == null) + { + return NotAccessible; + } + if (methodDefinition.IsPublic) { return Public; @@ -204,11 +293,5 @@ public static Visibility GetVisibility(this MethodDefinition methodDefinition) throw new ArgumentException("The method definition seems to have no visibility."); } - - public static string GetFullName(this MethodReference methodReference) - { - return methodReference.FullName + methodReference.GenericParameters.Aggregate(string.Empty, - (current, newElement) => current + "<" + newElement.Name + ">"); - } } } \ No newline at end of file diff --git a/ArchUnitNET/Loader/MonoCecilTypeExtensions.cs b/ArchUnitNET/Loader/MonoCecilTypeExtensions.cs new file mode 100644 index 000000000..c6fa84320 --- /dev/null +++ b/ArchUnitNET/Loader/MonoCecilTypeExtensions.cs @@ -0,0 +1,100 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System; +using ArchUnitNET.Domain; +using JetBrains.Annotations; +using Mono.Cecil; +using static ArchUnitNET.Domain.Visibility; +using GenericParameter = Mono.Cecil.GenericParameter; + +namespace ArchUnitNET.Loader +{ + internal static class MonoCecilTypeExtensions + { + internal static string BuildFullName(this TypeReference typeReference) + { + if (typeReference.IsGenericParameter) + { + var genericParameter = (GenericParameter) typeReference; + + return (genericParameter.Type == GenericParameterType.Type + ? genericParameter.DeclaringType.FullName + : genericParameter.DeclaringMethod.FullName) + + "+<" + genericParameter.Name + ">"; + } + + return typeReference.FullName.Replace("/", "+"); + } + + internal static GenericParameterVariance GetVariance(this GenericParameter genericParameter) + { + if (genericParameter.IsCovariant) + { + return GenericParameterVariance.Covariant; + } + + if (genericParameter.IsContravariant) + { + return GenericParameterVariance.Contravariant; + } + + return GenericParameterVariance.NonVariant; + } + + internal static bool IsAttribute([CanBeNull] this TypeDefinition typeDefinition) + { + if (typeDefinition?.BaseType != null) + { + return typeDefinition.BaseType.FullName == "System.Attribute" || + IsAttribute(typeDefinition.BaseType.Resolve()); + } + + return false; + } + + internal static Visibility GetVisibility([CanBeNull] this TypeDefinition typeDefinition) + { + if (typeDefinition == null) + { + return NotAccessible; + } + + if (typeDefinition.IsPublic || typeDefinition.IsNestedPublic) + { + return Public; + } + + if (typeDefinition.IsNestedPrivate) + { + return Private; + } + + if (typeDefinition.IsNestedFamily) + { + return Protected; + } + + if (typeDefinition.IsNestedFamilyOrAssembly) + { + return ProtectedInternal; + } + + if (typeDefinition.IsNestedFamilyAndAssembly) + { + return PrivateProtected; + } + + if (typeDefinition.IsNestedAssembly || typeDefinition.IsNotPublic) + { + return Internal; + } + + throw new ArgumentException("The provided type definition seems to have no visibility."); + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Loader/RegexUtils.cs b/ArchUnitNET/Loader/RegexUtils.cs index 7cafed55a..dce173dcc 100644 --- a/ArchUnitNET/Loader/RegexUtils.cs +++ b/ArchUnitNET/Loader/RegexUtils.cs @@ -5,29 +5,14 @@ // SPDX-License-Identifier: Apache-2.0 using System.Text.RegularExpressions; -using ArchUnitNET.Domain; namespace ArchUnitNET.Loader { public static class RegexUtils { - private static readonly Regex BackingFieldRegex = new Regex(@"<(.+)>" + StaticConstants.BackingField); private static readonly Regex GetMethodPropertyMemberRegex = new Regex(@"get_(.+)\(\)"); private static readonly Regex SetMethodPropertyMemberRegex = new Regex(@"set_(.+)\((.+)\)"); - - public static string MatchFieldName(string fieldName) - { - var match = BackingFieldRegex.Match(fieldName); - if (!match.Success) - { - return null; - } - - var matchingPropertyName = match.Groups[1].Value; - return matchingPropertyName; - } - public static string MatchGetPropertyName(string methodName) { var match = GetMethodPropertyMemberRegex.Match(methodName); diff --git a/ArchUnitNET/Loader/Type.cs b/ArchUnitNET/Loader/Type.cs index 5470b8f53..9a4f8e4f5 100644 --- a/ArchUnitNET/Loader/Type.cs +++ b/ArchUnitNET/Loader/Type.cs @@ -15,7 +15,7 @@ namespace ArchUnitNET.Loader public class Type : IType { public Type(string fullname, string name, Assembly assembly, Namespace namespc, Visibility visibility, - bool isNested) + bool isNested, bool isGeneric, bool isStub, bool isCompilerGenerated) { FullName = fullname; Name = name; @@ -23,8 +23,13 @@ public Type(string fullname, string name, Assembly assembly, Namespace namespc, Namespace = namespc; Visibility = visibility; IsNested = isNested; + IsGeneric = isGeneric; + IsStub = isStub; + IsCompilerGenerated = isCompilerGenerated; } + public bool IsAnonymousType { get; } + public string Name { get; } public string FullName { get; } @@ -37,12 +42,13 @@ public Type(string fullname, string name, Assembly assembly, Namespace namespc, public bool IsNested { get; } - public MemberList Members { get; } = new MemberList(); - public List GenericTypeParameters { get; set; } - public IType GenericType { get; set; } - - public List GenericTypeArguments { get; set; } + public bool IsGeneric { get; } + public bool IsGenericParameter => false; + public bool IsCompilerGenerated { get; } + public List GenericParameters { get; } = new List(); + public bool IsStub { get; } + public MemberList Members { get; } = new MemberList(); public List Attributes { get; } = new List(); public List Dependencies { get; } = new List(); @@ -56,7 +62,7 @@ public Type(string fullname, string name, Assembly assembly, Namespace namespc, public bool ImplementsInterface(Interface intf) { return ImplementedInterfaces.Any(implementedInterface => - Equals(implementedInterface, intf) || Equals(implementedInterface.GenericType, intf)); + Equals(implementedInterface, intf)); } public bool ImplementsInterface(string pattern, bool useRegularExpressions = false) @@ -67,9 +73,7 @@ public bool ImplementsInterface(string pattern, bool useRegularExpressions = fal } return ImplementedInterfaces.Any(implementedInterface => - implementedInterface.FullNameMatches(pattern, useRegularExpressions) || - implementedInterface.GenericType != null && - implementedInterface.GenericType.FullNameMatches(pattern, useRegularExpressions)); + implementedInterface.FullNameMatches(pattern, useRegularExpressions)); } public bool IsAssignableTo(IType assignableToType) diff --git a/ArchUnitNET/Loader/TypeFactory.cs b/ArchUnitNET/Loader/TypeFactory.cs index e0470f3b7..4dfb8fc86 100644 --- a/ArchUnitNET/Loader/TypeFactory.cs +++ b/ArchUnitNET/Loader/TypeFactory.cs @@ -4,14 +4,14 @@ // // SPDX-License-Identifier: Apache-2.0 -using System; +using System.Collections.Generic; using System.Linq; using ArchUnitNET.Domain; using ArchUnitNET.Loader.LoadTasks; using JetBrains.Annotations; using Mono.Cecil; using static ArchUnitNET.Domain.Visibility; -using Attribute = ArchUnitNET.Domain.Attribute; +using GenericParameter = ArchUnitNET.Domain.GenericParameter; namespace ArchUnitNET.Loader { @@ -19,37 +19,116 @@ internal class TypeFactory { private readonly AssemblyRegistry _assemblyRegistry; private readonly LoadTaskRegistry _loadTaskRegistry; + private readonly MethodMemberRegistry _methodMemberRegistry; private readonly NamespaceRegistry _namespaceRegistry; private readonly TypeRegistry _typeRegistry; - public TypeFactory(TypeRegistry typeRegistry, LoadTaskRegistry loadTaskRegistry, - AssemblyRegistry assemblyRegistry, - NamespaceRegistry namespaceRegistry) + public TypeFactory(TypeRegistry typeRegistry, MethodMemberRegistry methodMemberRegistry, + LoadTaskRegistry loadTaskRegistry, AssemblyRegistry assemblyRegistry, NamespaceRegistry namespaceRegistry) { _loadTaskRegistry = loadTaskRegistry; _assemblyRegistry = assemblyRegistry; _namespaceRegistry = namespaceRegistry; _typeRegistry = typeRegistry; + _methodMemberRegistry = methodMemberRegistry; + } + + public IEnumerable GetAllNonCompilerGeneratedTypes() + { + return _typeRegistry.GetAllTypes().Where(type => !type.IsCompilerGenerated); } [NotNull] internal IType GetOrCreateTypeFromTypeReference(TypeReference typeReference) { return _typeRegistry.GetOrCreateTypeFromTypeReference(typeReference, - s => CreateTypeFromTypeReference(typeReference, false)); + s => CreateTypeFromTypeReference(typeReference, false)).Type; } [NotNull] - internal IType GetOrCreateStubTypeFromTypeReference(TypeReference typeReference) + internal ITypeInstance GetOrCreateStubTypeInstanceFromTypeReference(TypeReference typeReference) { return _typeRegistry.GetOrCreateTypeFromTypeReference(typeReference, - f => CreateTypeFromTypeReference(typeReference, true)); + s => CreateTypeFromTypeReference(typeReference, true)); + } + + [NotNull] + internal MethodMemberInstance GetOrCreateMethodMemberFromMethodReference([NotNull] IType type, + [NotNull] MethodReference methodReference) + { + return _methodMemberRegistry.GetOrCreateMethodFromMethodReference(methodReference, + s => CreateMethodMemberFromMethodReference(new TypeInstance(type), methodReference)); + } + + [NotNull] + internal MethodMemberInstance GetOrCreateMethodMemberFromMethodReference( + [NotNull] ITypeInstance typeInstance, [NotNull] MethodReference methodReference) + { + return _methodMemberRegistry.GetOrCreateMethodFromMethodReference(methodReference, + s => CreateMethodMemberFromMethodReference(typeInstance, methodReference)); } [NotNull] - private IType CreateTypeFromTypeReference(TypeReference typeReference, bool isStub) + private ITypeInstance CreateTypeFromTypeReference(TypeReference typeReference, bool isStub) { - var type = SetupCreatedType(typeReference); + if (typeReference.IsGenericParameter) + { + var genericParameter = (Mono.Cecil.GenericParameter) typeReference; + var declarerIsMethod = genericParameter.Type == GenericParameterType.Method; + var declaringTypeFullName = declarerIsMethod + ? genericParameter.DeclaringMethod.BuildFullName() + : genericParameter.DeclaringType.BuildFullName(); + + return new TypeInstance(CreateGenericParameter(genericParameter, + declaringTypeFullName, + declarerIsMethod)); + } + + if (typeReference.IsArray) + { + var dimensions = new List(); + do + { + var arrayType = (ArrayType) typeReference; + dimensions.Add(arrayType.Rank); + typeReference = arrayType.ElementType; + } while (typeReference.IsArray); + + var elementTypeInstance = GetOrCreateStubTypeInstanceFromTypeReference(typeReference); + switch (elementTypeInstance.Type) + { + case Interface intf: + return new TypeInstance(intf, elementTypeInstance.GenericArguments, dimensions); + case Attribute att: + return new TypeInstance(att, elementTypeInstance.GenericArguments, dimensions); + case Class cls: + return new TypeInstance(cls, elementTypeInstance.GenericArguments, dimensions); + default: + return new TypeInstance(elementTypeInstance.Type, elementTypeInstance.GenericArguments, + dimensions); + } + } + + if (typeReference.IsGenericInstance) + { + var elementType = GetOrCreateStubTypeInstanceFromTypeReference(typeReference.GetElementType()).Type; + var genericInstance = (GenericInstanceType) typeReference; + var genericArguments = genericInstance.GenericArguments + .Select(CreateGenericArgumentFromTypeReference) + .Where(argument => !argument.Type.IsCompilerGenerated); + switch (elementType) + { + case Interface intf: + return new TypeInstance(intf, genericArguments); + case Attribute att: + return new TypeInstance(att, genericArguments); + case Class cls: + return new TypeInstance(cls, genericArguments); + default: + return new TypeInstance(elementType, genericArguments); + } + } + TypeDefinition typeDefinition; try @@ -61,133 +140,172 @@ private IType CreateTypeFromTypeReference(TypeReference typeReference, bool isSt typeDefinition = null; } + var typeName = typeReference.BuildFullName(); + var currentNamespace = _namespaceRegistry.GetOrCreateNamespace(typeReference.IsNested + ? typeReference.DeclaringType.Namespace + : typeReference.Namespace); + var currentAssembly = _assemblyRegistry.GetOrCreateAssembly(typeReference.Module.Assembly.Name.FullName, + typeReference.Module.Assembly.FullName, true); + + Type type; + bool isCompilerGenerated, isNested, isGeneric; + if (typeDefinition == null) { - return new Class(type); + 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(new Class(type)); } - IType createdType; + var visibility = typeDefinition.GetVisibility(); + isCompilerGenerated = typeDefinition.IsCompilerGenerated(); + isNested = typeDefinition.IsNested; + isGeneric = typeDefinition.HasGenericParameters; + type = new Type(typeName, typeReference.Name, currentAssembly, currentNamespace, visibility, isNested, + isGeneric, isStub, isCompilerGenerated); + + var genericParameters = GetGenericParameters(typeDefinition); + type.GenericParameters.AddRange(genericParameters); + + ITypeInstance createdTypeInstance; + var isInterface = typeDefinition.IsInterface; if (typeDefinition.IsInterface) { - createdType = new Interface(type); + createdTypeInstance = new TypeInstance(new Interface(type)); } else { - createdType = IsAttribute(typeDefinition) - ? new Attribute(type, typeDefinition.IsAbstract, typeDefinition.IsSealed) - : new Class(type, typeDefinition.IsAbstract, typeDefinition.IsSealed, - typeDefinition.IsValueType, typeDefinition.IsEnum); + if (typeDefinition.IsAttribute()) + { + createdTypeInstance = + new TypeInstance(new Attribute(type, typeDefinition.IsAbstract, + typeDefinition.IsSealed)); + } + else + { + createdTypeInstance = new TypeInstance(new Class(type, typeDefinition.IsAbstract, + typeDefinition.IsSealed, typeDefinition.IsValueType, typeDefinition.IsEnum)); + } } - if (isStub) + if (!isStub && !isCompilerGenerated) { - return createdType; - } + if (!isInterface) + { + LoadBaseTask((Class) createdTypeInstance.Type, type, typeDefinition); + } - if (createdType is Class @class) - { - LoadBaseTask(@class, type, typeDefinition); + LoadNonBaseTasks(createdTypeInstance.Type, type, typeDefinition); } - LoadNonBaseTasks(createdType, type, typeDefinition); - - return createdType; + return createdTypeInstance; } - private static bool IsAttribute([CanBeNull] TypeDefinition typeDefinition) + [NotNull] + private MethodMemberInstance CreateMethodMemberFromMethodReference( + [NotNull] ITypeInstance typeInstance, [NotNull] MethodReference methodReference) { - if (typeDefinition?.BaseType != null) + if (methodReference.IsGenericInstance) { - return typeDefinition.BaseType.FullName == "System.Attribute" || - IsAttribute(typeDefinition.BaseType.Resolve()); + var elementMethod = + CreateMethodMemberFromMethodReference(typeInstance, methodReference.GetElementMethod()).Member; + + var genericInstanceMethod = (GenericInstanceMethod) methodReference; + var genericArguments = genericInstanceMethod.GenericArguments + .Select(CreateGenericArgumentFromTypeReference) + .Where(argument => !argument.Type.IsCompilerGenerated); + + return new MethodMemberInstance(elementMethod, typeInstance.GenericArguments, genericArguments); } - return false; - } + var returnTypeReference = methodReference.ReturnType; + var returnType = GetOrCreateStubTypeInstanceFromTypeReference(returnTypeReference); - [NotNull] - private Type SetupCreatedType(TypeReference typeReference) - { - var typeNamespaceName = typeReference.Namespace; - var currentAssembly = _assemblyRegistry.GetOrCreateAssembly(typeReference.Module.Assembly.Name.FullName, - typeReference.Module.Assembly.FullName, true); - var currentNamespace = _namespaceRegistry.GetOrCreateNamespace(typeNamespaceName); - TypeDefinition typeDefinition; + var name = methodReference.BuildMethodMemberName(); + var fullName = methodReference.BuildFullName(); + var isGeneric = methodReference.HasGenericParameters; + var isCompilerGenerated = methodReference.IsCompilerGenerated(); + MethodForm methodForm; + Visibility visibility; + bool isStub; + + MethodDefinition methodDefinition; try { - typeDefinition = typeReference.Resolve(); + methodDefinition = methodReference.Resolve(); } catch (AssemblyResolutionException) { - typeDefinition = null; + methodDefinition = null; } - var visibility = GetVisibilityFromTypeDefinition(typeDefinition); - var isNested = typeReference.IsNested; - var type = new Type(typeReference.FullName.Replace("/", "+"), typeReference.Name, currentAssembly, - currentNamespace, visibility, isNested); - AssignGenericProperties(typeReference, type, typeDefinition); - return type; - } - - private static Visibility GetVisibilityFromTypeDefinition([CanBeNull] TypeDefinition typeDefinition) - { - if (typeDefinition == null) + if (methodDefinition == null) { - return NotAccessible; + visibility = Public; + methodForm = methodReference.HasConstructorName() ? MethodForm.Constructor : MethodForm.Normal; + isStub = true; } - - if (typeDefinition.IsPublic || typeDefinition.IsNestedPublic) + else { - return Public; + visibility = methodDefinition.GetVisibility(); + methodForm = methodDefinition.GetMethodForm(); + isStub = false; } - if (typeDefinition.IsNestedPrivate) - { - return Private; - } + var methodMember = new MethodMember(name, fullName, typeInstance.Type, visibility, returnType, + false, methodForm, isGeneric, isStub, isCompilerGenerated); - if (typeDefinition.IsNestedFamily) - { - return Protected; - } + var parameters = methodReference.GetParameters(this).ToList(); + methodMember.ParameterInstances.AddRange(parameters); - if (typeDefinition.IsNestedFamilyOrAssembly) - { - return ProtectedInternal; - } + var genericParameters = GetGenericParameters(methodReference); + methodMember.GenericParameters.AddRange(genericParameters); - if (typeDefinition.IsNestedFamilyAndAssembly) - { - return PrivateProtected; - } + return new MethodMemberInstance(methodMember, typeInstance.GenericArguments, + Enumerable.Empty()); + } - if (typeDefinition.IsNestedAssembly || typeDefinition.IsNotPublic) - { - return Internal; - } + [NotNull] + internal FieldMember CreateStubFieldMemberFromFieldReference([NotNull] IType type, + [NotNull] FieldReference fieldReference) + { + var typeReference = fieldReference.FieldType; + var fieldType = GetOrCreateStubTypeInstanceFromTypeReference(typeReference); + var isCompilerGenerated = fieldReference.IsCompilerGenerated(); - throw new ArgumentException("The provided type definition seems to have no visibility."); + return new FieldMember(type, fieldReference.Name, fieldReference.FullName, Public, fieldType, + isCompilerGenerated); } - private void AssignGenericProperties(IGenericParameterProvider typeReference, Type type, - [CanBeNull] TypeDefinition typeDefinition) + public IEnumerable GetGenericParameters(IGenericParameterProvider genericParameterProvider) { - if (typeReference is GenericInstanceType genericInstanceType) - { - var elementTypeReference = genericInstanceType.ElementType; - type.GenericType = - GetOrCreateStubTypeFromTypeReference(elementTypeReference); - type.GenericTypeArguments = genericInstanceType.GenericArguments? - .AsEnumerable().Select(GetOrCreateStubTypeFromTypeReference).ToList(); - } + return genericParameterProvider == null + ? Enumerable.Empty() + : genericParameterProvider.GenericParameters + .Select(param => GetOrCreateStubTypeInstanceFromTypeReference(param).Type).Cast(); + } - if (typeReference.HasGenericParameters) - { - type.GenericTypeParameters = typeDefinition?.GenericParameters? - .Select(GetOrCreateStubTypeFromTypeReference).ToList(); - } + + private GenericParameter CreateGenericParameter(Mono.Cecil.GenericParameter genericParameter, + [NotNull] string declarerFullName, bool declarerIsMethod) + { + var isCompilerGenerated = genericParameter.IsCompilerGenerated(); + var variance = genericParameter.GetVariance(); + var typeConstraints = genericParameter.Constraints.Select(con => + GetOrCreateStubTypeInstanceFromTypeReference(con.ConstraintType)); + return new GenericParameter(declarerFullName, genericParameter.Name, variance, typeConstraints, + genericParameter.HasReferenceTypeConstraint, genericParameter.HasNotNullableValueTypeConstraint, + genericParameter.HasDefaultConstructorConstraint, isCompilerGenerated, declarerIsMethod); + } + + internal GenericArgument CreateGenericArgumentFromTypeReference(TypeReference typeReference) + { + return new GenericArgument(GetOrCreateStubTypeInstanceFromTypeReference(typeReference)); } private void LoadBaseTask(Class cls, Type type, TypeDefinition typeDefinition) @@ -210,15 +328,18 @@ private void LoadNonBaseTasks(IType createdType, Type type, TypeDefinition typeD _loadTaskRegistry.Add(typeof(AddMembers), new AddMembers(createdType, typeDefinition, this, type.Members)); + _loadTaskRegistry.Add(typeof(AddGenericParameterDependencies), + new AddGenericParameterDependencies(type)); _loadTaskRegistry.Add(typeof(AddAttributesAndAttributeDependencies), new AddAttributesAndAttributeDependencies(createdType, typeDefinition, this)); _loadTaskRegistry.Add(typeof(AddFieldAndPropertyDependencies), new AddFieldAndPropertyDependencies(createdType)); _loadTaskRegistry.Add(typeof(AddMethodDependencies), new AddMethodDependencies(createdType, typeDefinition, this)); - _loadTaskRegistry.Add(typeof(AddClassDependencies), new AddClassDependencies(createdType, - typeDefinition, this, - type.Dependencies)); + _loadTaskRegistry.Add(typeof(AddGenericArgumentDependencies), + new AddGenericArgumentDependencies(type)); + _loadTaskRegistry.Add(typeof(AddClassDependencies), + new AddClassDependencies(createdType, typeDefinition, this, type.Dependencies)); _loadTaskRegistry.Add(typeof(AddBackwardsDependencies), new AddBackwardsDependencies(createdType)); } } diff --git a/ArchUnitNET/Loader/TypeInstance.cs b/ArchUnitNET/Loader/TypeInstance.cs new file mode 100644 index 000000000..5dd9c12cd --- /dev/null +++ b/ArchUnitNET/Loader/TypeInstance.cs @@ -0,0 +1,83 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Collections.Generic; +using System.Linq; +using ArchUnitNET.Domain; + +namespace ArchUnitNET.Loader +{ + public class TypeInstance : ITypeInstance where T : IType + { + public TypeInstance(T type, IEnumerable genericArguments, IEnumerable arrayDimensions) + { + Type = type; + GenericArguments = genericArguments; + ArrayDimensions = arrayDimensions; + IsArray = ArrayDimensions.Any(); + } + + public TypeInstance(T type, IEnumerable genericArguments) + : this(type, genericArguments, Enumerable.Empty()) + { + } + + public TypeInstance(T type) + : this(type, Enumerable.Empty()) + { + } + + public T Type { get; } + public IEnumerable GenericArguments { get; } + public bool IsArray { get; } + public IEnumerable ArrayDimensions { get; } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + return obj.GetType() == GetType() && Equals((TypeInstance) obj); + } + + private bool Equals(TypeInstance other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return Equals(Type, other.Type) && GenericArguments.SequenceEqual(other.GenericArguments) && + Equals(IsArray, other.IsArray) && ArrayDimensions.SequenceEqual(other.ArrayDimensions); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Type != null ? Type.GetHashCode() : 0; + hashCode = GenericArguments.Aggregate(hashCode, + (current, type) => (current * 397) ^ (type != null ? type.GetHashCode() : 0)); + hashCode = (hashCode * 397) ^ IsArray.GetHashCode(); + hashCode = ArrayDimensions.Aggregate(hashCode, (current, dim) => (current * 397) ^ dim.GetHashCode()); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/ArchUnitNET/Loader/TypeRegistry.cs b/ArchUnitNET/Loader/TypeRegistry.cs index 72b237b22..c5ab2e092 100644 --- a/ArchUnitNET/Loader/TypeRegistry.cs +++ b/ArchUnitNET/Loader/TypeRegistry.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Linq; using ArchUnitNET.Domain; using JetBrains.Annotations; using Mono.Cecil; @@ -14,13 +15,18 @@ namespace ArchUnitNET.Loader { internal class TypeRegistry { - private readonly Dictionary _allTypes = new Dictionary(); + private readonly Dictionary> _allTypes = + new Dictionary>(); - public IType GetOrCreateTypeFromTypeReference([NotNull] TypeReference typeReference, - [NotNull] Func createFunc) + public ITypeInstance GetOrCreateTypeFromTypeReference([NotNull] TypeReference typeReference, + [NotNull] Func> createFunc) { - return RegistryUtils.GetFromDictOrCreateAndAdd(typeReference.FullName, _allTypes, - createFunc); + return RegistryUtils.GetFromDictOrCreateAndAdd(typeReference.BuildFullName(), _allTypes, createFunc); + } + + public IEnumerable GetAllTypes() + { + return _allTypes.Values.Select(instance => instance.Type).Distinct(); } } } \ No newline at end of file diff --git a/ArchUnitNETTests/Dependencies/AccessFieldDependenciesTests.cs b/ArchUnitNETTests/Dependencies/AccessFieldDependenciesTests.cs new file mode 100644 index 000000000..f82a9522d --- /dev/null +++ b/ArchUnitNETTests/Dependencies/AccessFieldDependenciesTests.cs @@ -0,0 +1,184 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; +using Xunit; + +namespace ArchUnitNETTests.Dependencies +{ + public class AccessFieldDependenciesTests + { + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(AccessFieldDependenciesTests).Assembly).Build(); + + private readonly Class _accessingClass; + + private readonly Class _classWithNonStaticFields; + private readonly Class _classWithStaticFields; + private readonly FieldMember _constFieldMember; + + private readonly FieldMember _nonStaticFieldMember; + private readonly FieldMember _staticFieldMember; + + public AccessFieldDependenciesTests() + { + _classWithNonStaticFields = Architecture.GetClassOfType(typeof(ClassWithNonStaticFields)); + _classWithStaticFields = Architecture.GetClassOfType(typeof(ClassWithStaticFields)); + _accessingClass = Architecture.GetClassOfType(typeof(ClassAccessingFields)); + + + _nonStaticFieldMember = _classWithNonStaticFields.GetFieldMembersWithName("NonStaticField").First(); + _constFieldMember = _classWithStaticFields.GetFieldMembersWithName("ConstField").First(); + _staticFieldMember = _classWithStaticFields.GetFieldMembersWithName("StaticField").First(); + } + + [Fact(Skip = + "Can't find dependency, because only OpCode Ldstr is used and no Reference to the type which declares the const field can be found")] + public void PropertyAccessToConstFieldFound() + { + var property = _accessingClass.GetPropertyMembers() + .First(member => member.FullNameContains("PropertyAccessingConstField")); + var propertyTypeDependencies = property.GetTypeDependencies().ToList(); + var propertyFieldDependencies = property.GetAccessedFieldMembers().ToList(); + + Assert.Contains(_classWithStaticFields, propertyTypeDependencies); + Assert.Contains(_constFieldMember, propertyFieldDependencies); + } + + [Fact] + public void PropertyAccessToStaticFieldFound() + { + var property = _accessingClass.GetPropertyMembers() + .First(member => member.FullNameContains("PropertyAccessingStaticField")); + var propertyTypeDependencies = property.GetTypeDependencies().ToList(); + var propertyFieldDependencies = property.GetAccessedFieldMembers().ToList(); + + Assert.Contains(_classWithStaticFields, propertyTypeDependencies); + Assert.Contains(_staticFieldMember, propertyFieldDependencies); + } + + [Fact] + public void PropertyAccessToNonStaticFieldFound() + { + var property = _accessingClass.GetPropertyMembers() + .First(member => member.FullNameContains("PropertyAccessingNonStaticField")); + var propertyTypeDependencies = property.GetTypeDependencies().ToList(); + var propertyFieldDependencies = property.GetAccessedFieldMembers().ToList(); + + Assert.Contains(_classWithNonStaticFields, propertyTypeDependencies); + Assert.Contains(_nonStaticFieldMember, propertyFieldDependencies); + } + + [Fact(Skip = + "Can't find dependency, because only OpCode Ldstr is used and no Reference to the type which declares the const field can be found")] + public void MethodAccessToConstFieldFound() + { + var method = _accessingClass.GetMethodMembers() + .First(member => member.FullNameContains("MethodAccessingConstField")); + var methodTypeDependencies = method.GetTypeDependencies().ToList(); + var methodFieldDependencies = method.GetAccessedFieldMembers().ToList(); + + Assert.Contains(_classWithStaticFields, methodTypeDependencies); + Assert.Contains(_constFieldMember, methodFieldDependencies); + } + + [Fact] + public void SettingStaticFieldDependencyFound() + { + var method = _accessingClass.GetMethodMembers() + .First(member => member.FullNameContains("MethodSettingStaticField")); + var methodTypeDependencies = method.GetTypeDependencies().ToList(); + var methodFieldDependencies = method.GetAccessedFieldMembers().ToList(); + + Assert.Contains(_classWithStaticFields, methodTypeDependencies); + Assert.Contains(_staticFieldMember, methodFieldDependencies); + } + + [Fact] + public void GettingStaticFieldDependencyFound() + { + var method = _accessingClass.GetMethodMembers() + .First(member => member.FullNameContains("MethodGettingStaticField")); + var methodTypeDependencies = method.GetTypeDependencies().ToList(); + var methodFieldDependencies = method.GetAccessedFieldMembers().ToList(); + + Assert.Contains(_classWithStaticFields, methodTypeDependencies); + Assert.Contains(_staticFieldMember, methodFieldDependencies); + } + + [Fact] + public void SettingNonStaticFieldDependencyFound() + { + var method = _accessingClass.GetMethodMembers() + .First(member => member.FullNameContains("MethodSettingNonStaticField")); + var methodTypeDependencies = method.GetTypeDependencies().ToList(); + var methodFieldDependencies = method.GetAccessedFieldMembers().ToList(); + + Assert.Contains(_classWithNonStaticFields, methodTypeDependencies); + Assert.Contains(_nonStaticFieldMember, methodFieldDependencies); + } + + [Fact] + public void GettingNonStaticFieldDependencyFound() + { + var method = _accessingClass.GetMethodMembers() + .First(member => member.FullNameContains("MethodGettingNonStaticField")); + var methodTypeDependencies = method.GetTypeDependencies().ToList(); + var methodFieldDependencies = method.GetAccessedFieldMembers().ToList(); + + Assert.Contains(_classWithNonStaticFields, methodTypeDependencies); + Assert.Contains(_nonStaticFieldMember, methodFieldDependencies); + } + } + + internal class ClassWithNonStaticFields + { + public ClassAccessingFields NonStaticField; + } + + internal static class ClassWithStaticFields + { + public const string ConstField = "ConstField"; + public static ClassAccessingFields StaticField; + } + + internal class ClassAccessingFields + { + public string PropertyAccessingConstField => ClassWithStaticFields.ConstField; + public ClassAccessingFields PropertyAccessingStaticField => ClassWithStaticFields.StaticField; + public ClassAccessingFields PropertyAccessingNonStaticField => new ClassWithNonStaticFields().NonStaticField; + + public void MethodAccessingConstField() + { + var a = ClassWithStaticFields.ConstField; + } + + public void MethodGettingStaticField() + { + var b = ClassWithStaticFields.StaticField; + } + + public void MethodSettingStaticField() + { + ClassWithStaticFields.StaticField = new ClassAccessingFields(); + } + + public void MethodGettingNonStaticField() + { + var cls = new ClassWithNonStaticFields(); + var c = cls.NonStaticField; + } + + public void MethodSettingNonStaticField() + { + var cls = new ClassWithNonStaticFields {NonStaticField = new ClassAccessingFields()}; + } + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Dependencies/ArrayTests.cs b/ArchUnitNETTests/Dependencies/ArrayTests.cs new file mode 100644 index 000000000..a7de60ea3 --- /dev/null +++ b/ArchUnitNETTests/Dependencies/ArrayTests.cs @@ -0,0 +1,102 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Dependencies; +using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; +using Xunit; + +namespace ArchUnitNETTests.Dependencies +{ + public class ArrayTests + { + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(ArrayTests).Assembly).Build(); + + private readonly Class _bool; + + private readonly Class _classWithArrayMethod; + private readonly Class _classWithBoolArrayFields; + + private readonly Class _int; + + + public ArrayTests() + { + _bool = Architecture.GetClassOfType(typeof(bool)); + _int = Architecture.GetClassOfType(typeof(int)); + _classWithBoolArrayFields = Architecture.GetClassOfType(typeof(ClassWithBoolArrayFields)); + _classWithArrayMethod = Architecture.GetClassOfType(typeof(ClassWithArrayMethod)); + } + + [Fact] + public void FindDependenciesInArrayFields() + { + var fieldTypeDependencies = _classWithBoolArrayFields.Dependencies.OfType(); + var fieldMembers = _classWithBoolArrayFields.GetFieldMembers(); + + Assert.DoesNotContain(fieldMembers, member => !Equals(member.Type, _bool)); + Assert.DoesNotContain(fieldTypeDependencies, dependency => !dependency.Target.Equals(_bool)); + } + + [Fact] + public void FindDependenciesInArrayMethods() + { + var typeDependencies = _classWithArrayMethod.GetTypeDependencies().ToList(); + var method = _classWithArrayMethod.GetMethodMembers().First(member => member.NameContains("ArrayMethod")); + + Assert.Contains(_bool, typeDependencies); + Assert.Contains(_int, typeDependencies); + + Assert.Equal(_bool, method.ReturnType); + Assert.Equal(_int, method.Parameters.First()); + + Assert.DoesNotContain(method.ParameterInstances, parameter => !parameter.IsArray); + } + + [Fact] + public void FindArrayDimensions() + { + var bool1Array = _classWithBoolArrayFields.GetFieldMembersWithName("_bool1Array").First(); + var bool11Array = _classWithBoolArrayFields.GetFieldMembersWithName("_bool11Array").First(); + var bool2Array = _classWithBoolArrayFields.GetFieldMembersWithName("_bool2Array").First(); + var bool21Array = _classWithBoolArrayFields.GetFieldMembersWithName("_bool21Array").First(); + var bool412Array = _classWithBoolArrayFields.GetFieldMembersWithName("_bool412Array").First(); + + Assert.True(bool1Array.IsArray); + Assert.True(bool11Array.IsArray); + Assert.True(bool2Array.IsArray); + Assert.True(bool21Array.IsArray); + Assert.True(bool412Array.IsArray); + + Assert.Equal(new[] {1}, bool1Array.ArrayDimensions); + Assert.Equal(new[] {1, 1}, bool11Array.ArrayDimensions); + Assert.Equal(new[] {2}, bool2Array.ArrayDimensions); + Assert.Equal(new[] {2, 1}, bool21Array.ArrayDimensions); + Assert.Equal(new[] {4, 1, 2}, bool412Array.ArrayDimensions); + } + } + + internal class ClassWithBoolArrayFields + { + private bool[][] _bool11Array; + private bool[] _bool1Array; + private bool[,][] _bool21Array; + private bool[,] _bool2Array; + private bool[,,,][][,] _bool412Array; + } + + internal class ClassWithArrayMethod + { + public bool[] ArrayMethod(int[,] i) + { + return new bool[i[1, 0]]; + } + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Dependencies/CastDependenciesTest.cs b/ArchUnitNETTests/Dependencies/CastDependenciesTest.cs new file mode 100644 index 000000000..9c7bc347c --- /dev/null +++ b/ArchUnitNETTests/Dependencies/CastDependenciesTest.cs @@ -0,0 +1,82 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; +using Xunit; + +namespace ArchUnitNETTests.Dependencies +{ + public class CastDependenciesTest + { + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(ClassWithCastDependency).Assembly).Build(); + + private readonly Class _castClassA; + private readonly Class _castClassB; + private readonly Interface _castInterfaceA; + private readonly Class _classWithCastDependency; + private readonly MethodMember _methodWithCastDependency; + + public CastDependenciesTest() + { + _castClassA = Architecture.GetClassOfType(typeof(CastClassA)); + _castClassB = Architecture.GetClassOfType(typeof(CastClassB)); + _castInterfaceA = Architecture.GetInterfaceOfType(typeof(ICastInterfaceA)); + _classWithCastDependency = Architecture.GetClassOfType(typeof(ClassWithCastDependency)); + _methodWithCastDependency = (MethodMember) _classWithCastDependency.Members + .WhereNameIs("MethodWithCastDependencies(ArchUnitNETTests.Dependencies.CastClassA)").ToList().First(); + } + + [SkipInReleaseBuild] + public void CastTest() + { + var typeDependencies = _classWithCastDependency.GetTypeDependencies(Architecture).ToList(); + + Assert.Contains(_castClassA, typeDependencies); + Assert.Contains(_castClassB, typeDependencies); + Assert.Contains(_castInterfaceA, typeDependencies); + } + + [Fact] + public void MethodCastTest() + { + var typeDependencies = _methodWithCastDependency.GetTypeDependencies(Architecture).ToList(); + Assert.Contains(_castClassB, typeDependencies); + } + } + + internal class ClassWithCastDependency + { + private CastClassB target; + + public ClassWithCastDependency() + { + var type = (CastClassA) new CastClassB(); + var type2 = (ICastInterfaceA) new CastClassB(); + } + + public void MethodWithCastDependencies(CastClassA value) + { + target = (CastClassB) value; + } + } + + internal class CastClassA + { + } + + internal interface ICastInterfaceA + { + } + + internal class CastClassB : CastClassA, ICastInterfaceA + { + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Dependencies/ExceptionDependenciesTests.cs b/ArchUnitNETTests/Dependencies/ExceptionDependenciesTests.cs new file mode 100644 index 000000000..221d47a3b --- /dev/null +++ b/ArchUnitNETTests/Dependencies/ExceptionDependenciesTests.cs @@ -0,0 +1,55 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System; +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; +using Xunit; + +namespace ArchUnitNETTests.Dependencies +{ + public class ExceptionDependenciesTests + { + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(ExceptionDependenciesTests).Assembly).Build(); + + private readonly Class _classWithException; + private readonly Class _throwingClass; + + public ExceptionDependenciesTests() + { + _classWithException = Architecture.GetClassOfType(typeof(ClassWithException)); + _throwingClass = Architecture.GetClassOfType(typeof(ThrowingClass)); + } + + [Fact] + public void ThrowDependencyFound() + { + var typeDependencies = _throwingClass.GetTypeDependencies().ToList(); + var method = _throwingClass.GetMethodMembers().First(member => member.FullNameContains("ThrowingMethod")); + var methodTypeDependencies = method.GetTypeDependencies().ToList(); + + Assert.Contains(_classWithException, typeDependencies); + Assert.Contains(_classWithException, methodTypeDependencies); + } + } + + internal static class ClassWithException + { + public static readonly Exception Exception = new Exception(); + } + + internal class ThrowingClass + { + public void ThrowingMethod() + { + throw ClassWithException.Exception; + } + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Dependencies/ExternalDependenciesTests.cs b/ArchUnitNETTests/Dependencies/ExternalDependenciesTests.cs new file mode 100644 index 000000000..5bbbd1449 --- /dev/null +++ b/ArchUnitNETTests/Dependencies/ExternalDependenciesTests.cs @@ -0,0 +1,116 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using ArchUnitNET.Domain; +using ArchUnitNET.Loader; +using TestAssembly; +using Xunit; +using static ArchUnitNET.Fluent.ArchRuleDefinition; + +namespace ArchUnitNETTests.Dependencies +{ + public class ExternalDependenciesTests + { + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(ExternalDependenciesTests).Assembly).Build(); + + private static readonly Architecture ArchitectureBothAssemblies = + new ArchLoader().LoadAssemblies(typeof(ExternalDependenciesTests).Assembly, typeof(Class2).Assembly) + .Build(); + + private static readonly Architecture ArchitectureExternalDependency = + new ArchLoader().LoadAssemblyIncludingDependencies(typeof(ExternalDependenciesTests).Assembly).Build(); + + + [Fact] + public void PropertyDependencyTest() + { + var notDependOnAnyRuleClass = Classes().That() + .HaveFullNameMatching(typeof(PropertyDependency).FullName).Should() + .NotDependOnAny(typeof(Class2)); + var notDependOnAnyRuleString = Classes().That() + .HaveFullNameMatching(typeof(PropertyDependency).FullName).Should() + .NotDependOnAny(typeof(Class2).FullName); + Assert.False(notDependOnAnyRuleClass.HasNoViolations(Architecture)); //Class2 does not exist in Architecture + Assert.False(notDependOnAnyRuleString.HasNoViolations(Architecture)); + Assert.False(notDependOnAnyRuleClass.HasNoViolations(ArchitectureExternalDependency)); + } + + [Fact] + public void MethodBodyDependencyTest() + { + var notDependOnAnyRuleClass = Classes().That() + .HaveFullNameMatching(typeof(MethodBodyDependency).FullName).Should() + .NotDependOnAny(typeof(Class3)); + var notDependOnAnyRuleString = Classes().That() + .HaveFullNameMatching(typeof(MethodBodyDependency).FullName).Should() + .NotDependOnAny(typeof(Class3).FullName); + Assert.False(notDependOnAnyRuleClass.HasNoViolations(Architecture)); //Class3 does not exist in Architecture + Assert.False(notDependOnAnyRuleString.HasNoViolations(Architecture)); + Assert.False(notDependOnAnyRuleClass.HasNoViolations(ArchitectureBothAssemblies)); + } + + [Fact] + public void MethodArgumentDependencyTest() + { + var notDependOnAnyRuleClass = Classes().That() + .HaveFullNameMatching(typeof(MethodArgumentDependency).FullName).Should() + .NotDependOnAny(typeof(Class2)); + var notDependOnAnyRuleString = Classes().That() + .HaveFullNameMatching(typeof(MethodArgumentDependency).FullName).Should() + .NotDependOnAny(typeof(Class2).FullName); + Assert.False(notDependOnAnyRuleClass.HasNoViolations(Architecture)); //Class3 does not exist in Architecture + Assert.False(notDependOnAnyRuleString.HasNoViolations(Architecture)); + } + + [Fact] + public void FieldDependencyTest() + { + var notDependOnAnyRuleClass = Classes().That() + .HaveFullNameMatching(typeof(FieldDependency).FullName).Should() + .NotDependOnAny(typeof(Class2)); + var notDependOnAnyRuleString = Classes().That() + .HaveFullNameMatching(typeof(FieldDependency).FullName).Should() + .NotDependOnAny(typeof(Class2).FullName); + Assert.False(notDependOnAnyRuleClass.HasNoViolations(Architecture)); //Class3 does not exist in Architecture + Assert.False(notDependOnAnyRuleString.HasNoViolations(Architecture)); + } + } + + public class PropertyDependency + { + public string Prop + { + get + { + var b = true; + var a = new Class2(); + return ""; + } + } + } + + public class MethodBodyDependency + { + public void Method() + { + Class3.Class3StaticMethod(); + } + } + + public class MethodArgumentDependency + { + public void Method(Class2 class2) + { + } + } + + public class FieldDependency + { + private Class2 _class2 = new Class2(); + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Dependencies/GenericMemberDependenciesTests.cs b/ArchUnitNETTests/Dependencies/GenericMemberDependenciesTests.cs new file mode 100644 index 000000000..ead8942e0 --- /dev/null +++ b/ArchUnitNETTests/Dependencies/GenericMemberDependenciesTests.cs @@ -0,0 +1,21 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +namespace ArchUnitNETTests.Dependencies +{ + public class GenericMemberDependenciesTests + { + } + + internal class GenericClass + { + public M GenericMethod(T t) where M : new() + { + return new M(); + } + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Dependencies/KeepDependenciesInCompilerGeneratedTypesTests.cs b/ArchUnitNETTests/Dependencies/KeepDependenciesInCompilerGeneratedTypesTests.cs new file mode 100644 index 000000000..48e864b83 --- /dev/null +++ b/ArchUnitNETTests/Dependencies/KeepDependenciesInCompilerGeneratedTypesTests.cs @@ -0,0 +1,171 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System; +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; +using Xunit; + +namespace ArchUnitNETTests.Dependencies +{ + public class KeepDependenciesInCompilerGeneratedTypesTests + { + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(KeepDependenciesInCompilerGeneratedTypesTests).Assembly).Build(); + + private readonly Class _argumentClass; + private readonly Class _classWithIndexing; + private readonly Class _classWithLambda; + private readonly Class _classWithProperty; + private readonly Class _genericArgumentClass; + private readonly Class _returnedClass; + + public KeepDependenciesInCompilerGeneratedTypesTests() + { + _classWithProperty = Architecture.GetClassOfType(typeof(ClassWithPropertyDependency)); + _classWithLambda = Architecture.GetClassOfType(typeof(ClassWithLambdaDependency)); + _classWithIndexing = Architecture.GetClassOfType(typeof(ClassWithIndexingDependency)); + _returnedClass = Architecture.GetClassOfType(typeof(ReturnedClass)); + _argumentClass = Architecture.GetClassOfType(typeof(ArgumentClass)); + _genericArgumentClass = Architecture.GetClassOfType(typeof(GenericArgumentClass)); + } + + [Fact] + public void PropertyDependenciesNotLost() + { + var typeDependencies = _classWithProperty.GetTypeDependencies().ToList(); + var property = _classWithProperty.GetPropertyMembers().First(); + var propertyTypeDependencies = property.GetTypeDependencies().ToList(); + + + Assert.Contains(_returnedClass, typeDependencies); + Assert.Contains(_argumentClass, typeDependencies); + Assert.Single(_classWithProperty.GetPropertyMembers()); + Assert.Contains(_returnedClass, propertyTypeDependencies); + Assert.Contains(_argumentClass, propertyTypeDependencies); + } + + [Fact] + public void LambdaMethodCallDependenciesNotLost() + { + var typeDependencies = _classWithLambda.GetTypeDependencies().ToList(); + var method = _classWithLambda.GetMethodMembers().First(); + var methodTypeDependencies = method.GetTypeDependencies().ToList(); + + Assert.Contains(_returnedClass, typeDependencies); + Assert.Contains(_argumentClass, typeDependencies); + Assert.Contains(_classWithIndexing, typeDependencies); + Assert.Single(_classWithLambda.GetMethodMembers()); + Assert.Contains(_returnedClass, methodTypeDependencies); + Assert.Contains(_argumentClass, methodTypeDependencies); + Assert.Contains(_classWithIndexing, methodTypeDependencies); + } + + [SkipInReleaseBuild] + public void LambdaTypeDependenciesNotLost() + { + var typeDependencies = _classWithLambda.GetTypeDependencies().ToList(); + var method = _classWithLambda.GetMethodMembers().First(); + var methodTypeDependencies = method.GetTypeDependencies().ToList(); + + Assert.Contains(_classWithProperty, typeDependencies); + Assert.Contains(_classWithProperty, methodTypeDependencies); + } + + [Fact] + public void LambdaGenericArgumentDependenciesNotLost() + { + var typeDependencies = _classWithLambda.GetTypeDependencies().ToList(); + var method = _classWithLambda.GetMethodMembers().First(); + var methodTypeDependencies = method.GetTypeDependencies().ToList(); + + Assert.Contains(_genericArgumentClass, typeDependencies); + Assert.Contains(_genericArgumentClass, methodTypeDependencies); + } + + [Fact(Skip = "Fails because the string is created with OpCode Ldstr which has no TypeReference as Operand")] + public void LambdaPrimitiveDependenciesNotLost() + { + var typeDependencies = _classWithLambda.GetTypeDependencies().ToList(); + var method = _classWithLambda.GetMethodMembers().First(); + var methodTypeDependencies = method.GetTypeDependencies().ToList(); + + Assert.Contains(typeDependencies, dep => dep.FullNameContains("System.String")); + Assert.Contains(methodTypeDependencies, dep => dep.FullNameContains("System.String")); + } + + [Fact] + public void IndexingDependenciesNotLost() + { + var typeDependencies = _classWithIndexing.GetTypeDependencies().ToList(); + + Assert.Contains(_returnedClass, typeDependencies); + Assert.Contains(_argumentClass, typeDependencies); + } + + [Fact] + public void BackwardDependenciesAssignedCorrectly() + { + var argumentTypeBackwardDependencies = + _argumentClass.BackwardsDependencies.Select(dep => dep.Origin).ToList(); + var returnedTypeBackwardDependencies = + _returnedClass.BackwardsDependencies.Select(dep => dep.Origin).ToList(); + + Assert.Contains(_classWithProperty, argumentTypeBackwardDependencies); + Assert.Contains(_classWithLambda, argumentTypeBackwardDependencies); + Assert.Contains(_classWithIndexing, argumentTypeBackwardDependencies); + + Assert.Contains(_classWithProperty, returnedTypeBackwardDependencies); + Assert.Contains(_classWithLambda, returnedTypeBackwardDependencies); + Assert.Contains(_classWithIndexing, returnedTypeBackwardDependencies); + } + } + + internal class ClassWithPropertyDependency + { + public object Property => new ReturnedClass(new ArgumentClass()); + } + + internal class ClassWithLambdaDependency + { + private Func _lambda; + + public ClassWithLambdaDependency() + { + _lambda = argumentClass => + { + Func secondLambda = obj => new ClassWithIndexingDependency(); + Func thirdLambda = obj => "testString"; + Func fourthLambda = genericArgumentClass => true; + ClassWithPropertyDependency var = null; + return new ReturnedClass(new ArgumentClass()); + }; + } + } + + internal class GenericArgumentClass + { + } + + internal class ClassWithIndexingDependency + { + public object this[int index] => new ReturnedClass(new ArgumentClass()); + } + + internal class ReturnedClass + { + public ReturnedClass(object argument) + { + } + } + + internal class ArgumentClass + { + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Dependencies/PropertyDependencyTests.cs b/ArchUnitNETTests/Dependencies/PropertyDependencyTests.cs new file mode 100644 index 000000000..0f5fd0559 --- /dev/null +++ b/ArchUnitNETTests/Dependencies/PropertyDependencyTests.cs @@ -0,0 +1,143 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Dependencies; +using ArchUnitNET.Domain.Extensions; +using Xunit; + +namespace ArchUnitNETTests.Dependencies +{ + public class PropertyDependencyTests + { + private static readonly Architecture Architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; + + private readonly Class _dependOnClass; + private readonly Class _propertyTestDataClass; + private readonly PropertyMember _testStringProperty; + private readonly MethodMember _testStringPropertyGetter; + + public PropertyDependencyTests() + { + _dependOnClass = Architecture.GetClassOfType(typeof(PropertyDependOnClass)); + _propertyTestDataClass = Architecture.GetClassOfType(typeof(PropertyTestDataClass)); + _testStringProperty = _propertyTestDataClass.GetPropertyMembersWithName("TestStringProperty").ToList().First(); + _testStringPropertyGetter = + _propertyTestDataClass.GetMethodMembersWithName("get_TestStringProperty()").First(); + } + + [Fact] + public void PropertyGetterClassGetterIdentical() + { + if (_testStringProperty.Getter != null) + { + Assert.Equal(_testStringPropertyGetter, _testStringProperty.Getter); + } + else + { + Assert.True(false, "Property must have a getter"); + } + } + + [Fact] + public void PropertyGetterClassGetterSameDependencies() + { + if (_testStringProperty.Getter != null) + { + Assert.Equal(_testStringPropertyGetter.MemberDependencies, + _testStringProperty.Getter.MemberDependencies); + } + else + { + Assert.True(false, "Property must have a getter"); + } + } + + [Fact] + public void ClassHasDependencyFromProperty() + { + Assert.Contains(_dependOnClass, _propertyTestDataClass.Dependencies.Select(d => d.Target)); + } + + [Fact] + public void PropertyHasDependencyFromProperty() + { + Assert.Contains(_dependOnClass, _testStringProperty.MemberDependencies.Select(d => d.Target)); + } + + [Fact] + public void GetterHasDependencyFromProperty() + { + Assert.Contains(_dependOnClass, _testStringPropertyGetter.MemberDependencies.Select(d => d.Target)); + } + + [Fact] + public void ClassHasMethodCallDependencyFromProperty() + { + var methodCalls = _propertyTestDataClass.Dependencies.Where(d => d is MethodCallDependency).ToList(); + if (methodCalls.IsNullOrEmpty()) + { + Assert.True(false, "Class must have Method Call Dependency"); + } + Assert.Contains(_dependOnClass, methodCalls.Select(d => d.Target)); + } + + [Fact] + public void PropertyHasMethodCallDependencyFromProperty() + { + var methodCalls = _testStringProperty.MemberDependencies.Where(d => d is MethodCallDependency).ToList(); + if (methodCalls.IsNullOrEmpty()) + { + Assert.True(false, "Property must have Method Call Dependency"); + } + Assert.Contains(_dependOnClass, methodCalls.Select(d => d.Target)); + } + + [Fact] + public void GetterHasMethodCallDependencyFromProperty() + { + var methodCalls = _testStringPropertyGetter.MemberDependencies.Where(d => d is MethodCallDependency).ToList(); + if (methodCalls.IsNullOrEmpty()) + { + Assert.True(false, "Getter must have Method Call Dependency"); + } + Assert.Contains(_dependOnClass, methodCalls.Select(d => d.Target)); + } + + [Fact] + public void PropertyDependencyPassedOn() + { + Assert.Equal(_testStringPropertyGetter.MemberDependencies, + _testStringPropertyGetter.MemberDependencies.Intersect(_testStringProperty.MemberDependencies)); + Assert.Equal(_testStringProperty.MemberDependencies, + _testStringProperty.MemberDependencies.Intersect(_propertyTestDataClass.Dependencies)); + } + } + + public class PropertyTestDataClass + { + public string TestStringProperty + { + get + { + var b = new PropertyDependOnClass(); + b.PropertyDependOnClassMethod(); + return ""; + } + } + } + + public class PropertyDependOnClass + { + public void PropertyDependOnClassMethod() + { + + } + + } +} diff --git a/ArchUnitNETTests/Dependencies/StringDependenciesTests.cs b/ArchUnitNETTests/Dependencies/StringDependenciesTests.cs new file mode 100644 index 000000000..bf0b4180f --- /dev/null +++ b/ArchUnitNETTests/Dependencies/StringDependenciesTests.cs @@ -0,0 +1,80 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; +using Xunit; + +namespace ArchUnitNETTests.Dependencies +{ + public class StringDependenciesTests + { + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(StringDependenciesTests).Assembly).Build(); + + private readonly Class _classWithLocalString; + private readonly Class _classWithPropertyString; + + private readonly Class _classWithStringField; + + public StringDependenciesTests() + { + _classWithStringField = Architecture.GetClassOfType(typeof(ClassWithStringField)); + _classWithLocalString = Architecture.GetClassOfType(typeof(ClassWithLocalString)); + _classWithPropertyString = Architecture.GetClassOfType(typeof(ClassWithPropertyString)); + } + + [Fact(Skip = "Fails because the string is created with OpCode Ldstr which has no TypeReference as Operand")] + public void StringFieldDependencyFound() + { + var typeDependencies = _classWithStringField.GetTypeDependencies().ToList(); + Assert.Contains(typeof(string).FullName, typeDependencies.Select(dep => dep.FullName)); + } + + [Fact(Skip = "Fails because the string is created with OpCode Ldstr which has no TypeReference as Operand")] + public void LocalStringDependencyFound() + { + var typeDependencies = _classWithLocalString.GetTypeDependencies().ToList(); + var method = _classWithLocalString.GetMethodMembers().First(); + var methodTypeDependencies = method.GetTypeDependencies().ToList(); + + Assert.Contains(typeof(string).FullName, typeDependencies.Select(dep => dep.FullName)); + Assert.Contains(typeof(string).FullName, methodTypeDependencies.Select(dep => dep.FullName)); + } + + [Fact(Skip = "Fails because the string is created with OpCode Ldstr which has no TypeReference as Operand")] + public void PropertyStringDependencyFound() + { + var typeDependencies = _classWithPropertyString.GetTypeDependencies().ToList(); + var property = _classWithPropertyString.GetMethodMembers().First(); + var propertyTypeDependencies = property.GetTypeDependencies().ToList(); + + Assert.Contains(typeof(string).FullName, typeDependencies.Select(dep => dep.FullName)); + Assert.Contains(typeof(string).FullName, propertyTypeDependencies.Select(dep => dep.FullName)); + } + } + + internal class ClassWithStringField + { + private object Str = "FieldString"; + } + + internal class ClassWithLocalString + { + public ClassWithLocalString() + { + object str = "LocalString"; + } + } + + internal class ClassWithPropertyString + { + public object Prop => "PropertyString"; + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Dependencies/TypeCheckDependenciesTests.cs b/ArchUnitNETTests/Dependencies/TypeCheckDependenciesTests.cs new file mode 100644 index 000000000..0485e5f7e --- /dev/null +++ b/ArchUnitNETTests/Dependencies/TypeCheckDependenciesTests.cs @@ -0,0 +1,56 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; +using Xunit; + +namespace ArchUnitNETTests.Dependencies +{ + public class TypeCheckDependenciesTests + { + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(TypeCheckDependenciesTests).Assembly).Build(); + + private readonly Class _classWithTypeDepencency; + private readonly Class _dependingClass; + + public TypeCheckDependenciesTests() + { + _classWithTypeDepencency = Architecture.GetClassOfType(typeof(ClassWithTypeDependency)); + _dependingClass = Architecture.GetClassOfType(typeof(DependingClass)); + } + + [Fact] + public void TypeCheckDependencyTest() + { + var methodMember = + _classWithTypeDepencency.Members.First(member => member.NameContains("MethodWithTypeDependency")); + var typeDependencies = _classWithTypeDepencency.GetTypeDependencies().ToList(); + var methodTypeDependencies = methodMember.GetTypeDependencies().ToList(); + + Assert.Contains(_dependingClass, typeDependencies); + Assert.Contains(_dependingClass, methodTypeDependencies); + } + } + + internal class ClassWithTypeDependency + { + public void MethodWithTypeDependency(object obj) + { + if (obj is DependingClass) + { + } + } + } + + internal class DependingClass + { + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Dependencies/TypeOfDependenciesTests.cs b/ArchUnitNETTests/Dependencies/TypeOfDependenciesTests.cs new file mode 100644 index 000000000..9966623b7 --- /dev/null +++ b/ArchUnitNETTests/Dependencies/TypeOfDependenciesTests.cs @@ -0,0 +1,54 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; +using Xunit; + +namespace ArchUnitNETTests.Dependencies +{ + public class TypeOfDependenciesTests + { + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(TypeOfDependenciesTests).Assembly).Build(); + + private readonly Class _classWithTypeOfDependency; + private readonly Class _dependingTypeOfClass; + + public TypeOfDependenciesTests() + { + _classWithTypeOfDependency = Architecture.GetClassOfType(typeof(ClassWithTypeOfDependency)); + _dependingTypeOfClass = Architecture.GetClassOfType(typeof(DependingTypeOfClass)); + } + + [SkipInReleaseBuild] + public void TypeOfDependencyTest() + { + var methodMember = + _classWithTypeOfDependency.Members.First(member => member.NameContains("MethodWithTypeOfDependency")); + var typeDependencies = _classWithTypeOfDependency.GetTypeDependencies().ToList(); + var methodTypeDependencies = methodMember.GetTypeDependencies().ToList(); + + Assert.Contains(_dependingTypeOfClass, typeDependencies); + Assert.Contains(_dependingTypeOfClass, methodTypeDependencies); + } + } + + internal class ClassWithTypeOfDependency + { + public void MethodWithTypeOfDependency() + { + object a = typeof(DependingTypeOfClass); + } + } + + internal class DependingTypeOfClass + { + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Domain/ArchitectureCacheTests.cs b/ArchUnitNETTests/Domain/ArchitectureCacheTests.cs index 3221d8e4b..f9d318131 100644 --- a/ArchUnitNETTests/Domain/ArchitectureCacheTests.cs +++ b/ArchUnitNETTests/Domain/ArchitectureCacheTests.cs @@ -12,6 +12,10 @@ namespace ArchUnitNETTests.Domain { public class ArchitectureCacheTests { + private readonly TestArchitectureCache _testArchitectureCache; + private readonly ArchitectureCacheKey _testArchitectureCacheKey; + private readonly Architecture _testEmptyArchitecture; + public ArchitectureCacheTests() { _testArchitectureCache = new TestArchitectureCache(); @@ -19,13 +23,10 @@ public ArchitectureCacheTests() _testArchitectureCacheKey = new ArchitectureCacheKey(); _testArchitectureCacheKey.Add(typeof(ArchitectureCacheTests).Assembly.FullName, null); - _testEmptyArchitecture = new Architecture(new List(), new List(), new List()); + _testEmptyArchitecture = new Architecture(new List(), new List(), new List(), + new List(), new List()); } - private readonly TestArchitectureCache _testArchitectureCache; - private readonly ArchitectureCacheKey _testArchitectureCacheKey; - private readonly Architecture _testEmptyArchitecture; - [Fact] public void DuplicateArchitectureDetected() { diff --git a/ArchUnitNETTests/Domain/AttributeTests.cs b/ArchUnitNETTests/Domain/AttributeTests.cs index 2d3cbe143..fa2b9013c 100644 --- a/ArchUnitNETTests/Domain/AttributeTests.cs +++ b/ArchUnitNETTests/Domain/AttributeTests.cs @@ -17,6 +17,26 @@ namespace ArchUnitNETTests.Domain { public class AttributeTests { + private static readonly Architecture Architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; + + private readonly AttributeOriginClassPair _abstractAttributePair; + private readonly AttributeEquivalencyTestData _attributeEquivalencyTestData; + + private readonly AttributeOriginClassPair _attributeWithAttributesPair; + + private readonly AttributeOriginClassPair _constructorAttributePair; + + private readonly AttributeOriginClassPair _developerAttributePair; + private readonly Interface _iAttribute; + + private readonly Attribute _implementsAbstractAttribute; + private readonly FieldMember _inheritedFieldMember; + + private readonly AttributeOriginClassPair _interfaceImplementingAttributePair; + private readonly PropertyMember _propertyMember; + + private readonly Type _unrelatedType; + public AttributeTests() { _developerAttributePair = new AttributeOriginClassPair(typeof(CountryAttributeWithParameters)); @@ -28,7 +48,9 @@ public AttributeTests() _unrelatedType = new Type(_abstractAttributePair.OriginClass.Name, _abstractAttributePair.OriginClass.FullName, _abstractAttributePair.OriginClass.Assembly, _abstractAttributePair.OriginClass.Namespace, _abstractAttributePair.OriginClass.Visibility, - _abstractAttributePair.OriginClass.IsNested); + _abstractAttributePair.OriginClass.IsNested, _abstractAttributePair.OriginClass.IsGeneric, + _abstractAttributePair.OriginClass.IsStub, false); + _unrelatedType.GenericParameters.AddRange(_abstractAttributePair.OriginClass.GenericParameters); _propertyMember = _implementsAbstractAttribute .GetPropertyMembersWithName(nameof(ChildOfAbstractAttribute.Property)).SingleOrDefault(); _inheritedFieldMember = _abstractAttributePair.Attribute @@ -37,65 +59,6 @@ public AttributeTests() _attributeWithAttributesPair = new AttributeOriginClassPair(typeof(AttributeWithAttributes)); } - private static readonly Architecture Architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; - - private readonly AttributeOriginClassPair _developerAttributePair; - private readonly AttributeEquivalencyTestData _attributeEquivalencyTestData; - - private readonly AttributeOriginClassPair _abstractAttributePair; - - private readonly Attribute _implementsAbstractAttribute; - private readonly Interface _iAttribute; - - private readonly AttributeOriginClassPair _interfaceImplementingAttributePair; - - private readonly Type _unrelatedType; - private readonly PropertyMember _propertyMember; - private readonly FieldMember _inheritedFieldMember; - - private readonly AttributeOriginClassPair _constructorAttributePair; - - private readonly AttributeOriginClassPair _attributeWithAttributesPair; - - private class AttributeOriginClassPair - { - public AttributeOriginClassPair(System.Type originType) - { - OriginClass = Architecture.GetClassOfType(originType); - Attribute = Architecture.GetAttributeOfType(originType); - } - - [NotNull] - public Class OriginClass { get; } - - [NotNull] - public Attribute Attribute { get; } - } - - private class AttributeEquivalencyTestData - { - public AttributeEquivalencyTestData([NotNull] System.Type originType) - { - OriginAttribute = new Attribute(Architecture.GetClassOfType(originType)); - DuplicateAttribute = - new Attribute(Architecture.GetClassOfType(originType)); - AttributeReferenceDuplicate = OriginAttribute; - ObjectReferenceDuplicate = OriginAttribute; - } - - [NotNull] - public Attribute OriginAttribute { get; } - - [NotNull] - public object DuplicateAttribute { get; } - - [NotNull] - public Attribute AttributeReferenceDuplicate { get; } - - [NotNull] - public object ObjectReferenceDuplicate { get; } - } - [Fact] public void AssemblyAsExpected() { @@ -270,5 +233,38 @@ public void RecognizedAsNotAbstract() { Assert.False(_attributeEquivalencyTestData.OriginAttribute.IsAbstract); } + + private class AttributeOriginClassPair + { + public AttributeOriginClassPair(System.Type originType) + { + OriginClass = Architecture.GetClassOfType(originType); + Attribute = Architecture.GetAttributeOfType(originType); + } + + [NotNull] public Class OriginClass { get; } + + [NotNull] public Attribute Attribute { get; } + } + + private class AttributeEquivalencyTestData + { + public AttributeEquivalencyTestData([NotNull] System.Type originType) + { + OriginAttribute = new Attribute(Architecture.GetClassOfType(originType)); + DuplicateAttribute = + new Attribute(Architecture.GetClassOfType(originType)); + AttributeReferenceDuplicate = OriginAttribute; + ObjectReferenceDuplicate = OriginAttribute; + } + + [NotNull] public Attribute OriginAttribute { get; } + + [NotNull] public object DuplicateAttribute { get; } + + [NotNull] public Attribute AttributeReferenceDuplicate { get; } + + [NotNull] public object ObjectReferenceDuplicate { get; } + } } } \ No newline at end of file diff --git a/ArchUnitNETTests/Domain/ClassTests.cs b/ArchUnitNETTests/Domain/ClassTests.cs index be5139d25..75899aa51 100644 --- a/ArchUnitNETTests/Domain/ClassTests.cs +++ b/ArchUnitNETTests/Domain/ClassTests.cs @@ -18,6 +18,18 @@ namespace ArchUnitNETTests.Domain { public class ClassTests { + private static readonly Architecture Architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; + private readonly Class _baseClass; + private readonly Interface _chainedInterface; + private readonly Class _childClass; + + private readonly ClassEquivalencyTestData _classEquivalencyTestData; + private readonly Class _duplicateChildClass; + private readonly Interface _implementedInterface; + + private readonly Class _implementsInterface; + private readonly Type _misMatchType; + public ClassTests() { _baseClass = Architecture.GetClassOfType(typeof(BaseClass)); @@ -26,7 +38,8 @@ public ClassTests() var backingType = Architecture.GetITypeOfType(typeof(PropertyType)); _misMatchType = new Type(backingType.FullName, backingType.Name, backingType.Assembly, backingType.Namespace, - backingType.Visibility, backingType.IsNested); + backingType.Visibility, backingType.IsNested, backingType.IsGeneric, backingType.IsStub, false); + _misMatchType.GenericParameters.AddRange(backingType.GenericParameters); _implementsInterface = Architecture.GetClassOfType(typeof(InheritingType)); _implementedInterface = Architecture.GetInterfaceOfType(typeof(IInheritingInterface)); @@ -35,41 +48,6 @@ public ClassTests() _classEquivalencyTestData = new ClassEquivalencyTestData(typeof(ClassWithConstructors)); } - private static readonly Architecture Architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; - private readonly Class _baseClass; - private readonly Class _childClass; - private readonly Class _duplicateChildClass; - private readonly Type _misMatchType; - - private readonly Class _implementsInterface; - private readonly Interface _implementedInterface; - private readonly Interface _chainedInterface; - - private readonly ClassEquivalencyTestData _classEquivalencyTestData; - - private class ClassEquivalencyTestData - { - public ClassEquivalencyTestData([NotNull] System.Type originType) - { - OriginClass = Architecture.GetClassOfType(originType).RequiredNotNull(); - DuplicateClass = Architecture.GetClassOfType(originType).RequiredNotNull(); - ClassReferenceDuplicate = OriginClass; - ObjectReferenceDuplicate = OriginClass; - } - - [NotNull] - public Class OriginClass { get; } - - [NotNull] - public object DuplicateClass { get; } - - [NotNull] - public Class ClassReferenceDuplicate { get; } - - [NotNull] - public object ObjectReferenceDuplicate { get; } - } - [Fact] public void AssignableToDirectlyImplementedInterfaces() { @@ -200,5 +178,24 @@ public void ParentDependenciesAreInherited() Assert.Contains(parentDependency, _childClass.DependenciesIncludingInherited); }); } + + private class ClassEquivalencyTestData + { + public ClassEquivalencyTestData([NotNull] System.Type originType) + { + OriginClass = Architecture.GetClassOfType(originType).RequiredNotNull(); + DuplicateClass = Architecture.GetClassOfType(originType).RequiredNotNull(); + ClassReferenceDuplicate = OriginClass; + ObjectReferenceDuplicate = OriginClass; + } + + [NotNull] public Class OriginClass { get; } + + [NotNull] public object DuplicateClass { get; } + + [NotNull] public Class ClassReferenceDuplicate { get; } + + [NotNull] public object ObjectReferenceDuplicate { get; } + } } } \ No newline at end of file diff --git a/ArchUnitNETTests/Domain/CompilerGeneratedTypesTests.cs b/ArchUnitNETTests/Domain/CompilerGeneratedTypesTests.cs new file mode 100644 index 000000000..f8d2736bb --- /dev/null +++ b/ArchUnitNETTests/Domain/CompilerGeneratedTypesTests.cs @@ -0,0 +1,69 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Collections.Generic; +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Dependencies; +using Xunit; + +namespace ArchUnitNETTests.Domain +{ + public class CompilerGeneratedTypesTests + { + private static readonly Architecture Architecture = + StaticTestArchitectures.FullArchUnitNETArchitectureWithDependencies; + + private readonly IEnumerable _dependencies; + + public CompilerGeneratedTypesTests() + { + _dependencies = Architecture.Types.SelectMany(type => type.Dependencies); + } + + [Fact] + public void RecognizeCompilerGeneratedTypes() + { + Assert.DoesNotContain(Architecture.Types, + type => !type.IsCompilerGenerated && (type.Name.StartsWith("<") || type.Name.StartsWith("!"))); + } + + [Fact] + public void RecognizeCompilerGeneratedMethods() + { + Assert.DoesNotContain(Architecture.MethodMembers, + method => !method.IsCompilerGenerated && (method.Name.StartsWith("<") || method.Name.StartsWith("!"))); + } + + [Fact] + public void NoCompilerGeneratedTypeInArchitecture() + { + Assert.DoesNotContain(Architecture.Types, type => type.IsCompilerGenerated); + Assert.DoesNotContain(Architecture.ReferencedTypes, type => type.IsCompilerGenerated); + Assert.DoesNotContain(Architecture.GenericParameters, type => type.IsCompilerGenerated); + } + + [Fact] + public void NoCompilerGeneratedTypesAsDependencyTarget() + { + var dependencyTargets = _dependencies.Select(dep => dep.Target); + var compilerGeneratedDependencyTargets = dependencyTargets.Where(type => type.IsCompilerGenerated); + + Assert.Empty(compilerGeneratedDependencyTargets); + } + + [Fact] + public void NoCompilerGeneratedTypesAsGenericArguments() + { + var genericArgumentTypes = + _dependencies.SelectMany(dep => dep.TargetGenericArguments.Select(argument => argument.Type)); + var compilerGeneratedGenericArgumentTypes = genericArgumentTypes.Where(type => type.IsCompilerGenerated); + + Assert.Empty(compilerGeneratedGenericArgumentTypes); + } + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Domain/Dependencies/Attributes/AttributeAssertionRepository.cs b/ArchUnitNETTests/Domain/Dependencies/Attributes/AttributeAssertionRepository.cs index dd87182d2..bc82279fd 100644 --- a/ArchUnitNETTests/Domain/Dependencies/Attributes/AttributeAssertionRepository.cs +++ b/ArchUnitNETTests/Domain/Dependencies/Attributes/AttributeAssertionRepository.cs @@ -5,10 +5,10 @@ // SPDX-License-Identifier: Apache-2.0 using System; -using System.Linq; using ArchUnitNET.Domain; using ArchUnitNET.Domain.Dependencies; using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; using Xunit; using Attribute = ArchUnitNET.Domain.Attribute; @@ -83,13 +83,10 @@ public static void AttributeDependencyAsExpected(IMember targetMember, Class exp var expectedAttribute = new Attribute(expectedAttributeClass); var expectedAttributeDependency = - new AttributeMemberDependency(targetMember, expectedAttribute); + new AttributeMemberDependency(targetMember, new TypeInstance(expectedAttribute)); //Assert - Assert.Equal(expectedAttributeDependency, - targetMember.Dependencies.First()); - - Assert.True(targetMember.Dependencies.Contains(expectedAttributeDependency)); + Assert.Contains(expectedAttributeDependency, targetMember.Dependencies); } public static void AttributeDependencyAsExpected(IType targetType, Class expectedAttributeClass) @@ -109,7 +106,7 @@ public static void AttributeDependencyAsExpected(IType targetType, Class expecte var expectedAttribute = new Attribute(expectedAttributeClass); var expectedAttributeDependency = - new AttributeTypeDependency(targetType, expectedAttribute); + new AttributeTypeDependency(targetType, new TypeInstance(expectedAttribute)); //Assert Assert.True(targetType.HasDependency(expectedAttributeDependency)); diff --git a/ArchUnitNETTests/Domain/Dependencies/Members/BackingFieldDependencyTests.cs b/ArchUnitNETTests/Domain/Dependencies/Members/BackingFieldDependencyTests.cs deleted file mode 100644 index bf4afba93..000000000 --- a/ArchUnitNETTests/Domain/Dependencies/Members/BackingFieldDependencyTests.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2019 Florian Gather -// Copyright 2019 Paula Ruiz -// Copyright 2019 Fritz Brandhuber -// -// SPDX-License-Identifier: Apache-2.0 - -using System.Linq; -using ArchUnitNET.Domain; -using ArchUnitNET.Domain.Extensions; -using Xunit; - -namespace ArchUnitNETTests.Domain.Dependencies.Members -{ - public class BackingFieldDependencyTests - { - public BackingFieldDependencyTests() - { - var backingFieldClass = _architecture.GetClassOfType(typeof(BackingFieldExamples)); - _backedProperty = backingFieldClass - .GetPropertyMembersWithName(nameof(BackingFieldExamples.FieldPropertyPair)).First(); - _expectedBackingField = backingFieldClass.GetFieldMembersWithName("_fieldPropertyPair").First(); - _implicitlyBackedAutoProperty = backingFieldClass - .GetPropertyMembersWithName(nameof(BackingFieldExamples.AutoProperty)).First(); - _lambdaBackedProperty = backingFieldClass - .GetPropertyMembersWithName(nameof(BackingFieldExamples.LambdaFieldPropertyPair)).First(); - _lambdaBackingField = backingFieldClass.GetFieldMembersWithName("_lambdaFieldPropertyPair").First(); - } - - private readonly Architecture _architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; - - private readonly PropertyMember _backedProperty; - private readonly FieldMember _expectedBackingField; - private readonly PropertyMember _implicitlyBackedAutoProperty; - private readonly FieldMember _lambdaBackingField; - private readonly PropertyMember _lambdaBackedProperty; - - [Fact] - public void BackedPropertyInheritsBackingFieldDependencies() - { - _backedProperty.BackingField.MemberDependencies - .ForEach(memberDependency => Assert.Contains(memberDependency, _backedProperty.MemberDependencies)); - } - - [Fact] - public void ExplicitBackingFieldAssigned() - { - Assert.Equal(_expectedBackingField, _backedProperty.BackingField); - } - - [Fact] - public void ImplicitBackingFieldStubAssigned() - { - // BackingFields are not assigned if the backing field is auto-generated by the compiler - Assert.Null(_implicitlyBackedAutoProperty.BackingField); - } - - [Fact] - public void LambdaBackingFieldAssignment() - { - Assert.Equal(_lambdaBackingField, _lambdaBackedProperty.BackingField); - } - } - - // ReSharper disable All - public class BackingFieldExamples - { - private ChildField _fieldPropertyPair; - private PropertyType _lambdaFieldPropertyPair; - public PropertyType AutoProperty { get; set; } - - public PropertyType FieldPropertyPair - { - get { return _fieldPropertyPair; } - set { _fieldPropertyPair = (ChildField) value; } - } - - public PropertyType LambdaFieldPropertyPair - { - get => _lambdaFieldPropertyPair; - set => _lambdaFieldPropertyPair = value; - } - } -} \ No newline at end of file diff --git a/ArchUnitNETTests/Domain/Dependencies/Members/GetterSetterExamples.cs b/ArchUnitNETTests/Domain/Dependencies/Members/GetterSetterExamples.cs index 90b925107..51d86ffae 100644 --- a/ArchUnitNETTests/Domain/Dependencies/Members/GetterSetterExamples.cs +++ b/ArchUnitNETTests/Domain/Dependencies/Members/GetterSetterExamples.cs @@ -16,50 +16,69 @@ namespace ArchUnitNETTests.Domain.Dependencies.Members { public class GetterMethodDependencyExamples { - public Guid AcceptedCase { get; } = new Guid("35353"); + public Guid AcceptedCase + { + get + { + return new Guid("35353"); + } + } - public Guid FirstUnacceptedCase { get; } = Guid.NewGuid(); + public Guid FirstUnacceptedCase + { + get + { + return Guid.NewGuid(); + } + } public Guid SecondUnacceptedCase => Guid.NewGuid(); } public class SetterMethodDependencyExamples { - internal ChildField _castingLambdaPairBacking; - internal ChildField _castingPairBacking; - internal ChildField _constructorLambdaPairBacking; - internal ChildField _constructorPairBacking; - internal ChildField _methodLambdaPairBacking; - internal ChildField _methodPairBacking; + internal ChildField _customPropertyBacking; + internal ChildField _lambdaPairBacking; + internal PropertyType _constructorLambdaPairBacking; + internal PropertyType _methodPairBacking; + internal PropertyType _constructorPairBacking; + internal PropertyType _methodLambdaPairBacking; - public PropertyType CastingPair + public ChildField CustomProperty { - set { _castingPairBacking = (ChildField) value; } + set + { + _customPropertyBacking = value; + } + get + { + return _customPropertyBacking; + } } - public PropertyType CastingLambdaPair + public ChildField LambdaPair { - set => _castingLambdaPairBacking = (ChildField) value; + set => _lambdaPairBacking = value; } public PropertyType ConstructorPair { - set { _constructorPairBacking = new PropertyType(value) as ChildField; } + set {_constructorPairBacking = new PropertyType(value);} } public PropertyType ConstructorLambdaPair { - set => _constructorLambdaPairBacking = new PropertyType(value) as ChildField; + set => _constructorLambdaPairBacking = new PropertyType(value); } public PropertyType MethodPair { - set { _methodPairBacking = ChildField.NewPropertyType(value) as ChildField; } + set { _methodPairBacking = ChildField.NewPropertyType(value); } } public PropertyType MethodLambdaPair { - set { _methodLambdaPairBacking = ChildField.NewPropertyType(value) as ChildField; } + set { _methodLambdaPairBacking = ChildField.NewPropertyType(value); } } } diff --git a/ArchUnitNETTests/Domain/Dependencies/Members/GetterSetterMethodDependencyTests.cs b/ArchUnitNETTests/Domain/Dependencies/Members/GetterSetterMethodDependencyTests.cs index 1d880185b..03bd492fa 100644 --- a/ArchUnitNETTests/Domain/Dependencies/Members/GetterSetterMethodDependencyTests.cs +++ b/ArchUnitNETTests/Domain/Dependencies/Members/GetterSetterMethodDependencyTests.cs @@ -28,18 +28,16 @@ public GetterSetterMethodDependencyTests() [Theory] [ClassData(typeof(GetterSetterTestsBuild.SetterTestData))] - public void AssertSetterMethodDependencies(FieldMember backingField, PropertyMember backedProperty, - Class expectedTarget) + public void AssertSetterMethodDependencies(PropertyMember backedProperty, Class expectedTarget) { - backingField.MemberDependencies - .ForEach(dependency => + if (backedProperty.Setter != null) + { + foreach (var dependency in backedProperty.Setter.Dependencies) { - Assert.Contains(dependency, backedProperty.MemberDependencies); - Assert.Contains(dependency, backedProperty.Setter.MemberDependencies); - }); - - var backingFieldDependencyTargets = backingField.MemberDependencies.Select(dependency => dependency.Target); - Assert.Contains(expectedTarget, backingFieldDependencyTargets); + Assert.Contains(dependency, backedProperty.Dependencies); + } + } + Assert.Contains(expectedTarget, backedProperty.Dependencies.Select(t => t.Target)); } [Theory] diff --git a/ArchUnitNETTests/Domain/Dependencies/Members/GetterSetterTestsBuild.cs b/ArchUnitNETTests/Domain/Dependencies/Members/GetterSetterTestsBuild.cs index b5e8f94ad..2a4aa6d1d 100644 --- a/ArchUnitNETTests/Domain/Dependencies/Members/GetterSetterTestsBuild.cs +++ b/ArchUnitNETTests/Domain/Dependencies/Members/GetterSetterTestsBuild.cs @@ -32,19 +32,14 @@ public class GetterSetterTestsBuild private static readonly ConstructorInfo ConstructGuid = GuidType.GetConstructor(ExpectedParameters); private static readonly MethodMember MockConstructorMember = ConstructGuid.CreateStubMethodMember(); - private static object[] BuildSetterTestData(Type classType, string backingFieldName, - string backedPropertyName, Type expectedFieldDependencyTarget) + private static object[] BuildSetterTestData(Type classType, string backedPropertyName, + Type expectedFieldDependencyTarget) { if (classType == null) { throw new ArgumentNullException(nameof(classType)); } - if (backingFieldName == null) - { - throw new ArgumentNullException(nameof(backingFieldName)); - } - if (backedPropertyName == null) { throw new ArgumentNullException(nameof(backedPropertyName)); @@ -56,11 +51,10 @@ private static object[] BuildSetterTestData(Type classType, string backingFieldN } var baseClass = Architecture.GetClassOfType(classType); - var backingField = baseClass.GetFieldMembersWithName(backingFieldName).First(); var backedProperty = baseClass.GetPropertyMembersWithName(backedPropertyName).First(); var expectedDependencyTargetClass = Architecture.GetClassOfType(expectedFieldDependencyTarget); - return new object[] {backingField, backedProperty, expectedDependencyTargetClass}; + return new object[] {backedProperty, expectedDependencyTargetClass}; } private static object[] BuildGetterTestData(Type classType, string propertyName, @@ -114,7 +108,9 @@ private static object[] BuildAccessMethodTestData(Type classType, string propert private static MethodCallDependency CreateStubMethodCallDependency(IMember originMember, MethodMember targetMember) { - var methodCallDependency = new MethodCallDependency(originMember, targetMember); + var methodCallDependency = new MethodCallDependency(originMember, + new MethodMemberInstance(targetMember, Enumerable.Empty(), + Enumerable.Empty())); methodCallDependency.TargetMember.MemberBackwardsDependencies.Add(methodCallDependency); return methodCallDependency; } @@ -124,29 +120,23 @@ public class SetterTestData : IEnumerable private readonly List _setterTestData = new List { BuildSetterTestData(typeof(SetterMethodDependencyExamples), - nameof(SetterMethodDependencyExamples._castingPairBacking), - nameof(SetterMethodDependencyExamples.CastingPair), + nameof(SetterMethodDependencyExamples.CustomProperty), typeof(ChildField)), BuildSetterTestData(typeof(SetterMethodDependencyExamples), - nameof(SetterMethodDependencyExamples._castingLambdaPairBacking), - nameof(SetterMethodDependencyExamples.CastingLambdaPair), + nameof(SetterMethodDependencyExamples.LambdaPair), typeof(ChildField)), BuildSetterTestData(typeof(SetterMethodDependencyExamples), - nameof(SetterMethodDependencyExamples._constructorPairBacking), nameof(SetterMethodDependencyExamples.ConstructorPair), - typeof(ChildField)), + typeof(PropertyType)), BuildSetterTestData(typeof(SetterMethodDependencyExamples), - nameof(SetterMethodDependencyExamples._constructorLambdaPairBacking), nameof(SetterMethodDependencyExamples.ConstructorLambdaPair), - typeof(ChildField)), + typeof(PropertyType)), BuildSetterTestData(typeof(SetterMethodDependencyExamples), - nameof(SetterMethodDependencyExamples._methodPairBacking), nameof(SetterMethodDependencyExamples.MethodPair), - typeof(ChildField)), + typeof(PropertyType)), BuildSetterTestData(typeof(SetterMethodDependencyExamples), - nameof(SetterMethodDependencyExamples._methodLambdaPairBacking), nameof(SetterMethodDependencyExamples.MethodLambdaPair), - typeof(ChildField)) + typeof(PropertyType)) }; public IEnumerator GetEnumerator() @@ -191,10 +181,10 @@ public class AccessMethodDependenciesByPropertyTestData : IEnumerable private readonly List _accessMethodTestData = new List { BuildAccessMethodTestData(typeof(SetterMethodDependencyExamples), - nameof(SetterMethodDependencyExamples.CastingPair), + nameof(SetterMethodDependencyExamples.CustomProperty), MethodForm.Setter), BuildAccessMethodTestData(typeof(SetterMethodDependencyExamples), - nameof(SetterMethodDependencyExamples.CastingLambdaPair), + nameof(SetterMethodDependencyExamples.LambdaPair), MethodForm.Setter), BuildAccessMethodTestData(typeof(SetterMethodDependencyExamples), nameof(SetterMethodDependencyExamples.ConstructorPair), diff --git a/ArchUnitNETTests/Domain/Dependencies/Members/InheritsBaseClassDependencyTests.cs b/ArchUnitNETTests/Domain/Dependencies/Members/InheritsBaseClassDependencyTests.cs index 57709df56..f4642d0fe 100644 --- a/ArchUnitNETTests/Domain/Dependencies/Members/InheritsBaseClassDependencyTests.cs +++ b/ArchUnitNETTests/Domain/Dependencies/Members/InheritsBaseClassDependencyTests.cs @@ -7,23 +7,24 @@ using ArchUnitNET.Domain; using ArchUnitNET.Domain.Dependencies; using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; using Xunit; namespace ArchUnitNETTests.Domain.Dependencies.Members { public class BaseClassTest { + private readonly Architecture _architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; + + private readonly Class _baseClass; + private readonly Class _childClass; + public BaseClassTest() { _baseClass = _architecture.GetClassOfType(typeof(BaseClass)); _childClass = _architecture.GetClassOfType(typeof(ChildClass)); } - private readonly Architecture _architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; - - private readonly Class _baseClass; - private readonly Class _childClass; - [Fact] public void ChildClassHasAllMembers() { @@ -42,7 +43,8 @@ public void ChildClassHasBaseClass() [Fact] public void ChildClassHasBaseClassDependency() { - var expectedDependency = new InheritsBaseClassDependency(_childClass, _baseClass); + var expectedDependency = + new InheritsBaseClassDependency(_childClass, new TypeInstance(_baseClass)); Assert.True(_childClass.HasDependency(expectedDependency)); } diff --git a/ArchUnitNETTests/Domain/Dependencies/Members/KeepDependenciesInAnonymousTypesTests.cs b/ArchUnitNETTests/Domain/Dependencies/Members/KeepDependenciesInAnonymousTypesTests.cs new file mode 100644 index 000000000..4d2588574 --- /dev/null +++ b/ArchUnitNETTests/Domain/Dependencies/Members/KeepDependenciesInAnonymousTypesTests.cs @@ -0,0 +1,100 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; +using ArchUnitNETTests.Dependencies; +using Xunit; + +namespace ArchUnitNETTests.Domain.Dependencies.Members +{ + public class KeepDependenciesInAnonymousTypesTests + { + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(KeepDependenciesInCompilerGeneratedTypesTests).Assembly).Build(); + + private readonly Class _castType; + private readonly Class _class1WithAnonymous; + private readonly Class _class2WithAnonymous; + private readonly Class _instantiatedType; + + + public KeepDependenciesInAnonymousTypesTests() + { + _class1WithAnonymous = Architecture.GetClassOfType(typeof(Class1WithAnonymousType)); + _class2WithAnonymous = Architecture.GetClassOfType(typeof(Class2WithAnonymousType)); + _instantiatedType = Architecture.GetClassOfType(typeof(InstantiatedType)); + _castType = Architecture.GetClassOfType(typeof(CastType)); + } + + [Fact] + public void DependenciesInAnonymousTypesNotLost() + { + var typeDependencies = _class1WithAnonymous.GetTypeDependencies().ToList(); + var ctor = _class1WithAnonymous.GetMethodMembers().First(); + var ctorTypeDependencies = ctor.GetTypeDependencies(Architecture).ToList(); + + Assert.Contains(_instantiatedType, typeDependencies); + Assert.Single(_class1WithAnonymous.GetMethodMembers()); + Assert.Contains(_instantiatedType, ctorTypeDependencies); + } + + [Fact] + public void CastDependenciesInAnonymousTypesNotLost() + { + var typeDependencies = _class2WithAnonymous.GetTypeDependencies().ToList(); + var ctor = _class2WithAnonymous.GetMethodMembers().First(); + var ctorTypeDependencies = ctor.GetTypeDependencies(Architecture).ToList(); + + Assert.Contains(_instantiatedType, typeDependencies); + Assert.Contains(_castType, typeDependencies); + Assert.Single(_class2WithAnonymous.GetMethodMembers()); + Assert.Contains(_instantiatedType, ctorTypeDependencies); + Assert.Contains(_castType, ctorTypeDependencies); + } + + [Fact] + public void BackwardDependenciesAssignedCorrectly() + { + var instantiatedTypeBackwardDependencies = + _instantiatedType.BackwardsDependencies.Select(dep => dep.Origin).ToList(); + var castTypeBackwardDependencies = + _castType.BackwardsDependencies.Select(dep => dep.Origin).ToList(); + + Assert.Contains(_class1WithAnonymous, instantiatedTypeBackwardDependencies); + Assert.Contains(_class2WithAnonymous, instantiatedTypeBackwardDependencies); + + Assert.Contains(_class2WithAnonymous, castTypeBackwardDependencies); + } + } + + internal class Class1WithAnonymousType + { + public Class1WithAnonymousType() + { + var anonymousType = new {referencedType = new InstantiatedType()}; + } + } + + internal class Class2WithAnonymousType + { + public Class2WithAnonymousType() + { + var anonymousType = new {castType = (CastType) new InstantiatedType(), i = 3}; + } + } + + internal class InstantiatedType + { + } + + internal class CastType : InstantiatedType + { + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Domain/Dependencies/Members/MemberDependencyTestBuild.cs b/ArchUnitNETTests/Domain/Dependencies/Members/MemberDependencyTestBuild.cs index d8e1439c9..a6cbf637e 100644 --- a/ArchUnitNETTests/Domain/Dependencies/Members/MemberDependencyTestBuild.cs +++ b/ArchUnitNETTests/Domain/Dependencies/Members/MemberDependencyTestBuild.cs @@ -4,14 +4,15 @@ // // SPDX-License-Identifier: Apache-2.0 -using System; using System.Collections; using System.Collections.Generic; using System.Linq; using ArchUnitNET.Domain; using ArchUnitNET.Domain.Dependencies; using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; using ArchUnitNETTests.Fluent.Extensions; +using Type = System.Type; namespace ArchUnitNETTests.Domain.Dependencies.Members { @@ -30,9 +31,10 @@ private static object[] BuildBodyTypeMemberDependencyTestData(Type originType, s return new object[] {null, null, null, null}; } - var memberTypeDependency = new BodyTypeMemberDependency(originMember, classMemberInfo.TargetClass); + var memberTypeDependency = + new BodyTypeMemberDependency(originMember, new TypeInstance(classMemberInfo.TargetClass)); object duplicateMemberTypeDependency = - new BodyTypeMemberDependency(originMember, classMemberInfo.TargetClass); + new BodyTypeMemberDependency(originMember, new TypeInstance(classMemberInfo.TargetClass)); var dependencyReferenceDuplicate = memberTypeDependency; object objectReferenceDuplicate = memberTypeDependency; @@ -75,8 +77,12 @@ private static object[] BuildMethodCallDependencyTestData(Type originType, strin return new object[] {null, null, null, null}; } - var memberTypeDependency = new MethodCallDependency(classMemberInfo.OriginMember, targetMember); - object duplicateMemberTypeDependency = new MethodCallDependency(classMemberInfo.OriginMember, targetMember); + var memberTypeDependency = new MethodCallDependency(classMemberInfo.OriginMember, + new MethodMemberInstance(targetMember, Enumerable.Empty(), + Enumerable.Empty())); + object duplicateMemberTypeDependency = new MethodCallDependency(classMemberInfo.OriginMember, + new MethodMemberInstance(targetMember, Enumerable.Empty(), + Enumerable.Empty())); var dependencyReferenceDuplicate = memberTypeDependency; object objectReferenceDuplicate = memberTypeDependency; @@ -96,9 +102,10 @@ private static object[] BuildMethodSignatureDependencyTestData(Type originType, return new object[] {null, null, null, null}; } - var memberTypeDependency = new MethodSignatureDependency(originMember, classMemberInfo.TargetClass); + var memberTypeDependency = + new MethodSignatureDependency(originMember, new TypeInstance(classMemberInfo.TargetClass)); object duplicateMemberTypeDependency = - new MethodSignatureDependency(originMember, classMemberInfo.TargetClass); + new MethodSignatureDependency(originMember, new TypeInstance(classMemberInfo.TargetClass)); var dependencyReferenceDuplicate = memberTypeDependency; object objectReferenceDuplicate = memberTypeDependency; diff --git a/ArchUnitNETTests/Domain/Dependencies/Members/MethodDependencyTestBuild.cs b/ArchUnitNETTests/Domain/Dependencies/Members/MethodDependencyTestBuild.cs index b89103096..e69afc991 100644 --- a/ArchUnitNETTests/Domain/Dependencies/Members/MethodDependencyTestBuild.cs +++ b/ArchUnitNETTests/Domain/Dependencies/Members/MethodDependencyTestBuild.cs @@ -4,14 +4,15 @@ // // SPDX-License-Identifier: Apache-2.0 -using System; using System.Collections; using System.Collections.Generic; using System.Linq; using ArchUnitNET.Domain; using ArchUnitNET.Domain.Dependencies; using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; using ArchUnitNETTests.Fluent.Extensions; +using Type = System.Type; namespace ArchUnitNETTests.Domain.Dependencies.Members { @@ -26,7 +27,8 @@ private static object[] BuildMethodCallDependencyTestData(Type originType, strin var originMember = originClass.GetMembersWithName(nameOfOriginMember).Single(); var targetClass = Architecture.GetClassOfType(targetType); var targetMember = targetClass.GetMethodMembersWithName(nameOfTargetMember).Single(); - var expectedDependency = new MethodCallDependency(originMember, targetMember); + var expectedDependency = new MethodCallDependency(originMember, new MethodMemberInstance(targetMember, + Enumerable.Empty(), Enumerable.Empty())); return new object[] {originMember, expectedDependency}; } @@ -35,8 +37,9 @@ private static object[] BuildMethodSignatureDependencyTestData(Type originType, { var originClass = Architecture.GetClassOfType(originType); var originMember = originClass.GetMethodMembersWithName(nameOfOriginMember).Single(); - var target = Architecture.GetITypeOfType(targetType); - var expectedDependency = new MethodSignatureDependency(originMember, target); + var target = Architecture.GetClassOfType(targetType); + var expectedDependency = + new MethodSignatureDependency(originMember, new TypeInstance(target)); return new object[] {originMember, expectedDependency}; } diff --git a/ArchUnitNETTests/Domain/Dependencies/Types/GenericInterfaceTests.cs b/ArchUnitNETTests/Domain/Dependencies/Types/GenericInterfaceTests.cs index 1ce9d8af5..7d08403c8 100644 --- a/ArchUnitNETTests/Domain/Dependencies/Types/GenericInterfaceTests.cs +++ b/ArchUnitNETTests/Domain/Dependencies/Types/GenericInterfaceTests.cs @@ -6,9 +6,9 @@ using System.Linq; using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Dependencies; using ArchUnitNET.Domain.Extensions; using Xunit; -using Xunit.Sdk; // ReSharper disable UnusedTypeParameter @@ -16,6 +16,12 @@ namespace ArchUnitNETTests.Domain.Dependencies.Types { public class GenericInterfaceTests { + private readonly Architecture _architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; + + private readonly Interface _genericInterface; + private readonly Class _genericInterfaceImplementation; + private readonly Class _genericType; + public GenericInterfaceTests() { _genericInterface = _architecture.GetInterfaceOfType(typeof(IGenericInterface<>)); @@ -23,24 +29,16 @@ public GenericInterfaceTests() _genericType = _architecture.GetClassOfType(typeof(GenericType)); } - private readonly Architecture _architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; - - private readonly Interface _genericInterface; - private readonly Class _genericInterfaceImplementation; - private readonly Class _genericType; - [Fact] public void ClassImplementsGenericInterface() { - // Setup, Act - var implementedGenericInterface = _genericInterfaceImplementation.ImplementedInterfaces.FirstOrDefault(); - var genericArgumentsOfImplementedInterface = implementedGenericInterface?.GenericTypeArguments; + var implementsInterfaceDependency = _genericInterfaceImplementation.Dependencies + .OfType().First(); + var genericArgumentsOfImplementedInterface = implementsInterfaceDependency.TargetGenericArguments + .Select(argument => argument.Type).ToList(); - // Assert Assert.True(_genericInterfaceImplementation.ImplementsInterface(_genericInterface)); - Assert.Single(genericArgumentsOfImplementedInterface ?? - throw new NullException("No generic arguments from inherited " + - "generic interface implementation.")); + Assert.Single(genericArgumentsOfImplementedInterface); Assert.Contains(_genericType, genericArgumentsOfImplementedInterface); } } diff --git a/ArchUnitNETTests/Domain/Dependencies/Types/ImplementingInterfacesTest.cs b/ArchUnitNETTests/Domain/Dependencies/Types/ImplementingInterfacesTest.cs index 6e843e39f..3787b8e37 100644 --- a/ArchUnitNETTests/Domain/Dependencies/Types/ImplementingInterfacesTest.cs +++ b/ArchUnitNETTests/Domain/Dependencies/Types/ImplementingInterfacesTest.cs @@ -7,6 +7,7 @@ using ArchUnitNET.Domain; using ArchUnitNET.Domain.Dependencies; using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; using Xunit; using static ArchUnitNETTests.Domain.StaticTestTypes; @@ -14,6 +15,10 @@ namespace ArchUnitNETTests.Domain.Dependencies.Types { public class ImplementingInterfacesTest { + private readonly Interface _implementingInterface; + private readonly Interface _inheritedTestInterface; + private readonly Class _inheritingType; + public ImplementingInterfacesTest() { _implementingInterface = InheritingInterface; @@ -21,14 +26,11 @@ public ImplementingInterfacesTest() _inheritingType = StaticTestTypes.InheritingType; } - private readonly Interface _implementingInterface; - private readonly Interface _inheritedTestInterface; - private readonly Class _inheritingType; - [Fact] public void InheritingTypeImplementsInheritedInterface() { - var expectedDependency = new ImplementsInterfaceDependency(_inheritingType, _implementingInterface); + var expectedDependency = + new ImplementsInterfaceDependency(_inheritingType, new TypeInstance(_implementingInterface)); Assert.True(_inheritingType.HasDependency(expectedDependency)); Assert.True(_inheritingType.ImplementsInterface(_implementingInterface)); diff --git a/ArchUnitNETTests/Domain/GenericClassTests.cs b/ArchUnitNETTests/Domain/GenericClassTests.cs index 925022995..b938e2efe 100644 --- a/ArchUnitNETTests/Domain/GenericClassTests.cs +++ b/ArchUnitNETTests/Domain/GenericClassTests.cs @@ -21,6 +21,13 @@ public class GenericClassTests private const string GuidClassName = "Guid"; private const string SystemGuidFullName = StaticConstants.SystemNamespace + "." + GuidClassName; + private static readonly Architecture Architecture = + new ArchLoader().LoadAssembly(typeof(GenericClassTests).Assembly).Build(); + + private readonly Class _classWithGenericParameters; + private readonly Class _expectedGenericArgument; + private readonly FieldMember _genericallyTypedField; + public GenericClassTests() { _classWithGenericParameters = Architecture.GetClassOfType(typeof(ClassWithGenericParameters<>)); @@ -29,24 +36,15 @@ public GenericClassTests() .GetFieldMembersWithName(nameof(InvokesGenericClass.GuidGenericArgument)).SingleOrDefault(); var guidMock = new Type(SystemGuidFullName, GuidClassName, _classWithGenericParameters.Assembly, - new Namespace(StaticConstants.SystemNamespace, new List()), Public, false); + new Namespace(StaticConstants.SystemNamespace, new List()), Public, false, false, true, false); _expectedGenericArgument = new Class(guidMock, false, true, true, false); } - private static readonly Architecture Architecture = - new ArchLoader().LoadAssembly(typeof(GenericClassTests).Assembly).Build(); - - private readonly Class _classWithGenericParameters; - private readonly FieldMember _genericallyTypedField; - private readonly Class _expectedGenericArgument; - [Fact] public void GenericTypeArgumentsAsExpected() { - //Setup - var genericTypeArgumentClass = _genericallyTypedField.Type.GenericTypeArguments.First() as Class; + var genericTypeArgumentClass = _genericallyTypedField.GenericArguments.First().Type as Class; - //Assert Assert.NotNull(genericTypeArgumentClass); Assert.Equal(_expectedGenericArgument, genericTypeArgumentClass); } @@ -54,24 +52,22 @@ public void GenericTypeArgumentsAsExpected() [Fact] public void GenericTypeArgumentsFound() { - Assert.Single(_genericallyTypedField.Type.GenericTypeArguments); + Assert.Single(_genericallyTypedField.GenericArguments); } [Fact] public void GenericTypeAsExpected() { - //Setup var invokedGenericType = _genericallyTypedField.Type; - //Assert - Assert.Equal(_classWithGenericParameters, invokedGenericType.GenericType); + Assert.Equal(_classWithGenericParameters, invokedGenericType); } [Fact] public void GenericTypeParametersFound() { - Assert.NotEmpty(_classWithGenericParameters.GenericTypeParameters); - Assert.Single(_classWithGenericParameters.GenericTypeParameters); + Assert.NotEmpty(_classWithGenericParameters.GenericParameters); + Assert.Single(_classWithGenericParameters.GenericParameters); } } diff --git a/ArchUnitNETTests/Fluent/DependencyErrorMessageTests.cs b/ArchUnitNETTests/Fluent/DependencyErrorMessageTests.cs deleted file mode 100644 index 95af9e9db..000000000 --- a/ArchUnitNETTests/Fluent/DependencyErrorMessageTests.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2019 Florian Gather -// Copyright 2019 Fritz Brandhuber -// Copyright 2020 Pavel Fischer -// -// SPDX-License-Identifier: Apache-2.0 -// - -using System.Linq; -using ArchUnitNET.Domain; -using ArchUnitNET.Loader; -using Xunit; -using static ArchUnitNET.Fluent.ArchRuleDefinition; - -namespace ArchUnitNETTests.Fluent -{ - public class DependencyErrorMessageTests - { - private static readonly Architecture Architecture = - StaticTestArchitectures.ArchUnitNETTestArchitecture; - - [Fact] - public void ErrorMessageType() - { - var rule = Classes().That() - .HaveFullNameMatching(typeof(DependencyErrorMessageTestClass).FullName) - .Should().NotDependOnAny(typeof(ErrorMessageClass1), typeof(ErrorMessageClass2)); - var failDescription = rule.Evaluate(Architecture).ToList().First().Description; - Assert.DoesNotContain(typeof(string).FullName, failDescription); - Assert.DoesNotContain(typeof(ErrorMessageClass2).FullName, failDescription); - Assert.Contains(typeof(ErrorMessageClass1).FullName, failDescription); - } - - [Fact] - public void ErrorMessageIType() - { - var dependencyClass = Classes().That().HaveFullNameMatching(typeof(ErrorMessageClass1).FullName).Or().HaveFullNameMatching(typeof(ErrorMessageClass2).FullName) - .GetObjects(Architecture).ToList(); - var rule = Classes().That() - .HaveFullNameMatching(typeof(DependencyErrorMessageTestClass).FullName) - .Should().NotDependOnAny(dependencyClass); - var failDescription = rule.Evaluate(Architecture).ToList().First().Description; - Assert.DoesNotContain(typeof(string).FullName, failDescription); - Assert.DoesNotContain(typeof(ErrorMessageClass2).FullName, failDescription); - Assert.Contains(typeof(ErrorMessageClass1).FullName, failDescription); - } - - [Fact] - public void ErrorMessageIObjectProvider() - { - var dependencyClass = Classes().That().HaveFullNameMatching(typeof(ErrorMessageClass1).FullName).Or() - .HaveFullNameMatching(typeof(ErrorMessageClass2).FullName); - var rule = Classes().That() - .HaveFullNameMatching(typeof(DependencyErrorMessageTestClass).FullName) - .Should().NotDependOnAny(dependencyClass); - var failDescription = rule.Evaluate(Architecture).ToList().First().Description; - Assert.DoesNotContain(typeof(string).FullName, failDescription); - Assert.DoesNotContain(typeof(ErrorMessageClass2).FullName, failDescription); - Assert.Contains(typeof(ErrorMessageClass1).FullName, failDescription); - } - } - - public class DependencyErrorMessageTestClass - { - ErrorMessageClass1 testData = new ErrorMessageClass1(); - private string testString = ""; - } - - public class ErrorMessageClass1 - { - - } - - public class ErrorMessageClass2 - { - - } -} diff --git a/ArchUnitNETTests/Fluent/Extensions/BuildMocksExtensions.cs b/ArchUnitNETTests/Fluent/Extensions/BuildMocksExtensions.cs index f195a2fc9..4defdbee2 100644 --- a/ArchUnitNETTests/Fluent/Extensions/BuildMocksExtensions.cs +++ b/ArchUnitNETTests/Fluent/Extensions/BuildMocksExtensions.cs @@ -10,6 +10,7 @@ using System.Reflection; using System.Text; using ArchUnitNET.Domain; +using ArchUnitNET.Loader; using static ArchUnitNET.Domain.Visibility; using Assembly = ArchUnitNET.Domain.Assembly; using Type = ArchUnitNET.Loader.Type; @@ -23,7 +24,8 @@ private static Type CreateStubType(this System.Type type) var assembly = type.Assembly.CreateStubAssembly(); var namespc = type.Namespace.CreateStubNamespace(); var visibility = type.GetVisibility(); - return new Type(type.FullName, type.Name, assembly, namespc, visibility, type.IsNested); + return new Type(type.FullName, type.Name, assembly, namespc, visibility, type.IsNested, type.IsGenericType, + true, false); } private static Visibility GetVisibility(this System.Type type) @@ -74,8 +76,10 @@ public static Class CreateStubClass(this System.Type type) public static Type CreateShallowStubType(this Class clazz) { - return new Type(clazz.FullName, clazz.Name, clazz.Assembly, clazz.Namespace, clazz.Visibility, - clazz.IsNested); + var type = new Type(clazz.FullName, clazz.Name, clazz.Assembly, clazz.Namespace, clazz.Visibility, + clazz.IsNested, clazz.IsGeneric, clazz.IsStub, clazz.IsCompilerGenerated); + type.GenericParameters.AddRange(clazz.GenericParameters); + return type; } private static Assembly CreateStubAssembly(this System.Reflection.Assembly assembly) @@ -114,26 +118,33 @@ public static MethodMember CreateStubMethodMember(this MethodBase methodBase) var visibility = methodBase.GetVisibility(); var declaringType = methodBase.DeclaringType.CreateStubClass(); - var parameters = methodBase.CreateStubParameters(); + var parameters = methodBase.CreateStubParameters().Select(parameter => new TypeInstance(parameter)); var methodForm = methodBase.GetStubMethodForm(); - Class returnType = null; + var isGeneric = methodBase.IsGenericMethod; + + TypeInstance returnTypeInstance = null; string fullName = null; if (methodBase is ConstructorInfo constructor) { - returnType = typeof(void).CreateStubClass(); - fullName = constructor.CreateStubFullName(returnType); + var voi = typeof(void).CreateStubClass(); + returnTypeInstance = new TypeInstance(voi); + fullName = constructor.CreateStubFullName(voi); } if (methodBase is MethodInfo methodInfo) { - returnType = methodInfo.ReturnType.CreateStubClass(); + var returnType = methodInfo.ReturnType.CreateStubClass(); + returnTypeInstance = new TypeInstance(returnType); fullName = methodInfo.CreateStubFullName(); } - return new MethodMember(methodBase.BuildMockMethodName(), fullName, declaringType, visibility, parameters, - returnType, methodBase.IsVirtual, methodForm, new List()); + var methodMember = new MethodMember(methodBase.BuildMockMethodName(), fullName, declaringType, visibility, + returnTypeInstance, methodBase.IsVirtual, methodForm, isGeneric, false, false); + + methodMember.ParameterInstances.AddRange(parameters); + return methodMember; } private static string BuildMockMethodName(this MethodBase methodBase) diff --git a/ArchUnitNETTests/Fluent/Syntax/Elements/ObjectSyntaxElementsTests.cs b/ArchUnitNETTests/Fluent/Syntax/Elements/ObjectSyntaxElementsTests.cs index ec208769c..64bb1860b 100644 --- a/ArchUnitNETTests/Fluent/Syntax/Elements/ObjectSyntaxElementsTests.cs +++ b/ArchUnitNETTests/Fluent/Syntax/Elements/ObjectSyntaxElementsTests.cs @@ -19,13 +19,14 @@ namespace ArchUnitNETTests.Fluent.Syntax.Elements { public class ObjectSyntaxElementsTests { - private static readonly Architecture Architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; - private readonly IEnumerable _types = Architecture.Types; private const string NoTypeName = "NotTheNameOfAnyType_58391351286"; + private static readonly Architecture Architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; private readonly IEnumerable _falseDependencies = new List {typeof(ClassWithNoDependencies1), typeof(ClassWithNoDependencies2)}; + private readonly IEnumerable _types = Architecture.Types; + [Fact] public void AreTest() { @@ -186,6 +187,11 @@ public void DependOnTypesTest() { foreach (var type in _types) { + if (type.FullNameContains("ObjectSyntaxElementsTests")) + { + continue; + } + //One Argument var typeDependencies = type.GetTypeDependencies(Architecture).ToList(); var typesDependOnOwnDependencies = @@ -198,6 +204,7 @@ public void DependOnTypesTest() Types().That().Are(type).Should().OnlyDependOnTypesThat().Are(typeof(ClassWithNoDependencies1)); Assert.True(typesDependOnOwnDependencies.HasNoViolations(Architecture)); + typeDoesNotDependOnOneFalseDependency.Check(Architecture); Assert.True(typeDoesNotDependOnOneFalseDependency.HasNoViolations(Architecture)); Assert.False(typeDependsOnOneFalseDependency.HasNoViolations(Architecture)); Assert.Equal(typeDependencies.IsNullOrEmpty(), @@ -242,6 +249,11 @@ public void DependOnTypesThatTest() { foreach (var type in _types) { + if (type.FullNameContains("ObjectSyntaxElementsTests")) + { + continue; + } + // ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator foreach (var dependency in type.Dependencies) { @@ -303,12 +315,13 @@ public void DependOnTypesThatTest() } var noTypeDependsOnFalseDependency = - Types().That().DependOnAny(typeof(ClassWithNoDependencies1)).Should().NotExist(); + Types().That().DependOnAny(typeof(ClassWithNoDependencies1)).Should() + .Be(typeof(ObjectSyntaxElementsTests)); var noTypeDependsOnMultipleFalseDependencies = Types().That().DependOnAny(typeof(ClassWithNoDependencies1), typeof(ClassWithNoDependencies2)).Should() - .NotExist(); + .Be(typeof(ObjectSyntaxElementsTests)); var noTypeDependsOnListOfMultipleFalseDependencies = - Types().That().DependOnAny(_falseDependencies).Should().NotExist(); + Types().That().DependOnAny(_falseDependencies).Should().Be(typeof(ObjectSyntaxElementsTests)); var typesDoNotDependOnFalseDependency = Types().That().DoNotDependOnAny(typeof(ClassWithNoDependencies1)).Should().Exist(); var typesDoNotDependOnMultipleFalseDependencies = diff --git a/ArchUnitNETTests/GithubIssuesTests/GithubIssue16Test.cs b/ArchUnitNETTests/GithubIssuesTests/GithubIssue16Test.cs new file mode 100644 index 000000000..8999074cb --- /dev/null +++ b/ArchUnitNETTests/GithubIssuesTests/GithubIssue16Test.cs @@ -0,0 +1,40 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using ArchUnitNET.Domain; +using ArchUnitNET.Fluent; +using ArchUnitNET.Loader; +using Xunit; + +namespace ArchUnitNETTests.GithubIssuesTests +{ + public class GithubIssue16Test + { + private static readonly Architecture Architecture = + new ArchLoader().LoadAssemblies(typeof(Architecture).Assembly).Build(); + + private static readonly IObjectProvider CoreLayer = ArchRuleDefinition.Types().That() + .ResideInNamespace("ArchUnitNET.Core").As("ArchUnitNET.Core"); + + private static readonly IObjectProvider DomainLayer = ArchRuleDefinition.Types().That() + .ResideInNamespace("ArchUnitNET.Domain").As("ArchUnitNET.Domain"); + + private static readonly IObjectProvider FluentLayer = ArchRuleDefinition.Types().That() + .ResideInNamespace("ArchUnitNET.Fluent").As("ArchUnitNET.Fluent"); + + [Fact] + public void Issue16Test() + { + var rule1 = ArchRuleDefinition.Types() + .That().Are(FluentLayer) + .Should().OnlyDependOn(ArchRuleDefinition.Types().That().ResideInNamespace("System").Or() + .ResideInNamespace("ArchUnitNET.Core").Or().ResideInNamespace("ArchUnitNET.Domain").Or() + .ResideInNamespace("ArchUnitNET.Fluent")); + rule1.Check(Architecture); + } + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/GithubIssuesTests/GithubIssue27Test.cs b/ArchUnitNETTests/GithubIssuesTests/GithubIssue27Test.cs new file mode 100644 index 000000000..6d41969b8 --- /dev/null +++ b/ArchUnitNETTests/GithubIssuesTests/GithubIssue27Test.cs @@ -0,0 +1,25 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using ArchUnitNET.Fluent; +using Newtonsoft.Json; +using TestAssembly.GithubIssuesTests; +using Xunit; + +namespace ArchUnitNETTests.GithubIssuesTests +{ + public class GithubIssue27Test + { + [Fact] + public void Issue16Test() + { + var rule1 = ArchRuleDefinition.Classes().That().AreNot(typeof(ClassDependingOnJsonConvert)).Should() + .NotDependOnAny(typeof(JsonConvert)); + rule1.Check(StaticTestArchitectures.ArchUnitNETTestAssemblyArchitecture); + } + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/GithubIssuesTests/GithubIssue6Test.cs b/ArchUnitNETTests/GithubIssuesTests/GithubIssue6Test.cs new file mode 100644 index 000000000..c7a96021e --- /dev/null +++ b/ArchUnitNETTests/GithubIssuesTests/GithubIssue6Test.cs @@ -0,0 +1,61 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using System.Collections.Generic; +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Extensions; +using ArchUnitNET.Loader; +using Xunit; + +namespace ArchUnitNETTests.GithubIssuesTests +{ + public class GithubIssue6Test + { + private static readonly Architecture Architecture = + new ArchLoader().LoadAssemblies(typeof(ClassDependingOnStaticExtensionMethod).Assembly).Build(); + + private readonly Class _dependantClass; + private readonly Class _extensionClass; + private readonly MethodMember _extensionMethod; + + public GithubIssue6Test() + { + _dependantClass = Architecture.GetClassOfType(typeof(ClassDependingOnStaticExtensionMethod)); + _extensionClass = Architecture.GetClassOfType(typeof(StaticExtensionClass)); + _extensionMethod = _extensionClass.GetMethodMembers() + .First(member => member.FullNameContains("ExtensionMethod")); + } + + [Fact] + public void Issue6Test() + { + var typeDependencies = _dependantClass.GetTypeDependencies().ToList(); + var calledMethods = _dependantClass.GetCalledMethods().ToList(); + + Assert.Contains(_extensionClass, typeDependencies); + Assert.Contains(_extensionMethod, calledMethods); + } + } + + internal class ClassDependingOnStaticExtensionMethod + { + public bool MethodWithDependency() + { + var list = new List(); + return list.ExtensionMethod(); + } + } + + internal static class StaticExtensionClass + { + public static bool ExtensionMethod(this List list) + { + return list.Contains("test"); + } + } +} \ No newline at end of file diff --git a/ArchUnitNETTests/Loader/RegexUtilsTests.cs b/ArchUnitNETTests/Loader/RegexUtilsTests.cs index 755cb56ea..92002edf4 100644 --- a/ArchUnitNETTests/Loader/RegexUtilsTests.cs +++ b/ArchUnitNETTests/Loader/RegexUtilsTests.cs @@ -16,29 +16,21 @@ namespace ArchUnitNETTests.Loader { public class RegexUtilsTest { - public RegexUtilsTest() - { - var propertyClass = Architecture.GetClassOfType(typeof(BackingFieldExamples)); - _autoPropertyMember = propertyClass.GetPropertyMembersWithName("AutoProperty").Single(); - _expectedBackingFieldName = BuildExpectedBackingFieldName(_autoPropertyMember); - _expectedGetMethodName = BuildExpectedGetMethodName(_autoPropertyMember); - _expectedGetMethodFullName = BuildExpectedGetMethodFullName(_autoPropertyMember); - _expectedSetMethodName = BuildExpectedSetMethodName(_autoPropertyMember, _autoPropertyMember.DeclaringType); - } - private static readonly Architecture Architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; + private static readonly string _nonMatch = "Not expected to match."; private readonly PropertyMember _autoPropertyMember; - private readonly string _expectedBackingFieldName; - private readonly string _expectedGetMethodName; private readonly string _expectedGetMethodFullName; + private readonly string _expectedGetMethodName; private readonly string _expectedSetMethodName; private readonly string _nonMatchEmpty = string.Empty; - private static readonly string _nonMatch = "Not expected to match."; - private static string BuildExpectedBackingFieldName(PropertyMember propertyMember) + public RegexUtilsTest() { - return propertyMember.DeclaringType.FullName + " " + propertyMember.DeclaringType.Name + "::<" - + propertyMember.Name + ">" + StaticConstants.BackingField; + var propertyClass = Architecture.GetClassOfType(typeof(BackingFieldExamples)); + _autoPropertyMember = propertyClass.GetPropertyMembersWithName("AutoProperty").Single(); + _expectedGetMethodName = BuildExpectedGetMethodName(_autoPropertyMember); + _expectedGetMethodFullName = BuildExpectedGetMethodFullName(_autoPropertyMember); + _expectedSetMethodName = BuildExpectedSetMethodName(_autoPropertyMember, _autoPropertyMember.DeclaringType); } private static string BuildExpectedGetMethodName(PropertyMember propertyMember, @@ -92,20 +84,6 @@ private static StringBuilder AddParameterTypesToMethodName(StringBuilder nameBui return nameBuilder; } - [Fact] - public void BackingFieldRegexMatchAsExpected() - { - Assert.Equal(_autoPropertyMember.Name, - RegexUtils.MatchFieldName(_expectedBackingFieldName)); - } - - [Fact] - public void BackingFieldRegexRecognizesNonMatch() - { - Assert.Null(RegexUtils.MatchFieldName(_nonMatchEmpty)); - Assert.Null(RegexUtils.MatchFieldName(_nonMatch)); - } - [Fact] public void GetMethodFullNameRegexRecognizesNonMatch() { @@ -156,4 +134,18 @@ public void SetMethodPropertyMemberRegexMatchAsExpected() RegexUtils.MatchSetPropertyName(_expectedSetMethodName)); } } + + public class BackingFieldExamples + { + private ChildField _fieldPropertyPair; + public PropertyType AutoProperty { get; set; } + + public PropertyType FieldPropertyPair + { + get => _fieldPropertyPair; + set => _fieldPropertyPair = (ChildField) value; + } + + public PropertyType LambdaFieldPropertyPair { get; set; } + } } \ No newline at end of file diff --git a/ArchUnitNETTests/Loader/TypeTestBuild.cs b/ArchUnitNETTests/Loader/TypeTestBuild.cs index 60b5895e0..5b94b325a 100644 --- a/ArchUnitNETTests/Loader/TypeTestBuild.cs +++ b/ArchUnitNETTests/Loader/TypeTestBuild.cs @@ -25,7 +25,9 @@ private static object[] BuildTypeTestData(Type originType) clazz.RequiredNotNull(); var type = new ArchUnitType(clazz.FullName, clazz.Name, clazz.Assembly, clazz.Namespace, - clazz.Visibility, clazz.IsNested); + clazz.Visibility, clazz.IsNested, clazz.IsGeneric, clazz.IsStub, clazz.IsCompilerGenerated); + + type.GenericParameters.AddRange(clazz.GenericParameters); return new object[] {type}; } @@ -35,9 +37,10 @@ private static object[] BuildTypeEquivalenceTestData(Type originType) var clazz = Architecture.GetITypeOfType(originType); clazz.RequiredNotNull(); var type = new ArchUnitType(clazz.FullName, clazz.Name, clazz.Assembly, clazz.Namespace, - clazz.Visibility, clazz.IsNested); + clazz.Visibility, clazz.IsNested, clazz.IsGeneric, clazz.IsStub, clazz.IsCompilerGenerated); object duplicateType = new ArchUnitType(clazz.FullName, clazz.Name, clazz.Assembly, - clazz.Namespace, clazz.Visibility, clazz.IsNested); + clazz.Namespace, clazz.Visibility, clazz.IsNested, clazz.IsGeneric, clazz.IsStub, + clazz.IsCompilerGenerated); var typeCopy = type; object referenceCopy = type; diff --git a/ArchUnitNETTests/Loader/TypeTests.cs b/ArchUnitNETTests/Loader/TypeTests.cs index fdf0825e6..81dfeb28c 100644 --- a/ArchUnitNETTests/Loader/TypeTests.cs +++ b/ArchUnitNETTests/Loader/TypeTests.cs @@ -17,6 +17,13 @@ namespace ArchUnitNETTests.Loader { public class TypeTests { + private readonly Architecture _architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; + private readonly object _duplicateReference; + private readonly Type _duplicateType; + private readonly Interface _exampleInterface; + private readonly Class _expectedAttributeClass; + private readonly Type _type; + public TypeTests() { _type = _architecture.GetClassOfType(typeof(AssignClass)).CreateShallowStubType(); @@ -32,13 +39,6 @@ public TypeTests() _expectedAttributeClass = _architecture.GetClassOfType(typeof(ExampleAttribute)); } - private readonly Architecture _architecture = StaticTestArchitectures.ArchUnitNETTestArchitecture; - private readonly Type _type; - private readonly Type _duplicateType; - private readonly object _duplicateReference; - private readonly Interface _exampleInterface; - private readonly Class _expectedAttributeClass; - [Theory] [ClassData(typeof(TypeTestBuild.TypeModelingTestData))] public void ToStringAsExpected(Type type) @@ -123,7 +123,8 @@ public void IsAssignableToDuplicate() public void IsAssignableToImplementedInterface() { //Setup, Act - var interfaceDependency = new ImplementsInterfaceDependency(_type, _exampleInterface); + var interfaceDependency = + new ImplementsInterfaceDependency(_type, new TypeInstance(_exampleInterface)); _type.Dependencies.Add(interfaceDependency); //Assert diff --git a/ArchUnitNETTests/SkipInReleaseBuild.cs b/ArchUnitNETTests/SkipInReleaseBuild.cs new file mode 100644 index 000000000..b335b493e --- /dev/null +++ b/ArchUnitNETTests/SkipInReleaseBuild.cs @@ -0,0 +1,21 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using Xunit; + +namespace ArchUnitNETTests +{ + public sealed class SkipInReleaseBuild : FactAttribute + { + public SkipInReleaseBuild() + { +#if !DEBUG + Skip = "This test only works in debug build"; +#endif + } + } +} \ No newline at end of file diff --git a/TestAssembly/Class2.cs b/TestAssembly/Class2.cs index 50c0cd8e0..63d7220d2 100644 --- a/TestAssembly/Class2.cs +++ b/TestAssembly/Class2.cs @@ -8,5 +8,8 @@ namespace TestAssembly { public class Class2 { + public void Class2Method() + { + } } -} \ No newline at end of file +} diff --git a/TestAssembly/Class3.cs b/TestAssembly/Class3.cs new file mode 100644 index 000000000..711a265f1 --- /dev/null +++ b/TestAssembly/Class3.cs @@ -0,0 +1,16 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +namespace TestAssembly +{ + public class Class3 + { + public static void Class3StaticMethod() + { + } + } +} diff --git a/TestAssembly/GithubIssuesTests/ClassDependingOnJsonConvert.cs b/TestAssembly/GithubIssuesTests/ClassDependingOnJsonConvert.cs new file mode 100644 index 000000000..97ec4abde --- /dev/null +++ b/TestAssembly/GithubIssuesTests/ClassDependingOnJsonConvert.cs @@ -0,0 +1,20 @@ +// Copyright 2019 Florian Gather +// Copyright 2019 Fritz Brandhuber +// Copyright 2020 Pavel Fischer +// +// SPDX-License-Identifier: Apache-2.0 +// + +using Newtonsoft.Json; + +namespace TestAssembly.GithubIssuesTests +{ + public class ClassDependingOnJsonConvert + { + public void TestMethod() + { + var testObject = new ClassDependingOnJsonConvert(); + JsonConvert.SerializeObject(testObject); + } + } +} \ No newline at end of file diff --git a/TestAssembly/TestAssembly.csproj b/TestAssembly/TestAssembly.csproj index 707dc5a59..d45867f0a 100644 --- a/TestAssembly/TestAssembly.csproj +++ b/TestAssembly/TestAssembly.csproj @@ -8,4 +8,7 @@ + + + \ No newline at end of file