How to indicate to static null flow analysis in C# that a property from a method's return object is not null because it has been validated? See the code below.
Let's have this validation method
public static class AuthenticationHeader
{
public static bool TryParse(
HttpRequest request,
[NotNullWhen(returnValue: true)] out AuthenticationHeaderValue? value) =>
AuthenticationHeaderValue.TryParse(
request.Headers.Authorization,
out value) && !string.IsNullOrEmpty(value.Parameter);
}
and its use in some other method
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!AuthenticationHeader.TryParse(Request, out var authHeader))
{
return AuthenticateResult.Fail("Missing Authorization header");
}
_client.DefaultRequestHeaders.Authorization = authHeader;
var apiClient = new CommonApiClient(_client);
var userInfo = await TryGetUserInfo(apiClient);
if (userInfo is null)
{
return AuthenticateResult.Fail("Unauthorized");
}
var identity = new ClaimsIdentity(
BuildClaims(userInfo, authHeader.Parameter), // Possible null reference argument for parameter 'token' in ...
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), Scheme.Name);
return AuthenticateResult.Success(ticket);
}
[NotNullWhen(returnValue: true)]
works great on value
, but static analysis does not know that value.Parameter
has been checked already.
AFAIK this is not possible with current state of attributes for null-state static analysis interpreted by the C# compiler. You need to workaround with in-place checks:
if (!AuthenticationHeader.TryParse(Request, out var authHeader) || string.IsNullOrEmpty(authHeader.Parameter))
{
return AuthenticateResult.Fail("Missing Authorization header");
}
Or return both values from the method (changing the usage accordingly):
public static bool TryParse(
HttpRequest request,
[NotNullWhen(returnValue: true)] out AuthenticationHeaderValue? value,
[NotNullWhen(returnValue: true)] out string? par)
{
var tryParse = AuthenticationHeaderValue.TryParse(
request.Headers.Authorization,
out value);
par = value?.Parameter;
return tryParse && !string.IsNullOrEmpty(par);
}
Or use null-forgiving operator.
P.S.
Created discussion at the language repo to support MemberNotNullWhenAttribute
usage on method parameters.