diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b62b25e49fc6..d20e0d52e54c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,7 +1,6 @@ # Users referenced in this file will automatically be requested as reviewers for PRs that modify the given paths. # See https://help.github.com/articles/about-code-owners/ -* @Pilchie /**/PublicAPI.*Shipped.txt @dotnet/aspnet-api-review /global.json @dotnet/aspnet-build @dougbu @wtgodbe /.azure/ @dotnet/aspnet-build @dougbu @wtgodbe 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 @@ false diff --git a/eng/test-configuration.json b/eng/test-configuration.json index 57d8db3d3f3d..eba3e6de52a8 100644 --- a/eng/test-configuration.json +++ b/eng/test-configuration.json @@ -17,6 +17,7 @@ {"testName": {"contains": "POST_ServerAbort_ClientReceivesAbort"}}, {"testName": {"contains": "POST_ServerAbortAfterWrite_ClientReceivesAbort"}}, {"testAssembly": {"contains": "IIS"}}, + {"testAssembly": {"contains": "Template"}}, {"failureMessage": {"contains":"(Site is started but no worker process found)"}}, {"failureMessage": {"contains": "network disconnected"}} ], diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/RouteHandlers/DetectMisplacedLambdaAttribute.cs b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/RouteHandlers/DetectMisplacedLambdaAttribute.cs index 126f0a9c25d9..79fa7dd994c3 100644 --- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/RouteHandlers/DetectMisplacedLambdaAttribute.cs +++ b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/RouteHandlers/DetectMisplacedLambdaAttribute.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -22,23 +23,17 @@ private static void DetectMisplacedLambdaAttribute( // Hello(); // return "foo"; // } - IMethodSymbol? methodSymbol = null; - // () => 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), diff --git a/src/Http/Routing/src/RouteEndpointDataSource.cs b/src/Http/Routing/src/RouteEndpointDataSource.cs index 7e38da886cb5..2e39cd81effb 100644 --- a/src/Http/Routing/src/RouteEndpointDataSource.cs +++ b/src/Http/Routing/src/RouteEndpointDataSource.cs @@ -168,9 +168,13 @@ private RouteEndpointBuilder CreateRouteEndpointBuilder( { DisplayName = displayName, ApplicationServices = _applicationServices, - Metadata = { handler.Method }, }; + if (isRouteHandler) + { + builder.Metadata.Add(handler.Method); + } + if (entry.HttpMethods is not null) { builder.Metadata.Add(new HttpMethodMetadata(entry.HttpMethods)); diff --git a/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs b/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs index c58a14ed4223..0c693a01a2dd 100644 --- a/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs +++ b/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs @@ -190,10 +190,9 @@ public void MapEndpoint_AttributesCollectedAsMetadata() // Assert var endpointBuilder1 = GetRouteEndpointBuilder(builder); Assert.Equal("/", endpointBuilder1.RoutePattern.RawText); - Assert.Equal(3, endpointBuilder1.Metadata.Count); - Assert.Equal(((RequestDelegate)Handle).Method, endpointBuilder1.Metadata[0]); - Assert.IsType(endpointBuilder1.Metadata[1]); - Assert.IsType(endpointBuilder1.Metadata[2]); + Assert.Equal(2, endpointBuilder1.Metadata.Count); + Assert.IsType(endpointBuilder1.Metadata[0]); + Assert.IsType(endpointBuilder1.Metadata[1]); } [Fact] @@ -228,11 +227,10 @@ public void MapEndpoint_PrecedenceOfMetadata_BuilderMetadataReturned() // As with the Delegate Map method overloads for route handlers, the attributes on the RequestDelegate // can override the HttpMethodMetadata. Extension methods could already do this. - Assert.Equal(4, endpoint.Metadata.Count); - Assert.Equal(((RequestDelegate)HandleHttpMetdata).Method, endpoint.Metadata[0]); - Assert.Equal("METHOD", GetMethod(endpoint.Metadata[1])); - Assert.Equal("ATTRIBUTE", GetMethod(endpoint.Metadata[2])); - Assert.Equal("BUILDER", GetMethod(endpoint.Metadata[3])); + Assert.Equal(3, endpoint.Metadata.Count); + Assert.Equal("METHOD", GetMethod(endpoint.Metadata[0])); + Assert.Equal("ATTRIBUTE", GetMethod(endpoint.Metadata[1])); + Assert.Equal("BUILDER", GetMethod(endpoint.Metadata[2])); Assert.Equal("BUILDER", endpoint.Metadata.GetMetadata()?.HttpMethods.Single()); diff --git a/src/submodules/spa-templates b/src/submodules/spa-templates index f94b3a5f75b7..eb7442251b06 160000 --- a/src/submodules/spa-templates +++ b/src/submodules/spa-templates @@ -1 +1 @@ -Subproject commit f94b3a5f75b7556db10c345b158efedcd03147ce +Subproject commit eb7442251b063a26f9f94b13e11883f397022df7