From 07c0245667fe2693a2d2d6f574a1a01abf89e974 Mon Sep 17 00:00:00 2001 From: Alexander Linne Date: Mon, 2 Mar 2026 18:01:11 +0100 Subject: [PATCH 1/3] Correct behavior of ImplementAny fluent syntax for empty arguments Signed-off-by: Alexander Linne --- .../Types/TypeConditionsDefinition.cs | 16 +- .../Types/TypePredicatesDefinition.cs | 12 +- ...ts.ImplementAnyInterfacesTest.verified.txt | 192 +++++++----------- ...NotImplementAnyInterfacesTest.verified.txt | 179 ++++------------ .../Elements/TypeSyntaxElementsTests.cs | 56 +---- 5 files changed, 128 insertions(+), 327 deletions(-) diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Types/TypeConditionsDefinition.cs b/ArchUnitNET/Fluent/Syntax/Elements/Types/TypeConditionsDefinition.cs index bb1d5d595..2a49913d7 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Types/TypeConditionsDefinition.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Types/TypeConditionsDefinition.cs @@ -571,8 +571,7 @@ Architecture architecture { if ( interfaceList.Count > 0 - ? ruleType.ImplementedInterfaces.Intersect(interfaceList).Any() - : ruleType.ImplementedInterfaces.Any() + && ruleType.ImplementedInterfaces.Intersect(interfaceList).Any() ) { yield return new ConditionResult(ruleType, true); @@ -592,7 +591,7 @@ Architecture architecture } var description = interfaces.FormatDescription( - "implement any interface", + "implement any of no interfaces (always false)", "implement", "implement any" ); @@ -1211,11 +1210,10 @@ Architecture architecture var interfaceList = interfaces.GetObjects(architecture).ToList(); foreach (var ruleType in ruleTypes.ToList()) { - var matchingInterfaces = - interfaceList.Count > 0 - ? ruleType.ImplementedInterfaces.Intersect(interfaceList).ToList() - : ruleType.ImplementedInterfaces.ToList(); - if (matchingInterfaces.Any()) + var matchingInterfaces = ruleType.ImplementedInterfaces.Intersect( + interfaceList + ); + if (interfaceList.Count > 0 && matchingInterfaces.Any()) { yield return new ConditionResult( ruleType, @@ -1232,7 +1230,7 @@ Architecture architecture } var description = interfaces.FormatDescription( - "not implement any interface", + "not implement any of no interfaces (always true)", "not implement", "not implement any" ); diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Types/TypePredicatesDefinition.cs b/ArchUnitNET/Fluent/Syntax/Elements/Types/TypePredicatesDefinition.cs index 3244b7f6d..2031e1a62 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Types/TypePredicatesDefinition.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Types/TypePredicatesDefinition.cs @@ -348,12 +348,11 @@ IEnumerable Condition(IEnumerable ruleTypes, Architecture architecture) var interfaceList = interfaces.GetObjects(architecture).ToList(); return ruleTypes.Where(type => interfaceList.Count > 0 - ? type.ImplementedInterfaces.Intersect(interfaceList).Any() - : type.ImplementedInterfaces.Any() + && type.ImplementedInterfaces.Intersect(interfaceList).Any() ); } var description = interfaces.FormatDescription( - "implement any interface", + "implement any of no interfaces (always false)", "implement", "implement any" ); @@ -695,13 +694,12 @@ IEnumerable Condition(IEnumerable ruleTypes, Architecture architecture) { var interfaceList = interfaces.GetObjects(architecture).ToList(); return ruleTypes.Where(type => - interfaceList.Count > 0 - ? !type.ImplementedInterfaces.Intersect(interfaceList).Any() - : !type.ImplementedInterfaces.Any() + interfaceList.Count <= 0 + || !type.ImplementedInterfaces.Intersect(interfaceList).Any() ); } var description = interfaces.FormatDescription( - "do not implement any interface", + "do not implement any of no interfaces (always true)", "do not implement", "do not implement any" ); diff --git a/ArchUnitNETTests/Fluent/Syntax/Elements/Snapshots/TypeSyntaxElementsTests.ImplementAnyInterfacesTest.verified.txt b/ArchUnitNETTests/Fluent/Syntax/Elements/Snapshots/TypeSyntaxElementsTests.ImplementAnyInterfacesTest.verified.txt index e10ffdf37..bb85ad741 100644 --- a/ArchUnitNETTests/Fluent/Syntax/Elements/Snapshots/TypeSyntaxElementsTests.ImplementAnyInterfacesTest.verified.txt +++ b/ArchUnitNETTests/Fluent/Syntax/Elements/Snapshots/TypeSyntaxElementsTests.ImplementAnyInterfacesTest.verified.txt @@ -258,203 +258,157 @@ Message: -===== Empty Arguments (No Violations) ===== +===== Empty Arguments (Only Violations) ===== ----- Conditions ----- -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should implement any interface -Result: True -Description: InterfaceAssembly.IChildInterface passed -Message: -All Evaluations passed - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should implement any interface -Result: True -Description: InterfaceAssembly.IChildInterface passed -Message: -All Evaluations passed - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should implement any interface -Result: True -Description: InterfaceAssembly.IChildInterface passed -Message: -All Evaluations passed - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should implement any Interfaces that have name "NotTheNameOfAnyObject" -Result: True -Description: InterfaceAssembly.IChildInterface passed -Message: -All Evaluations passed - ------ Predicates ----- - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be Interfaces that implement any interface -Result: True -Description: InterfaceAssembly.IChildInterface passed -Message: -All Evaluations passed - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be Interfaces that implement any interface -Result: True -Description: InterfaceAssembly.IChildInterface passed -Message: -All Evaluations passed - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be Interfaces that implement any interface -Result: True -Description: InterfaceAssembly.IChildInterface passed -Message: -All Evaluations passed - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be Interfaces that implement any Interfaces that have name "NotTheNameOfAnyObject" -Result: True -Description: InterfaceAssembly.IChildInterface passed -Message: -All Evaluations passed - ------ Predicates as conditions ----- - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be types that implement any interface -Result: True -Description: InterfaceAssembly.IChildInterface passed -Message: -All Evaluations passed - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be types that implement any interface -Result: True -Description: InterfaceAssembly.IChildInterface passed -Message: -All Evaluations passed - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be types that implement any Interfaces that have name "NotTheNameOfAnyObject" -Result: True -Description: InterfaceAssembly.IChildInterface passed -Message: -All Evaluations passed - ------ Complex conditions ----- - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should implement any interfaces that are any of no objects (always empty) -Result: True -Description: InterfaceAssembly.IChildInterface passed -Message: -All Evaluations passed - -===== Empty Arguments (Violations) ===== - ------ Conditions ----- - -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should implement any of no interfaces (always false) Result: False Description: InterfaceAssembly.IBaseInterface does not implement any interface +Result: False +Description: InterfaceAssembly.IChildInterface only implements InterfaceAssembly.IBaseInterface Message: -"Interfaces that are "InterfaceAssembly.IBaseInterface" should implement any interface" failed: +"Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should implement any of no interfaces (always false)" failed: InterfaceAssembly.IBaseInterface does not implement any interface + InterfaceAssembly.IChildInterface only implements InterfaceAssembly.IBaseInterface -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should implement any of no interfaces (always false) Result: False Description: InterfaceAssembly.IBaseInterface does not implement any interface +Result: False +Description: InterfaceAssembly.IChildInterface only implements InterfaceAssembly.IBaseInterface Message: -"Interfaces that are "InterfaceAssembly.IBaseInterface" should implement any interface" failed: +"Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should implement any of no interfaces (always false)" failed: InterfaceAssembly.IBaseInterface does not implement any interface + InterfaceAssembly.IChildInterface only implements InterfaceAssembly.IBaseInterface -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should implement any of no interfaces (always false) Result: False Description: InterfaceAssembly.IBaseInterface does not implement any interface +Result: False +Description: InterfaceAssembly.IChildInterface only implements InterfaceAssembly.IBaseInterface Message: -"Interfaces that are "InterfaceAssembly.IBaseInterface" should implement any interface" failed: +"Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should implement any of no interfaces (always false)" failed: InterfaceAssembly.IBaseInterface does not implement any interface + InterfaceAssembly.IChildInterface only implements InterfaceAssembly.IBaseInterface -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should implement any Interfaces that have name "NotTheNameOfAnyObject" +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should implement any Interfaces that have name "NotTheNameOfAnyObject" Result: False Description: InterfaceAssembly.IBaseInterface does not implement any interface +Result: False +Description: InterfaceAssembly.IChildInterface only implements InterfaceAssembly.IBaseInterface Message: -"Interfaces that are "InterfaceAssembly.IBaseInterface" should implement any Interfaces that have name "NotTheNameOfAnyObject"" failed: +"Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should implement any Interfaces that have name "NotTheNameOfAnyObject"" failed: InterfaceAssembly.IBaseInterface does not implement any interface + InterfaceAssembly.IChildInterface only implements InterfaceAssembly.IBaseInterface ----- Predicates ----- -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be Interfaces that implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be Interfaces that implement any of no interfaces (always false) +Result: False +Description: InterfaceAssembly.IBaseInterface is not Interfaces that implement any of no interfaces (always false) Result: False -Description: InterfaceAssembly.IBaseInterface is not Interfaces that implement any interface +Description: InterfaceAssembly.IChildInterface is not Interfaces that implement any of no interfaces (always false) Message: -"Interfaces that are "InterfaceAssembly.IBaseInterface" should be Interfaces that implement any interface" failed: - InterfaceAssembly.IBaseInterface is not Interfaces that implement any interface +"Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be Interfaces that implement any of no interfaces (always false)" failed: + InterfaceAssembly.IBaseInterface is not Interfaces that implement any of no interfaces (always false) + InterfaceAssembly.IChildInterface is not Interfaces that implement any of no interfaces (always false) -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be Interfaces that implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be Interfaces that implement any of no interfaces (always false) +Result: False +Description: InterfaceAssembly.IBaseInterface is not Interfaces that implement any of no interfaces (always false) Result: False -Description: InterfaceAssembly.IBaseInterface is not Interfaces that implement any interface +Description: InterfaceAssembly.IChildInterface is not Interfaces that implement any of no interfaces (always false) Message: -"Interfaces that are "InterfaceAssembly.IBaseInterface" should be Interfaces that implement any interface" failed: - InterfaceAssembly.IBaseInterface is not Interfaces that implement any interface +"Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be Interfaces that implement any of no interfaces (always false)" failed: + InterfaceAssembly.IBaseInterface is not Interfaces that implement any of no interfaces (always false) + InterfaceAssembly.IChildInterface is not Interfaces that implement any of no interfaces (always false) -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be Interfaces that implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be Interfaces that implement any of no interfaces (always false) Result: False -Description: InterfaceAssembly.IBaseInterface is not Interfaces that implement any interface +Description: InterfaceAssembly.IBaseInterface is not Interfaces that implement any of no interfaces (always false) +Result: False +Description: InterfaceAssembly.IChildInterface is not Interfaces that implement any of no interfaces (always false) Message: -"Interfaces that are "InterfaceAssembly.IBaseInterface" should be Interfaces that implement any interface" failed: - InterfaceAssembly.IBaseInterface is not Interfaces that implement any interface +"Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be Interfaces that implement any of no interfaces (always false)" failed: + InterfaceAssembly.IBaseInterface is not Interfaces that implement any of no interfaces (always false) + InterfaceAssembly.IChildInterface is not Interfaces that implement any of no interfaces (always false) -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be Interfaces that implement any Interfaces that have name "NotTheNameOfAnyObject" +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be Interfaces that implement any Interfaces that have name "NotTheNameOfAnyObject" Result: False Description: InterfaceAssembly.IBaseInterface is not Interfaces that implement any Interfaces that have name "NotTheNameOfAnyObject" +Result: False +Description: InterfaceAssembly.IChildInterface is not Interfaces that implement any Interfaces that have name "NotTheNameOfAnyObject" Message: -"Interfaces that are "InterfaceAssembly.IBaseInterface" should be Interfaces that implement any Interfaces that have name "NotTheNameOfAnyObject"" failed: +"Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be Interfaces that implement any Interfaces that have name "NotTheNameOfAnyObject"" failed: InterfaceAssembly.IBaseInterface is not Interfaces that implement any Interfaces that have name "NotTheNameOfAnyObject" + InterfaceAssembly.IChildInterface is not Interfaces that implement any Interfaces that have name "NotTheNameOfAnyObject" ----- Predicates as conditions ----- -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be types that implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be types that implement any of no interfaces (always false) +Result: False +Description: InterfaceAssembly.IBaseInterface does exist Result: False -Description: InterfaceAssembly.IBaseInterface is not "InterfaceAssembly.IChildInterface" or "InterfaceAssembly.IOtherChildInterface" or "InterfaceAssembly.IInterfaceWithMultipleDependencies" +Description: InterfaceAssembly.IChildInterface does exist Message: -"Interfaces that are "InterfaceAssembly.IBaseInterface" should be types that implement any interface" failed: - InterfaceAssembly.IBaseInterface is not "InterfaceAssembly.IChildInterface" or "InterfaceAssembly.IOtherChildInterface" or "InterfaceAssembly.IInterfaceWithMultipleDependencies" +"Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be types that implement any of no interfaces (always false)" failed: + InterfaceAssembly.IBaseInterface does exist + InterfaceAssembly.IChildInterface does exist -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be types that implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be types that implement any of no interfaces (always false) Result: False -Description: InterfaceAssembly.IBaseInterface is not "InterfaceAssembly.IChildInterface" or "InterfaceAssembly.IOtherChildInterface" or "InterfaceAssembly.IInterfaceWithMultipleDependencies" +Description: InterfaceAssembly.IBaseInterface does exist +Result: False +Description: InterfaceAssembly.IChildInterface does exist Message: -"Interfaces that are "InterfaceAssembly.IBaseInterface" should be types that implement any interface" failed: - InterfaceAssembly.IBaseInterface is not "InterfaceAssembly.IChildInterface" or "InterfaceAssembly.IOtherChildInterface" or "InterfaceAssembly.IInterfaceWithMultipleDependencies" +"Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be types that implement any of no interfaces (always false)" failed: + InterfaceAssembly.IBaseInterface does exist + InterfaceAssembly.IChildInterface does exist -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be types that implement any Interfaces that have name "NotTheNameOfAnyObject" +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be types that implement any Interfaces that have name "NotTheNameOfAnyObject" +Result: False +Description: InterfaceAssembly.IBaseInterface does exist Result: False -Description: InterfaceAssembly.IBaseInterface is not "InterfaceAssembly.IChildInterface" or "InterfaceAssembly.IOtherChildInterface" or "InterfaceAssembly.IInterfaceWithMultipleDependencies" +Description: InterfaceAssembly.IChildInterface does exist Message: -"Interfaces that are "InterfaceAssembly.IBaseInterface" should be types that implement any Interfaces that have name "NotTheNameOfAnyObject"" failed: - InterfaceAssembly.IBaseInterface is not "InterfaceAssembly.IChildInterface" or "InterfaceAssembly.IOtherChildInterface" or "InterfaceAssembly.IInterfaceWithMultipleDependencies" +"Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be types that implement any Interfaces that have name "NotTheNameOfAnyObject"" failed: + InterfaceAssembly.IBaseInterface does exist + InterfaceAssembly.IChildInterface does exist ----- Complex conditions ----- -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should implement any interfaces that are any of no objects (always empty) +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should implement any interfaces that are any of no objects (always empty) Result: False Description: InterfaceAssembly.IBaseInterface does not implement any interface +Result: False +Description: InterfaceAssembly.IChildInterface only implements InterfaceAssembly.IBaseInterface Message: -"Interfaces that are "InterfaceAssembly.IBaseInterface" should implement any interfaces that are any of no objects (always empty)" failed: +"Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should implement any interfaces that are any of no objects (always empty)" failed: InterfaceAssembly.IBaseInterface does not implement any interface + InterfaceAssembly.IChildInterface only implements InterfaceAssembly.IBaseInterface diff --git a/ArchUnitNETTests/Fluent/Syntax/Elements/Snapshots/TypeSyntaxElementsTests.NotImplementAnyInterfacesTest.verified.txt b/ArchUnitNETTests/Fluent/Syntax/Elements/Snapshots/TypeSyntaxElementsTests.NotImplementAnyInterfacesTest.verified.txt index cd468f61a..63d706e82 100644 --- a/ArchUnitNETTests/Fluent/Syntax/Elements/Snapshots/TypeSyntaxElementsTests.NotImplementAnyInterfacesTest.verified.txt +++ b/ArchUnitNETTests/Fluent/Syntax/Elements/Snapshots/TypeSyntaxElementsTests.NotImplementAnyInterfacesTest.verified.txt @@ -262,217 +262,116 @@ Message: ----- Conditions ----- -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should not implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should not implement any of no interfaces (always true) Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should not implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should not implement any of no interfaces (always true) Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should not implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should not implement any of no interfaces (always true) Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should not implement any Interfaces that have name "NotTheNameOfAnyObject" +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should not implement any Interfaces that have name "NotTheNameOfAnyObject" Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed ----- Predicates ----- -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be Interfaces that do not implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be Interfaces that do not implement any of no interfaces (always true) Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be Interfaces that do not implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be Interfaces that do not implement any of no interfaces (always true) Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be Interfaces that do not implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be Interfaces that do not implement any of no interfaces (always true) Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be Interfaces that do not implement any Interfaces that have name "NotTheNameOfAnyObject" +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be Interfaces that do not implement any Interfaces that have name "NotTheNameOfAnyObject" Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed ----- Predicates as conditions ----- -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be types that do not implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be types that do not implement any of no interfaces (always true) Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be types that do not implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be types that do not implement any of no interfaces (always true) Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be types that do not implement any interface +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be types that do not implement any of no interfaces (always true) Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should be types that do not implement any Interfaces that have name "NotTheNameOfAnyObject" +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should be types that do not implement any Interfaces that have name "NotTheNameOfAnyObject" Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed ----- Complex conditions ----- -Query: Interfaces that are "InterfaceAssembly.IBaseInterface" should not implement any interfaces that are any of no objects (always empty) +Query: Interfaces that are "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IChildInterface" should not implement any interfaces that are any of no objects (always empty) Result: True Description: InterfaceAssembly.IBaseInterface passed +Result: True +Description: InterfaceAssembly.IChildInterface passed Message: All Evaluations passed -===== Empty Arguments (Violations) ===== - ------ Conditions ----- - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should not implement any interface -Result: False -Description: InterfaceAssembly.IChildInterface does implement InterfaceAssembly.IBaseInterface -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should not implement any interface" failed: - InterfaceAssembly.IChildInterface does implement InterfaceAssembly.IBaseInterface - - - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should not implement any interface -Result: False -Description: InterfaceAssembly.IChildInterface does implement InterfaceAssembly.IBaseInterface -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should not implement any interface" failed: - InterfaceAssembly.IChildInterface does implement InterfaceAssembly.IBaseInterface - - - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should not implement any interface -Result: False -Description: InterfaceAssembly.IChildInterface does implement InterfaceAssembly.IBaseInterface -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should not implement any interface" failed: - InterfaceAssembly.IChildInterface does implement InterfaceAssembly.IBaseInterface - - - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should not implement any Interfaces that have name "NotTheNameOfAnyObject" -Result: False -Description: InterfaceAssembly.IChildInterface does implement InterfaceAssembly.IBaseInterface -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should not implement any Interfaces that have name "NotTheNameOfAnyObject"" failed: - InterfaceAssembly.IChildInterface does implement InterfaceAssembly.IBaseInterface - - - ------ Predicates ----- - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be Interfaces that do not implement any interface -Result: False -Description: InterfaceAssembly.IChildInterface is not Interfaces that do not implement any interface -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should be Interfaces that do not implement any interface" failed: - InterfaceAssembly.IChildInterface is not Interfaces that do not implement any interface - - - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be Interfaces that do not implement any interface -Result: False -Description: InterfaceAssembly.IChildInterface is not Interfaces that do not implement any interface -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should be Interfaces that do not implement any interface" failed: - InterfaceAssembly.IChildInterface is not Interfaces that do not implement any interface - - - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be Interfaces that do not implement any interface -Result: False -Description: InterfaceAssembly.IChildInterface is not Interfaces that do not implement any interface -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should be Interfaces that do not implement any interface" failed: - InterfaceAssembly.IChildInterface is not Interfaces that do not implement any interface - - - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be Interfaces that do not implement any Interfaces that have name "NotTheNameOfAnyObject" -Result: False -Description: InterfaceAssembly.IChildInterface is not Interfaces that do not implement any Interfaces that have name "NotTheNameOfAnyObject" -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should be Interfaces that do not implement any Interfaces that have name "NotTheNameOfAnyObject"" failed: - InterfaceAssembly.IChildInterface is not Interfaces that do not implement any Interfaces that have name "NotTheNameOfAnyObject" - - - ------ Predicates as conditions ----- - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be types that do not implement any interface -Result: False -Description: InterfaceAssembly.IChildInterface is not "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IOtherBaseInterface" or "InterfaceAssembly.IInterfaceWithoutDependencies" or "System.Runtime.CompilerServices.CompilationRelaxationsAttribute" or "System.Int32" or "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute" or "System.Boolean" or "System.Diagnostics.DebuggableAttribute" or "System.Diagnostics.DebuggableAttribute+DebuggingModes" or "System.Runtime.Versioning.TargetFrameworkAttribute" or "System.String" or "System.Reflection.AssemblyCompanyAttribute" or "System.Reflection.AssemblyConfigurationAttribute" or "System.Reflection.AssemblyFileVersionAttribute" or "System.Reflection.AssemblyInformationalVersionAttribute" or "System.Reflection.AssemblyProductAttribute" or "System.Reflection.AssemblyTitleAttribute" -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should be types that do not implement any interface" failed: - InterfaceAssembly.IChildInterface is not "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IOtherBaseInterface" or "InterfaceAssembly.IInterfaceWithoutDependencies" or "System.Runtime.CompilerServices.CompilationRelaxationsAttribute" or "System.Int32" or "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute" or "System.Boolean" or "System.Diagnostics.DebuggableAttribute" or "System.Diagnostics.DebuggableAttribute+DebuggingModes" or "System.Runtime.Versioning.TargetFrameworkAttribute" or "System.String" or "System.Reflection.AssemblyCompanyAttribute" or "System.Reflection.AssemblyConfigurationAttribute" or "System.Reflection.AssemblyFileVersionAttribute" or "System.Reflection.AssemblyInformationalVersionAttribute" or "System.Reflection.AssemblyProductAttribute" or "System.Reflection.AssemblyTitleAttribute" - - - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be types that do not implement any interface -Result: False -Description: InterfaceAssembly.IChildInterface is not "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IOtherBaseInterface" or "InterfaceAssembly.IInterfaceWithoutDependencies" or "System.Runtime.CompilerServices.CompilationRelaxationsAttribute" or "System.Int32" or "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute" or "System.Boolean" or "System.Diagnostics.DebuggableAttribute" or "System.Diagnostics.DebuggableAttribute+DebuggingModes" or "System.Runtime.Versioning.TargetFrameworkAttribute" or "System.String" or "System.Reflection.AssemblyCompanyAttribute" or "System.Reflection.AssemblyConfigurationAttribute" or "System.Reflection.AssemblyFileVersionAttribute" or "System.Reflection.AssemblyInformationalVersionAttribute" or "System.Reflection.AssemblyProductAttribute" or "System.Reflection.AssemblyTitleAttribute" -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should be types that do not implement any interface" failed: - InterfaceAssembly.IChildInterface is not "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IOtherBaseInterface" or "InterfaceAssembly.IInterfaceWithoutDependencies" or "System.Runtime.CompilerServices.CompilationRelaxationsAttribute" or "System.Int32" or "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute" or "System.Boolean" or "System.Diagnostics.DebuggableAttribute" or "System.Diagnostics.DebuggableAttribute+DebuggingModes" or "System.Runtime.Versioning.TargetFrameworkAttribute" or "System.String" or "System.Reflection.AssemblyCompanyAttribute" or "System.Reflection.AssemblyConfigurationAttribute" or "System.Reflection.AssemblyFileVersionAttribute" or "System.Reflection.AssemblyInformationalVersionAttribute" or "System.Reflection.AssemblyProductAttribute" or "System.Reflection.AssemblyTitleAttribute" - - - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be types that do not implement any interface -Result: False -Description: InterfaceAssembly.IChildInterface is not "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IOtherBaseInterface" or "InterfaceAssembly.IInterfaceWithoutDependencies" or "System.Runtime.CompilerServices.CompilationRelaxationsAttribute" or "System.Int32" or "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute" or "System.Boolean" or "System.Diagnostics.DebuggableAttribute" or "System.Diagnostics.DebuggableAttribute+DebuggingModes" or "System.Runtime.Versioning.TargetFrameworkAttribute" or "System.String" or "System.Reflection.AssemblyCompanyAttribute" or "System.Reflection.AssemblyConfigurationAttribute" or "System.Reflection.AssemblyFileVersionAttribute" or "System.Reflection.AssemblyInformationalVersionAttribute" or "System.Reflection.AssemblyProductAttribute" or "System.Reflection.AssemblyTitleAttribute" -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should be types that do not implement any interface" failed: - InterfaceAssembly.IChildInterface is not "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IOtherBaseInterface" or "InterfaceAssembly.IInterfaceWithoutDependencies" or "System.Runtime.CompilerServices.CompilationRelaxationsAttribute" or "System.Int32" or "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute" or "System.Boolean" or "System.Diagnostics.DebuggableAttribute" or "System.Diagnostics.DebuggableAttribute+DebuggingModes" or "System.Runtime.Versioning.TargetFrameworkAttribute" or "System.String" or "System.Reflection.AssemblyCompanyAttribute" or "System.Reflection.AssemblyConfigurationAttribute" or "System.Reflection.AssemblyFileVersionAttribute" or "System.Reflection.AssemblyInformationalVersionAttribute" or "System.Reflection.AssemblyProductAttribute" or "System.Reflection.AssemblyTitleAttribute" - - - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should be types that do not implement any Interfaces that have name "NotTheNameOfAnyObject" -Result: False -Description: InterfaceAssembly.IChildInterface is not "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IOtherBaseInterface" or "InterfaceAssembly.IInterfaceWithoutDependencies" or "System.Runtime.CompilerServices.CompilationRelaxationsAttribute" or "System.Int32" or "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute" or "System.Boolean" or "System.Diagnostics.DebuggableAttribute" or "System.Diagnostics.DebuggableAttribute+DebuggingModes" or "System.Runtime.Versioning.TargetFrameworkAttribute" or "System.String" or "System.Reflection.AssemblyCompanyAttribute" or "System.Reflection.AssemblyConfigurationAttribute" or "System.Reflection.AssemblyFileVersionAttribute" or "System.Reflection.AssemblyInformationalVersionAttribute" or "System.Reflection.AssemblyProductAttribute" or "System.Reflection.AssemblyTitleAttribute" -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should be types that do not implement any Interfaces that have name "NotTheNameOfAnyObject"" failed: - InterfaceAssembly.IChildInterface is not "InterfaceAssembly.IBaseInterface" or "InterfaceAssembly.IOtherBaseInterface" or "InterfaceAssembly.IInterfaceWithoutDependencies" or "System.Runtime.CompilerServices.CompilationRelaxationsAttribute" or "System.Int32" or "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute" or "System.Boolean" or "System.Diagnostics.DebuggableAttribute" or "System.Diagnostics.DebuggableAttribute+DebuggingModes" or "System.Runtime.Versioning.TargetFrameworkAttribute" or "System.String" or "System.Reflection.AssemblyCompanyAttribute" or "System.Reflection.AssemblyConfigurationAttribute" or "System.Reflection.AssemblyFileVersionAttribute" or "System.Reflection.AssemblyInformationalVersionAttribute" or "System.Reflection.AssemblyProductAttribute" or "System.Reflection.AssemblyTitleAttribute" - - - ------ Complex conditions ----- - -Query: Interfaces that are "InterfaceAssembly.IChildInterface" should not implement any interfaces that are any of no objects (always empty) -Result: False -Description: InterfaceAssembly.IChildInterface does implement InterfaceAssembly.IBaseInterface -Message: -"Interfaces that are "InterfaceAssembly.IChildInterface" should not implement any interfaces that are any of no objects (always empty)" failed: - InterfaceAssembly.IChildInterface does implement InterfaceAssembly.IBaseInterface - - - ===== Multiple arguments ===== ----- Conditions ----- diff --git a/ArchUnitNETTests/Fluent/Syntax/Elements/TypeSyntaxElementsTests.cs b/ArchUnitNETTests/Fluent/Syntax/Elements/TypeSyntaxElementsTests.cs index b941bba42..fbc3499b6 100644 --- a/ArchUnitNETTests/Fluent/Syntax/Elements/TypeSyntaxElementsTests.cs +++ b/ArchUnitNETTests/Fluent/Syntax/Elements/TypeSyntaxElementsTests.cs @@ -720,39 +720,15 @@ public async Task ImplementAnyInterfacesTest() helper.AddSnapshotSubHeader("Complex conditions"); should.ImplementAnyInterfacesThat().Are(helper.OtherBaseInterface).AssertOnlyViolations(helper); - helper.AddSnapshotHeader("Empty Arguments (No Violations)"); - should = Interfaces().That().Are(helper.ChildInterface).Should(); - - helper.AddSnapshotSubHeader("Conditions"); - should.ImplementAnyInterfaces().AssertNoViolations(helper); - should.ImplementAnyInterfaces(new List()).AssertNoViolations(helper); - should.ImplementAnyInterfaces(new List()).AssertNoViolations(helper); - should.ImplementAnyInterfaces(Interfaces().That().HaveName(helper.NonExistentObjectName)).AssertNoViolations(helper); - - helper.AddSnapshotSubHeader("Predicates"); - should.Be(Interfaces().That().ImplementAnyInterfaces()).AssertNoViolations(helper); - should.Be(Interfaces().That().ImplementAnyInterfaces(new List())).AssertNoViolations(helper); - should.Be(Interfaces().That().ImplementAnyInterfaces(new List())).AssertNoViolations(helper); - should.Be(Interfaces().That().ImplementAnyInterfaces(Interfaces().That().HaveName(helper.NonExistentObjectName))).AssertNoViolations(helper); - - helper.AddSnapshotSubHeader("Predicates as conditions"); - should.BeTypesThat().ImplementAnyInterfaces(); - should.BeTypesThat().ImplementAnyInterfaces(new List()).AssertNoViolations(helper); - should.BeTypesThat().ImplementAnyInterfaces(new List()).AssertNoViolations(helper); - should.BeTypesThat().ImplementAnyInterfaces(Interfaces().That().HaveName(helper.NonExistentObjectName)).AssertNoViolations(helper); - - helper.AddSnapshotSubHeader("Complex conditions"); - should.ImplementAnyInterfacesThat().Are(new List()).AssertNoViolations(helper); - - helper.AddSnapshotHeader("Empty Arguments (Violations)"); - should = Interfaces().That().Are(helper.BaseInterface).Should(); + helper.AddSnapshotHeader("Empty Arguments (Only Violations)"); + should = Interfaces().That().Are(helper.BaseInterface, helper.ChildInterface).Should(); helper.AddSnapshotSubHeader("Conditions"); should.ImplementAnyInterfaces().AssertOnlyViolations(helper); should.ImplementAnyInterfaces(new List()).AssertOnlyViolations(helper); should.ImplementAnyInterfaces(new List()).AssertOnlyViolations(helper); should.ImplementAnyInterfaces(Interfaces().That().HaveName(helper.NonExistentObjectName)).AssertOnlyViolations(helper); - + helper.AddSnapshotSubHeader("Predicates"); should.Be(Interfaces().That().ImplementAnyInterfaces()).AssertOnlyViolations(helper); should.Be(Interfaces().That().ImplementAnyInterfaces(new List())).AssertOnlyViolations(helper); @@ -865,7 +841,7 @@ public async Task NotImplementAnyInterfacesTest() should.NotImplementAnyInterfacesThat().Are(helper.BaseInterface).AssertOnlyViolations(helper); helper.AddSnapshotHeader("Empty Arguments (No Violations)"); - should = Interfaces().That().Are(helper.BaseInterface).Should(); + should = Interfaces().That().Are(helper.BaseInterface, helper.ChildInterface).Should(); helper.AddSnapshotSubHeader("Conditions"); should.NotImplementAnyInterfaces().AssertNoViolations(helper); @@ -888,30 +864,6 @@ public async Task NotImplementAnyInterfacesTest() helper.AddSnapshotSubHeader("Complex conditions"); should.NotImplementAnyInterfacesThat().Are(new List()).AssertNoViolations(helper); - helper.AddSnapshotHeader("Empty Arguments (Violations)"); - should = Interfaces().That().Are(helper.ChildInterface).Should(); - - helper.AddSnapshotSubHeader("Conditions"); - should.NotImplementAnyInterfaces().AssertOnlyViolations(helper); - should.NotImplementAnyInterfaces(new List()).AssertOnlyViolations(helper); - should.NotImplementAnyInterfaces(new List()).AssertOnlyViolations(helper); - should.NotImplementAnyInterfaces(Interfaces().That().HaveName(helper.NonExistentObjectName)).AssertOnlyViolations(helper); - - helper.AddSnapshotSubHeader("Predicates"); - should.Be(Interfaces().That().DoNotImplementAnyInterfaces()).AssertOnlyViolations(helper); - should.Be(Interfaces().That().DoNotImplementAnyInterfaces(new List())).AssertOnlyViolations(helper); - should.Be(Interfaces().That().DoNotImplementAnyInterfaces(new List())).AssertOnlyViolations(helper); - should.Be(Interfaces().That().DoNotImplementAnyInterfaces(Interfaces().That().HaveName(helper.NonExistentObjectName))).AssertOnlyViolations(helper); - - helper.AddSnapshotSubHeader("Predicates as conditions"); - should.BeTypesThat().DoNotImplementAnyInterfaces().AssertOnlyViolations(helper); - should.BeTypesThat().DoNotImplementAnyInterfaces(new List()).AssertOnlyViolations(helper); - should.BeTypesThat().DoNotImplementAnyInterfaces(new List()).AssertOnlyViolations(helper); - should.BeTypesThat().DoNotImplementAnyInterfaces(Interfaces().That().HaveName(helper.NonExistentObjectName)).AssertOnlyViolations(helper); - - helper.AddSnapshotSubHeader("Complex conditions"); - should.NotImplementAnyInterfacesThat().Are(new List()).AssertOnlyViolations(helper); - helper.AddSnapshotHeader("Multiple arguments"); should = Interfaces().That().Are(helper.ChildInterface).Should(); From 38b447ead8c47c75601e430385e14c497731106b Mon Sep 17 00:00:00 2001 From: Alexander Linne Date: Mon, 2 Mar 2026 18:03:51 +0100 Subject: [PATCH 2/3] feat: dont deduplicate method parameter types Signed-off-by: Alexander Linne --- .../Loader/LoadTasks/AddMethodDependencies.cs | 7 +- .../Loader/MonoCecilMemberExtensions.cs | 66 ++--------- .../Loader/MonoCecilMemberExtensionsTests.cs | 104 ++++++++++++++++++ .../LoaderTestAssembly/LoaderTestAssembly.cs | 13 +++ 4 files changed, 134 insertions(+), 56 deletions(-) create mode 100644 ArchUnitNETTests/Loader/MonoCecilMemberExtensionsTests.cs diff --git a/ArchUnitNET/Loader/LoadTasks/AddMethodDependencies.cs b/ArchUnitNET/Loader/LoadTasks/AddMethodDependencies.cs index fd2734cd4..5878c6d28 100644 --- a/ArchUnitNET/Loader/LoadTasks/AddMethodDependencies.cs +++ b/ArchUnitNET/Loader/LoadTasks/AddMethodDependencies.cs @@ -119,8 +119,11 @@ private IEnumerable CreateMethodSignatureDependencies MethodMember methodMember ) { - return methodReference - .GetSignatureTypes(_typeFactory) + var returnType = methodReference.GetReturnType(_typeFactory); + return (returnType != null ? new[] { returnType } : Array.Empty>()) + .Concat(methodReference.GetParameters(_typeFactory)) + .Concat(methodReference.GetGenericParameters(_typeFactory)) + .Distinct() .Select(signatureType => new MethodSignatureDependency( methodMember, signatureType diff --git a/ArchUnitNET/Loader/MonoCecilMemberExtensions.cs b/ArchUnitNET/Loader/MonoCecilMemberExtensions.cs index 2adca2969..20379af43 100644 --- a/ArchUnitNET/Loader/MonoCecilMemberExtensions.cs +++ b/ArchUnitNET/Loader/MonoCecilMemberExtensions.cs @@ -82,75 +82,33 @@ this MethodDefinition methodDefinition .Concat(methodDefinition.MethodReturnType.CustomAttributes); } - [NotNull] - internal static IEnumerable> GetSignatureTypes( - this MethodReference methodReference, - TypeFactory typeFactory - ) - { - var parameters = GetAllParameters(methodReference, typeFactory).ToList(); - var returnType = GetReturnType(methodReference, typeFactory); - if (returnType != null) - { - parameters.Insert(0, returnType); - } - - return parameters; - } - - private static ITypeInstance GetReturnType( + internal static ITypeInstance GetReturnType( this MethodReference methodReference, TypeFactory typeFactory - ) - { - return ReturnsVoid(methodReference) + ) => + ReturnsVoid(methodReference) ? null : typeFactory.GetOrCreateStubTypeInstanceFromTypeReference( methodReference.MethodReturnType.ReturnType ); - } - - [NotNull] - private static IEnumerable> GetAllParameters( - this MethodReference methodReference, - TypeFactory typeFactory - ) - { - var parameters = methodReference.GetParameters(typeFactory).ToList(); - var genericParameters = methodReference.GetGenericParameters(typeFactory).ToList(); - parameters.AddRange(genericParameters); - return parameters; - } [NotNull] internal static IEnumerable> GetParameters( this MethodReference method, TypeFactory typeFactory - ) - { - return method - .Parameters.Select(parameter => - { - var typeReference = parameter.ParameterType; - return typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(typeReference); - }) - .Distinct(); - } + ) => + method.Parameters.Select(parameter => + typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(parameter.ParameterType) + ); [NotNull] - private static IEnumerable> GetGenericParameters( + internal static IEnumerable> GetGenericParameters( this MethodReference method, TypeFactory typeFactory - ) - { - return method - .GenericParameters.Select(parameter => - { - var typeReference = parameter.GetElementType(); - return typeFactory.GetOrCreateStubTypeInstanceFromTypeReference(typeReference); - }) - .Distinct(); - } + ) => + method.GenericParameters.Select( + typeFactory.GetOrCreateStubTypeInstanceFromTypeReference + ); [NotNull] internal static IEnumerable> GetBodyTypes( diff --git a/ArchUnitNETTests/Loader/MonoCecilMemberExtensionsTests.cs b/ArchUnitNETTests/Loader/MonoCecilMemberExtensionsTests.cs new file mode 100644 index 000000000..2026599c8 --- /dev/null +++ b/ArchUnitNETTests/Loader/MonoCecilMemberExtensionsTests.cs @@ -0,0 +1,104 @@ +using System.Linq; +using ArchUnitNET.Domain; +using ArchUnitNET.Domain.Extensions; +using Xunit; +using static ArchUnitNETTests.StaticTestArchitectures; + +namespace ArchUnitNETTests.Loader +{ + public class MonoCecilMemberExtensionsTests + { + private static readonly Architecture LoaderTestArchitecture = + StaticTestArchitectures.LoaderTestArchitecture; + + [Fact] + public void GetParameters_PreservesDuplicateParameterTypes() + { + var @class = LoaderTestArchitecture + .Types.OfType() + .FirstOrDefault(c => c.Name == "ClassWithDuplicateParameters"); + + Assert.NotNull(@class); + + var method = @class + .GetMethodMembers() + .First(m => m.FullName.Contains("MethodWithSameParameterType")); + + Assert.Equal(2, method.ParameterInstances.Count); + Assert.All( + method.ParameterInstances, + p => Assert.Equal("System.String", p.Type.FullName) + ); + } + + [Fact] + public void GetParameters_PreservesMultipleDuplicateParameterTypes() + { + var @class = LoaderTestArchitecture + .Types.OfType() + .FirstOrDefault(c => c.Name == "ClassWithDuplicateParameters"); + + Assert.NotNull(@class); + + var method = @class + .GetMethodMembers() + .First(m => m.FullName.Contains("MethodWithTripleDuplicate")); + + Assert.Equal(3, method.ParameterInstances.Count); + Assert.All( + method.ParameterInstances, + p => Assert.Equal("System.Int32", p.Type.FullName) + ); + } + + [Fact] + public void GetParameters_MixedTypeWithDuplicates() + { + var @class = LoaderTestArchitecture + .Types.OfType() + .FirstOrDefault(c => c.Name == "ClassWithDuplicateParameters"); + + Assert.NotNull(@class); + + var method = @class + .GetMethodMembers() + .First(m => m.FullName.Contains("MethodWithMixedParamTypes")); + + Assert.Equal(5, method.ParameterInstances.Count); + + var stringParameters = method.ParameterInstances.Count(p => + p.Type.FullName == "System.String" + ); + var intParameters = method.ParameterInstances.Count(p => + p.Type.FullName == "System.Int32" + ); + var boolParameters = method.ParameterInstances.Count(p => + p.Type.FullName == "System.Boolean" + ); + + Assert.Equal(3, stringParameters); + Assert.Equal(1, intParameters); + Assert.Equal(1, boolParameters); + } + + [Fact] + public void GetParameters_CustomTypeDuplicates() + { + var @class = LoaderTestArchitecture + .Types.OfType() + .FirstOrDefault(c => c.Name == "ClassWithDuplicateParameters"); + + Assert.NotNull(@class); + + var method = @class + .GetMethodMembers() + .First(m => m.FullName.Contains("MethodWithCustomTypeDuplicates")); + + Assert.Equal(2, method.ParameterInstances.Count); + Assert.All( + method.ParameterInstances, + p => Assert.Contains("CustomType", p.Type.FullName) + ); + } + } +} diff --git a/TestAssemblies/LoaderTestAssembly/LoaderTestAssembly.cs b/TestAssemblies/LoaderTestAssembly/LoaderTestAssembly.cs index 9de88db39..d78500f17 100644 --- a/TestAssemblies/LoaderTestAssembly/LoaderTestAssembly.cs +++ b/TestAssemblies/LoaderTestAssembly/LoaderTestAssembly.cs @@ -1,3 +1,16 @@ namespace LoaderTestAssembly; public class LoaderTestAssembly { } + +public class ClassWithDuplicateParameters +{ + public void MethodWithSameParameterType(string param1, string param2) { } + + public void MethodWithTripleDuplicate(int a, int b, int c) { } + + public void MethodWithMixedParamTypes(string s1, int i, string s2, bool b, string s3) { } + + public void MethodWithCustomTypeDuplicates(CustomType a, CustomType b) { } +} + +public class CustomType { } From 955e8b56548834a99d10b0c98a2c29b561ba41e6 Mon Sep 17 00:00:00 2001 From: Alexander Linne Date: Mon, 2 Mar 2026 18:08:49 +0100 Subject: [PATCH 3/3] fix: FieldMembersShould should yield a FieldMembersShouldConjunction Signed-off-by: Alexander Linne --- .../Elements/Members/FieldMembers/FieldMembersShould.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ArchUnitNET/Fluent/Syntax/Elements/Members/FieldMembers/FieldMembersShould.cs b/ArchUnitNET/Fluent/Syntax/Elements/Members/FieldMembers/FieldMembersShould.cs index d3c7d059f..d324fb7c8 100644 --- a/ArchUnitNET/Fluent/Syntax/Elements/Members/FieldMembers/FieldMembersShould.cs +++ b/ArchUnitNET/Fluent/Syntax/Elements/Members/FieldMembers/FieldMembersShould.cs @@ -3,17 +3,17 @@ namespace ArchUnitNET.Fluent.Syntax.Elements.Members.FieldMembers { - public sealed class FieldMembersShould : AddFieldMemberCondition + public sealed class FieldMembersShould : AddFieldMemberCondition { public FieldMembersShould(IArchRuleCreator ruleCreator) : base(ruleCreator) { } - protected override FieldMembersShould CreateNextElement( + protected override FieldMembersShouldConjunction CreateNextElement( IOrderedCondition condition ) { _ruleCreator.AddCondition(condition); - return new FieldMembersShould(_ruleCreator); + return new FieldMembersShouldConjunction(_ruleCreator); } } }