Skip to content

ASP0016 False positive for nested anonymous methods #64173

@halter73

Description

@halter73

You can add the following test to RequestDelegateReturnTypeAnalyzerTests.cs:

    [Fact]
    public async Task AnonymousDelegate_RequestDelegate_ReturnType_InNestedAynonymousDelegate_DoesNotReportDiagnostics()
    {
        await VerifyCS.VerifyAnalyzerAsync(@"
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
var webApp = WebApplication.Create();
webApp.Run((context) =>
{
    Func<Task<DateTime>> _ = () => Task.FromResult(DateTime.Now);
    return Task.CompletedTask;
});
");
    }

And you'll get the following failure:

  Message: 
System.InvalidOperationException : Mismatch between number of diagnostics returned, expected "0" actual "1"

Diagnostics:
// /0/Test0.cs(6,12): warning ASP0016: The method used to create a RequestDelegate returns Task<System.DateTime>. RequestDelegate discards this value. If this isn't intended then change the return type to non-generic Task or, if the delegate is a route handler, cast it to Delegate so the return value is written to the response.
VerifyCS.Diagnostic().WithSpan(6, 12, 10, 2).WithArguments("System.DateTime"),



  Stack Trace: 
DefaultVerifier.Equal[T](T expected, T actual, String message)
AnalyzerTest`1.VerifyDiagnosticResults(IEnumerable`1 actualResults, ImmutableArray`1 analyzers, DiagnosticResult[] expectedResults, IVerifier verifier)
AnalyzerTest`1.VerifyDiagnosticsAsync(EvaluatedProjectState primaryProject, ImmutableArray`1 additionalProjects, DiagnosticResult[] expected, IVerifier verifier, CancellationToken cancellationToken)
AnalyzerTest`1.RunImplAsync(CancellationToken cancellationToken)
AnalyzerTest`1.RunAsync(CancellationToken cancellationToken)
CSharpAnalyzerVerifier`1.VerifyAnalyzerAsync(String source, DiagnosticResult[] expected) line 46
RequestDelegateReturnTypeAnalyzerTests.AnonymousDelegate_RequestDelegate_ReturnType_InNestedAynonymousDelegate_DoesNotReportDiagnostics() line 73
--- End of stack trace from previous location ---

This is incorrect because the Task<System.DateTime> is not being returned directly from the anonymous RequestDelegate, but instead an inner anonymous method which is not a RequestDelegate. This was uncovered by #64120 which resulted in the following line getting incorrectly flagged by ASP0016 even though the lambda calling UpgradeAsync() and returning a Task<Stream> was being passed to ThrowsAsync as a Func<Task<Stream>> and was never converted to a RequestDelegate.

var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => upgradeFeature.UpgradeAsync());

D:\a\_work\1\s\src\Servers\IIS\IIS\test\testassets\InProcessWebSite\Startup.WebSockets.cs(98,17): error ASP0016: The method used to create a RequestDelegate returns Task<System.IO.Stream>. RequestDelegate discards this value. If this isn't intended then change the return type to non-generic Task or, if the delegate is a route handler, cast it to Delegate so the return value is written to the response. (https://aka.ms/aspnet/analyzers) [D:\a\_work\1\s\src\Servers\IIS\IIS\test\testassets\InProcessNewShimWebSite\InProcessNewShimWebSite.csproj::TargetFramework=net8.0]
##[error]src\Servers\IIS\IIS\test\testassets\InProcessWebSite\Startup.WebSockets.cs(98,17): error ASP0016: (NETCORE_ENGINEERING_TELEMETRY=Build) The method used to create a RequestDelegate returns Task<System.IO.Stream>. RequestDelegate discards this value. If this isn't intended then change the return type to non-generic Task or, if the delegate is a route handler, cast it to Delegate so the return value is written to the response. (https://aka.ms/aspnet/analyzers)

https://dev.azure.com/dnceng-public/public/_build/results?buildId=1186969&view=logs&jobId=1e4c7542-9334-5468-a143-1d4c0c4c3006%3Fpr%3D64120&j=4192b01c-4527-54c8-cf65-dc27f588da45&t=ee2d7ee8-2075-51eb-eb72-626500250fa2&s=d654deb9-056d-50a2-1717-90c08683d50a

@JamesNK

Metadata

Metadata

Assignees

Labels

analyzerIndicates an issue which is related to analyzer experiencearea-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etc

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions