dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.04k stars 4.03k forks source link

Missing/incorrect nullability warnings with lambda expressions #72274

Open TessenR opened 8 months ago

TessenR commented 8 months ago

Version Used:

Microsoft Visual Studio Professional 2022
Version 17.10.0 Preview 1.0
VisualStudio.17.Preview/17.10.0-pre.1.0+34607.79
Microsoft .NET Framework
Version 4.8.09037

Steps to Reproduce:

Compile and run the following code (.net8 target framework):

#nullable enable
string? maybeNull = null;
var x = () => maybeNull;
x().ToString(); // NRE at runtime

sharplab.io

Expected Behavior: A warning is reported for x().ToString() call since the return type of x should be inferred to be string?

Actual Behavior: No warnings at all in the code above. It crashes with a NullReferenceException at runtime

Notes: I've discovered a couple of related issues that all seems to have the same root cause - a lambda expression inferes its return type to be not nullable despite having nullable returns. Please let me know if I need to create separate issues for each case.

incorrect type inferecne with the same lambda expression:

#nullable enable
using System;

string? maybeNull = null;
var x = () => maybeNull;
Test(x); // missing warning; runtime crash

void Test<T>(Func<T> f) where T: notnull => f().ToString();

incorrect cascading diagnostics:

#nullable enable
using System;

string? maybeNull = null;
Test(() => maybeNull); // CS8714 (correct); CS8621 (incorrect)

void Test<T>(Func<T> f) where T: notnull => f().ToString();

This one is a bit different because it does report type constraint violation

warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'Test<T>(Func<T>)'. Nullability of type argument 'string?' doesn't match 'notnull' constraint.

However, the second diagnostic is very confusing:

CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'Func<string?>' (possibly because of nullability attributes).

According to the warning the lambda expression isn't compatible with Func<string?> which is the type inferred from the lambda expression itself. What could even be the return type of the lambda expression to warrant this warning?

It seems that Roslyn incorrectly considers the .Invoke method of the lambda expression to be not nullable (which might be the root caues of all the issues above) while correctly inferring the type argument to be nullable, resulting in the following "logically impossible" type infernce which gives the same set of warnings:

#nullable enable
using System;

string? maybeNull = null;
Test<string?>(string () => // CS8714 (correct); CS8621 (incorrect)
  throw null!); // lambda body is analyzed correctly in the previous example as it doesn't depend on the '.Invoke' method

void Test<T>(Func<T> f) where T: notnull => f().ToString();
jaredpar commented 7 months ago

@cston was nullability on return one of the items we considered when doing lambda natural types?

cston commented 7 months ago

Nullable analysis is not re-inferring the function type, so nullable analysis in the method uses the unannotated delegate type. This looks like an oversight.