diff --git a/ArchUnitNET/Domain/FieldMember.cs b/ArchUnitNET/Domain/FieldMember.cs index 14ce7306b..59ff721e7 100644 --- a/ArchUnitNET/Domain/FieldMember.cs +++ b/ArchUnitNET/Domain/FieldMember.cs @@ -15,7 +15,7 @@ public class FieldMember : IMember, ITypeInstance private readonly ITypeInstance _typeInstance; public FieldMember(IType declaringType, string name, string fullName, Visibility visibility, - ITypeInstance typeInstance, bool isCompilerGenerated) + ITypeInstance typeInstance, bool isCompilerGenerated, bool? isStatic) { DeclaringType = declaringType; Name = name; @@ -23,6 +23,7 @@ public FieldMember(IType declaringType, string name, string fullName, Visibility Visibility = visibility; IsCompilerGenerated = isCompilerGenerated; _typeInstance = typeInstance; + IsStatic = isStatic; } public Visibility Visibility { get; } @@ -32,10 +33,11 @@ public FieldMember(IType declaringType, string name, string fullName, Visibility public string FullName { get; } public bool IsCompilerGenerated { get; } + public bool? IsStatic { get; } public bool IsGeneric => false; public List GenericParameters => new List(); public IEnumerable Attributes => AttributeInstances.Select(instance => instance.Type); - public List AttributeInstances { get; } = new List(); + public List AttributeInstances { get; } = new List(); public List MemberDependencies { get; } = new List(); public List MemberBackwardsDependencies { get; } = new List(); public List Dependencies => MemberDependencies.Cast().ToList(); diff --git a/ArchUnitNET/Domain/IMember.cs b/ArchUnitNET/Domain/IMember.cs index f497db8cc..b92b1fa12 100644 --- a/ArchUnitNET/Domain/IMember.cs +++ b/ArchUnitNET/Domain/IMember.cs @@ -14,5 +14,6 @@ public interface IMember : ICanBeAnalyzed IType DeclaringType { get; } List MemberDependencies { get; } List MemberBackwardsDependencies { get; } + bool? IsStatic { get; } } } \ No newline at end of file diff --git a/ArchUnitNET/Domain/MethodMember.cs b/ArchUnitNET/Domain/MethodMember.cs index be8c883d4..f23ffc8c0 100644 --- a/ArchUnitNET/Domain/MethodMember.cs +++ b/ArchUnitNET/Domain/MethodMember.cs @@ -16,7 +16,7 @@ public class MethodMember : IMember public MethodMember(string name, string fullName, IType declaringType, Visibility visibility, ITypeInstance returnType, bool isVirtual, MethodForm methodForm, bool isGeneric, bool isStub, - bool isCompilerGenerated, bool? isIterator) + bool isCompilerGenerated, bool? isIterator, bool? isStatic) { Name = name; FullName = fullName; @@ -29,6 +29,7 @@ public MethodMember(string name, string fullName, IType declaringType, Visibilit IsStub = isStub; IsCompilerGenerated = isCompilerGenerated; IsIterator = isIterator; + IsStatic = isStatic; } public bool IsVirtual { get; } @@ -41,10 +42,11 @@ public MethodMember(string name, string fullName, IType declaringType, Visibilit public bool IsCompilerGenerated { get; } public bool? IsIterator { get; } public bool IsGeneric { get; } + public bool? IsStatic { get; } public List GenericParameters { get; } = new List(); public Visibility Visibility { get; } public IEnumerable Attributes => AttributeInstances.Select(instance => instance.Type); - public List AttributeInstances { get; } = new List(); + public List AttributeInstances { get; } = new List(); public List MemberDependencies { get; } = new List(); public List MemberBackwardsDependencies { get; } = new List(); public List Dependencies => MemberDependencies.Cast().ToList(); diff --git a/ArchUnitNET/Domain/PropertyMember.cs b/ArchUnitNET/Domain/PropertyMember.cs index b4bee0b62..16a9c94a6 100644 --- a/ArchUnitNET/Domain/PropertyMember.cs +++ b/ArchUnitNET/Domain/PropertyMember.cs @@ -17,7 +17,7 @@ public class PropertyMember : IMember, ITypeInstance private readonly ITypeInstance _typeInstance; public PropertyMember(IType declaringType, string name, string fullName, ITypeInstance type, - bool isCompilerGenerated) + bool isCompilerGenerated, bool? isStatic) { Name = name; FullName = fullName; @@ -25,6 +25,7 @@ public PropertyMember(IType declaringType, string name, string fullName, ITypeIn DeclaringType = declaringType; IsCompilerGenerated = isCompilerGenerated; PropertyTypeDependency = new PropertyTypeDependency(this); + IsStatic = isStatic; } public bool IsVirtual { get; internal set; } @@ -42,6 +43,7 @@ public PropertyMember(IType declaringType, string name, string fullName, ITypeIn public bool IsCompilerGenerated { get; } public bool IsGeneric => false; + public bool? IsStatic { get; } public List GenericParameters => new List(); public Visibility Visibility => GetterVisibility < SetterVisibility ? GetterVisibility : SetterVisibility; @@ -49,7 +51,7 @@ public PropertyMember(IType declaringType, string name, string fullName, ITypeIn public string FullName { get; } public IType DeclaringType { get; } public IEnumerable Attributes => AttributeInstances.Select(instance => instance.Type); - public List AttributeInstances { get; } = new List(); + public List AttributeInstances { get; } = new List(); public List MemberDependencies { diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Members/GivenMembersThat.cs b/ArchUnitNET/Fluent/Syntax/Elements/Members/GivenMembersThat.cs index 4a023943b..cd2b07e05 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Members/GivenMembersThat.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Members/GivenMembersThat.cs @@ -65,6 +65,12 @@ public TGivenRuleTypeConjunction AreDeclaredIn(IEnumerable types) return Create(_ruleCreator); } + public TGivenRuleTypeConjunction AreStatic() + { + _ruleCreator.AddPredicate(MemberPredicatesDefinition.AreStatic()); + return Create(_ruleCreator); + } + //Negations @@ -112,5 +118,11 @@ public TGivenRuleTypeConjunction AreNotDeclaredIn(IEnumerable types) _ruleCreator.AddPredicate(MemberPredicatesDefinition.AreNotDeclaredIn(types)); return Create(_ruleCreator); } + + public TGivenRuleTypeConjunction AreNotStatic() + { + _ruleCreator.AddPredicate(MemberPredicatesDefinition.AreNotStatic()); + return Create(_ruleCreator); + } } } \ No newline at end of file diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Members/IMemberConditions.cs b/ArchUnitNET/Fluent/Syntax/Elements/Members/IMemberConditions.cs index d1d88df6c..53dc6012f 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Members/IMemberConditions.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Members/IMemberConditions.cs @@ -20,6 +20,7 @@ public interface IMemberConditions : IObjectCond TReturnType BeDeclaredIn(IObjectProvider types); TReturnType BeDeclaredIn(IEnumerable types); TReturnType BeDeclaredIn(IEnumerable types); + TReturnType BeStatic(); //Negations @@ -31,5 +32,6 @@ public interface IMemberConditions : IObjectCond TReturnType NotBeDeclaredIn(IObjectProvider types); TReturnType NotBeDeclaredIn(IEnumerable types); TReturnType NotBeDeclaredIn(IEnumerable types); + TReturnType NotBeStatic(); } } \ No newline at end of file diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Members/IMemberPredicates.cs b/ArchUnitNET/Fluent/Syntax/Elements/Members/IMemberPredicates.cs index 9e800387a..727ed4810 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Members/IMemberPredicates.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Members/IMemberPredicates.cs @@ -21,6 +21,7 @@ public interface TRuleTypeConjunction AreDeclaredIn(IObjectProvider types); TRuleTypeConjunction AreDeclaredIn(IEnumerable types); TRuleTypeConjunction AreDeclaredIn(IEnumerable types); + TRuleTypeConjunction AreStatic(); //Negations @@ -32,5 +33,6 @@ public interface TRuleTypeConjunction AreNotDeclaredIn(IObjectProvider types); TRuleTypeConjunction AreNotDeclaredIn(IEnumerable types); TRuleTypeConjunction AreNotDeclaredIn(IEnumerable types); + TRuleTypeConjunction AreNotStatic(); } } \ No newline at end of file diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Members/MemberConditionsDefinition.cs b/ArchUnitNET/Fluent/Syntax/Elements/Members/MemberConditionsDefinition.cs index 93b82c1a2..0372afe7c 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Members/MemberConditionsDefinition.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Members/MemberConditionsDefinition.cs @@ -161,6 +161,12 @@ IEnumerable Condition(IEnumerable methods, Architect return new ArchitectureCondition(Condition, description); } + public static ICondition BeStatic() + { + return new SimpleCondition(member => !member.IsStatic.HasValue || member.IsStatic.Value, + "be static", "is not static"); + } + //Relation Conditions @@ -321,6 +327,12 @@ IEnumerable Condition(IEnumerable methods, Architect return new ArchitectureCondition(Condition, description); } + public static ICondition NotBeStatic() + { + return new SimpleCondition(member => !member.IsStatic.HasValue || !member.IsStatic.Value, + "not be static", "is static"); + } + //Relation Condition Negations diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Members/MemberPredicatesDefinition.cs b/ArchUnitNET/Fluent/Syntax/Elements/Members/MemberPredicatesDefinition.cs index 7162f194b..3aff90d6c 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Members/MemberPredicatesDefinition.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Members/MemberPredicatesDefinition.cs @@ -136,6 +136,11 @@ IEnumerable Condition(IEnumerable members, Architecture architecture) return new ArchitecturePredicate(Condition, description); } + public static IPredicate AreStatic() + { + return new SimplePredicate(member => member.IsStatic.HasValue && member.IsStatic.Value, "are static"); + } + //Negations @@ -260,5 +265,11 @@ IEnumerable Condition(IEnumerable members, Architecture architecture) return new ArchitecturePredicate(Condition, description); } + + public static IPredicate AreNotStatic() + { + return new SimplePredicate(member => member.IsStatic.HasValue && !member.IsStatic.Value, + "are not static"); + } } } \ No newline at end of file diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Members/MembersShould.cs b/ArchUnitNET/Fluent/Syntax/Elements/Members/MembersShould.cs index 6ac80228c..5ffef8d15 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Members/MembersShould.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Members/MembersShould.cs @@ -67,6 +67,12 @@ public TRuleTypeShouldConjunction BeDeclaredIn(IEnumerable types) return Create(_ruleCreator); } + public TRuleTypeShouldConjunction BeStatic() + { + _ruleCreator.AddCondition(MemberConditionsDefinition.BeStatic()); + return Create(_ruleCreator); + } + //Relation Conditions public ShouldRelateToTypesThat BeDeclaredInTypesThat() @@ -126,6 +132,12 @@ public TRuleTypeShouldConjunction NotBeDeclaredIn(IEnumerable types) return Create(_ruleCreator); } + public TRuleTypeShouldConjunction NotBeStatic() + { + _ruleCreator.AddCondition(MemberConditionsDefinition.NotBeStatic()); + return Create(_ruleCreator); + } + //Relation Condition Negations public ShouldRelateToTypesThat NotBeDeclaredInTypesThat() diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Members/ShouldRelateToMembersThat.cs b/ArchUnitNET/Fluent/Syntax/Elements/Members/ShouldRelateToMembersThat.cs index 496362989..b0265a81f 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Members/ShouldRelateToMembersThat.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Members/ShouldRelateToMembersThat.cs @@ -69,6 +69,12 @@ public TRuleTypeShouldConjunction AreDeclaredIn(IEnumerable types) return Create(_ruleCreator); } + public TRuleTypeShouldConjunction AreStatic() + { + _ruleCreator.ContinueComplexCondition(MemberPredicatesDefinition.AreStatic()); + return Create(_ruleCreator); + } + //Negations @@ -119,5 +125,11 @@ public TRuleTypeShouldConjunction AreNotDeclaredIn(IEnumerable types) _ruleCreator.ContinueComplexCondition(MemberPredicatesDefinition.AreNotDeclaredIn(types)); return Create(_ruleCreator); } + + public TRuleTypeShouldConjunction AreNotStatic() + { + _ruleCreator.ContinueComplexCondition(MemberPredicatesDefinition.AreNotStatic()); + return Create(_ruleCreator); + } } } \ No newline at end of file diff --git a/ArchUnitNET/Loader/LoadTasks/AddMembers.cs b/ArchUnitNET/Loader/LoadTasks/AddMembers.cs index d8293f1ba..a0701a4ec 100644 --- a/ArchUnitNET/Loader/LoadTasks/AddMembers.cs +++ b/ArchUnitNET/Loader/LoadTasks/AddMembers.cs @@ -54,7 +54,7 @@ private IMember CreateFieldMember([NotNull] FieldDefinition fieldDefinition) var isCompilerGenerated = fieldDefinition.IsCompilerGenerated(); return new FieldMember(_type, fieldDefinition.Name, fieldDefinition.FullName, visibility, fieldType, - isCompilerGenerated); + isCompilerGenerated, fieldDefinition.IsStatic); } [NotNull] @@ -63,9 +63,11 @@ private IMember CreatePropertyMember(PropertyDefinition propertyDefinition) var typeReference = propertyDefinition.PropertyType; var propertyType = _typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(typeReference); var isCompilerGenerated = propertyDefinition.IsCompilerGenerated(); + var isStatic = (propertyDefinition.SetMethod != null && propertyDefinition.SetMethod.IsStatic) || + (propertyDefinition.GetMethod != null && propertyDefinition.GetMethod.IsStatic); return new PropertyMember(_type, propertyDefinition.Name, propertyDefinition.FullName, propertyType, - isCompilerGenerated); + isCompilerGenerated, isStatic); } private static Visibility GetVisibilityFromFieldDefinition([NotNull] FieldDefinition fieldDefinition) diff --git a/ArchUnitNET/Loader/TypeFactory.cs b/ArchUnitNET/Loader/TypeFactory.cs index 0de38385f..35509018f 100644 --- a/ArchUnitNET/Loader/TypeFactory.cs +++ b/ArchUnitNET/Loader/TypeFactory.cs @@ -168,7 +168,7 @@ private ITypeInstance CreateTypeFromTypeReference(TypeReference typeRefer var arrayType = typeDefinition.Fields.First(field => field.Name == "FixedElementField").FieldType; var arrayTypeInstance = GetOrCreateStubTypeInstanceFromTypeReference(arrayType); var dimensions = new List {1}; - + switch (arrayTypeInstance.Type) { case Interface intf: @@ -256,6 +256,7 @@ private MethodMemberInstance CreateMethodMemberFromMethodReference( Visibility visibility; bool isStub; bool? isIterator; + bool? isStatic; MethodDefinition methodDefinition; try @@ -272,6 +273,7 @@ private MethodMemberInstance CreateMethodMemberFromMethodReference( visibility = Public; methodForm = methodReference.HasConstructorName() ? MethodForm.Constructor : MethodForm.Normal; isIterator = null; + isStatic = null; isStub = true; } else @@ -279,11 +281,12 @@ private MethodMemberInstance CreateMethodMemberFromMethodReference( visibility = methodDefinition.GetVisibility(); methodForm = methodDefinition.GetMethodForm(); isIterator = methodDefinition.IsIterator(); + isStatic = methodDefinition.IsStatic; isStub = false; } var methodMember = new MethodMember(name, fullName, typeInstance.Type, visibility, returnType, - false, methodForm, isGeneric, isStub, isCompilerGenerated, isIterator); + false, methodForm, isGeneric, isStub, isCompilerGenerated, isIterator, isStatic); var parameters = methodReference.GetParameters(this).ToList(); methodMember.ParameterInstances.AddRange(parameters); @@ -302,9 +305,14 @@ internal FieldMember CreateStubFieldMemberFromFieldReference([NotNull] IType typ var typeReference = fieldReference.FieldType; var fieldType = GetOrCreateStubTypeInstanceFromTypeReference(typeReference); var isCompilerGenerated = fieldReference.IsCompilerGenerated(); + bool? isStatic = null; + if (fieldReference is FieldDefinition fieldDefinition) + { + isStatic = fieldDefinition.IsStatic; + } return new FieldMember(type, fieldReference.Name, fieldReference.FullName, Public, fieldType, - isCompilerGenerated); + isCompilerGenerated, isStatic); } public IEnumerable GetGenericParameters(IGenericParameterProvider genericParameterProvider) diff --git a/ArchUnitNETTests/Fluent/Extensions/BuildMocksExtensions.cs b/ArchUnitNETTests/Fluent/Extensions/BuildMocksExtensions.cs index b4a8d10f5..0cd14ec19 100644 --- a/ArchUnitNETTests/Fluent/Extensions/BuildMocksExtensions.cs +++ b/ArchUnitNETTests/Fluent/Extensions/BuildMocksExtensions.cs @@ -10,7 +10,6 @@ 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; @@ -120,6 +119,7 @@ public static MethodMember CreateStubMethodMember(this MethodBase methodBase) var declaringType = methodBase.DeclaringType.CreateStubClass(); var parameters = methodBase.CreateStubParameters().Select(parameter => new TypeInstance(parameter)); var methodForm = methodBase.GetStubMethodForm(); + var isStatic = methodBase.IsStatic; var isGeneric = methodBase.IsGenericMethod; @@ -141,7 +141,7 @@ public static MethodMember CreateStubMethodMember(this MethodBase methodBase) } var methodMember = new MethodMember(methodBase.BuildMockMethodName(), fullName, declaringType, visibility, - returnTypeInstance, methodBase.IsVirtual, methodForm, isGeneric, false, false,false); + returnTypeInstance, methodBase.IsVirtual, methodForm, isGeneric, false, false, false, isStatic); methodMember.ParameterInstances.AddRange(parameters); return methodMember; diff --git a/ArchUnitNETTests/Fluent/Syntax/Elements/MemberSyntaxElementsTests.cs b/ArchUnitNETTests/Fluent/Syntax/Elements/MemberSyntaxElementsTests.cs index a742851f3..5ad3013fc 100644 --- a/ArchUnitNETTests/Fluent/Syntax/Elements/MemberSyntaxElementsTests.cs +++ b/ArchUnitNETTests/Fluent/Syntax/Elements/MemberSyntaxElementsTests.cs @@ -160,5 +160,19 @@ public void DeclaredInTest() Assert.True(allMembersAreNotDeclaredInEmptyTypeMultiple2.HasNoViolations(Architecture)); Assert.True(allMembersAreNotDeclaredInEmptyTypeMultiplePattern.HasNoViolations(Architecture)); } + + [Fact] + public void IsStaticTest() + { + var correctIsStatic = Members().That().AreStatic().Should().BeStatic(); + var correctIsStatic2 = Members().That().AreNotStatic().Should().NotBeStatic(); + var wrongStatic = Members().That().AreStatic().Should().NotBeStatic(); + var wrongStatic2 = Members().That().AreNotStatic().Should().BeStatic(); + + Assert.True(correctIsStatic.HasNoViolations(Architecture)); + Assert.True(correctIsStatic2.HasNoViolations(Architecture)); + Assert.False(wrongStatic.HasNoViolations(Architecture)); + Assert.False(wrongStatic2.HasNoViolations(Architecture)); + } } } \ No newline at end of file