diff --git a/ArchUnitNET/Loader/TypeProcessor.cs b/ArchUnitNET/Loader/TypeProcessor.cs index a55c63d4f..dceed9363 100644 --- a/ArchUnitNET/Loader/TypeProcessor.cs +++ b/ArchUnitNET/Loader/TypeProcessor.cs @@ -821,6 +821,17 @@ var calledMethodReference in calledMethodReferences.Except(visitedMethodReferenc ); } + if (calledMethodDefinition.IsAsync()) + { + HandleAsync( + out calledMethodDefinition, + ref calledMethodBody, + bodyTypes, + visitedMethodReferences, + domainResolver + ); + } + var calledScan = calledMethodDefinition.ScanMethodBody(domainResolver); bodyTypes.AddRange(calledScan.BodyTypes); castTypes.AddRange(calledScan.CastTypes); diff --git a/ArchUnitNETTests/Domain/Dependencies/Members/MethodCallDependencyTests.cs b/ArchUnitNETTests/Domain/Dependencies/Members/MethodCallDependencyTests.cs index 6e0ed9a7f..df49e098a 100644 --- a/ArchUnitNETTests/Domain/Dependencies/Members/MethodCallDependencyTests.cs +++ b/ArchUnitNETTests/Domain/Dependencies/Members/MethodCallDependencyTests.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System; +using System.Linq; +using System.Threading.Tasks; using ArchUnitNET.Domain; using ArchUnitNET.Domain.Dependencies; using ArchUnitNET.Domain.Extensions; @@ -107,6 +109,17 @@ MethodCallDependency expectedDependency Assert.Contains(expectedDependency, originMember.GetMethodCallDependencies()); } + [Theory] + [ClassData(typeof(MethodDependencyTestBuild.MethodCallDependencyInAsyncLambdaTestData))] + public void MethodCallDependenciesAreFoundInAsyncLambdas( + IMember originMember, + MethodCallDependency expectedDependency + ) + { + Assert.True(originMember.HasMemberDependency(expectedDependency)); + Assert.Contains(expectedDependency, originMember.GetMethodCallDependencies()); + } + [Theory] [ClassData( typeof(MethodDependencyTestBuild.MethodCallGenericConstructorArgumentsDependencyTestData) @@ -173,6 +186,88 @@ public static async void MethodAAsync() } } + public class ClassWithAsyncLambdas + { + public static void MethodWithAsyncLambda() + { +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + Func asyncLambda = async () => +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + { + var classWithMethodB = new ClassWithMethodB(); + ClassWithMethodB.MethodB(); + }; + } + } + + public class ClassWithAsyncLambdaVariants + { + public static void MethodWithCapturingAsyncLambda() + { + var capturedValue = 0; +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + Func asyncLambda = async () => +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + { + capturedValue++; + var classWithMethodB = new ClassWithMethodB(); + ClassWithMethodB.MethodB(); + }; + } + + public static void MethodWithAsyncLocalFunction() + { + _ = LocalFunctionAsync(); + +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + async Task LocalFunctionAsync() +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + { + var classWithMethodB = new ClassWithMethodB(); + ClassWithMethodB.MethodB(); + } + } + + public static void MethodWithNestedAsyncLambda() + { + Func outerAsyncLambda = async () => + { +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + Func innerAsyncLambda = async () => +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + { + var classWithMethodB = new ClassWithMethodB(); + ClassWithMethodB.MethodB(); + }; + await innerAsyncLambda(); + }; + } + + public static async Task AsyncMethodWithAsyncLambda() + { +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + Func asyncLambda = async () => +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + { + var classWithMethodB = new ClassWithMethodB(); + ClassWithMethodB.MethodB(); + }; + await asyncLambda(); + } + + public static void GenericMethodWithAsyncLambda() + { +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + Func> asyncLambda = async () => +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + { + var classWithMethodB = new ClassWithMethodB(); + ClassWithMethodB.MethodB(); + return default; + }; + } + } + public class ClassWithMethodWithGenericConstructorArguments { public static void Method() diff --git a/ArchUnitNETTests/Domain/Dependencies/Members/MethodDependencyTestBuild.cs b/ArchUnitNETTests/Domain/Dependencies/Members/MethodDependencyTestBuild.cs index 60cdc7282..baa059a33 100644 --- a/ArchUnitNETTests/Domain/Dependencies/Members/MethodDependencyTestBuild.cs +++ b/ArchUnitNETTests/Domain/Dependencies/Members/MethodDependencyTestBuild.cs @@ -146,6 +146,105 @@ IEnumerator IEnumerable.GetEnumerator() } } + public class MethodCallDependencyInAsyncLambdaTestData : IEnumerable + { + private readonly List _methodCallDependencyData = new List + { + BuildMethodCallDependencyTestData( + typeof(ClassWithAsyncLambdas), + nameof(ClassWithAsyncLambdas.MethodWithAsyncLambda).BuildMethodMemberName(), + typeof(ClassWithMethodB), + StaticConstants.ConstructorNameBase.BuildMethodMemberName() + ), + BuildMethodCallDependencyTestData( + typeof(ClassWithAsyncLambdas), + nameof(ClassWithAsyncLambdas.MethodWithAsyncLambda).BuildMethodMemberName(), + typeof(ClassWithMethodB), + nameof(ClassWithMethodB.MethodB).BuildMethodMemberName() + ), + BuildMethodCallDependencyTestData( + typeof(ClassWithAsyncLambdaVariants), + nameof(ClassWithAsyncLambdaVariants.MethodWithCapturingAsyncLambda) + .BuildMethodMemberName(), + typeof(ClassWithMethodB), + StaticConstants.ConstructorNameBase.BuildMethodMemberName() + ), + BuildMethodCallDependencyTestData( + typeof(ClassWithAsyncLambdaVariants), + nameof(ClassWithAsyncLambdaVariants.MethodWithCapturingAsyncLambda) + .BuildMethodMemberName(), + typeof(ClassWithMethodB), + nameof(ClassWithMethodB.MethodB).BuildMethodMemberName() + ), + BuildMethodCallDependencyTestData( + typeof(ClassWithAsyncLambdaVariants), + nameof(ClassWithAsyncLambdaVariants.MethodWithAsyncLocalFunction) + .BuildMethodMemberName(), + typeof(ClassWithMethodB), + StaticConstants.ConstructorNameBase.BuildMethodMemberName() + ), + BuildMethodCallDependencyTestData( + typeof(ClassWithAsyncLambdaVariants), + nameof(ClassWithAsyncLambdaVariants.MethodWithAsyncLocalFunction) + .BuildMethodMemberName(), + typeof(ClassWithMethodB), + nameof(ClassWithMethodB.MethodB).BuildMethodMemberName() + ), + BuildMethodCallDependencyTestData( + typeof(ClassWithAsyncLambdaVariants), + nameof(ClassWithAsyncLambdaVariants.MethodWithNestedAsyncLambda) + .BuildMethodMemberName(), + typeof(ClassWithMethodB), + StaticConstants.ConstructorNameBase.BuildMethodMemberName() + ), + BuildMethodCallDependencyTestData( + typeof(ClassWithAsyncLambdaVariants), + nameof(ClassWithAsyncLambdaVariants.MethodWithNestedAsyncLambda) + .BuildMethodMemberName(), + typeof(ClassWithMethodB), + nameof(ClassWithMethodB.MethodB).BuildMethodMemberName() + ), + BuildMethodCallDependencyTestData( + typeof(ClassWithAsyncLambdaVariants), + nameof(ClassWithAsyncLambdaVariants.AsyncMethodWithAsyncLambda) + .BuildMethodMemberName(), + typeof(ClassWithMethodB), + StaticConstants.ConstructorNameBase.BuildMethodMemberName() + ), + BuildMethodCallDependencyTestData( + typeof(ClassWithAsyncLambdaVariants), + nameof(ClassWithAsyncLambdaVariants.AsyncMethodWithAsyncLambda) + .BuildMethodMemberName(), + typeof(ClassWithMethodB), + nameof(ClassWithMethodB.MethodB).BuildMethodMemberName() + ), + BuildMethodCallDependencyTestData( + typeof(ClassWithAsyncLambdaVariants), + nameof(ClassWithAsyncLambdaVariants.GenericMethodWithAsyncLambda) + .BuildMethodMemberName(), + typeof(ClassWithMethodB), + StaticConstants.ConstructorNameBase.BuildMethodMemberName() + ), + BuildMethodCallDependencyTestData( + typeof(ClassWithAsyncLambdaVariants), + nameof(ClassWithAsyncLambdaVariants.GenericMethodWithAsyncLambda) + .BuildMethodMemberName(), + typeof(ClassWithMethodB), + nameof(ClassWithMethodB.MethodB).BuildMethodMemberName() + ), + }; + + public IEnumerator GetEnumerator() + { + return _methodCallDependencyData.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + public class MethodSignatureDependencyTestData : IEnumerable { private readonly List _methodSignatureDependencyData = new List