From 18d261b20c2e7f23ebd1971a8ce2fa701be757e0 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Thu, 10 Sep 2020 15:15:11 -0700 Subject: [PATCH] Use T? for unconstrained nullable types (#25772) --- .../src/AuthenticationProperties.cs | 5 ++--- src/Http/Headers/src/BaseHeaderParser.cs | 5 ++--- src/Http/Headers/src/CacheControlHeaderValue.cs | 2 +- .../Headers/src/ContentDispositionHeaderValue.cs | 2 +- src/Http/Headers/src/ContentRangeHeaderValue.cs | 2 +- src/Http/Headers/src/EntityTagHeaderValue.cs | 4 ++-- src/Http/Headers/src/GenericHeaderParser.cs | 5 ++--- src/Http/Headers/src/HttpHeaderParser.cs | 5 ++--- src/Http/Headers/src/MediaTypeHeaderValue.cs | 4 ++-- src/Http/Headers/src/NameValueHeaderValue.cs | 4 ++-- .../Headers/src/RangeConditionHeaderValue.cs | 2 +- src/Http/Headers/src/RangeHeaderValue.cs | 2 +- src/Http/Headers/src/SetCookieHeaderValue.cs | 4 ++-- .../Headers/src/StringWithQualityHeaderValue.cs | 4 ++-- .../src/HttpResponseJsonExtensions.cs | 8 ++++---- .../test/HttpResponseJsonExtensionsTests.cs | 2 +- src/Http/Http.Features/src/FeatureCollection.cs | 6 ++---- src/Http/Http.Features/src/FeatureReference.cs | 13 +++++-------- src/Http/Http.Features/src/FeatureReferences.cs | 16 +++++++--------- src/Http/Http/src/Builder/ApplicationBuilder.cs | 4 +--- 20 files changed, 43 insertions(+), 56 deletions(-) diff --git a/src/Http/Authentication.Abstractions/src/AuthenticationProperties.cs b/src/Http/Authentication.Abstractions/src/AuthenticationProperties.cs index 28afa3a7de..3195bfe0ec 100644 --- a/src/Http/Authentication.Abstractions/src/AuthenticationProperties.cs +++ b/src/Http/Authentication.Abstractions/src/AuthenticationProperties.cs @@ -144,8 +144,7 @@ namespace Microsoft.AspNetCore.Authentication /// Parameter type. /// Parameter key. /// Retrieved value or the default value if the property is not set. - [return: MaybeNull] - public T GetParameter(string key) + public T? GetParameter(string key) => Parameters.TryGetValue(key, out var obj) && obj is T value ? value : default; /// @@ -154,7 +153,7 @@ namespace Microsoft.AspNetCore.Authentication /// Parameter type. /// Parameter key. /// Value to set. - public void SetParameter(string key, [MaybeNull] T value) + public void SetParameter(string key, T value) => Parameters[key] = value; /// diff --git a/src/Http/Headers/src/BaseHeaderParser.cs b/src/Http/Headers/src/BaseHeaderParser.cs index 4e80787878..9400a13aef 100644 --- a/src/Http/Headers/src/BaseHeaderParser.cs +++ b/src/Http/Headers/src/BaseHeaderParser.cs @@ -1,7 +1,6 @@ // 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 @@ -13,9 +12,9 @@ namespace Microsoft.Net.Http.Headers { } - protected abstract int GetParsedValueLength(StringSegment value, int startIndex, [MaybeNull] out T parsedValue); + protected abstract int GetParsedValueLength(StringSegment value, int startIndex, out T? parsedValue); - public sealed override bool TryParseValue(StringSegment value, ref int index, [MaybeNull] out T parsedValue) + public sealed override bool TryParseValue(StringSegment value, ref int index, out T? parsedValue) { parsedValue = default; diff --git a/src/Http/Headers/src/CacheControlHeaderValue.cs b/src/Http/Headers/src/CacheControlHeaderValue.cs index 14a59b2422..e2e8b37c17 100644 --- a/src/Http/Headers/src/CacheControlHeaderValue.cs +++ b/src/Http/Headers/src/CacheControlHeaderValue.cs @@ -32,7 +32,7 @@ namespace Microsoft.Net.Http.Headers // 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 Parser - = new GenericHeaderParser(true, GetCacheControlLength!); + = new GenericHeaderParser(true, GetCacheControlLength); private static readonly Action CheckIsValidTokenAction = CheckIsValidToken; diff --git a/src/Http/Headers/src/ContentDispositionHeaderValue.cs b/src/Http/Headers/src/ContentDispositionHeaderValue.cs index 3e629d10cb..f7ea2047d7 100644 --- a/src/Http/Headers/src/ContentDispositionHeaderValue.cs +++ b/src/Http/Headers/src/ContentDispositionHeaderValue.cs @@ -27,7 +27,7 @@ namespace Microsoft.Net.Http.Headers private static readonly char[] SingleQuote = new char[] { '\'' }; private static readonly HttpHeaderParser Parser - = new GenericHeaderParser(false, GetDispositionTypeLength!); + = new GenericHeaderParser(false, GetDispositionTypeLength); // Use list instead of dictionary since we may have multiple parameters with the same name. private ObjectCollection? _parameters; diff --git a/src/Http/Headers/src/ContentRangeHeaderValue.cs b/src/Http/Headers/src/ContentRangeHeaderValue.cs index 1c9a7a55b7..1cbdb698ab 100644 --- a/src/Http/Headers/src/ContentRangeHeaderValue.cs +++ b/src/Http/Headers/src/ContentRangeHeaderValue.cs @@ -13,7 +13,7 @@ namespace Microsoft.Net.Http.Headers public class ContentRangeHeaderValue { private static readonly HttpHeaderParser Parser - = new GenericHeaderParser(false, GetContentRangeLength!); + = new GenericHeaderParser(false, GetContentRangeLength); private StringSegment _unit; private long? _from; diff --git a/src/Http/Headers/src/EntityTagHeaderValue.cs b/src/Http/Headers/src/EntityTagHeaderValue.cs index a468eeb5dc..1d07735c49 100644 --- a/src/Http/Headers/src/EntityTagHeaderValue.cs +++ b/src/Http/Headers/src/EntityTagHeaderValue.cs @@ -14,13 +14,13 @@ namespace Microsoft.Net.Http.Headers // Note that the ETag header does not allow a * but we're not that strict: We allow both '*' and ETag values in a single value. // We can't guarantee that a single parsed value will be used directly in an ETag header. private static readonly HttpHeaderParser SingleValueParser - = new GenericHeaderParser(false, GetEntityTagLength!); + = new GenericHeaderParser(false, GetEntityTagLength); // Note that if multiple ETag values are allowed (e.g. 'If-Match', 'If-None-Match'), according to the RFC // the value must either be '*' or a list of ETag values. It's not allowed to have both '*' and a list of // ETag values. We're not that strict: We allow both '*' and ETag values in a list. If the server sends such // an invalid list, we want to be able to represent it using the corresponding header property. private static readonly HttpHeaderParser MultipleValueParser - = new GenericHeaderParser(true, GetEntityTagLength!); + = new GenericHeaderParser(true, GetEntityTagLength); private static EntityTagHeaderValue? AnyType; diff --git a/src/Http/Headers/src/GenericHeaderParser.cs b/src/Http/Headers/src/GenericHeaderParser.cs index 53a5777448..1d4499e0b6 100644 --- a/src/Http/Headers/src/GenericHeaderParser.cs +++ b/src/Http/Headers/src/GenericHeaderParser.cs @@ -2,14 +2,13 @@ // 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 Microsoft.Extensions.Primitives; namespace Microsoft.Net.Http.Headers { internal sealed class GenericHeaderParser : BaseHeaderParser { - internal delegate int GetParsedValueLengthDelegate(StringSegment value, int startIndex, [MaybeNull] out T parsedValue); + internal delegate int GetParsedValueLengthDelegate(StringSegment value, int startIndex, out T? parsedValue); private GetParsedValueLengthDelegate _getParsedValueLength; @@ -24,7 +23,7 @@ namespace Microsoft.Net.Http.Headers _getParsedValueLength = getParsedValueLength; } - protected override int GetParsedValueLength(StringSegment value, int startIndex, [MaybeNull] out T parsedValue) + protected override int GetParsedValueLength(StringSegment value, int startIndex, out T? parsedValue) { return _getParsedValueLength(value, startIndex, out parsedValue); } diff --git a/src/Http/Headers/src/HttpHeaderParser.cs b/src/Http/Headers/src/HttpHeaderParser.cs index addb5479f8..942b470680 100644 --- a/src/Http/Headers/src/HttpHeaderParser.cs +++ b/src/Http/Headers/src/HttpHeaderParser.cs @@ -28,10 +28,9 @@ namespace Microsoft.Net.Http.Headers // pointing to the next non-whitespace character after a delimiter. E.g. if called with a start index of 0 // for string "value , second_value", then after the call completes, 'index' must point to 's', i.e. the first // non-whitespace after the separator ','. - public abstract bool TryParseValue(StringSegment value, ref int index, [MaybeNull] out T parsedValue); + public abstract bool TryParseValue(StringSegment value, ref int index, out T? parsedValue); - [return: MaybeNull] - public T ParseValue(StringSegment value, ref int index) + public T? ParseValue(StringSegment value, ref int index) { // Index may be value.Length (e.g. both 0). This may be allowed for some headers (e.g. Accept but not // allowed by others (e.g. Content-Length). The parser has to decide if this is valid or not. diff --git a/src/Http/Headers/src/MediaTypeHeaderValue.cs b/src/Http/Headers/src/MediaTypeHeaderValue.cs index 87ca778a7c..af2bba6e22 100644 --- a/src/Http/Headers/src/MediaTypeHeaderValue.cs +++ b/src/Http/Headers/src/MediaTypeHeaderValue.cs @@ -30,9 +30,9 @@ namespace Microsoft.Net.Http.Headers private static readonly char[] PeriodCharacterArray = new char[] { PeriodCharacter }; private static readonly HttpHeaderParser SingleValueParser - = new GenericHeaderParser(false, GetMediaTypeLength!); + = new GenericHeaderParser(false, GetMediaTypeLength); private static readonly HttpHeaderParser MultipleValueParser - = new GenericHeaderParser(true, GetMediaTypeLength!); + = new GenericHeaderParser(true, GetMediaTypeLength); // Use a collection instead of a dictionary since we may have multiple parameters with the same name. private ObjectCollection? _parameters; diff --git a/src/Http/Headers/src/NameValueHeaderValue.cs b/src/Http/Headers/src/NameValueHeaderValue.cs index 9ef6283cd9..462fbc11c7 100644 --- a/src/Http/Headers/src/NameValueHeaderValue.cs +++ b/src/Http/Headers/src/NameValueHeaderValue.cs @@ -17,9 +17,9 @@ namespace Microsoft.Net.Http.Headers public class NameValueHeaderValue { private static readonly HttpHeaderParser SingleValueParser - = new GenericHeaderParser(false, GetNameValueLength!); + = new GenericHeaderParser(false, GetNameValueLength); internal static readonly HttpHeaderParser MultipleValueParser - = new GenericHeaderParser(true, GetNameValueLength!); + = new GenericHeaderParser(true, GetNameValueLength); private StringSegment _name; private StringSegment _value; diff --git a/src/Http/Headers/src/RangeConditionHeaderValue.cs b/src/Http/Headers/src/RangeConditionHeaderValue.cs index ebe21f59b8..55e9cefda9 100644 --- a/src/Http/Headers/src/RangeConditionHeaderValue.cs +++ b/src/Http/Headers/src/RangeConditionHeaderValue.cs @@ -11,7 +11,7 @@ namespace Microsoft.Net.Http.Headers public class RangeConditionHeaderValue { private static readonly HttpHeaderParser Parser - = new GenericHeaderParser(false, GetRangeConditionLength!); + = new GenericHeaderParser(false, GetRangeConditionLength); private DateTimeOffset? _lastModified; private EntityTagHeaderValue? _entityTag; diff --git a/src/Http/Headers/src/RangeHeaderValue.cs b/src/Http/Headers/src/RangeHeaderValue.cs index 8247c3ff60..a022c056ec 100644 --- a/src/Http/Headers/src/RangeHeaderValue.cs +++ b/src/Http/Headers/src/RangeHeaderValue.cs @@ -13,7 +13,7 @@ namespace Microsoft.Net.Http.Headers public class RangeHeaderValue { private static readonly HttpHeaderParser Parser - = new GenericHeaderParser(false, GetRangeLength!); + = new GenericHeaderParser(false, GetRangeLength); private StringSegment _unit; private ICollection? _ranges; diff --git a/src/Http/Headers/src/SetCookieHeaderValue.cs b/src/Http/Headers/src/SetCookieHeaderValue.cs index 2360cf0daa..1afee5760f 100644 --- a/src/Http/Headers/src/SetCookieHeaderValue.cs +++ b/src/Http/Headers/src/SetCookieHeaderValue.cs @@ -33,9 +33,9 @@ namespace Microsoft.Net.Http.Headers private const string ExpiresDateFormat = "r"; private static readonly HttpHeaderParser SingleValueParser - = new GenericHeaderParser(false, GetSetCookieLength!); + = new GenericHeaderParser(false, GetSetCookieLength); private static readonly HttpHeaderParser MultipleValueParser - = new GenericHeaderParser(true, GetSetCookieLength!); + = new GenericHeaderParser(true, GetSetCookieLength); private StringSegment _name; private StringSegment _value; diff --git a/src/Http/Headers/src/StringWithQualityHeaderValue.cs b/src/Http/Headers/src/StringWithQualityHeaderValue.cs index 18fe33975c..1bb3003004 100644 --- a/src/Http/Headers/src/StringWithQualityHeaderValue.cs +++ b/src/Http/Headers/src/StringWithQualityHeaderValue.cs @@ -13,9 +13,9 @@ namespace Microsoft.Net.Http.Headers public class StringWithQualityHeaderValue { private static readonly HttpHeaderParser SingleValueParser - = new GenericHeaderParser(false, GetStringWithQualityLength!); + = new GenericHeaderParser(false, GetStringWithQualityLength); private static readonly HttpHeaderParser MultipleValueParser - = new GenericHeaderParser(true, GetStringWithQualityLength!); + = new GenericHeaderParser(true, GetStringWithQualityLength); private StringSegment _value; private double? _quality; diff --git a/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs b/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs index 802bb7f85d..b35c4f2b32 100644 --- a/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs +++ b/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs @@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Http [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] public static Task WriteAsJsonAsync( this HttpResponse response, - [AllowNull] TValue value, + TValue value, CancellationToken cancellationToken = default) { return response.WriteAsJsonAsync(value, options: null, contentType: null, cancellationToken); @@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Http [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] public static Task WriteAsJsonAsync( this HttpResponse response, - [AllowNull] TValue value, + TValue value, JsonSerializerOptions? options, CancellationToken cancellationToken = default) { @@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Http [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] public static Task WriteAsJsonAsync( this HttpResponse response, - [AllowNull] TValue value, + TValue value, JsonSerializerOptions? options, string? contentType, CancellationToken cancellationToken = default) @@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Http options ??= ResolveSerializerOptions(response.HttpContext); response.ContentType = contentType ?? JsonConstants.JsonContentTypeWithCharset; - return JsonSerializer.SerializeAsync(response.Body, value!, options, cancellationToken); + return JsonSerializer.SerializeAsync(response.Body, value, options, cancellationToken); } /// diff --git a/src/Http/Http.Extensions/test/HttpResponseJsonExtensionsTests.cs b/src/Http/Http.Extensions/test/HttpResponseJsonExtensionsTests.cs index 44def401e8..d0118166b0 100644 --- a/src/Http/Http.Extensions/test/HttpResponseJsonExtensionsTests.cs +++ b/src/Http/Http.Extensions/test/HttpResponseJsonExtensionsTests.cs @@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Http.Extensions.Tests context.Response.Body = body; // Act - await context.Response.WriteAsJsonAsync(value: null); + await context.Response.WriteAsJsonAsync(value: null); // Assert Assert.Equal(JsonConstants.JsonContentTypeWithCharset, context.Response.ContentType); diff --git a/src/Http/Http.Features/src/FeatureCollection.cs b/src/Http/Http.Features/src/FeatureCollection.cs index b5f70b7c4e..d46a1e38e5 100644 --- a/src/Http/Http.Features/src/FeatureCollection.cs +++ b/src/Http/Http.Features/src/FeatureCollection.cs @@ -4,7 +4,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Microsoft.AspNetCore.Http.Features @@ -94,10 +93,9 @@ namespace Microsoft.AspNetCore.Http.Features } } - [return: MaybeNull] - public TFeature Get() + public TFeature? Get() { - return (TFeature)this[typeof(TFeature)]; + return (TFeature?)this[typeof(TFeature)]; } public void Set(TFeature instance) diff --git a/src/Http/Http.Features/src/FeatureReference.cs b/src/Http/Http.Features/src/FeatureReference.cs index 516fb70e90..f93e92885a 100644 --- a/src/Http/Http.Features/src/FeatureReference.cs +++ b/src/Http/Http.Features/src/FeatureReference.cs @@ -1,31 +1,28 @@ // 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; - namespace Microsoft.AspNetCore.Http.Features { public struct FeatureReference { - private T _feature; + private T? _feature; private int _revision; - private FeatureReference(T feature, int revision) + private FeatureReference(T? feature, int revision) { _feature = feature; _revision = revision; } - public static readonly FeatureReference Default = new FeatureReference(default(T)!, -1); + public static readonly FeatureReference Default = new FeatureReference(default(T), -1); - [return: MaybeNull] - public T Fetch(IFeatureCollection features) + public T? Fetch(IFeatureCollection features) { if (_revision == features.Revision) { return _feature; } - _feature = (T)features[typeof(T)]!; + _feature = (T)features[typeof(T)]; _revision = features.Revision; return _feature; } diff --git a/src/Http/Http.Features/src/FeatureReferences.cs b/src/Http/Http.Features/src/FeatureReferences.cs index 84bf46974c..b263b3d160 100644 --- a/src/Http/Http.Features/src/FeatureReferences.cs +++ b/src/Http/Http.Features/src/FeatureReferences.cs @@ -2,7 +2,6 @@ // 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.Runtime.CompilerServices; namespace Microsoft.AspNetCore.Http.Features @@ -37,8 +36,7 @@ namespace Microsoft.AspNetCore.Http.Features // be able to pass ref values that "dot through" the TCache struct memory, // if it was a Property then that getter would return a copy of the memory // preventing the use of "ref" - [AllowNull, MaybeNull] - public TCache Cache; + public TCache? Cache; // Careful with modifications to the Fetch method; it is carefully constructed for inlining // See: https://github.com/aspnet/HttpAbstractions/pull/704 @@ -61,10 +59,10 @@ namespace Microsoft.AspNetCore.Http.Features // // Generally Fetch is called at a ratio > x4 of UpdateCached so this is a large gain [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TFeature Fetch( - [AllowNull, MaybeNull] ref TFeature cached, + public TFeature? Fetch( + ref TFeature? cached, TState state, - Func factory) where TFeature : class? + Func factory) where TFeature : class? { var flush = false; var revision = Collection?.Revision ?? ContextDisposed(); @@ -80,7 +78,7 @@ namespace Microsoft.AspNetCore.Http.Features } // Update and cache clearing logic, when the fast-path in Fetch isn't applicable - private TFeature UpdateCached(ref TFeature cached, TState state, Func factory, int revision, bool flush) where TFeature : class? + private TFeature? UpdateCached(ref TFeature? cached, TState state, Func factory, int revision, bool flush) where TFeature : class? { if (flush) { @@ -108,8 +106,8 @@ namespace Microsoft.AspNetCore.Http.Features return cached; } - public TFeature Fetch([AllowNull, MaybeNull] ref TFeature cached, Func factory) - where TFeature : class? => Fetch(ref cached!, Collection, factory); + public TFeature? Fetch(ref TFeature? cached, Func factory) + where TFeature : class? => Fetch(ref cached, Collection, factory); private static int ContextDisposed() { diff --git a/src/Http/Http/src/Builder/ApplicationBuilder.cs b/src/Http/Http/src/Builder/ApplicationBuilder.cs index f7cc65897c..552312fcaa 100644 --- a/src/Http/Http/src/Builder/ApplicationBuilder.cs +++ b/src/Http/Http/src/Builder/ApplicationBuilder.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -58,8 +57,7 @@ namespace Microsoft.AspNetCore.Builder public IDictionary Properties { get; } - [return: MaybeNull] - private T GetProperty(string key) + private T? GetProperty(string key) { return Properties.TryGetValue(key, out var value) ? (T)value : default(T); }