diff --git a/eng/Build.props b/eng/Build.props index 6599b9d6f23f..2fd291c27789 100644 --- a/eng/Build.props +++ b/eng/Build.props @@ -160,6 +160,7 @@ Hello() has a single child which is a BlockOperation so we check to see if - // expression associated with that operation is an invocation expression - if (lambda.ChildOperations.FirstOrDefault().Syntax is InvocationExpressionSyntax innerInvocation) + // All lambdas have a single child which is a BlockOperation. We search ChildOperations for + // the invocation expression. + if (lambda.ChildOperations.Count != 1 || lambda.ChildOperations.FirstOrDefault() is not IBlockOperation blockOperation) { - methodSymbol = lambda.Symbol; - } - - if (lambda.ChildOperations.FirstOrDefault().ChildOperations.FirstOrDefault() is IReturnOperation returnOperation - && returnOperation.ReturnedValue is IInvocationOperation returnedInvocation) - { - methodSymbol = returnedInvocation.TargetMethod; + Debug.Fail("Expected a single top-level BlockOperation for all lambdas."); + return; } // If no method definition was found for the lambda, then abort. - if (methodSymbol is null) + if (GetReturnedInvocation(blockOperation) is not IMethodSymbol methodSymbol) { return; } @@ -73,5 +68,38 @@ static bool IsInValidNamespace(INamespaceSymbol? @namespace) return false; } + + static IMethodSymbol? GetReturnedInvocation(IBlockOperation blockOperation) + { + foreach (var op in blockOperation.ChildOperations.Reverse()) + { + if (op is IReturnOperation returnStatement) + { + if (returnStatement.ReturnedValue is IInvocationOperation invocationReturn) + { + return invocationReturn.TargetMethod; + } + + // Sometimes expression backed lambdas include an IReturnOperation with a null ReturnedValue + // right after the IExpressionStatementOperation whose Operation is the real ReturnedValue, + // so we keep looking backwards rather than returning null immediately. + } + else if (op is IExpressionStatementOperation expression) + { + if (expression.Operation is IInvocationOperation invocationExpression) + { + return invocationExpression.TargetMethod; + } + + return null; + } + else + { + return null; + } + } + + return null; + } } } diff --git a/src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpWebApplicationBuilderCodeFixVerifier.cs b/src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpWebApplicationBuilderCodeFixVerifier.cs index aa8841c83be5..8073893db702 100644 --- a/src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpWebApplicationBuilderCodeFixVerifier.cs +++ b/src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpWebApplicationBuilderCodeFixVerifier.cs @@ -68,6 +68,7 @@ public WebApplicationBuilderAnalyzerTest() TrimAssemblyExtension(typeof(Microsoft.Extensions.Hosting.HostingHostBuilderExtensions).Assembly.Location), TrimAssemblyExtension(typeof(Microsoft.AspNetCore.Builder.ConfigureHostBuilder).Assembly.Location), TrimAssemblyExtension(typeof(Microsoft.AspNetCore.Builder.ConfigureWebHostBuilder).Assembly.Location), + TrimAssemblyExtension(typeof(Microsoft.AspNetCore.Builder.EndpointRoutingApplicationBuilderExtensions).Assembly.Location), TrimAssemblyExtension(typeof(Microsoft.AspNetCore.Hosting.HostingAbstractionsWebHostBuilderExtensions).Assembly.Location), TrimAssemblyExtension(typeof(Microsoft.Extensions.Logging.ILoggingBuilder).Assembly.Location), TrimAssemblyExtension(typeof(Microsoft.Extensions.Logging.ConsoleLoggerExtensions).Assembly.Location),