Skip to content

Add nullable annotations to Http.Abstractions, Http.Features, Connections.Abstractions #22337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Http/Headers/ref/Microsoft.Net.Http.Headers.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<Nullable>annotations</Nullable>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
<Compile Include="Microsoft.Net.Http.Headers.netcoreapp.cs" />
Expand Down
114 changes: 57 additions & 57 deletions src/Http/Headers/ref/Microsoft.Net.Http.Headers.netcoreapp.cs

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions src/Http/Headers/src/BaseHeaderParser.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Primitives;

namespace Microsoft.Net.Http.Headers
Expand All @@ -12,11 +13,11 @@ protected BaseHeaderParser(bool supportsMultipleValues)
{
}

protected abstract int GetParsedValueLength(StringSegment value, int startIndex, out T parsedValue);
protected abstract int GetParsedValueLength(StringSegment value, int startIndex, [MaybeNull] out T parsedValue);

public sealed override bool TryParseValue(StringSegment value, ref int index, out T parsedValue)
public sealed override bool TryParseValue(StringSegment value, ref int index, [MaybeNull] out T parsedValue)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can [NotNullWhen(true)] be applied here too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately not. The SupportsMultipleValues pattern makes nullable annotations really awkward to use .

{
parsedValue = default(T);
parsedValue = default;

// If multiple values are supported (i.e. list of values), then accept an empty string: The header may
// be added multiple times to the request/response message. E.g.
Expand Down
33 changes: 16 additions & 17 deletions src/Http/Headers/src/CacheControlHeaderValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
Expand Down Expand Up @@ -31,12 +32,12 @@ public class CacheControlHeaderValue
// Cache-Control headers, only one instance of CacheControlHeaderValue is created (if all headers contain valid
// values, otherwise we may have multiple strings containing the invalid values).
private static readonly HttpHeaderParser<CacheControlHeaderValue> Parser
= new GenericHeaderParser<CacheControlHeaderValue>(true, GetCacheControlLength);
= new GenericHeaderParser<CacheControlHeaderValue>(true, GetCacheControlLength!);

private static readonly Action<StringSegment> CheckIsValidTokenAction = CheckIsValidToken;

private bool _noCache;
private ICollection<StringSegment> _noCacheHeaders;
private ICollection<StringSegment>? _noCacheHeaders;
private bool _noStore;
private TimeSpan? _maxAge;
private TimeSpan? _sharedMaxAge;
Expand All @@ -47,10 +48,10 @@ private static readonly HttpHeaderParser<CacheControlHeaderValue> Parser
private bool _onlyIfCached;
private bool _public;
private bool _private;
private ICollection<StringSegment> _privateHeaders;
private ICollection<StringSegment>? _privateHeaders;
private bool _mustRevalidate;
private bool _proxyRevalidate;
private IList<NameValueHeaderValue> _extensions;
private IList<NameValueHeaderValue>? _extensions;

public CacheControlHeaderValue()
{
Expand Down Expand Up @@ -240,7 +241,7 @@ public override string ToString()
return sb.ToString();
}

public override bool Equals(object obj)
public override bool Equals(object? obj)
{
var other = obj as CacheControlHeaderValue;

Expand Down Expand Up @@ -325,7 +326,7 @@ public override int GetHashCode()

public static CacheControlHeaderValue Parse(StringSegment input)
{
int index = 0;
var index = 0;
// Cache-Control is unusual because there are no required values so the parser will succeed for an empty string, but still return null.
var result = Parser.ParseValue(input, ref index);
if (result == null)
Expand All @@ -335,9 +336,9 @@ public static CacheControlHeaderValue Parse(StringSegment input)
return result;
}

public static bool TryParse(StringSegment input, out CacheControlHeaderValue parsedValue)
public static bool TryParse(StringSegment input, [NotNullWhen(true)] out CacheControlHeaderValue? parsedValue)
{
int index = 0;
var index = 0;
// Cache-Control is unusual because there are no required values so the parser will succeed for an empty string, but still return null.
if (Parser.TryParseValue(input, ref index, out parsedValue) && parsedValue != null)
{
Expand All @@ -347,7 +348,7 @@ public static bool TryParse(StringSegment input, out CacheControlHeaderValue par
return false;
}

private static int GetCacheControlLength(StringSegment input, int startIndex, out CacheControlHeaderValue parsedValue)
private static int GetCacheControlLength(StringSegment input, int startIndex, out CacheControlHeaderValue? parsedValue)
{
Contract.Requires(startIndex >= 0);

Expand All @@ -361,16 +362,18 @@ private static int GetCacheControlLength(StringSegment input, int startIndex, ou
// Cache-Control header consists of a list of name/value pairs, where the value is optional. So use an
// instance of NameValueHeaderParser to parse the string.
var current = startIndex;
NameValueHeaderValue nameValue = null;
var nameValueList = new List<NameValueHeaderValue>();
while (current < input.Length)
{
if (!NameValueHeaderValue.MultipleValueParser.TryParseValue(input, ref current, out nameValue))
if (!NameValueHeaderValue.MultipleValueParser.TryParseValue(input, ref current, out var nameValue))
{
return 0;
}

nameValueList.Add(nameValue);
if (nameValue != null)
{
nameValueList.Add(nameValue);
}
}

// If we get here, we were able to successfully parse the string as list of name/value pairs. Now analyze
Expand Down Expand Up @@ -539,10 +542,8 @@ private static bool TrySetTokenOnlyValue(NameValueHeaderValue nameValue, ref boo
private static bool TrySetOptionalTokenList(
NameValueHeaderValue nameValue,
ref bool boolField,
ref ICollection<StringSegment> destination)
ref ICollection<StringSegment>? destination)
{
Contract.Requires(nameValue != null);

if (nameValue.Value == null)
{
boolField = true;
Expand Down Expand Up @@ -603,8 +604,6 @@ private static bool TrySetOptionalTokenList(

private static bool TrySetTimeSpan(NameValueHeaderValue nameValue, ref TimeSpan? timeSpan)
{
Contract.Requires(nameValue != null);

if (nameValue.Value == null)
{
return false;
Expand Down
38 changes: 19 additions & 19 deletions src/Http/Headers/src/ContentDispositionHeaderValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
Expand All @@ -26,10 +27,10 @@ public class ContentDispositionHeaderValue
private static readonly char[] SingleQuote = new char[] { '\'' };

private static readonly HttpHeaderParser<ContentDispositionHeaderValue> Parser
= new GenericHeaderParser<ContentDispositionHeaderValue>(false, GetDispositionTypeLength);
= new GenericHeaderParser<ContentDispositionHeaderValue>(false, GetDispositionTypeLength!);

// Use list instead of dictionary since we may have multiple parameters with the same name.
private ObjectCollection<NameValueHeaderValue> _parameters;
private ObjectCollection<NameValueHeaderValue>? _parameters;
private StringSegment _dispositionType;

private ContentDispositionHeaderValue()
Expand Down Expand Up @@ -109,11 +110,10 @@ public long? Size
get
{
var sizeParameter = NameValueHeaderValue.Find(_parameters, SizeString);
long value;
if (sizeParameter != null)
{
var sizeString = sizeParameter.Value;
if (HeaderUtilities.TryParseNonNegativeInt64(sizeString, out value))
if (HeaderUtilities.TryParseNonNegativeInt64(sizeString, out var value))
{
return value;
}
Expand All @@ -128,7 +128,7 @@ public long? Size
// Remove parameter
if (sizeParameter != null)
{
_parameters.Remove(sizeParameter);
_parameters!.Remove(sizeParameter);
}
}
else if (value < 0)
Expand All @@ -141,8 +141,8 @@ public long? Size
}
else
{
string sizeString = value.GetValueOrDefault().ToString(CultureInfo.InvariantCulture);
_parameters.Add(new NameValueHeaderValue(SizeString, sizeString));
var sizeString = value.GetValueOrDefault().ToString(CultureInfo.InvariantCulture);
Parameters.Add(new NameValueHeaderValue(SizeString, sizeString));
}
}
}
Expand Down Expand Up @@ -180,7 +180,7 @@ public override string ToString()
return _dispositionType + NameValueHeaderValue.ToString(_parameters, ';', true);
}

public override bool Equals(object obj)
public override bool Equals(object? obj)
{
var other = obj as ContentDispositionHeaderValue;

Expand All @@ -202,16 +202,16 @@ public override int GetHashCode()
public static ContentDispositionHeaderValue Parse(StringSegment input)
{
var index = 0;
return Parser.ParseValue(input, ref index);
return Parser.ParseValue(input, ref index)!;
}

public static bool TryParse(StringSegment input, out ContentDispositionHeaderValue parsedValue)
public static bool TryParse(StringSegment input, [NotNullWhen(true)] out ContentDispositionHeaderValue? parsedValue)
{
var index = 0;
return Parser.TryParseValue(input, ref index, out parsedValue);
return Parser.TryParseValue(input, ref index, out parsedValue!);
}

private static int GetDispositionTypeLength(StringSegment input, int startIndex, out ContentDispositionHeaderValue parsedValue)
private static int GetDispositionTypeLength(StringSegment input, int startIndex, out ContentDispositionHeaderValue? parsedValue)
{
Contract.Requires(startIndex >= 0);

Expand Down Expand Up @@ -253,7 +253,7 @@ private static int GetDispositionTypeLength(StringSegment input, int startIndex,

private static int GetDispositionTypeExpressionLength(StringSegment input, int startIndex, out StringSegment dispositionType)
{
Contract.Requires((input != null) && (input.Length > 0) && (startIndex < input.Length));
Contract.Requires((input.Length > 0) && (startIndex < input.Length));

// This method just parses the disposition type string, it does not parse parameters.
dispositionType = null;
Expand Down Expand Up @@ -318,7 +318,7 @@ private void SetDate(string parameter, DateTimeOffset? date)
// Remove parameter
if (dateParameter != null)
{
_parameters.Remove(dateParameter);
_parameters!.Remove(dateParameter);
}
}
else
Expand All @@ -343,7 +343,7 @@ private StringSegment GetName(string parameter)
var nameParameter = NameValueHeaderValue.Find(_parameters, parameter);
if (nameParameter != null)
{
string result;
string? result;
// filename*=utf-8'lang'%7FMyString
if (parameter.EndsWith("*", StringComparison.Ordinal))
{
Expand Down Expand Up @@ -375,7 +375,7 @@ private void SetName(StringSegment parameter, StringSegment value)
// Remove parameter
if (nameParameter != null)
{
_parameters.Remove(nameParameter);
_parameters!.Remove(nameParameter);
}
}
else
Expand Down Expand Up @@ -497,7 +497,7 @@ private unsafe string EncodeMime(StringSegment input)
}

// Attempt to decode MIME encoded strings
private bool TryDecodeMime(StringSegment input, out string output)
private bool TryDecodeMime(StringSegment input, [NotNullWhen(true)] out string? output)
{
Contract.Assert(input != null);

Expand Down Expand Up @@ -582,7 +582,7 @@ private static void HexEscape(StringBuilder builder, char c)

// Attempt to decode using RFC 5987 encoding.
// encoding'language'my%20string
private bool TryDecode5987(StringSegment input, out string output)
private bool TryDecode5987(StringSegment input, [NotNullWhen(true)] out string? output)
{
output = null;

Expand All @@ -593,7 +593,7 @@ private bool TryDecode5987(StringSegment input, out string output)
}

var decoded = new StringBuilder();
byte[] unescapedBytes = null;
byte[]? unescapedBytes = null;
try
{
var encoding = Encoding.GetEncoding(parts[0].ToString());
Expand Down
15 changes: 8 additions & 7 deletions src/Http/Headers/src/ContentRangeHeaderValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
Expand All @@ -12,7 +13,7 @@ namespace Microsoft.Net.Http.Headers
public class ContentRangeHeaderValue
{
private static readonly HttpHeaderParser<ContentRangeHeaderValue> Parser
= new GenericHeaderParser<ContentRangeHeaderValue>(false, GetContentRangeLength);
= new GenericHeaderParser<ContentRangeHeaderValue>(false, GetContentRangeLength!);

private StringSegment _unit;
private long? _from;
Expand Down Expand Up @@ -113,7 +114,7 @@ public long? Length
get { return _from != null; }
}

public override bool Equals(object obj)
public override bool Equals(object? obj)
{
var other = obj as ContentRangeHeaderValue;

Expand Down Expand Up @@ -176,16 +177,16 @@ public override string ToString()
public static ContentRangeHeaderValue Parse(StringSegment input)
{
var index = 0;
return Parser.ParseValue(input, ref index);
return Parser.ParseValue(input, ref index)!;
}

public static bool TryParse(StringSegment input, out ContentRangeHeaderValue parsedValue)
public static bool TryParse(StringSegment input, [NotNullWhen(true)] out ContentRangeHeaderValue parsedValue)
{
var index = 0;
return Parser.TryParseValue(input, ref index, out parsedValue);
return Parser.TryParseValue(input, ref index, out parsedValue!);
}

private static int GetContentRangeLength(StringSegment input, int startIndex, out ContentRangeHeaderValue parsedValue)
private static int GetContentRangeLength(StringSegment input, int startIndex, out ContentRangeHeaderValue? parsedValue)
{
Contract.Requires(startIndex >= 0);

Expand Down Expand Up @@ -351,7 +352,7 @@ private static bool TryCreateContentRange(
int toLength,
int lengthStartIndex,
int lengthLength,
out ContentRangeHeaderValue parsedValue)
[NotNullWhen(true)]out ContentRangeHeaderValue? parsedValue)
{
parsedValue = null;

Expand Down
6 changes: 2 additions & 4 deletions src/Http/Headers/src/CookieHeaderParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal CookieHeaderParser(bool supportsMultipleValues)
{
}

public override bool TryParseValue(StringSegment value, ref int index, out CookieHeaderValue parsedValue)
public override bool TryParseValue(StringSegment value, ref int index, out CookieHeaderValue? parsedValue)
{
parsedValue = null;

Expand Down Expand Up @@ -43,8 +43,7 @@ public override bool TryParseValue(StringSegment value, ref int index, out Cooki
return SupportsMultipleValues;
}

CookieHeaderValue result = null;
if (!CookieHeaderValue.TryGetCookieLength(value, ref current, out result))
if (!CookieHeaderValue.TryGetCookieLength(value, ref current, out var result))
{
return false;
}
Expand All @@ -64,7 +63,6 @@ public override bool TryParseValue(StringSegment value, ref int index, out Cooki

private static int GetNextNonEmptyOrWhitespaceIndex(StringSegment input, int startIndex, bool skipEmptyValues, out bool separatorFound)
{
Contract.Requires(input != null);
Contract.Requires(startIndex <= input.Length); // it's OK if index == value.Length.

separatorFound = false;
Expand Down
Loading